Azure VM への UDP 接続では IP フラグメンテーションに気をつけよう！

# はじめに
## この記事に書いてあること
Azure Virtual Machine へ UDP 接続でデータを送るときに、IP フラグメンテーションが発生しないようにデータを送らないとデータが届かないことについてまとめました。

## 何が起きたか？そして何をしたか？
アメリカ東海岸にある Azure VM から東日本にある Azure VM まで、UDP でデータを転送する際のスループットの測定を実施中に、Azure Virtual Network の仕様に起因して通信が未達となりました。最終的に UDP パケットを生成するツール (iPerf3) 側でソケットバッファの長さを調整して対応しました。

# ことの顛末
## やろうとしていた実験
下記の図のように、Azure の東日本リージョンと米国東部リージョンとの間を仮想ネットワークのピアリング機能で接続し、米国東部リージョンにデプロイした Ubuntu 18.04 の仮想マシンから東日本リージョンの Ubuntu 18.04 の仮想マシンまでのデータ転送のスループットを iPerf3 を利用して測定するシナリオです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199379/989be5a4-c3fd-ac05-ee4b-86c02f00e97f.png)

## 発生したこと
以下の iPerf3 のコマンドで、サーバー側（受信側）を東日本リージョンに、クライアント側（送信側）を米国東部リージョンにて起動しました。
```server.sh
iperf3 -s
```
```client_ng.sh
iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output
```
すると、クライアント側の出力でこんな結果が…。
```client_output_001
tokawa@eastus:~$ iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output
Connecting to host 10.35.0.5, port 5201
[  4] local 10.43.0.4 port 44851 connected to 10.35.0.5 port 5201
[ ID] Interval           Transfer     Bandwidth       Total Datagrams
[  4]   0.00-1.00   sec   881 MBytes  7.39 Gbits/sec  112740  (omitted)
[  4]   0.00-1.00   sec  1.01 GBytes  8.70 Gbits/sec  132780
[  4]   1.00-2.00   sec  1.01 GBytes  8.68 Gbits/sec  132390
[  4]   2.00-3.00   sec  1.01 GBytes  8.68 Gbits/sec  132400
[  4]   3.00-4.00   sec  1.01 GBytes  8.67 Gbits/sec  132360
[  4]   4.00-5.00   sec  1.02 GBytes  8.72 Gbits/sec  133050
[  4]   5.00-6.00   sec  1.02 GBytes  8.73 Gbits/sec  133200
[  4]   6.00-7.00   sec  1.01 GBytes  8.71 Gbits/sec  132870
[  4]   7.00-8.00   sec  1.02 GBytes  8.73 Gbits/sec  133160
[  4]   8.00-9.00   sec  1.01 GBytes  8.71 Gbits/sec  132860
[  4]   9.00-10.00  sec  1.01 GBytes  8.70 Gbits/sec  132760
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  4]   0.00-10.00  sec  10.1 GBytes  8.70 Gbits/sec  0.000 ms  18658/-90785 (0%)
[  4] Sent -90785 datagrams

Server output:
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 10.43.0.4, port 38056
[  5] local 10.35.0.5 port 5201 connected to 10.43.0.4 port 44851
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  5]   0.00-1.00   sec  25.8 MBytes   216 Mbits/sec  0.098 ms  18658/21955 (85%)  (omitted)
[  5]   0.00-1.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   1.00-2.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   2.00-3.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   3.00-4.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   4.00-5.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   5.00-6.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   6.00-7.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   7.00-8.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   8.00-9.00   sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]   9.00-10.00  sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
[  5]  10.00-10.15  sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  5]   0.00-10.15  sec  0.00 Bytes  0.00 bits/sec  0.000 ms  0/0 (0%)


iperf Done.
```
後半のセクションが、サーバー側で受信した結果ですが、Transfer と Bandwidth のところを見ると、0.00 Bytes ならびに 0.00bits/sec となっており、データが全く届いていなかったことがわかります。

## なぜこんな結果になったのか
当初全くわからずだったのですけれども、Azure サポートのエース某氏に相談したところ、大きなヒントをもらうことが出来ました。

https://www.syuheiuda.com/?p=4875

