获取本机所有接口和所有IP地址的函数

获取本机所有接口和所有IP地址的函数

最近一直在忙一个程序,为了保护自己机器上所有IP,便要获取它们。
虽然IPv6中根本没有ARP,但是我编写代码的原则一直希望能够独力于协议版本,所以不管怎么说一定要能获取IPv6地址了。
要说只要IPv4,就没必要写这篇文章了。
我首先考虑的就是使用NETLINK访问,可我不想现在用,因为我想过几天程序写完了,把哪些陈旧的ioctl彻底用NETLINK再重新写一遍的。
想起书上(UNPv3)说BSD中有getifaddrs函数,我就man 了一下,结果我的机器上也有,(LINUX啦)。看了手册页和ifaddrs.h
文件又迷惑了,这样的函数能支持IPv6么,因为struct ifaddrs结构里的成员只有struct sockaddr的指针,装个V4还可以,V6地址
行么?
往上搜了一下,结果在外国的一个邮件列表中照到了一个煎蛋(简单)代码。发现直接可以把那个struct sockaddr *ifa_addr成员根据
它之中的sa_family成员的值直接转换成相应的类型。感觉好多了。获取是没问题了,不果感觉返回来那么多地址,可lo接口地址对我根本
没用啊,想象就是在实际中的代码也很少去关心还回接口啊,变想办法过滤下。再看那个struct ifaddrs,明显是链表节点的类型(太自以为是了)
便自作聪明的以为是个真正的链表,只要把它给修正下就可以了。
于是忙活了半天,写了个小函数,这是片段:(写的有点变态,也没保证)
while (ifa) {
tmp = ifa;
tmp->ifa_next = NULL;
if (ifa->ifa_addr->sa_family != family){
if (family != AF_UNSPEC && (ifa->ifa_addr == NULL ||((ifa->ifa_flags & IFF_UP) == 0) || !strcmp(ifa->ifa_name, "lo"))) {
freeifaddrs(tmp);
if (flag) {
ifaddrs = ifa->ifa_next; /** never get here twice **/
flag =0;
}
}
ifa = ifa->ifa_next;
}
}
用地址类型做为参数,获取机器上指定类型的所有除还回地址以外所有活动的接口IP,
然后发现总是出错。因为第一个返回的是V4的还回地址,我要给它释放掉。可运行时总是提示:
lo: 127.0.0.1
eth0: 10.0.119.163
lo: ::1
eth0: fe80::203:dff:fe2f:9149
*** glibc detected *** d: free(): invalid pointer: 0x0804a4d4 ***
======= Backtrace: =========
/lib/libc.so.6[0xb7eda911]
/lib/libc.so.6(__libc_free+0x84)[0xb7edbf84]
/lib/libc.so.6(freeifaddrs+0x1d)[0xb7f512dd]
d[0x8048989]
d[0x80486a5]
/lib/libc.so.6(__libc_start_main+0xdc)[0xb7e8c87c]
d[0x8048491]
======= Memory map: ========
08048000-08049000 r-xp 00000000 03:07 48637 /home/souldump/bin/d
08049000-0804a000 rw-p 00000000 03:07 48637 /home/souldump/bin/d
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
b7d00000-b7d21000 rw-p b7d00000 00:00 0
b7d21000-b7e00000 ---p b7d21000 00:00 0
b7e76000-b7e77000 rw-p b7e76000 00:00 0
b7e77000-b7f90000 r-xp 00000000 03:05 16184 /lib/libc-2.4.so
b7f90000-b7f92000 r--p 00118000 03:05 16184 /lib/libc-2.4.so
b7f92000-b7f94000 rw-p 0011a000 03:05 16184 /lib/libc-2.4.so
b7f94000-b7f98000 rw-p b7f94000 00:00 0
b7fab000-b7fb5000 r-xp 00000000 03:05 20108 /lib/libgcc_s.so.1
b7fb5000-b7fb6000 rw-p 00009000 03:05 20108 /lib/libgcc_s.so.1
b7fb6000-b7fb7000 rw-p b7fb6000 00:00 0
b7fb7000-b7fd1000 r-xp 00000000 03:05 16177 /lib/ld-2.4.so
b7fd1000-b7fd3000 rw-p 00019000 03:05 16177 /lib/ld-2.4.so
bfb2b000-bfb41000 rw-p bfb2b000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]
已放弃

坏了,这说明不是真正的链表,指针非法。
于是激起了我翻看GLIBC代码的想法,我直到代码里有所有问题的答案。
赶紧翻开以前装LFS时用的glbc-2.3.6源代码。

