比较接口的 ipv4 地址和数据包的源地址 (libpcap)
Comparing ipv4 address of interface and source address of packet (libpcap)
我正在使用 libpcap 对 https://www.tcpdump.org/sniffex.c 中的 C 代码进行修改,以打印有关通过接口的 TCP 数据包的信息。
这是我尝试使用的回调代码示例,用于检查接收到的数据包的源字段是否等于当前接口的 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
) 和接口 IP 地址 (IPv4 address
) 不同,它们的值始终相等根据程序
可能是什么问题?
inet_ntoa
returns 指向在 inet_ntoa
内部的静态内存中构造的字符串的指针。每次调用时它都会重新使用相同的静态内存。当你这样做时:
dev_ip = inet_ntoa(...);
dev_ip
设置为指向该内部静态缓冲区。此时静态缓冲区包含一个表示接口地址的字符串,因此您的:
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_r
,则使用 inet_ntoa
的线程安全变体功能。 inet_ntoa_r
不使用(并重复使用)内部缓冲区。它要求其调用者提供放置字符串的缓冲区。
我正在使用 libpcap 对 https://www.tcpdump.org/sniffex.c 中的 C 代码进行修改,以打印有关通过接口的 TCP 数据包的信息。
这是我尝试使用的回调代码示例,用于检查接收到的数据包的源字段是否等于当前接口的 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
) 和接口 IP 地址 (IPv4 address
) 不同,它们的值始终相等根据程序
可能是什么问题?
inet_ntoa
returns 指向在 inet_ntoa
内部的静态内存中构造的字符串的指针。每次调用时它都会重新使用相同的静态内存。当你这样做时:
dev_ip = inet_ntoa(...);
dev_ip
设置为指向该内部静态缓冲区。此时静态缓冲区包含一个表示接口地址的字符串,因此您的:
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_r
,则使用 inet_ntoa
的线程安全变体功能。 inet_ntoa_r
不使用(并重复使用)内部缓冲区。它要求其调用者提供放置字符串的缓冲区。