上記のドキュメントより「5. UDP でフラグメントが発生した場合」のところに記載のある内容が、大ヒントになっておりました。なるほど UDP 通信で IP fragmentation が発生している可能性があるかも…というわけで、おもむろに tcpdump でパケットを取得。すると予想通りの結果が確認できたわけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199379/e066b3e1-381e-a981-bafd-62636b87db1c.png)
8KB の UDP パケットが 6 つに分割されて送信されており、また Frame #32 で送られているデータ以外は UDP ヘッダーすらついていないパケットとして送られていることがわかります。参照したブログの「TCP でも UDP でもない IP パケットとして届くことになりますが、Azure VNet は TCP / UDP / ICMP しかサポートしていません。」という条件にバッチリ該当してしまったことになりますね。

## 対応策
ここまでわかれば対応策の練りようはあります。パケットが fragmentation されないようにすればいいわけです。
[Azure の仮想マシンならびに Azure 内のネットワークにおける MTU は 1500 byte](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-tcpip-performance-tuning#azure-and-vm-mtu) ですので、そこから IP ヘッダーの 20 byte と UDP ヘッダーの 8byte を引いて、ペイロードが 1472 bytes になるようにすれば OK ということです！

というわけで、iPerf3 のオプションを一つ足して、以下のようにしてみます。
```client_ok.sh
iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output -l 1472
```
こんな感じで、iPerf3 の -l オプションでバッファ長を調整できるようなので、これを使いました。
```iperf3 option
iperf3 -h
~~中略~~
  -l, --len       #[KMG]    length of buffer to read or write
                            (default 128 KB for TCP, 8 KB for UDP)
```
## 結果
結果は以下の通りで、無事に UDP での通信が通るようになりました！
```result_ok
tokawa@eastus:~$ iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output -l 1472
Connecting to host 10.35.0.5, port 5201
[  4] local 10.43.0.4 port 55681 connected to 10.35.0.5 port 5201
[ ID] Interval           Transfer     Bandwidth       Total Datagrams
[  4]   0.00-1.00   sec   447 MBytes  3.75 Gbits/sec  318700  (omitted)
[  4]   0.00-1.00   sec   529 MBytes  4.44 Gbits/sec  376850
[  4]   1.00-2.00   sec   529 MBytes  4.44 Gbits/sec  376980
[  4]   2.00-3.00   sec   529 MBytes  4.44 Gbits/sec  376690
[  4]   3.00-4.00   sec   529 MBytes  4.44 Gbits/sec  376620
[  4]   4.00-5.00   sec   529 MBytes  4.44 Gbits/sec  376840
[  4]   5.00-6.00   sec   529 MBytes  4.43 Gbits/sec  376560
[  4]   6.00-7.00   sec   529 MBytes  4.44 Gbits/sec  377070
[  4]   7.00-8.00   sec   529 MBytes  4.43 Gbits/sec  376590
[  4]   8.00-9.00   sec   528 MBytes  4.43 Gbits/sec  376260
[  4]   9.00-10.00  sec   529 MBytes  4.44 Gbits/sec  376900
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  4]   0.00-10.00  sec  5.16 GBytes  4.44 Gbits/sec  0.001 ms  175481/3767352 (4.7%)
[  4] Sent 3767352 datagrams

Server output:
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 10.43.0.4, port 50088
[  5] local 10.35.0.5 port 5201 connected to 10.43.0.4 port 55681
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  5]   0.00-1.00   sec   350 MBytes  2.94 Gbits/sec  0.001 ms  11590/261006 (4.4%)  (omitted)
[  5]   0.00-1.00   sec   506 MBytes  4.24 Gbits/sec  0.001 ms  16644/376948 (4.4%)
[  5]   1.00-2.00   sec   506 MBytes  4.25 Gbits/sec  0.001 ms  16000/376713 (4.2%)
[  5]   2.00-3.00   sec   506 MBytes  4.24 Gbits/sec  0.001 ms  16559/376708 (4.4%)
[  5]   3.00-4.00   sec   507 MBytes  4.25 Gbits/sec  0.001 ms  15697/376761 (4.2%)
[  5]   4.00-5.00   sec   506 MBytes  4.25 Gbits/sec  0.001 ms  16156/376799 (4.3%)
[  5]   5.00-6.00   sec   507 MBytes  4.25 Gbits/sec  0.001 ms  15312/376542 (4.1%)
[  5]   6.00-7.00   sec   507 MBytes  4.25 Gbits/sec  0.001 ms  15849/377144 (4.2%)
[  5]   7.00-8.00   sec   507 MBytes  4.25 Gbits/sec  0.001 ms  15251/376458 (4.1%)
[  5]   8.00-9.00   sec   506 MBytes  4.25 Gbits/sec  0.001 ms  15832/376361 (4.2%)
[  5]   9.00-10.00  sec   506 MBytes  4.24 Gbits/sec  0.001 ms  16529/376955 (4.4%)
[  5]  10.00-10.15  sec  75.2 MBytes  4.12 Gbits/sec  0.001 ms  4062/57657 (7%)
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  5]   0.00-10.15  sec  0.00 Bytes  0.00 bits/sec  0.001 ms  163891/3825046 (4.3%)


iperf Done.
```

tcpdump の結果を見ても、ちゃんとすべてのパケットに UDP ヘッダーが付与された状態で送出されていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199379/de3ecc5b-be20-7653-68ad-9e4ff1751e1a.png)

