linux下c获取网卡信息

  • Post author:
  • Post category:linux


在Linux下开发网络程序时,经常会遇到需要取本地网络接口名、IP、广播地址、子网掩码或者MAC地址等信息的需求,最常见的办法是配合宏SIOCGIFHWADDR、SIOCGIFADDR、SIOCGIFBRDADDR与SIOCGIFNETMASK作为参数调用函数ioctl分别获得MAC地址、IP地址、广播地址与子网掩码来实现。一次性获取此类信息的C语言代码实现如下。

复制代码

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <net/if.h>
  4 #include <sys/ioctl.h>
  5 #include <arpa/inet.h>
  6 #include <errno.h>
  7 
  8 int getLocalInfo(void)
  9 {
 10     int fd;
 11     int interfaceNum = 0;
 12     struct ifreq buf[16];
 13     struct ifconf ifc;
 14     struct ifreq ifrcopy;
 15     char mac[16] = {0};
 16     char ip[32] = {0};
 17     char broadAddr[32] = {0};
 18     char subnetMask[32] = {0};
 19 
 20     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
 21     {
 22         perror("socket");
 23 
 24         close(fd);
 25         return -1;
 26     }
 27 
 28     ifc.ifc_len = sizeof(buf);
 29     ifc.ifc_buf = (caddr_t)buf;
 30     if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
 31     {
 32         interfaceNum = ifc.ifc_len / sizeof(struct ifreq);
 33         printf("interface num = %dn", interfaceNum);
 34         while (interfaceNum-- > 0)
 35         {
 36             printf("ndevice name: %sn", buf[interfaceNum].ifr_name);
 37 
 38             //ignore the interface that not up or not runing  
 39             ifrcopy = buf[interfaceNum];
 40             if (ioctl(fd, SIOCGIFFLAGS, &ifrcopy))
 41             {
 42                 printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
 43 
 44                 close(fd);
 45                 return -1;
 46             }
 47 
 48             //get the mac of this interface  
 49             if (!ioctl(fd, SIOCGIFHWADDR, (char *)(&buf[interfaceNum])))
 50             {
 51                 memset(mac, 0, sizeof(mac));
 52                 snprintf(mac, sizeof(mac), "%02x%02x%02x%02x%02x%02x",
 53                     (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[0],
 54                     (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[1],
 55                     (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[2],
 56 
 57                     (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[3],
 58                     (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[4],
 59                     (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[5]);
 60                 printf("device mac: %sn", mac);
 61             }
 62             else
 63             {
 64                 printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
 65                 close(fd);
 66                 return -1;
 67             }
 68 
 69             //get the IP of this interface  
 70 
 71             if (!ioctl(fd, SIOCGIFADDR, (char *)&buf[interfaceNum]))
 72             {
 73                 snprintf(ip, sizeof(ip), "%s",
 74                     (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_addr))->sin_addr));
 75                 printf("device ip: %sn", ip);
 76             }
 77             else
 78             {
 79                 printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
 80                 close(fd);
 81                 return -1;
 82             }
 83 
 84             //get the broad address of this interface  
 85 
 86             if (!ioctl(fd, SIOCGIFBRDADDR, &buf[interfaceNum]))
 87             {
 88                 snprintf(broadAddr, sizeof(broadAddr), "%s",
 89                     (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_broadaddr))->sin_addr));
 90                 printf("device broadAddr: %sn", broadAddr);
 91             }
 92             else
 93             {
 94                 printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
 95                 close(fd);
 96                 return -1;
 97             }
 98  99 
100             //get the subnet mask of this interface  
101             if (!ioctl(fd, SIOCGIFNETMASK, &buf[interfaceNum]))
102             {
103                 snprintf(subnetMask, sizeof(subnetMask), "%s",
104                     (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_netmask))->sin_addr));
105                 printf("device subnetMask: %sn", subnetMask);
106             }
107             else
108             {
109                 printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
110                 close(fd);
111                 return -1;
112 
113             }
114         }
115     }
116     else
117     {
118         printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
119         close(fd);
120         return -1;
121     }
122   
123     close(fd);
124 
125     return 0;
126 }
127   
128 int main(void)
129 {
130     getLocalInfo();
131 
132     return 0;
133 }

