LoginSignup
12

More than 3 years have passed since last update.

Wireshark dissector開発の流れ

Last updated at Posted at 2018-12-06

image.png

IIJ 2018 TECHアドベントカレンダー 12/7(金)の記事です】

Wiresharkがサポートするパケットはバージョン2.6.0で2080種類となっており(View → Internals → Supported Protocols より)、他の追随を許さない数となっています。(ちなみにtcpdump 4.9.2の tcpdump/print-*.c の数は145となっており、サポートするプロトコルの数もこの程度)

これだけプロトコルが多いとバグも多くなります。wireshark/test/captures/ を見るとテストされているプロトコルはそれほど多くはなく、git log -i --grep regress epan/dissectors/ を見ると、テストされていないプロトコルでよくregressionしていることが分かります。

L2TPv3はL2のフレームをカプセル化できるプロトコルで、ethernetHDLCなどの様々なL2のプロトコルをサポートしていますが、WiresharkではL2TPv3でカプセル化されたフレームは必ずCisco HDLCとしてデコードされてしまいます。これは「IIJ Engineers Blog - NGN VPN(IPoE)のフラグメントについて調べてみた」でも確認できます。L2TPv3解析機 (dissector) の開発者はCiscoの機器を用いてテストしたようです。

そこで、L2TPv3の下のフレームをethernetとしてデコードするように修正してみました。一応修正はできたものの、情報収集に苦労し、コーディング量に対して多くの時間がかかってしまいました。本記事ではこの修正を通して得られた情報をまとめ、dissector開発の敷居を少しでも下げられたら、と思います。

// 既存のdissectorを修正するのではなく新規に開発する場合、Luaによる拡張として実装した方が楽で、情報量も多いです。"wireshark lua" でググって下さい。

ドキュメント

ソースを読む前に見てもよく分からないと思うので、ソースを読んでいて躓いたら参照すると良いと思います。

ソースコードのダウンロードとビルド

ビルドの準備 (linux)

Ubuntu 18.04の場合

sudo apt install     \
  bison              \
  build-essential    \
  cmake              \
  flex               \
  git                \
  libgcrypt20-dev    \
  libpcap-dev        \
  libqt5svg5-dev     \
  qtbase5-dev        \
  qtmultimedia5-dev  \
  qttools5-dev       \
  wireshark-dev      \

ビルドの準備 (macOS)

brew install wireshark でビルドに必要なツール・ライブラリが全て入ります。

Wiresharkのダウンロードとビルド

git clone https://code.wireshark.org/review/wireshark
cd wireshark/
mkdir build/
cd build/
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$HOME/usr/opt/wireshark -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON ../
make -j3

run/wireshark で起動してみます。右上に "Development Build" と表示されているはずです。
image.png

L2TPv3 dissectorの読解・修正

L2TPv3 dissectorのソースは epan/dissectors/packet-l2tp.c です。GitHubで tl2tp とタイプすればすぐに見つかります。

image.png

対象プロトコルのpcapの取得

dissectしたいパケットをWiresharkでキャプチャして、File → Save で保存しておきます。今回はPacketLife.netの ICMP_over_L2TPv3_Pseudowire.pcap.cap を使うことにします。

人力dissect

RFCを読みながらパケットを人力で解析する、ということをします。これをやっておくとソースが格段に読みやすくなるためです。

epan/dissectors/*.c の冒頭には準拠する仕様が列挙されていることが多いです。packet-l2tp.c の場合、

/*
 * RFC 3931 for L2TPv3
 * http://tools.ietf.org/html/rfc3931
 */

よりRFC 3931に準拠していることが分かります。今回必要となる仕様を抜き出してみます:


3.2.2.  L2TP Data Message
...
                  Figure 3.2.2: L2TP Data Message Header

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      L2TP Session Header                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      L2-Specific Sublayer                     |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Tunnel Payload                      ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...
   to setup the session.  Section 4.1 defines two session headers, one
...

4.1.  L2TP Over Specific Packet-Switched Networks (PSNs)
...
   Session ID

      A 32-bit field containing a non-zero identifier for a session.
...

4.1.1.1.  L2TPv3 Session Header Over IP
...
               Figure 4.1.1.1: L2TPv3 Session Header Over IP

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           Session ID                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |               Cookie (optional, maximum 64 bits)...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                                                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   The Session ID and Cookie fields are as defined in Section 4.1.  The
...

これと、ICMP_over_L2TPv3_Pseudowire.pcap.cap をWiresharkで開いた結果:
image.png
を見比べながら、人力dissectしてみます。

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           Session ID                          | L2TPv3
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 0 1 0 1 0 0 0 1 0|
   |^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^|
   |           0x00            0x00            0x96            0x52|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----------
   |                         Ethernet dst                          | Ethernet
   |           0xca            0x02            0x10            0x78|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                               |         Ethernet src          |
   |           0x00            0x38|           0xca            0x03|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           0x10            0x78            0x00            0x1c|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----------
   | ...                                                           | IPv4

