定制板多网口怎么做?我从 Switch、NIC、PHY 到 Linux 的一次实战梳理 B站 嵌入式孙老师博主个人介绍博主书籍-京东购买链接Yocto项目实战教程加博主微信进技术交流群jerrydev最近在梳理一套多网口 Ethernet 设计时发现真正难的并不是“多接几个 RJ45”而是很容易把NIC、MAC、PHY、Switch、MDI、SGMII以及 Linux 里的 ethX混在一起。刚开始看原理图时我也有过几个很自然的疑问一颗 Switch 扩出 4 个网口Linux 会不会多出 4 个ethXSwitch 会不会给下面的设备分配 IP网卡已经能在ifconfig里看到为什么还是Link detected: noPHY 的 MDI 到底是数字信号还是物理信号两颗 PHY 都有 MDI是不是直接连起来就行硬件 Link 通了之后DHCP 又应该由谁负责把这些问题逐个拆开以后我对多网口设计的理解反而变得比较简单了Linux / Application │ TCP/IP │ NIC / MAC │ Uplink │ Switch ┌────┼────┐ │ │ │ PHY PHY PHY │ │ │ MDI MDI MDI │ │ │ Magnetics │ RJ45这篇就沿着这条链把我这次学习和排查过程中最容易混淆的地方整理一下。1. 多网口设计先把硬件角色分清楚NICCPU 接入 Ethernet 的入口NIC 全称Network Interface Controller工程里直接理解成网卡就可以。常见有两种SoC → PCIe → Ethernet NIC或者SoC → USB3 → Ethernet NIC这类设备被 Linux 驱动以后通常会变成eth0 eth1 eth2很多 PCIe / USB Ethernet NIC 内部其实已经包含NIC ┌───────────────┐ │ PCIe / USB │ │ ↓ │ │ MAC │ │ ↓ │ │ PHY │ └───────────────┘ ↓ MDI所以NIC 和 PHY 不是一回事。NIC 是一套完整的网络接口设备PHY 只是其中负责 Physical Layer 的一部分。MAC处理 Ethernet FrameMAC 还是数字世界里的东西。Application ↓ TCP / UDP ↓ IP ↓ Ethernet Frame ↓ MACMAC 主要处理MAC Address Ethernet Frame CRC TX / RX Queue Flow Control它知道怎么收发 Ethernet Frame但还没有真正开始驱动网线。PHY从数字 Ethernet 进入真正的物理层PHY 才是真正的 Physical Layer TransceiverMAC │ │ Digital Ethernet ▼ PHY │ │ Physical Signal ▼ 传输介质PHY 内部会完成Auto-Negotiation 编码 / 解码 调制 / 解调 均衡 时钟恢复 回波消除 模拟前端所以后来我觉得把 PHY 简单理解成“电平转换芯片”是不准确的。它实际上实现了一整套 Ethernet Physical Layer。MDIPHY 最靠近网线的一侧这是我这次比较容易混淆的一个概念。MDIMedium Dependent Interface1G / 2.5GBASE-T 常见四对MDI_A_P / MDI_A_N MDI_B_P / MDI_B_N MDI_C_P / MDI_C_N MDI_D_P / MDI_D_N这里已经不是普通的 0/1 数字接口而是Copper Ethernet PHY 的模拟差分物理信号。所以标准铜口结构应该这样看MAC ↓ PHY ↓ MDI ↓ Magnetics ↓ RJ45 ↓ Twisted Pair CableMagnetics也就是 Ethernet 变压器主要处理隔离、共模、EMC 等问题。它并不是负责“数字变模拟”的——这个工作在 PHY 里面已经完成了。我后来再看到MDIP/MDIN、MDI_A/B/C/D这类引脚时就会直接想到这里已经进入 Copper Physical Layer 了。Switch不是网卡而是多 Port 二层交换这是另一个一开始很容易混的地方。NIC 做的是CPU → NetworkSwitch 做的是CPU/Uplink │ ┌─ Switch ─┐ │ │ │ Port0 Port1 Port2 ...Switch 会学习MAC_A → Port0 MAC_B → Port2 MAC_C → Uplink以后收到 Ethernet Frame就根据目的 MAC 地址决定从哪个 Port 发出去。所以我现在最简单的区分就是NIC 负责 CPU 怎么进入 EthernetSwitch 负责多个 Ethernet Port 之间怎么交换 Frame。这也是为什么一颗多口 Switch 本身不能简单当成“多口网卡”。4 个 RJ45不代表 Linux 一定有 4 个 ethX这个问题我一开始也专门确认过。假设Linux │ eth1 │ Switch ├─ Port0 → RJ45 ├─ Port1 → RJ45 ├─ Port2 → RJ45 └─ Port3 → RJ45Linux 完全可能只看到eth1下面 4 个接口只是 Switch 内部的 Port。因为eth1 CPU 自己的网络接口 Port0~3 Switch 的交换端口两者不是一个概念。如果使用 Linux DSADistributed Switch Architecture情况会不同Switch 的 User Port 可以表现成swp0 swp1 swp2 swp3但那需要具体 Switch 有对应的 Kernel Driver 和软件架构支持。所以更准确地说多个 RJ45 ≠ 多个 Linux NIC这条结论对我理解 Switch 很有帮助。相关学习记录里也正是通过“Switch 自主工作”和 Linux Uplink 两个层面把这件事拆开的。RGMII、SGMII 和 MDI 不要混在一起在 Ethernet 原理图里它们经常同时出现。但其实非常好区分。RGMIIMAC │ RGMII │ PHY / Switch属于并行数字接口。SGMIIMAC / Switch │ SGMII │ PHY属于高速串行数字接口。MDIPHY │ MDI │ Magnetics │ Cable属于铜口物理层接口。所以我最后直接记成接口怎么理解RGMII芯片间并行数字 Ethernet 接口SGMII芯片间串行数字 Ethernet 接口MDICopper PHY 面向介质的物理接口另外SerDes 也不等于 SGMII。SerDes 是 Serializer / Deserializer是高速串行收发能力SGMII、2500BASE-X、USXGMII 等才是具体的 Ethernet 接口形式。Switch 的 Port 也分类型看到6-Port Switch不能马上理解成6 × RJ45真正应该看的是几个 Port 自带 Copper PHY 几个是 SerDes Port 哪个是 CPU/Uplink Port 支持什么速率如果 Switch Port 自带 PHYSwitch │ Integrated PHY │ MDI │ Magnetics │ RJ45这类最适合直接做普通铜口。如果 Port 只是 SerDesSwitch │ SGMII / 2500BASE-X │ External PHY │ MDI │ Magnetics │ RJ45就还需要一颗外部 PHY。我这次也是在分析一条Switch SerDes → External PHY → MDI的链路后才真正把这两个 Port 类型区分清楚。板内如果能走 SerDes就尽量不要反复进出 Copper PHY从架构上看我现在更喜欢SoC MAC │ SGMII / USXGMII │ Switch真正到了板外才Switch ↓ PHY ↓ MDI ↓ Magnetics ↓ RJ45而如果出现NIC ↓ Copper PHY ↓ MDI ↓ Copper PHY ↓ SerDes ↓ Switch链路就会复杂很多。这里我实际碰到过一个很有价值的问题两颗 PHY 都有 MDI能不能直接把两组 MDI 接在一起答案不能简单理解成“接口名字一样就能接”。MDI 是模拟 Physical Layer 接口标准铜口是按照 PHY 厂商的 Reference Design、Magnetics 和 Cable Channel 去设计的。如果要做 PHY-to-PHY 的 transformerless / back-to-back 连接必须确认两颗 PHY 是否明确支持以及偏置、阻抗、耦合等要求。不能把它当成TXP/N 直接接 RXP/N这种普通数字 SerDes 来处理。这也是我这次排查中最有价值的一个硬件认识**MDI 和 SGMII 虽然都是差分线但根本不是一类信号。**相关实测笔记里PHY 的 SerDes 一侧和 MDI 一侧也表现出了完全不同的连接方式。Switch 本身也有启动过程另一个之前容易忽略的地方是Switch 并不是“上电就天然开始交换数据”。一颗复杂一点的 Switch 通常还有Power ↓ Clock ↓ Reset ↓ Strap Sampling ↓ SPI Flash / EEPROM ↓ Firmware / Configuration ↓ PHY / SerDes Init ↓ Port Forwarding所以看 Switch 电路时除了数据线我现在还会先找Power Clock Reset Strap SPI Flash / EEPROM MDIO / SPI / I2CStrap 可能决定Boot Mode PHY Enable Port Mode Clock Mode Management Interface因此一个 Switch 没工作不一定和 Linux 有关系。有时候 Linux 根本就没有参与它的启动。实际学习记录里也能看到典型的 Strap、SPI Flash、自启动和 PHY Port 组合。2. 软件和调试我后来不再一上来就 pingethX出现只能证明网卡这一层起来了比如iplink能够看到eth1如果是 USB NIClsusb也正常。如果是 PCIe NIClspci也正常。这些只能证明SoC ↓ PCIe / USB ↓ NIC ↓ Driver ↓ ethX这部分工作了。它并不能证明PHY ↓ Cable ↓ Switch已经建立 Link。这个区别是我这次排查过程中很重要的一步。UP和Link detected: yes不是一回事例如iplinkshow eth1看到NO-CARRIER,BROADCAST,MULTICAST,UP这里的UP只表示软件把这个接口打开了。真正判断 Physical Link我更关注ethtooleth1里面Link detected: yes以及cat/sys/class/net/eth1/carrier正常应该1再看LOWER_UP RUNNING这些状态。我实际遇到过UP 但是 NO-CARRIER Link detected: no carrier 0这时候 IP 配置得再漂亮也没有意义。因为问题还停留在 Physical Layer。这个区分在实际日志分析里非常明显。Link 正常以后第二步应该看 Frame而不是马上 pingPhysical Link 成立以后ip-slinkshow eth1看RX packets TX packets有没有增长。然后tcpdump-ieth1-e-n看有没有ARP DHCP IPv6 Broadcast Multicast这一步很重要因为Ethernet Frame 能不能收到和 IP 是否在同一个网段是两件事情。即使 IP 没配对只要对端有广播、ARP 或 DHCPRX packets照样可以增长。因此我现在排网口基本按有没有 Carrier ↓ 有没有 Ethernet Frame ↓ 有没有 IP来走。169.254.x.x不是 Switch 分配的 IP这也是我学习过程中一个比较典型的误区。看到一个下游设备拿到了169.254.x.x第一反应很容易是是不是 Switch 给它分配了地址其实一般不是。169.254.0.0/16是 IPv4 Link-Local。很多系统在DHCP 请求 ↓ 没有服务器回应 ↓ 自己选择一个 169.254.x.x作为兜底地址。所以出现169.254.x.x反而经常是在告诉我们DHCP 没成功。对于普通二层 Switch 来说它负责的是 Ethernet Frame 转发而 DHCP Server 通常运行在主机、路由器或专门的网络服务上。DHCP Server 应该放在哪里假设Linux 主机 │ eth1 │ Switch ├─ Device A ├─ Device B └─ Device C如果希望下面的设备自动获得192.168.100.x可以在 Linux 主机上跑dnsmasq过程其实很简单Device │ │ DHCPDISCOVER ▼ Switch │ ▼ eth1 │ ▼ dnsmasq │ │ DHCPOFFER / DHCPACK ▼ Device 得到 IPSwitch 在这里依然只是转发二层广播。它并不负责“给谁分什么 IP”一个 dnsmasq 可以服务多个接口但网段要分开我这次还碰到了一个纯软件问题一台设备上可能同时有Wi-Fi AP Ethernet Port A Ethernet Port B都需要 DHCP。没有必要启动三个 dnsmasq一个实例就可以bind-interfaces interfacewlan-ap dhcp-rangeset:wifi,192.168.10.10,192.168.10.100,12h dhcp-optiontag:wifi,3,192.168.10.1 dhcp-optiontag:wifi,6,192.168.10.1 interfaceeth1 dhcp-rangeset:lan1,192.168.20.10,192.168.20.100,12h dhcp-optiontag:lan1,3,192.168.20.1 dhcp-optiontag:lan1,6,192.168.20.1这里后来我才注意到一个细节dhcp-option3 dhcp-option6如果不加 tag可能会错误地应用到其他接口。所以多网段时最好配成set:xxx tag:xxx让 Gateway 和 DNS 跟对应地址池绑定。这部分也是我在实际增加多个 DHCP 网段时踩到的一个软件配置点。systemd 依赖也可能让“DHCP 配置正确但服务就是起不来”还有一个问题跟 Ethernet 本身几乎没有关系却很容易误判成网络问题。例如dnsmasq.service被 systemd 强绑定到某一个网卡BindsTosys-subsystem-net-devices-xxx.device当这个接口不存在时Dependency failed整个 dnsmasq 都起不来。如果 dnsmasq 同时服务多个独立接口这种强依赖反而可能不合理接口 A 不存在 ↓ dnsmasq 整体启动失败 ↓ 接口 B 的 DHCP 也一起没了后来我更倾向把数据接口是否存在和DHCP Service 是否允许启动分开考虑。这种问题如果只盯着dnsmasq.conf本身很容易找错方向。最后形成了一套比较固定的 Ethernet 排查顺序现在再遇到多网口不通我基本不会从ping开始。我会按1. Power ↓ 2. Clock / Reset / Strap ↓ 3. NIC 是否枚举 ↓ 4. Switch 是否 Boot ↓ 5. PHY / SerDes Link ↓ 6. Carrier ↓ 7. Ethernet Frame ↓ 8. DHCP / ARP ↓ 9. IP / Route ↓ 10. Application具体 Linux 命令也很固定# 网卡有没有iplink# 物理 Linkethtooleth1cat/sys/class/net/eth1/carrier# 二层有没有包ip-slinkshow eth1 tcpdump-ieth1-e-n# DHCPjournalctl-udnsmasq-f# 最后才是ipaddriprouteping这样最大的好处是能够快速判断问题到底属于硬件 PHY Switch Driver DHCP 还是 IP而不是统称为“网口不通”。3. 这次学习下来我觉得最值得记住的几件事第一次接触多网口 Switch 设计时很容易从 RJ45 数量出发有 4 个网口 → 应该有 4 个网卡 → 应该有 4 个 IP后来发现这个思路本身就不对。更合理的理解应该是CPU │ NIC / MAC │ Uplink │ Switch ├─ Port ├─ Port ├─ Port └─ PortSwitch Port、Linux NIC 和 IP Address 本来就是三个不同层面的东西。另外一个比较深的体会是 Ethernet 原理图不能只看“差分线有没有接上”。下面这些RGMII SGMII USXGMII MDI虽然都在传 Ethernet但处在完全不同的位置。特别是SGMII → 芯片间数字接口 MDI → Copper PHY 模拟物理接口如果这一层没分清后面很容易把两种完全不同的差分信号当成同一种东西。最后则是软件。多网口不是把硬件 Link 做出来就结束了还要继续考虑Switch 谁初始化 PHY 谁管理 Linux 能看到哪些接口 DHCP Server 放在哪里 多个网段怎么隔离 服务启动顺序是否合理所以现在如果让我再画一次多网口架构我不会先画 RJ45而是先画Linux / Application │ TCP/IP │ NIC / MAC │ Uplink 带宽 │ Switch ┌─────────┼─────────┐ │ │ │ PHY PHY PHY │ │ │ MDI MDI MDI │ │ │ Magnetics Magnetics Magnetics │ │ │ RJ45 RJ45 RJ45然后逐层问SoC 怎么接入 Switch Uplink 带宽够不够 Switch Port 是内置 PHY 还是 SerDes MDI 后面的电气设计是否正确 Switch 是自主启动 还是由 BSP / Linux 配置 Linux 应该看到 ethX 还是 DSA 的 swpX DHCP 又由谁负责这些问题回答清楚之后多网口设计就不会再是一堆 NIC、PHY 和 Switch 芯片堆在一起。对我来说这次最大的收获也不是记住了某颗芯片而是终于能把“网络数据从 Linux 一直走到网线中间到底经过了什么”顺下来。后面无论换 1G、2.5G、5G 还是 10G器件和接口会变但这套分析方法基本不会变。