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


初めに

先人はもちろんいます.
遅延や帯域制限が物理的に簡単にできると仕事に役立つなと思い, 我が家の畑で採れる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

先人は, Raspberry Piを使うなら物理インターフェイスでなければならない, と言っていますが私はヘタレなのでGUIを作ります.

ncurses

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

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

初期カーソル位置とかいろいろおかしいですが, まあ使えなくはないのでさらしておきますね GitHub.

実際は仮想マシン上の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も作成しました.