在Linux上使用C语言编程获取IPv4地址及子网掩码
在Linux上(如Ubuntu或CentOS), 获取某个Network Interface比如eth0的IP地址等信息,我们可以使用ifconfig或者ip addr show命令。
$ ifconfig eth0 eth0 Link encap:Ethernet HWaddr 00:25:64:ba:8d:be inet addr:192.168.1.102 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::225:64ff:feba:8dbe/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:31242 errors:0 dropped:0 overruns:0 frame:0 TX packets:29415 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:29102650 (29.1 MB) TX bytes:4090669 (4.0 MB) Interrupt:16 $ ip addr show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:25:64:ba:8d:be brd ff:ff:ff:ff:ff:ff inet 192.168.1.102/24 brd 192.168.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::225:64ff:feba:8dbe/64 scope link valid_lft forever preferred_lft forever
那么用C语言编程怎么实现呢? 先用strace工具观察一下ifconfig eth0的运行内幕。
$ strace ifconfig eth0 execve("/sbin/ifconfig", ["ifconfig", "eth0"], [/* 71 vars */]) = 0 brk(0) = 0x9565000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fe000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=114965, ...}) = 0 mmap2(NULL, 114965, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76e1000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0P\234\1\0004\0\0\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1763068, ...}) = 0 mmap2(NULL, 1772156, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7530000 mmap2(0xb76db000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1aa000) = 0xb76db000 mmap2(0xb76de000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb76de000 close(3) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb752f000 set_thread_area({entry_number:-1 -> 6, base_addr:0xb752f940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 mprotect(0xb76db000, 8192, PROT_READ) = 0 mprotect(0x8058000, 4096, PROT_READ) = 0 mprotect(0xb7724000, 4096, PROT_READ) = 0 munmap(0xb76e1000, 114965) = 0 brk(0) = 0x9565000 brk(0x9586000) = 0x9586000 open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=8752496, ...}) = 0 mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb732f000 mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x855000) = 0xb76fd000 close(3) = 0 uname({sys="Linux", node="idorax", ...}) = 0 access("/proc/net", R_OK) = 0 access("/proc/net/unix", R_OK) = 0 socket(PF_LOCAL, SOCK_DGRAM, 0) = 3 socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4 access("/proc/net/if_inet6", R_OK) = 0 socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 5 access("/proc/net/ax25", R_OK) = -1 ENOENT (No such file or directory) access("/proc/net/nr", R_OK) = -1 ENOENT (No such file or directory) access("/proc/net/rose", R_OK) = -1 ENOENT (No such file or directory) access("/proc/net/ipx", R_OK) = -1 ENOENT (No such file or directory) access("/proc/net/appletalk", R_OK) = -1 ENOENT (No such file or directory) access("/proc/sys/net/econet", R_OK) = -1 ENOENT (No such file or directory) access("/proc/sys/net/ash", R_OK) = -1 ENOENT (No such file or directory) access("/proc/net/x25", R_OK) = -1 ENOENT (No such file or directory) open("/proc/net/dev", O_RDONLY) = 6 fstat64(6, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fc000 read(6, "Inter-| Receive "..., 1024) = 447 close(6) = 0 munmap(0xb76fc000, 4096) = 0 ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0 ioctl(5, SIOCGIFHWADDR, {ifr_name="eth0", ifr_hwaddr=00:25:64:ba:8d:be}) = 0 ioctl(5, SIOCGIFMETRIC, {ifr_name="eth0", ifr_metric=0}) = 0 ioctl(5, SIOCGIFMTU, {ifr_name="eth0", ifr_mtu=1500}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=16, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=16, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFTXQLEN, {ifr_name="eth0", ifr_qlen=1000}) = 0 open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 6 fstat64(6, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fc000 read(6, "# Locale name alias data base.\n#"..., 4096) = 2570 read(6, "", 4096) = 0 close(6) = 0 munmap(0xb76fc000, 4096) = 0 open("/usr/share/locale/en_US/LC_MESSAGES/net-tools.mo", O_RDONLY) = -1 ENOENT (No such file or directory) open("/usr/share/locale/en/LC_MESSAGES/net-tools.mo", O_RDONLY) = -1 ENOENT (No such file or directory) open("/usr/share/locale-langpack/en_US/LC_MESSAGES/net-tools.mo", O_RDONLY) = -1 ENOENT (No such file or directory) open("/usr/share/locale-langpack/en/LC_MESSAGES/net-tools.mo", O_RDONLY) = -1 ENOENT (No such file or directory) ioctl(4, SIOCGIFADDR, {ifr_name="eth0", ifr_addr={AF_INET, inet_addr("192.168.1.102")}}) = 0 ioctl(4, SIOCGIFDSTADDR, {ifr_name="eth0", ifr_dstaddr={AF_INET, inet_addr("192.168.1.102")}}) = 0 ioctl(4, SIOCGIFBRDADDR, {ifr_name="eth0", ifr_broadaddr={AF_INET, inet_addr("192.168.1.255")}}) = 0 ioctl(4, SIOCGIFNETMASK, {ifr_name="eth0", ifr_netmask={AF_INET, inet_addr("255.255.255.0")}}) = 0 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fc000 write(1, "eth0 Link encap:Ethernet H"..., 58eth0 Link encap:Ethernet HWaddr 00:25:64:ba:8d:be ) = 58 write(1, " inet addr:192.168.1.10"..., 75 inet addr:192.168.1.102 Bcast:192.168.1.255 Mask:255.255.255.0 ) = 75 open("/proc/net/if_inet6", O_RDONLY) = 6 fstat64(6, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fb000 read(6, "00000000000000000000000000000001"..., 1024) = 108 read(6, "", 1024) = 0 write(1, " inet6 addr: fe80::225:"..., 61 inet6 addr: fe80::225:64ff:feba:8dbe/64 Scope:Link ) = 61 read(6, "", 1024) = 0 close(6) = 0 munmap(0xb76fb000, 4096) = 0 write(1, " UP BROADCAST RUNNING M"..., 61 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 ) = 61 write(1, " RX packets:31318 error"..., 65 RX packets:31318 errors:0 dropped:0 overruns:0 frame:0 ) = 65 write(1, " TX packets:29503 error"..., 67 TX packets:29503 errors:0 dropped:0 overruns:0 carrier:0 ) = 67 write(1, " collisions:0 txqueuele"..., 40 collisions:0 txqueuelen:1000 ) = 40 write(1, " RX bytes:29127563 (29."..., 65 RX bytes:29127563 (29.1 MB) TX bytes:4104503 (4.1 MB) ) = 65 write(1, " Interrupt:16 \n", 24 Interrupt:16 ) = 24 write(1, "\n", 1 ) = 1 close(5) = 0 exit_group(0) = ? +++ exited with 0 +++
注意上面的socket()和ioctl(), 我们不难发现如下关键调用,
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4 ioctl(4, SIOCGIFADDR, {ifr_name="eth0", ifr_addr={AF_INET, inet_addr("192.168.1.102")}}) = 0 ioctl(4, SIOCGIFNETMASK, {ifr_name="eth0", ifr_netmask={AF_INET, inet_addr("255.255.255.0")}}) = 0
好了,现在可以上C代码了,(foo_get.c)
1 /** 2 * get IPv4 address and subnet mask of a network interface 3 */ 4 #include <stdio.h> 5 #include <unistd.h> 6 #include <string.h> 7 #include <sys/socket.h> 8 #include <sys/ioctl.h> 9 #include <net/if.h> 10 #include <arpa/inet.h> 11 12 int 13 main(int argc, char *argv[]) 14 { 15 int rc = 0; 16 struct sockaddr_in *addr = NULL; 17 18 if (argc != 2) { 19 fprintf(stderr, "Usage: %s <ifname>\n", argv[0]); 20 return -1; 21 } 22 23 char *ifname = argv[1]; 24 25 struct ifreq ifr; 26 memset(&ifr, 0, sizeof(struct ifreq)); 27 28 /* 0. create a socket */ 29 int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 30 if (fd == -1) 31 return -1; 32 33 /* 1. set type of address to retrieve : IPv4 */ 34 ifr.ifr_addr.sa_family = AF_INET; 35 36 /* 2. copy interface name to ifreq structure */ 37 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); 38 39 /* 3. get the IP address */ 40 if ((rc = ioctl(fd, SIOCGIFADDR, &ifr)) != 0) 41 goto done; 42 43 char ipv4[16] = { 0 }; 44 addr = (struct sockaddr_in *)&ifr.ifr_addr; 45 strncpy(ipv4, inet_ntoa(addr->sin_addr), sizeof(ipv4)); 46 47 /* 4. get the mask */ 48 if ((rc = ioctl(fd, SIOCGIFNETMASK, &ifr)) != 0) 49 goto done; 50 51 char mask[16] = { 0 }; 52 addr = (struct sockaddr_in *)&ifr.ifr_addr; 53 strncpy(mask, inet_ntoa(addr->sin_addr), sizeof(mask)); 54 55 /* 5. display */ 56 printf("IFNAME:IPv4:MASK\n"); 57 printf("%s:%s:%s\n", ifname, ipv4, mask); 58 59 /* 6. close the socket */ 60 done: 61 close(fd); 62 63 return rc; 64 }
编译并测试,
$ gcc -g -Wall -std=gnu99 -o foo_get foo_get.c $ ./foo_get eth0 IFNAME:IPv4:MASK eth0:192.168.1.102:255.255.255.0 $ ./foo_get lo IFNAME:IPv4:MASK lo:127.0.0.1:255.0.0.0
参考资料:
- NETDEVICE(7)
- INET(3)
- https://stackoverflow.com/questions/6652384/how-to-set-the-ip-address-from-c-in-linux