搜索了下在glibc-2.3.6/glibc-2.3.6/sysdeps/unix/sysv/linux/ifaddrs.c
里找到了getifaddrs的实现,第一眼就郁闷了,还是用NETLINK实现的。早直到我就不费这劲,自己写了。
他定义了这样一个结构,保证容纳每个接口的空间。这就是为什么能转换IPv6的原因了。
struct ifaddrs_storage
{
struct ifaddrs ifa;
union
{
/* Save space for the biggest of the four used sockaddr types and
avoid a lot of casts. */
struct sockaddr sa;
struct sockaddr_ll sl;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
} addr, netmask, broadaddr;
char name[IF_NAMESIZE + 1];
};
大概流程是这样:如果支持NETLINK,发送请求,不然调用fallback_getifaddrs(以前的getifaddrs实现)
函数实现不过只支持IPv4(一个一个接口用ioctl了)。(记得有个邮件列表里有人问过这个函数什么从版本的glibc
开始支持IPv6,不过那个日本人好象没说,只说看下一版本的了。),然后第一遍快速遍历处理返回的数据,
确定接口和地址数量,决定分配空间。然后一次再次遍历数据,初始化每个struct ifaddrs结构的ifa_next
指针(用map_newlink),根据每个rtattr结构类型把对应的项的值memcpy过去。
不子细说了,有兴趣的自己去看,其实只要看freeifaddrs函数就够了,只有free(ifa);一句。
终于弄明白了,既然还是用NETLINK,这次还不好办,有了上次操作路由表的经验和glibc的代码,于是把那个
库函数给改了,封装成一个新函数,留着以后用,用参数AF_INET, AF_INET6, AF_UNSPEC,


这是代码:

