深入了解getifaddrs函数

发布时间:2023-05-20

一、getifaddrs简介

getifaddrs 是一种系统级函数,可用于检索网络接口的地址信息。其定义在 ifaddrs.h 头文件中。该函数通过动态分配存储空间来保存设备地址列表。通常情况下,您需要使用 freeifaddrs 函数释放该列表。

struct ifaddrs {
   struct ifaddrs  *ifa_next;         /* 下一个地址 */
   char            *ifa_name;         /* 名称,任务类型,和类型信息 */
   unsigned int     ifa_flags;        /* 属性 */
   struct sockaddr *ifa_addr;         /* 地址信息 */
   struct sockaddr *ifa_netmask;      /* 网络掩码 */
   union {
       struct sockaddr *ifu_broadaddr;
                    /* 广播地址 */
       struct sockaddr *ifu_dstaddr;
                    /* 目标地址 */
   } ifa_ifu;
#define              ifa_broadaddr ifa_ifu.ifu_broadaddr
#define              ifa_dstaddr   ifa_ifu.ifu_dstaddr
   void            *ifa_data;         /* 地址信息 */
};

二、使用getifaddrs函数

下面是使用 getifaddrs 函数的基本步骤:

  1. 定义一个 ifaddrs 类型指针,用于存储指向设备地址列表的第一个元素的指针。
  2. 调用 getifaddrs 函数,并将指向 ifaddrs 类型指针的指针传递给函数。
  3. 遍历设备地址列表,并执行所需操作。
  4. 最终执行 freeifaddrs 函数,以释放分配给设备地址列表的存储空间。

三、getifaddrs的特性

1、获取网络接口信息

使用 getifaddrs 函数可获取网络接口的信息,包括接口名称、IP地址、子网掩码等。此外,还可以轻松获取相关的网络接口的广播地址和目标地址。

struct ifaddrs *ifaddr, *ifa;
int family, s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1) {
   perror("getifaddrs");
   exit(EXIT_FAILURE);
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
   if (ifa->ifa_addr == NULL)
       continue;
   family = ifa->ifa_addr->sa_family;
   /* 显示地址族(针对IPV4和IPV6),IPV4地址和端口号,IPV6地址和端口号,并将地址存储在host数组中 */
   if (family == AF_INET || family == AF_INET6) {
       s = getnameinfo(ifa->ifa_addr,
                       (family == AF_INET) ? sizeof(struct sockaddr_in) :
                                             sizeof(struct sockaddr_in6),
                       host, NI_MAXHOST,
                       NULL, 0, NI_NUMERICHOST);
       if (s != 0) {
           printf("getnameinfo() failed: %s\n", gai_strerror(s));
           exit(EXIT_FAILURE);
       }
       printf("%s  address: %s\n", ifa->ifa_name, host);
   }
}
freeifaddrs(ifaddr);

2、实现TCP/IP Server / Client 套接字列表

使用 getifaddrs 函数,可以轻松实现 TCP/IP 服务器和客户端套接字列表。下面是实现 TCP/IP 服务器/客户端套接字列表的代码示例。

int sockfd, status, s, max_fds = 0;
struct addrinfo hints, *servinfo, *p;
struct ifaddrs *ifaddr, *ifa;
/* 获取可用地址列表 */
getifaddrs(&ifaddr);
/* 循环连接可用地址 */
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
   if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET) {
       continue;
   }
   /* 定义TCP/IP Socket服务器地址 */
   memset(&hints, 0, sizeof hints);
   hints.ai_family = AF_INET;          /* IPV4 */
   hints.ai_socktype = SOCK_STREAM;    /* 流套接字(比如TCP) */
   hints.ai_flags = AI_PASSIVE;        /* 意味着底层套接字地址将被用于bind调用中data */
   /* 获取可用服务器地址 */
   s = getaddrinfo(NULL, "8080", &hints, &servinfo);
   if (s != 0) {
       fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
       return -1;
   }
   /* 循环连接可用地址 */
   for (p = servinfo; p != NULL; p = p->ai_next) {
       /* 创建TCP/IP Socket */
       sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
       if (sockfd < 0) {
           continue;
       }
       /* 确认连接 */
       status = connect(sockfd, p->ai_addr, p->ai_addrlen);
       if (status < 0) {
           close(sockfd);
           continue;
       }
       /* 监听客户端 */
       status = listen(sockfd, 10);
       if (status < 0) {
           close(sockfd);
           continue;
       }
       /* 记录最大的文件描述符号码 */
       max_fds = MAX(max_fds, sockfd);
   }
   /* free the list */
   freeaddrinfo(servinfo);
}
freeifaddrs(ifaddr);

四、Getifaddrs崩溃

在使用 getifaddrs 时,一些安全漏洞可能导致崩溃。应该注意以下问题:

1、内存泄漏

必须使用 freeifaddrs 函数显式释放分配的内存。否则可能会导致内存泄漏。

2、缓冲区溢出

缓冲区溢出是指将输入数据存储在内存缓冲区之外的情况。这可能导致程序失败或崩溃。可以使用 lwip 中的替代方案 lwip_getifaddrs 避免此问题。

结论

getifaddrs 是一种用于检索网络接口地址信息的系统级函数。通过动态分配存储空间来保存设备地址列表。使用该函数,您可以轻松实现 TCP/IP 服务器和客户端套接字列表、获取网络接口信息等。但是,在使用 getifaddrs 时,必须注意避免内存泄漏和缓冲区溢出引起的崩溃问题。