パケット生成ツールあれこれ（ハンズオン記録）

# この記事の目的  
この記事は、とあるツール勉強会（ハンズオン）での記録とした記事である。
対象者は、初めてパケットを作成する人向けであり、ハンズオンの実施時間を1時間としている。
最終的に、紹介するツールを使って、お手製パケットを作成できるところまでを目的としている。
（パケット作成初心者を対象としており、高度なファジングテスト用途などはここでは紹介しません）

# この記事の章構成  
* 【前編】Windows10(GUI環境)でのパケット作成ツールを利用する
  + 前編における前提条件
  + 必要となる知識
  + ツール類のインストール(ハンズオン開始までに各自で実施)
  + 注意事項
  + ipsendwinでのパケット送信
  + PacketBuilderでのパケット送信

* 【後編】Linux環境(CLI環境)でのパケット作成ツールを利用する
  + 後編における前提条件
  + 必要となる知識
  + ツール類のインストール（ハンズオン開始までに各自で実施）
  + ハンズオン環境構築
  + netcatでのパケット送信
  + scapyを利用したパケット送信
  + dgramを使ったパケット送信

---

# 【前編】Windows10(GUI環境)でのパケット作成ツールを利用する

## ◆ 前提となる環境  
ハンズオンの前提となる環境は下表のとおりです。  
（Mac、Linux環境の方は前編は、後編までは手を休めていてください）

|項目|条件|
|---|---|
|利用OS|Windows10(Memoryは8GByte程度は欲しいところ)|
|パケット確認ツール|Wireshark|
|ドライバ|Win10Pcap|
|利用ツール1|ipsendwin|
|利用ツール2|PacketBuilder|

## ◆ 必要となる知識
今回はIPv4のパケットを前提とするので、下記に占めるようなIPv4パケットの構造を知っておく必要があります。
（登場する単語が分からないときに、各自で参照できれば良いので、一度目を通しておいてください）

* IPv4パケット構造
https://ja.wikipedia.org/wiki/IPv4#%E3%83%91%E3%82%B1%E3%83%83%E3%83%88

## ◆ ツール類のインストール(ハンズオン開始までに各自で実施)
私の環境では、以下の掲載の順番にインストールを行いました。

* パケット確認アプリ（スニファーアプリ）
  + Wireshark
    - https://www.wireshark.org/download.html

* ドライバ
  + Win10Pcapをインストールしていること
    - http://www.win10pcap.org/ja/download/  
　　　※ ちなみにWin10Pcapは、登 大遊さんというかたが作成してくださっているドライバです。Windows10ではこれが無いと、後ほど利用するipsendwinが動作しない場合があります。

* 利用ツール１
  + ipsendwin
    - https://www.vector.co.jp/download/file/win95/net/fh392590.html

* 利用ツール２
  + Colasoft Packet Builder
    - https://www.colasoft.com/jp/packet_builder/

## ◆ 注意事項
* ハンズオンでのパケット送信先は、各自が管理している端末のインタフェースとするようお願いします。
* 作成したパケットを送信操作は、必要最小限にとどめるようにお願いします。
（必要以上に送信する行為は、DDoS攻撃に類する行為となりうる可能性もあることから、厳に慎むようお願いします。）

## ◆ ipsendwinでのパケット送信
### 1. 作成したいパケットの情報整理
* パケットを作成する前に、送信元のアドレス情報と送信先のアドレス情報を事前に確認しておく
  + 送信元情報は、コマンドプロンプトで、ipconfig /allにて表示し、IPアドレスとMACアドレスを確認しておくこと。
  + 宛先情報は、Ping応答する端末（自分自身で管理しているサーバ等が良いです）
  + 操作端末が属するサブネットの外側であれば、先ほどのipconfigの結果からデフォルトゲートウェイのIPと、arp -aコマンドで対応するMACアドレスを確認しておく

