Raspberry Pi2で、E-VPN / VXLAN動作を試してみる

従来、ネットワーク機器の内部機能は、ベンダプロプライエタリな領域であり、私たち利用者自らが、カスタマイズを行うことができませんでした。
ただ、最近は、SDN技術によって、ネットワーク機器のオープン化/ホワイトボックス化も着実に浸透しつつあります。

そんな折に、次のようなWeb記事
[「機能がないなら作れば良い？　SDN研究者から見た「ホワイトボックススイッチ」の現状と未来」](http://cloud.watch.impress.co.jp/docs/special/20160316_745619.html)
が公開されました。

とても興味深い内容なので、自分自身でも、Web記事で語られているE-VPNを試してみたいと思います。ただ、個人でCumulus Linuxを購入するのは、金銭面で敷居が高いので、今回は、Raspberry Pi2をホワイトボッックスと見立てて動作確認しております。

#◼︎ E-VPN / VXLANとは、、

EVPN と VXLAN は、次世代データセンタでのスケール性/拡張性を兼ね備えた新たなネットワークアーキテクチャ技術です。技術の詳細については、ここでは触れません。
ただ、個人的な所感としては、IoT分野においても活用が期待される技術だと思います。
さらに、E-VPNの実装事例として、[Github: osrg/goBGP](https://github.com/osrg/gobgp/blob/master/docs/sources/evpn.md) にてオープンソースとして技術公開されているという点は、とても興味深いです。

#◼︎ Raspberry Pi2環境整備
早速、E-VPN / VXLAN動作を試すRaspberry Pi2環境を取り掛かります。

##(1) Ubuntu導入
基本的に、"ARM/RaspberryPi - Ubuntu Wiki"の記載どおり、作業を進めていきます。
https://wiki.ubuntu.com/ARM/RaspberryPi

- SDフォーマッターなどを活用して、microSDHCカードをフォーマットする

- Raspberry Pi2用Ubuntu 14.04 LTSイメージをダウンロードする

- microSDHCカードが、どこにマウントされてるかを確認する

```
$ df
Filesystem    512-blocks      Used Available Capacity  iused   ifree %iused  Mounted on
/dev/disk1     487849984 431973088  55364896    89% 54060634 6920612   89%   /
devfs                659       659         0   100%     1140       0  100%   /dev
map -hosts             0         0         0   100%        0       0  100%   /net
map auto_home          0         0         0   100%        0       0  100%   /home
/dev/disk2s1    30852096      4928  30847168     1%        0       0  100%   /Volumes/NO NAME
```
　今回は、/dev/disk2s1にマウントされているようでした。

- microSDHCカードをアンマウントする

```
$ diskutil unmount /dev/disk2s1
Volume NO NAME on disk2s1 unmounted
$ df
Filesystem    512-blocks      Used Available Capacity  iused   ifree %iused  Mounted on
/dev/disk1     487849984 431975632  55362352    89% 54060952 6920294   89%   /
devfs                659       659         0   100%     1141       0  100%   /dev
map -hosts             0         0         0   100%        0       0  100%   /net
map auto_home          0         0         0   100%        0       0  100%   /home
```

- microSDHCカードに、Ubuntu 14.04 LTSイメージを書き込む

```
$ sudo dd bs=1m if=2015-04-06-ubuntu-trusty.img of=/dev/rdisk2
Password:
1792+0 records in
1792+0 records out
1879048192 bytes transferred in 201.154983 secs (9341296 bytes/sec)
```
　ddで書き込むときはdisk2s1の先頭にrをつけて、s1を削る必要があります。

- Ubuntuを起動して、最新にアップグレードする（ユーザ名、パスワードは、デフォルト"ubuntu"と設定されている）

```
$ sudo apt-get upgrade
...

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
```

- SSHアクセス環境を整える

```
$ sudo apt-get update
$ sudo apt-get install openssh-server
```

- rootディスク領域を拡張する

```
$ df -k
Filesystem     1K-blocks   Used Available Use% Mounted on
/dev/root        1707856 563264   1039788  36% /
devtmpfs          468776      4    468772   1% /dev
none                   4      0         4   0% /sys/fs/cgroup
none               94612    288     94324   1% /run
none                5120      0      5120   0% /run/lock
none              473056      0    473056   0% /run/shm
none              102400      0    102400   0% /run/user
/dev/mmcblk0p1     65390  19794     45596  31% /boot/firmware

$ sudo fdisk /dev/mmcblk0
[sudo] password for ubuntu: 

Command (m for help): d
Partition number (1-4): 2

Command (m for help): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
Partition number (1-4, default 2): 2
First sector (133120-30868479, default 133120): 
Using default value 133120
Last sector, +sectors or +size{K,M,G} (133120-30868479, default 30868479): 
Using default value 30868479

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

$ sudo reboot
...

$ sudo resize2fs /dev/mmcblk0p2
[sudo] password for ubuntu: 
resize2fs 1.42.9 (4-Feb-2014)
Filesystem at /dev/mmcblk0p2 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/mmcblk0p2 is now 3841920 blocks long.

ubuntu@ubuntu:~$ df -k
Filesystem     1K-blocks   Used Available Use% Mounted on
/dev/root       15100720 565128  13889496   4% /
devtmpfs          468776      4    468772   1% /dev
none                   4      0         4   0% /sys/fs/cgroup
none               94612    288     94324   1% /run
none                5120      0      5120   0% /run/lock
none              473056      0    473056   0% /run/shm
none              102400      0    102400   0% /run/user
/dev/mmcblk0p1     65390  19794     45596  31% /boot/firmware
```

- swap領域を確保する

```
$ sudo apt-get install dphys-swapfile
...

$ cat /proc/swaps
Filename				Type		Size	Used	Priority
/var/swap                               file		1910780	0	-1
```

- Raspberry Pi2のUSBポートに、USB Ethernetアダプタを装着し、ネットワーク設定を行う

```
$ sudo vi /etc/network/interfaces 
...
auto eth0
iface eth0 inet static
address 192.168.100.101
netmask 255.255.255.0
gateway 192.168.100.1

auto eth1
iface eth1 inet static
address 192.168.0.1
netmask 255.255.255.0

auto eth2
iface eth2 inet manual
up ifconfig $IFACE 0.0.0.0 up
up ip link set $IFACE promisc on
down ip link set $IFACE promisc off
down ifconfig $IFACE down
```
　ちなみに、今回は、uplink側に"eth1"を、downlink側に"eth2"を割り当てる予定です。

- DNS解決の設定を反映する

```
sudo vi /etc/resolvconf/resolv.conf.d/base
...
nameserver 192.168.100.1

$ sudo reboot
$ sudo apt-get update
```

##(2) E-VPNルータ（通称：gobgp/goplane）の環境整備

次のようなE-VPN / VXLAN動作環境を作成します。

```
         (eth2) +---------+ (eth1)         mp-BGP               +--------+
... ----------+ | gobgp   | +---------------------------------+ |  BGP   | +---- ...
                |/goplane | 192.168.0.1/30       192.168.0.2/30 | Router |
                +---------+                                     +--------+
                < AS65000 >                                     < AS65000 >

    <-----------  Target  ---------->                           <-- out of scope -->
```

- golangをインストールし、golang動作環境をセットアップする

```
$ wget https://storage.googleapis.com/golang/go1.6.2.linux-armv6l.tar.gz
$ sudo tar -C /usr/local -xzf go1.6.2.linux-armv6l.tar.gz
$ mkdir $HOME/golang
$ vi $HOME/.profile

...(snip)
export GOPATH=$HOME/golang
export PATH=$PATH:$GOPATH/bin:/usr/local/go/bin

$ source .profile
$ go version
go version go1.6.2 linux/arm
```

- glideをインストールする

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

- glide活用により、goplaneをインストールする

```
$ cd $GOPATH/src/github.com
$ mkdir osrg
$ cd osrg
$ git clone https://github.com/osrg/goplane.git
$ cd goplane
$ glide install
$ glide up
$ cd goplaned
$ go install .

$ cd $GOPATH/bin
$ ls -l
total 25604
-rwxrwxr-x 1 ubuntu ubuntu 10504584 Jul 13 11:09 glide
-rwxrwxr-x 1 ubuntu ubuntu 15708831 Jul 13 11:22 goplaned
```

- gobgpをインストールする

```
$ cd $GOPATH/src/github.com/osrg/goplane
$ cd vendor/github.com/osrg/gobgp/gobgp
$ go install .

$ cd $GOPATH/bin
$ ls -l|grep gobgp 
-rwxrwxr-x 1 ubuntu ubuntu 15643728 Jul 13 11:27 gobgp
```

- gobgpdをインストールする

```
$ cd $GOPATH/src/github.com/osrg/goplane
$ cd vendor/github.com/osrg/gobgp/gobgpd
$ go install .

$ cd $GOPATH/bin
$ ls -l|grep gobgpd
-rwxrwxr-x 1 ubuntu ubuntu 18679584 Jul 13 11:31 gobgpd
```

- gobgp設定ファイルを作成する

```text:$HOME/golang/bin/gobgpd.conf
[[neighbors]]
[[neighbors.afi-safis]]
[neighbors.afi-safis.config]
 afi-safi-name = "ipv4-unicast"
[[neighbors.afi-safis]]
[neighbors.afi-safis.config]
 afi-safi-name = "l2vpn-evpn"

[neighbors.config]
 neighbor-address = "192.168.0.2"
 peer-as = 65000
 local-as = 65000

[global.config]
 as = 65000
 router-id = "192.168.0.1"
```

- goplane設定ファイルを作成する

```text:$HOME/golang/bin/goplaned.conf
[dataplane]
 type = "netlink"
[[dataplane.virtual-network-list]]
 member-interfaces = [ "eth2",]
 vni = 10
 vxlan-port = 8472
 rd = "65000:10"
 vtep-interface = "vtep10"
 etag = 10
 sniff-interfaces = [ "eth2",]
```

#◼︎ 実際、gobgp/goplane起動して、E-VPNを試してみる

##(1) gobgp/goplaneを起動する
それでは、実際に、gobgp/goplaneを起動して、E-VPN / VXLANが動作させてみます。

- gobgpdを起動する

```
$ cd $HOME/golang/bin
$ sudo ./gobgpd -f gobgpd.conf
[sudo] password for ubuntu:
{"level":"info","msg":"gobgpd started","time":"2016-07-13T12:30:47Z"}
{"level":"info","msg":"finished reading the config file","time":"2016-07-13T12:30:47Z"}
{"level":"info","msg":"Peer 192.168.0.2 is added","time":"2016-07-13T12:30:47Z"}
{"level":"info","msg":"Add a peer configuration for 192.168.0.2","time":"2016-07-13T12:30:47Z"}
{"Key":"192.168.0.2","State":"BGP_FSM_OPENCONFIRM","Topic":"Peer","level":"info","msg":"Peer Up","time":"2016-07-13T12:33:58Z"}
```

- goplanedを起動する

```
$ cd $HOME/golang/bin
$ sudo ./goplaned -f goplaned.conf
[sudo] password for ubuntu: 
{"level":"info","msg":"VirtualNetwork 65000:10 is added","time":"2016-07-13T12:31:27Z"}
```
　対抗側でも同様に、gobgp/goplaneを起動することにより、BGPピアが開設されました。

##(2) gobgp/goplane動作状態を確認する
別ターミナルより、gobgpコマンドを用いてgobgp/goplane動作状態を、いろいろと確認してみます。

- BGPピア状態を確認する

```
$ gobgp neighbor 192.168.0.2
BGP neighbor is 192.168.0.2, remote AS 65000
  BGP version 4, remote router ID 192.168.0.2
  BGP state = BGP_FSM_ESTABLISHED, up for 00:21:22
  BGP OutQ = 0, Flops = 0
  Hold time is 90, keepalive interval is 30 seconds
  Configured hold time is 90, keepalive interval is 30 seconds
  Neighbor capabilities:
    BGP_CAP_MULTIPROTOCOL:
        ipv4-unicast:   advertised and received
        l2vpn-evpn:     advertised and received
    BGP_CAP_ROUTE_REFRESH:      advertised and received
    BGP_CAP_FOUR_OCTET_AS_NUMBER:       advertised and received
  Message statistics:
                         Sent       Rcvd
    Opens:                  2          2
    Notifications:          0          0
    Updates:                6         13
    Keepalives:            49         49
    Route Refesh:           0          0
    Discarded:              0          0
    Total:                 57         64
  Route statistics:
    Advertised:             3
    Received:               2
    Accepted:               2
```

- VTEP状態を確認する

```
$ gobgp global rib -a evpn
    Network                                                                                            Next Hop             AS_PATH              Age        Attrs
*>  [type:macadv][rd:65000:10][esi:single-homed][etag:10][mac:40:6c:8f:59:31:af][ip:<nil>][labels:[10]]0.0.0.0                                   00:04:50   [{Origin: i} {Extcomms: [VXLAN], [65000:10]}]
*>  [type:macadv][rd:65000:10][esi:single-homed][etag:10][mac:8e:7d:29:85:e9:c6][ip:<nil>][labels:[10]]192.168.0.2                               00:02:03   [{Origin: i} {LocalPref: 100} {Extcomms: [VXLAN], [65000:10]}]
*>  [type:multicast][rd:65000:10][etag:10][ip:192.168.0.1]                                             0.0.0.0                                   00:04:52   [{Origin: i} {Pmsi: type: ingress-repl, label: 0, tunnel-id: 192.168.0.1} {Extcomms: [65000:10]}]
*>  [type:multicast][rd:65000:10][etag:10][ip:192.168.0.2]                                             192.168.0.2                               00:02:21   [{Origin: i} {LocalPref: 100} {Extcomms: [65000:10]} {Pmsi: type: ingress-repl, label: 0, tunnel-id: 192.168.0.2}]
```

問題なく、E-VPN / VXLANは、動作しているようです。

##(3) エンド端末から、DataPlane通信を確認してみる

いよいよ、E-VPN / VXLANを用いて作成されたDataPlane通信路を用いて、実際にデータ流通を確認してみたいと思います。

- 事前準備として、自側インタフェスのネットワーク設定を確認しておく

```
$ ifconfig

...(snip)

en5: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
	ether 40:6c:8f:59:31:af 
	inet6 fe80::426c:8fff:fe59:31af%en5 prefixlen 64 scopeid 0x1c 
	inet 192.168.3.2 netmask 0xffffff00 broadcast 192.168.3.255
	nd6 options=1<PERFORMNUD>
	media: autoselect (100baseTX <full-duplex,flow-control>)
	status: active
```

- いよいよ、Pingコマンドをうってみる

```
$ ping 192.168.3.1
PING 192.168.3.1 (192.168.3.1): 56 data bytes
64 bytes from 192.168.3.1: icmp_seq=0 ttl=64 time=2.917 ms
64 bytes from 192.168.3.1: icmp_seq=1 ttl=64 time=1.327 ms
64 bytes from 192.168.3.1: icmp_seq=2 ttl=64 time=1.354 ms
64 bytes from 192.168.3.1: icmp_seq=3 ttl=64 time=1.311 ms
64 bytes from 192.168.3.1: icmp_seq=4 ttl=64 time=1.283 ms
^C
--- 192.168.3.1 ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.283/1.638/2.917/0.640 ms
```

- さらに、MACアドレスの学習のようすを確認してみる

```
$ arp -a
? (192.168.3.1) at 8e:7d:29:85:e9:c6 on en5 ifscope [ethernet]
```

E-VPN / VXLANにより作成されたDataPlane通信路を用いて、ICMP ECHO Request / Replyが期待どおり動作しました。


##(4) DataPlane通信のようすを、パケットキャプチャで確認してみる

- TCPDUMPによるVXLANパケット内部を確認する

![tcpdump.png](![vxlan.png](https://qiita-image-store.s3.amazonaws.com/0/103595/e377f1fd-620f-bd57-8e6d-3a13a030bad8.png)

VXLANによって、ICMP ECHO Request / Replyが転送されているようすが確認できました！

#◼︎ 終わりに...
今回は、あくまでも、Raspberry Pi2というホワイトボックスを活用して、E-VPNを動作させたに過ぎません。もちろん、DataPlane通信におけるパフォーマンス面は、全く期待できるものではありません。単に、pingが通信できただけです。
しかしながら、従来、ネットワーク機器の内部機能として、ベンダプロプライエタリな領域に、オープンソースなソフトウェアと、ホワイトボックスを組み合わせれば、私たち利用者自らが自由なネットワーク機能ブロックを構築できる将来への可能性を確認できたと思います。
そして、将来は、IoT時代にも、ホワイトボックス製品は、浸透していくものだと思います。

#◼︎ 参照元
- [MacでRaspberry PiにOSを入れるまでのやることリスト](http://qiita.com/hfm/items/96d20d9cc29fb46fd8a3)
- [［画像で解説］ RaspberryPIにUbuntuを入れる方法](http://lean-blog.hatenablog.com/entry/2015/12/13/201723)