为什么我们的数据包嗅探器不能接收到所有重放的 TCP 数据包?
Why can't our packet sniffer receive all of the replayed TCP packets?
我们正在尝试使用 tcpreplay 通过 10 GbE 连接重播 pcap 文件 (smallFlows.pcap) 并捕获所有数据包,记录源和目标 ports/IP 地址。但是,存在明显的数据包丢失。在 3 Gbps 时,我们丢失了大约 15% 的已发送数据包。即使在 1 Gbps 下,我们也会损失 7%。我们的嗅探器程序是使用 netmap-libpcap and is a modified version of sniffex.c.
用 C 语言编写的
我们在测试时删除了所有打印语句。我们尝试更改 snap 长度和缓冲区大小,但这只是略微改善了丢包率。我们还将发送器和接收器上的 CPU 内核设置为性能模式,以最大化时钟速度(接收器上约为 2.67 GHz),但这没有任何效果。根据 top 的说法,CPU 使用率相当低——大约 15%。
接收器具有 Intel Core i7 处理器。发送方是 运行 Ubuntu 12.04.3 LTS(linux 内核 3.8.13),接收方是 运行 Ubuntu 12.04(linux 内核3.2.0-23-通用)。
我们怎样做才能确保所有数据包都收到?
这是主要功能:
int main(int argc, char **argv)
{
char *dev = NULL; /* capture device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
pcap_t *handle; /* packet capture handle */
char filter_exp[] = "ip"; /* filter expression [3] */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
int num_packets = 10; /* number of packets to capture */
print_app_banner();
printf(pcap_lib_version());
/* check for capture device name on command-line */
if (argc == 2) {
dev = argv[1];
}
else if (argc > 2) {
fprintf(stderr, "error: unrecognized command-line options\n\n");
print_app_usage();
exit(EXIT_FAILURE);
}
else {
/* find a capture device if not specified on command-line */
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n",
errbuf);
exit(EXIT_FAILURE);
}
}
/* get network number and mask associated with capture device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
dev, errbuf);
net = 0;
mask = 0;
}
/* print capture info */
printf("Device: %s\n", dev);
printf("Number of packets: %d\n", num_packets);
printf("Filter expression: %s\n", filter_exp);
/* open capture device */
//handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
handle = pcap_create(dev, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
exit(EXIT_FAILURE);
}
pcap_set_snaplen(handle, 1518);
pcap_set_promisc(handle, 1);
pcap_set_timeout(handle, 1000);
pcap_set_buffer_size(handle, 20971520);
pcap_activate(handle);
/* make sure we're capturing on an Ethernet device [2] */
if (pcap_datalink(handle) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet\n", dev);
exit(EXIT_FAILURE);
}
/* now we can set our callback function */
pcap_loop(handle, 0/*num_packets*/, got_packet, NULL);
/* cleanup */
pcap_close(handle);
printf("\nCapture complete.\n");
return 0;
}
这是 pcap_loop() 调用的数据包处理程序代码:
/*
* dissect packet
*/
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
static int count = 1; /* packet counter */
/* declare pointers to packet headers */
const struct sniff_ethernet *ethernet; /* The ethernet header [1] */
const struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */
int size_ip;
int size_tcp;
int size_payload;
//printf("\nPacket number %d:\n", count);
count++;
//if(count >= 2852200)
printf("count: %d\n", count);
/* define ethernet header */
ethernet = (struct sniff_ethernet*)(packet);
/* define/compute ip header offset */
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
if (size_ip < 20) {
//printf(" * Invalid IP header length: %u bytes\n", size_ip);
return;
}
/* define/compute tcp header offset */
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
/* compute tcp payload (segment) size */
size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);
return;
}
感谢您的帮助。
CPU 的用法是什么?是单个内核的 15% 还是所有内核的 15%?如果它是所有核心的 15%,而你有 8 个核心,它实际上超过了单个核心的 100%。因此,这可以解释为什么您的单线程应用程序无法捕获所有数据包。
如果您无法使用pcap 库接收所有数据包,那么除了尝试使用另一种数据包接收机制之外别无他法。 Linux 有 PF_PACKET 个插座,可能对您的情况有所帮助。根据这个答案:libpcap or PF_PACKET? ...libpcap 应该比 PF_PACKET 更受欢迎,因为 libpcap 更便携,并且在内部使用 PF_PACKET 的内存映射机制,这很难使用。
根据回答,libpcap使用了PF_PACKET的内存映射机制。您可以尝试在非内存映射模式下手动使用 PF_PACKET,这样您的数据包访问机制就会有所不同。如果内存映射模式某处有bug,可能会导致丢包
你试过用tcpdump记录抓包吗? Tcpdump 内部使用 libpcap,因此如果 tcpdump 能够捕获所有数据包而您的软件无法做到这一点,则它证明该错误存在于您的软件中,而不是 libpcap 的固有限制。
我们正在尝试使用 tcpreplay 通过 10 GbE 连接重播 pcap 文件 (smallFlows.pcap) 并捕获所有数据包,记录源和目标 ports/IP 地址。但是,存在明显的数据包丢失。在 3 Gbps 时,我们丢失了大约 15% 的已发送数据包。即使在 1 Gbps 下,我们也会损失 7%。我们的嗅探器程序是使用 netmap-libpcap and is a modified version of sniffex.c.
用 C 语言编写的我们在测试时删除了所有打印语句。我们尝试更改 snap 长度和缓冲区大小,但这只是略微改善了丢包率。我们还将发送器和接收器上的 CPU 内核设置为性能模式,以最大化时钟速度(接收器上约为 2.67 GHz),但这没有任何效果。根据 top 的说法,CPU 使用率相当低——大约 15%。
接收器具有 Intel Core i7 处理器。发送方是 运行 Ubuntu 12.04.3 LTS(linux 内核 3.8.13),接收方是 运行 Ubuntu 12.04(linux 内核3.2.0-23-通用)。
我们怎样做才能确保所有数据包都收到?
这是主要功能:
int main(int argc, char **argv)
{
char *dev = NULL; /* capture device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
pcap_t *handle; /* packet capture handle */
char filter_exp[] = "ip"; /* filter expression [3] */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
int num_packets = 10; /* number of packets to capture */
print_app_banner();
printf(pcap_lib_version());
/* check for capture device name on command-line */
if (argc == 2) {
dev = argv[1];
}
else if (argc > 2) {
fprintf(stderr, "error: unrecognized command-line options\n\n");
print_app_usage();
exit(EXIT_FAILURE);
}
else {
/* find a capture device if not specified on command-line */
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n",
errbuf);
exit(EXIT_FAILURE);
}
}
/* get network number and mask associated with capture device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
dev, errbuf);
net = 0;
mask = 0;
}
/* print capture info */
printf("Device: %s\n", dev);
printf("Number of packets: %d\n", num_packets);
printf("Filter expression: %s\n", filter_exp);
/* open capture device */
//handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
handle = pcap_create(dev, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
exit(EXIT_FAILURE);
}
pcap_set_snaplen(handle, 1518);
pcap_set_promisc(handle, 1);
pcap_set_timeout(handle, 1000);
pcap_set_buffer_size(handle, 20971520);
pcap_activate(handle);
/* make sure we're capturing on an Ethernet device [2] */
if (pcap_datalink(handle) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet\n", dev);
exit(EXIT_FAILURE);
}
/* now we can set our callback function */
pcap_loop(handle, 0/*num_packets*/, got_packet, NULL);
/* cleanup */
pcap_close(handle);
printf("\nCapture complete.\n");
return 0;
}
这是 pcap_loop() 调用的数据包处理程序代码:
/*
* dissect packet
*/
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
static int count = 1; /* packet counter */
/* declare pointers to packet headers */
const struct sniff_ethernet *ethernet; /* The ethernet header [1] */
const struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */
int size_ip;
int size_tcp;
int size_payload;
//printf("\nPacket number %d:\n", count);
count++;
//if(count >= 2852200)
printf("count: %d\n", count);
/* define ethernet header */
ethernet = (struct sniff_ethernet*)(packet);
/* define/compute ip header offset */
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
if (size_ip < 20) {
//printf(" * Invalid IP header length: %u bytes\n", size_ip);
return;
}
/* define/compute tcp header offset */
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
/* compute tcp payload (segment) size */
size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);
return;
}
感谢您的帮助。
CPU 的用法是什么?是单个内核的 15% 还是所有内核的 15%?如果它是所有核心的 15%,而你有 8 个核心,它实际上超过了单个核心的 100%。因此,这可以解释为什么您的单线程应用程序无法捕获所有数据包。
如果您无法使用pcap 库接收所有数据包,那么除了尝试使用另一种数据包接收机制之外别无他法。 Linux 有 PF_PACKET 个插座,可能对您的情况有所帮助。根据这个答案:libpcap or PF_PACKET? ...libpcap 应该比 PF_PACKET 更受欢迎,因为 libpcap 更便携,并且在内部使用 PF_PACKET 的内存映射机制,这很难使用。
根据回答,libpcap使用了PF_PACKET的内存映射机制。您可以尝试在非内存映射模式下手动使用 PF_PACKET,这样您的数据包访问机制就会有所不同。如果内存映射模式某处有bug,可能会导致丢包
你试过用tcpdump记录抓包吗? Tcpdump 内部使用 libpcap,因此如果 tcpdump 能够捕获所有数据包而您的软件无法做到这一点,则它证明该错误存在于您的软件中,而不是 libpcap 的固有限制。