Linux 虚拟网络(一)-内核对象模型
这是Linux 虚拟网络'系列的第一篇。系列共四篇:第一、二篇讲内核的对象模型,第三篇讲收编机制(master/slave)与帧的转发路径,第四篇讲 nmcli 配置持久化。读完整个系列,再看 Proxmox 的 vmbr、EVE-NG 的 pnet、docker 的 veth,你看到的将不再是一堆陌生名词,而是同一张图的不同画法。
一、一屏幕陌生的接口名
在一台跑着 Proxmox、EVE-NG 或者 docker 的机器上敲一条 ip -br link,你大概率会看到这样一屏:
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>eth1 UP 3c:ec:ef:6a:12:34 <BROADCAST,MULTICAST,UP,LOWER_UP>bond0 UP ca:a0:14:c4:24:58<BROADCAST,MULTICAST,MASTER,UP,LOWER_UP>vmbr0 UP 3c:ec:ef:6a:12:34 <BROADCAST,MULTICAST,UP,LOWER_UP>eth1.100@eth1 UP 3c:ec:ef:6a:12:34 <BROADCAST,MULTICAST,UP,LOWER_UP>tap100i0 UNKNOWN 8a:1f:33:9c:57:02 <BROADCAST,MULTICAST,UP,LOWER_UP>veth1a2b3c@if14 UP f2:6d:0a:88:41:c5 <BROADCAST,MULTICAST,UP,LOWER_UP>docker0 DOWN 02:42:8e:11:2f:66 <NO-CARRIER,BROADCAST,MULTICAST,UP>
八个接口,真正的硬件只有一块 eth1。大多数人的第一反应是:虚拟的东西真多、真乱。但这一屏的"乱",不在于设备多,而在于缺一张地图。
先把本篇的结论放在最前面:Linux 没有"模拟"网络,它是把整个机房搬进了内核。
机房里的每一样东西——网卡、交换机、网线、链路聚合,甚至"另一台服务器"——内核里都有一个一一对应的软件对象。更关键的是,在内核眼里,这些软件对象和物理网卡是完全平等的公民:它们都是同一个内核抽象 net_device 的实例。
物理网卡是 net_device,网桥是 net_device,bond、veth、tap,全都是。
kernel.org 的网络设备文档写明,物理设备与虚拟设备走的是同一套 net_device 注册与生命周期
翻内核源码,bridge、bond、veth、tap 各自的创建路径,最终全部落在同一个 alloc_netdev 上
一共是四种设备,一个构造函数。
这解释了一个你可能从没细想过的事实:ip link 凭什么能用同一套语法操作所有这些东西?不是命令设计得巧,而是这些东西在内核里本来就是同一种对象。同一种对象,自然一套语法通吃。
1.1 ip 命令的语法
本篇后面每一条命令都是 ip 敲出来的,值得先花两分钟把语法骨架立住,后面就不用背了。iproute2 不是常见的 --option=value 风格,它把命令行当成一个从左到右读的关键字流:
ip link add br0 type bridge│ │ │ │ ││ │ │ │ └─ 设备类型:声明造的是什么│ │ │ └────── 新设备的名字,纯标识符│ │ └─────────── 命令:添加│ └───────────────── 对象:链路(二层设备)└───────────────────── 工具本体
从左到右读:对"链路"这个对象,执行"添加"操作,新接口叫 br0,类型是 bridge。type 后面能接什么参数,由类型自己决定:type vlan 后面要跟 id 100,type veth 后面要跟 peer name,后文遇到时再各自展开。记住"从左到右、关键字引导"这一条,本篇所有命令都能顺着读下来。
1.2 全系列的地图
这张对应表,就是全系列的地图:
| | |
|---|
| | |
| | ip link add br0 type bridge |
| | ip link add veth0 type veth peer name veth1 |
| | ip link add bond0 type bond |
| | ip link add link eth1 name eth1.100 type vlan id 100 |
| | |
| network namespace(网络命名空间) | ip netns add ns1 |
物理机房里你怎么组网——买交换机、拉网线、插网卡、接服务器——内核里就怎么组网,只是"买"和"拉"都变成了一条 ip link add。上篇(一、二两部分)的任务,是把这张表里能徒手造的逐个造出来、看清楚、拆掉;中篇再讲它们之间的收编规则,以及一个数据帧在这张图里怎么走。

