我正在使用 https://www.tcpdump.org/sniffex.c 中 C 代码的修改版使用 libpcap 打印有关通过接口(interface)的 TCP 数据包的信息。
这是我尝试用来检查接收到的数据包的源字段是否等于当前接口(interface)的 IP 地址的回调代码示例(以便仅分析传出数据包)。这是一个相当广泛的程序,所以我决定只包含有问题的部分:
// retrieve IP address of interface
char * dev_name = "eth0";
struct ifreq ifr;
int fd;
char *dev_ip;
fd = socket(AF_INET, SOCK_DGRAM, 0);
// type of address to retrieve (IPv4)
ifr.ifr_addr.sa_family = AF_INET;
// copy the interface name in the ifreq structure
strncpy(ifr.ifr_name , dev_name , IFNAMSIZ-1);
ioctl(fd, SIOCGIFADDR, &ifr);
close(fd);
dev_ip = inet_ntoa(( (struct sockaddr_in *)&ifr.ifr_addr )->sin_addr);
printf("IPv4 address: %s\n", dev_ip);
printf("inet_ntoa: %s\n",inet_ntoa(ip->ip_src));
if (strcmp(dev_ip,inet_ntoa(ip->ip_src)) == 0)
printf("EQUAL!\n");
但是,正如您在下面的屏幕截图中看到的,即使源 IP (inet_ntoa
) 和接口(interface)的 IP 地址 (IPv4 地址
) 不同,根据程序,它们的值始终相等。
问题可能是什么?
最佳答案
inet_ntoa
返回一个指向已在 inet_ntoa
内部的静态内存中构造的字符串的指针。 。每次调用时它都会重新使用相同的静态内存。当你这样做时:
dev_ip = inet_ntoa(...);
dev_ip
设置为指向该内部静态缓冲区。此时静态缓冲区包含一个表示接口(interface)地址的字符串,因此您的:
printf("IPv4 address: %s\n", dev_ip);
显示预期结果。但然后你这样做:
printf("inet_ntoa: %s\n",inet_ntoa(ip->ip_src));
并且覆盖 inet_ntoa
的内部缓冲区,其中包含表示数据包地址的字符串。
但请记住dev_ip
仍然指向该内部缓冲区。所以当你这样做时:
if (strcmp(dev_ip,inet_ntoa(ip->ip_src)) == 0)
(顺便说一句,不必要地用已经存在的数据包地址再次覆盖内部缓冲区)传递给 strcmp
的两个参数都是指向 inet_nota
的指针的内部缓冲区,因此 strcmp
总会发现目标字符串匹配——因为两个参数都指向同一个字符串。
要修复此问题,请制作 inet_ntoa
生成的字符串的本地副本调用它后立即通过复制到本地缓冲区或执行以下操作:
dev_ip = strdup(inet_ntoa(...));
(当您不再需要该字符串时,请记住free(dev_ip)
),或者更好的是,使用inet_ntoa
的线程安全变体叫inet_ntoa_r
如果你的平台有这个功能的话。 inet_ntoa_r
不使用(和重复使用)内部缓冲区。它要求调用者提供放置字符串的缓冲区。
关于比较接口(interface)的 ipv4 地址和数据包的源地址(libpcap),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58306371/