Back to index

lshw  02.16
network.cc
Go to the documentation of this file.
00001 /*
00002  * network.cc
00003  *
00004  * This scan uses the same IOCTLs as ethtool, ifconfig or mii-diag to report
00005  * information about network interfaces like:
00006  * - medium type (ethernet, token ring, PPP, etc.)
00007  * - hardware address (MAC address)
00008  * - link status (link detected, in error, etc.)
00009  * - speed (10Mbits, 100Mbits, etc.)
00010  * - IP addressing
00011  *
00012  * As network interfaces can be plugged on PCI, PCMCIA, ISA, USB, etc. this
00013  * scan should be executed after all bus-related scans.
00014  *
00015  */
00016 
00017 #include "version.h"
00018 #include "config.h"
00019 #include "network.h"
00020 #include "osutils.h"
00021 #include "sysfs.h"
00022 #include "options.h"
00023 #include "heuristics.h"
00024 #include <sys/socket.h>
00025 #include <sys/ioctl.h>
00026 #include <netinet/in.h>
00027 #include <arpa/inet.h>
00028 #include <net/if_arp.h>
00029 #include <linux/sockios.h>
00030 #include <net/if.h>
00031 #include <fcntl.h>
00032 #include <unistd.h>
00033 #include <stdio.h>
00034 #include <string.h>
00035 #include <string>
00036 #include <sys/types.h>
00037 using namespace std;
00038 
00039 __ID("@(#) $Id: network.cc 2433 2012-01-10 22:01:30Z lyonel $");
00040 
00041 #ifndef ARPHRD_IEEE1394
00042 #define ARPHRD_IEEE1394 24
00043 #endif
00044 #ifndef ARPHRD_SIT
00045 #define ARPHRD_SIT  776
00046 #endif
00047 
00048 #ifndef SIOCETHTOOL
00049 #define SIOCETHTOOL     0x8946
00050 #endif
00051 typedef unsigned long long u64;
00052 typedef __uint32_t u32;
00053 typedef __uint16_t u16;
00054 typedef __uint8_t u8;
00055 
00056 struct ethtool_cmd
00057 {
00058   u32 cmd;
00059   u32 supported;                                  /* Features this interface supports */
00060   u32 advertising;                                /* Features this interface advertises */
00061   u16 speed;                                      /* The forced speed, 10Mb, 100Mb, gigabit */
00062   u8 duplex;                                      /* Duplex, half or full */
00063   u8 port;                                        /* Which connector port */
00064   u8 phy_address;
00065   u8 transceiver;                                 /* Which tranceiver to use */
00066   u8 autoneg;                                     /* Enable or disable autonegotiation */
00067   u32 maxtxpkt;                                   /* Tx pkts before generating tx int */
00068   u32 maxrxpkt;                                   /* Rx pkts before generating rx int */
00069   u32 reserved[4];
00070 };
00071 
00072 #ifndef IFNAMSIZ
00073 #define IFNAMSIZ 32
00074 #endif
00075 #define SIOCGIWNAME     0x8B01                    /* get name == wireless protocol */
00076 /* SIOCGIWNAME is used to verify the presence of Wireless Extensions.
00077  * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... */
00078 
00079 #define ETHTOOL_BUSINFO_LEN     32
00080 /* these strings are set to whatever the driver author decides... */
00081 struct ethtool_drvinfo
00082 {
00083   u32 cmd;
00084   char driver[32];                                /* driver short name, "tulip", "eepro100" */
00085   char version[32];                               /* driver version string */
00086   char fw_version[32];                            /* firmware version string, if applicable */
00087   char bus_info[ETHTOOL_BUSINFO_LEN];             /* Bus info for this IF. */
00088 /*
00089  * For PCI devices, use pci_dev->slot_name.
00090  */
00091   char reserved1[32];
00092   char reserved2[16];
00093   u32 n_stats;                                    /* number of u64's from ETHTOOL_GSTATS */
00094   u32 testinfo_len;
00095   u32 eedump_len;                                 /* Size of data from ETHTOOL_GEEPROM (bytes) */
00096   u32 regdump_len;                                /* Size of data from ETHTOOL_GREGS (bytes) */
00097 };
00098 
00099 /* for passing single values */
00100 struct ethtool_value
00101 {
00102   u32 cmd;
00103   u32 data;
00104 };
00105 
00106 /* CMDs currently supported */
00107 #define ETHTOOL_GSET            0x00000001        /* Get settings. */
00108 #define ETHTOOL_GDRVINFO        0x00000003        /* Get driver info. */
00109 #define ETHTOOL_GLINK           0x0000000a        /* Get link status (ethtool_value) */
00110 
00111 /* Indicates what features are supported by the interface. */
00112 #define SUPPORTED_10baseT_Half          (1 << 0)
00113 #define SUPPORTED_10baseT_Full          (1 << 1)
00114 #define SUPPORTED_100baseT_Half         (1 << 2)
00115 #define SUPPORTED_100baseT_Full         (1 << 3)
00116 #define SUPPORTED_1000baseT_Half        (1 << 4)
00117 #define SUPPORTED_1000baseT_Full        (1 << 5)
00118 #define SUPPORTED_Autoneg               (1 << 6)
00119 #define SUPPORTED_TP                    (1 << 7)
00120 #define SUPPORTED_AUI                   (1 << 8)
00121 #define SUPPORTED_MII                   (1 << 9)
00122 #define SUPPORTED_FIBRE                 (1 << 10)
00123 #define SUPPORTED_BNC                   (1 << 11)
00124 #define SUPPORTED_10000baseT_Full       (1 << 12)
00125 
00126 /* The forced speed, 10Mb, 100Mb, gigabit, 10GbE. */
00127 #define SPEED_10                10
00128 #define SPEED_100               100
00129 #define SPEED_1000              1000
00130 #define SPEED_10000             10000
00131 
00132 /* Duplex, half or full. */
00133 #define DUPLEX_HALF             0x00
00134 #define DUPLEX_FULL             0x01
00135 
00136 /* Which connector port. */
00137 #define PORT_TP                 0x00
00138 #define PORT_AUI                0x01
00139 #define PORT_MII                0x02
00140 #define PORT_FIBRE              0x03
00141 #define PORT_BNC                0x04
00142 
00143 /* Which tranceiver to use. */
00144 #define XCVR_INTERNAL           0x00
00145 #define XCVR_EXTERNAL           0x01
00146 #define XCVR_DUMMY1             0x02
00147 #define XCVR_DUMMY2             0x03
00148 #define XCVR_DUMMY3             0x04
00149 
00150 #define AUTONEG_DISABLE         0x00
00151 #define AUTONEG_ENABLE          0x01
00152 
00153 bool load_interfaces(vector < string > &interfaces)
00154 {
00155   vector < string > procnetdev;
00156 
00157   interfaces.clear();
00158   if (!loadfile("/proc/net/dev", procnetdev))
00159     return false;
00160 
00161   if (procnetdev.size() <= 2)
00162     return false;
00163 
00164 // get rid of header (2 lines)
00165   procnetdev.erase(procnetdev.begin());
00166   procnetdev.erase(procnetdev.begin());
00167 
00168   for (unsigned int i = 0; i < procnetdev.size(); i++)
00169   {
00170 // extract interfaces names
00171     size_t pos = procnetdev[i].find(':');
00172 
00173     if (pos != string::npos)
00174       interfaces.push_back(hw::strip(procnetdev[i].substr(0, pos)));
00175   }
00176 
00177   return true;
00178 }
00179 
00180 
00181 static string getmac(const unsigned char *mac)
00182 {
00183   char buff[5];
00184   string result = "";
00185   bool valid = false;
00186 
00187   for (int i = 0; i < 6; i++)
00188   {
00189     snprintf(buff, sizeof(buff), "%02x", mac[i]);
00190 
00191     valid |= (mac[i] != 0);
00192 
00193     if (i == 0)
00194       result = string(buff);
00195     else
00196       result += ":" + string(buff);
00197   }
00198 
00199   if (valid)
00200     return result;
00201   else
00202     return "";
00203 }
00204 
00205 
00206 static const char *hwname(int t)
00207 {
00208   switch (t)
00209   {
00210     case ARPHRD_ETHER:
00211       return _("Ethernet");
00212     case ARPHRD_SLIP:
00213       return _("SLIP");
00214     case ARPHRD_LOOPBACK:
00215       return _("loopback");
00216     case ARPHRD_FDDI:
00217       return _("FDDI");
00218     case ARPHRD_IEEE1394:
00219       return _("IEEE1394");
00220     case ARPHRD_IRDA:
00221       return _("IRDA");
00222     case ARPHRD_PPP:
00223       return _("PPP");
00224     case ARPHRD_X25:
00225       return _("X25");
00226     case ARPHRD_TUNNEL:
00227       return _("IPtunnel");
00228     case ARPHRD_DLCI:
00229       return _("Framerelay.DLCI");
00230     case ARPHRD_FRAD:
00231       return _("Framerelay.AD");
00232     case ARPHRD_TUNNEL6:
00233       return _("IP6tunnel");
00234     case ARPHRD_SIT:
00235       return _("IP6inIP4");
00236     default:
00237       return "";
00238   }
00239 }
00240 
00241 
00242 static string print_ip(struct sockaddr_in *in)
00243 {
00244   return string(inet_ntoa(in->sin_addr));
00245 }
00246 
00247 
00248 static void scan_ip(hwNode & interface)
00249 {
00250   int fd = socket(AF_INET, SOCK_DGRAM, 0);
00251 
00252   if (fd > 0)
00253   {
00254     struct ifreq ifr;
00255 
00256 // get IP address
00257     memset(&ifr, 0, sizeof(ifr));
00258     strcpy(ifr.ifr_name, interface.getLogicalName().c_str());
00259     ifr.ifr_addr.sa_family = AF_INET;
00260     if (ioctl(fd, SIOCGIFADDR, &ifr) == 0)
00261     {
00262 // IP address is in ifr.ifr_addr
00263       interface.setConfig("ip", ::enabled("output:sanitize")?REMOVED:print_ip((sockaddr_in *) (&ifr.ifr_addr)));
00264       strcpy(ifr.ifr_name, interface.getLogicalName().c_str());
00265       if ((interface.getConfig("point-to-point") == "yes")
00266         && (ioctl(fd, SIOCGIFDSTADDR, &ifr) == 0))
00267       {
00268 // remote PPP address is in ifr.ifr_dstaddr
00269         interface.setConfig("remoteip",
00270           print_ip((sockaddr_in *) & ifr.ifr_dstaddr));
00271       }
00272     }
00273 
00274     close(fd);
00275   }
00276 }
00277 
00278 
00279 static bool isVirtual(const string & MAC)
00280 {
00281   if (MAC.length() < 8)
00282     return false;
00283 
00284   string manufacturer = uppercase(MAC.substr(0, 8));
00285 
00286   if ((manufacturer == "00:05:69") ||
00287     (manufacturer == "00:0C:29") || (manufacturer == "00:50:56"))
00288     return true;     // VMware
00289   if (manufacturer == "00:1C:42")
00290     return true;     // Parallels
00291   if (manufacturer == "0A:00:27")
00292     return true;     // VirtualBox
00293 
00294   return false;
00295 }
00296 
00297 
00298 bool scan_network(hwNode & n)
00299 {
00300   vector < string > interfaces;
00301   char buffer[2 * IFNAMSIZ + 1];
00302 
00303   if (!load_interfaces(interfaces))
00304     return false;
00305 
00306   int fd = socket(PF_INET, SOCK_DGRAM, 0);
00307 
00308   if (fd >= 0)
00309   {
00310     struct ifreq ifr;
00311     struct ethtool_drvinfo drvinfo;
00312     struct ethtool_cmd ecmd;
00313     struct ethtool_value edata;
00314 
00315     for (unsigned int i = 0; i < interfaces.size(); i++)
00316     {
00317       hwNode interface("network",
00318         hw::network);
00319 
00320       interface.setLogicalName(interfaces[i]);
00321       interface.claim();
00322       interface.addHint("icon", string("network"));
00323 
00324       string businfo = sysfs_getbusinfo(sysfs::entry::byClass("net", interface.getLogicalName()));
00325       interface.setBusInfo(businfo);
00326 
00327 //scan_mii(fd, interface);
00328       scan_ip(interface);
00329 
00330       memset(&ifr, 0, sizeof(ifr));
00331       strcpy(ifr.ifr_name, interfaces[i].c_str());
00332       if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0)
00333       {
00334 #ifdef IFF_PORTSEL
00335         if (ifr.ifr_flags & IFF_PORTSEL)
00336         {
00337           if (ifr.ifr_flags & IFF_AUTOMEDIA)
00338             interface.setConfig("automedia", "yes");
00339         }
00340 #endif
00341 
00342         if (ifr.ifr_flags & IFF_UP)
00343           interface.enable();
00344         else
00345           interface.disable();
00346         if (ifr.ifr_flags & IFF_BROADCAST)
00347           interface.setConfig("broadcast", "yes");
00348         if (ifr.ifr_flags & IFF_DEBUG)
00349           interface.setConfig("debug", "yes");
00350         if (ifr.ifr_flags & IFF_LOOPBACK)
00351           interface.setConfig("loopback", "yes");
00352         if (ifr.ifr_flags & IFF_POINTOPOINT)
00353           interface.setConfig("point-to-point", "yes");
00354         if (ifr.ifr_flags & IFF_PROMISC)
00355           interface.setConfig("promiscuous", "yes");
00356         if (ifr.ifr_flags & IFF_SLAVE)
00357           interface.setConfig("slave", "yes");
00358         if (ifr.ifr_flags & IFF_MASTER)
00359           interface.setConfig("master", "yes");
00360         if (ifr.ifr_flags & IFF_MULTICAST)
00361           interface.setConfig("multicast", "yes");
00362       }
00363 
00364       memset(&ifr, 0, sizeof(ifr));
00365       strcpy(ifr.ifr_name, interfaces[i].c_str());
00366 // get MAC address
00367       if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0)
00368       {
00369         string hwaddr = getmac((unsigned char *) ifr.ifr_hwaddr.sa_data);
00370         interface.addCapability(hwname(ifr.ifr_hwaddr.sa_family));
00371         if (ifr.ifr_hwaddr.sa_family >= 256)
00372           interface.addCapability("logical", _("Logical interface"));
00373         else
00374           interface.addCapability("physical", _("Physical interface"));
00375         interface.setDescription(string(hwname(ifr.ifr_hwaddr.sa_family)) +
00376           " interface");
00377         interface.setSerial(hwaddr);
00378 
00379         if (isVirtual(interface.getSerial()))
00380           interface.addCapability("logical", _("Logical interface"));
00381       }
00382 
00383 // check for wireless extensions
00384       memset(buffer, 0, sizeof(buffer));
00385       strncpy(buffer, interfaces[i].c_str(), sizeof(buffer));
00386       if (ioctl(fd, SIOCGIWNAME, &buffer) == 0)
00387       {
00388         interface.addCapability("wireless", _("Wireless-LAN"));
00389         interface.setConfig("wireless", hw::strip(buffer + IFNAMSIZ));
00390         interface.setDescription(_("Wireless interface"));
00391         interface.addHint("icon", string("wifi"));
00392         interface.addHint("bus.icon", string("radio"));
00393       }
00394 
00395       edata.cmd = ETHTOOL_GLINK;
00396       memset(&ifr, 0, sizeof(ifr));
00397       strcpy(ifr.ifr_name, interfaces[i].c_str());
00398       ifr.ifr_data = (caddr_t) &edata;
00399       if (ioctl(fd, SIOCETHTOOL, &ifr) == 0)
00400       {
00401         interface.setConfig("link", edata.data ? "yes":"no");
00402       }
00403 
00404       ecmd.cmd = ETHTOOL_GSET;
00405       memset(&ifr, 0, sizeof(ifr));
00406       strcpy(ifr.ifr_name, interfaces[i].c_str());
00407       ifr.ifr_data = (caddr_t) &ecmd;
00408       if (ioctl(fd, SIOCETHTOOL, &ifr) == 0)
00409       {
00410         if(ecmd.supported & SUPPORTED_TP)
00411           interface.addCapability("tp", _("twisted pair"));
00412         if(ecmd.supported & SUPPORTED_AUI)
00413           interface.addCapability("aui", _("AUI"));
00414         if(ecmd.supported & SUPPORTED_BNC)
00415           interface.addCapability("bnc", _("BNC"));
00416         if(ecmd.supported & SUPPORTED_MII)
00417           interface.addCapability("mii", _("Media Independant Interface"));
00418         if(ecmd.supported & SUPPORTED_FIBRE)
00419           interface.addCapability("fibre",_( "optical fibre"));
00420         if(ecmd.supported & SUPPORTED_10baseT_Half)
00421         {
00422           interface.addCapability("10bt", _("10Mbit/s"));
00423           interface.setCapacity(10000000L);
00424         }
00425         if(ecmd.supported & SUPPORTED_10baseT_Full)
00426         {
00427           interface.addCapability("10bt-fd", _("10Mbit/s (full duplex)"));
00428           interface.setCapacity(10000000L);
00429         }
00430         if(ecmd.supported & SUPPORTED_100baseT_Half)
00431         {
00432           interface.addCapability("100bt", _("100Mbit/s"));
00433           interface.setCapacity(100000000L);
00434         }
00435         if(ecmd.supported & SUPPORTED_100baseT_Full)
00436         {
00437           interface.addCapability("100bt-fd", _("100Mbit/s (full duplex)"));
00438           interface.setCapacity(100000000L);
00439         }
00440         if(ecmd.supported & SUPPORTED_1000baseT_Half)
00441         {
00442           interface.addCapability("1000bt", "1Gbit/s");
00443           interface.setCapacity(1000000000L);
00444         }
00445         if(ecmd.supported & SUPPORTED_1000baseT_Full)
00446         {
00447           interface.addCapability("1000bt-fd", _("1Gbit/s (full duplex)"));
00448           interface.setCapacity(1000000000L);
00449         }
00450         if(ecmd.supported & SUPPORTED_Autoneg)
00451           interface.addCapability("autonegotiation", _("Auto-negotiation"));
00452 
00453         switch(ecmd.speed)
00454         {
00455           case SPEED_10:
00456             interface.setConfig("speed", "10Mbit/s");
00457             interface.setSize(10000000L);
00458             break;
00459           case SPEED_100:
00460             interface.setConfig("speed", "100Mbit/s");
00461             interface.setSize(100000000L);
00462             break;
00463           case SPEED_1000:
00464             interface.setConfig("speed", "1Gbit/s");
00465             interface.setSize(1000000000L);
00466             break;
00467         }
00468         switch(ecmd.duplex)
00469         {
00470           case DUPLEX_HALF:
00471             interface.setConfig("duplex", "half");
00472             break;
00473           case DUPLEX_FULL:
00474             interface.setConfig("duplex", "full");
00475             break;
00476         }
00477         switch(ecmd.port)
00478         {
00479           case PORT_TP:
00480             interface.setConfig("port", "twisted pair");
00481             break;
00482           case PORT_AUI:
00483             interface.setConfig("port", "AUI");
00484             break;
00485           case PORT_BNC:
00486             interface.setConfig("port", "BNC");
00487             break;
00488           case PORT_MII:
00489             interface.setConfig("port", "MII");
00490             break;
00491           case PORT_FIBRE:
00492             interface.setConfig("port", "fibre");
00493             break;
00494         }
00495         interface.setConfig("autonegotiation", (ecmd.autoneg == AUTONEG_DISABLE) ?  "off" : "on");
00496       }
00497 
00498       drvinfo.cmd = ETHTOOL_GDRVINFO;
00499       memset(&ifr, 0, sizeof(ifr));
00500       strcpy(ifr.ifr_name, interfaces[i].c_str());
00501       ifr.ifr_data = (caddr_t) & drvinfo;
00502       if (ioctl(fd, SIOCETHTOOL, &ifr) == 0)
00503       {
00504         interface.setConfig("driver", drvinfo.driver);
00505         interface.setConfig("driverversion", drvinfo.version);
00506         interface.setConfig("firmware", drvinfo.fw_version);
00507         if (interface.getBusInfo() == "")
00508           interface.setBusInfo(guessBusInfo(drvinfo.bus_info));
00509       }
00510 
00511       if(sysfs::entry::byClass("net", interface.getLogicalName()).hassubdir("bridge"))
00512         interface.addCapability("logical", _("Logical interface"));
00513 
00514       if (hwNode * existing = n.findChildByBusInfo(interface.getBusInfo()))
00515       {
00516         existing->merge(interface);
00517         if(interface.getDescription()!="")
00518           existing->setDescription(interface.getDescription());
00519       }
00520       else
00521       {
00522         existing = n.findChildByLogicalName(interface.getLogicalName());
00523         if (existing)
00524         {
00525           existing->merge(interface);
00526         }
00527         else
00528         {
00529 // we don't care about loopback and "logical" interfaces
00530           if (!interface.isCapable("loopback") &&
00531             !interface.isCapable("logical"))
00532             n.addChild(interface);
00533         }
00534       }
00535     }
00536 
00537     close(fd);
00538     return true;
00539   }
00540   else
00541     return false;
00542 }