### 2. 流れているパケットを観測する
* Wiresharkを起動（Version3.4.3）  
　⇒メニューバー  
　　⇒キャプチャ  
　　　⇒オプション（NICのIPアドレスが同じアドレスとなっているところ）  
　　　　⇒選択したインタフェースのキャプチャフィルタ  
　　　　　　（半角小文字で、icmpと入力する。するとテキストボックスが緑になる）  
　　　　　　⇒開始を押す

### 3. ipsendwinの利用
* スタート→ipsendwinよりアプリを起動する。
　⇒送信したいパケットの基本構成を定義
　　⇒ウインドウの中央付近にあるフレーム構成
　　　⇒MAC ＋ IP ＋ ICMP ＋ PAYLOAD を選択する

* パケット構造の組み立て
　⇒メニューバー
　　⇒設定
　　　⇒フレーム定義
　　　（もしくはメニューバの直下のトンカチのアイコンをクリック）
　　　　⇒フレーム定義フォームが表示される
　⇒フレーム定義フォーム
　　⇒MACタブ（先ほどの情報）
　　　⇒IPタブ（─〃─）
　　　　⇒入力後にOKボタンを押して、フレーム定義フォームを閉じる。

* パケットの送信
　⇒ipsendwinのメイン画面
　　⇒インタフェース（送信するNICを選択していること）
　　　⇒送信フレーム数（1とすること）
　　　　⇒送信間隔（1パケットのみを送るので、デフォルトの1000ミリ秒のままで良い）
　　　　　⇒送信ボタン押下

* パケットの確認
　⇒Wiresharkのパケットリストを確認する。
　　⇒送信パケットが２重表示されていることがある。
　　　※ Wiresharkが気を利かして、すべてのドライバの情報を拾っていると思われる
　　　※ 自宅にて、送信端末外でキャプチャしたところ、実際には1パケットのみが送出されていることを確認している

### 4. 次に利用するPacketBuilderの下準備として、Wiresharkで取得したパケットをファイル保存する。
（停止操作）
　⇒Wiresharkのメニューバー
　　⇒キャプチャ
　　　⇒停止（またはメニューバー直下の赤■のアイコンを押下する）

（保存操作）
　⇒Wiresharkのメニューバー
　　　　⇒メニューバーのファイル
　　　　　⇒保存
　　　　　　⇒ファイルを保存するダイアログ
　　　　　　　⇒任意の名前でパケットをファイル保存する。
　　　　　　　　（拡張子はpcapngまたはpcapでよい。）
　　　　　　　　　⇒次のPacketBuilder利用の際の入力ファイルとするので、保存場所を覚えておいてください。  

## ◆ PacketBuilderでのパケット送信
### 1. 作成したいパケットの情報整理
⇒ ipsendwinと同様に進めることも可能ではあるが、ここでは、保存済みのパケットファイル（pcapファイル or pcapngファイル）を読み込み、読み込んだパケットデータをPacketBuilderで送信する手順を試してみる。
（ipsendwinの時と同じ環境でPingパケットを送信するだけなので、先ほど取得したパケットをそのまま再送信することが可能）

### 2. 流れているパケットを観測する(ipsendwinの時と全く同じ内容を掲載)
* Wiresharkを起動（Version3.4.3）
　⇒メニューバー
　　⇒キャプチャ
　　　⇒オプション（NICのIPアドレスが同じアドレスとなっているところ）
　　　　⇒選択したインタフェースのキャプチャフィルタ
　　　　　　（半角小文字で、icmpと入力する。するとテキストボックスが緑になる）
　　　　　　⇒開始を押す

