【Ubuntu】fragrouteを有効化したTcpreplayをインストール

### n本目です．
最初に書き始めたはずなのに，気が付いたら別の記事を何本もあげてしまっていました．
前回何も自己紹介していませんでしたが，私は現在大学院でセキュリティ系の研究に携わっていまして，中でもパケットやIDSに触れる機会が多いです．
今回はその中で扱っている`Tcpreplay`というツールのインストール方法について紹介します．
これもまた詰まりどころが多く，私は導入に2日かかりました...

# `Tcpreplay`とは
>`Tcpreplay`は，あらかじめキャプチャしたネットワークトラフィックを書き換えたり再送信したりするためのフリーのオープンソースユーティリティ群です．
引用元:[Tcpreplay - pcap ファイルの書き換えと再送信ユーティリティ](https://otsuka752.github.io/)

というとても便利なツールです．
私も研究で通信の再送信やフラグメント化に使っています．
上記のリンクのように非公式ですが日本語版のサイトも整っており，活用する上ではあまりハマるところはないかと思います．

# 今回の課題
本記事ではタイトルに記載した方法について紹介します．
が，その前にそこに至った経緯について軽く述べます．

私は結局は`Tcpreplay`を実際にインストールしたわけですが，私が使用していた環境にはなんと初めから`Tcpreplay`がインストールされていました．
ラッキーと思い早速研究に取り掛かったわけですね．
研究で`Tcpreplay`を使用する流れとしては，まず，対象のpcapファイルをフラグメント化した後に再送信します．
フラグメント化については，以下のサイトに詳しく載っているのでもし気になった方は見てみて下さい．
参照:[MTUとフラグメント](https://www.alaxala.com/jp/techinfo/archive/manual/AX7700R/HTML/10_10_/APGUIDE/0084.HTM)
ここでも一応簡単に説明すると，TCP/IP通信はパケットと呼ばれる単位で行われていますが，ネットワークによって通過できるパケットサイズに上限があるわけですね．
その上限を上回るサイズのパケットがそのネットワークを通るためにはあらかじめ，分割してあげる必要があり，この作業をフラグメント化と呼びます．
`Tcpreplay`にはこのフラグメント化を行ってくれるコマンドも備えています．

```sh
$ tcprewrite --fragroute=frag.cfg --infile=input.pcap --outfile=output.pcap
```

以上のコマンドを打つことで，`input.pcap`をフラグメント化した`output.pcap`が出力されます．
ちなみに，`frag.cfg`は分割サイズを設定するファイルで，コマンドを実行するディレクトリ内に自分で作成する必要があります．
例として，1400バイトで分割したければ，

```sh:frag.cfg
iP_frag 1400
```

とします．
参照:[tcprewrite](http://otsuka752.github.io/wiki/tcprewrite.html#fragroute)

ということで，私も以上の設定で`tcprewrite`を実行しました...

```sh
$ tcprewrite --fragroute=frag.cfg --infile=test.pcap --outfile=test.pcap
/usr/local/bin/tcprewrite: illegal option -- fragroute
tcprewrite (tcprewrite) - Rewrite the packets in a pcap file.
Usage:  tcprewrite [ -<flag> [<val>] | --<name>[{=| }<val>] ]...

   -r, --portmap=str          Rewrite TCP/UDP ports
   -s, --seed=num             Randomize src/dst IPv4/v6 addresses w/ given seed
   -N, --pnat=str             Rewrite IPv4/v6 addresses using pseudo-NAT
   -S, --srcipmap=str         Rewrite source IPv4/v6 addresses using pseudo-NAT
   -D, --dstipmap=str         Rewrite destination IPv4/v6 addresses using pseudo-NAT
   -e, --endpoints=str        Rewrite IP addresses to be between two endpoints
       --tcp-sequence=num     Change TCP Sequence (and ACK) numbers /w given seed
   -b, --skipbroadcast        Skip rewriting broadcast/multicast IPv4/v6 addresses
   -C, --fixcsum              Force recalculation of IPv4/TCP/UDP header checksums
   -m, --mtu=num              Override default MTU length (1500 bytes)
       --mtu-trunc            Truncate packets larger then specified MTU
   -E, --efcs                 Remove Ethernet checksums (FCS) from end of frames
       --ttl=str              Modify the IPv4/v6 TTL/Hop Limit
       --tos=num              Set the IPv4 TOS/DiffServ/ECN byte
       --tclass=num           Set the IPv6 Traffic Class byte
       --flowlabel=num        Set the IPv6 Flow Label
   -F, --fixlen=str           Pad or truncate packet data to match header length
       --fuzz-seed=num        Fuzz 1 in X packets.  Edit bytes, length, or emulate packet drop
       --fuzz-factor=num      Set the Fuzz 1 in X packet ratio (default 1 in 8 packets)
       --skipl2broadcast      Skip rewriting broadcast/multicast Layer 2 addresses
       --dlt=str              Override output DLT encapsulation
       --enet-dmac=str        Override destination ethernet MAC addresses
       --enet-smac=str        Override source ethernet MAC addresses
       --enet-subsmac=str     Substitute MAC addresses
       --enet-mac-seed=num    Randomize MAC addresses
       --enet-mac-seed-keep-bytes=num Randomize MAC addresses
       --enet-vlan=str        Specify ethernet 802.1q VLAN tag mode
       --enet-vlan-tag=num    Specify the new ethernet 802.1q VLAN tag value
       --enet-vlan-cfi=num    Specify the ethernet 802.1q VLAN CFI value
       --enet-vlan-pri=num    Specify the ethernet 802.1q VLAN priority
       --hdlc-control=num     Specify HDLC control value
       --hdlc-address=num     Specify HDLC address
       --user-dlt=num         Set output file DLT type
       --user-dlink=str       Rewrite Data-Link layer with user specified data
   -i, --infile=str           Input pcap file to be processed
   -o, --outfile=str          Output pcap file
   -c, --cachefile=str        Split traffic via tcpprep cache file
   -v, --verbose              Print decoded packets via tcpdump to STDOUT
   -A, --decode=str           Arguments passed to tcpdump decoder
       --skip-soft-errors     Skip writing packets with soft errors
   -V, --version              Print version information
   -h, --less-help            Display less usage information and exit
   -H, --help                 display extended usage information and exit
   -!, --more-help            extended usage information passed thru pager
       --save-opts[=arg]      save the option state to a config file
       --load-opts=str        load options from a config file

Options are specified by doubled hyphens and their name or by a single
hyphen and the flag character.

Please send bug reports to:  <tcpreplay-users@lists.sourceforge.net>
```

あれ？
上手くいかない．
どうやら`--fragroute`というオプションが認識されてないようです．
バージョンを確認してみると，

```sh
$ tcprewrite -V
tcprewrite version: 4.3.4 (build git:v4.3.4)
Copyright 2013-2018 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta
Copyright 2000-2012 by Aaron Turner <aturner at synfin dot net>
The entire Tcpreplay Suite is licensed under the GPLv3
Cache file supported: 04
Not compiled with libdnet.
Compiled against libpcap: 1.9.1
64 bit packet counters: enabled
Verbose printing via tcpdump: enabled
Fragroute engine: disabled
```

なるほど．
`Fragroute engine`というものが`disabeled`になっていますね．
これが原因のようです．
では，解決策はというとこのようなページを見つけました．

https://github.com/appneta/tcpreplay/issues/180

細かいところはよくわかりませんが，`libdnet`をインストールすれば良いようです．

確かに上記のバージョン情報でも`Not compiled with libdnet.`と出ています．
ここで大事なのは先に`libdnet`をインストールした後に`Tcpreplay`をインストールすることです．
私は試しにこの状態のまま`libdnet`をインストールしてみましたがやっぱり上手くいきませんでした．


## `libdnet`のインストール
本来であれば`apt-get`を使うことで簡単にインストールできますが，先程のサイトには`apt-get`を使わずにコンパイル&インストールしろと書いてあるので従います．
ちなみに真剣に`ダウンロード`→`コンパイル`→`インストール`の手順を踏んだのはこれが初めてだったので，少し丁寧に説明します．

### ソースコードをダウンロード
まずは対象のサイトからソースコードをダウンロードします．
今回は以下のサイトから`libdnet`のソースコードをダウンロードしました．

http://libdnet.sourceforge.net/

コマンドラインからダウンロードしたければ`wget`を使います．

```sh
$ wget http://prdownloads.sourceforge.net/libdnet/libdnet-1.11.tar.gz
...
2021-05-16 16:02:32 (608 KB/s) - ‘libdnet-1.11.tar.gz’ saved [446233/446233]
```

これで対象のURLからファイルをダウンロードできます．
もし，URLが分からなければwebサイト上で`右クリック`→`リンクのアドレスをコピー`とし，どこかに貼り付けることでわかります．
今回のサイトで行うと
`http://prdownloads.sourceforge.net/libdnet/libdnet-1.11.tar.gz?download`
このようなリンクが出てきます．
最後の`?download`が余計なので，消す必要がありますね．

### ソースコードを解凍
次にダウンロードしたファイルを以下のようにして解凍します．

```sh
$ tar -xzvf libdnet-1.11.tar.gz
```

解凍方法は対象のファイルによって異なります．
以下の記事がとても分かりやすくまとめてあります．

https://qiita.com/supersaiakujin/items/c6b54e9add21d375161f

### configure
次にインストール先の環境に合わせた設定を行います．
と言っても以下のコマンドで自動でやってくれますが...

```sh
$ cd libdnet-1.11  # 回答したフォルダに移動
$ ./configure
```

実はこのタイミングで様々な設定をすることが出来ます．
一つ例を挙げると，

```sh
$./configure --prefix=$HOME/  # インストール先をホームディレクト以下に変更
```

このように`--prefix`を使うとインストール先を指定することが出来ます．


### make
ここは私の理解不足もあり詳しく説明できませんが，以下のコマンドを打つだけです．

```sh
$ make
```

### make install
最後にインストールです．
以下のコマンドでインストールできます．

```sh
$ sudo make install
```

**configure**でインストール先を自分のホームディレクトリ以下にしていた場合，`sudo`は必要ないかもしれません(所有者による)．

何はともあれこれでインストールの完了です．

```sh
$ wget
$ tar
$ cd
$ ./configure
$ make
$ sudo make install
```

とりあえずはこの流れさえ押さえていれば大丈夫だと思います．


## `TCPReplay`のインストール
ここからやっと，`TCPReplay`のインストール方法を説明します．
`apt-get install`を使ったインストール方法もありますが，私の環境だとどうしても`fragroute`が有効化されなかったので，地道にビルドします．
流れは`libdnet`の時と同じなのでまとめて書いてしまいます．

```sh
$ sudo apt install build-essential libpcap-dev  #1

$ wget https://github.com/appneta/tcpreplay/releases/download/v4.3.4/tcpreplay-4.3.4.tar.gz
$ tar -zxvf tcpreplay-4.3.4.tar.gz
$ cd tcpreplay-4.3.4

$ ./configure
$ make
$ sudo ln -s /usr/local/lib/libdnet.1 /usr/lib/x86_64-linux-gnu/libdnet.1  #2
$ sudo make install
```

`#1`では`TCPReplay`のコンパイルに必要なパッケージをインストールしています．
`#2`は`libdnet`のシンボリックリンクを作成しています．
`TCPReplay`が`libdnet`の場所を上手く認識できるようにするためでしょうか．
環境によっては必要ないかもしれませんが，`sudo make install`後に`tcpreplay -V`としても以下のようなエラーが出る場合，`#2`を実行することで解決するかもしれません．

```sh
$ tcpreplay -V
tcpreplay: error while loading shared libraries: libdnet.1: cannot open shared object file: No such file or directory
```

参照

https://gist.github.com/zerkms/6bfc35632bfa372eb9c2c244e43ad20f

私も同じエラーが出ました．
そして，以上の記事を見つけました．
しかし，解決策を知ってから実際に解決するまで，半日もの時間を費やしてしまったのです．

### `1`と`l`
この違いわかりますか?
そう!
左が`いち`で右が`える`です．
では，`#2`の`ln`はどちらでしょう．
そう，`えるえぬ`ですね．
ではでは，`libdnet.1`は...
私はずっと`える`だと思ってました．
しかし，正解は`いち`です．
そう．
私はこの勘違いのせいで半日を無駄に過ごしました...
気を付けましょう．


### インストール完了!!
はい．
では，インストールできたか確認してみましょう．
```sh
$ tcpreplay -V
Warning: May need to run as root to get access to all network interfaces.
tcpreplay version: 4.3.4 (build git:v4.3.4)
Copyright 2013-2018 by Fred Klassen <tcpreplay at appneta dot com> - AppNeta
Copyright 2000-2012 by Aaron Turner <aturner at synfin dot net>
The entire Tcpreplay Suite is licensed under the GPLv3
Cache file supported: 04
Compiled against libdnet: 1.11
Compiled against libpcap: 1.9.1
64 bit packet counters: enabled
Verbose printing via tcpdump: enabled
Packet editing: disabled
Fragroute engine: enabled
Injection method: PF_PACKET send()
Not compiled with netmap
```

無事に出来たみたいです．
`Fragroute engine`もしっかり有効化されていますね．


## まとめ
`1`と`l`には気を付けよう．