ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ubuntu双网卡同网段导致网关不可达的排查与策略路由解决方案

Ubuntu双网卡同网段导致网关不可达的排查与策略路由解决方案 如果你在一台 Ubuntu 服务器上插了两块物理网卡又图省事把这两个口都配成了同一个网段的 IP那恭喜你你已经成功给自己埋了一颗雷。这颗雷的引爆时间不确定——可能开机半小时后爆也可能在你随手执行一条systemctl restart networking后爆症状都差不多网关 ping 不通外网时断时续SSH 掉线重连困难整个机器像被人下了咒。这不是硬件故障也不是运营商抽风纯粹是 Linux 多网卡 同网段地址之间的经典冲突。这篇文章我想完整还原一次“Ubuntu 双网卡同网段导致网关不可达”的排查过程。从现象入手把路由表、ARP、源地址选择这些内核行为一条条拆开讲清楚然后给出几种从“应急恢复”到“策略路由持久化”的解决方案。无论你是刚接触 Ubuntu 的运维新手还是已经被这个问题折磨到想重装系统的老哥这篇都可以直接当排查手册用。1. 故障现场还原同网段双网卡下的离奇现象先描述一下我遇到的现场你对比一下自己是不是也这样。1.1 业务部署时的“双网卡同一网段”到底是怎么回事有一台 Ubuntu 22.04 服务器板载两个千兆网口一个叫eth0一个叫eth1。部署人员一开始的规划是eth0接办公/业务交换机eth1接另一台设备或另一段汇聚结果实际接的时候发现两边都被分配到了192.168.2.0/24这个网段。于是配置变成了这样eth0192.168.2.10/24网关192.168.2.1eth1192.168.2.11/24同样也能“看到”网关192.168.2.1表面看没什么问题两个 IP 都在同一网段网关也是同一个。但真正的诡异表现出现在几分钟之后。1.2 最典型的现象网关时通时不通外网彻底瘫了我当时的操作步骤是这样的开机后能 ssh 登录ip addr看两块网卡 IP 都正常。ping 192.168.2.1能通有时连续通几十个包有时发两三个包就超时。执行apt update或者访问公司外网服务连接奇慢无比然后直接 timeout。等一会儿再 ping 网关发现完全 ping 不通了。但此时从交换机侧看服务器的两个网口 link 都是 up 的端口状态正常。这时候最迷惑人的是你如果手动执行ip route add default via 192.168.2.1 dev eth0看起来路由表恢复正常但过不了几十秒网络又断。你以为路由被谁改了反复查看ip route show发现默认路由还在但就是不通。1.3 一开始我绕过的好几个弯路老实说第一次遇到这个问题时我也没直接想到是“双网卡同网段”的锅先排了一堆无关因素换过交换机端口问题依旧排除物理链路故障。关掉 IPv6问题依旧排除radvd和邻居发现协议干扰。检查ufw状态放行 ICMP 后依然不通排除防火墙拦截。重装了一次netplan配置用netplan apply重新应用刚配完一分钟内是好的然后又坏。后来是盯着路由表发了一会儿呆发现192.168.2.0/24这条直连路由出现了两条一条对应eth0一条对应eth1才大概猜到问题出在“同网段多接口”上。提示这个故障最坑的地方在于它不是 100% 必现而是跟内核路由查找、ARP 缓存状态、报文从哪个物理口进出强相关所以特别容易被误判成“网线松动”或“交换机环路”。2. 内核视角拆解路由表、源地址与 ARP 回包到底怎么打架很多教程上来就丢给你几条命令让你照着敲但你不理解原理出了问题一样抓瞎。我尽量把这个冲突过程讲透。2.1 两张同网段网卡会让路由表出现什么变化Linux 路由查找的核心是fib_table也就是我们常说的路由表。正常情况下一张网卡一个网段路由表里一条192.168.2.0/24 dev eth0 scope link src 192.168.2.10就够了。但当你把eth1也配上192.168.2.11/24时内核会自动给路由表插入第二条直连路由192.168.2.0/24 dev eth0 proto kernel scope link src 192.168.2.10 192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.11这时候内核面对“目标地址在192.168.2.0/24内”的报文到底该从哪个口出如果两条路由的优先级相同它就可能走等价多路径ECMP逻辑在eth0和eth1之间做 hash 选择或者根据路由子项顺序选择其中一个作为“首选”。这里的问题就来了内核选择的出口不一定是对端设备实际连着的那个口。2.2 真正的直接凶手ARP 请求和回包的不对称以 ping 网关192.168.2.1为例。当你执行ping 192.168.2.1时目标地址和本机在同一网段所以内核不会走默认路由而是直接查 ARP 表、发 ARP 广播寻找网关的 MAC 地址。假如内核这次选择的出口是eth0但网关设备实际却接在eth1所连的交换机上或者两个口连的交换机互相隔离那么 ARP 请求广播从eth0出去后网关根本收不到自然也不会回应于是 ping 超时。反过来如果eth1和网关在同一广播域ARP 请求能到达网关但网关回 ARP 响应时Linux 的rp_filter反向路径过滤机制会发现这个来自192.168.2.1的报文本该走eth0出去怎么从eth1进来了于是直接把包丢掉。这就是很多场景下最常见的结果从某个网卡看是“只发不收”从另一块网卡看是“只收不发”。表象就是网关时通时不通或者干脆不通。2.3 为什么很多人靠“改 metric”解决不了问题有个很常见的尝试就是给一块网卡的默认路由设置较小的 metric以为这样 Linux 就会优先走这块网卡出去。这个思路对“两条不同默认路由”的场景基本有效但放在“同网段双网卡”里就是杯水车薪。原因很简单访问同一个网段里的网关时走的不是 default 路由而是直连路由connected route。Metric 只影响默认路由的优先级影响不到直连路由的多路径选择。你把default via 192.168.2.1 dev eth0 metric 100设得再低访问网关时根本不用这条路由仍然会陷入“直连路由到底从哪个口出”的泥潭。2.4 网络管理服务会加剧不确定性Ubuntu 服务器版默认用systemd-networkd或NetworkManager来管理网卡。如果你启用 DHCP那么在接口 up 之后客户端会发送 DHCP Discover 请求获取 IP 的同时也会拿到网关地址然后动态添加默认路由。当两块网卡同时启用 DHCP 并且处于同一个广播域时两个连接可能会各自拿到不同或相同的网关地址然后互相覆盖默认路由导致路由表一会指向eth0一会指向eth1。即使你手动改静态配置网络服务如果检测到连接状态变化或配置 reload也可能把路由重新洗一遍。所以在排查这种问题时除了看路由表本身还要确认是不是有 NetworkManager 的自动连接在背后捣乱。我见过一台机器上/etc/netplan/*.yaml写的是静态 IP但 NetworkManager 还保留着旧的 DHCP 连接配置重启用后者把路由表冲了。3. 一条一条命令筛下来完整的定位流程下面是完整的问题定位流程。我尽量不跳步骤每一条命令都说明你要看什么。3.1 先确认“同网段多 IP”这个前提登录服务器后不要急着改配置先把现状完整捞出来ip -4 addr show ip -4 route show table main ip rule show第一段命令输出两块网卡的 IPv4 地址。如果看到类似下面这样就说明已经踩雷了2: eth0: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 ... inet 192.168.2.10/24 brd 192.168.2.255 scope global eth0 3: eth1: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 ... inet 192.168.2.11/24 brd 192.168.2.255 scope global eth1然后看主路由表大概率会出现两条192.168.2.0/24直连路由或者一条 default 路由但不知道会从哪块网卡走。3.2 用“指定源地址”的方式分别测试两个网卡普通ping 192.168.2.1无法区分到底走了哪块网卡这时候一定要用-I参数指定网卡或者用ip route get看内核的选择结果ping -c 3 -I eth0 192.168.2.1 ping -c 3 -I eth1 192.168.2.1经验结果是往往有一块网卡是通的另一块完全不通。这个“只通一边”的现象基本可以锁定问题出在多接口 同网段的路由/ARP 行为上而不是单纯网线物理故障。也可以直接问内核“我要访问192.168.2.1它会选哪条路”ip route get 192.168.2.1 ip route get 192.168.2.1 from 192.168.2.10 ip route get 192.168.2.1 from 192.168.2.11ip route get会明确告诉你出口网卡和源地址。如果三条命令返回的出口不一致那问题基本就是它了。3.3 抓包确认 ARP 不对称如果你想让别人信服“这是 ARP 不对称”最好抓个包给他看。两块网卡分别抓tcpdump -n -e -i eth0 host 192.168.2.1 or arp tcpdump -n -e -i eth1 host 192.168.2.1 or arp在一个终端跑抓包另一个终端分别从eth0和eth1ping 网关。你通常会发现eth0上不断发出 ARP Request但没有对应 Response或者 ARP Response 从eth1进来但 ICMP 请求根本不在这个口上发。抓到这种“请求不走同一块网卡”的包问题原因就不需要再争辩了。3.4 检查 rp_filter 和防火墙的干扰接着检查反向路径过滤配置因为这是丢包的直接执行者sysctl net.ipv4.conf.all.rp_filter sysctl net.ipv4.conf.eth0.rp_filter sysctl net.ipv4.conf.eth1.rp_filter如果值是1表示严格模式2是松散模式0是关闭。多网卡同网段场景下严格模式几乎必然导致回包被丢弃。防火墙方面虽然它不是根因但也会干扰判断。建议排查期间先临时确认一下sudo ufw status sudo iptables -L -n -v | head -50如果发现 FORWARD 链或 INPUT 链有涉及时断时续的规则可以先备份规则后临时放行 ICMP 来做测试。3.5 快速定位命令汇总排查项用什么命令看到什么说明有问题多 IP 是否同网段ip -4 addr show两个接口地址都在 192.168.2.0/24直连路由是否重复ip -4 route show table main同前缀出现两个 dev 不同接口指定源地址访问网关ip route get 192.168.2.1 from IP出口网卡不一致ARP 是否不对称tcpdump -n -e -i eth0 arp请求和响应出现在不同接口回包是否被丢sysctl net.ipv4.conf.all.rp_filter值为 1 时高概率丢包路由表是否被动态修改ip rule show/ 查看 netplan NM出现多条动态 default这部分排查走完基本可以把“双网卡同网段”的锅钉死。4. 解决思路选型为什么先别碰策略路由从拓扑简化开始问题定位出来后很多人第一反应是“我要配置策略路由”但我不建议你上来就搞ip rule。先想清楚这块服务器到底需要几张网卡、怎么用。4.1 先反问需求你插两块网卡到底想干嘛根据我遇到过的场景大致分三类冗余高可用一块网卡断了另一块顶上业务不中断。这类需求应该用 bond而不是两张卡分别配 IP。业务隔离办公网和专线网分开各走各的接口。这种需求最合理的方式是把两个接口配成不同网段各配各的网关互不干扰。被迫同网段交换机端口、网线已经定死暂时没法改网段。这种才需要策略路由来“补救”。所以第一步永远是跟网络管理员确认拓扑。如果能把其中一个接口改成别的网段那这个故障就等于被“业务层面”解决了Linux 这边什么都不用折腾。4.2 不要害怕“先禁用一张网卡”的应急手段如果当前业务已经断了最优先做的是恢复可用性。你可以先禁用其中一张网卡让网络回到单网卡状态sudo ip link set eth1 down或者临时移除一张网卡上的 IPsudo ip addr del 192.168.2.11/24 dev eth1瞬间再 ping 网关你会发现稳如老狗。有些人觉得禁用网卡等于承认配置失败其实在生产环境里“先恢复业务再研究方案”才是最合理的选择。不要为了“把两个口都用起来”而强行增加系统复杂度。4.3 判断是误用了“同网段双 IP”还是需要 bond如果需求是做链路冗余同网段双 IP 是错误做法。正确的做法是 bonding。Ubuntu 22.04 服务器版在/etc/netplan/下可以用 bond 配置把两块物理网卡绑成一个逻辑口。以下是一个主备模式的示例network: version: 2 renderer: networkd ethernets: eth0: dhcp4: no eth1: dhcp4: no bonds: bond0: interfaces: [eth0, eth1] parameters: mode: active-backup primary: eth0 mii-monitor-interval: 100 addresses: - 192.168.2.10/24 routes: - to: default via: 192.168.2.1保存后执行sudo netplan generate sudo netplan apply用 bond 后mac 地址、IP、网关路径都归一了ARP 不会乱跑故障自然消失。不过要注意如果交换机不支持 LACP不要随便用802.3ad模式active-backup主备模式最稳。4.4 不同网段才是终局方案如果你不是做冗余而是两台设备之间直连或者接不同业务网那最好把两边拆成不重叠的网段。例如eth0192.168.2.10/24网关192.168.2.1eth110.88.88.10/24网关10.88.88.254这样 Linux 路由表里两条直连路由前缀不同访问不同目标时通过最长前缀匹配来选择出口天然不会冲突。多默认路由只要用metric区分优先级也不会互相覆盖。网络规划上有一条原则我一直很认同不让同一台服务器的两个三层接口落入同一个广播域。这能避免大量诡异的 ARP 和路由问题。5. 策略路由方案在 Ubuntu 上的完整落地如果拓扑确实没法改两个口都必须在192.168.2.0/24网段同时又要同时使用那只能上策略路由。5.1 配置前先理解 ip rule 的工作方式Linux 的ip rule允许根据源地址、目标地址、入接口等条件选择不同的路由表。相当于给报文处理加了一个“分类器”源地址是192.168.2.10的报文查表 A默认从 eth0 出去源地址是192.168.2.11的报文查表 B默认从 eth1 出去其他报文走默认的main表。这样两个接口虽然在同一个网段但因为从不同“门”出去ARP 请求发出的物理口就是可控的避免“请求从 eth0 发、响应却从 eth1 回”的尴尬。但这里有一个必须强调的前提这两个接口最好连接在不同的二层广播域中。如果 eth0 和 eth1 插在同一个交换机上且 VLAN 相同ARP 广播还是会同时到达两个口网关回包时选择哪个响应方向依然可能乱。真正干净的环境是 eth0 接交换机 Aeth1 接交换机 B两个交换机不互通二层但都配置了同网段的三层网关地址。5.2 实时命令行验证不修改任何配置文件先用命令临时验证是否可行。假设eth0192.168.2.10/24对端网关192.168.2.1eth1192.168.2.11/24对端网关192.168.2.254先给两个“路由表”起名字编辑/etc/iproute2/rt_tables追加100 eth0_table 200 eth1_table然后执行# eth0 对应的路由表 sudo ip route add default via 192.168.2.1 dev eth0 table eth0_table sudo ip route add 192.168.2.0/24 dev eth0 scope link src 192.168.2.10 table eth0_table # eth1 对应的路由表 sudo ip route add default via 192.168.2.254 dev eth1 table eth1_table sudo ip route add 192.168.2.0/24 dev eth1 scope link src 192.168.2.11 table eth1_table # 按源 IP 分流 sudo ip rule add from 192.168.2.10 lookup eth0_table priority 500 sudo ip rule add from 192.168.2.11 lookup eth1_table priority 501 # 刷新路由缓存 sudo ip route flush cache马上验证ip route get 192.168.2.1 from 192.168.2.10 ip route get 192.168.2.254 from 192.168.2.11 ping -c 3 -I eth0 192.168.2.1 ping -c 3 -I eth1 192.168.2.254如果两个源地址分别都能通到各自网关说明策略路由方案有效。注意这里eth1的网关地址和eth0不在同一个 IP 上。如果两个口连的网关就是同一个 IP等于两个口在同一广播域策略路由也救不了二层 ARP 的无序性请回到“禁用一张卡”或“改网段”方案。5.3 netplan 持久化配置Ubuntu 20.04 服务器版命令行验证没问题后要把配置固化下来。以/etc/netplan/01-netcfg.yaml为例network: version: 2 renderer: networkd ethernets: eth0: dhcp4: no addresses: - 192.168.2.10/24 routes: - to: 0.0.0.0/0 via: 192.168.2.1 table: 100 - to: 192.168.2.0/24 scope: link table: 100 routing-policy: - from: 192.168.2.10/32 table: 100 eth1: dhcp4: no addresses: - 192.168.2.11/24 routes: - to: 0.0.0.0/0 via: 192.168.2.254 table: 200 - to: 192.168.2.0/24 scope: link table: 200 routing-policy: - from: 192.168.2.11/32 table: 200然后sudo netplan generate sudo netplan apply注意几个细节routes里如果写了table默认不会自动生成直连路由所以必须显式加一条192.168.2.0/24 scope link的路由到对应表否则同网段访问会失败。routing-policy的from字段建议写成带掩码的形式比如192.168.2.10/32。netplan 对 YAML 缩进极其严格必须用空格不要用 Tab。netplan apply执行后如果报错大概率是缩进问题。5.4 桌面版 NetworkManager 的处理Ubuntu 桌面版默认使用 NetworkManager 管理网络netplan 的 renderer 是NetworkManager很多配置项不能直接套用上面的 YAML。这种情况下可以用nmcli给连接设置 route-table 和 routing rules。可以先看当前连接nmcli connection show然后修改对应连接配置例如nmcli connection modify Wired connection 1 ipv4.addresses 192.168.2.10/24 ipv4.gateway 192.168.2.1 ipv4.method manual nmcli connection modify Wired connection 1 ipv4.routes 192.168.2.0/24 table100 0.0.0.0/0 via192.168.2.1 table100 nmcli connection modify Wired connection 1 ipv4.routing-rules priority 500 from 192.168.2.10 lookup 100 nmcli connection up Wired connection 1不同 NetworkManager 版本对ipv4.routing-rules的语法支持不完全一致执行前先man nm-settings-nmcli或看版本手册。如果语法报错也可以退回用systemd-networkd在 netplan 里把 renderer 改成networkd并移除 NetworkManager 对该接口的管辖。6. 重启验证与换过坑之后的几条总结所有配置做完最怕的是什么重启一次又打回原形。6.1 验证前先清场在重启之前先把环境里可能干扰的变量处理掉确认有两块网卡的交换机端口是否在预期 VLAN 内二层广播域是否真的隔离。确认没有两个 DHCP 服务同时分发同网段地址。如果开启了容器或虚拟化确认net.ipv4.ip_forward是按需开启的否则可能放大路由混乱的影响。sysctl net.ipv4.ip_forward如果值为 1但本机并不是路由器建议临时置 0 测试排除转发逻辑的干扰sudo sysctl -w net.ipv4.ip_forward06.2 rp_filter 到底要不要动很多网上教程会直接让你把rp_filter改成 0我建议你别一上来就关。如果策略路由和二层隔离都做对了rp_filter 保持严格模式1其实也能正常工作。它是在发现“进接口与路由出接口不一致”时才丢包策略路由已经把“出口”定好了天然符合它的校验逻辑。但如果你因为历史原因无法做二层隔离或者某些报文必须跨接口进出那就只能把rp_filter改成松散模式2sudo sysctl -w net.ipv4.conf.all.rp_filter2 sudo sysctl -w net.ipv4.conf.eth0.rp_filter2 sudo sysctl -w net.ipv4.conf.eth1.rp_filter2松散模式的意思是只要路由表里存在一条能到达源地址的路由就认为这个包合法不强制要求“进接口等于出接口”。生产环境建议先设 2 观察一段时间别直接设 0。要持久化写到/etc/sysctl.d/99-rp_filter.confnet.ipv4.conf.all.rp_filter2 net.ipv4.conf.eth0.rp_filter2 net.ipv4.conf.eth1.rp_filter26.3 重启后的回归测试清单不要只 ping 一次网关就宣布完成建议按下面的清单走一遍systemctl reboot重启服务器。登录后立刻看ip route show table main和ip rule show确认策略路由规则还在。分别从 eth0 和 eth1 指定源 IP 访问各自网关确认都通。测试外网访问比如curl -I https://www.ubuntu.com不要只依赖 ICMP。长时间观察 SSH 连接是否稳定有没有“突然延迟拉满后断开”的情况。查看系统日志里有没有网络接口频繁 up/down 或路由表变化的记录journalctl -u systemd-networkd --since today | grep -E eth0|eth1|default via6.4 踩过几次坑之后我的几条心得说实话同网段双网卡属于“明明可以避开但总有人会踩”的配置。这几条经验你直接拿去用能用不同网段解决就用不同网段。策略路由只是补救方案不是最佳实践。网络规划的时候多写一行子网掩码后面能省一整天的排障时间。SSH 不要只监听一个地址。把 sshd 的监听地址临时改成0.0.0.0或用 IPMI/串口远程访问避免网卡切换时把自己锁在机器外面这个教训很疼。改配置之前先备份路由表。执行任何ip route、ip rule命令前先ip route show table all /tmp/route_backup.txt方便回滚。别迷信“重启大法”。这种问题重启后可能短暂恢复但过一段时间又复发因为底层冲突没有消除只是被重新初始化掩盖了而已。抓包是最有力的证据。如果和网络管理员的沟通陷入僵局直接把tcpdump抓到的“请求从 eth0 发、响应从 eth1 进”的包截图甩过去比任何口头描述都管用。
返回列表