### 3. PacketBuilderの利用
* スタート⇒PacketBuilderより、アプリを起動する。
* 先ほど保存したパケットファイルをインポートする。
　⇒メニューバー
　　⇒ファイル
　　　⇒インポート
　　　　⇒ファイルを開くダイアログ
　　　　　⇒先ほど保存したパケットファイルを選択して、ボタン（開く）を押下
* パケットの送信
　⇒PacketBuilderメイン画面の右側部分（パケットリストと表記している部分）に送信したいパケットが表示されていることを確認する。（表示されていないときは、パケットのインポートに失敗している可能性がある）
　　⇒送信したいパケットを一つ選択した状態でマウスを右クリックする
　　　⇒代替メニューがポップアップされ、`選択したパケットを送信`というサブメニューをクリックする
　　　　⇒ダイアログ（選択したパケットを送信する）が表示されるので、オプションのアダプタに欄に、送信に用いるNICが指定されていることを確認する。（目的のNICが選択されていないときは、右側にあるボタン(選択)を押して、適切なNICを指定する）
　　　　　⇒その他のオプションを変更することなく、ダイアログの下方のボタン(開始)を押下する。

* パケットの確認(ipsendwinの時と全く同じ内容を掲載)
　⇒Wiresharkのパケットリストを確認する。
　　⇒送信パケットが２重表示されていることがある。
　　　※ Wiresharkが気を利かして、すべてのドライバの情報を拾っていると思われる
　　　※ 自宅にて、送信端末外でキャプチャしたところ、実際には1パケットのみが送出されていることを確認している

## ◆ 補足
* ipsendwinの利用時、パケットの確認で二重にパケットを観測する事象に遭遇しないために、Win10Pcapをインストールしないという方法があると思うかもしれません。
しかし、ipsendwinでは、最新のWinPcapだけだと正しく動作しないことがあるため、今回はWin10Pcapを個別にインストールしています。
* ipsendwinで送信する際、Wiresharkでは2重にパケットが観測されてしまうのですが、Windows10の外側でパケットキャプチャを行ってみたところ、通過するパケットは1パケットだけが流れておりました。（質問されたときに備え、事前に確認をしておきました。）
* 本事象について詳細を追求することはしませんが、機会があったら調べてみるつもりです。

---
ここからが今回のメインテーマです

# 【後編】Linux環境(CLI環境)でのパケット作成ツールを利用する
ここでの手順通り操作すれば、手順を通して必要な環境が出来上がり、目的のパケット送信までが完結できるようになっています。

## ◆ 前提となる環境
ハンズオンの前提となる環境を下記に示します。

* Docker(コンテナ仮想化の基盤)をインストールしているMac、Windows、Linux。
* Docker上に動作用コンテナ2台を作成し、2台がIPv4で相互通信できる環境とする。
* インターネットへは、HTTP-Proxyを介さないで接続できる環境であることとする。

（補足）
※ 今回用意している動作用コンテナイメージは、Dockerfileを作りそびれてしまったなど諸事情から、マルチCPU対応ではないです。なので、Macの場合は多分IntelMacでないと動かない可能性がありますので、適合した環境を用意してください。
※ ここで用いるイメージには、主要なアプリがプリインストールされています。
※ CPUのアーキテクチャ的に対象外となっている方は、お手数ですが下方の構築手順（参考：独自で環境を作成したい方向け）を参考に各自でコンテナイメージ作成から実施していただくよう、お願いいたします。
※ HTTP-Proxy配下の環境の方は、Dockerとnodejsに対してHTTP-Proxyの設定を行うか、必要ファイルを個別でローカルにコピーし、ローカルインストールを行ってください。

## ◆ 必要となる知識
ハンズオンで必要となる知識は下記に掲載の通りですが、手順通りに操作するのであれば詳しくなくても問題ないです。

* netcatの利用
  + Linuxの基本的なコマンド
* scapyの利用
  + Pythonの基本的な使い方
* dgramの利用
  + nodejsの基本的な使い方

## ◆ 動作用コンテナの作成

### 1. 本ハンズオンで利用したDockerのバージョン確認

```
docker --version
Docker version 19.03.8, build afacb8b7f0
```

### 2. 本ハンズオンで利用するコンテナイメージのダウンロード

```
docker pull jwat/ubuntu2004:latest
```

ダウンロードが完了したら、`docker images`コマンドにてイメージがダウンロードされたことを確認してください。  

```
Server:~# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
jwat/ubuntu2004     latest              8239fcda55a7        1 hours ago         1.19GB
```

