Network Namespace を使用してネットワークに触れてみる

# Network Namespaceとは？

![スクリーンショット 2023-01-15 15.22.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536974/e397d17b-cf8a-40b0-61af-30835f998591.png)


* ネットワーク的にシステムから独立した環境の作成が出来る
* IPに関する処理を1台のLinux内で複数に分割することが出来る
* Dockerのようなコンテナ型仮想技術を構成する要素の一つ

# 環境

仮想マシンとしてUbuntuをインストールして、ubuntu上で検証します

# Network Namespaceを使ってみる

```
$ sudo ip netns add practice
$ ip netns list
practice
```

`practice`という名前のNetwork Namespaceを作成します

![スクリーンショット 2023-01-15 15.24.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536974/d0b25bf9-550f-c908-4e47-3504576680c0.png)

`ip netns list`では、作成されたNetwork Namespaceの確認をします

## ネットワークインターフェイスと、ルーティングテーブルの確認

```Network Namespace.
$ sudo ip netns exec practice ip address show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```

先ほど作成したNetwork Namespace環境で`$ ip address show`コマンドの実行しています
1つのネットワークインターフェース（1: lo）が表示されています

今度は、Network Namespaceを使用せずに実行してみます

```
$ ip address show
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: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:d9:74:4e:9b:d3 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 82340sec preferred_lft 82340sec
    inet6 fe80::d9:74ff:fe4e:9bd3/64 scope link
       valid_lft forever preferred_lft forever
```

表示される内容は変わり、こちらの環境ではネットワークインターフェース（2: enp0s3）が表示されました

今度はルーティングテーブルの確認をします

（Network Namespaceを使用しない場合）

```
ip route show
default via 10.0.2.2 dev enp0s3 proto dhcp src 10.0.2.15 metric 100
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
10.0.2.2 dev enp0s3 proto dhcp scope link src 10.0.2.15 metric 100
```

続いて、

（Network Namespace環境の場合）

```Network Namespace.
$ sudo ip netns exec practice ip route show
Error: ipv4: FIB table does not exist.
Dump terminated
```

このエラーから、ルーティングテーブルが存在しないことが確認できます
Network Namespaceへルーティングエントリを登録していないためです

# 実際に繋いでみる

![スクリーンショット 2023-01-15 17.15.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536974/d7901f72-2e7f-012d-0c58-2abbddd2355f.png)


2つのNetwork Namespaceを繋いでみます。
仮想的なネットワーク環境が２つあるようにイメージします。そのネットワークを仮想的なLANケーブルで繋いでみます。

まずは、それぞれのネットワークを作成します

```
$ sudo ip netns add practice1
$ sudo ip netns add practice2
$ sudo ip netns list
practice2
practice1
```

作成したネットワーク同士を繋ぎます

```
$ sudo ip link add practice1-veth0 type veth peer name practice2-veth0
$ ip link show | grep veth
3: practice2-veth0@practice1-veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
4: practice1-veth0@practice2-veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
```

`practice1-veth0`、`practice2-veth0`は作成するネットワークインターフェイスの名前です。
`ip link show | grep veth`から無事作成できていることが確認できます

しかし、現状は作成したvethインターフェイスはNetwork Namespaceで使用できないため使えるようにしていきます
そのためにシステム領域 → Network Namespace領域へ移す必要があります

```
$ sudo ip link set practice1-veth0 netns practice1
$ sudo ip link set practice2-veth0 netns practice2
```

これで、vethインターフェイスがNetwork Namespaceで使用できるようになりました

```
$ ip link show | grep veth
```

システム側ではもう確認できないことが分かります
Network Namespace側で確認してみます

```
$ sudo ip netns exec practice1 ip link show | grep veth
4: practice1-veth0@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
$ sudo ip netns exec practice2 ip link show | grep veth
3: practice2-veth0@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
```

これで、Network Namespace同士が仮想的なLANケーブルにつながりました。

しかし、これだけでは通信が出来ないので設定を行なっていきます

