LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3996|回复: 5

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

[复制链接]
发表于 2006-12-3 13:58:42 | 显示全部楼层 |阅读模式
最近一直在忙一个程序,为了保护自己机器上所有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,


这是代码:
  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. void print_ip(struct ifaddrs *ifaddrs);
  17. /** NOTE! caller must call freeifaddrs() after the use the pointer **/
  18. int get_local_ip(struct ifaddrs **ifap, int family);
  19. #define IS_UNSPEC(family) (family == AF_UNSPEC)
  20. #define NO_ADDRS(ifa) (ifa->ifa_addr == NULL)
  21. #define FAMILY_OK(ifa) (ifa->ifa_addr->sa_family == AF_INET ||\
  22.                                 ifa->ifa_addr->sa_family == AF_INET6)
  23. #define IN_FAMILY(ifa, family) (ifa->ifa_addr->sa_family == family)
  24. #define TEST_FLAG(ifa, flag) (ifa->ifa_flags | flag)
  25. #define IS_LOOPBACK(ifa) (strncmp(ifa->ifa_name,"lo", 2) == 0)
  26. #define move_ptr(ifa, tmp) do{  ifa->ifa_next = tmp->ifa_next;\
  27.                                 ifa->ifa_name = tmp->ifa_name;\
  28.                                 ifa->ifa_flags = tmp->ifa_flags;\
  29.                                 ifa->ifa_addr = tmp->ifa_addr;\
  30.                                 ifa->ifa_netmask = tmp->ifa_netmask;\
  31.                                 if (TEST_FLAG(ifa, IFF_BROADCAST)) \
  32.                                         ifa->ifa_broadaddr = tmp->ifa_broadaddr;\
  33.                                 else if (TEST_FLAG(ifa, IFF_POINTOPOINT))\
  34.                                         ifa->ifa_dstaddr = tmp->ifa_dstaddr;\
  35.                                 } while (0)
  36. int main ()
  37. {
  38.         struct ifaddrs *ifa, *ifaddrs;
  39.         struct ifaddrs *ifb, *ifc;
  40.         printf("AF_INET\n");
  41.         get_local_ip(&ifa, AF_INET);
  42.         print_ip(ifa);
  43.         printf("AF_INET6\n");
  44.         get_local_ip(&ifb, AF_INET6);
  45.         print_ip(ifb);
  46.         printf("AF_UNSPEC\n");
  47.         get_local_ip(&ifc, AF_UNSPEC);
  48.         print_ip(ifc);
  49. }
  50. void
  51. print_ip(struct ifaddrs *ifaddrs)
  52. {
  53.         struct ifaddrs *ifa;
  54.         struct sockaddr_in *sin;
  55.         struct sockaddr_in6 *sin6;
  56.         char buf[INET6_ADDRSTRLEN];
  57.         for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next)
  58.         {
  59.                 if (ifa->ifa_addr == NULL) continue;
  60.                 if ((ifa->ifa_flags & IFF_UP) == 0) continue;
  61.       
  62.                 if (ifa->ifa_addr->sa_family == AF_INET)
  63.                 {
  64.                         sin = (struct sockaddr_in *)(ifa->ifa_addr);
  65.                         if (inet_ntop(ifa->ifa_addr->sa_family, (void *)&(sin->sin_addr), buf, sizeof(buf)) == NULL)
  66.                         {
  67.                                 printf("%s: inet_ntop failed!\n", ifa->ifa_name);
  68.                         }
  69.                         else
  70.                         {
  71.                         printf("%s: %s\n", ifa->ifa_name, buf);
  72.                         }
  73.                 }
  74.                 else if (ifa->ifa_addr->sa_family == AF_INET6)
  75.                 {
  76.                         sin6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
  77.                         if (inet_ntop(ifa->ifa_addr->sa_family, (void *)&(sin6->sin6_addr), buf, sizeof(buf)) == NULL)
  78.                         {
  79.                                 printf("%s: inet_ntop failed!\n", ifa->ifa_name);
  80.                         }
  81.                         else
  82.                         {
  83.                                 printf("%s: %s\n", ifa->ifa_name, buf);
  84.                         }
  85.                 }
  86.         }
  87. }
  88. int get_local_ip(struct ifaddrs **ifap, int family)
  89. {
  90.         int n;
  91.         bool change = 0;
  92.         char *name;
  93.         struct ifaddrs *ifa;
  94.         struct ifaddrs *tmp;
  95.         struct sockaddr_in *sin;
  96.         struct sockaddr_in6 *sin6;
  97.         char buf[INET6_ADDRSTRLEN];
  98.         n = getifaddrs(&ifa);
  99.         if (n != 0)
  100.                 return -1;
  101.       
  102.         *ifap = ifa;
  103.         for (ifa; (tmp = ifa) != NULL; ifa = ifa->ifa_next) {
  104.                 while (tmp && (IS_LOOPBACK(tmp) ||
  105.                                  NO_ADDRS(tmp) || (!TEST_FLAG(tmp, IFF_UP))||
  106.                                         (FAMILY_OK(tmp) && (!IN_FAMILY(tmp, family)) && (!IS_UNSPEC(family))) ||
  107.                                                 !FAMILY_OK(tmp))) {
  108.                                 change = true;
  109.                                 tmp = tmp->ifa_next;
  110.                 }
  111.                 if (change) {
  112.                         if (tmp)
  113.                                 move_ptr(ifa, tmp);
  114.                         else
  115.                                 memset(ifa, 0, sizeof (struct ifaddrs));
  116.                                 /** An alternative way is use these instead:
  117.                                 ifa->ifa_next = NULL;
  118.                                 ifa->ifa_name = NULL;
  119.                                 ifa->ifa_addr = NULL;
  120.                                 ifa->ifa_netmask = NULL;
  121.                                 if (TEST_FLAG(ifa, IFF_BROADCAST))
  122.                                         ifa->ifa_broadaddr = NULL;
  123.                                 else if (TEST_FLAG(ifa, IFF_POINTOPOINT))
  124.                                         ifa->ifa_dstaddr = NULL;
  125.                                 **/
  126.                         change = false;
  127.                 }
  128.       
  129.         }
  130.         /** I think even *ifap now is NULL after check, we should not free the space either.
  131.             Since user who called this routine will call freeifaddrs() too, The space will be free safely . **/
  132.         return 0;
  133. }
复制代码
 楼主| 发表于 2006-12-3 14:01:06 | 显示全部楼层

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

论坛发帖就是好卡,不知道怎么的,没反应,
结果多点了好几下,发重复了,
不好意思
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-12-3 14:01:16 | 显示全部楼层
重复了,重复了,每次发东西都会重复
回复 支持 反对

使用道具 举报

发表于 2006-12-3 15:38:34 | 显示全部楼层
很好用,谢谢了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-12-6 19:10:20 | 显示全部楼层
版主以后不要加精了,我感觉我的帖子很没水准。
回复 支持 反对

使用道具 举报

发表于 2006-12-9 21:46:47 | 显示全部楼层
谢谢了,我下下来好好 看看,学习一下
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表