对于我为OSX构建的一个小型工具,我想捕获从某个以太网 Controller 发送和接收的数据包的长度。

当我获取以太网卡时,我还获得了额外的信息,例如最大数据包大小,链接速度等。

当我启动(我称之为)“trafficMonitor”时,我将其启动为:

static void initializeTrafficMonitor(const char* interfaceName, int packetSize) {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t* sessionHandle = pcap_open_live(interfaceName, packetSize, 1, 100, errbuf);
    if (sessionHandle == NULL)
    {
        printf("Error opening session for device %s: %s\n", interfaceName, errbuf);
        return;
    }

    pcap_loop(sessionHandle, -1, packetReceived, NULL);
}

提供的interfaceName是接口(interface)的BSD名称,例如en0packetSize变量是一个整数,在该整数中我为该以太网适配器提供了最大的包大小(当时看来很合理)。例如,我的WiFi适配器的数据包大小为1538

我的回调方法称为packetReceived,如下所示:
void packetReceived(u_char* args, const struct pcap_pkthdr* header, const u_char* packet) {

    struct pcap_work_item* item = malloc(sizeof(struct pcap_pkthdr) + header->caplen);
    item->header = *header;
    memcpy(item->data, packet, header->caplen);
    threadpool_add(threadPool, handlePacket, item, 0);
}

我将数据包的所有属性填充到新结构中,并启动一个工作线程来分析数据包并处理结果。这是为了不让pcap等待,并且是在添加此工作线程方法之前解决此问题的尝试。
handlePacket方法是这样的:
    void handlePacket(void* args) {

        const struct pcap_work_item* workItem = args;
        const struct sniff_ethernet* ethernet = (struct sniff_ethernet*)(workItem->data);

        u_int size_ip;
        const struct sniff_ip* ip = (struct sniff_ip*)(workItem->data + SIZE_ETHERNET);
        size_ip = IP_HL(ip) * 4;
        if (size_ip < 20) {
            return;
        }
        const u_int16_t type = ether_packet(&workItem->header, workItem->data);
        switch (ntohs(type)) {
            case ETHERTYPE_IP: {

                char sourceIP[INET_ADDRSTRLEN];
                char destIP[INET_ADDRSTRLEN];

                inet_ntop(AF_INET, &ip->ip_src, sourceIP, sizeof(sourceIP));
                inet_ntop(AF_INET, &ip->ip_dst, destIP, sizeof(destIP));

                [refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];

                break;
            }
            case ETHERTYPE_IPV6: {
                // handle v6
                char sourceIP[INET6_ADDRSTRLEN];
                char destIP[INET6_ADDRSTRLEN];

                inet_ntop(AF_INET6, &ip->ip_src, sourceIP, sizeof(sourceIP));
                inet_ntop(AF_INET6, &ip->ip_dst, destIP, sizeof(destIP));

                [refToSelf registerPacketTransferFromSource:sourceIP destinationIP:destIP packetLength:workItem->header.caplen packetType:ethernet->ether_type];

                break;
            }
        }
}

根据以太网数据包的类型,我尝试确定它是使用IPv4还是IPv6地址发送的数据包。确定之后,我将一些细节发送给ObjectiveC方法(源IP地址,目标IP地址和数据包长度)。

我将数据包转换为tcpdump网站(http://www.tcpdump.org/pcap.html)上说明的结构。

问题是pcap似乎不能跟上接收/发送的数据包的速度。我不是没有嗅探所有数据包,或者数据包长度是错误的。

是否有人在我需要调整我的代码以使pcap捕获它们的所有指针或我有某种问题的指针。

这些方法是从我的ObjectiveC应用程序调用的,refToSelf是对objC类的引用。

编辑:我在后台线程中调用initializeTrafficMonitor,因为pcap_loop正在阻塞。

最佳答案

这是在哪个OS X版本上?在Lion之前的版本中,在使用BPF的系统(例如OS X)上,libpcap的默认缓冲区大小为32K字节; 1992 called, they want their 4MB workstations and 10Mb Ethernets back。在Lion中,Apple将libpcap更新为1.1.1版本;在libpcap 1.1.0中,默认的BPF缓冲区大小已增加到512MB(大多数(如果不是全部)具有BPF的系统的最大值)。

如果是Snow Leopard,请尝试切换到新的pcap_create() / pcap_activate() API,然后使用pcap_set_buffer_size()将缓冲区大小设置为512MB。如果这是Lion或更高版本,那不会有什么不同。

如果您的程序无法跟上平均数据包速率,那将无济于事,但是,如果临时突发超过平均数据包速率,那么这至少意味着减少了数据包丢失。

如果您的程序无法跟上平均数据包速率,那么,如果您只想要数据包的IP地址,请尝试将快照长度(称为“packetSize”`)设置为足够大的值,以仅捕获以太网头和IPv4和IPv6的IP地址。对于IPv4,34字节就足够了(libpcap或BPF可能会将其舍入为一个更大的值),因为这是14字节的以太网头+ 20字节的IPv4头(不带选项)。对于IPv6,它是54字节,用于14字节的以太网头和40字节的IPv6头。因此,使用packetSize值为54。

请注意,在这种情况下,应该使用lencaplen字段(而不是struct pcap_pkthdr字段)来计算数据包长度。 caplen是已捕获的数据量,并且不大于指定的快照长度; len是“在线”的长度。

另外,您可能希望尝试在同一线程中运行pcap_loop()和所有处理,并避免为数据包数据分配缓冲区并进行复制,以查看这是否可以加快处理速度。如果必须在单独的线程中进行处理,请确保在完成处理后释放数据包数据。

关于c++ - 用pcap捕捉无法跟上?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12234533/

10-11 23:03