VXLAN Routing の検証

# モチベーション
- CLOS Network を想定した VXLAN/EVPNの動作を確認したい
    - SDNコントローラ無しで 自立分散型の Overlay Network を実現したい
- Linux でどのように VXLAN/EPVN を実現しているか?
    - CumulusLinux 上でconfigを投入し、Linuxのnetwork設定がどのように変更されているかを確認
- サーバーエンジニアから見たら触れる機会が少ない(と思われる)ネットワーク技術を使ってみたい
    - BGP, EVPN, VRF, Route Leaking など

# 何をやりたいか
VXLAN/EVPNを使用したOverlay Networkで、異なるToR間における相互接続を検証する.
各ToR配下で稼働する物理サーバ上では異なるネットワークに属するVMやコンテナが動作していると仮定する.

![vxlan-evpn-overview.png](https://qiita-image-store.s3.amazonaws.com/0/323524/d957cc66-db6f-50b4-f8da-75a01d60bad1.png)

動作確認したい点

- VXLAN/EVPN の動作
    - L2VNI や L3VNIなどを経由した通信経路の確認 (Symmetric Routing)
    - BUMの処理(Head End Replication) / 未学習経路への通信時の挙動 
    - EVPN Type-2,3 (overlayの経路を外に広報しないのでType5は未設定)

- 経路確認
    - case1: 異なるToR間で同subnetの疎通
        - 例) (subnet1-1) node1 --> (subnet1-1) node2
        - よく見かけるパターン(OpenvSwitchでremote_ipを指定するなど)
        - `L2VNI`の通信
    - case2: 異なるToR間で別subnetの疎通 (VXLAN Routing)
        - 例) (subnet1-1) node1 --> (subnet1-2) node4
        - `L3VNI`の通信
    - case3: 異なるVRF間で別subnetの疎通
        - 例) (VRF-1) node1 --> (VRF-2) node6
        - `Route Leaking`で疎通可能かどうか (通常は異なるVRFのため通信できない)

# 事前知識

VXLAN 検証を一通りやってみたが、ある程度勉強しないと何がなんだかわからなかったので以下参考. 
数年前の記事だが日本語で詳細が解説してあるので助かりました (それでもEVPN周りの理解は難しい..).

