orange723

#iptables - orange723

2 posts

iptables

iptables 有 5 张表:

• filter(默认):决定"放过/丢弃"
• nat:做 NAT,专门改写源/目的 IP 和端口
• mangle:改 TTL、TOS 等
• raw:在 conntrack 之前操作
• security:SELinux 用

阅读全文 →

ipvs nat

orbstack

vm-1: client

d2: caddy * 2

ipvs: lb

apt install -y ipvsadm conntrack tcpdump iproute2 net-tools curl iptables
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.eth0.rp_filter=0

modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe ip_vs_lc

ipvsadm -ln
iptables -t nat -S
ip addr show eth0

实验 A:经典 NAT 模式(不加 SNAT,访问失败)

ipvsadm -C
ipvsadm -A -t 192.168.139.29:80 -s rr
ipvsadm -a -t 192.168.139.29:80 -r 192.168.139.184:80 -m       # -m = NAT (Masq)
ipvsadm -a -t 192.168.139.29:80 -r 192.168.139.184:81 -m
ipvsadm -ln

没有任何 iptables 规则,没有开 vs.conntrack。
tcpdump -i eth0 -s0 -X -nn 'port 80' -w ipvs/not-snat/nat-vm-1-eth0.pcap --print

nat-vm-1-eth0.pcap

tcpdump -i eth0 -s0 -X -nn 'port 80 or port 81' -w ipvs/not-snat/nat-ipvs-eth0.pcap --print

nat-ipvs-eth0.pcap

tcpdump -i eth0 -s0 -X -nn 'port 80 or port 81' -w ipvs/not-snat/nat-d2-eth0.pcap --print

nat-d2-eth0.pcap

$ ipvsadm -lnc
IPVS connection entries
pro expire state       source             virtual            destination
TCP 00:59  SYN_RECV    192.168.139.68:37564 192.168.139.29:80  192.168.139.184:80

能看到一直卡在 SYN_RECV 所以 curl 会卡住

实验 B:NAT + SNAT(FullNAT,工作)

开启 conntrack

iptables -t nat -A POSTROUTING -d 192.168.139.184 -p tcp --dport 80 -j SNAT --to 192.168.139.29
iptables -t nat -A POSTROUTING -d 192.168.139.184 -p tcp --dport 81 -j SNAT --to 192.168.139.29

sysctl -w net.ipv4.vs.conntrack=1
iptables -t nat -S POSTROUTING
ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.139.29:80 rr
  -> 192.168.139.184:80           Masq    1      0          1
  -> 192.168.139.184:81           Masq    1      0          1
tcpdump -i eth0 -s0 -X -nn 'port 80' -w ipvs/snat/nat-vm-1-eth0.pcap --print

nat-vm-1-eth0.pcap

tcpdump -i eth0 -s0 -X -nn 'port 80 or port 81' -w ipvs/snat/nat-ipvs-eth0.pcap --print

nat-ipvs-eth0.pcap

tcpdump -i eth0 -s0 -X -nn 'port 80 or port 81' -w ipvs/snat/nat-d2-eth0.pcap --print

nat-d2-eth0.pcap

conntrack -L
tcp      6 84 TIME_WAIT src=192.168.139.68 dst=192.168.139.29 sport=43856 dport=80 src=192.168.139.184 dst=192.168.139.29 sport=80 dport=43856 [ASSURED] mark=0 use=1
tcp      6 46 TIME_WAIT src=192.168.139.68 dst=192.168.139.29 sport=47380 dport=80 src=192.168.139.184 dst=192.168.139.29 sport=81 dport=47380 [ASSURED] mark=0 use=1
conntrack v1.4.8 (conntrack-tools): 2 flow entries have been shown.

加了规则后链路通了,不过会有新的问题 在 vm-1 抓包能看到发起了重试,这很奇怪

叫 ai 分析了下

根因:net.ipv4.vs.conntrack=1 让 IPVS 跟 conntrack 共存,在连接刚建立的瞬间存在状态机竞争窗口,invalid 状态的包被丢

看 ipvs 视角,前 3 个 GET 的入向包都没有对应的出向(没有转发出去):

18:58:32.335745  ipvs 入向收到 GET(第 2 次重传)
               ← 没有对应的出向!
18:58:32.544XXX  ipvs 入向收到 GET(第 3 次重传)
               ← 也没有出向!
18:58:32.951XXX  ipvs 入向收到 GET(第 4 次重传)
18:58:32.951XXX  ipvs 出向转给 d2  ← 这次才转发!

ipvs 把前 3 个数据包丢了!

为什么?这是 IPVS + conntrack 的一个经典 race condition

T0: SYN              → IPVS 建立连接 → DNAT 到 d2:81 → 转发
  SYN-ACK          → ipvs → 反向 NAT → 回 vm-1
  ACK              → ipvs → 转发到 d2
T0+1ms: GET 数据包到达 ipvs
  │
  ├─ IPVS 已经知道这条 conn 转给 d2:81
  ├─ 但是 conntrack 表里这条连接还在 SYN_RECV 状态?
  │  或者 conntrack 在 GET 这一刻刚好被 d2 的 SYN-ACK 推到 ESTABLISHED,
  │  但 SNAT 状态机短暂混乱
  │
  └─ 结果:包被 conntrack 标记为 invalid 或丢弃
第 2 次重传 (200ms 后):仍然有问题,丢
第 3 次重传 (400ms 后):仍然有问题,丢
第 4 次重传 (800ms 后):状态稳定了,终于通过!

这是开了 net.ipv4.vs.conntrack=1 之后的已知副作用——IPVS 和 conntrack 两套状态机共存,握手完成的瞬间存在竞争窗口。

总结

NAT 模式

client ─请求─► director ──NAT──► RS

client ◄───响应────── director ◄───响应─── RS

                     ↑
                     └ 必经之路!双向流量都过 director

所有包:源/目的 IP 都被改写

DR 模式

client ─请求─► director ──仅改MAC──► RS

client ◄────响应────────────────────  RS
     └─ 直接回,回程不经过 director!

请求包:IP 不变,只换二层目的 MAC
响应包:直接二层送出(看起来像 RS 自己就是 VIP)
阅读全文 →