MCLAG 从原理到 SONiC 代码实现
摘要
MCLAG 的全称是 Multi-Chassis Link Aggregation,也就是跨设备的链路聚合。普通 LAG/PortChannel 只能把多条链路聚合到同一台交换机上;MCLAG 则让服务器或下游交换机把链路分别接到两台交换机,但在它看来仍然像接入了一个逻辑 PortChannel。
在 SONiC 中,MCLAG 不是一个独立函数完成的功能,而是由 iccpd、mclagsyncd、CONFIG_DB、STATE_DB、APPL_DB、orchagent、FDB 编排、ARP/ND 同步以及可选的 BGP host route 发布共同组成。
最核心的两条线是:
- 控制面:两台设备通过 ICCP/mLACP 交换角色、接口、MAC、ARP/ND 等信息。
- 数据面:本地和远端学习到的 FDB、端口隔离、MAC 学习模式、流量开关等,最终通过 SWSS 编排到 ASIC。
一句话总结:
MCLAG 的本质不是“两个交换机都配置了 PortChannel”,而是“两台交换机通过 ICCP/mLACP 把自己协调成一个逻辑聚合系统,同时避免环路、重复学习和异常转发”。
关键词
MCLAG、MLAG、ICCP、mLACP、PortChannel、peer-link、active/standby、FDB sync、ARP sync、ND sync、SONiC、SWSS、mclagsyncd、iccpd
1. 先建立直觉:MCLAG 解决什么问题?
假设一台服务器有两张网卡,希望做链路聚合:
Server eth0 ---- Switch A
Server eth1 ---- Switch B
如果没有 MCLAG,服务器不能简单地把 eth0 和 eth1 做成一个普通 LACP PortChannel,因为这两条链路接到了两台不同的交换机。对服务器来说,它希望对端是“一个逻辑交换机”;但真实网络里,对端其实是两台独立设备。
MCLAG 要解决的就是这个矛盾:
服务器视角: Server <==== PortChannel ====> 一个逻辑交换机
真实物理视角: Server eth0 -> Switch A
Server eth1 -> Switch B
为了做到这一点,两台交换机必须互相同步:
- 谁是 Active,谁是 Standby。
- 哪些 PortChannel 是 MCLAG 成员。
- peer-link 是哪个接口。
- 哪些 MAC 是本地学到的,哪些 MAC 是 peer 同步来的。
- ARP/ND 表项如何同步。
- 什么时候要隔离端口,避免流量环路。
- peer 掉线、keepalive 超时、接口状态变化时怎么处理。
2. 没有 MCLAG 时会有什么问题?
如果服务器双上联到两台独立交换机,而交换机之间没有 MCLAG 协调机制,就会遇到几个典型问题。
2.1 LACP 视角不一致
服务器认为自己在和一个 LACP partner 建立聚合,但 Switch A 和 Switch B 是两个独立系统。它们的 system ID、端口状态、聚合状态不一致,普通 LACP 无法天然把它们合成一个 PortChannel。
2.2 MAC 学习冲突
同一个服务器 MAC 可能在 Switch A 和 Switch B 上同时出现。如果两台交换机不共享 MAC 信息,就可能出现 MAC 抖动、错误转发或重复下发。
2.3 环路和双发风险
两台设备都能转发同一个二层域的流量,如果没有 peer-link 隔离、成员端口隔离和角色协调,就可能形成环路或重复流量。
没有 MCLAG 协调时的风险可以简化成下面这张图:

图 1:没有 MCLAG 协调时的风险
MCLAG 的目标就是让 Switch A 和 Switch B 在控制面上达成一致,对外表现得像一个逻辑系统。
3. 有 MCLAG 时的基本逻辑
MCLAG 场景里,常见角色如下:
Server / 下游设备:双上联,配置 bond/PortChannel
Switch A:MCLAG peer 之一
Switch B:MCLAG peer 之一
Peer Link:Switch A 与 Switch B 之间用于同步和转发的链路
ICCP:两台交换机之间的控制协议通道
mLACP:在 ICCP 之上同步 LACP/聚合相关状态
典型拓扑:

图 2:MCLAG 双归接入拓扑
可以把 MCLAG 理解成四件事:
- 配置一个 MCLAG domain,告诉设备本端 IP、peer IP、peer-link 等信息。
- 把某些 PortChannel 加入这个 domain,成为 MCLAG 成员。
- 两台设备通过 ICCP/mLACP 建立会话,同步角色、接口、MAC、ARP/ND 等信息。
- 本地 SWSS 根据同步结果设置 FDB、端口隔离、MAC 学习模式、流量开关等数据面状态。
4. SONiC 代码中的关键模块
下面是本文涉及的本地代码索引:
| 模块 | 关键文件 | 作用 |
|---|---|---|
| 构建开关 | rules/config |
INCLUDE_ICCPD = y 控制是否构建 MCLAG 支持 |
| Debian 包 | rules/iccpd.mk |
定义 iccpd 包版本、源码路径和依赖 |
| Docker 镜像 | rules/docker-iccpd.mk |
定义 docker-iccpd 镜像和容器包 |
| 容器脚本 | dockers/docker-iccpd/start.sh, iccpd.sh, iccpd.j2 |
生成配置并启动 mclagsyncd、iccpd |
| CLI | src/sonic-utilities/config/mclag.py |
提供 config mclag ... 命令 |
| YANG | src/sonic-yang-models/yang-models/sonic-mclag.yang |
定义 MCLAG_DOMAIN、MCLAG_INTERFACE、MCLAG_UNIQUE_IP |
| mclagsyncd | src/sonic-swss/mclagsyncd/mclagsyncd.cpp, mclaglink.cpp, mclag.h |
在 Redis DB 和 iccpd 之间转发配置、FDB、状态消息 |
| iccpd | src/iccpd/src/*.c, src/iccpd/include/*.h |
维护 ICCP 会话、mLACP 状态机、MAC/ARP/ND 同步 |
| MlagOrch | src/sonic-swss/orchagent/mlagorch.cpp |
让 orchagent 感知 peer-link 和 MCLAG 成员接口 |
| FdbOrch | src/sonic-swss/orchagent/fdborch.cpp |
消费 MCLAG FDB 表项,处理远端/本地 MAC 学习关系 |
| BGP 发布 | src/sonic-bgpcfgd/bgpcfgd/managers_advertise_rt.py |
将 MCLAG host route 转成 FRR BGP network 配置 |
| 诊断工具 | src/iccpd/src/mclagdctl/mclagdctl.c |
查询 MCLAG 状态、ARP/ND/MAC、端口列表和 debug counters |
5. 配置面:从 config mclag 到 CONFIG_DB
SONiC 的 MCLAG CLI 入口在:
src/sonic-utilities/config/mclag.py
常见命令和写入的数据如下:
| 命令 | 写入/修改的表 | 说明 |
|---|---|---|
config mclag add <domain_id> <source_ip> <peer_ip> [peer_link] |
CONFIG_DB:MCLAG_DOMAIN |
创建或修改 MCLAG domain |
config mclag del <domain_id> |
CONFIG_DB:MCLAG_DOMAIN, MCLAG_INTERFACE |
删除 domain,并清理成员接口 |
config mclag keepalive-interval <domain_id> <seconds> |
CONFIG_DB:MCLAG_DOMAIN.keepalive_interval |
设置 keepalive 周期 |
config mclag session-timeout <domain_id> <seconds> |
CONFIG_DB:MCLAG_DOMAIN.session_timeout |
设置会话超时时间 |
config mclag member add <domain_id> <PortChannel...> |
CONFIG_DB:MCLAG_INTERFACE |
添加 MCLAG 成员 PortChannel |
config mclag member del <domain_id> <PortChannel...> |
CONFIG_DB:MCLAG_INTERFACE |
删除 MCLAG 成员 PortChannel |
config mclag unique-ip add <Vlan...> |
CONFIG_DB:MCLAG_UNIQUE_IP |
开启 VLAN unique-ip |
config mclag unique-ip del <Vlan...> |
CONFIG_DB:MCLAG_UNIQUE_IP |
删除 VLAN unique-ip |
配置流如下:

图 3:从 config mclag 到 CONFIG_DB 和 iccpd
一个最小配置例子:
config mclag add 1 10.0.0.1 10.0.0.2 PortChannel100
config mclag keepalive-interval 1 5
config mclag session-timeout 1 30
config mclag member add 1 PortChannel10
含义是:
domain_id = 1
本端 ICCP IP = 10.0.0.1
对端 ICCP IP = 10.0.0.2
peer-link = PortChannel100
MCLAG 成员接口 = PortChannel10
6. YANG 模型:哪些配置是合法的?
MCLAG 配置模型定义在:
src/sonic-yang-models/yang-models/sonic-mclag.yang
6.1 MCLAG_DOMAIN
MCLAG_DOMAIN 描述一个 MCLAG domain。代码中 YANG 使用 max-elements 1,因此当前模型最多允许一个 domain。
| 字段 | 类型/约束 | 说明 |
|---|---|---|
domain_id |
uint16, 范围 1..4095 |
MCLAG domain ID,表 key |
source_ip |
IPv4 address | 本端 ICCP 源地址 |
peer_ip |
IPv4 address | 对端 ICCP 地址 |
peer_link |
Port 或 PortChannel leafref | peer-link 接口 |
keepalive_interval |
uint16, 范围 1..60, 默认 1 |
keepalive 周期 |
session_timeout |
uint16, 范围 1..3600, 默认 30 |
会话超时时间 |
arp_to_bgp_host_route |
boolean, 默认 false |
是否将 ARP 学习到的主机路由发布给 BGP |
YANG 约束:
keepalive_interval * 3 <= session_timeout
CLI 中还有额外检查:session_timeout 需要是 keepalive_interval 的整数倍。这个整数倍约束来自 CLI 逻辑,不是 YANG must 条件。
6.2 MCLAG_INTERFACE
MCLAG_INTERFACE 描述加入 MCLAG domain 的 PortChannel 成员。
| 字段 | 类型/约束 | 说明 |
|---|---|---|
domain_id |
leafref 到 MCLAG_DOMAIN |
所属 domain |
if_name |
leafref 到 PORTCHANNEL |
MCLAG 成员接口 |
if_type |
string | CLI 写入 PortChannel |
Redis key 形态:
MCLAG_INTERFACE|<domain_id>|<PortChannelName>
6.3 MCLAG_UNIQUE_IP
MCLAG_UNIQUE_IP 用于配置 VLAN 接口 unique-ip。
| 字段 | 类型/约束 | 说明 |
|---|---|---|
if_name |
VLAN 名称,例如 Vlan100 |
表 key |
unique_ip |
enum,仅支持 enable |
开启 unique-ip |
YANG 要求已经存在 MCLAG domain,并且接口名必须符合 VLAN 命名格式。CLI 还会检查 VLAN 接口不能绑定非 default VRF,且不能已经配置 IP 地址。
Redis key 形态:
MCLAG_UNIQUE_IP|<VlanName>
7. 容器启动:mclagsyncd 和 iccpd 是怎么起来的?
MCLAG 运行在 iccpd 容器里。构建和启动相关文件主要是:
rules/iccpd.mk
rules/docker-iccpd.mk
dockers/docker-iccpd/start.sh
dockers/docker-iccpd/iccpd.sh
dockers/docker-iccpd/iccpd.j2
启动流程可以简化为:

图 4:docker-iccpd 容器启动流程
这里要注意一个容易踩坑的点:
iccpd.j2模板读取的是MC_LAG字段,用来生成/etc/iccpd/iccpd.conf。- 当前 CLI/YANG 运行态主要使用
MCLAG_DOMAIN、MCLAG_INTERFACE、MCLAG_UNIQUE_IP。
所以看问题时要先确认配置到底来自启动模板,还是来自运行态 CONFIG_DB。
8. mclagsyncd:Redis DB 和 iccpd 之间的翻译层
mclagsyncd 的入口在:
src/sonic-swss/mclagsyncd/mclagsyncd.cpp
它做的事可以理解成“翻译”:
CONFIG_DB / STATE_DB / APPL_DB <--> mclagsyncd <--> iccpd
启动后,mclagsyncd 会创建 DB 连接,等待 iccpd 连上 127.0.0.6:2626。连接建立后,它会读取当前 MCLAG 配置,并把相关表加入事件循环。
核心监听表包括:
| 表 | DB | 作用 |
|---|---|---|
MCLAG_DOMAIN |
CONFIG_DB | domain 配置变化 |
MCLAG_INTERFACE |
CONFIG_DB | MCLAG 成员接口变化 |
MCLAG_UNIQUE_IP |
CONFIG_DB | unique-ip 配置变化 |
STATE_FDB_TABLE |
STATE_DB | 本地 FDB 变化 |
STATE_MCLAG_REMOTE_FDB_TABLE |
STATE_DB | 远端 MCLAG FDB 状态 |
STATE_VLAN_MEMBER_TABLE |
STATE_DB | VLAN member 变化 |
整体流程图:

图 5:mclagsyncd 在 DB 与 iccpd 之间的作用
消息类型定义在:
src/sonic-swss/mclagsyncd/mclag.h
mclagsyncd 发给 iccpd 的典型消息包括:
| 消息 | 含义 |
|---|---|
MCLAG_MSG_TYPE_CFG_MCLAG_DOMAIN |
domain 配置 |
MCLAG_MSG_TYPE_CFG_MCLAG_IFACE |
成员接口配置 |
MCLAG_MSG_TYPE_CFG_MCLAG_UNIQUE_IP |
unique-ip 配置 |
MCLAG_MSG_TYPE_FDB_OPERATION |
本地 FDB 变化 |
MCLAG_MSG_TYPE_VLAN_MBR_UPDATES |
VLAN member 变化 |
iccpd 发给 mclagsyncd 的典型消息包括:
| 消息 | 含义 |
|---|---|
MCLAG_MSG_TYPE_SET_FDB |
设置或删除 MCLAG FDB |
MCLAG_MSG_TYPE_PORT_ISOLATE |
设置端口隔离 |
MCLAG_MSG_TYPE_PORT_MAC_LEARN_MODE |
设置 MAC 学习模式 |
MCLAG_MSG_TYPE_SET_ICCP_STATE |
更新 ICCP 状态 |
MCLAG_MSG_TYPE_SET_ICCP_ROLE |
更新 Active/Standby 角色 |
MCLAG_MSG_TYPE_SET_REMOTE_IF_STATE |
更新远端接口状态 |
MCLAG_MSG_TYPE_SET_HOST_ROUTE |
设置或删除 BGP host route |
MCLAG_MSG_TYPE_SYNC_HOST_ROUTES |
同步 host route |
9. iccpd:ICCP 会话和 mLACP 状态机
iccpd 是 MCLAG 控制面的核心。它负责:
- 读取配置。
- 建立设备间 ICCP 会话。
- 维护 Active/Standby 角色。
- 运行 mLACP 状态机。
- 同步 MAC、ARP、ND、接口状态。
- 向
mclagsyncd下发 FDB、隔离、host route 等结果。
关键文件:
| 文件 | 作用 |
|---|---|
src/iccpd/src/system.c |
初始化全局 System、日志路径、配置路径、控制 socket 路径 |
src/iccpd/src/scheduler.c |
主调度循环,读取配置、初始化 socket/netlink、检查 heartbeat |
src/iccpd/include/iccp_csm.h |
ICCP CSM 状态、端口、domain 字段定义 |
src/iccpd/src/iccp_csm.c |
CSM 状态、Active/Standby 角色相关逻辑 |
src/iccpd/include/mlacp_fsm.h |
mLACP 状态机和同步阶段定义 |
src/iccpd/src/mlacp_fsm.c |
mLACP 状态机实现 |
src/iccpd/src/mlacp_link_handler.c |
与 mclagsyncd 的消息处理、host route、接口状态等 |
ICCP 会话状态包括:
NONEXISTENT
INITIALIZED
CAPSENT
CAPREC
CONNECTING
OPERATIONAL
mLACP 主状态可以简化为:
INIT -> STAGE1 -> STAGE2 -> EXCHANGE
状态机流程图:

图 6:mLACP 状态机主流程
角色选择也很重要。代码中按本端 IP 和 peer IP 比较:
source_ip 更小的一侧:Active,并主动连接 peer
source_ip 更大的一侧:Standby,等待 peer 连接
source_ip 等于 peer_ip:错误配置
对应流程:

图 7:ICCP Active/Standby 角色判断
10. 成员接口和 peer-link 如何生效
MCLAG 中有两类接口要分清:
| 接口 | 含义 |
|---|---|
| peer-link | 两台 MCLAG peer 之间的互联链路 |
| MCLAG member | 面向服务器或下游设备的双归 PortChannel |
CLI 添加成员接口:
config mclag member add 1 PortChannel10
写入 CONFIG_DB 后,链路如下:

图 8:MCLAG 成员接口配置流转
mclagsyncd 在处理成员 SET 时,会写入本地接口状态:
MCLAG_LOCAL_INTF_TABLE|<PortChannelName>
port_isolate_peer_link=true
MlagOrch 会监听:
MCLAG_DOMAIN:主要关注peer_link。MCLAG_INTERFACE:维护 MCLAG 成员接口集合。
MlagOrch 不负责建立 ICCP 会话,它更像 orchagent 内部的“接口身份识别器”:告诉其他模块某个接口是不是 MCLAG 成员,某个接口是不是 peer-link。
11. FDB 同步:MAC 是怎么跨设备同步的?
FDB 同步是理解 MCLAG 的重点。可以从两个方向看。
11.1 本地学习的 MAC 发给 peer
当本机学习到 MAC 后,FDB 变化会进入 STATE_FDB_TABLE。mclagsyncd 的 processStateFdb() 读取这些变化,转换为 MCLAG FDB 消息发给 iccpd,再由 iccpd 同步给 peer。
流程图:

图 9:FDB 同步流程
代码中 FDB 类型映射如下:
| DB 字段 | MCLAG 消息类型 |
|---|---|
dynamic |
MCLAG_FDB_TYPE_DYNAMIC |
dynamic_local |
MCLAG_FDB_TYPE_DYNAMIC |
static |
MCLAG_FDB_TYPE_STATIC |
DEL 事件可能不带完整字段,所以 mclagsyncd 会缓存 FDB 对应的 port/type,删除时仍能发出完整信息。
11.2 peer 同步来的 MAC 写入本机
peer 同步来的 FDB 由 iccpd 发给 mclagsyncd。mclagsyncd 再写入 APPL_DB:
| APPL_DB 表 | 用途 |
|---|---|
MCLAG_FDB_TABLE |
普通 MCLAG 同步 FDB |
MCLAG_CTRL_FDB_TABLE |
控制类 MCLAG FDB |
key 格式:
Vlan<vid>:<mac>
fdborch 消费这两张表,并用不同 origin 标记区分:
| 表 | origin 标记 |
|---|---|
APP_MCLAG_FDB_TABLE_NAME |
FDB_ORIGIN_MCLAG_ADVERTIZED |
APP_MCLAG_CTRL_FDB_TABLE_NAME |
FDB_ORIGIN_MCLAG_CTRL_ADVERTIZED |
还有一个细节:mclagsyncd 会读取 STATE_MCLAG_REMOTE_FDB_TABLE,但代码中避免将 remote MCLAG FDB 再发回 iccpd,防止重复同步或加删循环。
12. ARP/ND 同步与 BGP host route
MCLAG 不只同步 MAC,也会处理 ARP/ND。iccpd 通过 netlink 维护本机 ARP/ND 信息,并在 mLACP EXCHANGE 阶段与 peer 同步。
如果 MCLAG_DOMAIN 中启用:
arp_to_bgp_host_route=true
那么本地 ARP 产生的 IPv4 /32 主机路由可以被发布给 BGP。
流程图:

图 10:ARP 到 BGP host route 的流程
mclagsyncd 写入 STATE_DB 的形态:
ADVERTISE_NETWORK_TABLE|<ip>/32
source=mclag
如果带 VRF,key 会格式化为:
ADVERTISE_NETWORK_TABLE|<vrf>|<ip>/32
src/sonic-bgpcfgd/bgpcfgd/managers_advertise_rt.py 中的 AdvertiseRouteMgr 监听 STATE_DB:ADVERTISE_NETWORK_TABLE,把 source=mclag 的前缀转换成 FRR BGP address-family 下的 network <prefix> 配置。
代码限制也很明确:这里只处理 IPv4 /32 host route,非 /32 前缀会被拒绝。
13. 端口隔离、MAC 学习和流量开关
MCLAG 必须避免双归场景中的环路和错误转发。因此 iccpd 会通过 mclagsyncd 设置一些数据面状态。
| 功能 | 代码行为 |
|---|---|
| 端口隔离 | setPortIsolate() 更新 APP_ISOLATION_GROUP_TABLE 和 APP_ISOLATION_GROUP_MEMBER_TABLE |
| 保留隔离组 | 使用保留组名 MCLAG_ISO_GRP |
| MAC 学习模式 | setPortMacLearnMode() 向 PORT/LAG APP 表写入 learn_mode |
| FDB flush | setFdbFlush() 发送 FLUSHFDBREQUEST 通知 |
| 接口 MAC | setIntfMac() 设置接口 mac_addr |
| 流量开关 | mclagsyncdSetTrafficDisable() 写入 LAG 表 traffic_disable=true/false |
| 远端接口状态 | 写入 MCLAG_REMOTE_INTF_TABLE |
隔离相关流程:

图 11:端口隔离下发流程
src/sonic-utilities/config/isolation_group.py 中保留了 MCLAG_ISO_GRP,普通用户配置隔离组时不能使用这个名字。
14. 常用查看与排障命令
14.1 查看 CONFIG_DB
sonic-db-cli CONFIG_DB hgetall "MCLAG_DOMAIN|1"
sonic-db-cli CONFIG_DB hgetall "MCLAG_INTERFACE|1|PortChannel10"
sonic-db-cli CONFIG_DB hgetall "MCLAG_UNIQUE_IP|Vlan100"
14.2 查看 STATE_DB
sonic-db-cli STATE_DB hgetall "MCLAG_TABLE|1"
sonic-db-cli STATE_DB hgetall "MCLAG_LOCAL_INTF_TABLE|PortChannel10"
sonic-db-cli STATE_DB hgetall "MCLAG_REMOTE_INTF_TABLE|1|PortChannel10"
sonic-db-cli STATE_DB keys "ADVERTISE_NETWORK_TABLE*"
14.3 查看 APPL_DB
sonic-db-cli APPL_DB keys "MCLAG_FDB_TABLE*"
sonic-db-cli APPL_DB keys "MCLAG_CTRL_FDB_TABLE*"
sonic-db-cli APPL_DB keys "ISOLATION_GROUP*"
sonic-db-cli APPL_DB hgetall "LAG_TABLE:PortChannel10"
14.4 使用 mclagdctl
mclagdctl 的 host 侧包装脚本会通过 docker exec 进入 iccpd 容器执行真实命令。
常用命令:
mclagdctl -i 1 dump state
mclagdctl -i 1 dump arp
mclagdctl -i 1 dump nd
mclagdctl -i 1 dump mac
mclagdctl -i 1 dump unique_ip
mclagdctl -i 1 dump portlist local
mclagdctl -i 1 dump portlist peer
mclagdctl -i 1 dump debug counters
mclagdctl -l debug config loglevel
dump state 里可以看到:
- keepalive 状态。
- 信息同步是否完成。
- domain id。
- local IP / peer IP。
- peer-link。
- keepalive time / session timeout。
- Active 或 Standby 角色。
- MCLAG interface。
- 当前 loglevel。
14.5 核心故障案例:ICCP 会话建立失败,MAC/ARP/FDB 无法同步
现象
两台设备已经写入 MCLAG domain 和成员接口配置,但业务侧看不到跨设备同步的 MAC、ARP/ND,dump state 持续显示:
The MCLAG's keepalive is: ERROR
MCLAG info sync is: incomplete
在本端 IP 较小、负责主动连接 peer 的设备上,/var/log/iccpd.log 反复出现类似错误:
Bind socket failed. Error = -1 errno = 99
这里以 Linux 常见的 errno=99 为例;不同失败原因的 errno 可能不同,首先应抓住 Bind socket failed 这个关键信号。
定位
先确认两端 CONFIG_DB 中的本端和对端地址没有写反:
sonic-db-cli CONFIG_DB hgetall "MCLAG_DOMAIN|1"
再确认 source_ip 确实配置在本机,而不是只存在于 MCLAG 配置中:
ip -4 addr show
ip route get <peer_ip> from <source_ip>
ping -I <source_ip> <peer_ip>
最后检查对端 iccpd 是否监听 ICCP 使用的 TCP/8888:
docker ps --filter name=iccpd
docker exec iccpd ss -lntp | grep ':8888'
当前本地代码中,src/iccpd/src/scheduler.c 会先把出站 socket 绑定到 csm->sender_ip,然后才连接 csm->peer_ip:8888。如果 source_ip 不是本机地址,bind() 就会失败,后续 connect() 根本不会执行。src/iccpd/include/iccp_csm.h 则明确将 ICCP_TCP_PORT 定义为 8888。
根因
本例的根因是 MCLAG domain 中的 source_ip 填错,或者该地址尚未配置到本机接口。CONFIG_DB 里虽然存在字符串形式的 source_ip,但这不代表 Linux 已经拥有这个地址。
由于低 IP 一侧无法绑定本地源地址,它不能向 peer 发起 TCP/8888 连接,ICCP 状态无法进入 OPERATIONAL;mLACP 也无法进入 EXCHANGE,因此 MAC、ARP/ND 和接口状态同步都不会正常开始。
解决
先通过持久化接口配置把 ICCP 源地址配置到正确的本机接口,或改正写反的 MCLAG 地址。两台设备的地址必须互为 local/peer。例如:
# Switch A
config mclag add 1 10.0.0.1 10.0.0.2 PortChannel100
# Switch B
config mclag add 1 10.0.0.2 10.0.0.1 PortChannel100
config mclag add 对已存在的同一 domain 会修改 MCLAG_DOMAIN。生产环境变更会触发会话重新建立,应在维护窗口执行,并先确认 peer-link 和承载 ICCP 地址的三层接口都已就绪。不要只临时执行 ip addr add 后就认为问题永久解决,因为重启后临时地址可能丢失。
验证
修复后按下面顺序形成闭环:
ip -4 addr show能在本机看到正确的source_ip。ping -I <source_ip> <peer_ip>成功,对端能看到 TCP/8888 监听。- 日志不再出现
Bind socket failed,并出现连接成功记录。 mclagdctl -i 1 dump state显示The MCLAG's keepalive is: OK和MCLAG info sync is: completed。- 再用
dump mac、dump arp和 STATE_DB/APPL_DB 验证同步结果;表项为空不一定是故障,还要先确认网络中确实产生了对应学习事件。
14.6 查看日志
docker logs iccpd
iccpd 代码中的默认日志路径:
/var/log/iccpd.log
因为 iccpd.sh 同时启动了 mclagsyncd 和 iccpd,所以排查时要同时关注这两个进程的日志。
15. 一次完整配置后的代码链路
以这组配置为例:
config mclag add 1 10.0.0.1 10.0.0.2 PortChannel100
config mclag keepalive-interval 1 5
config mclag session-timeout 1 30
config mclag member add 1 PortChannel10
完整链路可以这样理解:

图 12:一次完整配置后的代码链路
对应文字版:
config mclag写 CONFIG_DB。mclagsyncd监听 CONFIG_DB,把 domain/member 配置发给iccpd。iccpd创建或更新 ICCP CSM。- 低 IP 一侧主动连接 peer,高 IP 一侧等待连接。
- 双方使用 TCP/8888 建立 ICCP 会话。
- mLACP FSM 进入同步阶段,交换系统、聚合口、peer-link、ARP/ND、MAC 等信息。
- 本地 FDB 变化经
STATE_FDB_TABLE -> mclagsyncd -> iccpd -> peer同步。 - peer FDB 经
peer iccpd -> local iccpd -> mclagsyncd -> APPL_DB MCLAG_FDB_TABLE下发给 orchagent。 MlagOrch和FdbOrch识别 peer-link、MCLAG 成员和 MCLAG FDB,最终影响 ASIC 编程。- 如果启用
arp_to_bgp_host_route,ARP 产生的 IPv4 /32 会进入STATE_DB:ADVERTISE_NETWORK_TABLE,由bgpcfgd发布到 BGP。
16. 新手排查思路
如果 MCLAG 不工作,可以按下面顺序看,不要一上来就直接抓复杂报文。

图 13:新手排查顺序
建议顺序:
- 先查 CONFIG_DB,确认配置有没有写进去。
- 再查
docker logs iccpd和mclagdctl -i 1 dump state,确认 ICCP 会话。 - 再查 STATE_DB,确认本地/远端接口状态。
- 再查 APPL_DB,确认 FDB、隔离组、LAG 状态是否被写入。
- 最后再看 orchagent、syncd、ASIC 层。
17. 代码层面的注意事项
-
当前 YANG 和 CLI 都体现了单 domain 设计,
MCLAG_DOMAIN最多一个实例。 -
keepalive_interval的 YANG 默认值是1,session_timeout的 YANG 默认值是30。但 CLI 辅助逻辑中,部分字段缺省时使用自己的 fallback 值,例如 keepalive 设置逻辑里对缺省session_timeout使用了15。线上建议显式配置 keepalive 和 timeout。 -
src/sonic-utilities/config/mclag.py中ADHOC_VALIDATION = False,实际写库通常依赖ValidatedConfigDBConnector/CVL/YANG 做校验。阅读 CLI 里的手工校验函数时,要注意它们不一定在默认路径全部启用。 -
启动模板
iccpd.j2使用MC_LAG,而当前配置 CLI 写入MCLAG_DOMAIN、MCLAG_INTERFACE、MCLAG_UNIQUE_IP。两套入口在代码中同时存在,分析问题时要确认目标版本实际使用的是哪条路径。 -
src/sonic-swss/mclagsyncd/mclaglink.cpp中mclagsyncdSendMclagUniqueIpCfg()对 key 的解析看起来按包含|的复合 key 处理;而 YANG/CLI 中MCLAG_UNIQUE_IP的 key 是 VLAN 接口名。如果发现 unique-ip 运行态不生效,应优先在目标分支上确认这段 key 解析与实际 Redis key 是否一致。 -
arp_to_bgp_host_route只会发布 IPv4 /32 host route。mclagsyncd写入 STATE_DB 的ADVERTISE_NETWORK_TABLE,再由bgpcfgd转换为 FRR BGP network 配置。 -
MCLAG_ISO_GRP是 MCLAG 内部使用的隔离组名,普通隔离组配置不应使用该名字。

浙公网安备 33010602011771号