### 3. コンテナの起動
2台の動作用コンテナを区別するため、コンテナ1号機の名前をserverX01、コンテナ2号機の名前をserverX02、として進めます。

* まずは1号機のコンテナ起動を行います。  
コンテナ名を、serverX01 とし、手元の端末からSSH接続（SCPでpcapファイルのダウンロード）可能な環境にしたいと思います。  
指定したコンテナにはOpensshserverがインストール済みであるので、下記のコマンドで起動します。

```
docker run -itd --privileged=true --name serverX01 -p 10022:22 jwat/ubuntu2004:latest /usr/bin/supervisord
```

上記で、`--privileged=true`としているのは、コンテナ内でroot権限によりアプリ操作を行うことを目的としています。
（tsharkによりパケットキャプチャをする際や、scapyでパケット送信する際に、root権限で実行が必要である）
また、`-p 10022:22`としているのは、コンテナ内でのOpen-SSHServerが使うポートである22番を、コンテナ外部からアクセスする際には10022番で待ち受けるための指定です。
各自の環境で都合の良い番号に変更することは可能ですが、この場合は接続時時に間違わないようにしてください。

最後の実行オプションである`/usr/bin/supervisord`は、OpenSSHServerをデーモンとして起動するために必要なオプションで、コンテナ内の設定ファイル`/etc/supervisor/conf.d/startup.conf`に`sshd`の起動コマンドを記述しています。
これらを指定することにより、コンテナ起動時にOpenSSHServerのプロセスが起動した状態を作り出しています。

* 1号機の起動確認

```
docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
ee3bdc572302        jwat/ubuntu2004:latest   "/bin/bash"         4 seconds ago       Up 3 seconds                            serverX01
server@jw:~#
```

* 1号機コンテナへの接続

```
docker exec -it serverX01 bash
```

* 1号機コンテナのrootパスワード設定
rootユーザのパスワードは、各自で自由に設定しておいてください。  
ここで設定しておかないと、あとで手戻りとなります。

```
passwd root
```

* 1号機コンテナのアドレス情報の確認
下記コマンドで、1号機のIPv4アドレスおよびMACアドレスを確認しておく。  
（デフォルトの起動方法だと、DHCPにてアドレスが払い出されるため）

```
root@ee3bdc572302:/# ip addr sh
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
82: eth0@if83: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
root@ee3bdc572302:/#
```

1号機のコンテナでの作業を終えたら、コンテナ内で`exit`コマンド実行により、コンテナから抜けて、元のOSへ戻ります。
その後に続いて、2号機も同様に実施します。

* 2号機のコンテナ起動を行います。
コンテナ名を、serverX02 とし、手元の端末からSSH接続（SCPでpcapファイルのダウンロード）可能な環境にしたいと思います。
1号機とほぼ同様のコマンドですが、次の2点は間違えないようにしてください。
  + コンテナ名は、`--name serverX02`とすること
  + OpenSSHServerの待ち受けポート番号は、1号機やその他の使用済みポートと重ならないようにすること。（`-p 20022:22`など）

   ```
    docker run -itd --privileged=true --name serverX02 -p 20022:22 jwat/ubuntu2004:latest /usr/bin/supervisord
   ```

* 2号機の起動確認
`docker ps -a`を実行すると、先ほど起動済みの1号機と合わせて2つのコンテナの状態を確認することができる。

```
docker ps -a
CONTAINER ID    IMAGE                    COMMAND         CREATED         STATUS        PORTS                   NAMES
ee3bdc572302    jwat/ubuntu2004:latest   "/bin/bash"     4 minutes ago   Up 3 minutes  0.0.0.0:10022->22/tcp   serverX01
abcf5389f3e7    jwat/ubuntu2004:latest   "/bin/bash"     3 seconds ago   Up 2 seconds  0.0.0.0:20022->22/tcp   serverX02
server@jw:~#
```

* 2号機コンテナへの接続

```
docker exec -it serverX02 bash
```

* 2号機コンテナのrootパスワード設定
rootユーザのパスワードは、各自で自由に設定しておいてください。
ここで設定しておかないと、あとで手戻りとなります。

