pcap_dispatch - 回调处理问题

发布于 2024-12-10 02:54:31 字数 259 浏览 0 评论 0原文

我正在编写相当简单的 pcap“实时”捕获引擎,但是 pcap_dispatch 的数据包处理回调实现应该花费相对较长的处理时间。 pcap 是否在单独的线程中运行每个“pcap_handler”回调?如果是,“pcap_handler”是线程安全的,还是应该注意用关键部分来保护它? 或者,pcap_dispatch回调是否以串行方式工作?例如,仅在数据包 1 的“pcap_handler”完成后才调用数据包 2 的“pcap_handler”吗?如果是这样,是否有办法避免累积延迟? 谢谢, -V

I am writing fairly simply pcap "live" capture engine, however the packet processing callback implementation for pcap_dispatch should take relatively long time for processing.
Does pcap run every "pcap_handler" callback in separate thread? If yes, is "pcap_handler" thread-safe, or should the care be taken to protect it with critical sections?
Alternatively, does pcap_dispatch callback works in serial fashion? E.g. is "pcap_handler" for the packet 2 called only after "pcap_handler" for packet 1 is done? If so, is there an approach to avoid accumulating latency?
Thanks,
-V

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

皓月长歌 2024-12-17 02:54:31

Pcap 基本上是这样工作的:有一个内核模式驱动程序捕获数据包并将它们放入大小为 B 的缓冲区中。用户模式应用程序可以使用 pcap_looppcap_dispatchpcap_next(后者基本上是 pcap_dispatch 与一个数据包)。

因此,当您使用 pcap_dispatch 请求某些数据包时,libpcap 会转到内核并请求缓冲区中的下一个数据包(如果没有超时代码和东西开始,但这与本讨论无关),将其传输到用户区并将其从缓冲区中删除。之后,pcap_dispatch 调用您的处理程序,减少其 packets-to-do 计数器并从头开始。因此,pcap_dispatch 仅在已处理请求数量的数据包、发生错误或发生超时时返回。

正如您所看到的,libpcap 是完全非线程的,就像大多数 C API 一样。然而,内核模式驱动程序显然很乐意将数据包传递到多个线程(否则您将无法从多个进程捕获数据包),并且是完全线程安全的(每个用户模式都有一个单独的缓冲区)处理)。

这意味着您必须自己实现所有并行性。您可能想要做这样的事情:

pcap_dispatch(P, count, handler, data);
.
.
.
struct pcap_work_item {
    struct pcap_pkthdr header;
    u_char data[];
};

void handler(u_char *user, struct pcap_pkthdr *header, u_char *data)
{
    struct pcap_work_item *item = malloc(sizeof(pcap_pkthdr) + header->caplen);
    item->header = *header;
    memcpy(item->data, data, header->caplen);
    queue_work_item(item);
}

请注意,我们必须将数据包复制到堆中,因为回调返回后 headerdata 指针无效。

函数queue_work_item应该找到一个工作线程,并为其分配处理数据包的任务。既然您说您的回调需要“相对较长的时间”,您可能需要大量的工作线程。寻找合适的工人数量需要进行微调。

在这篇文章的开头,我说过内核模式驱动程序有缓冲区来收集等待处理的传入数据包。该缓冲区的大小是实现定义的。 pcap_open_livesnaplen 参数仅控制捕获一个数据包的字节数,但是,无法以便携式方式控制数据包的数量 。它可能是固定大小的。随着越来越多的数据包到达,它可能会变得更大。然而,如果它溢出,所有进一步的数据包都会被丢弃,直到有足够的空间容纳下一个数据包到达。如果您想在高流量环境中使用应用程序,您需要确保 *pcap_dispatch* 回调快速完成。我的示例回调只是将数据包分配给工作人员,因此即使在高流量环境中它也能正常工作。

我希望这能回答您所有的问题。

Pcap basically works like this: There is a kernel-mode driver capturing the packets and placing them in a buffer of size B. The user-mode application may request any amount of packets at any time using pcap_loop, pcap_dispatch, or pcap_next (the latter is basically pcap_dispatch with one packet).

Therefore, when you use pcap_dispatch to request some packets, libpcap goes to the kernel and asks for the next packet in the buffer (If there isn't one the timeout code and stuff kicks in, but this is irrelevant for this discussion), transfers it into userland and deletes it from the buffer. After that, pcap_dispatch calls your handler, reduces it's packets-to-do counter and starts from the beginning. As a result, pcap_dispatch only returns if the requested amount of packets have been processed, an error ocurred, or a timeout happened.

As you can see, libpcap is completely non-threaded, as most C API's are. The kernel-mode driver, however, is obviously happy enough to deliver packets to multiple threads (else you wouldn't be able to capture from more than one process), and is completly thread-safe (there is one separate buffer for each usermode handle).

This implies that you must implement all parallelisms by yourself. You'd want to do something like this:

pcap_dispatch(P, count, handler, data);
.
.
.
struct pcap_work_item {
    struct pcap_pkthdr header;
    u_char data[];
};

void handler(u_char *user, struct pcap_pkthdr *header, u_char *data)
{
    struct pcap_work_item *item = malloc(sizeof(pcap_pkthdr) + header->caplen);
    item->header = *header;
    memcpy(item->data, data, header->caplen);
    queue_work_item(item);
}

Note that we have to copy the packet into the heap, because the header and data pointers are invalid after the callback returns.

The function queue_work_item should find a worker thread, and assign it the task of handling the packet. Since you said that your callback takes a 'relativley long time', you likely need a large number of worker threads. Finding a suitable number of workers is subject to fine-tweaking.

At the beginning of this post I said that the kernel-mode driver has buffer to collect incoming packets which await processing. The size of this buffer is implementation-defined. The snaplen parameter to pcap_open_live only controls how many bytes of one packet are captured, however, the number of packets cannot be controlled in a portable fashion. It might be fixed-size. It might get larger as more and more packets arrive. However, if it overflows, all further packets are discarded until there is enough space for the next one to arrive. If you want to use your application in a high-traffic environment, you want to make sure that your *pcap_dispatch* callback completes quickly. My sample callback simply assigns the packet to a worker, so it works fine even in high-traffic enviroments.

I hope this answers all your questions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文