Network Namespaceを使って仮想ネットワークを作る

## これは何？
Dockerなどのコンテナ技術で使用されるNetwork Namespaceを使い、小さな仮想ネットワークを作る記事です。
ゴールは、異なるNetwork Namespace間でpingできるようにすることです。

## Network Namespaceとは
Network NamespaceはLinuxのNamespace機能の一つで、ネットワーク関連のリソースを隔離する機能です。
Network Namespaceを使うことによって、ネットワーク的にシステムから独立した仮想的な空間を作ることができます。

https://man7.org/linux/man-pages/man7/network_namespaces.7.html
> Network namespaces provide isolation of the system resources associated with networking: network devices, IPv4 and IPv6 protocol stacks, IP routing tables, firewall rules, the /proc/net directory (which is a symbolic link to /proc/pid/net), the /sys/class/net directory, various files under /proc/sys/net, port numbers (sockets), and so on. In addition, network namespaces isolate the UNIX domain abstract socket namespace (see unix(7)).

ネットワーク的に独立した空間を作るため、ネットワーク関連の設定がホストマシンとNetwork Namespace内では異なります。

例えば、ホストマシンのRoute Tableと作成したNetwork Namespace内のRoute Tableを見ると、違う内容が表示されます。
```sh
$ ip route show
default via 10.32.0.1 dev eno1 proto dhcp src 10.32.1.33 metric 100 
10.32.0.0/16 dev eno1 proto kernel scope link src 10.32.1.33 metric 100
```
```sh
$ sudo ip netns add ns
$ sudo ip netns exec ns bash
$ ip route show
```
Network Namespaceを作成した段階ではRoute Tableにエントリが存在しないため、特に何も表示されません。

## 実践
### 環境
```sh
$ cat /etc/issue
Ubuntu 24.04.1 LTS \n \l
```

### Network Namespaceの作成
異なるNetwork Namespace間でpingが通るようにしたいので、まずは2つのNetwork Namespaceを作成します。
```sh
$ sudo ip netns add ns1
$ sudo ip netns add ns2
```

### 仮想Network Interfaceの作成
続いて、各Network Namespaceが通信するための仮想的なNetwork Interfaceを作成します。
この仮想的なNetwork Intefaceは[veth(Virtual Ethernet Device)](https://man7.org/linux/man-pages/man4/veth.4.html)と呼ばれます。
vethを作成する際にはNetwork Interfaceを単体で作るのではなく、両端がNetwork Interfaceになっている仮想的なケーブルを作成し、それをNetwork Namespaceにアタッチします。

```sh
$ sudo ip link add veth1 type veth peer name veth2
```
`veth1`, `veth2`というNetwork Interfaceが追加されていることが確認できます。
```sh
$ sudo ip link show
# ...
4: veth2@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 36:e9:5b:30:7a:75 brd ff:ff:ff:ff:ff:ff
5: veth1@veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 02:f2:f1:f3:55:82 brd ff:ff:ff:ff:ff:ff
```
この時点ではホストマシンの領域にできてしまっているので、両端のNetwork Interfaceを各Network Namespaceにアタッチします。
```sh
$ sudo ip link set veth1 netns ns1
$ sudo ip link set veth2 netns ns2
```
ns1, ns2に入ってNetwork Interfaceがアタッチされているか確認します。
```sh
$ sudo ip netns exec ns1 bash
$ ip link show
# ...
5: veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 02:f2:f1:f3:55:82 brd ff:ff:ff:ff:ff:ff link-netns ns2

$ sudo ip netns exec ns2 bash
$ ip link show
# ...
4: veth2@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 36:e9:5b:30:7a:75 brd ff:ff:ff:ff:ff:ff link-netns ns1
```

### IPアドレスの割当
この時点で異なるNetwork Namespace同士がvethを通して繋がりましたが、pingを通すためにはIPアドレスの設定が必要になります。
各Network Namespaceでvethに対してIPアドレスを割り当てます。
```sh
$ sudo ip netns exec ns1 bash
$ ip address add 192.0.1.1/24 dev veth1
$ ip address show
# ...
5: veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 02:f2:f1:f3:55:82 brd ff:ff:ff:ff:ff:ff link-netns ns2
    inet 192.0.1.1/24 scope global veth1
       valid_lft forever preferred_lft forever

$ sudo ip netns exec ns2 bash
$ ip address add 192.0.1.2/24 dev veth2
$ ip address show
# ...
4: veth2@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 36:e9:5b:30:7a:75 brd ff:ff:ff:ff:ff:ff link-netns ns1
    inet 192.0.1.2/24 scope global veth2
       valid_lft forever preferred_lft forever
```

### Network Interfaceの状態をUPに変更する
最後にNetwork Interfaceの状態をUPにすることで通信できるようにします。
Route tableにエントリが追加されます。

```sh
$ sudo ip netns exec ns1 bash
$ ip link set veth1 up
$ ip route show
192.0.1.0/24 dev veth1 proto kernel scope link src 192.0.1.1 linkdown

$ sudo ip netns exec ns2 bash
$ ip link set veth2 up
$ ip route show
192.0.1.0/24 dev veth2 proto kernel scope link src 192.0.1.2
```

### ping
```sh
$ sudo ip netns exec ns1 bash
$ ping -c 3 192.0.1.2
PING 192.0.1.2 (192.0.1.2) 56(84) bytes of data.
64 bytes from 192.0.1.2: icmp_seq=1 ttl=64 time=0.063 ms
64 bytes from 192.0.1.2: icmp_seq=2 ttl=64 time=0.066 ms
64 bytes from 192.0.1.2: icmp_seq=3 ttl=64 time=0.058 ms

--- 192.0.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2059ms
rtt min/avg/max/mdev = 0.058/0.062/0.066/0.003 ms
```

## 参考
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/dp/B08SH41SL6

https://www.sbcr.jp/product/4815618599/

https://tatsu-zine.com/books/superuser-linux-system