Hello all,
I did SNAT test as below and found it's strange:

Server-A with ip 192.168.1.99, with kernel 2.6.9-42.EL. An other server-B with ip 192.168.1.98

run below command on Server-A, to SNAT all source ip to 192.168.11.99
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to-source 192.168.11.99

when I tried to ping 192.168.1.98 on server-A, as expected, it failed. because server-B returned ping reply to 192.168.11.99.

but when I tried ping 192.168.1.99 on server-B, it successed. that means the echo-reply to 192.168.1.98 was NOT SNATed to 192.168.11.99 (it's confirmed as I have done tcpdump on server-B and saw the source ip of echo-reply was 192.168.1.99).

I tried TCP connection, it was the same as icmp.


I checked the kernel source code, in "ip_output.c":
279 return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
280 ip_finish_output,
281 !(IPCB(skb)->flags & IPSKB_REROUTED));

so if !(IPCB(skb)->flags & IPSKB_REROUTED) == 0, then ip_finish_output() is excuted, i.e. NF_IP_POST_ROUTING
is not checked. Details is in "include/linux/netfilter.h":
249 #define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) \
250 ({int __ret; \
251 if ((__ret=nf_hook_thresh(pf, hook, &(skb), indev, outdev, okfn, INT_MIN, cond)) == 1)\
252 __ret = (okfn)(skb); \
253 __ret;})

198 static inline int nf_hook_thresh(int pf, unsigned int hook,
199 struct sk_buff **pskb,
200 struct net_device *indev,
201 struct net_device *outdev,
202 int (*okfn)(struct sk_buff *), int thresh,
203 int cond)
204 {
205 if (!cond)
206 return 1;
..........
212 }

IPCB() is defined in include/net/ip.h: #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))

so when !(((struct inet_skb_parm*)((skb)->cb))->flags & IPSKB_REROUTED) == 0, NF_IP_POST_ROUTING is skipped.

It looks like that the local initiative packet(like ping 192.168.1.98 on 192.168.1.99) works as expected, i.e. it's SNATed, or saying the IPCB(skb)->flags is not set as IPSKB_REROUTED. But the echo replied packet is with IPCB(skb)->flags set to IPSKB_REROUTED, thus it skips being SNATed.

I guess the local initiative packet using a fresh allocated skb_buff, so its flag is not set. but the replied packet uses the skb_buff of received packet, which has gone through PREROUTING chain, so the flag is set.

So what's the purpose of doing !(IPCB(skb)->flags & IPSKB_REROUTED) check?(for IPSEC?) and is the above sympton a bug of netfilter?

Regards
Hank