二、网桥:内核中的二层交换机
先看它解决什么矛盾。 一块物理网卡,本质上只是物理网段上的一个二层端点:一个 MAC、一份载体。可机器上跑着三台虚拟机,每台都想以自己的 MAC、平等地出现在物理网段上,跟物理主机做邻居:同一广播域,能被二层直达。一个口,要喂多个平等端点,中间必须有个东西按 MAC 把帧分发到正确的虚拟口。那个东西,就是交换机。
而交换机这台设备,把外壳拆掉,本质上只做三件事:
学习:每收到一个帧,看它的源 MAC 从哪个口进来的,记进一张表(FDB,Forwarding Database,转发数据库,在物理交换机上就是你 display mac-address 看到的那张 MAC 地址表);
转发:查帧的目的 MAC,FDB 里有记录,就只从对应的那个口发出去;
泛洪:FDB 里查不到,就从除入口外的所有口发出去,广播帧同理。
网桥(Linux Bridge)就是这三件事的软件实现:它学 MAC、按 FDB 转发、查不到则泛洪,上面插着各虚拟机的虚拟口,下面用物理网卡当上行口接物理网。请记住"泛洪"这个行为:它是交换机能工作的保底手段,也是广播风暴得以形成的前提之一;至于怎么破环,那是 STP 的职责,不在本系列展开。

图里有两处细节先按下不表:物理网卡的副标"不再持 IP",第三章末尾讲透;插在网桥上的 vnet 口是什么,下一篇揭晓。
2.1 网桥的创建与观察
对应表说了,交换机不用买,两条命令:
ip link add br0 type bridgeip link set br0 up
造完先看清楚。-d(detail)这个标志能让 ip 吐出功能层的配置细节:
ip -d link show br0
输出里能看到 stp_state 0、forward_delay 1500、vlan_filtering 0 这些字段:一台交换机该有的功能开关,它一个不缺。这不是"像"交换机,这就是交换机。
配置看完,再看运行状态。-s(statistics)显示收发统计:
ip -s link show br0# 典型的回复3: br0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000 link/ether 12:88:3d:4e:f9:4c brd ff:ff:ff:ff:ff:ff RX: bytes packets errors dropped missed mcast 0 0 0 0 0 0 TX: bytes packets errors dropped carrier collsns 0 0 0 0 0 0
这段输出有两个细节值得停一下。
第一,明明刚执行过 up,state 却还是 DOWN,flags 里多了个 NO-CARRIER。一台一个口都没插的交换机,当然没有载波:管理上已拉起(UP 标志在),运行上起不来(state DOWN)。回头看开头那一屏,docker0 的 DOWN 就是同一回事:那台机器此刻没有跑着的容器,桥上没有一个活口。
第二,统计全是零。用法:ip -s link show <接口> 看接口收发统计,-s -s 叠加可显示更细的错误分类;排障时隔几秒执行两次做差看增量,历史累计值参考意义不大。
一句话总结:-d 看配置,-s 看运行,二层排障先跑一遍 -s。
再看它的 FDB,用 iproute2 自带的 bridge 命令:
bridge fdb show br br0# 典型的回复33:33:00:00:00:01 dev br0 self permanent12:88:3d:4e:f9:4c dev br0 vlan 1 master br0 permanent
第一条,33:33:00:00:00:01:33:33 是 IPv6 组播 MAC 的固定前缀,这一条对应 IPv6 的"所有节点"组播地址 ff02::1。self 表示表项挂在 br0 设备本身:发往这个组播 MAC 的帧要送进 br0 自己的协议栈;内核只要启用了 IPv6,建桥就自带它。permanent 表示静态项,永不老化。
第二条,12:88:3d:4e:f9:4c:这是 br0 自己的 MAC。master br0 表示这条挂在桥的转发平面上:发往这个 MAC 的帧应该"交给桥处理",具体到这条就是本机收包,而不是从某个口转出去。vlan 1 这个字段容易误读:前面 -d 的输出明明写着 vlan_filtering 0,怎么表项还带 VLAN?因为即使不开 VLAN 过滤,桥也自带"默认 PVID 1"的概念,表项一律归属 VLAN 1,它不代表 VLAN 感知已开启。permanent 同理:桥自己的 MAC 不需要学习,永不老化。
注意,此刻表里的两条全是建桥自带的,没有一条是"学"来的。一台崭新的交换机,MAC 表是空的,而且一个口都还没插。往上插东西,是第三章的事;插之前,先把这台交换机的另一个身份交代了。
2.2 vlan_filtering 与 802.1Q 能力
到这里你可能觉得网桥只是台"傻交换机"。不是。给它拨一个开关:
ip link set br0 type bridge vlan_filtering 1
它就成了一台支持 802.1Q 的 VLAN 交换机:每个端口可以配 tagged/untagged、配 PVID,用 bridge vlan 子命令管理,和你在物理交换机上配 access/trunk 口是同一套语义。
Proxmox 网络配置里那个"VLAN aware"复选框,本体就是 vlan_filtering 这个开关。

