Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

net_class_device.c

00001 /***************************************************************************
00002  * CVSID: $Id: net_class_device.c,v 1.2 2004/04/12 21:10:00 joe Exp $
00003  *
00004  * Network device class
00005  *
00006  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
00007  * Copyright (C) 2004 Novell, Inc.
00008  *
00009  * Licensed under the Academic Free License version 2.0
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  **************************************************************************/
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #  include <config.h>
00029 #endif
00030 
00031 #include <ctype.h>
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <getopt.h>
00036 #include <assert.h>
00037 #include <unistd.h>
00038 #include <stdarg.h>
00039 #include <limits.h>
00040 #include <errno.h>
00041 #include <sys/types.h>
00042 #include <sys/stat.h>
00043 #include <sys/ioctl.h>
00044 #include <fcntl.h>
00045 #include <net/if_arp.h> /* for ARPHRD_... */
00046 #include <sys/socket.h>
00047 #include <linux/types.h>
00048 #include <linux/netlink.h>
00049 #include <linux/rtnetlink.h>
00050 #include <sys/ioctl.h>
00051 #include <net/if.h>
00052 
00053 #include "../logger.h"
00054 #include "../device_store.h"
00055 #include "../hald.h"
00056 
00057 #include "class_device.h"
00058 #include "common.h"
00059 
00067 static const char *
00068 media_type_to_string (int media_type)
00069 {
00070     switch (media_type) {
00071     case ARPHRD_NETROM:
00072         return "NET/ROM pseudo";
00073 
00074     case ARPHRD_ETHER:
00075         return "Ethernet";
00076 
00077     case ARPHRD_EETHER:
00078         return "Experimental Ethernet";
00079 
00080     case ARPHRD_AX25:
00081         return "AX.25 Level 2";
00082 
00083     case ARPHRD_PRONET:
00084         return "PROnet token ring";
00085 
00086     case ARPHRD_CHAOS:
00087         return "Chaosnet";
00088 
00089     case ARPHRD_ARCNET:
00090         return "ARCnet";
00091 
00092     case ARPHRD_APPLETLK:
00093         return "Appletalk";
00094 
00095     case ARPHRD_DLCI:
00096         return "Frame Relay DLCI";
00097 
00098     case ARPHRD_ATM:
00099         return "ATM";
00100 
00101     case ARPHRD_METRICOM:
00102         return "Metricom STRIP (new IANA id)";
00103 
00104 #ifdef ARPHRD_IEEE1394
00105     case ARPHRD_IEEE1394:
00106         return "IEEE1394 IPv4 - RFC 2734";
00107 #endif
00108 
00109     default:
00110         return "Unknown";
00111     }
00112 }
00113 
00120 static guint16
00121 mdio_read (int sockfd, struct ifreq *ifr, int location,
00122        gboolean new_ioctl_nums)
00123 {
00124     guint16 *data = (guint16 *) &(ifr->ifr_data);
00125 
00126     data[1] = location;
00127 
00128     if (ioctl (sockfd,
00129            new_ioctl_nums ? 0x8948 : SIOCDEVPRIVATE + 1,
00130            ifr) < 0) {
00131         HAL_ERROR (("SIOCGMIIREG on %s failed: %s\n",
00132                 ifr->ifr_name, strerror (errno)));
00133         return -1;
00134     }
00135     return data[3];
00136 }
00137 
00138 static void
00139 mii_get_rate (HalDevice *d)
00140 {
00141     const char *ifname;
00142     int sockfd;
00143     struct ifreq ifr;
00144     gboolean new_ioctl_nums;
00145     guint16 link_word;
00146 
00147     ifname = hal_device_property_get_string (d, "net.interface");
00148 
00149     sockfd = socket (AF_INET, SOCK_DGRAM, 0);
00150     if (sockfd < 0) {
00151         HAL_ERROR (("cannot open socket on interface %s; errno=%d",
00152                 ifname, errno));
00153         return;
00154     }
00155 
00156     snprintf (ifr.ifr_name, IFNAMSIZ, ifname);
00157 
00158     if (ioctl (sockfd, 0x8947, &ifr) >= 0)
00159         new_ioctl_nums = TRUE;
00160     else if (ioctl (sockfd, SIOCDEVPRIVATE, &ifr) >= 0)
00161         new_ioctl_nums = FALSE;
00162     else {
00163         HAL_ERROR (("SIOCGMIIPHY on %s failed: %s",
00164                 ifr.ifr_name, strerror (errno)));
00165         close (sockfd);
00166         return;
00167     }
00168 
00169     /* Read link word
00170      *
00171      * 0x8000  Link partner can send more info.
00172      * 0x4000  Link partner got our advertised abilities.
00173      * 0x2000  Fault detected by link partner (uncommon).
00174      * 0x0400  Flow control supported (currently uncommon)
00175      * 0x0200  100baseT4 supported (uncommon)
00176      * 0x0100  100baseTx-FD (full duplex) supported
00177      * 0x0080  100baseTx supported
00178      * 0x0040  10baseT-FD supported
00179      * 0x0020  10baseT supported
00180      * 0x001F  Protocol selection bits, always 0x0001 for Ethernet.
00181      */
00182     link_word = mdio_read (sockfd, &ifr, 5, new_ioctl_nums);
00183 
00184     if (link_word & 0x380) {
00185         hal_device_property_set_int (d, "net.ethernet.rate",
00186                          100 * 1000 * 1000);
00187     }
00188 
00189     if (link_word & 0x60) {
00190         hal_device_property_set_int (d, "net.ethernet.rate",
00191                          10 * 1000 * 1000);
00192     }
00193 
00194     close (sockfd);
00195 }
00196 
00197 static void
00198 mii_get_link (HalDevice *d)
00199 {
00200     const char *ifname;
00201     int sockfd;
00202     struct ifreq ifr;
00203     gboolean new_ioctl_nums;
00204     guint16 status_word;
00205 
00206     ifname = hal_device_property_get_string (d, "net.interface");
00207 
00208     sockfd = socket (AF_INET, SOCK_DGRAM, 0);
00209     if (sockfd < 0) {
00210         HAL_ERROR (("cannot open socket on interface %s; errno=%d",
00211                 ifname, errno));
00212         return;
00213     }
00214 
00215     snprintf (ifr.ifr_name, IFNAMSIZ, ifname);
00216 
00217     if (ioctl (sockfd, 0x8947, &ifr) >= 0)
00218         new_ioctl_nums = TRUE;
00219     else if (ioctl (sockfd, SIOCDEVPRIVATE, &ifr) >= 0)
00220         new_ioctl_nums = FALSE;
00221     else {
00222         HAL_ERROR (("SIOCGMIIPHY on %s failed: %s",
00223                 ifr.ifr_name, strerror (errno)));
00224         close (sockfd);
00225         return;
00226     }
00227 
00228     /* Refer to http://www.scyld.com/diag/mii-status.html for
00229      * the full explanation of the numbers
00230      *
00231      * 0x8000  Capable of 100baseT4.
00232      * 0x7800  Capable of 10/100 HD/FD (most common).
00233      * 0x0040  Preamble suppression permitted.
00234      * 0x0020  Autonegotiation complete.
00235      * 0x0010  Remote fault.
00236      * 0x0008  Capable of Autonegotiation.
00237      * 0x0004  Link established ("sticky"* on link failure)
00238      * 0x0002  Jabber detected ("sticky"* on transmit jabber)
00239      * 0x0001  Extended MII register exist.
00240      *
00241      */
00242 
00243     /* We have to read it twice to clear any "sticky" bits */
00244     status_word = mdio_read (sockfd, &ifr, 1, new_ioctl_nums);
00245     status_word = mdio_read (sockfd, &ifr, 1, new_ioctl_nums);
00246 
00247     if ((status_word & 0x0016) == 0x0004)
00248         hal_device_property_set_bool (d, "net.ethernet.link", TRUE);
00249     else
00250         hal_device_property_set_bool (d, "net.ethernet.link", FALSE);
00251 
00252     /* Also get the link rate */
00253     mii_get_rate (d);
00254 
00255     close (sockfd);
00256 }
00257 
00258 static void
00259 link_detection_handle_message (struct nlmsghdr *hdr, HalDevice *d)
00260 {
00261     struct ifinfomsg *ifinfo;
00262     char ifname[1024];
00263     struct rtattr *attr;
00264     int attr_len;
00265 
00266     ifinfo = NLMSG_DATA (hdr);
00267 
00268     if (hdr->nlmsg_len < NLMSG_LENGTH (sizeof (struct ifinfomsg))) {
00269         HAL_ERROR (("Packet too small or truncated for ifinfomsg"));
00270         return;
00271     }
00272 
00273     memset (&ifname, 0, sizeof (ifname));
00274 
00275     attr = (void *) ifinfo + NLMSG_ALIGN (sizeof (struct ifinfomsg));
00276     attr_len = NLMSG_PAYLOAD (hdr, sizeof (struct ifinfomsg));
00277 
00278     while (RTA_OK (attr, attr_len)) {
00279         if (attr->rta_type == IFLA_IFNAME) {
00280             int l = RTA_PAYLOAD (attr);
00281 
00282             if (l > 1023)
00283                 l = 1023;
00284 
00285             strncpy (ifname, RTA_DATA (attr), l);
00286         }
00287 
00288         attr = RTA_NEXT (attr, attr_len);
00289     }
00290 
00291     hal_device_property_set_bool (d, "net.ethernet.link",
00292                       ifinfo->ifi_flags & IFF_RUNNING ?
00293                       TRUE : FALSE);
00294 
00295     /*
00296      * Check the MII registers to set our link rate if we haven't set
00297      * it previously.
00298      */
00299     if (!hal_device_has_property (d, "net.ethernet.rate"))
00300         mii_get_rate (d);
00301 }
00302 
00303 #define VALID_NLMSG(h, s) ((NLMSG_OK (h, s) && \
00304                            s >= sizeof (struct nlmsghdr) && \
00305                            s >= h->nlmsg_len))
00306 
00307 static gboolean
00308 link_detection_data_ready (GIOChannel *channel, GIOCondition cond,
00309                gpointer user_data)
00310 {
00311     HalDevice *d = HAL_DEVICE (user_data);
00312     int fd;
00313     int bytes_read;
00314     int total_read = 0;
00315     char buf[1024];
00316 
00317     if (cond & ~(G_IO_IN | G_IO_PRI)) {
00318         HAL_ERROR (("Error occurred on netlink socket"));
00319         return FALSE;
00320     }
00321 
00322     fd = g_io_channel_unix_get_fd (channel);
00323 
00324     do {
00325         errno = 0;
00326         bytes_read = recv (fd,
00327                    buf + total_read,
00328                    sizeof (buf) - total_read,
00329                    MSG_DONTWAIT);
00330 
00331         if (bytes_read > 0)
00332             total_read += bytes_read;
00333     } while (bytes_read > 0 || errno == EINTR);
00334 
00335     if (bytes_read < 0 && errno != EAGAIN) {
00336         HAL_ERROR (("Error reading data off netlink socket"));
00337         return FALSE;
00338     }
00339 
00340     if (total_read > 0) {
00341         struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
00342         int offset = 0;
00343 
00344         while (offset < total_read &&
00345                VALID_NLMSG (hdr, total_read - offset)) {
00346             if (hdr->nlmsg_type == NLMSG_DONE)
00347                 break;
00348 
00349             if (hdr->nlmsg_type == RTM_NEWLINK ||
00350                 hdr->nlmsg_type == RTM_DELLINK)
00351                 link_detection_handle_message (hdr, d);
00352 
00353             offset += hdr->nlmsg_len;
00354             hdr = (struct nlmsghdr *) (buf + offset);
00355         }
00356 
00357         if (offset < total_read &&
00358             !VALID_NLMSG (hdr, total_read - offset)) {
00359             HAL_ERROR (("Packet too small or truncated"));
00360             return FALSE;
00361         }
00362     }
00363 
00364     return TRUE;
00365 }
00366 
00367 static void
00368 link_detection_init (HalDevice *d)
00369 {
00370     int fd;
00371     struct sockaddr_nl addr;
00372     GIOChannel *channel;
00373 
00374     fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
00375 
00376     if (fd < 0) {
00377         HAL_ERROR (("Unable to create netlink socket"));
00378         return;
00379     }
00380 
00381     memset (&addr, 0, sizeof (addr));
00382     addr.nl_family = AF_NETLINK;
00383     addr.nl_pid = getpid ();
00384     addr.nl_groups = RTMGRP_LINK;
00385 
00386     if (bind (fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
00387         HAL_ERROR (("Unable to bind to netlink socket"));
00388         return;
00389     }
00390 
00391     channel = g_io_channel_unix_new (fd);
00392 
00393     g_io_add_watch (channel, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_NVAL,
00394             link_detection_data_ready, d);
00395 }
00396 
00411 static void 
00412 net_class_pre_process (ClassDeviceHandler *self,
00413                HalDevice *d,
00414                const char *sysfs_path,
00415                struct sysfs_class_device *class_device)
00416 {
00417     struct sysfs_attribute *attr;
00418     char *address = NULL;
00419     int media_type = 0;
00420     const char *media;
00421 
00422     hal_device_property_set_string (d, "net.linux.sysfs_path", sysfs_path);
00423     hal_device_property_set_string (d, "net.interface",
00424                     class_device->name);
00425 
00426     attr = sysfs_get_classdev_attr (class_device, "address");
00427 
00428     if (attr != NULL)
00429         address = g_strstrip (g_strdup (attr->value));
00430 
00431     attr = sysfs_get_classdev_attr (class_device, "type");
00432 
00433     if (attr != NULL)
00434         media_type = parse_dec (attr->value);
00435 
00436     /* FIXME: Other address types for non-ethernet devices */
00437     if (address != NULL && media_type == ARPHRD_ETHER) {
00438         unsigned int a5, a4, a3, a2, a1, a0;
00439 
00440         hal_device_property_set_string (d, "net.ethernet.mac_addr",
00441                         address);
00442 
00443         if (sscanf (address, "%x:%x:%x:%x:%x:%x",
00444                 &a5, &a4, &a3, &a2, &a1, &a0) == 6) {
00445             dbus_uint32_t mac_upper, mac_lower;
00446 
00447             mac_upper = (a5 << 16) | (a4 << 8) | a3;
00448             mac_lower = (a2 << 16) | (a1 << 8) | a0;
00449 
00450             hal_device_property_set_int (d,
00451                              "net.ethernet.mac_addr_upper24",
00452                              (dbus_int32_t) mac_upper);
00453             hal_device_property_set_int (d,
00454                              "net.ethernet.mac_addr_lower24",
00455                              (dbus_int32_t) mac_lower);
00456         }
00457 
00458         /* Get the initial link state from the MII registers */
00459         mii_get_link (d);
00460     }
00461 
00462     g_free (address);
00463 
00464     hal_device_property_set_int (d, "net.arp_proto_hw_id", media_type);
00465 
00466     media = media_type_to_string (media_type);
00467     hal_device_property_set_string (d, "net.media", media);
00468 }
00469 
00470 static void
00471 net_class_post_merge (ClassDeviceHandler *self, HalDevice *d)
00472 {
00473     if (hal_device_has_capability (d, "net.ethernet"))
00474         link_detection_init (d);
00475 }
00476 
00478 ClassDeviceHandler net_class_handler = {
00479     class_device_init,                  
00480     class_device_detection_done,        
00481     class_device_shutdown,              
00482     class_device_tick,                  
00483     class_device_accept,                
00484     class_device_visit,                 
00485     class_device_removed,               
00486     class_device_udev_event,            
00487     class_device_get_device_file_target,
00488     net_class_pre_process,              
00489     net_class_post_merge,               
00490     class_device_got_udi,               
00491     NULL,                               
00492     "net",                              
00493     "net",                              
00494     FALSE,                              
00495     TRUE                                
00496 };
00497 

Generated on Sat Apr 24 19:57:45 2004 for HAL by doxygen 1.3.6-20040222