```
passwd root
```

* 2号機コンテナのアドレス情報の確認
1号機の時と同様に、2号機のIPv4アドレスおよびMACアドレスを確認しておく。

```
root@abcf5389f3e7:/# ip addr sh
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
84: eth0@if85: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
root@abcf5389f3e7:/#
```

2号機のコンテナでの作業を終えたら、コンテナ内で`exit`コマンド実行により、コンテナから抜けて、元のOSへ戻ります。

### 4. 2台のコンテナそれぞれへの接続
2台の動作用コンテナに対して、コンテナの外部にあるSSHクライアント（Teratermのようなターミナルアプリを使って）別々にSSH接続を行います。
そのことで、パケット送信側のコンテナと、パケット観測側のコンテナで、独立してコマンド実行を行えるようにする。
またSSH接続とすることで、パケット観測後に保存したPcapファイルをSCPコピーしたり、手元の端末から設定ファイルなどを送りやすくすることも目的としています。

※ Dockerが動作する端末では、コンテナの内部と外部をつなぐネットワークはNATネットワークとして起動している。
（今回のように、ネットワーク指定をしていない場合は、コンテナが所属するネットワークはDockerが用意するNATルータ配下となる）

※ 今回は、Dockerが稼働するOSのIPv4アドレスが192.168.0.101、Dockerが用意してくれているNATルータの内側のIPv4アドレスを172.17.0.1である前提で説明を行う。
（上記と異なる場合は、アドレスを読み替えて操作をしてください）
また、ここまでの手順により、1号機のコンテナのIPv4が`172.17.0.2`、2号機のコンテナのIPv4が`172.17.0.3`と仮定して、以降を進めていく。

この段階で、各コンテナへ接続する際は、下表のとおりとなる。
各自で操作のしやすい接続パターンを選択し、1号機、2号機、それぞれ別のターミナルにより接続すること。

|接続元|接続先|SSH接続IPv4|SSH接続時ポート|
|:---|---|---|---|
|Docker端末(172.17.0.1)|1号機(172.17.0.2)|172.17.0.2|22|
|Docker端末(172.17.0.1)|2号機(172.17.0.3)|172.17.0.3|22|
|Docker端末外|1号機(172.17.0.2)|172.17.0.1|10022|
|Docker端末外|2号機(172.17.0.3)|172.17.0.1|20022|

以上で環境準備は完了です。

## ◆ netcatによるパケット送信（UDPパケットを簡単に送信するためだけの簡便な手法）
※ コンテナ環境にはあらかじめ下記のコマンドでインストールを行っている。

```
apt install -y netcat
```

### 1. パケット受信側の準備
tsharkを利用して、到着パケットを確認する。
2号機側で、port番号12345のパケットの観測を開始する。

```
tshark -f 'port 12345'
```

### 2. netcatによりUDPパケットの送信
1号機側で、2号機あてにパケットを送信する。

```
nc -u 172.17.0.3 12345
```

※ netcatは、臨時のプロキシサーバを稼働する際によく利用されるが、ポート指定だけの条件であれば、UDPパケットをサクッと送信する際にも利用できる。

netcatでのパケット送信例は以上で終了です。

## ◆ scapyによるパケット送信
※ コンテナ環境にはあらかじめ下記コマンドで、ディレクトリ`root/`にインストールを行っているので、手順通りに環境を作成した方は実施しなくてよいです。

```
git clone https://github.com/secdev/scapy.git
```

（個別で環境を用意する方は、各自でcloneしてださい）

### 1. パケット受信側の準備
tsharkを利用して、到着パケットを確認する。
2号機側で、icmpのパケットの観測を開始する。

```
tshark -Y icmp
```

### 2. 1号機側で2号機に向けて送信する。
scapyは/root/ディレクトリ配下にgit clone にてインストールされている。
今回は対話モードで利用するので、run_scapyを実行する。

```
cd /root/scapy/
./run_scapy
```