Shell代码
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include <stdbool.h>  
  4. #include<string.h>  
  5. #include<unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<net/if.h>  
  8. #include<netinet/in.h>  
  9. #include<sys/types.h>  
  10. #include<linux/netlink.h>  
  11. #include<linux/rtnetlink.h>  
  12. #include <assert.h>  
  13. #include<errno.h>  
  14. #include<ifaddrs.h>  
  15. #include<netpacket/packet.h>  
  16.   
  17. void print_ip(struct ifaddrs *ifaddrs);  
  18.   
  19. /** NOTE! caller must call freeifaddrs() after the use the pointer **/  
  20. int get_local_ip(struct ifaddrs **ifap, int family);  
  21.   
  22. #define IS_UNSPEC(family) (family == AF_UNSPEC)  
  23. #define NO_ADDRS(ifa) (ifa->ifa_addr == NULL)  
  24. #define FAMILY_OK(ifa) (ifa->ifa_addr->sa_family == AF_INET ||\  
  25.                                 ifa->ifa_addr->sa_family == AF_INET6)  
  26. #define IN_FAMILY(ifa, family) (ifa->ifa_addr->sa_family == family)  
  27. #define TEST_FLAG(ifa, flag) (ifa->ifa_flags | flag)  
  28. #define IS_LOOPBACK(ifa) (strncmp(ifa->ifa_name,"lo"2) == 0)  
  29. #define move_ptr(ifa, tmp) do{  ifa->ifa_next = tmp->ifa_next;\  
  30.                                 ifa->ifa_name = tmp->ifa_name;\  
  31.                                 ifa->ifa_flags = tmp->ifa_flags;\  
  32.                                 ifa->ifa_addr = tmp->ifa_addr;\  
  33.                                 ifa->ifa_netmask = tmp->ifa_netmask;\  
  34.                                 if (TEST_FLAG(ifa, IFF_BROADCAST)) \  
  35.                                         ifa->ifa_broadaddr = tmp->ifa_broadaddr;\  
  36.                                 else if (TEST_FLAG(ifa, IFF_POINTOPOINT))\  
  37.                                         ifa->ifa_dstaddr = tmp->ifa_dstaddr;\  
  38.                                 } while (0)  
  39.   
  40. int main ()  
  41. {  
  42.         struct ifaddrs *ifa, *ifaddrs;  
  43.         struct ifaddrs *ifb, *ifc;  
  44.   
  45.   
  46.         printf("AF_INET\n");  
  47.         get_local_ip(&ifa, AF_INET);  
  48.         print_ip(ifa);  
  49.         printf("AF_INET6\n");  
  50.         get_local_ip(&ifb, AF_INET6);  
  51.         print_ip(ifb);  
  52.         printf("AF_UNSPEC\n");  
  53.         get_local_ip(&ifc, AF_UNSPEC);  
  54.         print_ip(ifc);  
  55.   
  56.   
  57. }  
  58.   
  59. void  
  60. print_ip(struct ifaddrs *ifaddrs)  
  61. {  
  62.         struct ifaddrs *ifa;  
  63.         struct sockaddr_in *sin;  
  64.         struct sockaddr_in6 *sin6;  
  65.         char buf[INET6_ADDRSTRLEN];  
  66.   
  67.   
  68.         for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next)  
  69.         {  
  70.                 if (ifa->ifa_addr == NULL) continue;  
  71.                 if ((ifa->ifa_flags & IFF_UP) == 0) continue;  
  72.          
  73.                 if (ifa->ifa_addr->sa_family == AF_INET)  
  74.                 {  
  75.                         sin = (struct sockaddr_in *)(ifa->ifa_addr);  
  76.                         if (inet_ntop(ifa->ifa_addr->sa_family, (void *)&(sin->sin_addr), buf, sizeof(buf)) == NULL)  
  77.                         {  
  78.                                 printf("%s: inet_ntop failed!\n", ifa->ifa_name);  
  79.                         }  
  80.                         else  
  81.                         {  
  82.                         printf("%s: %s\n", ifa->ifa_name, buf);  
  83.                         }  
  84.                 }  
  85.                 else if (ifa->ifa_addr->sa_family == AF_INET6)  
  86.                 {  
  87.                         sin6 = (struct sockaddr_in6 *)(ifa->ifa_addr);  
  88.                         if (inet_ntop(ifa->ifa_addr->sa_family, (void *)&(sin6->sin6_addr), buf, sizeof(buf)) == NULL)  
  89.                         {  
  90.                                 printf("%s: inet_ntop failed!\n", ifa->ifa_name);  
  91.                         }  
  92.                         else  
  93.                         {  
  94.                                 printf("%s: %s\n", ifa->ifa_name, buf);  
  95.                         }  
  96.                 }  
  97.         }  
  98. }  
  99.   
  100. int get_local_ip(struct ifaddrs **ifap, int family)  
  101. {  
  102.         int n;  
  103.         bool change = 0;  
  104.         char *name;  
  105.         struct ifaddrs *ifa;  
  106.         struct ifaddrs *tmp;  
  107.         struct sockaddr_in *sin;  
  108.         struct sockaddr_in6 *sin6;  
  109.         char buf[INET6_ADDRSTRLEN];  
  110.   
  111.         n = getifaddrs(&ifa);  
  112.         if (n != 0)  
  113.                 return -1;  
  114.          
  115.         *ifap = ifa;  
  116.         for (ifa; (tmp = ifa) != NULL; ifa = ifa->ifa_next) {  
  117.   
  118.                 while (tmp && (IS_LOOPBACK(tmp) ||  
  119.                                  NO_ADDRS(tmp) || (!TEST_FLAG(tmp, IFF_UP))||  
  120.                                         (FAMILY_OK(tmp) && (!IN_FAMILY(tmp, family)) && (!IS_UNSPEC(family))) ||  
  121.                                                 !FAMILY_OK(tmp))) {  
  122.                                 change = true;  
  123.                                 tmp = tmp->ifa_next;  
  124.                 }  
  125.                 if (change) {  
  126.                         if (tmp)  
  127.                                 move_ptr(ifa, tmp);  
  128.                         else  
  129.                                 memset(ifa, 0, sizeof (struct ifaddrs));  
  130.                                 /** An alternative way is use these instead:  
  131.                                 ifa->ifa_next = NULL;  
  132.                                 ifa->ifa_name = NULL;  
  133.                                 ifa->ifa_addr = NULL;  
  134.                                 ifa->ifa_netmask = NULL;  
  135.                                 if (TEST_FLAG(ifa, IFF_BROADCAST))  
  136.                                         ifa->ifa_broadaddr = NULL;  
  137.                                 else if (TEST_FLAG(ifa, IFF_POINTOPOINT))  
  138.                                         ifa->ifa_dstaddr = NULL;  
  139.                                 **/  
  140.   
  141.                         change = false;  
  142.                 }  
  143.          
  144.         }  
  145.   
  146.         /** I think even *ifap now is NULL after check, we should not free the space either.  
  147.             Since user who called this routine will call freeifaddrs() too, The space will be free safely . **/  
  148.         return 0;  

摘自:http://www.linuxsir.org/bbs/thread284220.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值