# まとめ
ここまでの実験結果で、以下のことがわかりました。
- Azure の仮想マシンや仮想ネットワークは、フラグメント化されたパケットをサポートしていない
- UDP の場合は特に、ペイロードが 1472 bytes 以下になるように調整しないと UDP だけ通信が通らない原因にもなりうる
- iPerf3 を使って Azure VM のネットワークスループットを UDP で測るときには、`-l 1472` オプションをつけるべし

Azure の内部ネットワークのレイテンシーやスループットは結構優秀だと思いますので、IP fragmentation に気を付けつつ、うまく活用していただけたら良いかな…と思います。

# オマケ
## 参考文献
https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-tcpip-performance-tuning

## TCP で米国東部→東日本のスループットを測ってみた
同じ環境で、以下のコマンドを使って TCP 20 並列接続のスループットを測ってみた結果も掲載しておきます。
```TCP_P20.sh
tokawa@eastus:~$ iperf3 -c 10.35.0.5 -b 0 --omit 1 --get-server-output -P 20
```
TCP 接続での、米国東部→東日本へのトータルのスループットとは、1.70 Gbits/sec という結果になりました。秒間 200MB 超のデータを転送できるという結果ですので、大容量のデータを海外からいち早く安く日本に送ってくるような用途におススメできるかもしれません。
```TCP
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[  5]   0.00-10.17  sec  91.8 MBytes  75.7 Mbits/sec                  receiver
[  7]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[  7]   0.00-10.17  sec  97.7 MBytes  80.6 Mbits/sec                  receiver
[  9]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[  9]   0.00-10.17  sec  99.1 MBytes  81.7 Mbits/sec                  receiver
[ 11]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 11]   0.00-10.17  sec  76.8 MBytes  63.3 Mbits/sec                  receiver
[ 13]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 13]   0.00-10.17  sec  94.7 MBytes  78.1 Mbits/sec                  receiver
[ 15]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 15]   0.00-10.17  sec  88.0 MBytes  72.6 Mbits/sec                  receiver
[ 17]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 17]   0.00-10.17  sec   140 MBytes   115 Mbits/sec                  receiver
[ 19]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 19]   0.00-10.17  sec   161 MBytes   132 Mbits/sec                  receiver
[ 21]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 21]   0.00-10.17  sec  95.4 MBytes  78.7 Mbits/sec                  receiver
[ 23]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 23]   0.00-10.17  sec  84.5 MBytes  69.7 Mbits/sec                  receiver
[ 25]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 25]   0.00-10.17  sec   102 MBytes  83.9 Mbits/sec                  receiver
[ 27]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 27]   0.00-10.17  sec  94.5 MBytes  77.9 Mbits/sec                  receiver
[ 29]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 29]   0.00-10.17  sec  76.4 MBytes  63.0 Mbits/sec                  receiver
[ 31]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 31]   0.00-10.17  sec  74.8 MBytes  61.7 Mbits/sec                  receiver
[ 33]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 33]   0.00-10.17  sec   134 MBytes   111 Mbits/sec                  receiver
[ 35]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 35]   0.00-10.17  sec  77.7 MBytes  64.0 Mbits/sec                  receiver
[ 37]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 37]   0.00-10.17  sec   149 MBytes   123 Mbits/sec                  receiver
[ 39]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 39]   0.00-10.17  sec  75.0 MBytes  61.9 Mbits/sec                  receiver
[ 41]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 41]   0.00-10.17  sec   143 MBytes   118 Mbits/sec                  receiver
[ 43]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[ 43]   0.00-10.17  sec   102 MBytes  83.8 Mbits/sec                  receiver
[SUM]   0.00-10.17  sec  0.00 Bytes  0.00 bits/sec                  sender
[SUM]   0.00-10.17  sec  2.01 GBytes  1.70 Gbits/sec                  receiver
```