异步libpcap: 丢包?

8

我有一个程序,使用原始套接字向主机发送一组TCP SYN数据包,并使用带过滤器的libpcap获取响应。我尝试在异步I/O框架中实现这个程序,但似乎libpcap会丢失一些响应(即当TCP SYN和响应之间的时间少于100微秒时,第一组数据包就丢失了)。 pcap句柄的设置如下:

pcap_t* pcap = pcap_open_live(NULL, -1, false, -1, errorBuffer);
pcap_setnonblock(pcap, true, errorBuffer);

接下来我添加了一个过滤器(包含在filterExpression字符串中):

struct bpf_program filter;
pcap_compile(pcap, &filter, filterExpression.c_str(), false, 0);
pcap_setfilter(pcap, &filter);
pcap_freecode(&filter);

在发送每个数据包后,我使用select函数来判断是否可以从libpcap中读取:

int pcapFd = pcap_get_selectable_fd(pcap);
fd_set fdRead;
FD_ZERO(&fdRead);
FD_SET(pcapFd, &fdRead);
select(pcapFd + 1, &fdRead, NULL, NULL, &selectTimeout);

并且阅读它:

if (FD_ISSET(pcapFd, &fdRead)) {
     struct pcap_pkthdr* pktHeader;
     const u_char* pktData;
     if (pcap_next_ex(pcap, &pktHeader, &pktData) > 0) {
         // Process received response.
     }
     else {
         // Nothing to receive (or error).
     }
}

如我之前所说,一些数据包会丢失(落入“没有接收”else语句中)。我知道这些数据包存在,因为我可以同步捕获它们(使用 tcpdump 或运行 pcap_loop 的线程)。我在这里缺少一些细节吗?还是这是与 libpcap 有关的问题?


可能的情况是您发送请求过于频繁,服务器发送响应比您处理它们的速度更快,从而超载操作系统的网络缓冲区并丢弃数据包。或者可能是您的接收套接字没有及时设置以处理初始响应。您能否验证您认为正在接收的所有响应是否实际到达?为此,请同时在应用程序上运行tcpdump。如果您在tcpdump中看到了所有预期的数据包但未在应用程序中看到,则可能存在上述问题之一。 - ryanbwork
很遗憾,我没有控制服务器的权限:(。 我看到的是pcap_next_ex在前几个数据包中返回0,即使它们被另一个线程(和tcpdump)捕获。还有其他可能性吗? - bruno nery
我尝试指定1000(1秒),但显然当pcap处于非阻塞模式时,这并没有起到任何作用(至少在Linux中)。 - bruno nery
重新阅读您的问题后,听起来您正在同一个循环(因此是同一个线程)中发送请求并处理响应?我会将其分成两个不同的线程,使读取线程永远不需要等待写入线程,反之亦然。希望这样解决了问题? - ryanbwork
这将打败异步I/O的目的(因为我需要另一个线程)。 - bruno nery
显示剩余3条评论
2个回答

3
如果通过select()(或poll()或其他你使用的调用/机制)报告pcap_t的FD可读,则不能保证这意味着只有一个数据包可以读取而不阻塞。
如果使用pcap_next_ex(),则只会读取一个数据包; 如果有多个可读取的数据包,则如果再次调用select(),它应立即返回,并将FD报告为可读,然后您可能会再次调用pcap_next_ex(),以此类推。这意味着每个数据包至少需要一个系统调用(select()),并且根据您正在使用的OS的版本和libpcap的版本,可能需要更多的调用。
相反,如果您调用pcap_dispatch(),并带有-1的数据包计数参数,则该调用将返回可以在单个读取操作中获取的所有数据包,并处理所有这些数据包,因此,在大多数平台上,如果有多个数据包可用(如果您正在测试程序并进行SYN洪水攻击,则可能会发生这种情况),则可能会使用一两个系统调用获得多个数据包。
此外,在支持内存映射数据包捕获的Linux系统上(我认为所有2.6及更高版本的内核都支持,大多数2.4内核也支持),并且使用较新版本的libpcap时,pcap_next_ex()必须复制数据包以避免内核在代码处理数据包时更改数据包并避免无限期地“锁定”环形缓冲区中的插槽,因此涉及额外的复制。

+1 对于不熟悉异步I/O的用户来说,这是一个常见的陷阱 :) 你应该继续阅读!))) - user405725
虽然这是有用的信息,但在这种情况下并不适用。主要问题是select指控一个活动的pcap_t,然后pcap_next_ex返回零。请注意,“丢失的数据包”会落入“没有接收到任何内容”的分支中。 - bruno nery

2
这似乎是在Linux下使用内存映射时libpcap存在的问题。请参阅我的另一个问题以获取详细信息。

所以,你解决了这个问题吗?如何为libpcap启用内存映射? - misteryes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接