DUICUO

ByteDance は、Linux カーネル ネットワーク パケット キャプチャ ツールである netcap をリリースしました。

I. 背景紹介

Linuxカーネルネットワーク開発において、ネットワークパケットロスは一般的な課題です。従来のネットワークキャプチャツール(tcpdumpなど)は開発者が問題を特定するのに役立ちますが、効率が悪く、ネットワークの深層部の問題を正確に特定する能力には限界があります。eBPF技術の急速な発展に伴い、より高度な問題追跡機能が登場しています。ByteDanceのSTEチームは、この技術をベースに次世代カーネルネットワークキャプチャツールnetcap (net capture、社内では当初xcapと呼ばれていました 」を開発し、正式にオープンソース化しました。GitHubアドレス:https://github.com/bytedance/netcap。

tcpdumpはカーネルネットワークプロトコルスタック内のパケット送受信準備の固定ポイントのみを操作しますが、netcapはカーネルネットワークプロトコルスタックのほぼ全体をトレースできます(skbをパラメータとして機能します)。ByteDanceのSTEチームは、tcpdump構文をフィルタリング条件として使用し、skb(ソケットバッファ)をコンテキストとして、カーネルネットワークプロトコルスタック内のパケット全体の完全なトレースを容易に把握できるようにすることで、開発者がカーネルネットワークのパケットロス問題の特定効率を大幅に向上させます。

II. 使用例

例1 :IPアドレス10.227.0.45からのICMPパケットがカーネルの想定される関数呼び出しポイントに到達したかどうかを確認します。これを行う利点は、ネットワークの問題を特定してトラブルシューティングする際に、疑わしい範囲を容易に絞り込み、効率を向上させることができることです。

 netcap skb -f icmp_rcv@1 -i eth0 -e "host 10.227.0.45" -t "-nnv"

-f に続くパラメーターは、kprobe または tracepoint の特定の機能 (デフォルトは kprobe) であり、この機能内のどのパラメーター skb であるか (1 から始まる) を netcap に伝える必要があります。この例では、これが最初のパラメーターです。

  • -i オプションは、skb の dev パラメータに対応するネットワークインターフェースカードを参照します。一部の関数では skb に dev が設定されていないため、このオプションは注意して使用する必要があります。
  • -e パラメータは tcpdump フィルタリング構文です。
  • -t パラメータは tcpdump の表示方法を指定します。Netcap はパケットの内容自体を表示せず、tcpdump の表示方法を借用します。

例 2: TCP ポート 9000 のカーネル内でパケット損失の場所を確認します。

 netcap skb -f tracepoint:skb:kfree_skb -e "tcp port 9000" -S

-f に続くパラメータは、kprobe または tracepoint の特定の機能です。tracepoint の場合、skb がどのパラメータであるかを渡す必要はありません。

-S オプションは、この呼び出しのスタックも出力することを示します。この例では、スタックにはパケットロスが発生した場所が表示されます。

たとえば、次の図に示すように、着信 TCP 9000 パケットをドロップするようにマシン上で iptables ルールを構成します。

 iptables -A INPUT -p tcp --dport 9000 -j DROP

次に、netcap のコマンドを使用してパケット損失の状況を観察します。

その他のコマンドライン パラメータの詳細については、オープン ソース コードの README を読むか、コマンド netcap help skb を使用してください。

III. 設計と実装

メインフレームワーク

Netcap は kprobe/tracepoint を使用して関数をフックし、関数パラメータを通じて skb および sock キー構造を取得し、ネットワーク パケット データを取得してから、BPF マップを介してデータをユーザー空間に送信します。


実施原則

netcapの基本的な動作原理は次のとおりです。eBPFプログラムでは、tcpdump構文でフィルタリングされたパケットを識別するためのパケットフィルタリングが実行されます。このパケットはnetcapアプリケーションに渡され、netcapアプリケーションはtcpdumpに送信して表示するか、直接pcapファイルに出力します。下の図をご覧ください。


1. tcpdump構文に従ってフィルタリングする方法

tcpdumpのフィルタリング構文はcBPFに基づいており、オープンソースライブラリ(https://github.com/cloudflare/cbpfc)を使用しています。このライブラリはtcpdumpのフィルタリング構文をC関数に変換し、netcapのeBPFプログラムに組み込むことができます。C関数への変換の基本原理は次のとおりです。まず、libpcapライブラリを使用してtcpdumpのフィルタリング構文をcBPFの命令コードに変換し、次にこの命令コードをC言語の関数に変換します。下の図をご覧ください。


2. tcpdump を使用してデータ パケットの内容を表示するにはどうすればよいですか?

netcap プログラムが起動すると、tcpdump プログラムも起動します。tcpdump は標準入力で pcap 形式の入力ストリームを受け取り、解析されたフォーマットを標準出力に出力します。出力には様々なパラメータ(例:-e は MAC アドレスを表示します)が用いられます。以下の図をご覧ください。

3. データ パケットの内容を見つけるにはどうすればよいでしょうか?

カーネルでは、データパケットはskbを用いて記述されます。skbに指定された様々なヘッダーの位置を見つけることで、データパケット全体を特定できます。skbの一般的な構造は次のとおりです。

4. 送信方向のデータ パケットが不完全な場合にデータ パケットをフィルター処理するにはどうすればよいですか?

__ip_finish_output 関数などでデータパケットを送信する際、ETH ヘッダー、IP ヘッダー、TCP ヘッダーが完全に埋められていない場合があります。では、どうすれば完全なパケットを取得できるのでしょうか?

Netcapはskbのsock構造に基づいてデータパケットを推測し、再構築しようとします。ただし、キャプチャされたパケット内の重要でない情報(IPヘッダーのidフィールドなど)は、実際のデータと異なる場合があります。skbがsock構造からパケットの内容を推測する一般的なロジックは、次の図に示されています。


IV. その他の用途と拡張

マルチトレースポイントサマリー分析

Netcapは、データパケットが複数のポイントを通過するのにかかる時間を分析し、その出力を要約してパフォーマンスを分析できます。例えば、次のコマンドを使用します。

 netcap skb -f tracepoint:net:netif_receive_skb,ip_local_deliver@1,ip_local_deliver_finish@3,icmp_rcv@1 -e "host 10.227.0.72 and icmp" -i eth0 --gather --gather-output-color cyan

出力は次のように確認できます。トレースポイントに到達するまでの時間に基づいて、データパケットのパフォーマンスが低下している場所や、レイテンシが発生している可能性のある場所を分析できます。

拡張機能

ユーザーは独自のフィルタリングと出力関数を定義できます。以下に例を示します。

 netcap skb -f icmp_rcv@1 -e "host 10.227.0.72" -i eth0 --user-filter skb_user_filter.c --user-action skb_user_action.c --user-output-color green

拡張フィルター ファイル skb_user_filter.c は次のとおりです。

 // Return 0 means it's not need, pls filter out it. static inline int xcap_user_filter(void *ctx, void *pkt, u16 trace_index) { return 1; }

この拡張機能の戻り値が0の場合、tcpdump構文のフィルタリング後に、ユーザー定義のフィルタが再度実行されることを意味します。例えば、数行のスクリプトを記述し、skb->markに従ってフィルタリングするだけで簡単に実現できます。

拡張出力ファイル skb_user_action.c は次のとおりです。

 struct xcap_user_extend { int a; // format: 0x%x uint32_t b; // int64_t c; uint8_t x1; // format: %c uint8_t x2; // format: 0x%x uint16_t x3; // format: 0x%x }; // Return 0 means not need to ouput static inline int xcap_user_action(void *ctx, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index) { user->a = 0x12345678; user->b = 1000; user->c = 2002; user->x1 = 'M'; user->x2 = 0x11; user->x3 = 0xabcd; return 1; }

`struct xcap_user_extend` はユーザー定義構造体です。必要な出力情報はこの構造体内で定義され、値が割り当てられます。この構造体でサポートされる型は以下のとおりです(注:ポインタと配列はサポートされていません):`int8_t`、`uint8_t`、`char`、`int16_t`、`uint16_t`、`int`、`uint32_t`、`int64_t`、`uint64_t`。

これにより、下の画像に示すように、追加情報を出力できるようになります。

V. 今後の展望

開発者の日常業務において、ネットワークパケットキャプチャツールは、ネットワークエンジニアやテストエンジニアなどにとって不可欠なスキルとなっています。ByteDanceのSTEチームは、カーネルネットワークのパケットロス問題の特定効率向上に貢献できるよう、netcapネットワークパケットキャプチャツールをオープンソース化しました。開発者の皆様のご参加とプルリクエストの投稿を心よりお待ちしております。このオープンソースプロジェクトの開発を共同で推進してまいります。今後、以下の分野においても最適化を進めていく予定ですので、ご期待ください。

  • DPDKへのさらなるサポートが必要ですが、アップストリームのUSDTインベントリに問題があるため、アプリケーションでのUSDTサポートはまだ提供されていません。ご興味のある方は、コードを修正してサポートすることができます。
  • 複数のカーネル バージョンに対する統合サポート。
  • カスタム出力を使用する場合、データパケット数が多いと印刷エラーが発生する可能性があります。これは、tcpdumpの出力情報とユーザー定義の出力情報が同じ標準出力を共有するためです。この問題は、今後の最適化で修正される予定です。