mahimahi LinkShellで帯域のエミュレーション

[mahimahi](http://mahimahi.mit.edu/) というネットワークをエミュレーションするツールには、LinkShellという帯域のエミュレーションをするツールが含まれています。

LinkShellを使うとトレースファイルをもとにネットワークの上りと下りの帯域のエミュレーションができます。LinkShellでは動的に変動する帯域や固定された帯域のエミュレーションを行えます。

# 簡単な使い方
早速試してみます。以下のようにファイルを用意すると12Mbpsの帯域をエミュレーションできます。

```12mbps.trace
1
```

帯域の測定には、iperf3を使います。

```sh
$ mm-link 12Mbps.trace 12Mbps.trace -- iperf3 -c 192.168.11.13
Connecting to host 192.168.11.13, port 5201
[  4] local 100.64.0.2 port 40690 connected to 192.168.11.13 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  2.00 MBytes  16.7 Mbits/sec    0    115 KBytes       
[  4]   1.00-2.00   sec  1.80 MBytes  15.1 Mbits/sec    0    185 KBytes       
[  4]   2.00-3.00   sec  1.74 MBytes  14.6 Mbits/sec    0    256 KBytes       
[  4]   3.00-4.00   sec  1.74 MBytes  14.6 Mbits/sec    0    327 KBytes       
[  4]   4.00-5.00   sec  1.80 MBytes  15.1 Mbits/sec    0    397 KBytes       
[  4]   5.00-6.05   sec  1.49 MBytes  12.0 Mbits/sec    0    458 KBytes       
[  4]   6.05-7.00   sec  1.80 MBytes  15.8 Mbits/sec    0    532 KBytes       
[  4]   7.00-8.00   sec  1.74 MBytes  14.6 Mbits/sec    0    601 KBytes       
[  4]   8.00-9.01   sec  1.74 MBytes  14.5 Mbits/sec    0    672 KBytes       
[  4]   9.01-10.00  sec  1.43 MBytes  12.1 Mbits/sec    0    742 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  17.3 MBytes  14.5 Mbits/sec    0             sender
[  4]   0.00-10.00  sec  14.4 MBytes  12.1 Mbits/sec                  receiver

iperf Done.

```

結果は `[  4]   0.00-10.00  sec  14.4 MBytes  12.1 Mbits/sec                  receiver` となっており、おおむね12Mbpsとなっていることが確認できました。


# 原理
LinkShellでは、トレースファイルに基づいてエミュレーションを行います。トレースファイルのそれぞれの行は合計1500バイトのパケットを送信するタイミングを表しています。単位はmsecです。

例えば先ほどの例のように1行に`1` が書かれたトレースファイルがあるとします。

```12mbps.trace
1
```

トレースファイルは最後まで読み込まれたら先頭に戻って繰り返し使用されます。なので、上記のトレースファイルを使用すると、1000msec * 1500byte * 8bit = 12,000,000 = 12Mbps のエミュレーションができます。

また、同じタイムスタンプを複数書くことも可能です。

例えば、以下のように2行に`1`を書くと、24Mbpsのエミュレーションが可能になります。

```24mbps.trace
1
1
```

```sh
$ mm-link 24Mbps.trace 24Mbps.trace -- iperf3 -c 192.168.11.13
Connecting to host 192.168.11.13, port 5201
[  4] local 100.64.0.2 port 32994 connected to 192.168.11.13 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  3.68 MBytes  30.8 Mbits/sec    0    164 KBytes       
[  4]   1.00-2.00   sec  3.48 MBytes  29.2 Mbits/sec    0    305 KBytes       
[  4]   2.00-3.00   sec  3.54 MBytes  29.7 Mbits/sec    0    447 KBytes       
[  4]   3.00-4.00   sec  3.48 MBytes  29.2 Mbits/sec    0    588 KBytes       
[  4]   4.00-5.00   sec  3.29 MBytes  27.6 Mbits/sec    0    730 KBytes       
[  4]   5.00-6.00   sec  2.67 MBytes  22.4 Mbits/sec    0    871 KBytes       
[  4]   6.00-7.00   sec  2.73 MBytes  22.9 Mbits/sec    0   1012 KBytes       
[  4]   7.00-8.00   sec  2.67 MBytes  22.4 Mbits/sec    0   1.13 MBytes       
[  4]   8.00-9.00   sec  2.73 MBytes  22.9 Mbits/sec    0   1.26 MBytes       
[  4]   9.00-10.00  sec  2.67 MBytes  22.4 Mbits/sec    0   1.40 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  31.0 MBytes  26.0 Mbits/sec    0             sender
[  4]   0.00-10.00  sec  29.0 MBytes  24.3 Mbits/sec                  receiver

iperf Done.
```


# オプション
引数無しで `mm-link` を実行するとオプションが確認できます。

```
$ mm-link 
Usage: mm-link UPLINK-TRACE DOWNLINK-TRACE [OPTION]... [COMMAND]

Options = --once
          --uplink-log=FILENAME --downlink-log=FILENAME
          --meter-uplink --meter-uplink-delay
          --meter-downlink --meter-downlink-delay
          --meter-all
          --uplink-queue=QUEUE_TYPE --downlink-queue=QUEUE_TYPE
          --uplink-queue-args=QUEUE_ARGS --downlink-queue-args=QUEUE_ARGS

          QUEUE_TYPE = infinite | droptail | drophead | codel | pie
          QUEUE_ARGS = "NAME=NUMBER[, NAME2=NUMBER2, ...]"
              (with NAME = bytes | packets | target | interval | qdelay_ref | max_burst)
                  target, interval, qdelay_ref, max_burst are in milli-second

Died on std::runtime_error: invalid arguments
```

## `--once`
トレースファイルを1度だけ実行します

## `--uplink-log=FILENAME`
uplinkのログファイルを生成します。

## `downlink-log=FILENAME`
downlinkのログファイルを生成します。

## `--meter-uplink`
このオプションを付けると、uplinkのエミュレーションされた帯域、uplinkで送信しようとしているデータ量、実際に送信しているデータ量をリアルタイムでモニタリングできます。

![uplink.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302126/1a8ee707-5eda-1ac5-51d5-0b456243b343.png)

赤で囲まれた場所がトレースファイルの帯域で、青い線が送信しようとしているデータ量になります。赤い線が実際に送信できているデータ量です

## `--meter-uplink-delay`
uplinkのqueueing delayをリアルタイムでモニタリングできます。

![uplink_queing_delay.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302126/c63dbd34-bb13-4cfe-ffe1-a4222dfdcd4d.png)


## `--meter-downlink` 
このオプションを付けると、downlinkのエミュレーションされた帯域、downlinkで送信しようとしているデータ量、実際に送信しているデータ量をリアルタイムでモニタリングできます。

## `--meter-downlink-delay`
downlinkのqueueing delayをリアルタイムでモニタリングできます。

## `--meter-all` 
`--meter-uplink` `--meter-uplink-delay` `--meter-downlink --meter-downlink-delay` の4つを同時に表示します。

## `--uplink-queue=QUEUE_TYPE`
uplinkのキューの種類を定義します。キューの種類は以下の5種類があります。

```
 QUEUE_TYPE = infinite | droptail | drophead | codel | pie
```

`droptail`はキューのサイズを超えるパケットはキューに入れずに捨てます。逆に、`drophead`の場合は、キューの先頭から要素を捨てます。

`codel`や`pie`は Active Queue Managementの手法のCoDel、PIEをそれぞれ表しています。

## `--uplink-queue-args=QUEUE_ARGS`
`--uplink-queue-args` ではキューの振る舞いを定義します。以下のようなオプションが存在しています。

```
 QUEUE_ARGS = "NAME=NUMBER[, NAME2=NUMBER2, ...]"
              (with NAME = bytes | packets | target | interval | qdelay_ref | max_burst)
                  target, interval, qdelay_ref, max_burst are in milli-second
```

`bytes`、`packets`は、キューの種類を`droptail`、`drophead` としているときに使えます。それぞれバイト長、パケット数でキューの上限を設定します。

`target`、`interval` はCoDel用のオプションのようです。

`qdelay_ref`、`max_burst` はPIE用のオプションのようです。


# 動的な帯域制御の例
LinkShellの特徴の一つに、動的な帯域変更のエミュレーションがあります。

実際に用意されているトレースファイルを https://github.com/ravinet/mahimahi/tree/master/traces から選んでiperf3を実行してみます。aptでインストールした場合は、`/usr/share/mahimahi/traces/` にあります

今回は、`ATT-LTE-driving-2016.up` と `ATT-LTE-driving-2016.down` を使ってみました。

iperf3の結果は以下のようになります

```sh

$ mm-link /usr/share/mahimahi/traces/ATT-LTE-driving-2016.up /usr/share/mahimahi/traces/ATT-LTE-driving-2016.down --meter-uplink -- iperf3 -c 192.168.11.13 -b 6M
Connecting to host 192.168.11.13, port 5201
[  4] local 100.64.0.2 port 40788 connected to 192.168.11.13 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   594 KBytes  4.86 Mbits/sec    1   1.41 KBytes       
[  4]   1.00-2.00   sec   445 KBytes  3.65 Mbits/sec    1   59.4 KBytes       
[  4]   2.00-3.01   sec   573 KBytes  4.67 Mbits/sec    0   62.2 KBytes       
[  4]   3.01-4.00   sec  0.00 Bytes  0.00 bits/sec    2   1.41 KBytes       
[  4]   4.00-5.00   sec  0.00 Bytes  0.00 bits/sec    1   1.41 KBytes       
[  4]   5.00-6.00   sec   382 KBytes  3.13 Mbits/sec    0   70.7 KBytes       
[  4]   6.00-7.00   sec   573 KBytes  4.69 Mbits/sec    0   82.0 KBytes       
[  4]   7.00-8.00   sec   445 KBytes  3.65 Mbits/sec    0   87.7 KBytes       
[  4]   8.00-9.00   sec  63.6 KBytes   521 Kbits/sec    1   87.7 KBytes       
[  4]   9.00-10.00  sec   445 KBytes  3.64 Mbits/sec    0   97.6 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  3.44 MBytes  2.88 Mbits/sec    6             sender
[  4]   0.00-10.00  sec  2.94 MBytes  2.46 Mbits/sec                  receiver

iperf Done.
```

モニタリングの結果は以下のようになりました。実際に動的な帯域のシミュレーションができていることが分かります。

![variable_rate.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302126/c8bda06e-e5e0-5aed-ae96-a5ff414988a5.png)

# 参考

https://github.com/ravinet/mahimahi

http://mahimahi.mit.edu/mahimahi_atc.pdf