Network Namespace (NS)の基礎

### 概要

Network Namespace（ネットワークネームスペース、以下NS）は、Linuxカーネルの機能の一つで、複数のネットワークスタックを分離して管理することができます。同一OSシステムの中で異なるネットワーク環境を提供するために広く利用されており、コンテナにおける個別ネットワークの提供のためや仮想ネットワークの作成に利用されています。スタック自体が分離されるため深いリソースの分離が可能になる反面で柔軟な利用ができない場面もあるので利用時には最適な技術の利用の検討が必要です。

### 使い方

NSの使い方はシンプルで、いくつかの基本的なコマンドを用いて管理することができます。以下に、典型的な操作手順を示します。本記事では基本的にはiproute2 を用いて操作を行います。

#### 1. ネットワークネームスペースの作成

```bash
$ sudo ip netns add mynamespace
```

このコマンドで「mynamespace」という名前のNSを作成します。
コマンドを実行しても何も表示されませんが新しくNSが作成されています。作成されたNSは`ip netns list`コマンドで確認できます。

```bash
$ ip netns list
mynamespace
```

作成されたNS内ではコマンドを実行できます。この操作には`ip netns exec`を使うと指定したNS内でのコマンドを実行できます。また、実行コマンドにシェルを指定することで連続してコマンドを入力することもできます。

```bash
$ sudo ip netns exec mynamespace 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
    
$ sudo ip netns exec mynamespace bash
# 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
# exit

```

不要になったNSを削除するには`ip netns delete`コマンドを使います。作成した`netns` の環境は永続性のある設定ではないため、OSを再起動することで削除することもできます。

```bash
$ sudo ip netns delete mynamespace
$ ip netns list
(何も表示されません)
```

#### 2. ネットワークデバイスの追加

NSにネットワークデバイスを追加するには、vethペア（仮想イーサネットデバイス）を作成し、一方をホスト側に、もう一方をNS側に割り当てます。これを実ネットワークに例えるならばケーブルをルータに接続しているようなものです。

```bash
$ sudo ip link add veth0 type veth peer name veth1
$ sudo ip link set veth1 netns mynamespace
```

このコマンドでveth0はホストに、veth1はNS「mynamespace」に割り当てられます。ここではリンクレベルで接続されたことになりますのでL3の設定を実施する必要があります。

#### 3. ネットワークデバイスの設定

グローバルのName Spaceであるホスト側のvethデバイスにIPアドレスを割り当て、起動します。

```bash
$ sudo ip addr add 192.168.10.1/24 dev veth0
$ sudo ip link set veth0 up
$ ip address show veth0
5: veth0@if4: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether 1e:4b:7b:d8:84:10 brd ff:ff:ff:ff:ff:ff link-netns mynamespace
    inet 192.168.10.1/24 scope global veth0
       valid_lft forever preferred_lft foreve
```

NS内のネットワークデバイスを設定するには、`ip netns exec`を使用して、L3の設定を実施します。

```bash
$ sudo ip netns exec mynamespace ip addr add 192.168.10.2/24 dev veth1
$ sudo ip netns exec mynamespace ip link set veth1 up
$ sudo ip netns exec mynamespace ip link set lo up
$ sudo ip netns exec mynamespace ip address show veth1
4: veth1@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 92:8d:2d:c5:ed:00 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.10.2/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::908d:2dff:fec5:ed00/64 scope link
       valid_lft forever preferred_lft forever                                                          
 
$ ip address show veth0
5: veth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 1e:4b:7b:d8:84:10 brd ff:ff:ff:ff:ff:ff link-netns mynamespace
    inet 192.168.10.1/24 scope global veth0
       valid_lft forever preferred_lft forever
    inet6 fe80::1c4b:7bff:fed8:8410/64 scope link
       valid_lft forever preferred_lft forever
```

vethの両端で適切な設定がなされることでインターフェースの状態が`UP`に変化したことが確認できます。実際にこの仮想ネットワークへパケットを送信して導通できているか確認してみます。

```bash
$ sudo ip netns exec mynamespace ping -c 3 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.125 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.087 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=0.072 ms

--- 192.168.10.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2033ms
rtt min/avg/max/mdev = 0.072/0.094/0.125/0.022 ms
```

### 参考

現在の環境のnsを識別するには`/proc`の状態を確認することで実施できます。selfのnetの部分にSymリンクになりますが、inode番号が記載されています。実際に`ns1`の環境にシェルが入ると、inode番号が変化していることがわかります。

```bash
$ ls -alF /proc/self/ns/net
lrwxrwxrwx 1 ubuntu ubuntu 0 Aug  6 05:36 /proc/self/ns/net -> 'net:[4026531840]'
$ sudo ip netns add ns1
$ sudo ip netns exec ns1 bash
# ls -alF /proc/self/ns/net
lrwxrwxrwx 1 root root 0 Aug  6 05:37 /proc/self/ns/net -> 'net:[4026532242]'
# stat -L /proc/self/ns/net
  File: /proc/self/ns/net
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 4h/4d	Inode: 4026532242  Links: 1
Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2024-08-06 05:37:17.438093308 +0000
Modify: 2024-08-06 05:37:17.438093308 +0000
Change: 2024-08-06 05:37:17.438093308 +0000
 Birth: -
# exit
$ ls -alF /proc/self/ns/net
lrwxrwxrwx 1 ubuntu ubuntu 0 Aug  6 05:40 /proc/self/ns/net -> 'net:[4026531840]'
$ ls /var/run/netns
ns1
$ ip netns list
ns1
```

### まとめ

Network Namespaceは、ネットワークの柔軟性とセキュリティを高めるためのツールです。コンテナ技術や仮想化環境で広く利用されており、システム管理者や開発者にとって有用です。基本的なコマンドを理解し、適切に活用することで、複雑なネットワーク環境も簡単に管理することができます。