本文基于本地 sonic 代码整理,适合作为 VXLAN、EVPN、ARP suppression、SONiC 数据面编排的学习笔记。

摘要

在传统二层网络里,ARP Request 是广播报文。到了 VXLAN 网络中,一个 VLAN 可能被扩展到多个 VTEP,ARP 广播如果直接进入 overlay,就会变成跨 VTEP 的 BUM 流量复制,带来带宽浪费、控制面压力和故障扩散风险。

VXLAN ARP 代答,也常被叫作 ARP suppression,本质是让本地 VTEP 在已经掌握 IP/MAC/VNI/VTEP 关系时,直接替远端主机回复 ARP,避免把 ARP Request flood 到整个 VXLAN 网络。

结合本地 SONiC 代码来看,这个能力不是一个孤立函数完成的,而是由配置面、FDB/邻居学习、EVPN 控制面、Linux bridge/VXLAN netdevice 以及 SAI 编排共同完成。尤其要理解两个开关:

  • redistribute_host:决定本地学到的邻居是否以 MAC+IP Type-2 的形式发布出去。
  • flood_suppress:决定 VXLAN VLAN 上 unknown/broadcast/multicast flood 是否被抑制,并触发 Linux VXLAN proxy、bridge neigh_suppress 等能力。

关键词

VXLAN、EVPN、VTEP、VNI、ARP 代答、ARP suppression、BUM、SONiC、SWSS、NeighOrch、FdbOrch、VxlanMgr、SAI


1. 先建立直觉:ARP 代答到底在代谁答?

假设有两台主机:

Host A: 10.1.1.10 / MAC_A,在 VTEP1 后面
Host B: 10.1.1.20 / MAC_B,在 VTEP2 后面
VLAN 100 映射到 VNI 10000

Host A 想访问 Host B,首先要知道 10.1.1.20 对应哪个 MAC,于是发 ARP:

Who has 10.1.1.20? Tell 10.1.1.10

如果没有 ARP 代答,VTEP1 会把这个广播封装成 VXLAN BUM 流量,复制到其他远端 VTEP。远端 VTEP2 收到后,再交给 Host B,Host B 回 ARP Reply。

如果有 ARP 代答,并且 VTEP1 已经知道:

10.1.1.20 -> MAC_B -> VNI 10000 -> remote VTEP2

那么 VTEP1 就可以直接在本地回复 Host A:

10.1.1.20 is at MAC_B

这样 ARP Request 就不需要进入 VXLAN overlay。

一句话总结:

ARP 代答的核心不是“会构造 ARP Reply”,而是“VTEP 已经知道目标 IP 对应的 MAC,以及这个 MAC 属于哪个 VNI、在哪个远端 VTEP 后面”。


2. 没有 ARP 代答时的 VXLAN 广播问题

VXLAN 把二层网络建在三层 underlay 之上。一个二层广播域可能跨越多个机架、多个交换机和多个 VTEP。

ARP Request 是广播报文,因此它天然属于 BUM 流量:

  • Broadcast:广播,例如 ARP Request。
  • Unknown unicast:未知目的 MAC 的单播。
  • Multicast:组播。

没有代答时,ARP 的典型路径如下:

在这里插入图片描述

图 1:没有 ARP 代答时的 VXLAN BUM 复制

如果一个 VNI 下挂了很多 VTEP,广播复制会被放大。主机越多、ARP 越频繁,overlay 里的无效流量越多。

因此 VXLAN 网络通常希望:

  1. 控制面提前学习 IP/MAC/VTEP/VNI 关系。
  2. 数据面在 ARP Request 到达时优先本地回复。
  3. 对未知流量进行 flood 抑制,避免广播风暴式扩散。

3. 有 ARP 代答时的逻辑

有代答时,路径会短很多:

在这里插入图片描述

图 2:有 ARP 代答时的本地命中流程

用伪代码表达就是:

收到 ARP Request(target_ip):
    if 本地表中存在 target_ip -> target_mac:
        回复 ARP Reply(target_ip is target_mac)
    else:
        if flood_suppress enabled:
            抑制 flood,等待控制面学习或静态配置
        else:
            按 VXLAN BUM 复制到远端 VTEP

4. SONiC 代码中的关键模块

下面是本文涉及的本地代码索引:

模块关键文件作用
CLIsrc/sonic-utilities/config/vxlan.py提供 config vxlan ... 命令,写入 CONFIG_DB
showsrc/sonic-utilities/show/vxlan.py提供 show vxlan ... 命令,查看 remote VNI、remote MAC、状态
YANGsrc/sonic-yang-models/yang-models/sonic-vxlan.yang定义 VXLAN_TUNNEL_MAPVXLAN_REMOTE_VNIVXLAN_FDB 等配置结构
cfgmgrsrc/sonic-swss/cfgmgr/vxlanmgr.cpp把 CONFIG_DB 的 VXLAN 配置转成 APP_DB,并创建 Linux VXLAN netdevice
orchagentsrc/sonic-swss/orchagent/vxlanorch.cpp编程 VXLAN tunnel、tunnel map、remote VNI、SAI tunnel next-hop
FDBsrc/sonic-swss/orchagent/fdborch.cpp消费 VXLAN_FDB_TABLE,处理 VXLAN 学习的 MAC
Neighborsrc/sonic-swss/orchagent/neighorch.cpp本地邻居学习后写入 VXLAN_FDB_TABLE,控制 MAC+IP Type-2 语义
Routesrc/sonic-swss/orchagent/routeorch.cpp处理 overlay next-hop,解析 vni_labelrouter_mac 等字段
FPMsrc/sonic-swss/fpmsyncd/fpmlink.cpp接收 FRR/zebra 的 FPM/netlink 信息,识别 EVPN FDB 相关消息
ARP responder 示例src/sonic-restapi/arp_responder/*.cc展示 ARP Request 如何被改造成 ARP Reply
测试src/sonic-swss/tests/evpn_tunnel.pytest_evpn_fdb.py验证 EVPN/VXLAN tunnel、remote VNI、tunnel next-hop、FDB 行为

5. 配置面:从 config vxlan 到 CONFIG_DB

SONiC 的 VXLAN CLI 入口在:

src/sonic-utilities/config/vxlan.py

本地代码中可以看到这些命令入口:

命令Python 函数写入的数据
config vxlan add <vxlan_name> <src_ip>add_vxlan()CONFIG_DB:VXLAN_TUNNEL
config vxlan evpn_nvo add <nvo_name> <vxlan_name>add_vxlan_evpn_nvo()CONFIG_DB:VXLAN_EVPN_NVO
config vxlan map add <vxlan_name> <vlan_id> <vni>add_vxlan_map()CONFIG_DB:VXLAN_TUNNEL_MAP
config vxlan neighbor add <vlan_id> <vni> <remote_vtep_ip>add_vxlan_neighbor()CONFIG_DB:VXLAN_REMOTE_VNI
config vxlan redistribute_host enable/disable <vlan_id>_set_redistribute_host()更新 VXLAN_TUNNEL_MAP.redistribute_host
config vxlan flood-suppress enable/disable <vlan_id>_set_flood_suppress()更新 VXLAN_TUNNEL_MAP.flood_suppress
config vxlan mac add <vni> <mac> <remote_vtep_ip>add_vxlan_mac()CONFIG_DB:VXLAN_FDB

配置面的关键点是:用户操作不会直接改硬件,而是先写 CONFIG_DB。后续由 vxlanmgrdorchagent 消费 DB 事件,逐层下发。

流程如下:

在这里插入图片描述

图 3:从 config vxlan 到 DB 和 ASIC 的配置链路


6. vxlanmgrd:把 VXLAN 配置翻译成 APP_DB 和 Linux netdevice

VxlanMgr::doTask() 位于:

src/sonic-swss/cfgmgr/vxlanmgr.cpp

它根据表名分发处理:

VXLAN_TUNNEL      -> doVxlanTunnelCreateTask()
VXLAN_TUNNEL_MAP  -> doVxlanTunnelMapCreateTask()
VXLAN_EVPN_NVO    -> doVxlanEvpnNvoCreateTask()
VXLAN_REMOTE_VNI  -> doVxlanRemoteVniCreateTask()
VXLAN_FDB         -> doVxlanFdbCreateTask()

其中跟 ARP 代答最相关的是 VXLAN_TUNNEL_MAP,因为 VLAN-VNI 映射是 ARP suppression 的作用范围。没有 VLAN 到 VNI 的映射,VTEP 就不知道这个二层域对应哪个 overlay 网络。

doVxlanTunnelMapCreateTask() 里,代码会读取:

vlan
vni
flood_suppress

然后调用:

createVxlanNetdevice(..., proxy_arp_enabled, flood_suppress_enabled)

本地代码中的逻辑可以概括为:

proxy_arp_enabled = flood_suppress_enabled || isVlanProxyArpEnabled(vlan)

也就是说,只要 flood_suppress 打开,或者 VLAN interface 本身启用了 proxy ARP,创建 VXLAN netdevice 时就会打开 proxy 相关能力。


7. flood_suppress:抑制 flood,也触发 ARP/ND suppression 能力

flood_suppress 在代码里有两层落地:

  1. Linux bridge/VXLAN netdevice 层。
  2. SAI VLAN flood control 层。

7.1 Linux netdevice 层

src/sonic-swss/cfgmgr/vxlanmgr.cppcreateVxlanNetdevice() 中,会拼出类似下面的命令:

ip link add <vxlan_dev> address <switch_mac> type vxlan id <vni> local <src_ip> dstport 4789 ... proxy
bridge link set dev <vxlan_dev> neigh_suppress on
bridge link set dev <vxlan_dev> flood off
bridge link set dev <vxlan_dev> bcast_flood off
bridge link set dev <vxlan_dev> mcast_flood off

这里要分清两个动作:

  • proxy + neigh_suppress on:让 Linux bridge/VXLAN 具备邻居代答/抑制能力。
  • flood offbcast_flood offmcast_flood off:抑制未知单播、广播和组播的 flood。

因此 flood_suppress 不只是 ARP 的开关,它更像是 VXLAN 二层域里 BUM 控制的开关。

7.2 SAI 层

src/sonic-swss/orchagent/vxlanorch.cpp 中,VxlanTunnelMapOrch::addOperation() 会读取 flood_suppress,并调用:

gPortsOrch->setVlanFloodSuppress(vlan_id, flood_suppress_enable)

PortsOrch::setVlanFloodSuppress() 位于:

src/sonic-swss/orchagent/portsorch.cpp

它把 VLAN 的 flood control 设置为:

enable  -> SAI_VLAN_FLOOD_CONTROL_TYPE_NONE
disable -> SAI_VLAN_FLOOD_CONTROL_TYPE_ALL

这说明 flood_suppress 最终会影响硬件 VLAN 的 unknown unicast 和 broadcast flood 行为。

流程图:

在这里插入图片描述

图 4:flood_suppress 的 Linux 与 SAI 落地流程


8. redistribute_host:决定远端能不能学到 IP/MAC

ARP 问的是 IP 到 MAC 的映射。因此,远端 VTEP 想做 ARP 代答,不能只知道 MAC,还要知道 IP 对应哪个 MAC。

这正是 redistribute_host 的意义。

在本地代码 src/sonic-swss/orchagent/neighorch.cpp 中,本地主机邻居学习进入 NeighOrch::addNeighbor()。如果这个邻居属于 VLAN interface,并且该 VLAN 有 VNI 映射,代码会调用:

writeVxlanFdbEntry(vlan_name, macAddress, ip_address, vni, true)

writeVxlanFdbEntry() 里会判断:

rd_host_enable = tunnel_orch->isRedistributeHostEnabled(vlan_id)

redistribute_host 打开时,写入 VXLAN_FDB_TABLE 的字段包含 ip

key: VlanX:<mac>
fields:
  vni=<vni>
  type=dynamic
  remote_vtep=<local_vtep_ip>
  ip=<host_ip>

redistribute_host 关闭时,写入的是 MAC-only:

key: VlanX:<mac>
fields:
  vni=<vni>
  type=dynamic
  remote_vtep=<local_vtep_ip>

差异非常关键:

模式EVPN 语义对 ARP 代答的影响
MAC-only Type-2只告诉远端“这个 MAC 在哪个 VTEP 后面”远端不能仅凭 MAC-only 精确回答“某 IP 是哪个 MAC”
MAC+IP Type-2告诉远端“这个 IP 对应这个 MAC,在这个 VTEP 后面”远端可以建立 IP/MAC 缓存,支持本地 ARP 代答

流程图:

在这里插入图片描述

图 5:redistribute_host 对 VXLAN FDB 字段的影响


9. 远端 VTEP 与远端 MAC 如何进入本机

VXLAN 网络不只需要知道“本地主机在哪里”,还需要知道“远端主机在哪里”。

远端 VTEP/VNI 信息主要体现在:

APPL_DB:VXLAN_REMOTE_VNI_TABLE

远端 MAC 信息主要体现在:

APPL_DB:VXLAN_FDB_TABLE

src/sonic-swss/fpmsyncd/fpmlink.cpp 中有一个很关键的判断:

RTM_NEWNEIGH / RTM_DELNEIGH + AF_BRIDGE -> EVPN FDB handler

代码注释说明这类消息携带 remote VTEP 和 VNI 信息,用于 VXLAN tunnel setup。

整体流程可以理解成:

在这里插入图片描述

图 6:远端 VTEP 与远端 MAC 学习流程


10. SAI tunnel next-hop:为什么 TUNNEL_MAC 很重要

在 VXLAN overlay 路由场景中,数据面需要知道:

外层目的 IP = remote VTEP IP
VNI = overlay 网络标识
内层目的 MAC = remote host/router MAC

src/sonic-swss/orchagent/vxlanorch.cpp 中,create_nexthop_tunnel() 会创建 SAI tunnel next-hop。代码里可以看到这些属性:

SAI_NEXT_HOP_ATTR_TYPE = SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP
SAI_NEXT_HOP_ATTR_IP = remote VTEP IP
SAI_NEXT_HOP_ATTR_TUNNEL_ID = tunnel object
SAI_NEXT_HOP_ATTR_TUNNEL_VNI = VNI
SAI_NEXT_HOP_ATTR_TUNNEL_MAC = inner destination MAC

其中 SAI_NEXT_HOP_ATTR_TUNNEL_MAC 表示硬件下一跳不只是“发到哪个 VTEP”,还知道内层要封装成哪个目的 MAC。

本地测试文件 src/sonic-swss/tests/evpn_tunnel.py 中也对 SAI_NEXT_HOP_ATTR_TUNNEL_MAC 做了检查,说明这是 EVPN/VXLAN overlay next-hop 的重要属性。


11. ARP Reply 报文本身是怎么构造的

上面讲的是 VXLAN/EVPN 主链路。代码里还有一块很适合教学的 ARP responder:

src/sonic-restapi/arp_responder

它不是 VXLAN 主链路的全部,但可以直观看到“收到 ARP Request 后如何改成 ARP Reply”。

核心函数是:

Arp::make_reply_from_request(const Interface& iface)

位于:

src/sonic-restapi/arp_responder/arp.cc

它做的事情可以概括为:

  1. 以原 ARP Request 报文为基础。
  2. 把以太网目的 MAC 改成原请求方的源 MAC。
  3. 把 ARP opcode 改成 ARPOP_REPLY
  4. 交换 ARP sender IP 和 target IP。
  5. 把 ARP target hardware address 改成原请求方 MAC。
  6. 用本接口 MAC 填充以太网源 MAC和 ARP sender hardware address。

流程图:

在这里插入图片描述

图 7:ARP Reply 报文构造流程

arpresponder_bm.cc 的处理很直接:

ARPResponder::process(fd):
    接收 RawArp
    如果无效,丢弃
    如果是 ARP Request:
        arp.make_reply_from_request(*iface)
        iface->send(...)

arpresponder_msee.cc 则多了控制面:

  • ADD_INTERFACE:添加接口。
  • ADD_IP:记录某接口/tag 组合可代理的 IP。
  • REQUEST_MAC_*:主动发 ARP Request 学习 MAC。
  • process_intf():收到 ARP Request 后查 proxy_arp 表,命中才回复。

这段代码说明:ARP 代答报文动作并不复杂,难点在于“什么时候允许代答”和“代答所需的 IP/MAC 数据从哪里来”。


12. 一张总流程图:从学习到代答

在这里插入图片描述

图 8:从学习到代答的总流程


13. 常用排查命令

13.1 看 VXLAN 配置

sonic-db-cli CONFIG_DB keys 'VXLAN*'
sonic-db-cli CONFIG_DB hgetall 'VXLAN_TUNNEL|<vtep_name>'
sonic-db-cli CONFIG_DB hgetall 'VXLAN_TUNNEL_MAP|<vtep_name>|<map_name>'
sonic-db-cli CONFIG_DB hgetall 'VXLAN_REMOTE_VNI|Vlan100|<remote_vtep>'
sonic-db-cli CONFIG_DB hgetall 'VXLAN_FDB|Vlan100:<mac>'

13.2 看 APP_DB 是否生成

sonic-db-cli APPL_DB keys 'VXLAN_TUNNEL_TABLE*'
sonic-db-cli APPL_DB keys 'VXLAN_TUNNEL_MAP_TABLE*'
sonic-db-cli APPL_DB keys 'VXLAN_REMOTE_VNI_TABLE*'
sonic-db-cli APPL_DB keys 'VXLAN_FDB_TABLE*'

13.3 看 show 命令

show vxlan name
show vxlan vlanvnimap
show vxlan remotevni all
show vxlan remotemac all
show vxlan status

show vxlan remotevni 对应 src/sonic-utilities/show/vxlan.pyremotevni()

show vxlan remotemac 对应 src/sonic-utilities/show/vxlan.pyremotemac(),它读取 VXLAN_FDB_TABLE

13.4 看 Linux VXLAN/bridge 状态

ip -d link show type vxlan
bridge link show
bridge fdb show
ip neigh show dev Vlan100

重点关注:

  • VXLAN netdevice 是否存在。
  • bridge link show 是否能看到 neigh_suppress on
  • flood/bcast_flood/mcast_flood 是否符合预期。
  • ip neigh 是否存在目标 IP/MAC。
  • 远端 EVPN 学到的邻居是否带 extern_learn

13.5 看 ASIC_DB

sonic-db-cli ASIC_DB keys 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL*'
sonic-db-cli ASIC_DB keys 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP*'
sonic-db-cli ASIC_DB keys 'ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP*'
sonic-db-cli ASIC_DB keys 'ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY*'

如果 overlay next-hop 带内层 MAC,应该重点关注类似:

SAI_NEXT_HOP_ATTR_TUNNEL_MAC
SAI_NEXT_HOP_ATTR_TUNNEL_VNI
SAI_NEXT_HOP_ATTR_IP

13.6 抓包验证

tcpdump -eni <access_port> arp
tcpdump -eni <underlay_port> udp port 4789

如果 ARP 代答命中,通常能看到:

  1. access 侧主机发出 ARP Request。
  2. 本地很快出现 ARP Reply。
  3. underlay 侧不应出现对应 ARP Request 被 VXLAN 封装后 flood 出去。

如果未命中且允许 flood,underlay 侧会看到 UDP 4789 的 VXLAN BUM 流量。


14. 常见问题

14.1 为什么配置了 VXLAN,ARP 代答还是不生效?

常见原因:

  • VLAN 没有映射到 VNI。
  • 本地或远端没有学到 IP/MAC 绑定。
  • EVPN Type-2 只有 MAC,没有 IP。
  • redistribute_host 被关闭,导致本地主机不带 IP 发布。
  • flood_suppress 未按预期打开。
  • Linux VXLAN netdevice 没有 proxy 或 bridge port 没有 neigh_suppress on
  • 远端 VTEP 没有加入对应 VNI,VXLAN_REMOTE_VNI_TABLE 缺失。

14.2 redistribute_hostflood_suppress 谁更重要?

它们解决的问题不同。

redistribute_host 决定“有没有 IP/MAC 关系可以被远端学习”。没有 MAC+IP,远端无法准确回答 ARP。

flood_suppress 决定“未知或广播流量是否继续 flood”。它能减少 BUM,但如果数据库里没有目标 IP/MAC,打开它也不能凭空生成正确 ARP Reply。

14.3 MAC-only Type-2 为什么不够?

ARP 问的是 IP,不是 MAC。MAC-only Type-2 只能告诉远端 MAC 的位置,不能告诉远端某个 IP 对应哪个 MAC。因此要做 ARP 代答,最好需要 MAC+IP Type-2。

14.4 ARP 代答是不是一定由 CPU 构造报文?

不一定。不同平台可能由 Linux bridge/VXLAN、ASIC 或控制面代理协同完成。本文基于本地 SONiC 代码能看到的是:配置面会打开 Linux VXLAN proxy、bridge neigh_suppress,并通过 SAI 设置 VLAN flood control;同时通过 EVPN/DB/SAI 建立远端 MAC/IP/VTEP/VNI 信息。具体数据面实现会和平台能力有关。


15. 推荐阅读代码顺序

如果第一次读 SONiC VXLAN 代码,建议不要从 orchagent 的大文件开始硬啃,可以按下面顺序来:

  1. src/sonic-utilities/config/vxlan.py
    先理解 CLI 写了哪些 CONFIG_DB 表。

  2. src/sonic-yang-models/yang-models/sonic-vxlan.yang
    看 VXLAN 配置模型,理解 VXLAN_TUNNEL_MAPVXLAN_REMOTE_VNIVXLAN_FDB

  3. src/sonic-swss/cfgmgr/vxlanmgr.cpp
    VxlanMgr::doTask() 如何分发配置,看 createVxlanNetdevice() 如何创建 Linux VXLAN netdevice。

  4. src/sonic-swss/orchagent/vxlanorch.cpp
    看 tunnel、tunnel map、remote VNI 和 SAI tunnel next-hop 如何编排。

  5. src/sonic-swss/orchagent/neighorch.cpp
    看本地邻居如何写入 VXLAN_FDB_TABLE,以及 redistribute_host 如何影响 ip 字段。

  6. src/sonic-swss/orchagent/fdborch.cpp
    看 VXLAN 学习到的 MAC 如何进入 FDB 处理流程。

  7. src/sonic-restapi/arp_responder/arp.cc
    最后看 ARP Reply 报文如何被构造,加深对“代答”动作本身的理解。


16. 结语

VXLAN ARP 代答可以拆成两句话:

第一句,原理上它是为了减少 ARP 广播在 VXLAN overlay 中的 BUM 扩散。

第二句,工程上它依赖控制面提前学到 IP/MAC/VNI/VTEP,并把这些信息正确地下发到 Linux bridge/VXLAN netdevice、APP_DB、orchagent、SAI 和硬件。

结合本地 SONiC 代码,最值得抓住的两个开关是:

  • redistribute_host 影响 MAC+IP Type-2 学习,是“能不能知道 IP/MAC”的问题。
  • flood_suppress 影响 proxy/neigh_suppress 和 flood control,是“未知流量还要不要扩散”的问题。

把这两点讲清楚,再顺着 config vxlan -> CONFIG_DB -> vxlanmgrd -> APP_DB -> orchagent -> SAI 的链路走,VXLAN ARP 代答就不再是一个黑盒功能了。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