勾上之后,配置文件里写入的 bridge-vlan-aware yes 落到内核就是 vlan_filtering=1,同时补一行 bridge-vids 2-4094 把全部 VLAN 放行(过滤一开,不声明放行的 VLAN 会被直接滤掉,这行不能省)。
顺带一提:不勾这个框,VM 打 VLAN tag 照样能通:那是 Proxmox 在背后替你走了另一条路,给每个 VLAN 自动生成"物理口的 VLAN 子设备 + 一座专属小桥";那个"VLAN 子设备"是什么,下一篇开篇就讲。
本篇知道这两条路都通向 802.1Q 即可。
三、veth 与 netns:互联与隔离
交换机造好了,一个口都没插。要插东西,先得有网线,它对应的内核对象就是 veth。
veth(virtual ethernet)是 Linux 内核提供的成对虚拟以太网设备,两端各是一个 net_device,一端发出的帧从另一端原样收到;两端可分属不同 network namespace,是跨命名空间通信的标准通道,典型场景是 docker 容器经 veth 接入网桥。
3.1 veth 的创建
veth 是成对创建的:
ip link add veth0 type veth peer name veth1│ │ │ │ │ ││ │ │ │ │ └─ veth 类型专属参数:另一头叫什么│ │ │ │ └─────────── 设备类型:veth(虚拟以太网)│ │ │ └────────────────── 新设备的名字,纯标识符│ │ └─────────────────────── 命令:添加│ └───────────────────────────── 对象:链路(二层接口)└───────────────────────────────── 工具本体
按第一章的读法拆:对"链路"执行"添加",新接口叫 veth0,类型是 veth;最后那段 peer name veth1,就是 veth 这个类型专属的参数。veth的本质是一根虚拟网线,网线必然有两头,所以创建时必须同时给两端命名:veth0 是一头,peer name veth1 声明另一头叫 veth1。
整句话翻译成人话:创建一对虚拟以太网接口,一头叫 veth0,另一头叫 veth1,从 veth0 进去的帧会从 veth1 出来,反之亦然,就是一根软件网线。
你创建 veth pair 时,本质上创建了一根“虚拟网线”:
veth0 <======虚拟网线======> veth1
类似这种结构:
宿主机 namespace | br0 | veth0@if6 | | 虚拟网线 | veth1@if7 |ns1 namespace
注意:if6、if7 里的数字是 interface index,也就是网卡在内核里的编号。
3.2 netns:网络命名空间
先说为什么需要它。网线已经有了,可另一头接谁?一个自然的想法是:不引入任何新东西,把 veth1 留在宿主机上直接配 IP。这样行不行?
不行,坏就坏在它表面上是成功的。veth0、veth1 都在宿主机上、各配一个 IP,这时去 ping veth1 的地址:内核发包前先查路由表,查到的结果是"这个目的地址是我自己的"。对本机地址,内核不会把包交给网卡驱动,而是走协议栈内部的回环(loopback)通道直接送达。于是你会看到 ping 通了,帧却从头到尾没上过这根"网线",通的是回环,不是 veth,实验就假了。
问题不在命令,在于一台 Linux 主机只有一份网络协议栈状态:一张路由表、一套接口列表、一个端口空间。在同一份状态内部,不存在"两台主机对话"这回事。要让帧真正走上网线,两端必须分属两份互相独立的协议栈状态。
这份"独立的协议栈状态",内核早就提供了,它就是 network namespace,网络命名空间,下文简称 netns。netns 是 Linux 命名空间机制在网络子系统的实现:内核的协议栈代码只有一份,netns 隔离的是状态,每个 netns 持有一份完全独立的接口列表、路由表、ARP 表、conntrack 和端口空间。落到可感知的现象上:ns1 里配的 IP,宿主机的路由表看不见;ns1 里监听 80 端口,和宿主机已有的 80 互不冲突。
它也不是什么新事物,内核 2.6.24 起就已合入主线。你如果用过 docker,其实早就在和它打交道:docker 容器的网络隔离,用的就是 network namespace,每启动一个容器,docker 就替你创建一个 netns,和下面手工创建的是同一个内核对象,下一篇会当场验证。
所以 netns 解决的问题可以归结为一句:让一台物理机上并存多份互不干扰的网络协议栈状态。对应表把它写作"另一台独立的服务器",这是一个类比,但根据很扎实:一台主机在网络上区别于另一台主机,靠的恰恰就是独立的接口、路由表和端口空间,netns 把这一整套都隔离了出来。类比也有边界:netns 不隔离 CPU、内存和文件系统。所以准确的说法是:netns 是内核给出的、网络意义上"一台主机"的最小化实现。记住这层身份,下一篇有它的正式出场。
创建它只要一条命令:
ip netns add ns1
把 veth1 塞进 ns1 之后,veth1 的地址就属于另一份协议栈状态,宿主机的路由表里查不到它是本机地址。这时再 ping,内核查表得到的答案是"从 veth0 发出去",帧这才真正走上网线。怎么塞、怎么组网,下一节的六条命令一次做完。
创建完可以验证
ip netns list返回ns1
3.3 组网与验证:FDB 的学习过程
到这里,三样东西都齐了:交换机 br0、网线 veth0/veth1、主机 ns1。但它们还是三个孤立对象,彼此之间没有建立任何关联。
接下来六条命令完成组装,结构是对称的三段:前两条把网线的 veth0 这头插进交换机并拉起;中间三条把 veth1 那头塞进 ns1,在"主机"内部把口拉起、配上 IP;最后一条给宿主机自己配 IP,注意它配在了 br0 上而不是某块网卡上。这个落点是本章埋的最后一个问题,命令跑完马上讲:
ip link set veth0 master br0 ip link set veth0 upip link set veth1 netns ns1 ip netns exec ns1 ip link set veth1 upip netns exec ns1 ip addr add 192.168.100.2/24 dev veth1ip addr add 192.168.100.1/24 dev br0 ip link set br0 up
验证,并且顺便看一眼交换机的学习过程:
ping -c 2 192.168.100.2bridge fdb show br br0
FDB 里多了一条动态表项,类似这样:

注意这条的读法:这个 MAC 不是 veth0 的,是 ns1 里 veth1 的:桥学到的是对端主机的 MAC;dev veth0 说的是"它从 veth0 这个口学来",以后发往这个 MAC 的帧就从 veth0 出去。没有 permanent,说明它是学来的动态项,会老化。
复盘一下刚才这两个 ping 包背后发生的事:第一个 ping 之前,FDB 里没有任何学来的表项;宿主机的 ARP 请求以广播帧进入 br0,br0 查不到、泛洪;应答从 veth0 口回来,br0 看到源 MAC,学习、记入 FDB;后续的帧直接查表、单口转发。学习、转发、泛洪——交换机的三件事,一次 ping 全演了一遍。
3.4 交换机端口没有 IP
回头看第二章图里物理网卡的那个副标:"不再持 IP"。这句话背后是本章最重要的一个角色转变。
用物理交换机想这个问题最清楚:交换机那 24 个口,哪个口有 IP?都没有。
它们是纯二层口,只负责帧的进出。交换机自己要被管理、要有三层身份时,IP 配在一个虚接口上(SVI,华为也可以叫做vlanif或者叫管理接口),而不是配在某个物理口上。
网桥是同一套逻辑:
- 物理网卡 = 交换机的一个口。 被并入 br0 之后,它降级成纯二层上行口,职责只剩"把帧交给网桥、从网桥发出去",不再终结 IP;
- br0 = 这台交换机的 SVI。 宿主机若要在这个网段上有三层身份(IP、网关、能被 ping、能 SSH),这个 IP 必须落在 br0 上。
刚才实验里 ip addr add 192.168.100.1/24 dev br0 这一步,配的就是这个 SVI。
生产环境里把物理口并入网桥,前后对比是这样:
***桥接之前***eth1: inet 192.168.1.10/24 ← IP 在物理口上***把 eth1 并入 br0 之后***eth1: <UP> master br0 ← 纯二层口,只标着 master br0br0: inet 192.168.1.10/24 ← 三层身份落在网桥上
3.5 生产环境的巨坑
这里必须讲清一个极易误解的细节:内核不会替你搬这个 IP。 执行 ip link set eth1 master br0 的那一刻,eth1 上原来配的 IP 原样留在 eth1 上,一个字节都没动。"IP 还在,但已失效"和"IP 被挪走"是两回事,而前者才是真相。
失效的表现有个反直觉的特点:连接往往不会立刻断。配完之后的几十秒到几分钟里,一切看起来都正常,然后突然中断。要理解这个"延迟发作",得把收包和发包两个方向分开看。