# 通信の設定

IPを使用して通信を行うためにvethインターフェイスにIPアドレスを付与します

```
$ sudo ip netns exec practice1 ip address add 192.0.2.1/24 dev practice1-veth0
$ sudo ip netns exec practice2 ip address add 192.0.2.2/24 dev practice2-veth0
```

これでIPアドレスの付与が出来ました
これで、通信ができる？

```
$ sudo ip netns exec practice1 ip link show practice1-veth0 | grep state
4: practice1-veth0@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
$ sudo ip netns exec practice2 ip link show practice2-veth0 | grep state
3: practice2-veth0@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
```

まだ出来ないみたいです。
ネットワークインタフェースが`state DOWN`になっています。
ネットワークインターフェースは初期状態ではDOWNになっていて通信が出来ないようです

ネットワークインターフェースのリンクを`UP`へ変更し、通信ができるようにします

```
$ sudo ip netns exec practice1 ip link set practice1-veth0 up
$ sudo ip netns exec practice2 ip link set practice2-veth0 up
```

ここまで来れば、pingコマンドで疎通の確認をしてみます

```
$ sudo ip netns exec practice1 ping -c 3 192.0.2.2
PING 192.0.2.2 (192.0.2.2) 56(84) bytes of data.
64 bytes from 192.0.2.2: icmp_seq=1 ttl=64 time=2.19 ms
64 bytes from 192.0.2.2: icmp_seq=2 ttl=64 time=0.056 ms
64 bytes from 192.0.2.2: icmp_seq=3 ttl=64 time=0.054 ms

--- 192.0.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2022ms
rtt min/avg/max/mdev = 0.054/0.765/2.185/1.004 ms
```

これで、TCP/IP環境が出来上がりました
practice1 → practice2 へpingで疎通確認を行なってみます

```
$ sudo ip netns exec practice1 ping -c 3 192.0.2.2 -I 192.0.2.1
PING 192.0.2.2 (192.0.2.2) from 192.0.2.1 : 56(84) bytes of data.
64 bytes from 192.0.2.2: icmp_seq=1 ttl=64 time=0.033 ms
64 bytes from 192.0.2.2: icmp_seq=2 ttl=64 time=0.044 ms
64 bytes from 192.0.2.2: icmp_seq=3 ttl=64 time=0.043 ms

--- 192.0.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2008ms
rtt min/avg/max/mdev = 0.033/0.040/0.044/0.005 ms
```

`-I`オプションの後の、`192.0.2.1`が送信元のIPアドレスです。
返答がある事を確認できました

# ルータは必要ないのか？

ここまでの構築では　ルータを使用することなくIPを使ったネットワークが構築出来ました。
ルータ無しでなぜ通信が出来たのか？

それは、**IPで構築されたネットワークのセグメント**が関係しています

同じセグメントに属するIP同士はルータがなくても互いに通信を行うことが出来ます

セグメント・・・ネットワーク部とホスト部を識別するためにあります。
例えば、`192.0.2.1/24`のIPアドレスの場合は`192.0.2.0`がネットワークアドレスになります
ネットワーク部（`192.0.2`）が同じIPアドレスは、同じセグメントに属しているということになります

先ほど構築したvethインターフェイスを例にとってみます。

![スクリーンショット 2023-01-27 0.51.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536974/92bcdf9c-6b75-802d-06c9-b1f8a80e4666.png)

`/24`。つまり、先頭から24bitなので赤字の部分がネットワーク部になります。

両方に同じセグメントのIPが振られているのでルータがなくても通信が可能になります

# 実際にルータを入れてみる

![スクリーンショット 2023-01-27 1.28.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536974/903bc339-c3d5-1732-97f4-84c0949c4cd8.png)

ルータを使用したネットワークを作成します。
ルータがあるということは、セグメントが２つになります

構成図は以下です。

![スクリーンショット 2023-01-28 8.43.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536974/fa2f932d-5626-27f1-9f14-7b23cd392035.png)


