[NETFILTER]: Redo policy lookups after NAT when neccessary
Patrick McHardy [Sat, 7 Jan 2006 07:05:36 +0000 (23:05 -0800)]
When NAT changes the key used for the xfrm lookup it needs to be done
again. If a new policy is returned in POST_ROUTING the packet needs
to be passed to xfrm4_output_one manually after all hooks were called
because POST_ROUTING is called with fixed okfn (ip_finish_output).

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/net/xfrm.h
net/ipv4/ip_output.c
net/ipv4/netfilter/ip_nat_standalone.c
net/ipv4/xfrm4_output.c

index d6111a2..d09ca0e 100644 (file)
@@ -866,6 +866,7 @@ extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
 extern int xfrm_init_state(struct xfrm_state *x);
 extern int xfrm4_rcv(struct sk_buff *skb);
 extern int xfrm4_output(struct sk_buff *skb);
+extern int xfrm4_output_finish(struct sk_buff *skb);
 extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler);
 extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler);
 extern int xfrm6_rcv_spi(struct sk_buff **pskb, u32 spi);
index 8b1c9bd..59fdac3 100644 (file)
@@ -202,6 +202,11 @@ static inline int ip_finish_output2(struct sk_buff *skb)
 
 static inline int ip_finish_output(struct sk_buff *skb)
 {
+#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
+       /* Policy lookup after SNAT yielded a new policy */
+       if (skb->dst->xfrm != NULL)
+               return xfrm4_output_finish(skb);
+#endif
        if (skb->len > dst_mtu(skb->dst) &&
            !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size))
                return ip_fragment(skb, ip_finish_output2);
index 1bb5089..b518697 100644 (file)
@@ -187,12 +187,30 @@ ip_nat_out(unsigned int hooknum,
           const struct net_device *out,
           int (*okfn)(struct sk_buff *))
 {
+       struct ip_conntrack *ct;
+       enum ip_conntrack_info ctinfo;
+       unsigned int ret;
+
        /* root is playing with raw sockets. */
        if ((*pskb)->len < sizeof(struct iphdr)
            || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
                return NF_ACCEPT;
 
-       return ip_nat_fn(hooknum, pskb, in, out, okfn);
+       ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
+       if (ret != NF_DROP && ret != NF_STOLEN
+           && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) {
+               enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+
+               if (ct->tuplehash[dir].tuple.src.ip !=
+                   ct->tuplehash[!dir].tuple.dst.ip
+#ifdef CONFIG_XFRM
+                   || ct->tuplehash[dir].tuple.src.u.all !=
+                      ct->tuplehash[!dir].tuple.dst.u.all
+#endif
+                   )
+                       return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
+       }
+       return ret;
 }
 
 static unsigned int
@@ -217,7 +235,12 @@ ip_nat_local_fn(unsigned int hooknum,
                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 
                if (ct->tuplehash[dir].tuple.dst.ip !=
-                   ct->tuplehash[!dir].tuple.src.ip)
+                   ct->tuplehash[!dir].tuple.src.ip
+#ifdef CONFIG_XFRM
+                   || ct->tuplehash[dir].tuple.dst.u.all !=
+                      ct->tuplehash[dir].tuple.src.u.all
+#endif
+                   )
                        return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
        }
        return ret;
index 160c488..d4df0dd 100644 (file)
@@ -152,7 +152,7 @@ error_nolock:
        goto out_exit;
 }
 
-static int xfrm4_output_finish(struct sk_buff *skb)
+int xfrm4_output_finish(struct sk_buff *skb)
 {
        int err;