netnsを使ったホスト内ネットワーク構築

ネットワークエンジニアから「Linux Bridgeがわからん」と言われて説明用に書いたLinuxホスト内部ネットワークの概念説明と作り方です。

実用的なものが必要ならば以下のリンクがわかりやすいかと思います。
http://kurochan-note.hatenablog.jp/entry/2015/10/11/110649
http://ameblo.jp/principia-ca/entry-12103919307.html

なお、リソースさえあればコンテナよりもVMで構築したほうが圧倒的に楽です:-)


# 環境
・OS
CentOS7

・ルーティングプロトコル
OSPF(on quagga)



#前提知識
Linux内に以下のものを複数作成することができる
・インターフェース
・ブリッジ
・ルーティングテーブル、プロトコルスタック


#シナリオ
こういうネットワーク作ってと言われました。

![ipnetns3.png](https://qiita-image-store.s3.amazonaws.com/0/19360/20d5ea81-65ff-d360-21b7-242348b7fe60.png)



実ネットワークを組むには機材が足りないのでLinux上に仮想ネットワークを組むことにしました。




# 考え方
以下のようにあたまのなかで置き換えます。

スイッチ　⇔　Linux Bridge
　ルータ　⇔　netns
ケーブル　⇔　veth pair



# 用語説明
##Linux Bridge
Linux内部に作成されるブリッジ。
STPなどの基本的なL2機能を持っているが一般的なL2/L3スイッチというよりも「ルータのブリッジ＋L2機能」と考えた方いいかと。


##netns
network namespaceの略
Linux内に複数のルーティングテーブルやプロトコルスタックを作成することが出来ます。
このnetns毎にIPアドレスやsocketがnetns単位で独立し、重複したIPやsocketもns毎に指定可能になります。
これがLinux内のルータに相当します。

##veth pair
P2Pなイーサネットインターフェース
ケーブルに相当します。




#登場人物

ルータ６台
スイッチ１台
ケーブル１１本


# 構築
物理とあまりかわりません。
以下全てrootで作業する必要があります。

## 機器を作る

###スイッチ

```
brctl addbr SW1
```

###ルータ６台

```
ip netns add RT1
ip netns add RT2
ip netns add RT3
ip netns add RT4
ip netns add CE1
ip netns add CE2
```

###ケーブル１１本
```
ip link add CE1_to_RT1 type veth peer name RT1_to_CE1
ip link add CE1_to_RT3 type veth peer name RT3_to_CE1

ip link add CE2_to_RT2 type veth peer name RT2_to_CE2
ip link add CE2_to_RT4 type veth peer name RT4_to_CE2

ip link add RT1_to_RT3 type veth peer name RT3_to_RT1
ip link add RT2_to_RT4 type veth peer name RT4_to_RT2
ip link add RT3_to_RT4 type veth peer name RT4_to_RT3

ip link add RT1_to_SW1 type veth peer name SW1_to_RT1
ip link add RT2_to_SW1 type veth peer name SW1_to_RT2
ip link add RT3_to_SW1 type veth peer name SW1_to_RT3
ip link add RT4_to_SW1 type veth peer name SW1_to_RT4
```


※ポイント
スイッチ・ルータ（＝ブリッジ、netns）には名前を付けられますが、ケーブルに付与出来るのは両端のIFの名前だけです。
物理ケーブルでいうところの両端のコネクタに名札を付けるのに相当します。

大量の配線でケーブルを追えなくなるような状況で名札は威力を発揮しますが、命名則がいい加減だと対向を見失うのは仮想環境でも同じです。

このケーブル相当のIFから対向IFを探すのはコマンド一発では出来ない（且つ接続後はさらに面倒）ので、命名則を規定しておいたほうがあとあと楽が出来ます。
ここでは命名即を「接続元_to_接続先」としました。




##機器をつなぐ
ケーブル相当のvethペアの両端をそれぞれの機器に接続します。

###スイッチ（ブリッジ）に接続

```
brctl addif SW1 SW1_to_RT1
brctl addif SW1 SW1_to_RT2
brctl addif SW1 SW1_to_RT3
brctl addif SW1 SW1_to_RT4
```


###ルータに接続

```
ip link set RT1_to_CE1 netns RT1 up
ip link set RT1_to_RT3 netns RT1 up
ip link set RT1_to_SW1 netns RT1 up

ip link set RT2_to_CE2 netns RT2 up
ip link set RT2_to_RT4 netns RT2 up
ip link set RT2_to_SW1 netns RT2 up

ip link set RT3_to_CE1 netns RT3 up
ip link set RT3_to_RT1 netns RT3 up
ip link set RT3_to_RT4 netns RT3 up
ip link set RT3_to_SW1 netns RT3 up

ip link set RT4_to_CE2 netns RT4 up
ip link set RT4_to_RT2 netns RT4 up
ip link set RT4_to_RT3 netns RT4 up
ip link set RT4_to_SW1 netns RT4 up

ip link set CE1_to_RT1 netns CE1 up
ip link set CE1_to_RT3 netns CE1 up

ip link set CE2_to_RT2 netns CE2 up
ip link set CE2_to_RT4 netns CE2 up
```



##ルータIFにIPを割り当てる
各netns内での作業になり、ip netnsコマンドで設定を行います

```
ip netns exec RT1 ip addr add 192.168.1.1/24 dev RT1_to_CE1
ip netns exec RT1 ip addr add 172.16.0.1/24  dev RT1_to_SW1
ip netns exec RT1 ip addr add 172.17.13.1/24 dev RT1_to_RT3

ip netns exec RT2 ip addr add 192.168.2.2/24 dev RT2_to_CE2
ip netns exec RT2 ip addr add 172.16.0.2/24  dev RT2_to_SW1
ip netns exec RT2 ip addr add 172.16.24.2/24 dev RT2_to_RT4

ip netns exec RT3 ip addr add 192.168.3.3/24 dev RT3_to_CE1
ip netns exec RT3 ip addr add 172.16.0.3/24  dev RT3_to_SW1
ip netns exec RT3 ip addr add 172.17.13.3/24 dev RT3_to_RT1
ip netns exec RT3 ip addr add 172.18.34.3/24 dev RT3_to_RT4

ip netns exec RT4 ip addr add 192.168.4.4/24 dev RT4_to_CE2
ip netns exec RT4 ip addr add 172.16.0.4/24  dev RT4_to_SW1
ip netns exec RT4 ip addr add 172.19.24.4/24 dev RT4_to_RT2
ip netns exec RT4 ip addr add 172.18.34.4/24 dev RT4_to_RT3


ip netns exec CE1 ip addr add 192.168.1.254/24 dev CE1_to_RT1
ip netns exec CE1 ip addr add 192.168.3.254/24 dev CE1_to_RT3

ip netns exec CE2 ip addr add 192.168.2.254/24 dev CE2_to_RT2
ip netns exec CE2 ip addr add 192.168.4.254/24 dev CE2_to_RT4
```


ループバックIFも忘れずに:-)
忘れるとpingが打てなくなります

