Linux ルータにてパケット遅延・ロスを再現する

# 目的

近年、IoT の普及等の影響から、分散型アーキテクチャのエッジコンピューティングが注目を集めている。
エッジコンピューティングでは IoT 端末などのデバイスそのものや、近くに設置されたサーバでデータ処理・分析を行う。
この時、クラウドにデータを送らず、エッジ側でデータの処理・分析を行うためリアルタイム性が高く、負荷が分散されることで通信の遅延も起こりにくいという特徴を持つ。

エッジコンピューティングで求められるリアルタイム性にはデバイス間の物理的な距離が大きく関係する。
しかし、この物理的な距離を実現するために違うロケーションにデバイスを準備することは非効率である。
そこで、本記事では Linux ルータにてパケット遅延/ロスを再現する。
これにより、社内などのローカル空間であってもエッジ-クラウド間など、デバイス間の物理的な距離を再現できる。

# 構成

Linux Machine (Ubuntu 20.04) を 3 台を使用し、下記の構成を作成する。

- edge (192.168.30.0/24)
	- 192.168.30.1
- router
  - 10.100.2.45 (外部疎通用 IP)
  - 192.168.20.254
  - 192.168.30.254
- cloud (192.168.20.0/24)
	- 192.168.20.1

![Linux_router.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735085/a538cb2c-edda-c330-dfbe-e3afc43bf592.png)

# 手順

## iptables-persistent

- 対象: router

iptables に NAT の設定を追加する。
再起動時に iptables  の設定を削除されないようにするために iptables-persistent をインストールする。

下記コマンドで iptables-persistent のインストール可否を確認できる。

```
dpkg -l | grep iptables-persistent
```

iptables-persistent をインストールする。
この時、IPv4, IPv6 の設定は保持させる。

```
sudo apt install iptables-persistent
```

インストール後、iptables-persistent の有無を確認すると、下記のように確認できる。

```
$ dpkg -l | grep iptables-persistent
ii  iptables-persistent             1.0.14ubuntu1                     all          boot-time loader for netfilter rules, iptables plugin
```

## IP フォワード

- 対象: router

既存の IP フォワード設定を確認する。
デフォルトの設定では下記のようにコメントアウトされている。

```
$ grep net.ipv4.ip_forward /etc/sysctl.conf
#net.ipv4.ip_forward=1
```

コメントアウトを解除し、IP フォワードの設定値を変更する。

```
$ grep net.ipv4.ip_forward /etc/sysctl.conf
net.ipv4.ip_forward=1
```

## NAT の設定

- 対象: router

現状の NAT 設定を確認する。

```
$ cat /etc/iptables/rules.v4
# Generated by iptables-save v1.8.4 on Thu Apr 20 10:10:21 2023
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Thu Apr 20 10:10:21 2023
```

下記を実行することで NAT 設定が可能である。
今回は edge, cloud の両方が外部と疎通可能となるように設定している。

```
sudo iptables -t nat -A POSTROUTING -s 192.168.30.0/24 -o eth0 -j MASQUERADE
sudo iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o eth0 -j MASQUERADE
```

## Traffic Control 

### 前確認

- 対象: edge, cloud

ping, iperf3 コマンドで疎通確認をする。

iperf3 コマンドのインストールする。

```
sudo apt install iperf3
```

- 対象: cloud 

```
cloud-host:~$ iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 192.168.30.1, port 38368
[  5] local 192.168.20.1 port 5201 connected to 192.168.30.1 port 38370
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  2.14 GBytes  18.4 Gbits/sec
[  5]   1.00-2.00   sec  2.19 GBytes  18.8 Gbits/sec
[  5]   2.00-3.00   sec  2.26 GBytes  19.4 Gbits/sec
[  5]   3.00-4.00   sec  2.25 GBytes  19.3 Gbits/sec
[  5]   4.00-5.00   sec  2.24 GBytes  19.3 Gbits/sec
[  5]   5.00-6.00   sec  2.29 GBytes  19.6 Gbits/sec
[  5]   6.00-7.00   sec  2.29 GBytes  19.7 Gbits/sec
[  5]   7.00-8.00   sec  2.29 GBytes  19.7 Gbits/sec
[  5]   8.00-9.00   sec  2.27 GBytes  19.5 Gbits/sec
[  5]   9.00-10.00  sec  2.25 GBytes  19.3 Gbits/sec
[  5]  10.00-10.00  sec  2.82 MBytes  18.4 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  22.5 GBytes  19.3 Gbits/sec                  receiver
```

- 対象: edge

現状は Bitrate に約 20G, ping の遅延も特に発生していない状態である。