复制代码

使用ioctl函数虽然可以获取所有的信息,但是使用起来比较麻烦,如果不需要获取MAC地址,那么使用getifaddrs函数来获取更加方便与简洁。值得一提的是,在MacOS或iOS系统上(如iPhone程序开发),上述iotcl函数没法获得mac地址跟子网掩码,这个使用,使用getifaddrs函数便更有优势了。下面是使用getiaddrs函数获取网卡信息的C语言代码实现。

复制代码

 1 #include <stdio.h>  
 2 #include <ifaddrs.h>  
 3 #include <arpa/inet.h>  
 4   
 5 int getSubnetMask()
 6 {
 7     struct sockaddr_in *sin = NULL;
 8     struct ifaddrs *ifa = NULL, *ifList;
 9 
10     if (getifaddrs(&ifList) < 0)
11     {
12         return -1;
13     }
14 
15     for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
16     {
17         if(ifa->ifa_addr->sa_family == AF_INET)
18         {
19             printf("n>>> interfaceName: %sn", ifa->ifa_name);
20 
21             sin = (struct sockaddr_in *)ifa->ifa_addr;
22             printf(">>> ipAddress: %sn", inet_ntoa(sin->sin_addr));
23 
24             sin = (struct sockaddr_in *)ifa->ifa_dstaddr;
25             printf(">>> broadcast: %sn", inet_ntoa(sin->sin_addr));
26 
27             sin = (struct sockaddr_in *)ifa->ifa_netmask;
28             printf(">>> subnetMask: %sn", inet_ntoa(sin->sin_addr));
29         }
30     }
31 
32     freeifaddrs(ifList);
33 
34     return 0;
35 }
36   
37 int main(void)
38 {
39     getSubnetMask();
40 
41     return 0;
42 }

复制代码

ifaddrs结构体定义如下:

复制代码

 1 struct ifaddrs   
 2 {   
 3     struct ifaddrs  *ifa_next;    /* Next item in list */   
 4     char            *ifa_name;    /* Name of interface */   
 5     unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */   
 6     struct sockaddr *ifa_addr;    /* Address of interface */   
 7     struct sockaddr *ifa_netmask; /* Netmask of interface */   
 8     union   
 9     {   
10         struct sockaddr *ifu_broadaddr; /* Broadcast address of interface */   
11         struct sockaddr *ifu_dstaddr; /* Point-to-point destination address */   
12     } ifa_ifu;   
13     #define              ifa_broadaddr ifa_ifu.ifu_broadaddr   
14     #define              ifa_dstaddr   ifa_ifu.ifu_dstaddr   
15     void            *ifa_data;    /* Address-specific data */   
16 };   

复制代码

ifa_next指向链表的下一个成员;ifa_name是接口名称,以0结尾的字符串,比如eth0,lo;ifa_flags是接口的标识位(比如当IFF_BROADCAST或IFF_POINTOPOINT设置到此标识位时,影响联合体变量ifu_broadaddr存储广播地址或ifu_dstaddr记录点对点地址);ifa_netmask存储该接口的子网掩码;结构体变量存储广播地址或点对点地址(见括弧介绍ifa_flags);ifa_data存储了该接口协议族的特殊信息,它通常是NULL(一般不关注他)。

函数getifaddrs(int getifaddrs (struct ifaddrs **__ifap))获取本地网络接口信息,将之存储于链表中,链表头结点指针存储于__ifap中带回,函数执行成功返回0,失败返回-1,且为errno赋值。

很显然,函数getifaddrs用于获取本机接口信息,比如最典型的获取本机IP地址。