```
ip netns exec RT1 ip addr add 127.0.0.1/8 dev lo
ip netns exec RT2 ip addr add 127.0.0.1/8 dev lo
ip netns exec RT3 ip addr add 127.0.0.1/8 dev lo
ip netns exec RT4 ip addr add 127.0.0.1/8 dev lo
ip netns exec CE1 ip addr add 127.0.0.1/8 dev lo
ip netns exec CE2 ip addr add 127.0.0.1/8 dev lo
```


##リンク開放
各nsのloとSW1のlinkをあげます

```
ip netns exec RT1 ip link set lo up
ip netns exec RT2 ip link set lo up
ip netns exec RT3 ip link set lo up
ip netns exec RT4 ip link set lo up
ip netns exec CE1 ip link set lo up
ip netns exec CE2 ip link set lo up

ip link set SW1_to_RT1 up
ip link set SW1_to_RT2 up
ip link set SW1_to_RT3 up
ip link set SW1_to_RT4 up
ip link set SW1 up
```


## 確認
pingで疎通を確認します

```
ip netns exec CE1 ping 192.168.1.1
```





##ルーティングデーモン
開発版（？）のquaggaではVRFが使えるようですが、ここではnetns単位にルーティングデーモンを立ち上げることとします。
今回はファイルシステム・プロセス空間は分離させていないので、設定ファイルやpidファイルの置き場をそれぞれ分離させて、各デーモンを複数立ち上げられるようにします。


