最近连续做了几个多网口设备的预研项目,手头这款双网口模块的规格书第一页就写着:Dual Ethernet Module Operates as Independent Ports or Switch。这句话翻译过来很好理解——两个物理网口,既能各自当作独立网卡使用,也能通过芯片内部的交换逻辑变成一个二口交换机。做网关、边缘计算盒子、工业路由器的朋友看到这类模块大概率都会眼前一亮:同一个硬件,能覆盖三种产品形态,独立端口模式下做双线上网、WAN/LAN 隔离,交换机模式下做串联接入,或者直接塞在设备中间当一个无感的分线器。这篇文章就把这两种模式从硬件到软件、从原理到调试完整拆一遍,重点说清楚为什么“一个模块两种性格”这件事可行,以及落地时最容易在哪几个环节翻车。
1. 双网口模块到底在解决什么场景问题
1.1 从“一根网线只能接一个设备”说起
单网口设备有一个天然痛点:数据通路只有一条,无论它是上行还是下行,设备在链路里都只能扮演一个端点。工业现场最常见的接法是“PLC 之间手拉手串联”,控制器的以太网口只有两个的非常少,绝大多数 PLC 只有一个 RJ45,想做菊花链就必须外接一个工业交换机,多一个设备就多一个故障点、多一路电源、多一堆现场接线端子。我在现场见过很多配电柜,里面的工业交换机比控制器本身还占空间。
双网口模块解决的就是这类“设备串接”问题。两个口一个接上行、一个接下行的设备,如果模块内部能以交换机模式工作,数据就在芯片内部直接完成二层转发,不占主控 CPU 的资源。这个场景下,模块对两端设备来说就是一根“会发热的网线”,不需要任何配置,插上就能通。
还有一种更常见的需求是网关类产品。设备需要一个 WAN 口接宽带、一个 LAN 口接内网设备,两个口必须各自独立,IP 段不同、路由策略不同、防火墙策略也不同。这种就是典型的独立端口模式,两个口在操作系统里呈现为 eth0 和 eth1 两张网卡,各自拥有 MAC 地址和 IP 地址。
1.2 两类用户对同一个模块的不同期待
我在评估模块时接触过两类完全不同的项目方,需求差异很能说明问题。
第一类是做数据采集网关的,他们对模块的期待是“两个口必须物理独立”。因为现场组网环境不可控,两个口可能分别连接两个完全隔离的业务网络,A 网的广播风暴不能影响到 B 网,A 网的设备也不能通过模块偷偷访问到 B 网。这要求模块的独立端口模式不是“软件桥接之后逻辑隔离”,而是从硬件数据通路层面就把两个口切开。
第二类是做人机界面或者工业屏的,他们的期待恰恰相反。现场设备通常没有交换机,却需要一台 HMI 同时连接 PLC 和上位机,或者两台 PLC 串联到一个触摸屏。他们希望模块插上就用,两个口之间自动互通,不需要配 IP、不需要敲命令,最好未知设备插上就能通。
这两类需求看起来矛盾,但聚焦到模块设计上其实只是一个配置差异:交换芯片内部怎么处理端口间的转发关系。前者做端口隔离,后者做端口互通。芯片本身并不区分谁是谁,完全是寄存器里几个 bit 的事情。
2. 独立端口模式与交换机模式:一枚芯片的两种性格
2.1 独立端口模式:每个口都有独立 MAC 和独立 IP
在独立端口模式下,两个网口从主控 CPU 的视角看就是两张普通网卡。数据到达 PHY 之后,经过 MAC 进到内存,由网卡驱动交给内核协议栈,内核根据 IP 地址、路由表、防火墙规则决定这个包是转发出去还是交给本地进程。
这个模式最大的特点是“CPU 站在每一跳的中间”。两个口之间的通信,哪怕是同一网段的两台设备,数据也得先从 A 口进内存,内核查一遍路由,再从 B 口发出去。好处是灵活:你可以对流量做任意处理,NAT、防火墙、流量整形、报文过滤都没问题。代价是两个端口之间的吞吐上限就是 CPU 的处理能力,如果主控是个低主频的 MCU,跑满百兆口都费劲,更别说千兆。
很多双网口模块的“独立端口模式”在硬件上并不见得有两个独立 MAC,而是交换芯片内部做了端口隔离。这部分我在后面硬件拓扑里细说,先记住结论:逻辑上的独立网卡,物理上可能共用一颗交换芯片。
2.2 交换机模式:硬件转发才是灵魂
交换机模式就完全不同了。两个外部端口和连接主控的 CPU 端口之间,由交换芯片内置的 MAC 地址表驱动转发。A 口收到一个数据帧,芯片查表发现目的 MAC 在 B 口,二话不说直接从 B 口发出去,整个过程 CPU 连包都见不着。
这里有三个关键机制需要理解:
- 地址学习:芯片自动记录每个源 MAC 地址是从哪个端口学到的,形成 MAC 地址表。
- 地址老化:MAC 表条目有生存时间,典型值 300 秒,设备移动端口之后旧条目自动失效,不会一直错转。
- 洪泛与广播:目的 MAC 查不到或者目的地址是广播地址时,芯片会把帧发向除接收端口外的所有端口。
交换机模式的价值不只是“能通”,而是“通得快”。百兆交换芯片的转发能力通常在线速,每个端口都能跑满 100Mbps,两端口之间同时双向收发也不会丢包。对主控 CPU 来说,这种模式几乎零负担,哪怕主控在休眠,两个外设之间照样通信。
2.3 三种硬件拓扑的横向对比
做技术选型之前,得先看清“双网口模块”这个说法下面藏着好几种实现路径,我整理了一个对比表:
| 实现方案 | 典型芯片/平台 | 独立端口能力 | 交换机模式 | 主控占用 | 适用场景 |
|---|---|---|---|---|---|
| SoC 双 MAC + 双 PHY | RK3568 双 GMAC、i.MX8M Plus | 物理真独立 | 天然不支持,需软件桥接 | 高,转发全靠 CPU | 路由器、防火墙、网闸 |
| 集成双 PHY 的交换芯片 | LAN9303、LAN9352 | 支持,靠 VLAN / 端口隔离 | 原生支持 | 低,硬件转发 | 双网口模块、工业串接设备 |
| CPU 口 + 外置交换芯片 | KSZ9896、RTL8367 | 支持 | 原生支持 | 低 | 多口交换机、管理型交换机 |
| USB/PCIe 桥接芯片 | AX88179、RTL8153 组双口 | 物理独立 | 不支持,软件桥接 | 高 | 树莓派扩展双网口 |
标题里写 “Operates as Independent Ports or Switch” 的模块,绝大多数是第二种方案,也就是以一颗集成两个 PHY 的交换芯片为核心,通过寄存器配置切换性格。第三种方案多见于对端口数要求更多的产品,原理一脉相承。选型最怕的是拿方案的工程师对着第四种方案和客户大谈独立模式,结果客户追问“那能当交换机用吗”,当场卡壳。
3. 硬件设计要点:双 PHY 布局、管理通道与复位时序
3.1 芯片选型:10/100 与千兆的分水岭
模块产品选交换芯片,第一个分水岭是速率。10/100M 的典型选择是 Microchip LAN9303、LAN9352 这类三端口芯片,内部集成两个 PHY,CPU 通过 MII 或 RMII 接口连接内部第三个端口。千兆级别就得换到 KSZ9896、KSZ9897、RTL8367 这一档,外部端口数量更多,通常 PHY 也外置,PCB 面积和成本都会上去。
选型时要盯几个硬指标:
- 内部端口数:CPU 口 + 外部口基本决定了产品形态。
- 主机接口类型:百兆芯片常见 MII/RMII,千兆芯片常见 RGMII。RMII 信号少、布线容易,但需要 50MHz 参考时钟;RGMII 信号多,布线麻烦但是吞吐高。
- 管理接口:I2C、SPI、MDIO 都可以对芯片做配置,有些芯片只在启动时读取一次配置,有些支持运行时动态改寄存器。
如果你的产品有可能从独立端口模式切换到交换机模式,必须选支持动态改端口转发表项的芯片,别选那种只能用外部引脚 strapping 固定模式的。前者是软件能切换,后者一焊上去就定死了,完全违背标题里的 “or” 字。
3.2 一颗芯片怎么管理两个口:PHY 地址与 MDIO 总线
LAN9303 这种双 PHY 芯片最让人容易忽略的是 PHY 地址分配。芯片内部两个 PHY 通常通过 strap 引脚固定到不同的地址,比如 PHY1 在 0x01、PHY2 在 0x02,主机通过 MDIO 总线访问时,一个地址对应一个 PHY,互不干扰。
设计 PCB 时一定要注意 MDIO 的时序和上拉。MDC 时钟一般跑 2.5MHz,MDIO 是双向数据线,必须接对方向的上拉电阻,阻值常见 1.5kΩ 到 10kΩ。我遇到过一块板子两个网口都能 link up,但读 PHY 寄存器全是 0xFFFF,查了半天发现 MDIO 上拉电阻没贴,总线电平不稳,芯片 ID 都读不出来,自然没法进入正常驱动流程。
如果 SoC 只有一个 MAC,却要通过独立 PHY 芯片扩展两个网口,那就得保证两颗 PHY 的外部地址不冲突,常见做法是把其中一颗的 RXD 引脚做地址 strap,硬件上拉或下拉成不同值。两颗 PHY 用同一个地址的后果是驱动 probe 时只能认出一颗,另一颗直接消失,而且日志里没有任何报错,特别隐蔽。
3.3 复位时序:两个 PHY 的“起床时间”必须同步
PHY 芯片上电之后需要一段内部初始化时间,典型值是 10ms 到 50ms 不等,具体看数据手册。模块设计上通常把两颗 PHY 的复位引脚接在一起,由主控 GPIO 统一控制。上电时先拉低复位至少 10ms,再释放,然后等待至少 100ms 再让驱动去访问 MDIO 总线。
这个 100ms 的等待非常关键。PHY 内部 PLL 没锁定时,MDIO 读写寄存器会不稳定,Probe 时的 PHY ID 读错,驱动就会认为芯片型号不对,拒绝注册。我之前调一块板子时,问题表现是“十个模块里有两三个网口不通”,定位到根因就是复位释放后立刻读寄存器,PHY 还没准备好。后来把复位后的延时从 10ms 加大到 150ms,故障率直接清零。
此外还要注意复位释放时的电平毛刺。如果复位引脚没有加 RC 滤波或者由电源监控芯片控制,电源上电瞬间会产生抖动,PHY 可能复位到不确定状态。工业级产品建议直接用带延时功能的电源监控芯片控制复位而不是 GPIO 硬拉。
4. 软件侧落地:从驱动注册到业务配置
4.1 内核驱动框架:交换芯片驱动的接入方式
Linux 内核处理这类嵌入式交换机有一套成熟框架叫 DSA(Distributed Switch Architecture)。DSA 的精髓是把交换芯片的每个外部端口映射成一个标准 net_device,比如 eth0、eth1,同时保留一个面向主控的 CPU 端口。对用户态和协议栈来说,这些端口看起来就像普通网卡,但实际数据通路是经过交换芯片转发的。
LAN9303 这类芯片在内核已经有现成驱动,设备树里声明好之后,系统起来一般能看到 eth0 和 eth1 两个接口。下面是设备树片段的简化写法,实际项目里要根据总线地址和中断号调整:
&mdio0 { eth_switch: lan9303@5 { compatible = "microchip,lan9303"; reg = <5>; reset-gpios = <&gpio4 15 GPIO_ACTIVE_LOW>; interrupt-parent = <&gpio4>; interrupts = <16 IRQ_TYPE_LEVEL_LOW>; }; };驱动会通过 MDIO 总线读取芯片 ID,确认是 LAN9303 之后注册 DSA 端口。一个常见问题是设备树里忘了写 reset-gpios,或者 GPIO 号和实际接线不对,表现就是系统起来了但一个网口都看不到,内核日志里只有一行 “mdio_bus: probe of mdio-bus failed”,排查时特别容易误判为芯片没焊好。
如果芯片驱动没有被内核编译进去,常见报错就是找不到驱动模组,这类问题通常是内核 config 没开 CONFIG_NET_DSA_MICROCHIP_KSZ 或对应选项,编译内核时记得确认。
4.2 独立端口模式的系统配置:双网卡与策略路由
独立端口模式下,系统起来后 eth0 和 eth1 是两个独立接口,直接按普通网卡配置 IP 即可。如果两个口分别接两个运营商线路需要同时上外网,单靠默认路由不够,需要策略路由。
假设 eth0 对应 192.168.1.0/24,网关是 192.168.1.1,eth1 对应 192.168.2.0/24,网关是 192.168.2.1,两个口都要能访问外网,可以建两个路由表:
echo "100 wan1" >> /etc/iproute2/rt_tables echo "200 wan2" >> /etc/iproute2/rt_tables ip route add default via 192.168.1.1 dev eth0 table wan1 ip rule add from 192.168.1.0/24 table wan1 ip route add default via 192.168.2.1 dev eth1 table wan2 ip rule add from 192.168.2.0/24 table wan2这套配置对双网口模块的意义在于:模块的独立端口模式不只是“两个口能同时 up”,还要保证流量按照源 IP 从对应口出去,否则会出现从 eth0 进来的请求,回包从 eth1 走了,对端永远收不到响应,表现为“ping 得通但网页打不开”。
4.3 交换机模式的系统配置:bridge 与端口隔离
交换机模式有两种落地路径。如果模块在出厂时就固定为交换机模式,最简单的方式是把两个 DSA 端口都加进 Linux bridge:
ip link add name br0 type bridge ip link set eth0 master br0 ip link set eth1 master br0 ip link set br0 up用 bridge 之后,主机本身也成了一个三层管理口,可以给 br0 配一个 IP 做远程管理。数据流量在两个口之间走的是交换芯片硬件转发,bridge 只是提供了管理控制面。
如果希望两个口“物理通、逻辑断”,即插在 A 口的设备不能访问 B 口的设备,就需要做端口隔离。先用 port-based VLAN 把两个口切成两个独立广播域,再把主机口配成 trunk 口进行管理:
| 端口 | VLAN 成员 | 是否打标签 | 用途 |
|---|---|---|---|
| eth0 | VLAN 10 | PVID,untagged | 业务 A |
| eth1 | VLAN 20 | PVID,untagged | 业务 B |
| CPU 口 | VLAN 10/20 | tagged | 网管 |
这种配置在标题里对应的正是 “Independent Ports” 的深层含义:两个物理口都在工作,数据也走硬件转发,但从二层广播域看它们互不可见。很多网口隔离设备、多 WAN 安全网关用的就是这套逻辑。
4.4 应用层状态:别让 systemd 把网口顺序搞乱
独立端口模式还有一个软件坑:Linux 的网口命名顺序由内核探测顺序决定,如果两个 PHY 驱动 probe 的次序不稳定,可能出现上次启动 eth0 是 A 口、这次启动 eth0 变成 B 口。解决办法是用 udev 规则基于 MAC 地址固定网口名:
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="xx:xx:xx:xx:xx:01", NAME="wan0" SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="xx:xx:xx:xx:xx:02", NAME="lan0"模块出厂时每个网口的 MAC 地址要提前规划。两颗 PHY 可以共用同一个 MAC 池里的连续地址,也可以分别烧录独立 MAC。注意如果两颗 PHY 的 MAC 地址相同,交换机模式下会出幺蛾子,后面排查部分详细说。
5. 实战调试:四个典型故障的完整排查链路
5.1 第二个网口不出现:先看 PHY 地址,再看复位时序
曾在调试中遇到过一块双网口模块,内核起来之后只认出一个口,另外一个口连 link 状态都不对。我的排查链路是这样的:
第一步,看内核日志。登录系统执行 dmesg | grep mdio,确认两个 PHY 的 ID 是否都被识别到。如果只识别到一个 ID,优先怀疑两个 PHY 地址冲突。用 busybox devmem 之类工具直接读取 MDIO 地址 0-31 的寄存器,理论上两个 PHY 应该落在两个不同的地址上。
第二步,查硬件 strap。双 PHY 芯片的地址一般由引脚电平决定,拿万用表量一下 strap 引脚的电压,确认确实是一高一低。
第三步,查复位。很多模块设计上把两颗 PHY 用同一个 GPIO 复位,但要确认 PCB 上复位引脚有没有连到 GPIO,而不是被电阻直接拉高,导致芯片永远处于复位状态。
最后一步,查内核 config。确认 DSA 驱动和 PHY 驱动都编进去了,而且没有因为内核版本差异导致驱动匹配失败。
这个排查过程最容易被忽视的是第一步,很多人上来就怀疑 PCB 焊接,其实 PHY 地址冲突和复位时序出问题的概率远高于虚焊。
5.2 link up 但 ping 不通:MAC 地址与 VLAN 的锅
另一个高频故障是两个口都能正常 link up,但两端设备就是 ping 不通。如果确认 IP 地址没配错,重点排查两个方向。
第一是 MAC 地址冲突。如果两颗 PHY 出厂烧录了相同的 MAC,交换芯片的地址学习表会来回抖动,A 口学到 MAC X 在端口 1,B 口又学到 MAC X 在端口 2,数据帧就会在两个端口之间乒乓转发,最终丢包。排查办法是用 tcpdump 在主机侧抓包,发现请求帧能进来但响应帧源 MAC 地址不对,八成就是地址冲突。解决方法是给两个端口分别设置唯一的 MAC,用 ip link set eth0 address 命令可以临时验证。
第二是 VLAN 配置不一致。如果模块在独立端口模式下做过端口隔离测试,寄存器里还残留着 VLAN 配置,重新上电后默认配置没有恢复,就可能出现“A 口的设备只能和主机通信,不能和 B 口设备通信”。解决方法是恢复出厂默认配置,或者把交换机芯片的配置动作封装成独立脚本,系统初始化时强制执行一遍。
5.3 交换机模式的广播风暴:环路把芯片打懵
测试时还有一次印象很深:两块双网口模块用网线把 A 模块的 lan 口和 B 模块的 lan 口对接,然后把两个模块的 wan 口都接在同一台交换机上,瞬间整个局域网开始疯狂广播,CPU 占用直接飙到 100%。
这是典型的二层环路。两个模块之间形成了一条物理环路,广播帧在环里无限循环,MAC 地址表被反复刷新,最终打满所有带宽。排查时先断掉其中一条链路,广播立刻消失,就能确认环路。
解决方式是让交换机模式支持 STP/RSTP。工业级模块建议在驱动层就启用 STP 而不是指望用户手动断开网线。如果是模块在半成品阶段做测试,先把环路物理拆除,等软件方案成熟后再验证。
5.4 独立端口模式吞吐上不去:中断与缓冲区的博弈
有次客户反馈双网口模块在独立模式下千兆带宽只能跑到 400Mbps,排除硬件链路问题之后,我做了两件事:
一是看中断分布。两个网口共用同一个中断号时,高吞吐流量会让单核 CPU 陷入中断风暴,处理不过来的包只能丢弃。做法是把两个网口的中断分开,绑定到不同 CPU 核,充分发挥多核能力。
二是调大 ring buffer。默认的 RX ring 深度可能只有 256,突发流量一来就溢出丢包。用 ethtool -G eth0 rx 1024 调大缓冲,再用 ethtool -L eth0 combined 2 开多队列,吞吐提升非常明显。针对转发类业务,还可以检查驱动是否开启 NAPI 和 busy poll,这些对低延迟场景都有帮助。
6. 选型建议与产品形态扩展
6.1 怎么选:先问清楚“独立”是哪一种独立
如果带着需求去选型,我的建议是先分清你要的“独立端口模式”是哪种。如果你需要两个口在物理上完全独立、各自有独立 MAC 和独立中断、操作系统层面就是两张独立网卡,那就选 SoC 双 MAC 方案,别选交换芯片方案。反过来,如果所谓独立只是逻辑上的隔离,同时希望保留交换机模式的在线转发能力,那选 LAN9303 这类双 PHY 交换芯片最合适。
从标题里的 Dual Ethernet Module Operates as Independent Ports or Switch 来看,这种模块的定位是“一套硬件覆盖两个模式”,本质上是让产品团队少做一个 SKU。选型时注意芯片是否支持运行态切换,如果只支持上电时 strap 选择模式,那意味着产品机型还得分两个版本,模块的灵活性就打了折扣。
6.2 模块形态的扩展思考
这类模块做成熟之后,产品形态可以顺着两条线延伸。一条线是做四口、八口版本,核心还是端口隔离加硬件转发,只是交换芯片从三端口换到多端口,主机接口从 MII 升级到 RGMII 或 PCIe,软件框架从 DSA 扩展成更大规模的交换架构,管理功能也可以加上 port mirror、QoS、链路聚合。另一条线是做混合模式,两个口默认独立,但在系统里软件调度,断电或者系统崩溃时自动切换成硬件直通。这个特性对工业现场特别有用,设备死机了链路还能保持畅通,不至于把整个网络段都带崩。
我实际测试下来,最稳妥的做法是在出厂固件里同时保留两套配置文件,一套是独立端口的 sysctl 和 udev 规则,一套是交换机模式的 bridge 初始化脚本,用一个标志位控制启动时执行哪套。这样模块交付给客户之后,改一个配置文件就能切换模式,不需要重新烧固件,现场维护的同事会感谢你的。
6.3 调试经验最后补一句
双网口模块的调试,硬件问题大多出在复位时序和 PHY 地址,软件问题大多出在 MAC 地址唯一性和 VLAN 配置残留。这两类问题有一个共同特点:日志都不明显,系统不会报错,只会表现为“偶尔不通”或者“时通时断”。遇到这种情况,不要急于改软件,先把硬件基础项一项项确认完再动代码,能省下大量排查时间。另外,模块的 MAC 地址池在量产时就要做好规划,宁可浪费几个地址也不要让两个网口重复,否则出货之后用户报修,你在办公室排查链路都会被折腾得够呛。