Carpe Diem

備忘録

ip netnsコマンドで学ぶNetwork Namespace

概要

Linuxには名前空間(Namespace)というカーネルの機能が提供されています。
これは1つのプロセスが1つのリソースセットを参照し、別のプロセスが異なるリソースセットを参照するようにカーネルリソースを分割する機能です。

その中の1つであるネットワーク名前空間(Network Namespace)の機能を学んでみます。

環境

ip netnsを使ってみる

初期状態

デフォルトのUbuntuでは以下のように2つのNICが存在します。

  • lo
  • enp0s3

コマンドで確認します。

$ ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff

図では以下のような状態です。

f:id:quoll00:20200125201941p:plain

ネットワーク名前空間を作る

host1というネットワーク名前空間を作ります。

ip netns addで作ります。

$ sudo ip netns add host1
$ ip netns l
host1

ここで作成された名前空間にはループバックインタフェースのみが存在します。

f:id:quoll00:20200125204036p:plain

ネットワーク名前空間の中に入ってみる

ip netns execで中に入ることができます。

vagrant@ubuntu-bionic:~$ sudo ip netns exec host1 bash
root@ubuntu-bionic:~#

プロンプトが変わっていることが分かりますね。中のNICを確認すると、先ほどの図の通りループバックのloが存在します。

root@ubuntu-bionic:~# ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

ただ初期状態ではDOWNしているのでifconfigでは見えません。
先ほどの図でloが点線だったのはそのためです。

root@ubuntu-bionic:~# ifconfig
root@ubuntu-bionic:~#

pingを打ってもunreachableが返ります。

root@ubuntu-bionic:~# ping 127.0.0.1
connect: Network is unreachable

なのでNICをUP状態にします。

root@ubuntu-bionic:~# ip link set lo up
root@ubuntu-bionic:~# ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.018 ms

pingも返るようになりました。

f:id:quoll00:20200125202348p:plain

ホストLinuxと疎通させる

このままだと何も通信できないので、ホストLinuxと疎通できるようにします。
ホストLinux側で以下のようにip link addvethを用意します。

$ sudo ip link add name veth1 type veth peer name host1-veth1
$ ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff
3: host1-veth1@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:fc:67:d3:45:bc brd ff:ff:ff:ff:ff:ff
4: veth1@host1-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 76:40:af:6f:eb:67 brd ff:ff:ff:ff:ff:ff

name veth1peer name host1-veth1はオプションなので省略しても問題ありません。

図で表すとこの状態です。

f:id:quoll00:20200125203927p:plain

vethのピアをネットワーク名前空間

先ほど作ったvethのピアhost1-veth1host1へバインドします。

$ sudo ip link set host1-veth1 netns host1

するとホストLinuxからNICが消えます。

$ ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff
4: veth1@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 76:40:af:6f:eb:67 brd ff:ff:ff:ff:ff:ff link-netnsid 0

host1に入って確認してみると、先ほどのNICが存在しています。

$ sudo ip netns exec host1 bash
root@ubuntu-bionic:~# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: host1-veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:fc:67:d3:45:bc brd ff:ff:ff:ff:ff:ff link-netnsid 0

図で表すと以下の状態です。

f:id:quoll00:20200125204351p:plain

IPアドレスを付与

では最後にIPアドレスを付与してNICをUPさせます。

互いに通信できるよう、

  • 同一サブネットにする
  • お互いのIPは重複しないようにする

の2点に注意してください。

今回はサブネットを10.0.0.0/24として付与していきます。

ホストLinux

IPアドレス10.0.0.1を付与。サブネットマスク/24(255.255.255.0)で。

$ sudo ip addr add 10.0.0.1/24 dev veth1

NICをUP。

$ sudo ip link set veth1 up
$ ip 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: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 83214sec preferred_lft 83214sec
    inet6 fe80::b7:1fff:fe33:e924/64 scope link
       valid_lft forever preferred_lft forever
4: veth1@if3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether 76:40:af:6f:eb:67 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.1/24 scope global veth1
       valid_lft forever preferred_lft forever

ネットワーク名前空間host1

IPアドレス10.0.0.2を付与。サブネットマスク/24(255.255.255.0)で。

# ip addr add 10.0.0.2/24 dev host1-veth1

NICをUP。

# ip link set host1-veth1 up
# ip 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
3: host1-veth1@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c6:fc:67:d3:45:bc brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.2/24 scope global host1-veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::c4fc:67ff:fed3:45bc/64 scope link
       valid_lft forever preferred_lft forever

図で表すと以下です。

f:id:quoll00:20200125205838p:plain

動作確認

ホストLinuxからhost1へのping

10.0.0.1から10.0.0.2です。

vagrant@ubuntu-bionic:~$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.020 ms

疎通できてますね。

host1からホストLinuxへのping

10.0.0.2から10.0.0.1です。

root@ubuntu-bionic:~# ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms

こちらも疎通できてますね。

その他

ネットワーク名前空間はホストLinuxを再起動すると消えてなくなります。
なので再起動しても同じような環境を用意したい場合、ブート時に名前空間を作成するスクリプトを用意する必要があります。

unix.stackexchange.com

まとめ

ip netnsコマンドを使ってネットワーク名前空間について検証してみました。
これに加えてbridgeやiptablesを用いたNATなどを駆使するとdockerと同じような状態を構築することが可能です。

ソース