GoBGP + GoPlane を使って Linux Network Namespace (netns) で EVPN / VXLAN

# 環境
以下のような VXLAN ネットワークを構築します．
2 台の物理マシンそれぞれに Linux Network Namespace (netns) を作成し，それらを EVPN / VXLAN で接続します．

```
Underlay: 192.168.40.0/24
Overlay:  10.0.1.0/24
    _____________________       _____________________
   |  host1              |     |  host2              |
   |     ___________     |     |     ___________     |
   |    | netns5    |    |     |    | netns6    |    |
   |    |           |    |     |    |           |    |
   |    |   _____   |    |     |    |   _____   |    |
   |    |__|VXLAN|__|    |     |    |__|VXLAN|__|    |
   |       |_____|       |     |       |_____|       |
   |  10.0.1.5:   ____   |     |  10.0.1.6:   ____   | 
   |__________:__|p1p1|__|     |__________:__|p1p1|__|
              :  |____|192.168.40.5       :  |____|192.168.40.6
  ____________:_____|_____________________:_____|_______________
              :...........................:
```

# Go
GoBGP や GoPlane を使うために Golang や Glide をインストールしておきます．

## Golang
[Getting Started - The Go Programming Language](https://golang.org/doc/install)
公式サイトからダウンロードしてインストールします．
apt install 等を使うと古いバージョンの go がインストールされてしまうそうです．

```
$ wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.11.linux-amd64.tar.gz
$ echo 'export GOPATH=$HOME/.golang' >> $HOME/.profile
$ echo 'export PATH=$PATH:$GOPATH/bin:/usr/local/go/bin' >> $HOME/.profile
$ source $HOME/.profile
```

## Glide
GoPlane の依存関係を解決するのに必要です．

```
$ sudo apt install git gcc
$ go get github.com/Masterminds/glide
```

## GoPlane, GoBGP
- [osrg/goplane: an agent for configuring linux network stack via GoBGP](https://github.com/osrg/goplane)
- [osrg/gobgp: BGP implemented in the Go Programming Language](https://github.com/osrg/gobgp)

順番にインストールしていきます．最後に `$GOPATH/bin` を確認しましょう．
goplane は GoBGP のデーモンである gobgpd を含んでいますが，今回は `gobgp` コマンドを使用するために gobgp も別途インストールします．

```
# goplane
$ go get github.com/osrg/goplane   # 警告が出力される
$ cd $GOPATH/src/github.com/osrg/goplane; glide install
$ go get github.com/osrg/goplane   # 再度入力する

# gobgp
$ go get github.com/spf13/cobra
$ go get github.com/kr/pretty
$ cd vendor/github.com/osrg/gobgp/gobgp
$ go install .

$ ls $GOPATH/bin
glide  gobgp  goplane
```

# 設定ファイル
2 台の物理マシンそれぞれに対して，netns を作成するシェルスクリプトと goplane 用の TOML ファイルを用意します．goplane ではオプションによって YAML や JSON を使用することもできます．

## netns
`veth0` が netns 内の仮想インタフェース，`veth1` が netns 外の仮想インタフェースとなっています．
veth (virtual Ethernet) で L2 的に接続されているイメージです．

```netns5.sh
ip netns add netns5
ip link add veth0 type veth peer name veth1
ip link set veth0 mtu 1450
ip link set veth1 mtu 1450
ip link set veth0 netns netns5
ip netns exec netns5 ifconfig lo up
ip netns exec netns5 ifconfig veth0 10.0.10.5 netmask 255.255.255.0 broadcast 10.0.10.255 up
```

```netns6.sh
ip netns add netns6
ip link add veth0 type veth peer name veth1
ip link set veth0 mtu 1450
ip link set veth1 mtu 1450
ip link set veth0 netns netns6
ip netns exec netns6 ifconfig lo up
ip netns exec netns6 ifconfig veth0 10.0.10.6 netmask 255.255.255.0 broadcast 10.0.10.255 up
```

## goplane
BGP Peer を確立するための IP アドレス (Underlay) や，VXLAN の TEP (Tunnel End point) となる VTEP インタフェース名を指定します．
以下のように VNI (VXLAN Network Identifier) として `10` を指定すると，ホスト上に `br10` が自動生成されます．

```goplane5.toml
[bgp.global.config]
    as = 65000
    router-id = "192.168.40.5"

[[bgp.neighbors]]
    [bgp.neighbors.config]
        neighbor-address = "192.168.40.6"
        peer-as = 65000
    [bgp.neighbors.ebgp-multihop.config]
        enabled = true
        multihop-ttl = 100
    [[bgp.neighbors.afi-safis]]
        [bgp.neighbors.afi-safis.config]
            afi-safi-name = "l2vpn-evpn"

[dataplane]
    type = "netlink"
[[dataplane.virtual-network-list]]
    member-interfaces = [ "veth1", ]
    vni = 10
    vxlan-port = 8472
    rd = "65000:10"
    vtep-interface = "vtep10"
    etag = 10
    sniff-interfaces = [ "veth1", ]
```

```goplane6.toml
[bgp.global.config]
    as = 65000
    router-id = "192.168.40.6"

[[bgp.neighbors]]
    [bgp.neighbors.config]
        neighbor-address = "192.168.40.5"
        peer-as = 65000
    [bgp.neighbors.ebgp-multihop.config]
        enabled = true
        multihop-ttl = 100
    [[bgp.neighbors.afi-safis]]
        [bgp.neighbors.afi-safis.config]
            afi-safi-name = "l2vpn-evpn"

[dataplane]
    type = "netlink"
[[dataplane.virtual-network-list]]
    member-interfaces = [ "veth1", ]
    vni = 10
    vxlan-port = 8472
    rd = "65000:10"
    vtep-interface = "vtep10"
    etag = 10
    sniff-interfaces = [ "veth1", ]
```

# 実行
では，これらの設定ファイルを用いて実行していきます．

## 準備
もし `/etc/sysctl.conf` において `net.ipv4.ip_forward=1` がコメントアウトされていたら外してください．

```diff
- #net.ipv4.ip_forward=1
+ net.ipv4.ip_forward=1
```
以下のように表示されれば OK です．これを忘れると思ったようにパケットが転送されません．

```
$ sudo sysctl -p
net.ipv4.ip_forward = 1
```
設定を変更したらシステムの再起動をしましょう．

ちなみに `sysctl` はカーネル内のパラメータを変更してくれるコマンドです．
ユーザ空間の話では無いので，問題が生じた時に原因を突き止めるのが難しいですね．
何度これに気づけず時間を溶かしたかわかりません．

## netns の作成 (host1)
```
$ sudo ./netns5.sh
```
host2 では `netns6.sh` を実行します．

## goplane の起動 (host1)
```
$ sudo $HOME/.golang/bin/goplane -f ./goplane5.toml
{"level":"info","msg":"Peer 192.168.40.6 is added","time":"2018-09-29T16:04:15+09:00"}
{"Topic":"Peer","level":"info","msg":"Add a peer configuration for:192.168.40.6","time":"2018-09-29T16:04:15+09:00"}
{"level":"info","msg":"VirtualNetwork 65000:10 is added","time":"2018-09-29T16:04:15+09:00"}
{"Key":"192.168.40.6","State":"BGP_FSM_OPENCONFIRM","Topic":"Peer","level":"info","msg":"Peer Up","time":"2018-09-29T16:04:28+09:00"}
```
こちらも同様に，host2 では `goplane6.toml` を使用してください．
正常に設定ができていれば，最後の行で `"msg":"Peer Up"` が表示されるはずです．

# 検証
正しく環境を構築できているかどうか確認していきます．

## Peer
確かに `State` が `Establ` になっていますね．

```
$ gobgp neigh
Peer            AS  Up/Down State       |#Received  Accepted
192.168.40.6 65000 00:02:58 Establ      |        2         2
```

## 疎通確認
netns 内から `ping` を打つと，Overlay で疎通していることが確認できます．

```
$ sudo ip netns exec netns5 ping 10.0.10.6 -c 3
PING 10.0.10.6 (10.0.10.6) 56(84) bytes of data.
64 bytes from 10.0.10.6: icmp_seq=1 ttl=64 time=0.588 ms
64 bytes from 10.0.10.6: icmp_seq=2 ttl=64 time=0.690 ms
64 bytes from 10.0.10.6: icmp_seq=3 ttl=64 time=0.574 ms

--- 10.0.10.6 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2039ms
rtt min/avg/max/mdev = 0.574/0.617/0.690/0.055 ms
```
このとき，以下のようなコマンドを実行すると，MAC アドレス情報を学習できていることがわかります．

```
$ gobgp global rib -a evpn
    Network                                                                                       Next Hop             AS_PATH              Age        Attrs
*>  [type:macadv][rd:0:0][esi:single-homed][etag:10][mac:2a:1f:8f:52:1f:60][ip:<nil>][labels:[10]]192.168.40.6                              00:08:16   [{Origin: i} {LocalPref: 100} {Extcomms: [VXLAN]}]
*>  [type:macadv][rd:0:0][esi:single-homed][etag:10][mac:3e:96:f0:e6:db:e9][ip:<nil>][labels:[10]]0.0.0.0                                   00:08:29   [{Origin: i} {Extcomms: [VXLAN]}]
*>  [type:multicast][rd:65000:10][etag:10][ip:192.168.40.5]0.0.0.0                                   00:08:29   [{Origin: i} {Pmsi: type: ingress-repl, label: 0, tunnel-id: 192.168.40.5} {Extcomms: [65000:10]}]
*>  [type:multicast][rd:65000:10][etag:10][ip:192.168.40.6]192.168.40.6                              00:08:16   [{Origin: i} {LocalPref: 100} {Extcomms: [65000:10]} {Pmsi: type: ingress-repl, label: 0, tunnel-id: 192.168.40.6}]
```

## Bridge
最後に `brctl` でブリッジの様子を見てみましょう．
goplane の実行によって生成された `br10` というブリッジグループに，`veth1` と `vtep10` が含まれています．
`vtep10` においてカプセル化を解除されたフレームが `br10` に転送され，veth を介して netns の内部に届きます．

```
$ brctl show br10
bridge name     bridge id               STP enabled     interfaces
br10            8000.caaf73c60baf       no              veth1
                                                        vtep10
```


# 参考
- [GoBGPでEVPN/VXLANを試す](http://skjune12.hatenadiary.com/entry/2017/12/09/235550)
- [Raspberry Pi2で、E-VPN / VXLAN動作を試してみる](https://qiita.com/ttsubo/items/99bb4826538bf1353de5)