サマリー
Linuxにパケットジェネレータ機能を提供するpktgenは、非常に高速にパケット送信を行うことができるカーネルモジュールである。
pktgen自体については PCとpktgenで行くショートパケットワイヤレートの旅 を参照。
pktgenで送信中のパケットがtcpdumpで確認できないという話があったため、その説明のため簡単にコールスタックを追った結果を残しておく。
はじめに
賢明な諸氏のことなので、図を見れば説明は不要なことと思う。
参照すべきドキュメントと部位
今回はUbuntu 16.04が手元にあったので、それを参考に話を進める。
最初に参考にすべきドキュメントは、おそらく Linux foundationのnetworking/kernel_flow だと思われる。
ネットワークの重要な関数についての概要が説明されていて、おそらく読むべきは dev_hard_start_xmit()
/ dev_queue_xmit_nit
の記述じゃないかと思う。
とはいえ、上記の説明で使用されている関数と、Linux kernel 4.4は少し状況が違う気がするので、何かを探すときのフックポイントくらいに思っておいて良いんじゃないかと思う。
とりあえずpktgenのソースコードのうち、送信に関する部分はこの辺りだ。
pktgenの送信関数 pktgen_xmit()
-> http://lxr.free-electrons.com/source/net/core/pktgen.c?v=4.4#L3364
|
|
のように netdev_start_xmit が呼ばれている。
tcpdumpがパケットのコピー対象としているのが netdev_start_xmit の前なのか後なのかが分かれば、疑問に答えることができるだろう。
static int __dev_queue_xmit()
-> http://lxr.free-electrons.com/source/net/core/dev.c?v=4.4#L3044
struct sk_buff *dev_hard_start_xmit()
-> http://lxr.free-electrons.com/source/net/core/dev.c?v=4.4#L2719
static int xmit_one()
ここ分岐点
-> http://lxr.free-electrons.com/source/net/core/dev.c?v=4.4#L2701
void dev_queue_xmit_nit()
-> http://lxr.free-electrons.com/source/net/core/dev.c?v=4.4#L1854
最終的に dev_queue_xmit_nit
が呼ばれるポイントに到達することで、パケットのコピーが行われる。
static int xmit_one()
を見ると dev_queue_xmit_nit()
が呼ばれた後に netdev_start_xmit()
が呼ばれていることが分かる。
|
|
従って pktgen_xmit()
が呼び出している netdev_start_xmit()
は、パケットキャプチャの対象となる処理をバイパスしていると考えられる。
その辺りを図示したのが、冒頭の図になる。
まとめ
pktgenで送信中のパケットはtcpdumpで確認することはできません。 (一部例外除く)
当然、tcpdumpに限らず、同様のパケットキャプチャポイントを持つwireshark, tshark(即ち htons(ETH_P_ALL)
を指定した socket(AF_PACKET, int socket_type, int protocol);
を用いるもの全て)は、同様にpktgenの送信パケットを観測することはできません。
現場からは以上です。
おまけ
当初、pktgenには送信モードは存在しなかったのだが、途中から M_NETIF_RECEIVE
(4.2以降)や M_QUEUE_XMIT
(4.8以降)が導入された。
これを使用した場合は netdev_start_xmit()
を直接呼ぶ以外の処理経路が生まれるため、必ずしもパケットキャプチャできないわけではなくなる。
恐らく M_QUEUE_XMIT
辺りを使えば dev_queue_xmit()
が呼ばれるので dev_queue_xmit_nit()
を通過できるはずだ。
また、今回は Linux kernel 4.4 をベースに調査しているが、Ubuntu 14.04で使用される Linux kernel 3.16 の場合は odev -> netdev_ops -> ndo_start_xmit が本体となってしまうため、使用しているドライバごとの送信処理にいきなり飛んでしまう。
興味のある人は(もちろん何でも良いんだけど) e1000 ドライバなどを読むと良いんじゃないだろうか。この辺 http://lxr.free-electrons.com/source/drivers/net/ethernet/intel/e1000/e1000_main.c?v=3.16#L840 から追えると思う。