1. 什么是 GoBGP?

利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java

GoBGP 是使用 Go 语言开发的,运行在 Linux 系统上的开源工具,可以提供 BGP 协议的控制平面功能。与 Guagga/FRRouting 相比,GoBGP 的性能更好,收敛时间更短,可以适用于更大规模的网络,比如充当 IXP 路由器:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_02

[图1. GoBGP Performance]


GoBGP 仅支持 BGP 这一种路由协议,但是它可以和 Zebra 集成,通过 API 的方式与 Guagga/FRR 协同工作,以支持多种路由协议。


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_03

[图2. GoBGP Architecture]


可以使用 Python、C++ 等多种语言,通过 gRPC API 对 GoBGP 进行配置,当然也支持 CLI。GoBGP 还支持 OpenConfig,其 YANG 模型符合 draft-ietf-idr-bgp-model-03。


因为 GoBGP 可以很方便地人工干涉路由,参与感更强,是一个很好的实验工具。本文就利用 GoBGP 和 Cisco NXOSv 搭建实验环境,来学习 VXLAN EVPN 的一些原理。


2. 安装 GoBGP

GoBGP 的安装非常简单,从 https://github.com/osrg/gobgp/releases 下载 tar.gz 文件,解压即可。在元旦之前,GoBGP 刚刚 Release 了 v2.0.0。


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_04


解压之后可以看到 2 个可执行文件:`gobgp` 和 `gobgpd`。其中 `gobgp` 是 gRPC 的 CLI client 工具,`gobgpd` 是 GoBGP 主程序。


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_05


如果使用源代码方式安装,可以获取最新的 master branch,但是要复杂一些。


首先安装 Golang。从 https://golang.org/dl 下载 binary release,需要 1.11 或更高版本,解压到 /usr/local 目录:



然后设置环境变量:



最后 `go get` 两个 binary 文件:



如果无法直接访问境外的服务器,源代码安装很容易失败。所以还是建议使用 binary release。


3. 准备 Cisco 网络环境

在继续实验之前,先按照下面的拓扑准备好 Cisco 的网络环境。本次实验采用 iBGP,仅配置 `l2vpn evpn` address-family。


[图3. Topology]


3.1 R1-Spine 设备配置



3.2 R2-VTEP & R3-VTEP 设备配置


然后用 3 个 Puppy Linux 分别作为 H1,H2 和 H3。从 H2 ping H3,可以 ping 通:


[图4. H2_ping_H3]


从 H1 ping H3 和 H2,也都可以 ping 通:

[图5. H1_ping_H3&H2]


4. Symmetric IRB

您应该注意到了,虽然我为 2 个 VTEP R2 和 R3 配置了相同的 L2VNI 和 L3VNI,但是我为它们配置了不同的 VLAN ID。R2 的 3 个 VLAN ID 分别为 200、300、999,而 R3 的 3 个 VLAN ID 分别为 288、388、888。这并不影响 VXLAN 网络的连通性,3 台 Host 都可以互相 ping 通。


draft-ietf-bess-evpn-inter-subnet-forwarding 定义了 2 种 IRB 操作模型,Asymmetric IRB 和 Symmetric IRB,Cisco Nexus 采用的是后一种。在 Symmetric IRB 模型中,Tenant System 1 的流量抵达 Ingress VTEP 之后,先在 Bridge Table 查找 ARP,然后进入 IP-VRF 查找路由表,再封装 **L3VNI** 传递给 Egress VTEP。流量抵达 Egress VTEP,解封装 **L3VNI**,在 IP-VRF 查找路由表,然后在 Bridge Table 查找 ARP,最后发给本地的 Tenant System。下图是流量的传输路径:


图6. Symmetric IRB


现在我们检查一下 VTEP 的 ARP 表:



在 ARP 表里面有 VLAN ID 的信息,H1 和 H3 虽然位于相同的子网,但是在 R2 和 R3 的 ARP 表中,VLAN ID 并不相同。这并没有关系,因为 ARP 表是本地有效的。


再检查一下 VTEP 的 Bridge 表,请注意第 2 条 entry,这是 H3 的 Bridge 信息。Topology 是本地的 VLAN ID,采用的协议是 BGP 而不是 Local,下一跳是 R3-VTEP 的 IP 而不是本地的接口。这意味着 Intra-Subnet 的流量如果需要跨越不同的 VTEP,也要通过 EVPN 路由。只不过这不涉及到 Symmetric IRB Model,封装的 VNI 是 L2VNI。



再检查一下 VTEP 的路由表:



请注意 `172.16.3.10/32` 这条路由,下一跳是 R3-VTEP,vn-segment(segid)是 50999。如果是 Inter-Subnet 的流量跨越不同的 VTEP,需要通过 EVPN 路由并符合 Symmetric IRB 模型,封装 L3VNI。