###コンフィグファイル作成
慣れてる人はCLIで変更したほうが早いかもしれません。

```/etc/quagga/RT1_zebra.conf
hostname RT1
password zebra
ip forwarding
```

```/etc/quagga/RT1_ospfd.conf
hostname RT1
password zebra
router ospf
 ospf router-id 10.0.0.1
 network 172.16.0.1/24 area 0.0.0.0
 network 172.17.13.1/24 area 0.0.0.0
 network 192.168.1.1/24 area 0.0.0.0
```

```/etc/quagga/RT2_zebra.conf
hostname RT2
password zebra
ip forwarding
```

```/etc/quagga/RT2_ospfd.conf
hostname RT2
password zebra
router ospf
 ospf router-id 10.0.0.2
 network 172.16.0.2/24 area 0.0.0.0
 network 172.19.24.2/24 area 0.0.0.0
 network 192.168.2.2/24 area 0.0.0.0
```

```/etc/quagga/RT3_zebra.conf
hostname RT3
password zebra
ip forwarding
```

```/etc/quagga/RT3_ospfd.conf
hostname RT3
password zebra
router ospf
 ospf router-id 10.0.0.3
 network 172.16.0.0/24 area 0.0.0.0
 network 172.17.13.0/24 area 0.0.0.0
 network 172.18.34.0/24 area 0.0.0.0
 network 192.168.3.0/24 area 0.0.0.0
```

```/etc/quagga/RT4_zebra.conf
hostname RT4
password zebra
ip forwarding
```

```/etc/quagga/RT4_ospfd.conf
hostname RT4
password zebra
router ospf
 ospf router-id 10.0.0.4
 network 172.16.0.0/24 area 0.0.0.0
 network 172.18.34.0/24 area 0.0.0.0
 network 172.19.24.0/24 area 0.0.0.0
 network 192.168.3.0/24 area 0.0.0.0
```

```/etc/quagga/CE1_zebra.conf
hostname CE1
password zebra
ip forwarding
```

```/etc/quagga/CE1_ospfd.conf
hostname CE1
password zebra
router ospf
 ospf router-id 10.1.1.1
 network 192.168.1.254/24 area 0.0.0.0
 network 192.168.3.254/24 area 0.0.0.0
```

```/etc/quagga/CE2_zebra.conf
hostname CE2
password zebra
ip forwarding
```

```/etc/quagga/CE2_ospfd.conf
hostname CE2
password zebra
router ospf
 ospf router-id 10.2.2.2
 network 192.168.2.254/24 area 0.0.0.0
 network 192.168.4.254/24 area 0.0.0.0
```

コンフィグファイルのowner変更

```
chown quagga.quagga /etc/quagga/*conf
```


###ルーティングデーモン起動
各netns内でzebraとospfdを起動します。

