Open15

RasPiで作る「不安定なネットワーク」エミュレータの作り方

こーのいけこーのいけ

準備するもの

  • Raspberry Pi 4 (USB3.0が欲しいだけ。スペックは要らないからRAM 2GBモデルで十分)
  • USB 3.0のGigabit Ethernet アダプタ
  • SDカード(多分8GBもあれば十分)
  • 設定時用キーボード
  • 設定時用モニタ

ネットワークをあれこれいじるので念のためにモニタ・キーボードで設定することにした。
結果から言えばしっかり間違いなく設定できれば大丈夫なので /boot/ssh を置いてSSH接続で設定してもいいかも。

こーのいけこーのいけ

OSの準備

  1. Raspberry Pi OS Liteの最新版を適当にSDカードに書き込む
  2. キーボード・モニタを接続して起動してデフォルトアカウントでログイン
  3. sudo raspi-configでLocalisation Optionsからキーボードレイアウトを設定する
  4. ついでにTimeZoneも設定しておく

あとはパスワード変えたり色々設定しておく。

こーのいけこーのいけ

Bridge Networkの準備

(参考:http://nort-wmli.blogspot.com/2019/09/rapberry-pi-bridge-utils.html)

まずは sudo apt update して sudo apt install bridge-utilsとしてbrctlとかを入れる。

/etc/network/interfaces

auto br0
iface br0 inet manual
    bridge_ports eth0 eth1
    bridge_stp off
    bridge_maxwait 3

/etc/dhcpcd.conf

denyinterfaces eth0 eth1

interface br0
static ip_address=192.168.2.3/24
static routers=192.168.2.1
static domain_name_servers=192.168.2.1 8.8.8.8

固定IP関係はそれぞれの環境に応じて設定。
再起動してbr0に指定したIPアドレスが割り振られていて、eth0/eth1に割り振られていなければOK。

こーのいけこーのいけ

tc & tcguiのインストール

設定しやすいようにtcguiを入れてみる。

  1. sudo apt install iproute2 python3-flask git
  2. git clone https://github.com/tum-lkn/tcgui.git
  3. sudo python3 main.py --ip 0.0.0.0 --port 80 で試しに起動
  4. 別マシンからブラウザでアクセスして試してみる
こーのいけこーのいけ

自動起動設定

/etc/rc.localの exit 0 の前に以下を挿入する(最後の&まで忘れずに)

nohup python3 /home/pi/tcgui/main.py --ip 0.0.0.0 --port 80 --regex 'eth[01]' &

ネットワークデバイスのうち、eth0とeth1で設定すればいいっぽい。

eth0をいじるとuplinikが、eth1をいじるとdownlinkがロスしたり遅延したりする・・・らしい(この記事に書いてある)。ラズパイから出ていくパケットにだけ効果があるということかな?

Read only file system 設定

sudo raspi-config して Performance OptionsOverlay File System を有効にしておく。これで電源ぶちっと抜いてオフにしたときの耐性が高くなる。

こーのいけこーのいけ

以下、iperf3とpingを使って調べていくので、外側にサーバ、内側にクライアントを置いておく。構成は大体こんな感じ。今回サーバは適当なLinuxマシン、クライアントはMacBook Proを使用した(MacBook ProはWiFiを切ってUSB LANアダプタで有線接続)。双方にiperf3を入れておく。

下側のネットワークは普通にハブ使ってもよし、直結でも良し。

こーのいけこーのいけ

まずは正常時の確認。

% iperf3 -c 192.168.2.193
Connecting to host 192.168.2.193, port 5201
[  5] local 192.168.2.110 port 52807 connected to 192.168.2.193 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  97.9 MBytes   822 Mbits/sec
[  5]   1.00-2.00   sec   107 MBytes   900 Mbits/sec
[  5]   2.00-3.00   sec   109 MBytes   913 Mbits/sec
[  5]   3.00-4.00   sec   110 MBytes   921 Mbits/sec
[  5]   4.00-5.00   sec   111 MBytes   928 Mbits/sec
[  5]   5.00-6.00   sec   108 MBytes   906 Mbits/sec
[  5]   6.00-7.00   sec   108 MBytes   905 Mbits/sec
[  5]   7.00-8.00   sec   108 MBytes   910 Mbits/sec
[  5]   8.00-9.00   sec   108 MBytes   904 Mbits/sec
[  5]   9.00-10.00  sec   109 MBytes   918 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  1.05 GBytes   903 Mbits/sec                  sender
[  5]   0.00-10.00  sec  1.05 GBytes   901 Mbits/sec                  receiver

iperf Done.
% ping -c 100 -i 0.2 192.168.2.193
PING 192.168.2.193 (192.168.2.193): 56 data bytes
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=0.952 ms
64 bytes from 192.168.2.193: icmp_seq=1 ttl=64 time=1.395 ms
64 bytes from 192.168.2.193: icmp_seq=2 ttl=64 time=1.137 ms
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=1.176 ms
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=1.291 ms
64 bytes from 192.168.2.193: icmp_seq=5 ttl=64 time=0.972 ms
〜〜〜略〜〜〜
64 bytes from 192.168.2.193: icmp_seq=96 ttl=64 time=1.323 ms
64 bytes from 192.168.2.193: icmp_seq=97 ttl=64 time=1.078 ms
64 bytes from 192.168.2.193: icmp_seq=98 ttl=64 time=1.218 ms
64 bytes from 192.168.2.193: icmp_seq=99 ttl=64 time=1.106 ms

--- 192.168.2.193 ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.917/1.223/1.539/0.128 ms

900Mbpsでpingは1ms程度、この程度ではパケロスはほぼ出ない普通なネットワークになっている。

こーのいけこーのいけ

まずは帯域制限をしてみる。Rate のところに値を入れれば良い。ちなみにソースを読んでコメントで分かったのだが「bit」はbit per second、「bps」はbyte per secondらしい。

とりあえず500Mbpsにしてみると・・・

Rateしか入れてないのになぜかDelayとLimitがついてるのが気になる・・・あとで確認しよう。

% iperf3 -c 192.168.2.193
Connecting to host 192.168.2.193, port 5201
[  5] local 192.168.2.110 port 52909 connected to 192.168.2.193 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  35.3 MBytes   296 Mbits/sec
[  5]   1.00-2.00   sec  57.3 MBytes   480 Mbits/sec
[  5]   2.00-3.00   sec  57.3 MBytes   481 Mbits/sec
[  5]   3.00-4.00   sec  57.1 MBytes   479 Mbits/sec
[  5]   4.00-5.00   sec  57.2 MBytes   480 Mbits/sec
[  5]   5.00-6.00   sec  57.2 MBytes   480 Mbits/sec
[  5]   6.00-7.00   sec  57.2 MBytes   480 Mbits/sec
[  5]   7.00-8.00   sec  57.2 MBytes   479 Mbits/sec
[  5]   8.00-9.00   sec  57.1 MBytes   479 Mbits/sec
[  5]   9.00-10.00  sec  57.1 MBytes   479 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec   550 MBytes   461 Mbits/sec                  sender
[  5]   0.00-10.00  sec   549 MBytes   460 Mbits/sec                  receiver

iperf Done.

ちょっと低く出てるのが気になるが制限されていることがわかる。
特にコマンドラインオプションを与えていない場合はクライアント→サーバ方向の通信を測定しているらしい。

なので、eth0の設定を外し、eth1で帯域制限をしてみると・・・

% iperf3 -c 192.168.2.193
Connecting to host 192.168.2.193, port 5201
[  5] local 192.168.2.110 port 52925 connected to 192.168.2.193 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  53.5 MBytes   449 Mbits/sec
[  5]   1.00-2.00   sec  92.4 MBytes   775 Mbits/sec
[  5]   2.00-3.00   sec  92.8 MBytes   779 Mbits/sec
[  5]   3.00-4.00   sec  93.3 MBytes   783 Mbits/sec
[  5]   4.00-5.00   sec  91.8 MBytes   770 Mbits/sec
[  5]   5.00-6.00   sec  95.3 MBytes   799 Mbits/sec
[  5]   6.00-7.00   sec  92.0 MBytes   772 Mbits/sec
[  5]   7.00-8.00   sec  91.0 MBytes   763 Mbits/sec
[  5]   8.00-9.00   sec  90.6 MBytes   760 Mbits/sec
[  5]   9.00-10.00  sec  91.7 MBytes   769 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec   884 MBytes   742 Mbits/sec                  sender
[  5]   0.00-10.00  sec   883 MBytes   741 Mbits/sec                  receiver

iperf Done.

帯域制限が外れていることが確認できる。iperfには -R オプションがあり、これを設定すると計測に使う通信がサーバ→クライアントになる。

% iperf3 -c 192.168.2.193 -R
Connecting to host 192.168.2.193, port 5201
Reverse mode, remote host 192.168.2.193 is sending
[  5] local 192.168.2.110 port 52927 connected to 192.168.2.193 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  33.0 MBytes   277 Mbits/sec
[  5]   1.00-2.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   2.00-3.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   3.00-4.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   4.00-5.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   5.00-6.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   6.00-7.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   7.00-8.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   8.00-9.00   sec  34.2 MBytes   287 Mbits/sec
[  5]   9.00-10.00  sec  34.2 MBytes   287 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   342 MBytes   287 Mbits/sec    0             sender
[  5]   0.00-10.00  sec   341 MBytes   286 Mbits/sec                  receiver

iperf Done.
こーのいけこーのいけ

eth0のDelayを200msに設定してみる。相変わらず表示がおかしいけど設定はされている様子。

iperf3ではDelayは測ってくれないのでpingで見る。

% ping 192.168.2.193 -c 10 -i 0.2
PING 192.168.2.193 (192.168.2.193): 56 data bytes
Request timeout for icmp_seq 0
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=201.231 ms
64 bytes from 192.168.2.193: icmp_seq=1 ttl=64 time=201.311 ms
64 bytes from 192.168.2.193: icmp_seq=2 ttl=64 time=201.285 ms
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=201.131 ms
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=201.233 ms
64 bytes from 192.168.2.193: icmp_seq=5 ttl=64 time=201.291 ms
64 bytes from 192.168.2.193: icmp_seq=6 ttl=64 time=201.263 ms
64 bytes from 192.168.2.193: icmp_seq=7 ttl=64 time=201.277 ms
64 bytes from 192.168.2.193: icmp_seq=8 ttl=64 time=201.098 ms
64 bytes from 192.168.2.193: icmp_seq=9 ttl=64 time=201.269 ms

--- 192.168.2.193 ping statistics ---
10 packets transmitted, 10 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 201.098/201.239/201.311/0.067 ms

こんな感じに均一に200ms程度の遅延が追加されている。右側のボックスはDelayに揺らぎを持たせる設定で、左に200、右に50と入れると

% ping 192.168.2.193 -c 10 -i 0.2
PING 192.168.2.193 (192.168.2.193): 56 data bytes
Request timeout for icmp_seq 0
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=243.310 ms
64 bytes from 192.168.2.193: icmp_seq=1 ttl=64 time=167.944 ms
64 bytes from 192.168.2.193: icmp_seq=2 ttl=64 time=190.028 ms
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=230.964 ms
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=178.245 ms
64 bytes from 192.168.2.193: icmp_seq=5 ttl=64 time=209.384 ms
64 bytes from 192.168.2.193: icmp_seq=6 ttl=64 time=208.928 ms
64 bytes from 192.168.2.193: icmp_seq=7 ttl=64 time=241.856 ms
64 bytes from 192.168.2.193: icmp_seq=8 ttl=64 time=166.389 ms
64 bytes from 192.168.2.193: icmp_seq=9 ttl=64 time=154.431 ms

--- 192.168.2.193 ping statistics ---
10 packets transmitted, 10 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 154.431/199.148/243.310/30.909 ms

こんな風になる。

こーのいけこーのいけ

eth0に30%のパケロスを設定するとこんな感じ

% ping 192.168.2.193 -c 10 -i 0.2
PING 192.168.2.193 (192.168.2.193): 56 data bytes
Request timeout for icmp_seq 0
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=243.310 ms
64 bytes from 192.168.2.193: icmp_seq=1 ttl=64 time=167.944 ms
64 bytes from 192.168.2.193: icmp_seq=2 ttl=64 time=190.028 ms
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=230.964 ms
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=178.245 ms
64 bytes from 192.168.2.193: icmp_seq=5 ttl=64 time=209.384 ms
64 bytes from 192.168.2.193: icmp_seq=6 ttl=64 time=208.928 ms
64 bytes from 192.168.2.193: icmp_seq=7 ttl=64 time=241.856 ms
64 bytes from 192.168.2.193: icmp_seq=8 ttl=64 time=166.389 ms
64 bytes from 192.168.2.193: icmp_seq=9 ttl=64 time=154.431 ms

--- 192.168.2.193 ping statistics ---
10 packets transmitted, 10 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 154.431/199.148/243.310/30.909 ms
kounoikeyuusuke@MacBook-Pro ~ % ping 192.168.2.193 -c 100 -i 0.2
PING 192.168.2.193 (192.168.2.193): 56 data bytes
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=1.046 ms
64 bytes from 192.168.2.193: icmp_seq=1 ttl=64 time=1.234 ms
Request timeout for icmp_seq 2
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=1.101 ms
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=0.979 ms
Request timeout for icmp_seq 5
64 bytes from 192.168.2.193: icmp_seq=6 ttl=64 time=1.129 ms
Request timeout for icmp_seq 7
64 bytes from 192.168.2.193: icmp_seq=8 ttl=64 time=1.215 ms
64 bytes from 192.168.2.193: icmp_seq=9 ttl=64 time=1.128 ms
64 bytes from 192.168.2.193: icmp_seq=10 ttl=64 time=1.212 ms
〜〜〜略〜〜〜
64 bytes from 192.168.2.193: icmp_seq=93 ttl=64 time=1.057 ms
64 bytes from 192.168.2.193: icmp_seq=94 ttl=64 time=1.278 ms
64 bytes from 192.168.2.193: icmp_seq=95 ttl=64 time=1.297 ms
64 bytes from 192.168.2.193: icmp_seq=96 ttl=64 time=1.233 ms
64 bytes from 192.168.2.193: icmp_seq=97 ttl=64 time=0.976 ms
64 bytes from 192.168.2.193: icmp_seq=98 ttl=64 time=1.249 ms
64 bytes from 192.168.2.193: icmp_seq=99 ttl=64 time=0.980 ms

--- 192.168.2.193 ping statistics ---
100 packets transmitted, 71 packets received, 29.0% packet loss
round-trip min/avg/max/stddev = 0.771/1.107/1.297/0.122 ms

iperf3でも-uオプションをつけてUDP通信させるとわかりやすい。

% iperf3 -c 192.168.2.193 -u
Connecting to host 192.168.2.193, port 5201
[  5] local 192.168.2.110 port 54707 connected to 192.168.2.193 port 5201
[ ID] Interval           Transfer     Bitrate         Total Datagrams
[  5]   0.00-1.00   sec   129 KBytes  1.05 Mbits/sec  91
[  5]   1.00-2.00   sec   127 KBytes  1.04 Mbits/sec  90
[  5]   2.00-3.00   sec   129 KBytes  1.05 Mbits/sec  91
[  5]   3.00-4.00   sec   129 KBytes  1.05 Mbits/sec  91
[  5]   4.00-5.00   sec   127 KBytes  1.04 Mbits/sec  90
[  5]   5.00-6.00   sec   129 KBytes  1.05 Mbits/sec  91
[  5]   6.00-7.00   sec   127 KBytes  1.04 Mbits/sec  90
[  5]   7.00-8.00   sec   129 KBytes  1.05 Mbits/sec  91
[  5]   8.00-9.00   sec   127 KBytes  1.04 Mbits/sec  90
[  5]   9.00-10.00  sec   129 KBytes  1.05 Mbits/sec  91
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-10.00  sec  1.25 MBytes  1.05 Mbits/sec  0.000 ms  0/906 (0%)  sender
[  5]   0.00-10.00  sec   884 KBytes   724 Kbits/sec  0.031 ms  281/906 (31%)  receiver

receiver側がパケットロスしているので、サーバ側から見た結果を報告しているのかな?

左側だけを使うと均一な確率でパケットロスする。右側の入力を使うと「一度発生するとその後も連続して発生しやすい」ような状況を作ることが出来る。詳しくはこのへん参照。

こーのいけこーのいけ

Duplicateでパケットが重複するようにも出来る。30% Duplicateするようにしてこんな感じ。

% ping 192.168.2.193 -c 100 -i 0.2
PING 192.168.2.193 (192.168.2.193): 56 data bytes
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=1.018 ms
64 bytes from 192.168.2.193: icmp_seq=0 ttl=64 time=1.033 ms (DUP!)
64 bytes from 192.168.2.193: icmp_seq=1 ttl=64 time=1.241 ms
64 bytes from 192.168.2.193: icmp_seq=2 ttl=64 time=0.935 ms
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=1.278 ms
64 bytes from 192.168.2.193: icmp_seq=3 ttl=64 time=1.301 ms (DUP!)
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=1.268 ms
64 bytes from 192.168.2.193: icmp_seq=4 ttl=64 time=1.292 ms (DUP!)
64 bytes from 192.168.2.193: icmp_seq=5 ttl=64 time=1.150 ms
64 bytes from 192.168.2.193: icmp_seq=5 ttl=64 time=1.174 ms (DUP!)
64 bytes from 192.168.2.193: icmp_seq=6 ttl=64 time=1.102 ms
64 bytes from 192.168.2.193: icmp_seq=7 ttl=64 time=1.033 ms
64 bytes from 192.168.2.193: icmp_seq=8 ttl=64 time=1.243 ms
64 bytes from 192.168.2.193: icmp_seq=9 ttl=64 time=1.261 ms
64 bytes from 192.168.2.193: icmp_seq=10 ttl=64 time=0.939 ms
64 bytes from 192.168.2.193: icmp_seq=10 ttl=64 time=0.962 ms (DUP!)
〜〜〜略〜〜〜
64 bytes from 192.168.2.193: icmp_seq=94 ttl=64 time=0.815 ms
64 bytes from 192.168.2.193: icmp_seq=95 ttl=64 time=1.255 ms
64 bytes from 192.168.2.193: icmp_seq=96 ttl=64 time=1.312 ms
64 bytes from 192.168.2.193: icmp_seq=96 ttl=64 time=1.336 ms (DUP!)
64 bytes from 192.168.2.193: icmp_seq=97 ttl=64 time=1.095 ms
64 bytes from 192.168.2.193: icmp_seq=98 ttl=64 time=1.128 ms
64 bytes from 192.168.2.193: icmp_seq=99 ttl=64 time=1.298 ms

--- 192.168.2.193 ping statistics ---
100 packets transmitted, 100 packets received, +32 duplicates, 0.0% packet loss
round-trip min/avg/max/stddev = 0.707/1.129/1.390/0.156 ms
こーのいけこーのいけ

残りの設定項目は説明だけで。Reorderは一定確率でパケットの順序が入れ替わる。Corruptは一定確率でパケットの中身が壊れる(ビットが反転する)。Limitは・・・ちょっと良く分かってないので後で調べる。

こーのいけこーのいけ

とりあえず有線で欲しかったものは出来た。ただ、せっかくのRasPi4なんだしhostapdを使ってWiFiにも同じ機能を作っても良いかもしれない。WiFi対応すればスマホクライアントにも同じことが出来るようになるはず。

こーのいけこーのいけ

tcguiで247.9sって出るのが気持ち悪くてUbuntu 20.04 64bitで設定してみた。

ブリッジの作成

以下を/etc/netplan/99-cloud-init.yamlに記載

network:
    version: 2
    ethernets:
        eth0:
            dhcp4: false
            dhcp6: false
        eth1:
            dhcp4: false
            dhcp6: false
    bridges:
        br0:
            interfaces: [eth0, eth1]
            dhcp4: true
            dhcp6: true
            optional: true

なんとなくDHCPにしたのでIPアドレスは起動時に見るか、avahi入れて名前でアクセスするかのどっちかにしないとならない。固定IPにするならこの辺を参考にして設定する。

自動起動の設定

/etc/systemd/system/tcgui.serviceに下記を記載

[Unit]
Description=TC Web GUI
After=network.target

[Service]
WorkingDirectory=/home/ubuntu/tcgui
ExecStart=/usr/bin/python3 main.py --ip 0.0.0.0 --port 80 --regext eth[01]

[Install]
WantedBy=multi-user.target

systemctl enable tcgui で有効化する。

Read Only File Systemの設定

後で調べる