无论是 Intra-Subnet 还是 Inter-Subnet,VLAN ID 都是本地有效的。所以在配置 Cisco Nexus 交换机的时候,每一台 VTEP 都可以单独规划 VLAN ID 和 VNI 和 VRF 的 mapping 关系 —— 虽然在 Cisco Nexus 交换机的配置文件中,VNI 必须配置在 VLAN 下面,但这并不意味着整个 Fabric 的 VNI 数量就受限于 VLAN ID 的数量。


5. GoBGP 和 NXOSv 建立 BGP EVPN Neighbor

GoBGP 可以通过 CLI 来建立 Neighbor,也可以采用配置文件的方式。我们先来创建一个名称为 `gobgpd.conf` 的配置文件:



其中 10.15.0.100 是 H5 的 lo 接口地址。如果不采用 Loopback 接口建立 Neighbor,则不需要在配置文件中写入 `neighbors.transport.config` 字段。


然后启动 `gobgpd`,同时开启 debug:



在 R1-Spine 可以观察到 Neighbor 已经 Up:



新开一个 SSH 窗口,使用`./gobgp neighbor` 可以观察到 R1-Spine 已成为邻居并且状态是 Up:



使用 `./gobgp global rib -a evpn` 命令可以观察到 GoBGP 收到了 NXOS 发来的完整的 EVPN 路由表。


请注意:不能用 Ctrl+C 终止 `gobgpd` 的屏幕输出,否则进程也会终止。NXOS 会观察到 Neighbor 状态变为 Idle,运行 `gobgp` 之后会提示 gRPC error:



6. Route-Type 2: MAC/IP Advertisement Route

接下来我们利用 GoBGP 向 NXOS 发送 RT-2 路由更新。RT-2 路由的作用是向 Fabric 通告 MAC/IP 的位置。根据 RFC-7432,RT-2 的 NLRI 格式如下:



在 VXLAN 场景,MPLS Label 就是 VNI,第 1 个是 L2VNI,第 2 个是 L3VNI。GoBGP 发送 RT-2 的命令格式为:



其中 ESI 和 Etag 的 Value 应该是什么呢?我们先来看看 NXOS 向邻居通告的路由信息是怎样的:



R2-VTEP 连接了 2 个 Host,H1 和 H2,分别位于 2 个不同的 Subnet,对应 2 个不同的 L2VNI。它为每个 Host 向每个 L2 RD 通告了 2 条路由:一条通告 MAC,用于 Bridging;另外一条通告 MAC/IP,用于 Routing。然后我们再观察每个 MAC 对应的路由详细内容:



用于 Bridging 的路由仅携带了 L2VNI 和 MAC VRF 的 RT,而用于 Routing 的路由则同时携带了 L2VNI、 L3VNI 以及 MAC VRF 和 IP VRF 的 RT,还有 NVE Tunnel 接口的 MAC。在路由信息中, `[2]:[0]:[0]:[48]` 的含义是:

  •  [2] :Route Type 2

  •  第 1 个 [0] :Ethernet Segment Identifier

  •  第 2 个 [0] :Ethernet Tag ID

  •  [48] :MAC 地址长度


从上面的 `show` 结果,我们可以知道正常的 RT-2 路由都应该包括哪些内容。我们先使用 GoBGP 向 Fabric 通告一个并不存在 Host(GoBGP 不支持将直连网段重分布到 BGP。需要安装 Quagga/FRR 然后通过 Zebra API 才能将直连路由或其他协议的路由重分布到 BGP),MAC 地址为 aa:bb:cc:dd:ee:ff,IP 地址为 172.16.2.20,和 H2 属于同一个子网:



在 R2-VTEP 上可以观察到已经收到这 2 条 RT-2 路由并加表:



并且 R2-VTEP 已经和 H5-GoBGP 成功建立了 NVE Peer:



6.1 ARP 报文传输路径

在 H2 上 ping 相同子网的 172.16.2.20,然后在 ESXi 上抓取 H2 的流量,发现 H2 发送 ARP 请求 172.16.2.20 的 MAC,但一直没有收到 ARP Response。在 H5 上抓包,发现并没有抓取到 ICMP 或 ARP 报文。


但是在 H3 上 ping 不同子网的 172.16.2.20,却可以在 H5 上抓取到 ICMP 报文。从下面的截图可以看到 ICMP 报文有 VXLAN 的报头,目的 VTEP IP 是 10.15.0.100,因为是 Inter-Subnet 的流量,所以封装的 VNI 是 L3VNI 50999。然后 H5 向 R3 回应了 ICMP 不可达:


[图7. H3_ICMP]