```
ip netns exec RT1 /usr/sbin/zebra-d-f/etc/quagga/RT1_zebra.conf -i /var/run/quagga/RT1_zebra.pid -A 127.0.0.1 -z /var/run/quagga/RT1_zebra.vty
ip netns exec RT1 /usr/sbin/ospfd-d-f/etc/quagga/RT1_ospfd.conf -i /var/run/quagga/RT1_ospfd.pid -A 127.0.0.1 -z /var/run/quagga/RT1_zebra.vty

ip netns exec RT2 /usr/sbin/zebra-d-f/etc/quagga/RT2_zebra.conf -i /var/run/quagga/RT2_zebra.pid -A 127.0.0.1 -z /var/run/quagga/RT2_zebra.vty
ip netns exec RT2 /usr/sbin/ospfd-d-f/etc/quagga/RT2_ospfd.conf -i /var/run/quagga/RT2_ospfd.pid -A 127.0.0.1 -z /var/run/quagga/RT2_zebra.vty

ip netns exec RT3 /usr/sbin/zebra-d-f/etc/quagga/RT3_zebra.conf -i /var/run/quagga/RT3_zebra.pid -A 127.0.0.1 -z /var/run/quagga/RT3_zebra.vty
ip netns exec RT3 /usr/sbin/ospfd-d-f/etc/quagga/RT3_ospfd.conf -i /var/run/quagga/RT3_ospfd.pid -A 127.0.0.1 -z /var/run/quagga/RT3_zebra.vty

ip netns exec RT4 /usr/sbin/zebra-d-f/etc/quagga/RT4_zebra.conf -i /var/run/quagga/RT4_zebra.pid -A 127.0.0.1 -z /var/run/quagga/RT4_zebra.vty
ip netns exec RT4 /usr/sbin/ospfd-d-f/etc/quagga/RT4_ospfd.conf -i /var/run/quagga/RT4_ospfd.pid -A 127.0.0.1 -z /var/run/quagga/RT4_zebra.vty

ip netns exec CE1 /usr/sbin/zebra-d-f/etc/quagga/CE1_zebra.conf -i /var/run/quagga/CE1_zebra.pid -A 127.0.0.1 -z /var/run/quagga/CE1_zebra.vty
ip netns exec CE1 /usr/sbin/ospfd-d-f/etc/quagga/CE1_ospfd.conf -i /var/run/quagga/CE1_ospfd.pid -A 127.0.0.1 -z /var/run/quagga/CE1_zebra.vty

ip netns exec CE2 /usr/sbin/zebra-d-f/etc/quagga/CE2_zebra.conf -i /var/run/quagga/CE2_zebra.pid -A 127.0.0.1 -z /var/run/quagga/CE2_zebra.vty
ip netns exec CE2 /usr/sbin/ospfd-d-f/etc/quagga/CE2_ospfd.conf -i /var/run/quagga/CE2_ospfd.pid -A 127.0.0.1 -z /var/run/quagga/CE2_zebra.vty
```





##オペレーション例
・neighbor確認
2604がOSPFデーモンのポート番号、passwordはコンフィグで設定した"zebra"

```
# ip netns exec RT1 telnet 127.0.0.1 2604
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

Hello, this is Quagga (version 0.99.22.4).
Copyright 1996-2005 Kunihiro Ishiguro, et al.


User Access Verification

Password: 
RT1> en
RT1# show ip ospf neighbor 

    Neighbor ID Pri State           Dead Time Address         Interface            RXmtL RqstL DBsmL
10.1.1.1          1 Full/DR           35.388s 192.168.1.254   RT1_to_CE1:192.168.1.1     0     0     0
10.0.0.3          1 Full/DR           35.340s 172.17.13.3     RT1_to_RT3:172.17.13.1     0     0     0
10.0.0.2          1 2-Way/DROther     35.307s 172.16.0.2      RT1_to_SW1:172.16.0.1     0     0     0
10.0.0.3          1 Full/Backup       35.340s 172.16.0.3      RT1_to_SW1:172.16.0.1     0     0     0
10.0.0.4          1 Full/DR           35.364s 172.16.0.4      RT1_to_SW1:172.16.0.1     0     0     0
RT1# exit
```



・linkのup/down

```
# ip netns exec RT1 ip link set RT1_to_SW1 down
```

・疎通確認

```
# ip netns exec CE1 traceroute 192.168.4.254                                                                    
traceroute to 192.168.4.254 (192.168.4.254), 30 hops max, 60 byte packets
 1  192.168.3.3 (192.168.3.3)  0.095 ms  0.043 ms  0.043 ms
 2  172.16.0.2 (172.16.0.2)  0.058 ms  0.036 ms  0.033 ms
 3  192.168.4.254 (192.168.4.254)  0.091 ms  0.047 ms  0.046 ms
```


##　その他
操作コマンドをまとめたスクリプトと各コンフィグファイルはgithubに置きましたので必要な方はご利用下さい。
https://github.com/nori-nori/netns-testbed