今回はシンプルなパケットでしたが、より複雑な構造になるほど人力dissectの恩恵が感じられると思います。

dissectorの読解

run/wireshark を立ち上げて、ICMP_over_L2TPv3_Pseudowire.pcap.cap を開き、デバッガでattachします。デバッグツールとして、本記事ではCLionを使いました。packet-l2tp.c 内の dissect_*() にブレークポイントを設置しておきます。L2TPv3が含まれるパケット#16をクリックした瞬間、packet-l2tp.c のL2TPv3 dissectorが走り、dissect_l2tp_ip() でブレークしました。
image.png
ブレーク中はWiresharkが固まるので、もう1つWiresharkを立ち上げて動作確認用に使うと良いです。

ステップ実行して、tvb_get_*() の実行に着目します。
image.png
パケットの中身は (tvbuff_t *tvb)->real_data (testy, virtual(-izable) buffer) に格納されていて、図のように tvb_get_*(tvb, offset) で取得できます。「人力dissect」と見比べると、L2TPv3 Session IDは38482となっていることが分かります。このように tvb の処理が理解できると、大まかなコードの意図も理解できるようになるはずです。

他に重要な用語に以下のようなものがあります。

Ethereal

Wiresharkの旧名。

epan

Enhanced Packet ANalyzer(これは後付けで、元々はEthereal Packet ANalyzerだった)。6.2. Overviewの図にあるように、Wiresharkの機能のうちパケット解析を担当する部分。

tree

image.png
Packet Details Paneで使われるデータ構造。ett_* という変数と共に登場するが、ettはEthereal Tree Typeのアクロニム

conversation

8.5. Conversations参照。

修正

コードをなんとなく理解したところで修正してみます。

diff --git a/epan/dissectors/packet-l2tp.c b/epan/dissectors/packet-l2tp.c
index 14881a7e03..eff682564a 100644
--- a/epan/dissectors/packet-l2tp.c
+++ b/epan/dissectors/packet-l2tp.c
@@ -306,7 +306,7 @@ static const enum_val_t l2tpv3_cookies[] = {
 };

 #define L2TPv3_COOKIE_DEFAULT       0
-#define L2TPv3_PROTOCOL_DEFAULT     L2TPv3_PROTOCOL_CHDLC
+#define L2TPv3_PROTOCOL_DEFAULT     L2TPv3_PROTOCOL_ETH

 #define L2TPv3_L2_SPECIFIC_NONE         0
 #define L2TPv3_L2_SPECIFIC_DEFAULT      1
@@ -957,6 +957,7 @@ static const true_false_string tfs_new_existing = { "New", "Existing" };

 static dissector_handle_t ppp_hdlc_handle;
 static dissector_handle_t ppp_lcp_options_handle;
+static dissector_handle_t ethertype_handle;

 static dissector_handle_t atm_oam_handle;
 static dissector_handle_t llc_handle;
@@ -3113,7 +3114,7 @@ dissect_l2tp_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
         /* If we have data, signified by having a length bit, dissect it */
         if (tvb_offset_exists(tvb, idx)) {
             next_tvb = tvb_new_subset_remaining(tvb, idx);
-            call_dissector(ppp_hdlc_handle, next_tvb, pinfo, tree);
+            call_dissector(ethertype_handle, next_tvb, pinfo, tree);
         }
         return tvb_reported_length(tvb);
     }
@@ -3791,6 +3792,11 @@ proto_reg_handoff_l2tp(void)
     ppp_hdlc_handle = find_dissector_add_dependency("ppp_hdlc", proto_l2tp);
     ppp_lcp_options_handle = find_dissector_add_dependency("ppp_lcp_options", proto_l2tp);

+    /*
+     * Get a handle for the Ethernet-framing dissector.
+     */
+    ethertype_handle = find_dissector_add_dependency("ethertype", proto_l2tp);
+
     /* Register vendor AVP dissector(s)*/
     dissector_add_uint("l2tp.vendor_avp", VENDOR_CABLELABS, create_dissector_handle(dissect_l2tp_vnd_cablelabs_avps, proto_l2tp));

ビルドし直してWiresharkを起動すると、無事冒頭の画像のようにethernetとしてdecodeしてくれました。ただし、この修正だとCisco HDLCであってもethernetとしてデコードされてしまいます。プロトコルを自動判別するか、選べるようにきちんと修正する予定です。

おわりに

dissectorの修正を行うのに必要な情報源や手順を紹介してみました。開発の敷居を下げることに貢献になれば幸いです。

Wiresharkのコード管理にはGerritが使われていて、Gerritに関しても情報が少ないです。近いうち本家へのコミットを行い、その手順も紹介したいと思います。

ライセンス

クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。

関連するソフトウェアの公式ドキュメントのメンテナの方へ:この記事の内容を公式ドキュメントに記載して頂ける場合、この記事にコメントして頂くか、twitter @wata_ash にご連絡下さい。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12