- [VXLAN チュートリアル](https://www.janog.gr.jp/meeting/janog37/download_file/vxlan.pdf)
- [EVPNで変わる・変えられるデータセンターネットワーク](https://www.netone.co.jp/report/column/column1/20160308.html)
- [ジュニパーネットワークス EVPN 次世代データ センターアーキテクチャの実装](https://www.juniper.net/assets/jp/jp/local/pdf/whitepapers/2000606-jp.pdf)
- [EVPN の概要](https://www.cisco.com/c/ja_jp/td/docs/iosxr/ncs5xx/l2vpn/63x/b-l2vpn-cg-63x-ncs5xx/b-l2vpn-cg-63x-ncs5xx_chapter_0111.pdf)

# Setup
動作理解のため必要最小限の設定で組む. (今回は)ネットワーク構成の冗長を考慮しない. 

- [GNS3 2.19](https://gns3.com/)
- [Cumulus VX 3.7](https://cumulusnetworks.com/products/cumulus-vx/)
- (option) [OpenvSwitch](https://gns3.com/marketplace/appliance/open-vswitch)
- (option) [Ubuntu Docker Guest](https://gns3.com/marketplace/appliance/ubuntu)

## GNS3 を使用した環境構築

物理環境を用意するのは大変なのでGNS3で環境を構築する.

- node や OVS は GNS3 組み込みの VPCS とEthernetSwitch で代用可能
    - VLAN が設定できればなんでもよい
- Cumulus-VX
    - sshdも使える軽量linux(Debian)としてnodeのように利用してもいいかもしれない
    - メモリ消費量が300MB程度
    - qcow2 イメージが提供されているので GNS3VM内で起動させるのが手っ取り早い

<img width="682" alt="Screen Shot 2018-12-10 at 19.50.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/323524/2c2fdcd5-e49a-cc2b-6f4a-fb8047aa1236.png">

## Cumulus の使い方
Cumulus には[NCLU](https://docs.cumulusnetworks.com/display/DOCS/Network+Command+Line+Utility+-+NCLU)とよばれる一般的なネットワーク機器ライクなCLIが標準で用意されている. これにより Linuxの`ip`コマンドやルーティングソフトウェア[FRR](https://frrouting.org/)を直接叩かなくても、統一的なインターフェースでL2/L3の設定を投入することができる.　

- CumulsVX へのログインは `cumulus` / `CumulusLinux!` を使用
- `net show config` で設定確認. `net commit` で設定保存
    - `net pending` で commit 前に設定差分を確認
    - `net abort` で設定差分を取り消し
    - ロールバックは `net show commit history` から `net rollback`
    - 設定を完全リセットしたい場合は `net del all` 後に `net commit`
- わからなかったら `Tab` で候補表示
- [公式マニュアル](https://docs.cumulusnetworks.com/display/DOCS) に詳しくまとまっているので読むだけで勉強になる

## Underlay Network
![BGP-underlay.png](https://qiita-image-store.s3.amazonaws.com/0/323524/c7f7aa9a-f1ee-182a-2a74-49463cffff50.png)

ここから設定を投入していく. 設計や設定は一例にすぎないので参考程度に.
Underlay は冗長化等を無視すれば BGPとEVPNを有効化してloopbackを交換するのみ.

- [Cumulus 公式ガイド](https://docs.cumulusnetworks.com/display/DOCS/Border+Gateway+Protocol+-+BGP)
- [iBGP/eBGP について](https://www.infraexpert.com/study/bgpz03.html)
- [BGP Unnumbered の解説](https://adtech.cyberagent.io/techblog/archives/658)

### Spineスイッチの設定

```bash:spine-1
# Hostname
net add hostname spine-1
net add loopback lo ip address 5.5.5.5/32

# Physical IF MTU for VXLAN tunneling
net add interface swp1 mtu 9000
net add interface swp2 mtu 9000
net add interface swp3 mtu 9000
net add interface swp4 mtu 9000

# Underlay BGP
net add bgp autonomous-system 65000
net add bgp router-id 5.5.5.5
net add bgp redistribute connected

# peer-group
net add bgp neighbor LEAF peer-group
net add bgp neighbor swp1 interface peer-group LEAF
net add bgp neighbor swp2 interface peer-group LEAF
net add bgp neighbor swp3 interface peer-group LEAF
net add bgp neighbor swp4 interface peer-group LEAF

# Underlay eBGP Neighbor (spine <--> leaf)
net add bgp neighbor LEAF remote-as external

# Enable EVPN
net add bgp l2vpn evpn neighbor LEAF activate
net add bgp l2vpn evpn advertise-all-vni

# Commit
net pending
net commit

# Hostname を反映させるために一旦抜ける
exit
```

```bash:spine-2
# Hostname
net add hostname spine-2
net add loopback lo ip address 6.6.6.6/32

# Physical IF MTU
net add interface swp1 mtu 9000
net add interface swp2 mtu 9000
net add interface swp3 mtu 9000
net add interface swp4 mtu 9000

# Underlay BGP
net add bgp autonomous-system 65000
net add bgp router-id 6.6.6.6
net add bgp redistribute connected

# peer-group
net add bgp neighbor LEAF peer-group
net add bgp neighbor swp1 interface peer-group LEAF
net add bgp neighbor swp2 interface peer-group LEAF
net add bgp neighbor swp3 interface peer-group LEAF
net add bgp neighbor swp4 interface peer-group LEAF

# Underlay eBGP Neighbor (spine <--> leaf)
net add bgp neighbor LEAF remote-as external

# Enable EVPN
net add bgp l2vpn evpn neighbor LEAF activate
net add bgp l2vpn evpn advertise-all-vni

# Commit
net pending
net commit

# Hostname を反映させるために一旦抜ける
exit
```

### Leafスイッチの設定

```bash:leaf-1
# Hostname
net add hostname leaf-1
net add loopback lo ip address 1.1.1.1/32

# Physical IF MTU
net add interface swp1 mtu 9000
net add interface swp2 mtu 9000

# Underlay BGP
net add bgp autonomous-system 65001
net add bgp router-id 1.1.1.1
net add bgp redistribute connected

# peer-group
net add bgp neighbor SPINE peer-group
net add bgp neighbor swp1 interface peer-group SPINE
net add bgp neighbor swp2 interface peer-group SPINE

# Underlay eBGP Neighbor (leaf <--> spine)
net add bgp neighbor SPINE remote-as external

# Enable EVPN
net add bgp l2vpn evpn neighbor SPINE activate
net add bgp l2vpn evpn advertise-all-vni

# Commit
net pending
net commit

# Hostname を反映させるために一旦抜ける
exit
```

```bash:leaf-2
# Hostname
net add hostname leaf-2
net add loopback lo ip address 2.2.2.2/32

# Physical IF MTU
net add interface swp1 mtu 9000
net add interface swp2 mtu 9000

# Underlay BGP
net add bgp autonomous-system 65002
net add bgp router-id 2.2.2.2
net add bgp redistribute connected

# peer-group
net add bgp neighbor SPINE peer-group
net add bgp neighbor swp1 interface peer-group SPINE
net add bgp neighbor swp2 interface peer-group SPINE

# Underlay eBGP Neighbor (leaf <--> spine)
net add bgp neighbor SPINE remote-as external

# Enable EVPN
net add bgp l2vpn evpn neighbor SPINE activate
net add bgp l2vpn evpn advertise-all-vni

# Commit
net pending
net commit

# Hostname を反映させるために一旦抜ける
exit
```

```bash:leaf-3
# Hostname
net add hostname leaf-3
net add loopback lo ip address 3.3.3.3/32

# Physical IF MTU
net add interface swp1 mtu 9000
net add interface swp2 mtu 9000

# Underlay BGP
net add bgp autonomous-system 65003
net add bgp router-id 3.3.3.3
net add bgp redistribute connected

# peer-group
net add bgp neighbor SPINE peer-group
net add bgp neighbor swp1 interface peer-group SPINE
net add bgp neighbor swp2 interface peer-group SPINE

# Underlay eBGP Neighbor (leaf <--> spine)
net add bgp neighbor SPINE remote-as external

# Enable EVPN
net add bgp l2vpn evpn neighbor SPINE activate
net add bgp l2vpn evpn advertise-all-vni

# Commit
net pending
net commit

# Hostname を反映させるために一旦抜ける
exit
```

```bash:leaf-4
# Hostname
net add hostname leaf-4
net add loopback lo ip address 4.4.4.4/32

# Physical IF MTU
net add interface swp1 mtu 9000
net add interface swp2 mtu 9000

# Underlay BGP
net add bgp autonomous-system 65004
net add bgp router-id 4.4.4.4
net add bgp redistribute connected

# peer-group
net add bgp neighbor SPINE peer-group
net add bgp neighbor swp1 interface peer-group SPINE
net add bgp neighbor swp2 interface peer-group SPINE

# Underlay eBGP Neighbor (leaf <--> spine)
net add bgp neighbor SPINE remote-as external

# Enable EVPN
net add bgp l2vpn evpn neighbor SPINE activate
net add bgp l2vpn evpn advertise-all-vni

# Commit
net pending
net commit

# Hostname を反映させるために一旦抜ける
exit
```

設定後に各スイッチすべてのloopbackに到達可能か確認.

```bash:Check-all-node
# Login to spine-1, spine-2, leaf-1, leaf-2, leaf-3, leaf-4

# Check
net show interface
net show bgp summary
net show bgp
net show route

# cumulus@leaf-1:~$ net show route
# ...
# C>* 1.1.1.1/32 is directly connected, lo, 00:25:10
# B>* 2.2.2.2/32 [20/0] via fe80::e3b:9cff:fecd:9a01, swp1, 00:22:33
# *                   via fe80::e3b:9cff:fe18:5401, swp2, 00:22:33
# B>* 3.3.3.3/32 [20/0] via fe80::e3b:9cff:fecd:9a01, swp1, 00:25:03
# B>* 4.4.4.4/32 [20/0] via fe80::e3b:9cff:fe18:5401, swp2, 00:22:33

# Check
ping 1.1.1.1
ping 2.2.2.2
ping 3.3.3.3
ping 4.4.4.4
ping 5.5.5.5
ping 6.6.6.6
```


## Overlay Network

Overlay Network 図がこちら. 
![overlay-network22.png](https://qiita-image-store.s3.amazonaws.com/0/323524/890cf27a-3f87-a372-9409-3a64c9d77329.png)

config から推測した Cumulus-VX 内部の仮想ネットワーク図がこちら(`leaf-1` `leaf-4`のみ抜粋). 

![cumulus-internal.png](https://qiita-image-store.s3.amazonaws.com/0/323524/0cb023b1-7b98-e8c1-ea68-9a6954337ff6.png)

L2VNI,L3VNIと各VLANの関係、どのインターフェースがどのVRFに所属するか等を意識しながらconfigを投入していかないと混乱しやすい. またVXLANを構成するインターフェース同士は[VLAN-aware-Bridge](https://docs.cumulusnetworks.com/display/DOCS/VLAN-aware+Bridge+Mode) によってひとつの`bridge`に接続されVLANで分離されている.



### Leafスイッチの設定

#### leaf-1 の設定

```bash:leaf-1
#-------------------------------------------------------
# L2 Subnet Gateway
#-------------------------------------------------------
# Downlink
net add interface swp3 bridge trunk vlans 1001,1002,2001

# Mapping SVI <--> VRF
net add vlan 1001 vrf vrf-1
net add vlan 1001 ip address 10.1.1.254/24
net add vlan 1001 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan1001/proxy_arp"

net add vlan 1002 vrf vrf-1
net add vlan 1002 ip address 10.1.2.254/24
net add vlan 1002 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan1002/proxy_arp"

net add vlan 2001 vrf vrf-2
net add vlan 2001 ip address 10.2.1.254/24
net add vlan 2001 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan2001/proxy_arp"

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L2VNI
#-------------------------------------------------------
# Mapping L2VNI and VLAN
net add vxlan L2VNI101001 vxlan id 101001
net add vxlan L2VNI101001 vxlan local-tunnelip 1.1.1.1
net add vxlan L2VNI101001 bridge access 1001
net add vxlan L2VNI101001 bridge learning off
net add vxlan L2VNI101001 bridge arp-nd-suppress on
net add vxlan L2VNI101001 mtu 1550

net add vxlan L2VNI101002 vxlan id 101002
net add vxlan L2VNI101002 vxlan local-tunnelip 1.1.1.1
net add vxlan L2VNI101002 bridge access 1002
net add vxlan L2VNI101002 bridge learning off
net add vxlan L2VNI101002 bridge arp-nd-suppress on
net add vxlan L2VNI101002 mtu 1550

net add vxlan L2VNI102001 vxlan id 102001
net add vxlan L2VNI102001 vxlan local-tunnelip 1.1.1.1
net add vxlan L2VNI102001 bridge access 2001
net add vxlan L2VNI102001 bridge learning off
net add vxlan L2VNI102001 bridge arp-nd-suppress on
net add vxlan L2VNI102001 mtu 1550

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L3VNI
#-------------------------------------------------------
# Mapping VRF and L3VNI
net add vrf vrf-1 vni 101000
net add vrf vrf-2 vni 102000

# Mapping VLAN IF and VRF
net add vlan 1000 vrf vrf-1
net add vlan 2000 vrf vrf-2

# L3VNI (vrf-1)
net add vxlan L3VNI101000 vxlan id 101000
net add vxlan L3VNI101000 vxlan local-tunnelip 1.1.1.1
net add vxlan L3VNI101000 bridge access 1000
net add vxlan L3VNI101000 bridge learning off
net add vxlan L3VNI101000 bridge arp-nd-suppress on
net add vxlan L3VNI101000 mtu 1550

# L3VNI (vrf-2)
net add vxlan L3VNI102000 vxlan id 102000
net add vxlan L3VNI102000 vxlan local-tunnelip 1.1.1.1
net add vxlan L3VNI102000 bridge access 2000
net add vxlan L3VNI102000 bridge learning off
net add vxlan L3VNI102000 bridge arp-nd-suppress on
net add vxlan L3VNI102000 mtu 1550

# Commit
net pending
net commit
```

#### leaf-2 の設定

```bash:leaf-2
#-------------------------------------------------------
# L2 Subnet Gateway
#-------------------------------------------------------
# Downlink
net add interface swp3 bridge trunk vlans 1001

# Mapping SVI <--> VRF
net add vlan 1001 vrf vrf-1
net add vlan 1001 ip address 10.1.1.254/24
net add vlan 1001 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan1001/proxy_arp"

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L2VNI
#-------------------------------------------------------
# Mapping L2VNI and VLAN
net add vxlan L2VNI101001 vxlan id 101001
net add vxlan L2VNI101001 vxlan local-tunnelip 2.2.2.2
net add vxlan L2VNI101001 bridge access 1001
net add vxlan L2VNI101001 bridge learning off
net add vxlan L2VNI101001 bridge arp-nd-suppress on
net add vxlan L2VNI101001 mtu 1550

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L3VNI
#-------------------------------------------------------
# Mapping VRF and L3VNI
net add vrf vrf-1 vni 101000

# Mapping VLAN IF and VRF
net add vlan 1000 vrf vrf-1

# L3VNI (vrf-1)
net add vxlan L3VNI101000 vxlan id 101000
net add vxlan L3VNI101000 vxlan local-tunnelip 2.2.2.2
net add vxlan L3VNI101000 bridge access 1000
net add vxlan L3VNI101000 bridge learning off
net add vxlan L3VNI101000 bridge arp-nd-suppress on
net add vxlan L3VNI101000 mtu 1550

# Commit
net pending
net commit
```

#### leaf-3 の設定

```bash:leaf-3
#-------------------------------------------------------
# L2 Subnet Gateway
#-------------------------------------------------------
# Downlink
net add interface swp3 bridge trunk vlans 2001

# Mapping SVI <--> VRF
net add vlan 2001 vrf vrf-2
net add vlan 2001 ip address 10.2.1.254/24
net add vlan 2001 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan2001/proxy_arp"

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L2VNI
#-------------------------------------------------------
# Mapping L2VNI and VLAN
net add vxlan L2VNI102001 vxlan id 102001
net add vxlan L2VNI102001 vxlan local-tunnelip 3.3.3.3
net add vxlan L2VNI102001 bridge access 2001
net add vxlan L2VNI102001 bridge learning off
net add vxlan L2VNI102001 bridge arp-nd-suppress on
net add vxlan L2VNI102001 mtu 1550

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L3VNI
#-------------------------------------------------------
# Mapping VRF and L3VNI
net add vrf vrf-2 vni 102000

# Mapping VLAN IF and VRF
net add vlan 2000 vrf vrf-2

# L3VNI (vrf-2)
net add vxlan L3VNI102000 vxlan id 102000
net add vxlan L3VNI102000 vxlan local-tunnelip 3.3.3.3
net add vxlan L3VNI102000 bridge access 2000
net add vxlan L3VNI102000 bridge learning off
net add vxlan L3VNI102000 bridge arp-nd-suppress on
net add vxlan L3VNI102000 mtu 1550

# Commit
net pending
net commit
```

#### leaf-4 の設定

```bash:leaf-4
#-------------------------------------------------------
# L2 Subnet Gateway
#-------------------------------------------------------
# Downlink
net add interface swp3 bridge trunk vlans 1001,1002,2001

# Mapping SVI <--> VRF
net add vlan 1001 vrf vrf-1
net add vlan 1001 ip address 10.1.1.254/24
net add vlan 1001 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan1001/proxy_arp"

net add vlan 1002 vrf vrf-1
net add vlan 1002 ip address 10.1.2.254/24
net add vlan 1002 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan1002/proxy_arp"

net add vlan 2001 vrf vrf-2
net add vlan 2001 ip address 10.2.1.254/24
net add vlan 2001 post-up "echo 1 > /proc/sys/net/ipv4/conf/vlan2001/proxy_arp"

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L2VNI
#-------------------------------------------------------
# Mapping L2VNI and VLAN
net add vxlan L2VNI101001 vxlan id 101001
net add vxlan L2VNI101001 vxlan local-tunnelip 4.4.4.4
net add vxlan L2VNI101001 bridge access 1001
net add vxlan L2VNI101001 bridge learning off
net add vxlan L2VNI101001 bridge arp-nd-suppress on
net add vxlan L2VNI101001 mtu 1550

net add vxlan L2VNI101002 vxlan id 101002
net add vxlan L2VNI101002 vxlan local-tunnelip 4.4.4.4
net add vxlan L2VNI101002 bridge access 1002
net add vxlan L2VNI101002 bridge learning off
net add vxlan L2VNI101002 bridge arp-nd-suppress on
net add vxlan L2VNI101002 mtu 1550

net add vxlan L2VNI102001 vxlan id 102001
net add vxlan L2VNI102001 vxlan local-tunnelip 4.4.4.4
net add vxlan L2VNI102001 bridge access 2001
net add vxlan L2VNI102001 bridge learning off
net add vxlan L2VNI102001 bridge arp-nd-suppress on
net add vxlan L2VNI102001 mtu 1550

# Commit
net pending
net commit

#-------------------------------------------------------
# VXLAN L3VNI
#-------------------------------------------------------
# Mapping VRF and L3VNI
net add vrf vrf-1 vni 101000
net add vrf vrf-2 vni 102000

# Mapping VLAN IF and VRF
net add vlan 1000 vrf vrf-1
net add vlan 2000 vrf vrf-2

# L3VNI (vrf-1)
net add vxlan L3VNI101000 vxlan id 101000
net add vxlan L3VNI101000 vxlan local-tunnelip 4.4.4.4
net add vxlan L3VNI101000 bridge access 1000
net add vxlan L3VNI101000 bridge learning off
net add vxlan L3VNI101000 bridge arp-nd-suppress on
net add vxlan L3VNI101000 mtu 1550

# L3VNI (vrf-2)
net add vxlan L3VNI102000 vxlan id 102000
net add vxlan L3VNI102000 vxlan local-tunnelip 4.4.4.4
net add vxlan L3VNI102000 bridge access 2000
net add vxlan L3VNI102000 bridge learning off
net add vxlan L3VNI102000 bridge arp-nd-suppress on
net add vxlan L3VNI102000 mtu 1550

# Commit
net pending
net commit
```

### openvSwitch の設定

openvSwitchの設定は `ovs-1`と`ovs-4`は共通

```bash:ovs-1
# initialization
ovs-vsctl init

# br0 からinterfaceを外す
ovs-vsctl list-br
ovs-vsctl del-br br0

# Access port & Trunk
ovs-vsctl add-port br1 eth0 trunks=1001,1002,2001
ovs-vsctl add-port br1 eth1 tag=1001
ovs-vsctl add-port br1 eth2 tag=1002
ovs-vsctl add-port br1 eth3 tag=2001
ip link set br1 up
ovs-vsctl show

# / # ovs-vsctl show
# 09fd174b-738f-417b-b0f0-e9303f30601f
#     Bridge "br1"
#         Port "eth0"
#             trunks: [1001, 1002, 2001]
#             Interface "eth0"
#         Port "eth2"
#             tag: 1002
#             Interface "eth2"
#         Port "eth1"
#             tag: 1001
#             Interface "eth1"
#         Port "br1"
#             Interface "br1"
#                 type: internal
#         Port "eth3"
#             tag: 2001
#             Interface "eth3"
#...
```

```bash:ovs-2
# initialization
ovs-vsctl init

# br0 からinterfaceを外す
ovs-vsctl list-br
ovs-vsctl del-br br0

# Access port & Trunk
ovs-vsctl add-port br1 eth0 trunks=1001
ovs-vsctl add-port br1 eth1 tag=1001
ip link set br1 up
ovs-vsctl show
```

```bash:ovs-3
# initialization
ovs-vsctl init

# br0 からinterfaceを外す
ovs-vsctl list-br
ovs-vsctl del-br br0

# Access port & Trunk
ovs-vsctl add-port br1 eth0 trunks=2001
ovs-vsctl add-port br1 eth1 tag=2001
ip link set br1 up
ovs-vsctl show
```

### node の設定

コンテナにもIPアドレスを設定する.　ipコマンドがなかったため伝統的手法で設定.

```bash:node-1
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.1.1.1
   netmask 255.255.255.0
   network 10.1.1.0
   gateway 10.1.1.254
EOF
ifconfig eth0 10.1.1.1 netmask 255.255.255.0
route add default gw 10.1.1.254 dev eth0
```

```bash:node-2
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.1.1.2
   netmask 255.255.255.0
   network 10.1.1.0
   gateway 10.1.1.254
EOF
ifconfig eth0 10.1.1.2 netmask 255.255.255.0
route add default gw 10.1.1.254 dev eth0
```

```bash:node-3
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.1.2.3
   netmask 255.255.255.0
   network 10.1.2.0
   gateway 10.1.2.254
EOF
ifconfig eth0 10.1.2.3 netmask 255.255.255.0
route add default gw 10.1.2.254 dev eth0
```

```bash:node-4
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.1.2.4
   netmask 255.255.255.0
   network 10.1.2.0
   gateway 10.1.2.254
EOF
ifconfig eth0 10.1.2.4 netmask 255.255.255.0
route add default gw 10.1.2.254 dev eth0
```

```bash:node-5
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.2.1.5
   netmask 255.255.255.0
   network 10.2.1.0
   gateway 10.2.1.254
EOF
ifconfig eth0 10.2.1.5 netmask 255.255.255.0
route add default gw 10.2.1.254 dev eth0
```

```bash:node-6
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.2.1.6
   netmask 255.255.255.0
   network 10.2.1.0
   gateway 10.2.1.254
EOF
ifconfig eth0 10.2.1.6 netmask 255.255.255.0
route add default gw 10.2.1.254 dev eth0
```

```bash:node-7
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.1.1.7
   netmask 255.255.255.0
   network 10.1.1.0
   gateway 10.1.1.254
EOF
ifconfig eth0 10.1.1.7 netmask 255.255.255.0
route add default gw 10.1.1.254 dev eth0
```

```bash:node-8
cat > /etc/network/interfaces <<EOF
auto eth0
iface eth0 inet static
   address 10.2.1.8
   netmask 255.255.255.0
   network 10.2.1.0
   gateway 10.2.1.254
EOF
ifconfig eth0 10.2.1.8 netmask 255.255.255.0
route add default gw 10.2.1.254 dev eth0
```

# 動作確認
`node-1`から対向ホストの各subnetにpingで疎通確認を実施する.
想定どおり VXLAN Routing による疎確認ができた.

```bash:ping-check
# case1: 異なるToR間で同subnetの疎通 (VXLAN Unicast) --> OK
# node-1(10.1.1.1/24) --> node2(10.1.1.2/24)
root@node-1:~# ping -c1 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=3.71 ms

# case2: 異なるToR間で別subnetの疎通 (VXLAN Routing) --> OK
# node-1(10.1.1.1/24@subnet1-1) --> node-4(10.1.2.4/24@subnet1-2)
root@node-1:~# ping -c1 10.1.2.4
PING 10.1.2.4 (10.1.2.4) 56(84) bytes of data.
64 bytes from 10.1.2.4: icmp_seq=1 ttl=62 time=5.10 ms

# case3: 異なるVRF間で別subnetの疎通 --> Failed
# node1(10.1.1.1/24@vrf-1) --> node-6(10.2.1.6/24@vrf-2)
root@node-1:~# ping -c1 10.2.1.6
PING 10.2.1.6 (10.2.1.6) 56(84) bytes of data.
From 10.1.1.254 icmp_seq=1 Destination Host Unreachable
```

case-3 について、`vrf-1` のルーティングテーブルを確認する. `10.2.1.0/24` は宛先がないので破棄されている.

```bash:leaf-1
cumulus@leaf-1:~$ net show route vrf vrf-1
show ip route vrf vrf-1
========================
 ...
VRF vrf-1:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:24:22 # <--- エントリにない通信は破棄
C>* 10.1.1.0/24 is directly connected, vlan1001, 00:24:20
B>* 10.1.1.7/32 [20/0] via 2.2.2.2, vlan1000 onlink, 00:24:15 # <--- 宛先が Host-2のloopback
C>* 10.1.2.0/24 is directly connected, vlan1002, 00:24:20
B>* 10.1.2.4/32 [20/0] via 4.4.4.4, vlan1000 onlink, 00:24:15 # <--- 宛先が Host-2のloopback
```

一度 node-6 で node-8 に通信を発生させてみると、EVPNに情報が載ってくる.
`10.2.1.6` のMACアドレスとremote VTEP `4.4.4.4`の情報が広報されているのがわかる(経路が2つあるので2つのパスが表示されている).

```bash:leaf-1
cumulus@leaf-1:~$ net show bgp evpn route type macip
... 抜粋
Route Distinguisher: 4.4.4.4:2
*  [2]:[0]:[0]:[48]:[da:69:91:93:e5:97]　4.4.4.4     0 65000 65004 i
*> [2]:[0]:[0]:[48]:[da:69:91:93:e5:97]　4.4.4.4     0 65000 65004 i
*  [2]:[0]:[0]:[48]:[da:69:91:93:e5:97]:[32]:[10.2.1.6]　4.4.4.4   0 65000 65004 i
*> [2]:[0]:[0]:[48]:[da:69:91:93:e5:97]:[32]:[10.2.1.6]　4.4.4.4   0 65000 65004 i
```

`vrf-2` のルーティングテーブルには`10.2.1.6`の経路が乗っていることが確認できる.　
後述の`Route Leaking` で`vrf-2`のルーティングテーブルを`vrf-1`にリークしてみる.

```bash:leaf-1
cumulus@leaf-1:~$ net show route vrf vrf-2
show ip route vrf vrf-2
========================
...
VRF vrf-2:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:18:05
C>* 10.2.1.0/24 is directly connected, vlan2001, 00:18:05
B>* 10.2.1.6/32 [20/0] via 4.4.4.4, vlan2000 onlink, 00:02:14 # <-- check
B>* 10.2.1.8/32 [20/0] via 3.3.3.3, vlan2000 onlink, 00:02:14
```

## Route Leaking

Cumulus は `Route Leaking` をサポートしているのでVRF間の通信を試してみる. テナントデザインの際には最大VRF数に注意する必要がある.　本環境向けに`leaf-1`と`leaf-4`に設定する.

- [公式ガイド](https://docs.cumulusnetworks.com/display/DOCS/Virtual+Routing+and+Forwarding+-+VRF)
- EVPN環境だと Static Route Leaking のみサポート
- Leak したルートをデフォルトルートにするべきではない
- VRF数は最大255までサポート

```bash:leaf-1
# Enable the VRF route leaking option
sudo sed -i -e "s/#vrf_route_leak_enable = FALSE/vrf_route_leak_enable = TRUE/g" /etc/cumulus/switchd.conf
sudo systemctl restart switchd.service

# vrf-2 のルートを vrf-1 にリーク
net add routing route 10.2.1.0/24 vrf-2 vrf vrf-1 nexthop-vrf vrf-2

# vrf-1 のルートを vrf-2 にリーク
net add routing route 10.1.1.0/24 vrf-1 vrf vrf-2 nexthop-vrf vrf-1
net add routing route 10.1.2.0/24 vrf-1 vrf vrf-2 nexthop-vrf vrf-1

# Commit
net pending
net commit
```

`vrf-1`と`vrf-2`の経路について確認後に ping 疎通も確認する.

```bash:leaf-1
root@leaf-1:~# net show route vrf vrf-2
show ip route vrf vrf-1
========================
...
VRF vrf-1:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:58:33
C>* 10.1.1.0/24 is directly connected, vlan1001, 00:58:31
B>* 10.1.1.2/32 [20/0] via 4.4.4.4, vlan1000 onlink, 00:58:26
C>* 10.1.2.0/24 is directly connected, vlan1002, 00:58:31
B>* 10.1.2.4/32 [20/0] via 4.4.4.4, vlan1000 onlink, 00:58:26
S>* 10.2.1.0/24 [1/0] is directly connected, vrf-2(vrf vrf-2), 00:01:04 # <--- vrf-2の経路が static として追加

root@leaf-1:~# net show route vrf vrf-2
show ip route vrf vrf-2
========================
...
VRF vrf-2:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:59:45
S>* 10.1.1.0/24 [1/0] is directly connected, vrf-1(vrf vrf-1), 00:02:17 # <--- vrf-1の経路が追加
S>* 10.1.2.0/24 [1/0] is directly connected, vrf-1(vrf vrf-1), 00:02:17 # <--- vrf-1の経路が追加
C>* 10.2.1.0/24 is directly connected, vlan2001, 00:59:44
B>* 10.2.1.6/32 [20/0] via 4.4.4.4, vlan2000 onlink, 00:59:39

# VRF-1 から VRF-2 への疎通OK
root@node-1:~# ping -c1 10.2.1.6
PING 10.2.1.6 (10.2.1.6) 56(84) bytes of data.
64 bytes from 10.2.1.6: icmp_seq=1 ttl=62 time=5.35 ms
```

## 経路確認

実際にどのような経路でパケットがルーティングされているかを`leaf`スイッチ内でパケットキャプチャを取りながら確認する. 既に全nodeのEVPN Type-2ルートが広報されている状態とする.

```bash:case-1
# case-1: 異なるToR間で同subnetの疎通 (VXLAN Unicast) --> OK
# node-1(10.1.1.1/24)@leaf-1 --> node-2(10.1.1.2/24)@leaf-4

行き) node-1 --> L2VNI101001@leaf-1 --> L2VNI101001@leaf-4 --> bridge --> node-2
戻り) node-2 --> L2VNI101001@leaf-4 --> L2VNI101001@leaf-1 --> bridge --> node-1
```
case1 は`L2VNI`を経由して往路と復路が同じ経路となっている.
![case1-cumulus-internal.png](https://qiita-image-store.s3.amazonaws.com/0/323524/c364cd22-692a-4773-d103-025819a479c2.png)

```bash:case-2
# case2: 異なるToR間で別subnetの疎通 (VXLAN Routing) --> OK
# node-1(10.1.1.1/24)@leaf-1 --> node-4(10.1.2.4/24)@leaf-4

行き) node-1 --> vlan1001@leaf-1 --> L3VNI101000@leaf-1 --> L3VNI101000@leaf-4 --> vlan1002 --> node-4
戻り) node-4 --> vlan1002@leaf-4 --> L3VNI101000@leaf-4 --> L3VNI101000@leaf-1 --> vlan1001 --> node-1
```
case-2 は`L3VNI`を経由して往路と復路が同じ経路となっている.
![case2-cumulus-internal.png](https://qiita-image-store.s3.amazonaws.com/0/323524/d2de4ea1-42a8-e2d5-359f-5362549f4c91.png)

```bash:case-3
# case3: 異なるVRF間で別subnetの疎通 --> OK
# (vrf-1)node-1(10.1.1.1/24)@leaf-1 --> (vrf-2)node-6(10.2.1.6/24)@leaf-4

行き) node-1 --> (vrf-1)vlan1001 --> (vrf-2)L3VNI102000@leaf-1 --> (vrf-2)L3VNI102000@leaf-4 --> vlan2001 --> node-6
戻り) node-6 --> (vrf-2)vlan2001 --> (vrf-1)L3VNI101000@leaf-4 --> (vrf-1)L3VNI101000@leaf-1 --> vlan1001 --> node-1
```
case-3 は往路と復路で異なる経路となる.
![case3-cumulus-internal.png](https://qiita-image-store.s3.amazonaws.com/0/323524/7481b910-ced4-e7a8-36de-fa8f8f1cfc2c.png)


## 確認コマンド
設定の確認やトラブルシューティングに使えそうなコマンドを列挙する.  

```bash:cumulus-cmd
#----------------------------------------------------
# Basic
#----------------------------------------------------
net show system                # version 確認
net show configuration         # config 情報 (commit後)
net show interface             # 物理/仮想 interface 一覧
net show interface swp1 detail # IF詳細
net show lldp                  # 物理IFと対向スイッチIF間の接続の対応
sudo tcpdump -i any port 4789  # VXLAN 通信のキャプチャ

#----------------------------------------------------
# Underlay Network
#----------------------------------------------------
net show route        # Underlay のルーティングテーブル
net show bgp summary  # BGP Neighbor の情報

#----------------------------------------------------
# VXLAN/EVPN
#----------------------------------------------------
net show bgp evpn route                # EVPN Type 情報の一覧
net show bgp evpn route type macip     # EVPN Type2 情報
net show bgp evpn route type multicast # EVPN Type3 情報
net show bgp evpn route type prefix    # EVPN Type5 情報
net show bgp evpn vni                  # LocalのVNI/VRF の一覧
net show evpn mac vni all              # MAC/VTEP/VLAN の一覧
net show evpn arp-cache vni all        # Subnet/VM/MAC の一覧
net show evpn next-hops vni all        # VTEP/MAC の一覧

#----------------------------------------------------
# Overlay Network
#----------------------------------------------------
net show route vrf vrf-1       # VRF-1 のルーティングテーブル
net show route vrf vrf-2       # VRF-2 のルーティングテーブル
net show bgp vrf vrf-1 summary # VRF-1 のBGP情報
net show bgp vrf vrf-2 summary # VRF-2 のBGP情報
net show vrf vni               # VRF/L2VNI/L3VNI の対応一覧

#----------------------------------------------------
# L2
#----------------------------------------------------
net show bridge macs # VLAN/IF/MACの一覧
net show bridge vlan # LocalのIF/VLAN/VNI の対応一覧
```

# 考察

## EVPN について

EVPN(Ethernet VPN)の理解が曖昧だったのだが、以下の理解で落ち着いた.

- EVPN とはIPアドレスとそれに対応するMACをBGPで広報する仕組みのこと
- Underlayにブロードキャストを流すことなく宛先MACを知ることができる
- EVPN には扱う情報によって複数のタイプが存在する
    - Type-2: MAC/IP 広報ルート
        - 各VTEP配下のIPアドレスと対応するMACアドレスを学習可能
        - leaf が配下のノードのMACを学習すると即時に各VTEPにBGPで広報される
        - データセンター内のVXLAN通信は Type-2 が主役
    - Type-3: Inclusive Multicast ルート
        - ネットワーク上のVTEPの検出に使用
    - Type-5: IP Prefix ルート
        - Overlay の経路情報を外部に広報するのに使用
        - 各VTEP配下のサブネット情報を学習可能
        - データセンター間接続(DCI)の際にも使用される
        - EVPNはもともとDCIを念頭に設計されたプロトコル

以下、`leaf-1`におけるEVPN情報の出力結果を(若干整形したものを)ペーストする.

```bash:evpn-info
cumulus@leaf-1:~$ net show bgp evpn route

BGP table version is 3, local router ID is 1.1.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
Origin codes: i - IGP, e - EGP, ? - incomplete
EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]
EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]
EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]
    Network          Next Hop            Metric LocPrf Weight Path

# leaf-1 が広報しているEVPN情報が表示されている
# leaf-1 に存在する3ノードのIPアドレスとMACが Type-2 として広報されている
# leaf の起動直後は Type-2 の情報がないため Type-3 をもとに Ingress Replication される(後述)
Route Distinguisher: 1.1.1.1:2
*> [2]:[0]:[0]:[48]:[5a:51:82:5f:27:4d] 1.1.1.1                  32768 i
*> [2]:[0]:[0]:[48]:[5a:51:82:5f:27:4d]:[32]:[10.2.1.5] 1.1.1.1  32768 i
*> [3]:[0]:[32]:[1.1.1.1] 1.1.1.1                                32768 i
Route Distinguisher: 1.1.1.1:3
*> [2]:[0]:[0]:[48]:[c6:f5:ba:4e:a9:4d] 1.1.1.1                  32768 i
*> [2]:[0]:[0]:[48]:[c6:f5:ba:4e:a9:4d]:[32]:[10.1.1.1] 1.1.1.1  32768 i
*> [3]:[0]:[32]:[1.1.1.1] 1.1.1.1                                32768 i
Route Distinguisher: 1.1.1.1:5
*> [2]:[0]:[0]:[48]:[96:9d:94:2f:42:dd] 1.1.1.1                  32768 i
*> [2]:[0]:[0]:[48]:[96:9d:94:2f:42:dd]:[32]:[10.1.2.3] 1.1.1.1  32768 i
*> [3]:[0]:[32]:[1.1.1.1] 1.1.1.1                                32768 i

# leaf-2 から受け取ったEVPN情報. 
Route Distinguisher: 2.2.2.2:3
*  [2]:[0]:[0]:[48]:[36:37:55:b5:bb:6f] 2.2.2.2                  0 65000 65002 i
*> [2]:[0]:[0]:[48]:[36:37:55:b5:bb:6f] 2.2.2.2                  0 65000 65002 i
*  [2]:[0]:[0]:[48]:[36:37:55:b5:bb:6f]:[32]:[10.1.1.7] 2.2.2.2  0 65000 65002 i
*> [2]:[0]:[0]:[48]:[36:37:55:b5:bb:6f]:[32]:[10.1.1.7] 2.2.2.2  0 65000 65002 i
*  [3]:[0]:[32]:[2.2.2.2] 2.2.2.2                                0 65000 65002 i
*> [3]:[0]:[32]:[2.2.2.2] 2.2.2.2                                0 65000 65002 i

# leaf-3 から受け取ったEVPN情報
Route Distinguisher: 3.3.3.3:2
*  [2]:[0]:[0]:[48]:[ca:a1:38:9a:05:93] 3.3.3.3                  0 65000 65003 i
*> [2]:[0]:[0]:[48]:[ca:a1:38:9a:05:93] 3.3.3.3                  0 65000 65003 i
*  [2]:[0]:[0]:[48]:[ca:a1:38:9a:05:93]:[32]:[10.2.1.8] 3.3.3.3  0 65000 65003 i
*> [2]:[0]:[0]:[48]:[ca:a1:38:9a:05:93]:[32]:[10.2.1.8] 3.3.3.3  0 65000 65003 i
*  [3]:[0]:[32]:[3.3.3.3] 3.3.3.3                                0 65000 65003 i
*> [3]:[0]:[32]:[3.3.3.3] 3.3.3.3                                0 65000 65003 i

# leaf-4 から受け取った情報
Route Distinguisher: 4.4.4.4:2
*  [2]:[0]:[0]:[48]:[32:2d:be:b5:f5:53] 4.4.4.4                  0 65000 65004 i
*> [2]:[0]:[0]:[48]:[32:2d:be:b5:f5:53] 4.4.4.4                  0 65000 65004 i
*  [2]:[0]:[0]:[48]:[32:2d:be:b5:f5:53]:[32]:[10.2.1.6] 4.4.4.4  0 65000 65004 i
*> [2]:[0]:[0]:[48]:[32:2d:be:b5:f5:53]:[32]:[10.2.1.6] 4.4.4.4  0 65000 65004 i
*  [3]:[0]:[32]:[4.4.4.4] 4.4.4.4                                0 65000 65004 i
*> [3]:[0]:[32]:[4.4.4.4] 4.4.4.4                                0 65000 65004 i
Route Distinguisher: 4.4.4.4:3
*> [2]:[0]:[0]:[48]:[7a:dd:0c:ae:22:45] 4.4.4.4                  0 65000 65004 i
*  [2]:[0]:[0]:[48]:[7a:dd:0c:ae:22:45] 4.4.4.4                  0 65000 65004 i
*> [2]:[0]:[0]:[48]:[7a:dd:0c:ae:22:45]:[32]:[10.1.1.2] 4.4.4.4  0 65000 65004 i
*  [2]:[0]:[0]:[48]:[7a:dd:0c:ae:22:45]:[32]:[10.1.1.2] 4.4.4.4  0 65000 65004 i
*  [3]:[0]:[32]:[4.4.4.4] 4.4.4.4                                0 65000 65004 i
*> [3]:[0]:[32]:[4.4.4.4] 4.4.4.4                                0 65000 65004 i
Route Distinguisher: 4.4.4.4:5
*> [2]:[0]:[0]:[48]:[52:ae:ca:21:eb:63] 4.4.4.4                  0 65000 65004 i
*  [2]:[0]:[0]:[48]:[52:ae:ca:21:eb:63] 4.4.4.4                  0 65000 65004 i
*> [2]:[0]:[0]:[48]:[52:ae:ca:21:eb:63]:[32]:[10.1.2.4] 4.4.4.4  0 65000 65004 i
*  [2]:[0]:[0]:[48]:[52:ae:ca:21:eb:63]:[32]:[10.1.2.4] 4.4.4.4  0 65000 65004 i
*  [3]:[0]:[32]:[4.4.4.4] 4.4.4.4                                0 65000 65004 i
*> [3]:[0]:[32]:[4.4.4.4] 4.4.4.4                                0 65000 65004 i
Displayed 32 prefixes (52 paths)
```

## BUM について
EVPNには、Underlay にそのままブロードキャストを流すのではなく、送信元VTEPでVNIに基づいてパケットを複製して複数の対向VTEPに送信する仕組みがある. これは`Ingress Replication`と呼ばれている. これによりUnderlayネットワークがマルチキャストに対応する必要なく VTEPで効率的にBUMトラフィックを処理(ユニキャスト変換)できるようになった.
Cumulus において`Ingress Replication`は [Head End Replication](https://docs.cumulusnetworks.com/display/DOCS/Lightweight+Network+Virtualization+Overview#LightweightNetworkVirtualizationOverview-head-end) (HER)と呼ばれている. マニュアルによると最大128台の制限とあるので、同じsubnet(VNI)を持つVTEPの数に注意する必要がある. Whitebox スイッチだと搭載チップによるが、ASICが高速処理してくれるのでパケット複製のオーバーヘッドは気にしなくていいようだ.

## 未学習経路への通信について
例えばUPしたばかりのnodeに対して通信が発生した場合、そのnodeへの EVPN Type-2 経路は送信元leafのルーティングテーブルにはまだ存在していない.　
結論として EVPN Type-2 の経路はなくても問題なく通信できるし、nodeへの通信を発生させれば自動でルーティングテーブルに最適経路が乗ってくるのであまり意識しなくてもいいのだが、実際どのようにして最初にMACを学習しているのか気になったのでケースに分けて考えてみる. どのケースにおいても共通動作は ARP Request を`L2VNI`で`Ingress Replication`して宛先MACを持つVTEPをどうにかして見つけ出すことである.

- case1: 同サブネット
    - 例) `node-5(10.2.1.5)@leaf-1` --> `node-6(10.2.1.6)@leaf-4`
- case2: 送信元VTEPが宛先サブネットを持つ場合
    - 例) `node-1(10.1.1.1)@leaf-1` --> `node-4(10.1.2.4)@leaf-4`
- case3: 送信元VTEPが宛先サブネットを持たない場合
    - 例) `node-7(10.1.1.7)@leaf-2` --> `node-3(10.1.2.3)@leaf-1`

case1 についてVXLANトラフィックのパケットキャプチャの結果をもとに挙動を確認する. 

```bash:case1
cumulus@leaf-1:~$ sudo tcpdump -i any port 4789

# 10.2.1.5 から見て10.2.1.6 は同サブネットだがMACを知らないので ARP Request をブロードキャスト
# ブロードキャストは以下に伝搬する
# 1. 自身の 10.2.1.0/24 のサブネット
# 2. VNI102001 を持つVTEP 3.3.3.3 と 4.4.4.4 (Ingress Replication)
    # (Underlay にはブロードキャストでなく VXLANのユニキャストトラフィックしか流れない)
15:49:01.912781 IP 1.1.1.1.40612 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 102001
ARP, Request who-has 10.2.1.6 tell 10.2.1.5, length 46
15:49:01.914379 IP 1.1.1.1.40612 > 3.3.3.3.4789: VXLAN, flags [I] (0x08), vni 102001
ARP, Request who-has 10.2.1.6 tell 10.2.1.5, length 46

# 4.4.4.4 と 3.3.3.3 はVXLANを解き ARP Request を自身のVNI102001サブネットにブロードキャスト
# 4.4.4.4 で稼働していた 10.2.1.6 は ARP Request を受信して ARP Reply を返す
15:49:01.928885 IP 4.4.4.4.51126 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 102001
ARP, Reply 10.2.1.6 is-at 5a:b4:ce:11:a8:cd (oui Unknown), length 46

# 1.1.1.1 は ARP Reply を 10.2.1.5 に転送
# 10.2.1.5 は 10.2.1.6 の宛先MACを学習したので ICMP で通信を開始
15:49:01.936788 IP 1.1.1.1.45513 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 102001
IP 10.2.1.5 > 10.2.1.6: ICMP echo request, id 61, seq 1, length 64
15:49:01.943692 IP 4.4.4.4.60994 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 102001
IP 10.2.1.6 > 10.2.1.5: ICMP echo reply, id 61, seq 1, length 64
```

case2 について `node-1(10.1.1.1)@leaf-1` --> `node-4(10.1.2.4)@leaf-4`への通信を確認する. 送信元leafが宛先サブネットを持つので、`L3VNI`でなく先に内部ルーティングされてから`L2VNI` で宛先MACを探しに行く.

```bash:leaf-1-vrf-1-route
# leaf-1 の ルーティングテーブルを確認
cumulus@leaf-1:~$ net show route vrf vrf-1
...
VRF vrf-1:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:52:36
C>* 10.1.1.0/24 is directly connected, vlan1001, 00:52:36
C>* 10.1.2.0/24 is directly connected, vlan1002, 00:52:36 # <-- 10.1.2.0/24 への経路が存在
```

```bash:case2
cumulus@leaf-1:~$ sudo tcpdump -i any port 4789

# 10.1.1.1 から 10.1.2.4 へのパケットは subnet1-1 のゲートウェイ(10.1.1.254) へ転送
# 10.1.1.254 は vrf-1 のルーティングテーブルを元に 10.1.2.254 へ内部ルーティング
# 10.1.2.254 は 10.1.2.4 のMACを知らないので ARP Request をブロードキャスト
# ブロードキャストは以下に伝搬する
    # 1. 自身の 10.1.2.0/24 サブネット
    # 2. VNI101002 を持つVTEP 4.4.4.4 (Ingress Replication)
16:32:42.555356 IP 1.1.1.1.40612 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 101002
ARP, Request who-has 10.1.2.4 tell 10.1.2.254, length 28

# 4.4.4.4 は VXLAN を解き、ARP Request を自身の VNI101002 サブネットにブロードキャスト
# 10.1.2.4 は ARP Request を受信し、ARP Reply を 1.1.1.1 に返す
16:32:42.581769 IP 4.4.4.4.51126 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101002
ARP, Reply 10.1.2.4 is-at ca:b4:59:30:93:f2 (oui Unknown), length 46
# 1.1.1.1 は ARP Reply を 10.1.2.254 に転送する

# 10.1.2.254 は 10.1.2.4 の MAC を学習したので ICMP通信を開始
# Type-2 ルートがまだないので L2VNI101002 を使って VXLAN
16:32:42.582062 IP 1.1.1.1.34723 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 101002
IP 10.1.1.1 > 10.1.2.4: ICMP echo request, id 62, seq 1, length 64
16:32:42.599751 IP 4.4.4.4.39390 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101002
IP 10.1.2.4 > 10.1.1.1: ICMP echo reply, id 62, seq 1, length 64

# 10.1.2.4 の EVPN Type-2 ルートが広報されたタイミングで L3VNI 経由に切り替えて通信開始
# Type-2　ルートは /32 なのでロンゲストマッチで/24より優先される
16:32:43.554157 IP 1.1.1.1.34723 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 101000
IP 10.1.1.1 > 10.1.2.4: ICMP echo request, id 62, seq 2, length 64

# 4.4.4.4 の方はまだ 10.1.1.1 の Type-2 ルートを持っていないようだ
16:32:43.574385 IP 4.4.4.4.39390 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101002
IP 10.1.2.4 > 10.1.1.1: ICMP echo reply, id 62, seq 2, length 64

# 以降はL3VNI経由の通信となる
16:32:44.554186 IP 1.1.1.1.34723 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 101000
IP 10.1.1.1 > 10.1.2.4: ICMP echo request, id 62, seq 3, length 64
16:32:44.563471 IP 4.4.4.4.39390 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101000
IP 10.1.2.4 > 10.1.1.1: ICMP echo reply, id 62, seq 3, length 64
```

ARP Reply により Type-2 が広報された後のルーティングテーブルは以下

```bash:case2-routing
# leaf-1
cumulus@leaf-1:~$ net show route vrf vrf-1
...
VRF vrf-1:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:52:36
C>* 10.1.1.0/24 is directly connected, vlan1001, 00:52:36
C>* 10.1.2.0/24 is directly connected, vlan1002, 00:52:36
B>* 10.1.2.4/32 [20/0] via 4.4.4.4, vlan1000 onlink, 00:01:09 # <--- EVPN Type-2

# leaf-4
cumulus@leaf-4:~$ net show route vrf vrf-1
...
VRF vrf-1:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 00:52:34
C>* 10.1.1.0/24 is directly connected, vlan1001, 00:52:34
B>* 10.1.1.1/32 [20/0] via 1.1.1.1, vlan1000 onlink, 00:01:08 # <--- EVPN Type-2
C>* 10.1.2.0/24 is directly connected, vlan1002, 00:52:34
```

case3 について `node-7(10.1.1.7)@leaf-2` --> `node-3(10.1.2.3)@leaf-1`への通信を確認する. case2 と違い、送信元leafが宛先サブネットを持たないため`L3VNI`を使って宛先MACを探そうとするが、思ったよりややこしい動きをしたので図も添付する.

```bash:leaf2-vrf-1-route
# leaf-2 のルーティングテーブルを事前に確認
cumulus@leaf-2:~$ net show route vrf vrf-1
...
VRF vrf-1:
K * 0.0.0.0/0 [255/8192] unreachable (ICMP unreachable), 02:30:30
C>* 10.1.1.0/24 is directly connected, vlan1001, 02:30:30
B>* 10.1.2.0/24 [20/0] via 1.1.1.1, vlan1000 onlink, 02:30:15 # <-- 今回は 1.1.1.1 がベストパス
  *                    via 4.4.4.4, vlan1000 onlink, 02:30:15

# leaf-2 は初期状態で対向L3VNIのMAC情報をもつ(L3VNIの初期通信に必要)
cumulus@leaf-2:~$ ip neighbor
169.254.0.1 dev swp1 lladdr 0c:3b:9c:cd:9a:02 PERMANENT
4.4.4.4 dev vlan1000 lladdr 0c:3b:9c:90:e1:03 offload NOARP # <--- 4.4.4.4 のL3VNIのMAC
1.1.1.1 dev vlan1000 lladdr 0c:3b:9c:2a:09:03 offload NOARP # <--- 1.1.1.1 のL3VNIのMAC
fe80::e3b:9cff:fecd:9a02 dev swp1 lladdr 0c:3b:9c:cd:9a:02 router REACHABLE
```

![arp-learning-case3.png](https://qiita-image-store.s3.amazonaws.com/0/323524/4dd024c7-adfb-76cf-acbf-c92a16410fd7.png)

```bash:case3
cumulus@leaf-1:~$ sudo tcpdump -i any port 4789

# (1) 10.1.1.7 から 10.1.2.3 へのパケットはleaf-2の subnet1-1 のゲートウェイ(10.1.1.254) へ転送
# (2) 10.1.1.254 は vrf-1 のルーティングテーブルを元に vlan1000(L3VNI) へルーティング
# (3) 2.2.2.2 はベストパスになっている1.1.1.1にICMPを(とりあえず)送信
    # src_mac を自身のL3VNI, dst_mac を 1.1.1.1 の L3VNIにしている
    # (この時点では 2.2.2.2 は宛先10.1.2.3が 1.1.1.1 と 4.4.4.4 どちらに存在するのかわかっていない)
18:37:49.617205 IP 2.2.2.2.48377 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101000
IP 10.1.1.7 > 10.1.2.3: ICMP echo request, id 60, seq 1, length 64

# (4) 1.1.1.1 はVXLANを解いて ICMP echo を 10.1.2.254 にルーティング
# (5) 10.1.2.254 は 10.1.2.3 の MAC を知らないので ARP Request をブロードキャスト
# ブロードキャストは以下に伝搬する
    # 1. 自身の 10.1.2.0/24 のサブネット
    # 2. VNI101002 を持つVTEP 4.4.4.4 (Ingress Replication)
18:37:49.617597 IP 1.1.1.1.39745 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 101002
ARP, Request who-has 10.1.2.3 tell 10.1.2.254, length 28

# (6.1) この時点で 10.1.2.254 は ARP Reply を 10.1.2.3 から受信し ICMP echo を転送している
# (6.2) 10.1.2.3 は ICMP reply を 10.1.1.7 に返すために自身のゲートウェイ 10.1.2.254 に送信
# (7) ICMP reply は VRF-1 のルーティングテーブルを元に10.1.1.254 にルーティングされる

# (8) 10.1.1.254 は 10.1.1.7 の MAC を知らないので ARP Request をブロードキャスト
# ブロードキャストは以下に伝搬する
    # 1. 自身の 10.1.1.0/24 のサブネット
    # 2. VNI101001 を持つVTEP 2.2.2.2 と 4.4.4.4 (Ingress Replication)
18:37:49.636547 IP 1.1.1.1.39745 > 4.4.4.4.4789: VXLAN, flags [I] (0x08), vni 101001
ARP, Request who-has 10.1.1.7 tell 10.1.1.254, length 28
18:37:49.637486 IP 1.1.1.1.39745 > 2.2.2.2.4789: VXLAN, flags [I] (0x08), vni 101001
ARP, Request who-has 10.1.1.7 tell 10.1.1.254, length 28

# (9,10) 2.2.2.2 上の 10.1.1.7 が L2VNI で 1.1.1.1 に ARP Reply
18:37:49.645649 IP 2.2.2.2.41851 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101001
ARP, Reply 10.1.1.7 is-at a2:e5:c9:1f:b2:7c (oui Unknown), length 46

# (11) 10.1.1.254 が 10.1.1.17 の MAC を知ることができたので ICMP reply をL2VNI経由で返信
# (12) 10.1.1.7 に ICMP reply が届く
18:37:49.645979 IP 1.1.1.1.51781 > 2.2.2.2.4789: VXLAN, flags [I] (0x08), vni 101001
IP 10.1.2.3 > 10.1.1.7: ICMP echo reply, id 60, seq 1, length 64

# EVPN Type-2 ルートが広報されたタイミングで L3VNI を使って相互通信開始
18:37:50.616684 IP 2.2.2.2.48377 > 1.1.1.1.4789: VXLAN, flags [I] (0x08), vni 101000
IP 10.1.1.7 > 10.1.2.3: ICMP echo request, id 60, seq 2, length 64
18:37:50.617994 IP 1.1.1.1.51781 > 2.2.2.2.4789: VXLAN, flags [I] (0x08), vni 101000
IP 10.1.2.3 > 10.1.1.7: ICMP echo reply, id 60, seq 2, length 64

# 余談だが、最初のICMP echoの送信先が4.4.4.4だった場合、もう一段階手間がかかる動きになると予想される
# 今回は(たまたま)ICMP request を飛ばした 1.1.1.1 上に宛先ノードがいたため、すぐに ICMP replyを返せている
```

まとめると、宛先経路がBGPで学習されていない状況では、各VTEPがもつサブネットによってARPの初動が以下のように異なる. 

- 同サブネットへの通信は同じ`L2VNI`をもつVTEPに`Head End Replication`することでARP解決
- 異なるサブネットへの通信は
    - 送信元VTEPに宛先サブネットが存在する場合は内部でルーティングされて`L2VNI`経由でARP解決
    - 送信元VTEPに宛先サブネットが存在しない場合は`L3VNI`で宛先サブネットを持つVTEPにARP解決依頼
        - 宛先VTEPが`L2VNI`でARP解決した後、EVPN Type-2でMACが広報される

VXLAN周りのAPRに関して、[EVPNで変わる・変えられるデータセンターネットワーク-5](https://www.netone.co.jp/report/column/column1/20160510b.html) を見ると、2016年当時のCisco Nexusでは宛先スイッチで`ARP Reply`を終端して(VXLANでreplyさせない)、EVPN Type-2 で送信元スイッチにMACアドレスを広報するようだ. Ciscoの実装の方がMAC学習はEVPN経由で統一されるのでわかりやすい気がするが、いかにMAC学習を早く完了させるかの観点からだとはARP Replyもあったほうが早く通信を開始できるようになりそう. 今回のCumulus環境ではARP ReplyがVXLANで流れており終端されているようには見えなかった(FRRの実装に依存?). 

長々と書いたが、1箇所で取得したキャプチャ結果を元に動作を推測したので間違った記述がある可能性がある. また製品によって実際の動きがそもそも異なる可能性があることに留意する.


## Anycast Gateway について
分散ゲートウェイとも呼ばれている. 各leafスイッチには Overlay Network の各サブネットの デフォルトゲートウェイ(.254)が設定されており、leafが複数台になると同じIPアドレスを持つゲートウェイが複数存在することになる.  これはOverlay Networkの通信に悪影響があるのではと疑問に思ったが、結論は問題なしとなる.
理由は、leafスイッチの設定において ゲートウェイのインターフェースは `bridge learning off` でMAC学習の対象から除外されており、これによりEVPN経由で他のleafスイッチに対してゲートウェイのMAC情報が外部に広報されることはないからである.　よって leaf スイッチ配下の node からはその leaf スイッチ内のゲートウェイしか見えない. 
Underlay の観点から見ると、overlay network のサブネットゲートウェイに関する通信はVXLANでカプセル化されているので、Unerlay にいるleaf/spineスイッチからは隠蔽されて見えない. したがってループしたり`DUP!`になるような問題は発生しない.
 Anycast gateway は他にも downlinkに対して ProxyARP や、 uplinkに対して ARP/ND 抑制してたりと 通信帯域を効率的に使えるような仕組みを動作させている.  Anycast gateway 自体は `EVPN`の機能の一部という位置づけである.

## VXLAN Routing について

VXLAN Routingについて、こちらの記事のいち部分を超訳してみる.　記事中の図を見るのがわかりやすい.
[VXLAN routing with EVPN: asymmetric vs. symmetric model](https://cumulusnetworks.com/blog/asymmetric-vs-symmetric-model/)

>VXLAN/EVPN環境におけるVTEP間の通信において、2つのルーティング方式がある.
>
>### Asymmetric model (非対称ルーティング)
>- downlink からの通信(ingress)のみルーティングする
>- downlink への通信(egress)はブリッジするのみ
>- (欠点) 送信元のleafでルーティングし対向のVNIを使ってVXLANトンネルを張るため、全てのleafスイッチにあらかじめ通信されるであろう全てのVNIを設定しておく必要がある
>    - leafスイッチ配下に存在しない VNIのIP/MAC情報も余分に保持する必要があるため、スケールの観点から問題が出てくるかもしれない
>- (利点) そんなに大きな環境でなければ、設定がシンプルかつホップ数的に(対称ルーティングより)遅延が少ない非対称ルーティングを使ってもいいんじゃない
>
>### Symmetric model (対称ルーティング)
>- (利点) `L3VNI` という VXLANルーティングのためのVNIを各leafに設定する
>    - 各leaf に全てのVNIをわざわざ設定する必要はないし、スケールしやすい
>    - VRFごとに`L3VNI`をひとつずつ設定する必要がある
>- ingress に加えて egress もルーティングする
>    - 送信元leaf は`L3VNI`経由で対向leaf と通信する. 戻りも同じ経路なのでわかりやすい
>- (欠点) (非対称ルーティングより)ホップ数が多くなるので遅いかも

今回は対称ルーティングを採用しており各leafに`L3VNI`を設定している.  VNI数の制限がある`Head End Replication`の観点からも対称ルーティングの方が使われるケースが多いのではないだろうか.

# おわり

結論として、IP-fabric な Underlay Network 上で VXLAN/EVPN を使った Overlay Networkを構築し、異なるToR間で異なるSubnet同士、異なるVRF同士の相互疎通を確認できた。 

仮想ラボ環境が起因しているのかもしれないが、ルーティングテーブルが正しく更新されなかったり、ルーティングテーブルどおりにパケットが転送されないなど、不安定?な挙動が時折見られた。怪しい動作をした際は スイッチをreboot もしくは設定をいったんクリアして投入し直すと想定どおりの動作をしたので注意する必要がある.