・send関数で直接送信する。
スラッシュでヘッダをつないでいく

```
send(IP(dst="172.17.0.3")/UDP(dport=12345))
send(IP(dst="172.17.0.3")/ICMP(code=0))
```

入力していないところは自動生成される

・変数にパケット構造を代入して、完成したら送信する。

```
p = IP(dst="172.17.0.3")/ICMP()
```

作成したら、途中で構造の確認を行う

```
p.show()
```

送信するときはsend関数で送信する。

```
send(p)
```

### 3. 送信パケットの定義を、pcapファイル内のパケットにより直接指定する
2号機側でパケット観測を継続していたら、Ctrl+Cなどでtsharkを停止する。
次に、tsharkをファイル書き込みモードで起動し、観測パケットをpcapファイルとして保存できるようにTsharkを起動する。
注意事項として、バックグラウンドではSSH通信が流れており、今回は観測不要なパケットであることから、TCP22番のパケットを除外指定することとする。

```
tshark -f 'not port 22' -w hoge.pcap
```

上記実行後に、ターミナルでパケット数がカウントアップされ始めたら、1号機にて適当なパケットを2号機あてに送信する。
2号機側で程よくパケットが蓄積されたことをカウンタで確認出来たら、Ctrl+cなどでtsharkでのパケット記録を停止する。
その後、1号機側でSCPコマンドにファイルをコピーし、2号機に保存されたpcapファイルを1号機側に複製する。

```
scp root@172.17.0.3:/root/hoge.pcap /root/scapy/
```

pcapファイルがコピーされたことを確認出来たら、下記のように変数`p`にpcapファイルのパスを代入する。

```
p=rdpcap("hoge.pcap")
```

読み込んだ"hoge.pcap"の概要を確認するには、

```
p.show()
```

を実行します。すると下記のように読み込まれたパケットの一覧がリスト表示されます。

```
0000 Ether / IP / ICMP 172.17.0.2 > 172.17.0.3 echo-request 0 / Raw
0001 Ether / IP / ICMP 172.17.0.3 > 172.17.0.2 echo-reply 0 / Raw
0002 Ether / IP / ICMP 172.17.0.2 > 172.17.0.3 echo-request 0 / Raw
0003 Ether / IP / ICMP 172.17.0.3 > 172.17.0.2 echo-reply 0 / Raw
0004 Ether / IP / ICMP 172.17.0.2 > 172.17.0.3 echo-request 0 / Raw
0005 Ether / IP / ICMP 172.17.0.3 > 172.17.0.2 echo-reply 0 / Raw
```

上記の一番左の数字は、Wiresharkなどでいうところのフレーム番号に相当します。

読み込んだパケットを送信したいときは、sendp関数を使います。
その書式に従いパケットを送信しようとすると `sendp(p)` となりますが、これだと遅延がほぼゼロのネットワークで一気に流してしまうだけでなく、宛先アドレスや送信元アドレスがネットワーク構成とは矛盾するパケットを送信してしまいます。その結果、観測地点では想定外の見え方となることが予想されます。
なのでフレーム番号0000のパケットだけを、フレーム番号指定で流すことにします。
その時のコマンドは下記です。（先ほど送信に利用したsend関数とは違い、sendp関数ですのでスペルミスに注意して下さい。）

```
sendp(p[0])
```

これでフレーム番号0番のパケットが送信されました。

では意地悪で、フレーム番号1番のパケットを送信してみることにします。
その書式は下記です。

```
sendp(p[1])
```

上記を実行すると、観測側ではARPのフレームが表示されるだけだと思います。
（なぜなら、MACアドレスとIPアドレスの送信と受信が反転した不正パケットを送信したことから、コンテナでパケットを送信する前にARPによるアドレス解決を実施し、悪いことにARP解決不能状態に陥ってしまうという事象が観測されるにとどまるためです）

scapyハンズオンはここで終了です。
（最後に、対話型のPythonを抜けるために、`exit()`を実行しておいてください。）

## ◆ dgram（nodejsモジュール）利用によるパケット送信