假设原来服务器是这样:
eth1 = 192.168.1.10/24默认网关 = 192.168.1.1
这时候 eth1 自己承担两个角色:
外部网络 | eth1 |Linux 协议栈 |192.168.1.10
也就是说:
这时没问题。
当执行 ip link set eth1 master br0 后:eth1 变成了“交换机端口”
意思是:把 eth1 加入 br0 这个 Linux 网桥,让 eth1 成为 br0 的一个桥端口。
这时候逻辑变成:
外部网络 | eth1 ← 现在只是 br0 的一个二层端口 | br0 ← 现在才是主机应该使用的三层接口 |Linux 协议栈
重要的规则发生了变化,请留意一下流程:
当 eth1 被加入 br0 时,Linux bridge 代码会把 eth1 注册为 bridge port,并通过 netdev_rx_handler_register() 给 eth1 安装 bridge 的收包处理函数。之后 eth1 收到的帧在进入普通 ARP/IP 协议栈之前,会先被这个 bridge handler 处理。因此 eth1 的主要职责变成二层桥端口,br0 才是本机应该配置 IP 的三层逻辑接口。
“收包方向立刻有问题”
加入桥之后,外部发给服务器的包会先到 eth1。
但 eth1 现在是 br0 的桥端口,所以包会被 br0 接管。
对于 Linux 协议栈来说,包更像是从 br0 进来的,而不是从 eth1 进来的。
但是你的 IP 和路由还在 eth1 上。
于是出现这种不一致:
路由表认为:192.168.1.0/24 这个网段在 eth1 上实际收包时:包从 br0 进来
系统可能会觉得:奇怪,这个包按路由判断应该从 eth1 来,为什么实际从 br0 来?
如果启用了严格的 rp_filter,系统可能直接把这种包丢掉。你可以先不深究 rp_filter,只要理解它干的事:它检查“包来的方向”和“路由认为应该来的方向”是否一致。 不一致,就可能认为异常,然后丢包。
“发包方向可能不会立刻有问题”
虽然收包方向已经有问题,但发包方向有时候不会马上坏。因为系统里可能还有旧的 ARP 缓存。
这个信息还没过期时,服务器发包可以直接发:
我要发给网关 192.168.1.1我已经知道它的 MAC 地址直接从 eth1 发出去
所以刚配完桥的几十秒到几分钟里,可能 SSH 还没断。这就是所谓的:看起来还正常,其实只是靠旧缓存续命。
等缓存过期后,系统需要重新确认网关的 MAC 地址。ARP 请求可能还能从 eth1 发出去;
ARP 请求可能还能从 eth1 发出去;
网关也会回 ARP 应答;
但 ARP 应答回来后,被 br0 接管了;
eth1 自己等不到它想要的 ARP 确认;
eth1 的邻居表更新失败;
发包路径最终也断了。
所以配桥接的铁律是:enslave、迁地址、补默认路由,必须作为一个整体操作完成。
核心原则就是:
物理网卡 eth1 加入 bridge 之后,不再配 IP;IP 应该配置在 br0 上。
但是特别注意!这套流程逻辑虽然是对的,但在生产环境不应该把这串 ip 命令 当作最终交付,而是交给发行版自带的持久化网络配置系统:
- RHEL / CentOS / openEuler 系:NetworkManager(nmcli), 旧版本还有 network-scripts 的 ifcfg 文件(RHEL 9 起已弃用);
- Debian:ifupdown,配置写在 /etc/network/interfaces;
- Proxmox VE:ifupdown2,配置文件同上,Web 界面改的就是它;
- Ubuntu Server:netplan,它自己不干活,把 YAML 渲染给 systemd-networkd 或 NetworkManager 去执行;
- 纯 systemd 环境:systemd-networkd 的 .netdev / .network 文件
iproute2 的核心定位是运行态网络配置工具,它主要通过 netlink/rtnetlink 向内核发送网络配置请求;而内核中的 net_device 是运行期内存对象,本身没有对应的持久化配置文件。因此,直接用 ip 命令添加的地址、路由、链路、桥等配置,默认只在当前系统运行期间有效。是否能重启后恢复,取决于 NetworkManager、systemd-networkd、libvirt、ifcfg、netplan 等上层持久化配置系统。
四、实验环节
先说清楚这个实验的性质:它是一次故意的错误示范,目的是让 3.5 节讲的断联机制在你眼前真实发生一次,而不是教你操作步骤。
生产环境既不会用这个顺序,也不会裸敲 ip 命令,而是交给持久化工具一次成型(见上文)。
所以请务必在实验虚拟机上做,并确保手里有控制台或 VNC 这类不依赖网络的通道,SSH 断了才救得回来。
4.1 错误操作,会造成ssh断联
本实验用于验证一个典型现象:当物理网卡 ens18 被加入网桥 br0 后,如果没有及时将 IP 地址和默认路由迁移到 br0,远程 SSH 连接可能会立即断开。
该现象说明:物理网卡加入 Linux bridge 后,其角色会从普通三层网卡变成二层桥端口。如果 IP 仍然保留在物理网卡上,就可能导致收包路径和三层 IP 配置发生错位,从而造成网络中断。
1 在实验前,先对虚拟机创建快照,方便网络配置异常后快速恢复。
2 查看ip地址
ip -br ad返回lo UNKNOWN 127.0.0.1/8 ::1/128 ens18 UP 172.18.3.10/24 fe80::be24:11ff:fea1:2190/64
可以看到,当前主机的业务网卡为 ens18,其 IPv4 地址为:
172.18.3.10/24
查看路由信息
ip routedefault via 172.18.3.254 dev ens18 proto static metric 100 172.18.3.0/24 dev ens18 proto kernel scope link src 172.18.3.10 metric 100
可以看到,当前默认路由为:default via 172.18.3.254 dev ens18
也就是说,当前主机通过 ens18 访问默认网关 172.18.3.254。
3 添加网桥并验证
ip link add br0 type bridgeip -br adlo UNKNOWN 127.0.0.1/8 ::1/128 ens18 UP 172.18.3.10/24 fe80::be24:11ff:fea1:2190/64 br0 DOWN
此时可以看到,系统中已经出现了 br0,但 br0 目前处于 DOWN 状态,并且尚未配置 IP 地址。
4 加入网桥
ip link set ens18 master br0
5 此时ssh立刻断联,执行该命令后,ens18 会变成 br0 的桥端口。也就是说,ens18 的角色发生了变化:执行前:ens18 是普通三层网卡,直接承载 IP 地址和默认路由。执行后:ens18 变成 br0 的二层桥端口
6 实验成功复现了“只入桥、不迁 IP 会导致远程断联”的问题
4.2 正确操作
你可以创建脚本:
vi /root/lab-netns-bridge.sh
脚本内容如下
#!/usr/bin/env bashset -euo pipefail[ "$EUID" -eq 0 ] || {echo"请使用 root 用户执行"exit 1}IF=ens18BR=br0ADDR=172.18.3.10/24GW=172.18.3.254echo"创建网桥 $BR..."ip link add "$BR"type bridge 2>/dev/null || trueecho"启动 $BR 和 $IF..."ip link set"$BR" upip link set"$IF" upecho"将 $IF 加入 $BR..."ip link set"$IF" master "$BR"echo"将 IP 从 $IF 迁移到 $BR..."ip -4 addr flush dev "$IF"ip addr replace "$ADDR" dev "$BR"echo"切换默认路由到 $BR..."ip route replace default via "$GW" dev "$BR"echoecho"当前地址:"ip -br addrechoecho"当前路由:"ip routeechoecho"测试网关连通性:"ping -c 3 "$GW"
执行脚本后,验证结果如下

全程SSH无断联
小结与下集预告
iproute2 的核心定位是运行态网络配置工具,它主要通过 netlink/rtnetlink向内核发送配置请求;而内核中的 net_device 是运行期内存对象,本身没有对应的持久化配置文件。因此,直接用 ip 命令添加的地址、路由、链路、桥, 默认只在本次开机期间有效。重启后还在不在,取决于上层持久化系统:NetworkManager、ifupdown(2)、systemd-networkd、netplan,以及虚拟化平台自带的网络管理(如 libvirt、Proxmox)。这些工具的原理殊途同归:把配置落在自己的文件里,开机时再把文件"重放"成 netlink 调用,等价于每次开机替你把那串 ip 命令重新执行一遍。所以运行态和持久化不是两套网络,而是同一套内核对象、两个入口。怎么用 nmcli 把本篇徒手搭的东西变成配置文件,第四篇讲。