ルータを合わせたNetwork namespaceが1つ増えて3つになりました。

ここでは、セグメントを跨いだ通信をします。
つまり、間にルータがある状態でpractice1とpractice2の間をパケット通信してみます

## 実際にネットワークを作っていく

最初に作成したネットワークをまずは消しておきます

```
$ sudo ip list
practice1
practice2
```

listで確認してから先ほど作成したnetwork namespaceを全て削除します

```
$ sudo ip --all netns delete
```

ルータも含めた3つのnetwork namespace を作成します

```
$ sudo ip netns add practice1
$ sudo ip netns add practice2
$ sudo ip netns add router
$ sudo ip netns list
router
practice2
practice1
```

続いて、インターフェースを作成します

| セグメント | practice | ルータ側 |
| :--- | :---: | ---: |
| 1 | practice1-veth0 | gw-veth0 |
| 2 | practice2-veth0 | gw-veth1 |


```
$ sudo ip link add practice1-veth0 type veth peer name gw-veth0
$ sudo ip link add practice2-veth0 type veth peer name gw-veth1
```

それぞれのNetWork Namespace へインターフェースを所属させて、UPします

|  NetWork Namespace  |  インターフェース  |
| ---- | ---- |
|  practice1  |  practice1-veth0  |
|  router  |  gw-veth0 &  gw-veth1 |
|  practice2  |  practice2-veth0  |

```
$ sudo ip link set practice1-veth0 netns practice1
$ sudo ip link set gw-veth0 netns router
$ sudo ip link set gw-veth1 netns router
$ sudo ip link set practice2-veth0 netns practice2

vagrant@ubuntu-focal:~$ sudo ip netns exec practice1 ip link set practice1-veth0 up
vagrant@ubuntu-focal:~$ sudo ip netns exec router ip link set gw-veth0 up
vagrant@ubuntu-focal:~$ sudo ip netns exec router ip link set gw-veth1 up
vagrant@ubuntu-focal:~$ sudo ip netns exec practice2 ip link set practice2-veth0 up
```

続いて、IPアドレスの設定を行います

|Network Namespace|  IPアドレス  |  インターフェース  |
| ---- | ---- | ---- |
|practice1|  192.0.2.1/24  |  practice1-veth0  |
|router|  192.0.2.254/24  |  gw-veth0  |
|router|  192.51.100.254/24  |  gw-veth1  |
|practice2|  192.51.100.1/24  |  practice2-veth0  |

practice1とrouter間の設定を行います。
それぞれのインターフェースに同セグメントのIPを当てます

```
$ sudo ip netns exec practice1 ip address add 192.0.2.1/24 dev practice1-veth0
$ sudo ip netns exec router ip address add 192.0.2.254/24 dev gw-veth0

$ sudo ip netns exec router ip address add 192.51.100.254/24 dev gw-veth1
$ sudo ip netns exec practice2 ip address add 192.51.100.1/24 dev practice2-veth0
```

ここまでできれば、practice1 - router 間の疎通確認を行います

```
$ sudo ip netns exec practice1 ping -c 3 192.0.2.254 -I 192.0.2.1
PING 192.0.2.254 (192.0.2.254) from 192.0.2.1 : 56(84) bytes of data.
64 bytes from 192.0.2.254: icmp_seq=1 ttl=64 time=2.78 ms
64 bytes from 192.0.2.254: icmp_seq=2 ttl=64 time=0.048 ms
64 bytes from 192.0.2.254: icmp_seq=3 ttl=64 time=0.047 ms

--- 192.0.2.254 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 0.047/0.959/2.782/1.289 ms
```

practice2 - router 間も同様に

```
$ sudo ip netns exec practice2 ping -c 3 192.51.100.254 -I 192.51.100.1
PING 192.51.100.254 (192.51.100.254) from 192.51.100.1 : 56(84) bytes of data.
64 bytes from 192.51.100.254: icmp_seq=1 ttl=64 time=1.79 ms
64 bytes from 192.51.100.254: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 192.51.100.254: icmp_seq=3 ttl=64 time=0.048 ms

--- 192.51.100.254 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2008ms
rtt min/avg/max/mdev = 0.047/0.627/1.786/0.819 ms
```