这是因为在 H3 在请求 172.16.2.20 的 MAC 的时候,Anycast Gateway R3 会将自己的 MAC 回应给 H3,也就是 `fabric forwarding anycast-gateway-mac 0001.0001.0001` 所配置的 MAC 地址:


[图8. AnycastGW-ARP-Response]


而 H2 所请求的是相同子网的 MAC,虽然在 R2-VTEP 上存在去往 172.16.2.20 的 EVPN 路由,但为了处理 BUM 流量,例如 ARP,R2-VTEP 需要将 ARP Request 用 VXLAN 封装之后发往组播地址,本实验我们所配置的组播地址是 239.0.0.1。其他 VTEP 收到 ARP Request 之后会解封装并检查所请求的地址是否在本地,如果不在本地则丢弃,如果在本地就发给目的主机,然后由目的主机单播回复 ARP Response。


H5-GoBGP 并没有办法像 NXOS 那样为 L2VNI 指定一个组播地址加入组播组,所以它无法收到 ARP。我们采用一个变通的办法,在 R1-Spine 上将连接 H5-GoBGP 的接口加入到组播组:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_06


然后在 H2 上 ping 172.16.2.20,就可以在 H5 上抓到 ARP Request 报文了:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_07

[图9. ARP-Request]


在上面的截图我们可以看到 VXLAN Outer 目的 IP 是 239.0.0.1。因为是 Intra-Subnet 的流量,所以封装的 VNI 是 L2VNI 50200。


当然也可以改用 BGP 而不使用组播的方式来复制 BUM,需要更改 `int nve1` 的配置:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_08


在没有组播的情况下,就需要另外一种路由: Route Type 3 —— Inclusive Multicast Ethernet Tag route。每台 VTEP 需要先向所有相关的 MAC VRF RD 通告 RT-3,告诉其他 VTEP 应该把 BUM 复制到哪里,然后再按照上文介绍的流程通告 RT-2。


RT-3 NLRI 的格式如下:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_09


同时,BGP Extended Communities 必须要携带 RFC 6514 所定义的 Provider Multicast Service Interface (PMSI) Tunnel 属性,以实现这种 Point-to-Multipoint 的复制。PMSI BGP 属性的格式如下:


  利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_10


其中 Flags 只定义了一种:Leaf Information Required (L)。在 VXLAN EVPN 的场景,Tunnel Type 的值是 6,表示这是 Ingress Replication。剩下 2 个字段就比较好理解了,分别是 VNI 和本地 NVE 接口的 IP 地址。


GoBGP 通告 RT-3 的 CLI 格式为:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_11


其中 `<ip address>` 就是 Originating Router's IP Address,而 `<tunnel-id>` 的值和 `<ip address>` 是一样的,因为 Cisco VTEP 只有 1 个 NVE 接口。我们可以在 GoBGP 上使用命令 `gobgp global rib -a evpn` 来看看 Cisco NXOSv 发给 GoBGP 的 RT-3 更新,比较一下 `ip address` 和 `tunnel-id` :


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_12

[图9-2. GoBGP-RT-2]


在上面的截图中,我们也可以看到 pmsi type 是 `ingress-repl`。


需要注意的是,GoBGP v1.3.3 在通告 RT-3 的时候会提示 runtime error,建议大家使用 v2.0.0 版本。


现在我们再回过头来看看 Ethernet Segment Identifier 和 Ethernet Tag ID。


6.2 Ethernet Segment Identifier


我们知道数据中心为了给服务器提供链路冗余,通常会采用 2 台 Leaf 堆叠的方式。但堆叠之后交换机升级会影响业务连续性,而且有可能会出现一些奇葩的问题。RFC-7432 提供了一种去堆叠的方式:EVPN multi-homing。


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_13

[图10. EVPN_multihoming]


在 EVPN multi-homing 的场景,服务器正常 bonding 2 条 uplink,但 VTEP-1 和 VTEP-2 之间并没有 peer-link,需要通过 EVPN Fabric 来交换信息。它们交换的就是 ESI 和 Etag。ESI 配置在每条 bundled link 上,如果相同 Domain 的 VTEP 有相同的 ESI,它们就会共享 multihomed neighbor。这种场景需要用到 Route Type 1 Auto-Discovery 路由。


本实验的服务器都采用单挂,single-homing,所以 ESI 为 0。


除了采用 ESI 来实现 multi-homing 之外,以阿里为代表的国内互联网公司正在使用另外一种去堆叠方式,Cisco 对应的技术是 VPC-less,即 VPC without peering link。


6.3 Ethernet Tag ID

Etag 的作用是在 EVPN Instance 中标识一个特定的广播域(Bridge Table)。一个 EVPN Instance 就是一个 MAC-VRF,包含 1 个或多个 Bridge Table,这些 BT 共享相同的 Route Target。