```
edge-host:~$ sudo iperf3 -c 192.168.20.1
Connecting to host 192.168.20.1, port 5201
[  5] local 192.168.30.1 port 38370 connected to 192.168.20.1 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  2.14 GBytes  18.4 Gbits/sec    0   3.04 MBytes
[  5]   1.00-2.00   sec  2.19 GBytes  18.8 Gbits/sec    0   3.04 MBytes
[  5]   2.00-3.00   sec  2.26 GBytes  19.4 Gbits/sec    0   3.04 MBytes
[  5]   3.00-4.00   sec  2.25 GBytes  19.3 Gbits/sec    0   3.04 MBytes
[  5]   4.00-5.00   sec  2.24 GBytes  19.3 Gbits/sec    0   3.04 MBytes
[  5]   5.00-6.00   sec  2.29 GBytes  19.6 Gbits/sec    0   3.04 MBytes
[  5]   6.00-7.00   sec  2.29 GBytes  19.7 Gbits/sec    0   3.04 MBytes
[  5]   7.00-8.00   sec  2.29 GBytes  19.7 Gbits/sec    0   3.04 MBytes
[  5]   8.00-9.00   sec  2.28 GBytes  19.5 Gbits/sec    0   3.04 MBytes
[  5]   9.00-10.00  sec  2.25 GBytes  19.3 Gbits/sec    0   3.04 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  22.5 GBytes  19.3 Gbits/sec    0             sender
[  5]   0.00-10.00  sec  22.5 GBytes  19.3 Gbits/sec                  receiver

iperf Done.

edge-host:~$ ping  192.168.20.1
PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data.
64 bytes from 192.168.20.1: icmp_seq=1 ttl=63 time=0.615 ms
64 bytes from 192.168.20.1: icmp_seq=2 ttl=63 time=0.408 ms
64 bytes from 192.168.20.1: icmp_seq=3 ttl=63 time=0.445 ms
64 bytes from 192.168.20.1: icmp_seq=4 ttl=63 time=0.489 ms
64 bytes from 192.168.20.1: icmp_seq=5 ttl=63 time=0.484 ms
64 bytes from 192.168.20.1: icmp_seq=6 ttl=63 time=0.409 ms
^C
--- 192.168.20.1 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5104ms
rtt min/avg/max/mdev = 0.408/0.475/0.615/0.070 ms
```

- 対象: router

qdisc とは Queueing discipline のことであり、パケットを一時的に格納し、順番を並べ替えたり、遅延させたり、破棄したりすることができる。

初期状態の qdisc を確認する。

```
$ tc qdisc show
qdisc noqueue 0: dev lo root refcnt 2
qdisc fq_codel 0: dev eth0 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
qdisc fq_codel 0: dev eth1 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
qdisc fq_codel 0: dev eth2 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
```

### レイテンシを大きくする

- 対象: router

インターフェースの情報を確認する。

```
router:~$ ip -c a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 5a:71:6f:5f:5f:e4 brd ff:ff:ff:ff:ff:ff
    inet 10.100.2.45/22 brd 10.100.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5871:6fff:fe5f:5fe4/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 3e:14:39:9a:85:0f brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.254/24 brd 192.168.20.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::3c14:39ff:fe9a:850f/64 scope link
       valid_lft forever preferred_lft forever
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether ae:d0:2b:52:35:40 brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.254/24 brd 192.168.30.255 scope global eth2
       valid_lft forever preferred_lft forever
    inet6 fe80::acd0:2bff:fe52:3540/64 scope link
       valid_lft forever preferred_lft forever
```

100ms のジッターのある 200ms のディレイを挿入する

```
sudo tc qdisc add dev eth1 root netem delay 200ms 100ms
```

上記設定後の qdisc を確認する。

```
$ tc qdisc show
qdisc noqueue 0: dev lo root refcnt 2
qdisc fq_codel 0: dev eth0 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
qdisc netem 8001: dev eth1 root refcnt 2 limit 1000 delay 200.0ms  100.0ms
qdisc fq_codel 0: dev eth2 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
```

- 対象: edge

ping の状況を確認すると遅延していることがわかる。

```
edge-host:~$  ping  192.168.20.1
...
64 bytes from 192.168.20.1: icmp_seq=637 ttl=63 time=0.380 ms
64 bytes from 192.168.20.1: icmp_seq=638 ttl=63 time=0.409 ms
64 bytes from 192.168.20.1: icmp_seq=639 ttl=63 time=0.372 ms
64 bytes from 192.168.20.1: icmp_seq=640 ttl=63 time=0.340 ms
64 bytes from 192.168.20.1: icmp_seq=641 ttl=63 time=0.517 ms
64 bytes from 192.168.20.1: icmp_seq=642 ttl=63 time=205 ms
64 bytes from 192.168.20.1: icmp_seq=643 ttl=63 time=235 ms
64 bytes from 192.168.20.1: icmp_seq=644 ttl=63 time=238 ms
64 bytes from 192.168.20.1: icmp_seq=645 ttl=63 time=181 ms
```

- 対象: router

追加した qdisc を削除する。

```
sudo tc qdisc delete dev eth1 root
```

### パケットロスを生じさせる

- 対象: router

30% のパケットをロスさせる。

```
sudo tc qdisc add dev eth1 root netem loss random 30%
```

- 対象: edge

ping で確認する。
約 30% のパケットがロスしていることがわかる。

```
edge-host:~$ ping  192.168.20.1
...
^C
--- 192.168.20.1 ping statistics ---
100 packets transmitted, 73 received, 27% packet loss, time 101380ms
rtt min/avg/max/mdev = 0.297/0.374/0.526/0.046 ms
```

# まとめ

tc コマンドを利用することで容易にパケットのコントロールが可能である。
今回はパケットの遅延とロスのみを試したが、データの破損やパケットの重複、帯域制限なども実行可能である。
これらを活用することで、本記事の物理的な距離再現以外に再現可能な事象は多く存在する。
例えば、迂回路の検証や通信負荷の検知検証なども同様の手法で再現可能であると考えている。

- 迂回路の検証
  - 作成した迂回路上のインターフェースでパケットロス・遅延を発生させ、設定した迂回路を正しく利用できるか検証
- 通信負荷の検知検証
  - 帯域を制限することで疑似的な通信負荷を再現