在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
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
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
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
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
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;
4 char * ifa_name;
5 unsigned int ifa_flags;
6 struct sockaddr * ifa_addr;
7 struct sockaddr * ifa_netmask;
8 union
9 {
10 struct sockaddr * ifu_broadaddr;
11 struct sockaddr * ifu_dstaddr;
12 } ifa_ifu;
13 #define ifa_broadaddr ifa_ifu. ifu_broadaddr
14 #define ifa_dstaddr ifa_ifu. ifu_dstaddr
15 void * ifa_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地址。
struct ifreq
{
# define IFHWADDRLEN 6
# define IFNAMSIZ IF_NAMESIZE
union
{
char ifrn_name[ IFNAMSIZ] ;
} ifr_ifrn;
union
{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short int ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[ IFNAMSIZ] ;
char ifru_newname[ IFNAMSIZ] ;
__caddr_t ifru_data;
} ifr_ifru;
} ;
以上为ifreq的结构体定义