在 Asymmetric IRB 模型中,1 个 MAC-VRF/EVI 会包含多个 BT。但是在 Symmetric IRB 操作模型中,1 个 MAC-VRF 仅包含 1 个 BT,是 1:1 的 mapping 关系,请看下面的 NXOS 相关配置:



所以在 Symmetric IRB 模型中,Etag 必须置为 0,不需要用它来区分广播域。


 7. Route-Type 5: IP Prefix Route

接下来我们让 H5 作为 EVPN Border,测试 RT-5 路由。由于 GoBGP 必须与 Quagga/FRR 的 Zebra API 集成,才能重分发外部路由到 BGP,所以这次实验我们仍然只能用 1 个虚拟的 IP 地址作为目的 IP。


GoBGP 通告 RT-5 的 CLI 格式是:



从上文的分析中,我们已经知道 esi 和 etag 都为 0。但是 gateway 又是怎么回事?先不管这个,我们直接在 H5 向 Fabric 通告一条 RT-5:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_14


在 Cisco VTEP 可以观察到已经收到了这条 RT-5 路由:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_15


在 H3 上 ping 一个路由表里不存在的地址 100.100.100.100,然后可以在 H5 上抓取到这些 ICMP 报文,RT-5 路由生效了:


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_16

[图11. RT-5_ICMP]


另外请注意 Inner MAC(我忘记用红色框出来了),这是在 RT-2 和 RT-5 路由更新中所携带的 `router-mac` 属性。VTEP 解封装 VXLAN 数据包之后看到 Inner MAC 是自己的 Tunnel 接口地址,就会进一步地处理报文转发。


H5-GoBGP 在通告 RT-5 路由的时候,并没有携带 `[gw <gateway>]` 参数,那么这个参数的作用是什么呢?


上文已经谈到 Cisco 采用的 Inter-Subnet 操作模型是 Symmetric IRB。在这个 IRB 模型中,Inter-Subnet 路由是 IP-VRF to IP-VRF。在 draft-ietf-bess-evpn-prefix-advertisement 中又定义了 3 种 IP-VRF-to-IP-VRF 模型,分别是:


  •  Interface-less IP-VRF-to-IP-VRF Model

  •  Interface-ful IP-VRF-to-IP-VRF with SBD IRB

  •  Interface-ful IP-VRF-to-IP-VRF with Unnumbered SBD IRB


7.1 Interface-less IP-VRF-to-IP-VRF Model


Cisco Nexus 采用的就是这种最简单的模型,IP-VRF 直连 IP-VRF,不需要额外的 Overlay Index。正如我们刚才所操作的那样,在这种模型中,Border Node 只需要通告 1 条 RT-5 路由并且不需要携带 GW 参数,只需要携带 `router-mac` 参数把自己的 NVE 接口的 MAC 地址通告出去。其他 VTEP 在收到这条 RT-5 路由之后,会将 next-hop 指向 Border 的 NVE 接口。


7.2 Interface-ful IP-VRF-to-IP-VRF with SBD IRB


这种模型是最复杂的,IP-VRF 不能直连其他 VTEP 的 IP-VRF,需要一个 Supplementary Broadcast Domain (SBD) 作为一个 “Super IRB”,连接租户**所有的** IP-VRF。SBD 还需要有自己的 IP 地址和 MAC 地址。同样地,在 Border Node (Datacenter GW) 上也需要有 SBD。


 利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_17

   [图12. Interface-ful with SBD IRB model]


在这个操作模型中,Border 需要通告 2 条路由:

  •  1 条 RT-5,通告 Prefix,同时把自己的 SBD IRB 的 IP 地址作为 GW 通告出去。GoBGP 的 `[gw <gateway>]` 参数就是为这个模型准备的

  •  1 条 RT-2,向 Fabric 通告自己的 SBD IRB 的 MAC/IP,同时还要携带 `router-mac` Ext-community


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_18

[图13. Advertisement_Interface-ful with SBD IRB model]


7.3 Interface-ful IP-VRF-to-IP-VRF with Unnumbered SBD IRB


这种模型相对简单一点,VTEP 和 Border 同样需要 SBD 作为 overlay index,但 SBD IRB 不再需要 IP 地址,只需要 MAC 地址。


利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_19

  [图14. Interface-ful with unnumbered SBD IRB model]


在这个操作模型中,Border 也还是需要通告 2 条路由:

  •  1 条 RT-5,通告 Prefix,不需要携带 GW,但需要携带 `router-mac` Ext-community

  •   1 条 RT-2,仅向 Fabric 通告自己的 SBD IRB 的 MAC



利用GoBGP和Cisco NXOSv学习VXLAN EVPN_java_20

[图15. Advertisement_Interface-ful with unnumbered SBD IRB model]


GoBGP 的功能还有很多,以后如果有时间我们再试一下与 Zebra API 集成。