-
各节点 OS 版本:Red Hat Enterprise Linux release 8.2 (Ootpa)
-
故障描述:内网的 server 节点需要访问外部 Internet,但在多级路由转发过程中发现无法访问外部 Internet,因此进入到各个 Linux 路由节点分析内核静态路由表(kernel static route table)以及对指定网口进行抓包分析。
-
👉 通过该示例进一步理解
firewalld
、iptables
与nftables
的相互关系,以及配置 SNAT 与 MASQUERADE 的方法。 -
故障节点的架构与抓包示意:
-
如上所示,server 节点访问 Internet 需要通过 Linux-router1 节点、Linux-router2 节点与 foundation 节点的路由转发。
-
Linux-router1 节点排查:
👉 使用 firewalld 配置 SNAT 规则。
-
该节点的 IP 与路由表,如下所示:
$ ip -br a s lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 172.25.250.254/24 fe80::3a50:cfa:5e7:a805/64 eth1 UP 172.25.252.250/24 fe80::783e:7d62:966a:66b9/64 $ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.25.252.254 0.0.0.0 UG 101 0 0 eth1 172.25.250.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0 172.25.252.0 0.0.0.0 255.255.255.0 U 101 0 0 eth1
-
该节点由 Linux 实现路由转发,因此从该节点 eth0 接收的
S:172.25.250.15, D:192.168.110.1
的数据包通过 iptables 的FORWARD
链经由 kernel 转发(该过程需要启用net.ipv4.ip_forward = 1
内核参数),再通过路由决定(routing decision)与POSTROUTING
链后至 eth1,最后数据包经由 eth1 发出。$ sysctl --value net.ipv4.ip_forward 1 # 该内核参数位于 /proc/sys/net/ipv4/ip_forward
-
若 eth1 不配置 SNAT,那么从 eth1 发出的数据包依然为
S:172.25.250.15, D:192.168.110.1
,在 Linux-router1 与 Linux-router2 之间三层互通的情况下,Linux-router2 的 eth0 也接收到S:172.25.250.15, D:192.168.110.1
数据包。 -
尝试从 Linux-router1 的 eth1 抓包验证,如下所示:
$ tcpdump -n -i eth1 -p icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes 01:17:10.718270 IP 172.25.250.15 > 192.168.110.1: ICMP echo request, id 43854, seq 17, length 64 01:17:11.742314 IP 172.25.250.15 > 192.168.110.1: ICMP echo request, id 43854, seq 18, length 64 01:17:12.767261 IP 172.25.250.15 > 192.168.110.1: ICMP echo request, id 43854, seq 19, length 64 01:17:13.790068 IP 172.25.250.15 > 192.168.110.1: ICMP echo request, id 43854, seq 20, length 64 01:17:14.812036 IP 172.25.250.15 > 192.168.110.1: ICMP echo request, id 43854, seq 21, length 64 01:17:15.838103 IP 172.25.250.15 > 192.168.110.1: ICMP echo request, id 43854, seq 22, length 64
💥 故障点:抓包结果提示,只有从 172.25.250.15 不断发出的请求包(
request
),而无任何目标回包的响应(reply
)。 -
因此,在该路由节点上需配置
SNAT
或MASQUERADE
将源地址从172.25.250.15
转换为 eth1 的172.25.252.250
后再发出,可使用如下命令:$ systemctl is-active firewalld.service active # 查看 firewalld 守护进程的状态 ### 方法 1 ### $ firewall-cmd --permanent --zone=trusted --change-interface=eth0 # 将 eth0 永久添加至 trusted zone(允许所有的进入流量)中 $ firewall-cmd --permanent --zone=external --change-interface=eth1 # 将 eth1 永久添加至 external zone 中 # 注意: # 1. 将网络接口添加至指定的 zone 中,该 zone 即可激活(active)。 # 2. external zone 中默认启用 MASQUERADE(masquerade: yes),因此从该网口发出的 # 数据包将进行 SNAT。 ### 方法 2 ### $ firewall-cmd --permanent --zone=trusted --change-interface=eth0 # 将 eth0 永久添加至 trusted zone(允许所有的进入流量)中 $ firewall-cmd --permanent --zone=external --change-interface=eth1 $ firewall-cmd --permanent --zone=external --remove-masquerade # 尝试使用单独的 SNAT,因此禁用 MASQUERADE。 $ firewall-cmd --permanent --direct --passthrough ipv4 \ -t nat -A POSTROUTING -s 172.25.250.0/24 -j SNAT --to-source 172.25.252.250 # 使用 iptables 的类似的语法添加 SNAT 规则
通过上述命令其中任意一种方法即可实现 SNAT。由于在 RHEL 8 中默认使用
firewalld
守护进程作为nftables
的前端管理程序,nftables 默认作为用户空间层(user space)firewalld 的后端实现(内核空间层 kernel space 由netfilter
实现),因此,在 nftables 的 NAT 表中可查看 firewalld 配置的 SNAT 规则,如下所示:$ nft list table nat table ip nat { ... chain POSTROUTING { type nat hook postrouting priority srcnat; policy accept; ip saddr 172.25.250.0/24 counter packets 8 bytes 608 snat to 172.25.252.250 } ... } # 以上的规则通过方法 2 实现的 SNAT 规则的添加
-
-
Linux-router2 节点排查:
👉 使用 iptables 配置 SNAT 规则。
-
该节点的 IP 与路由表,如下所示:
$ ip -br a s lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 172.25.254.254/24 172.25.253.254/24 172.25.252.254/24 fe80::5054:ff:fe00:fe/64 eth1 UP $ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.25.254.250 0.0.0.0 UG 0 0 0 eth0 172.25.252.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 172.25.253.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0 172.25.254.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
-
根据路由表所示,从 Linux-router1 的 eth1 发出的
S:172.25.252.250, D:192.168.110.1
数据包可被 Linux-router2 的 eth0 接收,经过路由决定后需进行 SNAT 才能将其转换为S:172.25.254.254, D:192.168.110.1
再转发至 foundation 节点,因此该节点上必须配置 SNAT 规则,经查该节点上已存在相应规则,无故障点出现:$ nft list table nat table ip nat { ... chain POSTROUTING { type nat hook postrouting priority srcnat; policy accept; ip saddr 172.25.252.0/24 counter packets 14 bytes 1176 snat to 172.25.254.254 } ... } # 查看 nftables 的 NAT 表 POSTROUTING 链中的规则
-
若需配置上述规则,可使用如下方法:
$ iptables -t nat -A POSTROUTING -s 172.25.252.0/24 -j SNAT --to-source 172.25.254.254 # 使用 iptables 添加 SNAT 规则
🤘 注意:iptables 与 nftables 均使用 kernel 的 netfilter,因此两种方式均可配置与查看相应的规则!
-
-
foundation 节点排查:
-
该节点的 IP 与路由表,如下所示:
$ ip -br a s lo UNKNOWN 127.0.0.1/8 ::1/128 ens160 UP ens224 UP br2 UP 192.168.110.239/24 fe80::20c:29ff:fe84:7c70/64 br0 UP 172.25.254.250/24 172.25.0.250/24 fe80::f078:6fff:fef9:21a/64 br3 DOWN fe80::a808:13ff:fee9:35a2/64 ... $ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.110.1 0.0.0.0 UG 0 0 0 br2 172.25.0.0 0.0.0.0 255.255.255.0 U 425 0 0 br0 172.25.0.0 172.25.254.254 255.255.0.0 UG 425 0 0 br0 172.25.250.0 0.0.0.0 255.255.255.0 U 0 0 0 privbr0 172.25.254.0 0.0.0.0 255.255.255.0 U 425 0 0 br0 192.168.110.0 0.0.0.0 255.255.255.0 U 426 0 0 br2 192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0 192.168.123.0 0.0.0.0 255.255.255.0 U 0 0 0 br1
-
可通过前文相同的方法,查看该节点是否存在相应的 SNAT 规则,如下所示,无故障点出现:
$ nft list table nat table ip nat { ... chain POSTROUTING { ... oifname "br2" ip saddr 172.25.254.0/24 counter packets 1809 bytes 136485 masquerade } ... }
-
-
除 Linux-router1 节点上未配置 SNAT 规则外其余节点均配置正确。
-
故障点排除后可重新从 server 节点对外发起访问,并在各个节点的指定网口进行抓包即可获得与图中一致的结果。