Linux 网络实验(1)
Linux网络命名空间实验与深度分析 本实验通过创建虚拟网络环境,验证了Linux网络命名空间的核心功能。实验一构建了两台直连主机,验证了网络隔离性和独立防火墙规则;实验二模拟了三节点路由拓扑,实现了跨网段通信和NAT转换。关键发现包括: 每个命名空间拥有完全独立的协议栈、路由表和防火墙规则 通过veth pair可实现命名空间间的虚拟直连 路由转发需同时配置IP地址、路由规则和内核转发参数 NA
实验一:你好,Namespace (两台主机直连)
第一个实验,我们要创建两个虚拟主机(ns1 和 ns2),并通过一对“虚拟网线”(Veth Pair)将它们连接起来,模拟两台电脑直连的场景。
1. 拓扑结构
+-----------------+ +-----------------+
| Namespace: ns1 | | Namespace: ns2 |
| | | |
| IP: 192.168.1.1 | <---------> | IP: 192.168.1.2 |
| (veth1) | | (veth2) |
+-----------------+ +-----------------+
2. 实战命令
第一步:创建虚拟环境
# 创建两个命名空间
sudo ip netns add ns1
sudo ip netns add ns2
# 创建一对虚拟网线 (veth pair)
sudo ip link add veth1 type veth peer name veth2
# 把网线两头分别插到两个房间里
sudo ip link set veth1 netns ns1
sudo ip link set veth2 netns ns2
第二步:配置 IP 并启动
注意:新创建的网卡默认是 Down 状态,需要手动 Up。
# 配置 ns1
sudo ip netns exec ns1 ip addr add 192.168.1.1/24 dev veth1
sudo ip netns exec ns1 ip link set veth1 up
sudo ip netns exec ns1 ip link set lo up
# 配置 ns2
sudo ip netns exec ns2 ip addr add 192.168.1.2/24 dev veth2
sudo ip netns exec ns2 ip link set veth2 up
sudo ip netns exec ns2 ip link set lo up
第三步:连通性测试
# 从 ns1 ping ns2
sudo ip netns exec ns1 ping 192.168.1.2
如果看到回复,说明物理(虚拟)链路通了!
3. Iptables 防火墙实验
既然有了独立的协议栈,我们就可以测试防火墙规则。假设我们要禁止 ns1 访问 ns2。
我们在 ns2 上配置 iptables:
# 在 ns2 的 INPUT 链(入口)丢弃来自 192.168.1.1 的 ICMP 包
sudo ip netns exec ns2 iptables -A INPUT -s 192.168.1.1 -p icmp -j DROP
再次尝试 Ping,你会发现请求被阻塞了。这证明了每一个 Namespace 确实拥有独立的 iptables 规则表。
实验二:路由器 (Routing & NAT)
在真实世界中,我们通常通过路由器访问不同网段的主机。现在我们来模拟一个经典的三节点拓扑:内网客户端 -> 路由器 -> 外网服务器。
1. 拓扑结构
Namespace: client Namespace: router Namespace: server
+---------------------+ +-----------------------+ +---------------------+
| (veth-c) | | (veth-rc) (veth-rs) | | (veth-s) |
| IP: 10.1.1.2 | <--> | 10.1.1.1 10.2.2.1 | <--> | IP: 10.2.2.2 |
| GW: 10.1.1.1 | | | | GW: 10.2.2.1 |
+---------------------+ +-----------------------+ +---------------------+
Subnet A Router Subnet B
(10.1.1.0/24) (10.2.2.0/24)
2. 核心配置步骤
第一步:环境搭建
# 清理旧环境
sudo ip netns del ns1 2>/dev/null; sudo ip netns del ns2 2>/dev/null
# 创建三个 Namespace
sudo ip netns add client
sudo ip netns add router
sudo ip netns add server
# 创建并连接网线
sudo ip link add veth-c type veth peer name veth-rc
sudo ip link set veth-c netns client
sudo ip link set veth-rc netns router
sudo ip link add veth-rs type veth peer name veth-s
sudo ip link set veth-rs netns router
sudo ip link set veth-s netns server
第二步:配置 IP 和 默认网关 (Routing)
这是最关键的一步,Client 和 Server 必须知道“不知道往哪发的时候,就发给 Router”。
# --- Client ---
sudo ip netns exec client ip link set lo up
sudo ip netns exec client ip link set veth-c up
sudo ip netns exec client ip addr add 10.1.1.2/24 dev veth-c
# 设置网关指向 Router 左手
sudo ip netns exec client ip route add default via 10.1.1.1
# --- Router (两张网卡) ---
sudo ip netns exec router ip link set lo up
sudo ip netns exec router ip link set veth-rc up
sudo ip netns exec router ip addr add 10.1.1.1/24 dev veth-rc
sudo ip netns exec router ip link set veth-rs up
sudo ip netns exec router ip addr add 10.2.2.1/24 dev veth-rs
# --- Server ---
sudo ip netns exec server ip link set lo up
sudo ip netns exec server ip link set veth-s up
sudo ip netns exec server ip addr add 10.2.2.2/24 dev veth-s
# 设置网关指向 Router 右手
sudo ip netns exec server ip route add default via 10.2.2.1
第三步:开启 Router 的转发功能
Linux 默认出于安全考虑是禁止转发数据包的。我们要手动开启它,让它变成真正的路由器。
sudo ip netns exec router sysctl -w net.ipv4.ip_forward=1
此时,client 已经可以 ping 通 server 了。
3. NAT 魔法:SNAT 与 DNAT
为了模拟真实的家庭上网体验,我们要在 Router 上配置 NAT。
场景 A:内网访问外网 (SNAT / Masquerade)
我们要让 Server 觉得所有流量都是 Router 发的,隐藏 Client 的真实 IP。
# 在 POSTROUTING 链做源地址转换
sudo ip netns exec router iptables -t nat -A POSTROUTING -o veth-rs -j MASQUERADE
验证: 在 server 上抓包 (tcpdump -i veth-s),client 此时 ping server,server 看到的源 IP 将是 10.2.2.1 (Router),而不是 10.1.1.2。
场景 B:外网访问内网 (DNAT / Port Forwarding)
假设 Client 在 8000 端口运行了一个 Web 服务(python3 -m http.server 8000),我们希望 Server 通过访问 Router 的 80 端口来访问它。
# 在 PREROUTING 链做目标地址转换
sudo ip netns exec router iptables -t nat -A PREROUTING -d 10.2.2.1 -p tcp --dport 80 -j DNAT --to-destination 10.1.1.2:8000
验证: Server 执行 curl 10.2.2.1:80,实际上访问到了 Client 的文件服务器。
总结
通过这两个实验,在 Linux 内核中复现了:
- L3 路由转发 (Routing Table)
- 防火墙规则 (Filter Table)
- 地址转换 (NAT Table)
这是为你准备的“深度思考”板块。你可以把它作为博客的**“进阶分析”或“排错与思考”**章节放在实验步骤之后。这部分内容往往是区分“只会敲命令的人”和“真正懂原理的人”的关键。
深度思考与排错分析
1. ARP 的“薛定谔”状态:为什么 Ping 总是在发 ARP,而 TCP 却很安静?
现象描述:
当我们清除 ARP 缓存并开始 ping 另一台主机时,如果你在 Switch 上抓包,会发现一个有趣的现象:
- 使用 Ping (ICMP) 时: 即使网络一直通畅,每隔几秒钟(默认约 5-6 秒),发送端还是会发出一个 ARP Request (“Who has 192.168.1.x?”)。
- 使用 Netcat (TCP) 时: 只要连接建立并持续传输数据,除了最开始的一次 ARP,之后网络非常安静,完全没有后续的 ARP 广播。
深度解析:Linux 的 NUD 状态机
这涉及到 Linux 内核邻居子系统(Neighbor Subsystem)的 NUD (Neighbor Unreachability Detection) 机制。
Linux 的 ARP 缓存是有“保质期”的。当一个 ARP 条目超过 base_reachable_time(默认 30秒)后,状态会变成 STALE (陈旧)。
- 对于 Ping (ICMP): 内核比较保守。虽然收到了 Ping 回包,但它认为这不足以完全证明链路层的双向可达性。当内核再次需要通过这个 STALE 的地址发包时,会进入 DELAY -> PROBE 状态,强行发送 ARP 请求来再次确认 MAC 地址。
- 对于 TCP: TCP 是面向连接的协议,具有 ACK (确认应答) 机制。当 TCP 连接正常传输时,接收到的 ACK 包会被内核视为**“上层协议确认” (Upper Layer Protocol Confirmation)**。TCP 协议栈会告诉 ARP 子系统:“嘿,我刚收到确认,这个人还活着!”。于是 ARP 状态直接从 STALE 刷新回 REACHABLE,完全跳过了发送 ARP 请求的步骤。
结论: Linux 内核非常智能,它利用 TCP 的 ACK 机制“免费”维护了 ARP 表的活性,从而减少了网络中的广播风暴。
2. NAT 的“自动挡”机制:为什么回包不需要配置 SNAT?
现象描述:
在实验二(DNAT 端口映射)中,我们配置了如下规则:
- DNAT (进站): 访问
Router:80-> 转给Client:8000。 - 我们并没有配置 SNAT (出站): 没有规则告诉 Router,当 Client 回复 Server 时,要把源 IP 改回 Router。
疑问: 既然没配 SNAT,Client 回复给 Server 的包,源 IP 应该是 Client 的内网 IP。Server 收到这个包(源 IP 不匹配),应该会丢弃才对。为什么实验却成功了?
深度解析:Conntrack (连接跟踪)
这就是 Linux “有状态防火墙 (Stateful Firewall)” 的核心魔力。iptables 的 nat 表有一个黄金法则:
NAT 表的规则,只对连接的“第一个数据包”生效。
-
第一阶段 (Request): Server 发出的第一个 SYN 包到达 Router。Router 匹配到 DNAT 规则,修改目标 IP,并且在内核的“连接跟踪表” (
conntrack table) 中记录下这次修改:- “记录:Server(1.2.3.4) 找 Router(5.6.7.8),但我改成了 Client(10.0.0.1)。如果 Client 回话,记得改回去。”
-
第二阶段 (Reply): Client 回复的数据包到达 Router。
- Router 的内核首先检查
conntrack表。 - 它发现这个包属于一个已建立的连接 (ESTABLISHED)。
- 自动操作: 根据之前的记录,内核自动执行反向的 SNAT 操作(把源 IP 改回 Router IP),完全不需要用户手动写 iptables 规则。
- 这个包甚至根本不会去查
nat表。
- Router 的内核首先检查
验证方法:
你可以在 Router 上执行 conntrack -L,会看到类似这样的记录:
tcp ... src=Server dst=Router ... [UNREPLIED] src=Client dst=Server ...
这就是内核默默为你工作的证据。
结论: 在 Linux 中做 NAT,我们只需要关注“去程”,“回程”是全自动的。
没问题!这确实是一个非常关键的知识点,很多初学者(甚至有经验的工程师)容易在这里绕晕。
把这段关于 “SNAT 和 DNAT 是否会打架(冲突)” 的讨论加入到博客的“深度思考”部分,能完美解释为什么我们在同一个路由器上既能配上网(SNAT),又能配端口映射(DNAT)。
以下是为你补充的第三个深度思考板块:
3. SNAT vs DNAT:它们会“打架”吗?(谁先发起谁说了算)
疑问描述:
在实验中,我们在 Router 上同时配置了两条规则:
- SNAT (Masquerade): 为了让内网 Client 能上网。
- DNAT (Port Forwarding): 为了让外网 Server 能访问内网服务。
这引发了一个经典的逻辑陷阱:“当数据包经过路由器时,这两条规则会不会冲突?比如 Server 访问 Client 时,会不会错误的触发了 SNAT 规则?”
深度解析:方向决定一切
答案是绝对不会冲突。Linux 内核通过判断**“是谁先发起的连接”**(Who Pings Who),来决定使用哪套逻辑。
这里的核心原则依然是基于 Conntrack(连接跟踪) 的状态机制:
-
场景 A:Client 主动找 Server (Client Ping Server)
- 动作: Client 发出第一个包 (SYN)。
- 流程: 数据包从内网流向外网。
- 命中规则: 内核检查
POSTROUTING链,匹配到 SNAT 规则。 - 结果: 源 IP 被伪装。内核记录:“这是一个 SNAT 连接”。
- 回包: Server 回复时,内核看到是 SNAT 连接的回包,自动还原目标 IP。DNAT 规则根本不会被触发。
-
场景 B:Server 主动找 Client (Server Ping Router)
- 动作: Server 发出第一个包 (SYN) 访问 Router IP。
- 流程: 数据包从外网流向内网。
- 命中规则: 内核检查
PREROUTING链,匹配到 DNAT 规则。 - 结果: 目标 IP 被修改为 Client IP。内核记录:“这是一个 DNAT 连接”。
- 回包: Client 回复时,内核看到是 DNAT 连接的回包,自动还原源 IP。SNAT 规则被完全无视。
一句话总结:
SNAT 守着出口(负责出去),DNAT 守着入口(负责进来)。它们就像大楼的“出站闸机”和“进站闸机”,互不干扰。谁先发起了握手,谁就定义了这条连接的性质。
更多推荐


所有评论(0)