在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地址。