Wireshark dissector開発の流れ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/167409/bc52cb86-9ce4-0aaf-4617-34d6988f9c61.png)

【[IIJ 2018 TECHアドベントカレンダー](https://eng-blog.iij.ad.jp/archives/2215) 12/7（金）の記事です】

<!-- ネットワークプロトコルアナライザWireshark -->

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

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

[L2TPv3](http://tools.ietf.org/html/rfc3931)はL2のフレームをカプセル化できるプロトコルで、[ethernet](http://tools.ietf.org/html/rfc4719)や[HDLC](https://tools.ietf.org/html/rfc4349)などの[様々なL2のプロトコル](https://www.iana.org/assignments/l2tp-parameters/l2tp-parameters.xhtml)をサポートしていますが、WiresharkではL2TPv3でカプセル化されたフレームは必ずCisco HDLCとしてデコードされてしまいます。これは[「IIJ Engineers Blog - NGN VPN(IPoE)のフラグメントについて調べてみた」](https://eng-blog.iij.ad.jp/archives/611)でも確認できます。L2TPv3解析機 (dissector) の開発者はCiscoの機器を用いてテストしたようです。

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

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










## ドキュメント

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

- [Get Involved](https://www.wireshark.org/develop.html) ソースコードのダウンロード・パッチの送り方
- [Wireshark Developer’s Guide](https://www.wireshark.org/docs/wsdg_html_chunked/) かなりしっかりまとまっていて一読の価値あり
  - [I. Wireshark Build Environment](https://www.wireshark.org/docs/wsdg_html_chunked/PartEnvironment.html) 開発の準備
  - [II. Wireshark Development](https://www.wireshark.org/docs/wsdg_html_chunked/PartDevelopment.html) ソースコードの説明
     - [6. How Wireshark Works](https://www.wireshark.org/docs/wsdg_html_chunked/ChapterWorks.html) 設計について
     - [7. Introduction](https://www.wireshark.org/docs/wsdg_html_chunked/ChapterBuildIntro.html) ソースコードの概要。短いのでざっと読んでみて下さい
     - [9. Packet dissection](https://www.wireshark.org/docs/wsdg_html_chunked/ChapterDissection.html) dissectorの説明
- [Wireshark wiki - Development](https://wiki.wireshark.org/Development) 雑多な情報
- [wireshark/README.md](https://github.com/wireshark/wireshark/blob/master/README.md) あまり情報は無い
- [wireshark/README.{bsd,linux,macos,windows}](https://github.com/wireshark/wireshark) 古い。これに従ってもビルドできない
- [wireshark/doc/README.developer](https://github.com/wireshark/wireshark/blob/master/doc/README.developer) コーディング規約
- [wireshark/doc/README.dissector](https://github.com/wireshark/wireshark/blob/master/doc/README.dissector) dissectorの説明










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

### ビルドの準備 (linux)

Ubuntu 18.04の場合

```sh
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のダウンロードとビルド

<!-- https://www.wireshark.org/develop.html -->

```
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](https://qiita-image-store.s3.amazonaws.com/0/167409/d18ef9a7-2579-1add-4803-12e792d37ebc.png)

<!-- TODO: linux group wireshark -->

<!-------------------------------------------------->
<!-------------------------------------------------->
<!-------------------------------------------------->
<!-------------------------------------------------->
<!-------------------------------------------------->

## L2TPv3 dissectorの読解・修正

L2TPv3 dissectorのソースは [epan/dissectors/packet-l2tp.c](https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-l2tp.c) です。GitHubで `t` → `l2tp` とタイプすればすぐに見つかります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/167409/3e26f50f-62f5-032e-01a3-90938590632e.png)

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

dissectしたいパケットをWiresharkでキャプチャして、File → Save で保存しておきます。今回はPacketLife.netの [ICMP_over_L2TPv3_Pseudowire.pcap.cap](http://packetlife.net/captures/protocol/l2tp/) を使うことにします。

### 人力dissect

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

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

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

より[RFC 3931](http://tools.ietf.org/html/rfc3931)に準拠していることが分かります。今回必要となる仕様を抜き出してみます：

```

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](https://qiita-image-store.s3.amazonaws.com/0/167409/d5fd9e50-5593-d4d5-0613-5cb10bc5a7fd.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](https://www.jetbrains.com/clion/)を使いました。`packet-l2tp.c` 内の `dissect_*()` にブレークポイントを設置しておきます。L2TPv3が含まれるパケット#16をクリックした瞬間、`packet-l2tp.c` のL2TPv3 dissectorが走り、`dissect_l2tp_ip()` でブレークしました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/167409/555bb39c-128e-4e40-c6f1-b9c5b067b98a.png)
ブレーク中はWiresharkが固まるので、もう1つWiresharkを立ち上げて動作確認用に使うと良いです。

ステップ実行して、`tvb_get_*()` の実行に着目します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/167409/9dee6119-fbd8-2100-c3e7-94f242f1cd81.png)
パケットの中身は `(tvbuff_t *tvb)->real_data` ([testy, virtual(-izable) buffer](https://github.com/wireshark/wireshark/blob/wireshark-2.6.5/epan/tvbuff.h#L37)) に格納されていて、図のように `tvb_get_*(tvb, offset)` で取得できます。「人力dissect」と見比べると、L2TPv3 Session IDは38482となっていることが分かります。このように `tvb` の処理が理解できると、大まかなコードの意図も理解できるようになるはずです。

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

**Ethereal**

Wiresharkの旧名。

**epan**

[Enhanced Packet ANalyzer](https://www.wireshark.org/docs/wsdg_html_chunked/ChWorksOverview.html)（これは後付けで、元々は[Ethereal Packet ANalyzer](https://www.wireshark.org/lists/wireshark-dev/201012/msg00284.html)だった）。[6.2. Overview](https://www.wireshark.org/docs/wsdg_html_chunked/ChWorksOverview.html)の図にあるように、Wiresharkの機能のうちパケット解析を担当する部分。

**tree**

![image.png](https://qiita-image-store.s3.amazonaws.com/0/167409/3f8b9845-eee7-f27e-814c-cc70139bdefb.png)
Packet Details Paneで使われるデータ構造。`ett_*` という変数と共に登場するが、[ettはEthereal Tree Typeのアクロニム](https://www.wireshark.org/lists/wireshark-dev/201012/msg00284.html)。

**conversation**

[8.5. Conversations](https://www.wireshark.org/docs/wsug_html_chunked/ChStatConversations.html)参照。

<!-- https://github.com/wireshark/wireshark/blob/master/doc/README.dissector 2.2 Following "conversations". -->










## 修正

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

```diff
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が使われていて](https://code.wireshark.org/review/)、Gerritに関しても情報が少ないです。近いうち本家へのコミットを行い、その手順も紹介したいと思います。










## ライセンス

<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="クリエイティブ・コモンズ・ライセンス" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />この 作品 は <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">クリエイティブ・コモンズ 表示 4.0 国際 ライセンス</a>の下に提供されています。

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