Dockerネットワークで遊ぶ_その5_ルーター準備編

## はじめに

ここまで（[その1](https://qiita.com/t13801206/items/0fd95e85377098f996b3)、[その4](https://qiita.com/t13801206/items/478493c1527cbcf0511c)）、**同一ネットワークに属するサーバクライアント間の通信**を試してきた
※ネットワークはDockerネットワーク

今回は新たに**ネットワークを１つ追加**作成し
**２つのネットワークに接続するルーター**と
**新規ネットワークにだけ属する孤独なクライアント**を作成する

そして２つのネットワーク間での通信を実現する

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/7a898c77-9f3c-ab8e-5696-0fa688d33124.png)

結構長くなったので、**IPフォワード無効化**や**静的ルーティング**などの事前準備と
PINGの応答確認まで行う

### 連載情報

連載|内容
---|---
[その１](https://qiita.com/t13801206/items/0fd95e85377098f996b3)|〈実践〉同じセグメント内のPING疎通確認
[その2](https://qiita.com/t13801206/items/0858f2af9c57a9db65aa)|〈コード〉イーサネットヘッダの中身をみてみる
[その3](https://qiita.com/t13801206/items/81459f9374bffcd293ec)|〈コード〉ARP,IP,ICMPの中身をみてみる
[その4](https://qiita.com/t13801206/items/478493c1527cbcf0511c)|〈実践〉IP,TCPをキャプチャする
[その5](https://qiita.com/t13801206/items/7fa2f7bf75021899e05e)|〈実践）ルーターを準備する
[その6](https://qiita.com/t13801206/items/0aa1d0f2b762d013e9f8)|〈実践〉別セグメントのWEBへアクセスをする

### 状況整理

- testnw `172.18.0.0/16`
    - Server1
    - Client1
    - Router1 **new!!**
- testnw2 `172.19.0.0/16` **new!!**
    - Router1 **new!!**
    - Client2 **new!!**

いずれも`ubuntu:latest`イメージを使用

:::note warn
各マシンに固定IPを振っていない。コンテナを起動する順番ごとにIPアドレスの若番が振られる。
:::

## 環境構築

### ネットワーク新規追加

新規ネットワークを作成する。ネットワークの名前は`testnw2`とする。

```powershell
docker network create --driver bridge testnw2
docker network ls
```

### Linuxマシン２台追加

- ルーターマシンとクライアントを１台ずつ立ち上げる
- ルーティングテーブルを変更するので、特権`--privileged`オプションを付ける

```powershell
docker container run -itd --privileged --mount type=bind,source=/d/200_Study/2304_Router_Jisaku,target=/app --net testnw2 --hostname Router1 --name myrouter ubuntu:latest
docker container run -itd --privileged --mount type=bind,source=/d/200_Study/2304_Router_Jisaku,target=/app --net testnw2 --hostname Client2 --name myclient2 ubuntu:latest
docker container ls
```

### ルーターマシンを２つのネットワークに所属させる

ルーターマシンは既存のネットワーク`testnw`にも所属させる
NICが２つある想定

```powershell
docker network connect testnw myrouter
```

### ネットワークと所属するマシンの確認

`testnw`には既存のサーバ・クライアントと、新規のルーターがある
作った順番にIPアドレスの若い番号が振られている

```json:docker network inspect testnw（一部抜粋）
[
    {
        "Name": "testnw",
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        "Containers": {
                "Name": "myserver",
                "IPv4Address": "172.18.0.2/16",
                "Name": "myrouter",
                "IPv4Address": "172.18.0.4/16",
                "Name": "myclient",
                "IPv4Address": "172.18.0.3/16",
            }
    }
]
```

新たに作成した`testnw2`にはルーターマシンとクライアント2号機が所属している

```json:docker network inspect testnw2（一部抜粋）
[
    {
        "Name": "testnw2",
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        "Containers": {
                "Name": "myclient2",
                "IPv4Address": "172.19.0.3/16",
                "Name": "myrouter",
                "IPv4Address": "172.19.0.2/16",
            }
    }
]
```

## 疎通確認

別ネットワークに所属するクライアント同士で PINGを送る

まずはネットワーク系の必要なパッケージをインストールする
このあたりは、[その1](https://qiita.com/t13801206/items/0fd95e85377098f996b3)と同じ

```powershell
docker container exec -it myclient2 /bin/bash

apt update
apt install -y iputils-ping net-tools traceroute ncat
```

プログラミング用のパッケージもインストールする
ルーターでは`/proc/sys/net/ipv4/ip_forward`も書き換えるので、`sudo`を追加する

```powershell
apt install -y sudo
apt install -y vim make build-essential
```

### PING結果

- `ping Client1`も逆の`ping Client2`も**不通**
  - 別ネットワークに所属するクライアントなので通信できない
- `ping Router1`はクライアント1号2号どちらからも応答あり
  - ルーターは両方のネットワークに所属するので、どのクライアントからも通信可能

ルーターから各クライアントにPINGを送った結果

`testnw`内、ルーターマシンからクライアント１号機へ
```Powershell
root@Router1: ping Client1
PING Client1 (172.18.0.3) 56(84) bytes of data.
64 bytes from myclient.testnw (172.18.0.3): icmp_seq=1 ttl=64 time=7.29 ms
```

`testnw2`内、ルーターマシンからクライアント２号機へ

```Powershell
root@Router1: ping Client2
PING Client2 (172.19.0.3) 56(84) bytes of data.
64 bytes from myclient2.testnw2 (172.19.0.3): icmp_seq=1 ttl=64 time=0.810 ms
```

## Linux標準のIPフォワード機能

`ip_forward`が **1** の場合は、ルーターマシンに２つ備わるNIC間でパケット転送をしてしまう

今回の検証では、ルータープログラムを使ってその転送機能を実現することが目的なので
Linux標準のIPフォワード機能を無効にする必要がある

そのために（検証の妨げにならないために）、`ip_forward`を 0 にする必要がある（本書より）

### IPフォワード設定を変更する（標準機能の無効化）

ルーターマシンでデフォルトの設定を確認する
デフォルトの設定は 1 になっている

```bash:cat /proc/sys/net/ipv4/ip_forward
1
```

`sudo vi /proc/sys/net/ipv4/ip_forward`で0に書き換えようとしてもできないLinux初心者

[こちらのサイト](https://software.fujitsu.com/jp/manual/manualfiles/M060001/J2S19710/01Z2A/sft05/sft00179.html)によると、`/etc/sysctl.conf`の`net.ipv4.ip_forward = $value`を変更するらしい

#### sysctlコンフィグを変更する

変更前は `#` でコメントアウトされている

```bash:cat /etc/sysctl.conf | grep ip_forward
#net.ipv4.ip_forward=1
```
`vi /etc/sysctl.conf`で該当行の行頭のコメント用 # を外し、右辺を0にする

```bash:cat /etc/sysctl.conf | grep ip_forward
net.ipv4.ip_forward=0
```

最後に `sysctl -p` で変更を反映し、`cat`で確認する

```bash:cat /proc/sys/net/ipv4/ip_forward
0
```

### IPフォワードが有効になっていると

`ltest`でパケットキャプチャすると、eth0->eth1へデータが流れていることがわかる
本来、ルーターとしての機能をまだ有効化していないので、片方のNICから流れてきたデータについては、もう片方のNICは関与しないはず

## 静的ルーティングを変更する

**クライアント2号 ⇒ ルーター ⇒ クライアント1号**へPINGを通すために
クライアントのゲートウェイを変更する

具体的には、172.18.0.0/16宛てのパケットは、ルーターマシンに渡す
（dockerではデフルトゲートウェイをの初期値がそのネットワークの `x.x.x.1` になっている？）

```bash:netstat -r
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         172.19.0.1      0.0.0.0         UG        0 0          0 eth0
172.19.0.0      0.0.0.0         255.255.0.0     U         0 0          0 eth0
```

下記コマンドで 172.18.0.0/16　ネットワークへの通信をルーターマシンに向ける

`sudo route add -net 172.18.0.0 gw 172.19.0.2 netmask 255.255.0.0 eth0`

```bash
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         172.19.0.1      0.0.0.0         UG        0 0          0 eth0
172.18.0.0      myrouter.testnw 255.255.0.0     UG        0 0          0 eth0
172.19.0.0      0.0.0.0         255.255.0.0     U         0 0          0 eth0
```

### 別ネットワークのクライアントへのルート確認

ルーターマシン(172.19.0.2)を経由しようとしていることがわかる

```bash:traceroute -I 172.18.0.2
traceroute to 172.18.0.2 (172.18.0.2), 30 hops max, 60 byte packets
 1  myrouter.testnw2 (172.19.0.2)  0.534 ms  0.032 ms  0.018 ms
 2  * * *
```

が、クライアントまでは到着しない。。。

### ICMP応答（設定が足りていないケース）

受信側のパケットをキャプチャしたところ、ICMPの応答がデフォルトゲートウェイ`172.18.0.1`を向いている
（`arp -a`の結果、「172.18.0.1 at 02:42:2c:99:3a:c2 [ether] on eth0」とのこと）

```bash
ether_header----------------------------
ether_dhost=02:42:2c:99:3a:c2　！！！！！！！！！！！
ether_shost=02:42:ac:12:00:03
ether_type=800(IP)
ip--------------------------------------
version=4,ihl=5,tos=0,tot_len=84,id=921
frag_off=0,0,ttl=64,protocol=1(ICMP),check=e51e
saddr=172.18.0.3,daddr=172.19.0.3
icmp------------------------------------
icmp_type=0(Echo Reply),icmp_code=0,icmp_cksum=51281
icmp_id=27,icmp_seq=1
```

「！！！！」の行のとおり、ICMP応答の宛先`dhost`がルーターマシンを向いていないので
送信側クライアントと同様に、受信側のクライアントにもスタティックルートを設定する

#### ICMPを応答する側のマシンにもルーティング情報を追加

dockerデフォルトのままでは、`x.x.x.1`がゲートウェイになっているので
`sudo route add -net 172.19.0.0 gw 172.18.0.2 netmask 255.255.0.0 eth0`
で設定を追加する

```bash:netstat -r
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         172.18.0.1      0.0.0.0         UG        0 0          0 eth0
172.18.0.0      0.0.0.0         255.255.0.0     U         0 0          0 eth0
172.19.0.0      myrouter.testnw 255.255.0.0     UG        0 0          0 eth0
```

`172.19.0.0`宛はルーターマシンに送られるようになった

### ICMP応答（OK）

```bash
ether_header----------------------------
ether_dhost=02:42:ac:12:00:02
ether_shost=02:42:ac:12:00:03
ether_type=800(IP)
ip--------------------------------------
version=4,ihl=5,tos=0,tot_len=84,id=50845
frag_off=0,0,ttl=64,protocol=1(ICMP),check=e05b
saddr=172.18.0.3,daddr=172.19.0.3
icmp------------------------------------
icmp_type=0(Echo Reply),icmp_code=0,icmp_cksum=58385
icmp_id=32,icmp_seq=1
```

## 実際に動いている様子

一番右の窓から左の窓へPINGを打つ
真ん中のルーターマシンが `.Src/Router/router` を実行していないとPINGが相手に届かない

![Animation1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/7c686e4c-555a-d8d5-2876-a6d6c96d329b.gif)

## 次回、WEBアクセス編

PINGが通れば、その上のレイヤも難なくいけるだろう
と思っていたが、なかなか苦戦したＷＥＢアクセス

## 参考

https://qiita.com/BooookStore/items/5862515209a31658f88c

- [複数のDockerネットワークを使った基本的な構成例を3つご紹介](https://www.memotansu.jp/docker/865/)
- [Linux IP forwarding – How to Disable/Enable using net.ipv4.ip_forward](https://linuxconfig.org/how-to-turn-on-off-ip-forwarding-in-linux)

## 糸冬了！！