続いて、セグメントを超えた通信が可能か試してみます。

practice1 → practice2 へpingで疎通確認してみます

```
$ sudo ip netns exec practice1 ping -c 3 4/24 dev gw-veth1 192.51.100.1 -I 192.0.2.1
ping: 4/24: Temporary failure in name resolution
```

しかし、先ほどと同じ方法では疎通に失敗してしまいました。

**Network Namespaceにルーティングの設定**が必要になります
現状のNetwork Namespaceのルーティングを確認してみます

```
$ sudo ip netns exec practice1 ip route show
192.0.2.0/24 dev practice1-veth0 proto kernel scope link src 192.0.2.1
```

practice1にはルーティングエントリーが1つあります。
内容は、practice1-veth0というインターフェイスで`192.0.2.0/24`宛に通信を行うとあるので、
practice1と、practice2は直接はつながっていません

### どうすればいいのか？🤔

**ルーティングテーブルに新たにルーティングエントリを追加する**必要があります

**ルーティングエントリとして、デフォルトルート（すべてのルートを表す）** を追加します

practice1にデフォルトルートを追加します

```
$ sudo ip netns exec practice1 ip route add default via 192.0.2.254
$ sudo ip netns exec practice1 ip route show
default via 192.0.2.254 dev practice1-veth0
192.0.2.0/24 dev practice1-veth0 proto kernel scope link src 192.0.2.1
```

192.0.2.254はルータのipアドレスで、デフォルトルートをルータに向けました

practice2にも、同様にデフォルトルートとなるルーティングエントリを追加します

```
$ sudo ip netns exec practice2 ip route add default via 192.51.100.254
$ sudo ip netns exec practice2 ip route show
default via 192.51.100.254 dev practice2-veth0
192.51.100.0/24 dev practice2-veth0 proto kernel scope link src 192.51.100.1
```

ここまで来れば、再度、practice1 → practice2への疎通確認をしてみます

```
~$ sudo ip netns exec practice1 ping -c 3 198.51.100.1 -I 192.0.2.1
PING 198.51.100.1 (198.51.100.1) from 192.0.2.1 : 56(84) bytes of data.

--- 198.51.100.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2035ms
```

エラーは出ませんでしたが、`100% packet loss`とあるように返答が無かったみたいです

ipフォワードの設定が必要になります

>ここで、「IPフォワード」という技術が用意されています。この技術は、その名の通り、２つのNICの間でパケットを転送するという技術です。これにより、パケットが異なるネットワークの間を往き来できるようになります。
LinuxでIPフォワードを有効にするには、「/etc/sysctl.conf」ファイルに、「net.ipv4.ip_forward=1」という記述を行い（デフォルトではnet.ipv4.ip_forward=0）、ネットワークを再起動します。
https://linuc.org/study/knowledge/422/

```
$ sudo ip netns exec router sysctl net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
```

これでもう一度試してみます

```
$ sudo ip netns exec practice1 ping -c 3 198.51.100.1 -I 192.0.2.1
PING 198.51.100.1 (198.51.100.1) from 192.0.2.1 : 56(84) bytes of data.
From 192.0.2.254 icmp_seq=1 Destination Net Unreachable
From 192.0.2.254 icmp_seq=2 Destination Net Unreachable
From 192.0.2.254 icmp_seq=3 Destination Net Unreachable

--- 198.51.100.1 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2031ms
```

今度は疎通が出来ました

おしまい

# 参考

https://www.amazon.co.jp/Linux%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%97%E3%81%AA%E3%81%8C%E3%82%89%E5%AD%A6%E3%81%B6TCP-IP%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E5%85%A5%E9%96%80-%E3%82%82%E3%81%BF%E3%81%98%E3%81%82%E3%82%81-ebook/dp/B085BG8CH5