Raspberry Piでネットワークエミュレータ

# 初めに
[先人](http://dsas.blog.klab.org/archives/raspi-netem1.html)はもちろんいます.
遅延や帯域制限が物理的に簡単にできると仕事に役立つなと思い, 我が家の畑で採れるRaspberryを使ってPiを作りました.
通信の前にアプリケーション側で遅延を入れたりという実装を見かけたりするのですが, ライブラリを作っている側としては足りないなと思います. アプリケーションの実装側としてはそれで足りるのですが, ネットワークはそんなに単純じゃないですよね.

ハードウェアはRaspberry Pi 2 Model Bを使います.

# コンポーネント
## tc
tcコマンドで, qdisc(Queueing Discipline)を設定すると, 遅延や帯域制限ができるらしい.
https://labs.gree.jp/blog/2014/10/11266/

プロトコルとドライバの間に, キューを入れて制御するのかな？

```
+------------------+    +---------------+
| network protocol | -> | device driver |
+------------------+    +---------------+

+------------------+    +-------+    +---------------+
| network protocol | -> | qdisc | -> | device driver |
+------------------+    +-------+    +---------------+
```

tcコマンドを試して, 上手くいかなかった場合は, カーネルモジュールを追加するらしいです.

```
Specified qdisc not found
$ sudo yum install -y kernel-modules-extra
$ sudo modprobe sch_netem
```

状態確認と設定削除. 以降, インターフェイスeth0に何かすると仮定.

```
$ sudo tc qdisc show dev eth0
$ sudo tc qdisc del dev eth0 root
```

### 遅延とパケットロス
queueを追加していく仕組みのため, 新しく何かを追加するときはadd, 既存のキューの設定を変更するときはchangeを使う.

delayで遅延を, 続く時間で揺らぎを設定する. `delay 10ms 1ms`は, 遅延10msで, +-1msの範囲でランダムに遅延が変化するという意味.
lossはパケットロス率.

```
$ sudo tc qdisc add dev eth0 root netem delay 10ms 1ms loss 0.1%
$ sudo tc qdisc change dev eth0 root netem delay 20ms 1ms loss 1%
```

### 帯域制限
limitは送信バッファサイズ, burstサイズまで貯まると送信を開始するらしい. rateは帯域なのでこの値で帯域制限ができる.

```
$ sudo tc qdisc add dev eth0 root tbf limit 256kb burst 32kb rate 64kbit
```

### 遅延と帯域制限
tbfだのnetemだのがでてきたが, ある機能を持ったキューという理解でいいのかな？（よく解っていない）.
キューがツリー構造になるわけだが, 頭を使わないで適当にする.

```
$ sudo tc qdisc add dev eth0 root handle 1: tbf limit 256kb burst 32kb rate 64kb
$ sudo tc qdisc add dev eth0 parent 1: handle 10: netem delay 10ms 1ms loss 0.1%
```
# UI
[先人](http://dsas.blog.klab.org/archives/raspi-netem1.html)は, Raspberry Piを使うなら物理インターフェイスでなければならない, と言っていますが私はヘタレなのでGUIを作ります.

## ncurses
CUIでGUIがしたいので, ncursesで作ります. ライブラリは名前に揺れがあるらしいのでいくつか試します.

```
$ sudo apt install ncurses-devel
$ sudo apt install ncurses-dev
$ sudo apt install libncurses5-dev
```

初期カーソル位置とかいろいろおかしいですが, まあ使えなくはないのでさらしておきますね [GitHub](https://github.com/taqu/gtc).
![gtc00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27065/d5e8ed1e-3a79-39d0-e3a8-4a41341c099f.png)
![gtc01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27065/a52260c4-d8fa-6ca7-fbcc-95aff7eb6463.png)

実際は仮想マシン上のCentOSで開発したのですが, これが罠でした.
実機のRaspberry Pi 2では動作しない. どうやらtcコマンドで何をしてもdelayが274.9sになるらしい. SSHが返ってこないよ.
Raspberry Pi 3を試したりしたが, どうもRaspbian Busterだとtcコマンドが正しく動かないらしい.
作り的にカーネルモジュールとかなんとかしたらいいのでしょうが, そんな知識はないのでJessieにします, あっさり動作する, なんでや.

# 動作確認
こんなかんじに設定して, 動作確認します.

```
$ sudo tc qdisc add dev eth0 root handle 1: tbf limit 256kb burst 32kb rate 64kb
$ sudo tc qdisc add dev eth0 parent 1: handle 10: netem delay 10ms 1ms loss 0.1%
```

## 遅延確認

```
$ sudo ping -c 10 xx.xx.xx.xx
PING xx.xx.xx.xx (xx.xx.xx.xx) 56(84) bytes of data.
64 bytes from xx.xx.xx.xx: icmp_seq=1 ttl=127 time=11.5 ms
...

--- xx.xx.xx.xx ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 43ms
rtt min/avg/max/mdev = 9.682/10.354/11.531/0.634 ms
```

## 帯域制限確認
iperf3を使用します. Windows用バイナリはこちら, https://iperf.fr/iperf-download.php

```
$ iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from xx.xx.xx.xx, port 51847
[  5] local xx.xx.xx.xx port 5201 connected to xx.xx.xx.xx port 51848
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-1.00   sec  28.5 KBytes   233 Kbits/sec
[  5]   1.00-2.00   sec  2.85 KBytes  23.4 Kbits/sec
[  5]   2.00-3.00   sec  14.3 KBytes   117 Kbits/sec
[  5]   3.00-4.00   sec  7.13 KBytes  58.4 Kbits/sec
[  5]   4.00-5.00   sec  7.13 KBytes  58.4 Kbits/sec
[  5]   5.00-6.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   6.00-7.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   7.00-8.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   8.00-9.00   sec  0.00 Bytes  0.00 bits/sec
[  5]   9.00-10.00  sec  0.00 Bytes  0.00 bits/sec
[  5]  10.00-11.00  sec  0.00 Bytes  0.00 bits/sec
[  5]  11.00-12.00  sec  0.00 Bytes  0.00 bits/sec
[  5]  12.00-13.00  sec  0.00 Bytes  0.00 bits/sec
[  5]  13.00-13.15  sec  1.43 KBytes  77.6 Kbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-13.15  sec  0.00 Bytes  0.00 bits/sec    sender
[  5]   0.00-13.15  sec  61.3 KBytes  38.2 Kbits/sec  receiver
```

```
$ iperf3 -c <サーバのIP>
Connecting to host xx.xx.xx.xx, port 5201
[  5] local xx.xx.xx.xx port 50436 connected to xx.xx.xx.xx port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   140 KBytes  1.14 Mbits/sec    5   21.4 KBytes       
[  5]   1.00-2.00   sec  0.00 Bytes  0.00 bits/sec    0   24.2 KBytes       
[  5]   2.00-3.00   sec   125 KBytes  1.03 Mbits/sec    0   38.5 KBytes       
[  5]   3.00-4.01   sec  0.00 Bytes  0.00 bits/sec    0   45.6 KBytes       
[  5]   4.01-5.00   sec  0.00 Bytes  0.00 bits/sec    0   52.8 KBytes       
[  5]   5.00-6.00   sec  0.00 Bytes  0.00 bits/sec    0   52.8 KBytes       
[  5]   6.00-7.00   sec  0.00 Bytes  0.00 bits/sec    0   52.8 KBytes       
[  5]   7.00-8.00   sec  0.00 Bytes  0.00 bits/sec    1   44.2 KBytes       
[  5]   8.00-9.00   sec  0.00 Bytes  0.00 bits/sec    0   34.2 KBytes       
[  5]   9.00-10.00  sec  0.00 Bytes  0.00 bits/sec    0   28.5 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   265 KBytes   217 Kbits/sec    6 sender
[  5]   0.00-10.00  sec  61.3 KBytes  50.2 Kbits/sec      receiver

iperf Done.
```

# ブリッジ設定
ソフトウェアができたので, ネットワーク上のポイント間に挟まって遅延させるハードウェアを設定します.
Raspberry Pi 2にUSB接続のLANアダプタを差し, 経路の途中にいれます. 次のWANやLANは特に意味のない名前です.

```
+-----+     +-------------------+     +-----+
| WAN | <-> |eth0 Raspberry eth1| <-> | LAN |
+-----+     +-------------------+     +-----+
```

IPは適当ですが, こんなかんじに設定します.

```
$ apt install bridge-utils
$ brctl addbr br0
$ ip addr add 192.168.1.11/24 dev br0
$ brctl addif br0 eth0 eth1
$ ip link set eth0 up
$ ip link set eth1 up
$ ip link set br0 up
```

~~ブリッジでもちゃんと遅延するか確認はまだですが, 通信が通るところまで確認済みです.~~
ブリッジに繋がっているインターフェイスそれぞれに対して個別に設定します.

# まとめ
ちゃんとした装置を買うべきだと思いますが, 貧乏プロジェクトというものがこの世にはあります.
幸い私の家ではRaspberryやその他パチモノがたくさん収穫できるので, 作成してみました.
コマンドを覚えたら余計なものは作らなくてもいいのですが, 勉強がてらGUIも作成しました.