次に、dgramというnodejsのモジュールを使ってパケット送信する例を紹介します。
ここでは、数行のJavaScriptのコードを作成（サンプルのコピー＆ペーストでよいです）を行うため、次の手順で進めます。  

* 1. 作業用ディレクトリの作成
* 2. nodejsのプロジェクトの定義
* 3. 利用モジュール（dgram）のインストール。（要ネット接続）
* 4. パケット送信用のJavaScriptの作成
* 5. パケット送信
* 6. パケット受信側での観測結果確認

なお、ここで使用するコンテナについては、上記の1～5はコンテナ1号機（serverX01）、6はコンテナ2号機（serverX02を用いて行います。

### 1. 作業用ディレクトリの作成
作業する際はrootユーザで実施します。
まず始めに、適当なディレクトリに作業用ディレクトリを作成します。
ここではディレクトリ名を`nodetest`として作成することにします。

```
mkdie nodetest
```

### 2. nodejsのプロジェクトの定義
先ほど作成した作業用ディレクトリに移動します。

```
cd nodetest
```

そして、作成プロジェクトを初期化します。

```
npm init
```

上記実行後、対話型形式で質問が出てきますので、すべて`Enterキー`を連打して、デフォルト値として回答します。（将来送信ロジックを組まれる方は、各自で適切に回答してください。）

### 3. 利用モジュール（dgram）のインストール。（要ネット接続）
次に、今回使用するnodejsのモジュール`dgram`をインストールします。

```
npm install dgram
```

### 4. パケット送信用のJavaScriptの作成
dgramを使ってパケットを送信するコードを作成します。
ここでは下記に示すサンプルコードをそのまま用いることにします。
（作成するファイル名は `serverX00.js`として進めていきます。）

```
// Module
var dgram          = require("dgram");

// Parameter
var packerGenerate = dgram.createSocket("udp4");
var strMessage     = "hogehoge"
var portDst        = 12345
var ipv4Dst        = "172.17.0.3"

// Packet Send
packerGenerate.send(strMessage, 0, strMessage.length, portDst, ipv4Dst)

// Fin.
console.log("Packet sent.")
```

### 5. パケット送信
いよいよパケットを送信します。
下記のようにコマンドを実行することで、パケットが送信されます。

```
node serverX00.js
```

送信側で`Packet sent.`と表示されたことを確認したら、`Ctrl + c`で処理を抜けてください。

### 6. パケット受信側での観測結果確認
ここで、コンテナ2号機（serverX02）のターミナルを見ると、下記のようにUDPパケットが観測されているはずです。

```
n x.xxxxxxxxx   172.17.0.2 ? 172.17.0.3   UDP 50 33561 ? 12345 Len=8
m y.yyyyyyyyy   172.17.0.3 ? 172.17.0.2   ICMP 78 Destination unreachable (Port unreachable)
```

※ 1. ここまでの作業の途中でtshark を停止ししてしまった方は、tsharkを起動し直して手順5の`パケット送信`をもう一度実施してください。
※ 2. それでも観測できない方は、これまでの手順の再点検をお願いします。


以上で、本日のパケット生成ツールあれこれハンズオンを終了とします。
各ツールごとに異なっている特徴を活かして、適材適所で使い分ける参考となりましたら幸いです。
（次回のネタの案としては、パケット受信、パケット解析、簡単なサーバ作成、、、etc.あります。要望がありましたらどれかをネタにして登壇してみるのも面白いかなと考えております。ありがとうございました。）


# 参考：独自で環境を作成したい方向け
（独自環境で参加される方は、ハンズオンまでに実施しておいてください。）

* コンテナイメージのベースはUbuntu2004を使っています。
* コンテナイメージ作成時の手順を以下に示します。（実行時はroot権限で実施しています）
* 提供イメージではなくご自身で納得いく環境を整えたい方は、下記を参考にして各自でアレンジし、ハンズオンに望まれるようお願いします。

### 1. 作業用ディレクトリの作成（何でもよい）

