网络命令空间
本文介绍了Linux网络命名空间(Network Namespace)的基本概念及其典型应用场景。网络命名空间是Linux内核提供的一种网络隔离机制,每个命名空间拥有独立的网络接口、IP地址、路由表等资源,彼此互不干扰。这种机制广泛应用于容器化和虚拟化技术,如Docker容器就是通过命名空间实现网络隔离。 文章通过实验演示了如何创建两个隔离的网络命名空间(ns1和ns2),并使用veth pair
网络命令空间简介
网络命令空间(Network Namespace)是Linux内核提供的一种隔离机制,用于创建独立的网络环境。每个命名空间拥有自己的网络接口、IP地址、路由表、防火墙规则等资源,彼此互不干扰。这类似于在单个物理主机上虚拟出多个网络栈。
其主要目的是支持容器化和虚拟化技术,例如在Docker中,每个容器运行在自己的网络命名空间内,实现资源隔离和安全性。好处包括:
- 隔离网络配置,避免冲突(如不同容器使用相同IP)。
- 简化测试环境搭建(如模拟多主机网络)。
- 提升资源利用率(无需额外硬件)。
实验概述
目的:创建两个隔离的网络命名空间,并通过veth pair实现它们之间的通信
详细操作步骤说明
1. 创建网络命名空间
# 创建两个隔离的网络命名空间
sudo ip netns add ns1
sudo ip netns add ns2
# 验证创建成功
sudo ip netns list
输出:ns2 ns1 - 表示两个命名空间已创建
2. 初始状态检查
# 查看主机默认命名空间的网络接口
ip addr
显示:
lo: 回环接口ens33: 物理网卡,IP为192.168.136.128/24
# 查看ns1中的初始网络接口
sudo ip netns exec ns1 ip addr
显示:只有lo接口,且状态为DOWN
3. 启用回环接口
# 启用两个命名空间的lo接口
sudo ip netns exec ns1 ip link set lo up
sudo ip netns exec ns2 ip link set lo up
# 验证lo接口状态
sudo ip netns exec ns1 ip addr show lo
显示:lo状态变为UP,并自动配置了127.0.0.1/8
4. 创建并配置veth pair
# 创建一对虚拟以太网设备
sudo ip link add veth0 type veth peer name veth1
# 查看创建的veth设备
ip link show | grep veth
# 将veth设备分别移动到命名空间中
sudo ip link set veth0 netns ns1
sudo ip link set veth1 netns ns2
# 验证设备移动成功
sudo ip netns exec ns1 ip link show veth0
sudo ip netns exec ns2 ip link show veth1
5. 配置IP地址并启用接口
# 为ns1的veth0配置IP并启用
sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth0
sudo ip netns exec ns1 ip link set veth0 up
# 为ns2的veth1配置IP并启用
sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth1
sudo ip netns exec ns2 ip link set veth1 up
6. 测试连通性
# 从ns1 ping ns2
sudo ip netns exec ns1 ping -c 3 10.0.0.2
# 从ns2 ping ns1
sudo ip netns exec ns2 ping -c 3 10.0.0.1
结果:双向ping通,延迟约0.03ms,证明两个命名空间可以通信
7. 验证路由配置
# 查看ns1的路由表
sudo ip netns exec ns1 ip route
# 输出:10.0.0.0/24 dev veth0 proto kernel scope link src 10.0.0.1
# 查看ns2的路由表
sudo ip netns exec ns2 ip route
# 输出:10.0.0.0/24 dev veth1 proto kernel scope link src 10.0.0.2
# 查看主机路由表
ip route
# 输出:默认路由指向192.168.136.2 via ens33
技术要点总结
核心概念
- 网络命名空间:隔离的网络栈实例,拥有独立的接口、路由表、iptables规则等
- veth pair:成对出现的虚拟以太网设备,像一根虚拟网线连接两个命名空间
- 网络隔离:ns1和ns2与主机默认命名空间完全隔离
网络拓扑
ns1 (10.0.0.1) <--veth pair--> ns2 (10.0.0.2)
veth0 veth1
关键命令说明
ip netns add <name>:创建命名空间ip netns exec <name> <command>:在指定命名空间执行命令ip link add type veth peer name:创建veth设备对ip link set <dev> netns <name>:将设备移动到命名空间
这个实验成功演示了Linux网络虚拟化的基础,常用于容器网络、网络测试等场景。
三节点网络拓扑实验
通过 Linux ip netns 命名空间机制,构建一个虚拟的三节点网络拓扑,包括两个终端主机(ns1、ns2)和一个路由器(router)。
目标是验证 Linux 网络命名空间之间的隔离与连通性原理,掌握虚拟以太网接口(veth pair)与 IP 转发机制的配置。
二、实验原理
-
网络命名空间(Network Namespace)
每个 namespace 拥有独立的网络栈(接口、路由表、ARP 缓存等)。
命名空间之间默认隔离,但可以通过 veth pair 连接形成虚拟网络。 -
veth pair(虚拟网线)
veth 设备成对出现,一端发出的数据包会从另一端接收。
它常用于跨 namespace 通信,相当于一根虚拟网线。 -
路由器转发机制(IP Forwarding)
当开启net.ipv4.ip_forward=1时,Linux 内核可以像真实路由器一样转发来自不同子网的包。
三、实验拓扑(Mermaid 拓扑图)
🔹 说明:
ns1和ns2分别代表两个不同子网:192.168.1.0/24 与 192.168.2.0/24。router担任三层转发角色,实现两个子网间的通信。veth1-*与veth2-*是虚拟网卡对,连接不同命名空间。
四、实验过程详细解析
Step 1. 创建命名空间
sudo ip netns add ns1
sudo ip netns add ns2
sudo ip netns add router
✅ 成功创建 3 个命名空间。
Step 2. 创建并分配 veth pair
sudo ip link add veth1-ns1 type veth peer name veth1-router
sudo ip link add veth2-ns2 type veth peer name veth2-router
这创建了两对虚拟网卡(veth 对),相当于两根“虚拟网线”。
Step 3. 将 veth 接口移动到命名空间
sudo ip link set veth1-ns1 netns ns1
sudo ip link set veth1-router netns router
sudo ip link set veth2-ns2 netns ns2
sudo ip link set veth2-router netns router
✅ 每个命名空间现在有自己的接口,彼此独立。
Step 4. 为各接口配置 IP 地址
sudo ip netns exec ns1 ip addr add 192.168.1.10/24 dev veth1-ns1
sudo ip netns exec ns2 ip addr add 192.168.2.10/24 dev veth2-ns2
sudo ip netns exec router ip addr add 192.168.1.1/24 dev veth1-router
sudo ip netns exec router ip addr add 192.168.2.1/24 dev veth2-router
Step 5. 启动接口
sudo ip netns exec ns1 ip link set lo up
sudo ip netns exec ns1 ip link set veth1-ns1 up
sudo ip netns exec ns2 ip link set lo up
sudo ip netns exec ns2 ip link set veth2-ns2 up
sudo ip netns exec router ip link set lo up
sudo ip netns exec router ip link set veth1-router up
sudo ip netns exec router ip link set veth2-router up
Step 6. 开启路由器的 IP 转发功能
sudo ip netns exec router sysctl -w net.ipv4.ip_forward=1
✅ 输出显示:
net.ipv4.ip_forward = 1
说明路由器现在具备跨子网转发能力。
Step 7. 设置默认路由
sudo ip netns exec ns1 ip route add default via 192.168.1.1
sudo ip netns exec ns2 ip route add default via 192.168.2.1
✅ 这一步确保 ns1 与 ns2 的数据包能够通过 router 进行转发。
五、实验结果验证
✅ 连通性测试
1. ns1 → ns2 跨网段通信
sudo ip netns exec ns1 ping -c 3 192.168.2.10
输出:
3 packets transmitted, 3 received, 0% packet loss
说明数据包成功经 router 转发到达 ns2。
2. ns2 → ns1 反向通信
sudo ip netns exec ns2 ping -c 3 192.168.1.10
同样 0% 丢包,说明双向通信正常。
3. 各自 ping 默认网关(路由器)
sudo ip netns exec ns1 ping -c 3 192.168.1.1
sudo ip netns exec ns2 ping -c 3 192.168.2.1
返回 TTL=64,说明路由器接口工作正常。
六、实验结果分析
| 测试项目 | 预期结果 | 实际结果 | 结论 |
|---|---|---|---|
| 命名空间创建 | 成功 | 成功 | OK |
| veth 配置 | 成功 | 成功 | OK |
| IP 地址与路由配置 | 正确 | 正确 | OK |
| 路由器转发 | 正常 | 正常 | OK |
| ns1 ↔ ns2 连通性 | 能通信 | 能通信 | ✅ 成功 |
实验验证了:
- 网络命名空间可以实现隔离与虚拟网络构建;
- veth pair 可以在命名空间间建立“虚拟链路”;
- 开启
ip_forward后,Linux 完全可以充当三层路由器角色。
七、结论与拓展
-
本实验成功构建了一个纯软件虚拟网络,结构清晰、逻辑正确。
-
通过
ip netns工具可轻松模拟多主机网络环境,无需真实物理设备。 -
若进一步拓展:
- 可添加 NAT 转换(用
iptables -t nat模拟访问外网); - 可引入
bridge模拟交换机; - 可测试
tc延迟、丢包模拟网络性能。
- 可添加 NAT 转换(用
好的。下面把你在每一步验证时看到的返回值/输出逐行“读懂”,说明它们各自代表什么、是否符合预期、若异常应如何判断与处理。为便于查找,我按你执行的实际顺序分组解析。
1)命名空间相关
ip netns list
返回(最初为空):
root@ubuntu24:/home/keson# sudo ip netns list
(空)
- 含义:当前系统中还没有持久化的 network namespace(文件位于
/run/netns)。 - 结论:正常;尚未创建。
ip link show(宿主机)
3: veth1-router@veth1-ns1: <BROADCAST,MULTICAST,M-DOWN> ... state DOWN ...
4: veth1-ns1@veth1-router: <BROADCAST,MULTICAST,M-DOWN> ... state DOWN ...
5: veth2-router@veth2-ns2: <BROADCAST,MULTICAST,M-DOWN> ... state DOWN ...
6: veth2-ns2@veth2-router: <BROADCAST,MULTICAST,M-DOWN> ... state DOWN ...
@含义:显示对端接口名(veth 成对出现,这里彼此指向对方)。M-DOWN:carrier(物理层)未 up。对 veth 来说,只有两端都处于 up,链路层才会LOWER_UP。state DOWN:接口未ip link set ... up。- 结论:符合刚创建后的默认状态。
3)将 veth 移入各命名空间
ip link set <ifname> netns <ns>
无显式输出(返回 0)
- 含义:接口成功移动到对应 ns。
宿主机再次查找:
ip link show | grep -E "veth1-ns1|veth1-router|veth2-ns2|veth2-router"
(无输出)
- 含义:这些接口已不在宿主机网络栈,而是在各自的 namespace 中。
- 结论:符合预期。
进入各 ns 查看接口
ns1: 4: veth1-ns1@if3: <BROADCAST,MULTICAST> ... state DOWN ...
router: 3: veth1-router@if4: ...
5: veth2-router@if6: ...
ns2: 6: veth2-ns2@if5: ...
@if3/@if4:显示对端接口的 ifindex(内核索引),可忽略。state DOWN:仍未up。- 结论:移动成功,状态待配置。
4)配置 IP 地址
ip addr add ... dev <if>
无显式输出(返回 0)
- 含义:为接口分配 IPv4 地址成功。
查看地址(仍未 up)
inet 192.168.1.10/24 scope global veth1-ns1
link-netns router
inet .../24:IPv4 地址与掩码设置正确。link-netns router:对端接口位于router命名空间(有助于排错)。- 结论:IP 配置正确。
5)接口 up 与 loopback 启动
ip link set lo up + ip link set veth* up
无显式输出(返回 0)
再次查看(后续汇总)
你在后面一次性展示了完整地址、路由和链路层状态,重点解读:
-
<...UP,LOWER_UP>:UP:接口被管理地置为 up;LOWER_UP:链路层“载波”已 up(对 veth 而言需两端都 up)。
-
qdisc noqueue:此类虚拟接口没有复杂队列规则,是常见默认值。 -
state UP:接口运行中。
结论:接口均已正确拉起。
6)开启路由转发
sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
- 含义:在 router 命名空间内,内核三层转发开启。
- 二次确认
sysctl net.ipv4.ip_forward:
net.ipv4.ip_forward = 1
- 结论:路由器具备跨子网转发能力。
7)配置默认路由
ip route add default via ...(在 ns1/ns2)
无显式输出(返回 0)
ip route(在 ns1)
default via 192.168.1.1 dev veth1-ns1
192.168.1.0/24 dev veth1-ns1 proto kernel scope link src 192.168.1.10
default via ...:默认网关指向 router 对应接口。proto kernel scope link src ...:本子网直连路由,由内核自动生成。- 结论:路由表正确;ns2 的输出与之对应(换成 192.168.2.0/24 与 192.168.2.1)。
ip addr show(在 router)
192.168.1.0/24 dev veth1-router ... src 192.168.1.1
192.168.2.0/24 dev veth2-router ... src 192.168.2.1
- 含义:router 对两个子网均为直连网络,无需额外静态路由。
- 结论:符合三层路由器角色。
8)连通性验证(ping)
A)跨网段:ns1 → ns2
64 bytes from 192.168.2.10: icmp_seq=1 ttl=63 time=0.051 ms
...
--- 192.168.2.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss
rtt min/avg/max/mdev = 0.036/0.049/0.061/0.010 ms
-
0% packet loss:三次 ICMP 往返均成功。 -
time=...ms:往返时延(RTT),veth 内核路径极短,微秒~低毫秒级正常。 -
ttl=63(重点):- Linux 发包默认 TTL=64;
- 经过 router 转发时,TTL 在转发处会 减 1;
- 收到回显应答时显示的 TTL=63 → 说明确实经过了一跳(router)。
-
结论:跨子网转发有效,路径正确(经 router)。
B)反向:ns2 → ns1
ttl=63
0% packet loss
- 含义:同理,双向通信路径经 router,TTL=63 符合“过一跳”的规律。
- 结论:双向跨网段连通。
C)同网段:各自 ping 默认网关(ns1 → 192.168.1.1、ns2 → 192.168.2.1)
ttl=64
- 原因:与自己在同一命名空间、同一子网里的接口通信,不经过三层转发(或认为不跨“下一跳”),TTL 不会被减 1。
- 结论:ttl=64 是同子网直连的“金标准”现象;与上面 ttl=63 形成对比,充分证明路由转发只发生在跨子网时。
小结:
- ttl=64:本 ns 内直连;
- ttl=63:跨 router 转发 1 跳;
- 若出现 ttl=62,意味着还多过了一跳(例如再串一个路由节点)。
10)若验证出现异常,如何用返回值快速定位
-
ping100% 丢包
1)看router内sysctl net.ipv4.ip_forward是否为1;
2)看ip link show是否UP,LOWER_UP;
3)看ns1/ns2的ip route是否存在正确的default via;
4)ip addr show是否 IP/掩码配置错误或落在同网段冲突。 -
ttl异常- 应为 64(直连)或 63(跨一跳);若仍是 64 且你预期跨网段,说明实际未过路由器(比如路由/掩码错误导致被认为直连)。
- 若是 62 或更低,说明多了一跳,可能又插入了额外节点或宿主机 netfilter 做了转发。
-
接口
state DOWN/ 无LOWER_UP- 用
ip link set <if> up; - veth 需要两端都 up 才会
LOWER_UP; - 若移入错误 ns,会在该 ns 里找不到,宿主机也查不到(需在目标 ns 查询)。
- 用
-
Cannot create namespace file ... File exists- 表示命名空间已存在;用
ip netns list确认即可。并不影响后续。
- 表示命名空间已存在;用
11)对你的验证结果的“对照判定”(一眼看懂)
| 验证点 | 你看到的关键返回值 | 解释 | 是否达标 |
|---|---|---|---|
| 命名空间存在 | router / ns1 / ns2 |
三个 ns 就绪 | ✅ |
| veth 初始状态 | <...,M-DOWN> state DOWN |
刚创建,未 up,未连通 | ✅(随后会 up) |
| 移入 ns | 宿主机查不到,ns 内可见 | 已成功移入目标 ns | ✅ |
| 配置 IP | inet x.x.x.x/24 |
地址/掩码正确 | ✅ |
| 接口 up | <...,UP,LOWER_UP> |
两端均 up,链路就绪 | ✅ |
| 路由转发 | net.ipv4.ip_forward = 1 |
路由器具备转发功能 | ✅ |
| 路由表 | default via ... + 直连路由 |
默认网关与直连均正确 | ✅ |
| 跨网段 ping | ttl=63、0% 丢包 |
经过 1 跳 router 转发 | ✅ |
| 同网段 ping 网关 | ttl=64 |
直连,无减 TTL | ✅ |
结论
- 你每一步验证返回值都符合预期:接口状态从
DOWN/M-DOWN→UP/LOWER_UP,路由器转发开启为1,路由表正确,跨网段 TTL=63 与 同网段 TTL=64 的对照强力佐证了三层转发路径与直连路径的差别。
八、结果和验证
root@ubuntu24:/home/keson# sudo ip netns exec ns1 ping -c 3 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.039 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.038 ms
--- 192.168.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2036ms
rtt min/avg/max/mdev = 0.027/0.034/0.039/0.005 ms
root@ubuntu24:/home/keson# sudo ip netns exec ns2 ping -c 3 192.168.2.1
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=0.028 ms
64 bytes from 192.168.2.1: icmp_seq=2 ttl=64 time=0.036 ms
64 bytes from 192.168.2.1: icmp_seq=3 ttl=64 time=0.040 ms
--- 192.168.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2048ms
rtt min/avg/max/mdev = 0.028/0.034/0.040/0.005 ms
root@ubuntu24:/home/keson# sudo ip netns exec ns1 ping -c 3 192.168.2.10
PING 192.168.2.10 (192.168.2.10) 56(84) bytes of data.
64 bytes from 192.168.2.10: icmp_seq=1 ttl=63 time=0.031 ms
64 bytes from 192.168.2.10: icmp_seq=2 ttl=63 time=0.050 ms
64 bytes from 192.168.2.10: icmp_seq=3 ttl=63 time=0.049 ms
--- 192.168.2.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2065ms
rtt min/avg/max/mdev = 0.031/0.043/0.050/0.008 ms
root@ubuntu24:/home/keson# sudo ip netns exec ns2 ping -c 3 192.168.1.10
PING 192.168.1.10 (192.168.1.10) 56(84) bytes of data.
64 bytes from 192.168.1.10: icmp_seq=1 ttl=63 time=0.044 ms
64 bytes from 192.168.1.10: icmp_seq=2 ttl=63 time=0.049 ms
64 bytes from 192.168.1.10: icmp_seq=3 ttl=63 time=0.049 ms
--- 192.168.1.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2033ms
rtt min/avg/max/mdev = 0.044/0.047/0.049/0.002 ms
keson@ubuntu24:~$ sudo ip netns exec ns1 tcpdump -i veth1-ns1 -nn -l -v icmp
tcpdump: listening on veth1-ns1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
00:59:28.076528 IP (tos 0x0, ttl 64, id 59581, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.1.1: ICMP echo request, id 5672, seq 1, length 64
00:59:28.076545 IP (tos 0x0, ttl 64, id 40486, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.1 > 192.168.1.10: ICMP echo reply, id 5672, seq 1, length 64
00:59:29.090051 IP (tos 0x0, ttl 64, id 60281, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.1.1: ICMP echo request, id 5672, seq 2, length 64
00:59:29.090072 IP (tos 0x0, ttl 64, id 40814, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.1 > 192.168.1.10: ICMP echo reply, id 5672, seq 2, length 64
00:59:30.114207 IP (tos 0x0, ttl 64, id 61026, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.1.1: ICMP echo request, id 5672, seq 3, length 64
00:59:30.114227 IP (tos 0x0, ttl 64, id 40839, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.1 > 192.168.1.10: ICMP echo reply, id 5672, seq 3, length 64
01:00:15.344266 IP (tos 0x0, ttl 64, id 30699, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 1, length 64
01:00:15.344289 IP (tos 0x0, ttl 63, id 22249, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 1, length 64
01:00:16.386388 IP (tos 0x0, ttl 64, id 31026, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 2, length 64
01:00:16.386417 IP (tos 0x0, ttl 63, id 22952, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 2, length 64
01:00:17.409830 IP (tos 0x0, ttl 64, id 32004, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 3, length 64
01:00:17.409860 IP (tos 0x0, ttl 63, id 23748, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 3, length 64
01:00:20.176476 IP (tos 0x0, ttl 63, id 24968, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 1, length 64
01:00:20.176484 IP (tos 0x0, ttl 64, id 32091, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 1, length 64
01:00:21.185957 IP (tos 0x0, ttl 63, id 25487, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 2, length 64
01:00:21.185968 IP (tos 0x0, ttl 64, id 32639, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 2, length 64
01:00:22.210169 IP (tos 0x0, ttl 63, id 25550, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 3, length 64
01:00:22.210179 IP (tos 0x0, ttl 64, id 32920, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 3, length 64
root@ubuntu24:/home/keson# sudo ip netns exec router tcpdump -i any -nn -l -v icmp
tcpdump: data link type LINUX_SLL2
tcpdump: listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
00:59:28.076532 veth1-router In IP (tos 0x0, ttl 64, id 59581, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.1.1: ICMP echo request, id 5672, seq 1, length 64
00:59:28.076544 veth1-router Out IP (tos 0x0, ttl 64, id 40486, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.1 > 192.168.1.10: ICMP echo reply, id 5672, seq 1, length 64
00:59:29.090056 veth1-router In IP (tos 0x0, ttl 64, id 60281, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.1.1: ICMP echo request, id 5672, seq 2, length 64
00:59:29.090071 veth1-router Out IP (tos 0x0, ttl 64, id 40814, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.1 > 192.168.1.10: ICMP echo reply, id 5672, seq 2, length 64
00:59:30.114212 veth1-router In IP (tos 0x0, ttl 64, id 61026, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.1.1: ICMP echo request, id 5672, seq 3, length 64
00:59:30.114226 veth1-router Out IP (tos 0x0, ttl 64, id 40839, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.1 > 192.168.1.10: ICMP echo reply, id 5672, seq 3, length 64
01:00:07.937478 veth2-router In IP (tos 0x0, ttl 64, id 36745, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.2.1: ICMP echo request, id 5916, seq 1, length 64
01:00:07.937486 veth2-router Out IP (tos 0x0, ttl 64, id 56364, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.1 > 192.168.2.10: ICMP echo reply, id 5916, seq 1, length 64
01:00:08.962506 veth2-router In IP (tos 0x0, ttl 64, id 36904, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.2.1: ICMP echo request, id 5916, seq 2, length 64
01:00:08.962519 veth2-router Out IP (tos 0x0, ttl 64, id 56722, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.1 > 192.168.2.10: ICMP echo reply, id 5916, seq 2, length 64
01:00:09.985933 veth2-router In IP (tos 0x0, ttl 64, id 37627, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.2.1: ICMP echo request, id 5916, seq 3, length 64
01:00:09.985947 veth2-router Out IP (tos 0x0, ttl 64, id 57314, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.1 > 192.168.2.10: ICMP echo reply, id 5916, seq 3, length 64
01:00:15.344269 veth1-router In IP (tos 0x0, ttl 64, id 30699, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 1, length 64
01:00:15.344275 veth2-router Out IP (tos 0x0, ttl 63, id 30699, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 1, length 64
01:00:15.344285 veth2-router In IP (tos 0x0, ttl 64, id 22249, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 1, length 64
01:00:15.344288 veth1-router Out IP (tos 0x0, ttl 63, id 22249, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 1, length 64
01:00:16.386393 veth1-router In IP (tos 0x0, ttl 64, id 31026, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 2, length 64
01:00:16.386401 veth2-router Out IP (tos 0x0, ttl 63, id 31026, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 2, length 64
01:00:16.386413 veth2-router In IP (tos 0x0, ttl 64, id 22952, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 2, length 64
01:00:16.386416 veth1-router Out IP (tos 0x0, ttl 63, id 22952, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 2, length 64
01:00:17.409835 veth1-router In IP (tos 0x0, ttl 64, id 32004, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 3, length 64
01:00:17.409843 veth2-router Out IP (tos 0x0, ttl 63, id 32004, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 3, length 64
01:00:17.409856 veth2-router In IP (tos 0x0, ttl 64, id 23748, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 3, length 64
01:00:17.409859 veth1-router Out IP (tos 0x0, ttl 63, id 23748, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 3, length 64
01:00:20.176467 veth2-router In IP (tos 0x0, ttl 64, id 24968, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 1, length 64
01:00:20.176473 veth1-router Out IP (tos 0x0, ttl 63, id 24968, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 1, length 64
01:00:20.176486 veth1-router In IP (tos 0x0, ttl 64, id 32091, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 1, length 64
01:00:20.176488 veth2-router Out IP (tos 0x0, ttl 63, id 32091, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 1, length 64
01:00:21.185947 veth2-router In IP (tos 0x0, ttl 64, id 25487, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 2, length 64
01:00:21.185956 veth1-router Out IP (tos 0x0, ttl 63, id 25487, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 2, length 64
01:00:21.185969 veth1-router In IP (tos 0x0, ttl 64, id 32639, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 2, length 64
01:00:21.185972 veth2-router Out IP (tos 0x0, ttl 63, id 32639, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 2, length 64
01:00:22.210159 veth2-router In IP (tos 0x0, ttl 64, id 25550, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 3, length 64
01:00:22.210168 veth1-router Out IP (tos 0x0, ttl 63, id 25550, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 3, length 64
01:00:22.210180 veth1-router In IP (tos 0x0, ttl 64, id 32920, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 3, length 64
01:00:22.210183 veth2-router Out IP (tos 0x0, ttl 63, id 32920, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 3, length 64
root@ubuntu24:/home/keson# sudo ip netns exec ns2 tcpdump -i veth2-ns2 -nn -l -v icmp
tcpdump: listening on veth2-ns2, link-type EN10MB (Ethernet), snapshot length 262144 bytes
01:00:07.937474 IP (tos 0x0, ttl 64, id 36745, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.2.1: ICMP echo request, id 5916, seq 1, length 64
01:00:07.937487 IP (tos 0x0, ttl 64, id 56364, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.1 > 192.168.2.10: ICMP echo reply, id 5916, seq 1, length 64
01:00:08.962501 IP (tos 0x0, ttl 64, id 36904, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.2.1: ICMP echo request, id 5916, seq 2, length 64
01:00:08.962520 IP (tos 0x0, ttl 64, id 56722, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.1 > 192.168.2.10: ICMP echo reply, id 5916, seq 2, length 64
01:00:09.985927 IP (tos 0x0, ttl 64, id 37627, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.2.1: ICMP echo request, id 5916, seq 3, length 64
01:00:09.985948 IP (tos 0x0, ttl 64, id 57314, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.1 > 192.168.2.10: ICMP echo reply, id 5916, seq 3, length 64
01:00:15.344276 IP (tos 0x0, ttl 63, id 30699, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 1, length 64
01:00:15.344285 IP (tos 0x0, ttl 64, id 22249, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 1, length 64
01:00:16.386402 IP (tos 0x0, ttl 63, id 31026, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 2, length 64
01:00:16.386412 IP (tos 0x0, ttl 64, id 22952, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 2, length 64
01:00:17.409844 IP (tos 0x0, ttl 63, id 32004, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo request, id 5963, seq 3, length 64
01:00:17.409855 IP (tos 0x0, ttl 64, id 23748, offset 0, flags [none], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo reply, id 5963, seq 3, length 64
01:00:20.176463 IP (tos 0x0, ttl 64, id 24968, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 1, length 64
01:00:20.176489 IP (tos 0x0, ttl 63, id 32091, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 1, length 64
01:00:21.185942 IP (tos 0x0, ttl 64, id 25487, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 2, length 64
01:00:21.185973 IP (tos 0x0, ttl 63, id 32639, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 2, length 64
01:00:22.210153 IP (tos 0x0, ttl 64, id 25550, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.2.10 > 192.168.1.10: ICMP echo request, id 5997, seq 3, length 64
01:00:22.210184 IP (tos 0x0, ttl 63, id 32920, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.10 > 192.168.2.10: ICMP echo reply, id 5997, seq 3, length 64
一、Ping 输出解读
1)同网段:ns1 → 192.168.1.1 与 ns2 → 192.168.2.1
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.027 ms
...
rtt min/avg/max/mdev = 0.027/0.034/0.039/0.005 ms
- 目标:各自命名空间内的“网关口”(router 的直连接口)。
- ttl=64:回包由 router 自身产生(本机回应),未经过转发,因此 TTL 是默认初始值 64。
- 时延 ~0.03 ms:命名空间 + veth 内核路径,极短延迟,符合预期。
- 0% packet loss:无丢包,链路与 IP 配置正确。
结论:本地子网连通,router 两侧接口工作正常;此类 ping 不涉及三层转发,TTL 不会被减 1。
2)跨网段:ns1 ↔ ns2
ns1 → 192.168.2.10: ttl=63
ns2 → 192.168.1.10: ttl=63
- ttl=63:Linux 发包默认 TTL=64,跨一跳路由器后 TTL 减 1 → 63。
- 对称性:两个方向相同,说明双向经 router 转发各 1 跳。
- 时延 ~0.03–0.05 ms:同样是内核级虚拟路径,符合预期。
结论:跨子网三层转发成功,且路径只有 1 跳(router)。
二、ns1 上的 tcpdump(veth1-ns1)逐行要点
示例片段:
IP (tos 0x0, ttl 64, id 59581, flags [DF], length 84) 192.168.1.10 > 192.168.1.1: ICMP echo request id 5672 seq 1
IP (tos 0x0, ttl 64, id 40486, flags [none], length 84) 192.168.1.1 > 192.168.1.10: ICMP echo reply id 5672 seq 1
...
IP (tos 0x0, ttl 64, id 30699, flags [DF], length 84) 192.168.1.10 > 192.168.2.10: ICMP echo request id 5963 seq 1
IP (tos 0x0, ttl 63, id 22249, flags [none], length 84) 192.168.2.10 > 192.168.1.10: ICMP echo reply id 5963 seq 1
ttl 64(本机发出):ns1 发出的请求默认 TTL=64。ttl 63(跨网段收到的回复):从 ns2 返回的应答在 router 处 TTL 减 1,因此 ns1 看到 63。flags [DF]:IP 头 Don’t Fragment 置位;Linux 进行 PMTU 发现的默认行为。用 84 字节不会触发分片,这里仅表明 DF 位为 1。length 84:IP 总长度 = 20(IP头) + 8(ICMP头) + 56(默认 ping 负载)= 84。id 59581/40486/...(IP Identification):这是 IP 层的 ID,由发送主机的 IP 栈生成;请求与应答来自不同主机,因此 ID 序列各自独立。ICMP id 5963, seq N:这是 ICMP 层的标识与序号,用于把请求和回应配对(同一对话中 id 不变,seq 递增)。
关键信号:**同网段的回包 TTL=64;跨网段的回包 TTL=63。**这正是“是否经过路由器”的直接证据。
三、router 上的 tcpdump(-i any)逐行要点
示例片段(已标出接口与方向):
veth1-router In IP ... ttl 64 ... 192.168.1.10 > 192.168.2.10: ICMP echo request id 5963 seq 1
veth2-router Out IP ... ttl 63 ... 192.168.1.10 > 192.168.2.10: ICMP echo request id 5963 seq 1
veth2-router In IP ... ttl 64 ... 192.168.2.10 > 192.168.1.10: ICMP echo reply id 5963 seq 1
veth1-router Out IP ... ttl 63 ... 192.168.2.10 > 192.168.1.10: ICMP echo reply id 5963 seq 1
-
vethX-router In/Out:Linux cooked capture(SLL2)会标注**“相对该接口的入/出方向”**。In表示这个包是从该接口收到的(进入 router);Out表示这个包是从该接口发出的(离开 router)。
-
TTL 的变化:
- 入 veth1 时
ttl 64→ 出 veth2 时ttl 63; - 入 veth2 时
ttl 64→ 出 veth1 时ttl 63。
→ 清晰地看到路由器在转发时把 TTL 减 1。
- 入 veth1 时
-
IP 头
id的保留:注意这两行(同一个请求包)——In ... id 30699 ... Out ... id 30699 ...转发不会改变 IP Identification(除非发生分片),因此 In/Out 的 IP ID 相同,证明这是同一数据包被转发。
-
同网段 ping 网关(例如 ns1→192.168.1.1)时,你看到:
veth1-router In的 echo request(目标就是 router 自己);- 随后
veth1-router Out的 echo reply(由 router 产生的新包,默认 TTL=64)。
→ 这不涉及跨接口转发,只是本机应答。
关键信号:In→Out 成对出现 + TTL 从 64 变 63 + IP ID 不变 = “这是同一个 IP 包被路由器三层转发”的铁证。
四、ns2 上的 tcpdump(veth2-ns2)逐行要点
示例片段:
IP ... ttl 63 ... 192.168.1.10 > 192.168.2.10: ICMP echo request id 5963 seq 1
IP ... ttl 64 ... 192.168.2.10 > 192.168.1.10: ICMP echo reply id 5963 seq 1
- 请求到达 ns2 时 ttl=63:表明此请求从 ns1 来,经 router 一跳;
- ns2 发出的回包 ttl=64:回包的初始 TTL;随后在 router 转发回 ns1 时会变为 63(你在 router 与 ns1 的抓包中已经看到了)。
关键信号:到达端看到的跨网段请求 TTL=63,与 ns1/route 抓包共同构成完整的路径链条。
五、字段速查表
| 字段 | 出现位置 | 含义 | 与本实验的关系 |
|---|---|---|---|
ttl |
IP 头 | 生存时间(每经一跳减 1) | 同网段回包=64;跨网段=63(经 1 跳 router) |
flags [DF] |
IP 头 | 不分片标志 | Linux PMTU 默认置位;本实验报文很小,不会分片 |
length 84 |
IP 头 | IP 总长 | 20(IP) + 8(ICMP) + 56(负载) |
id <number> |
IP 头 | IP Identification | 转发过程中 保持不变;不同主机各自递增 |
ICMP id/seq |
ICMP 头 | 会话标识与序号 | 匹配 request/reply 对 |
In/Out(SLL2) |
router 抓包 | 相对接口方向 | 证明入/出路由器的每一步 |
接口名 vethX-router |
router 抓包 | 入/出具体哪条链路 | 证明“从 ns1 侧进、从 ns2 侧出”(及反向) |
六、结论
- 同网段连通:
ns1→192.168.1.1与ns2→192.168.2.1的回包 TTL=64,表明报文在路由器本机终止与产生,无路由跳转。 - 跨网段转发:
ns1↔ns2的请求/应答在路由器处出现 成对的 In/Out 记录,TTL 从 64 变 63,且 IP Identification 在转发前后保持一致,证实同一 IP 包完成了一跳三层转发。
七、TTL 变化:
TTL 变化:
更多推荐


所有评论(0)