1

OSX用に構築している小さなツールの場合、特定のイーサネットコントローラーとの間で送受信されるパケットの長さをキャプチャしたいと思います。

イーサネットカードを取得すると、最大パケットサイズ、リンク速度などの追加情報も取得します。

(私が呼んでいる)'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のは、たとえば、インターフェースのBSD名ですen0。変数は整数であり、そのpacketSizeイーサネットアダプターの最大パケットサイズを指定します(当時は論理的であるように見えました)。たとえば、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のWebサイト(http://www.tcpdump.org/pcap.html)で説明されている構造体にパケットをキャストしました。

問題は、pcapが送受信されたパケットに追いついていないように見えることです。すべてのパケットをスニッフィングしていないか、パケット長が間違っています。

pcapがそれらすべてをキャッチするようにコードを調整する必要がある場所、または何らかの問題がある場所にポインターがありますか?

これらのメソッドは私のobjectiveCアプリケーションから呼び出されrefToSelf、objCクラスへの参照です。

編集: pcap_loopがブロックしているため、バックグラウンドスレッドでinitializeTrafficMonitorを呼び出しています。

4

1 に答える 1

3

これはどのバージョンの OS X に搭載されていますか? Lion より前のリリースでは、OS X などの BPF を使用するシステムでの libpcap のデフォルトのバッファ サイズは 32K バイトでした。1992 が呼び出され、彼らは 4MB ワークステーションと 10Mb イーサネットを取り戻したいと考えています 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 バイト、IPv6 ヘッダーの場合は 40 バイトです。したがって、54 の packetSize 値を使用します。

この場合、 のフィールドではなく、フィールドを使用してパケットlenを計算する必要があることに注意してください。 キャプチャされたデータの量であり、指定されたスナップショットの長さを超えることはありません。「ワイヤ上」の長さです。caplenstruct pcap_pkthdrcaplenlen

また、pcap_loop() とすべての処理を同じスレッドで実行してみて、パケット データにバッファを割り当ててコピーするのを避けて、処理速度が向上するかどうかを確認することもできます。別々のスレッドでそれらを実行する必要がある場合は、処理が終わったら必ずパケット データを解放してください。

于 2012-09-02T23:29:28.500 に答える