### 2. ベースイメージのダウンロード
wget https://partner-images.canonical.com/core/focal/current/ubuntu-focal-core-cloudimg-amd64-root.tar.gz

### 3. Dockerfileの作成

```
FROM scratch
ADD ubuntu-focal-core-cloudimg-amd64-root.tar.gz /
# verify that the APT lists files do not exist
RUN [ -z "$(apt-get indextargets)" ]
# (see https://bugs.launchpad.net/cloud-images/+bug/1699913)

# a few minor docker-specific tweaks
# see https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap
RUN set -xe \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L40-L48
        && echo '#!/bin/sh' > /usr/sbin/policy-rc.d \
        && echo 'exit 101' >> /usr/sbin/policy-rc.d \
        && chmod +x /usr/sbin/policy-rc.d \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L54-L56
        && dpkg-divert --local --rename --add /sbin/initctl \
        && cp -a /usr/sbin/policy-rc.d /sbin/initctl \
        && sed -i 's/^exit.*/exit 0/' /sbin/initctl \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L71-L78
        && echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L85-L105
        && echo 'DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' > /etc/apt/apt.conf.d/docker-clean \
        && echo 'APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' >> /etc/apt/apt.conf.d/docker-clean \
        && echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";' >> /etc/apt/apt.conf.d/docker-clean \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L109-L115
        && echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/docker-no-languages \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L118-L130
        && echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > /etc/apt/apt.conf.d/docker-gzip-indexes \
        \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L134-L151
        && echo 'Apt::AutoRemove::SuggestsImportant "false";' > /etc/apt/apt.conf.d/docker-autoremove-suggests

# make systemd-detect-virt return "docker"
# See: https://github.com/systemd/systemd/blob/aa0c34279ee40bce2f9681b496922dedbadfca19/src/basic/virt.c#L434
RUN mkdir -p /run/systemd && echo 'docker' > /run/systemd/container

CMD ["/bin/bash"]
```

### 4. Dockerイメージのビルド

```
docker build -t ubuntu2004simple:1 .
```

### 5. コンテナイメージ自身の内部環境構築
* コンテナを起動する

```
docker run -itd --name test-01 ubuntu2004simple:1
```

* コンテナへアクセスする

```
docker exec -it test-01 bash
```

* 利用するアプリのインストール作業
（詳細は省略）

```
adduser ubuntu
gpasswd -a ubuntu sudo
apt update
apt upgrade
apt install -y sudo
apt install -y git
apt install -y vim
apt install -y curl
apt install -y wget
apt install -y jq
apt install -y openssh-server
apt install -y netcat
apt install -y tshark
apt install -y iproute2
apt install -y iputils-ping net-tools dnsutils
apt install -y netplan.io
apt install -y supervisor
mkdir /var/run/sshd
ln -s /usr/bin/python3.8 /usr/bin/python
apt install -y python3-pip
ln -s /usr/bin/pip3 //usr/bin/pip
git clone https://github.com/secdev/scapy.git
apt install -y nodejs npm
npm install n -g
n stable
apt purge -y nodejs npm
exec $SHELL -l
vi /etc/ssh/sshd_config
vi /etc/supervisor/supervisord.conf
```

* コンテナ起動時にSSHを起動する設定
`/etc/supervisor/conf.d/startup.conf`を下記のように作成する。

```
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
```

* Supervisordの管理画面を有効化する設定

```
; supervisor config file

[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0700                       ; sockef file mode (default 0700)

[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/var/log/supervisor            ; ('AUTO' child log dir, default $TEMP)

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket

; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.

[include]
files = /etc/supervisor/conf.d/*.conf

[inet_http_server]
port=0.0.0.0:9001
username=ubuntu
password=************
```

* rootユーザでSSH接続を許可する設定(/etc/ssh/sshd_config)

PermitRootLogin の設定を `yes`に変更する。


* 内部環境構築後のスナップショットを作成する

```
docker commit test-01 testImage-01
```

# 参考資料
https://scapy.readthedocs.io/en/latest/usage.html