[NETFILTER]: Handle NAT in IPsec policy checks
authorPatrick McHardy <kaber@trash.net>
Sat, 7 Jan 2006 07:06:30 +0000 (23:06 -0800)
committerDavid S. Miller <davem@sunset.davemloft.net>
Sat, 7 Jan 2006 20:57:37 +0000 (12:57 -0800)
Handle NAT of decapsulated IPsec packets by reconstructing the struct flowi
of the original packet from the conntrack information for IPsec policy
checks.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netfilter.h
net/dccp/ipv4.c
net/ipv4/netfilter.c
net/ipv4/netfilter/ip_nat_standalone.c
net/xfrm/xfrm_policy.c

index 79bb977afeace0a7c05114f988521942c9e69367..84506dfa1f37dd8635828adee6edeac980c85347 100644 (file)
@@ -274,6 +274,20 @@ struct nf_queue_rerouter {
 extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
 extern int nf_unregister_queue_rerouter(int pf);
 
+#include <net/flow.h>
+extern void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *);
+
+static inline void
+nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family)
+{
+#ifdef CONFIG_IP_NF_NAT_NEEDED
+       void (*decodefn)(struct sk_buff *, struct flowi *);
+
+       if (family == AF_INET && (decodefn = ip_nat_decode_session) != NULL)
+               decodefn(skb, fl);
+#endif
+}
+
 #ifdef CONFIG_PROC_FS
 #include <linux/proc_fs.h>
 extern struct proc_dir_entry *proc_net_netfilter;
@@ -282,6 +296,8 @@ extern struct proc_dir_entry *proc_net_netfilter;
 #else /* !CONFIG_NETFILTER */
 #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
 static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
+static inline void
+nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family) {}
 #endif /*CONFIG_NETFILTER*/
 
 #endif /*__KERNEL__*/
index 23ba177c11506a83b46fffde00f2d4bb84882971..00f983226672a6c70aca4a67368cceae2d7a118b 100644 (file)
@@ -986,6 +986,7 @@ int dccp_v4_rcv(struct sk_buff *skb)
 
        if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
+       nf_reset(skb);
 
        return sk_receive_skb(sk, skb);
 
@@ -1099,7 +1100,6 @@ int dccp_v4_destroy_sock(struct sock *sk)
                kfree_skb(sk->sk_send_head);
                sk->sk_send_head = NULL;
        }
-       nf_reset(skb);
 
        /* Clean up a referenced DCCP bind bucket. */
        if (inet_csk(sk)->icsk_bind_hash != NULL)
index 4c637a1cbd23466934b0c4253b84325bce2eba56..3321092b0914be3a04f82d850b78c47ccfc6449b 100644 (file)
@@ -86,6 +86,9 @@ int ip_route_me_harder(struct sk_buff **pskb)
 }
 EXPORT_SYMBOL(ip_route_me_harder);
 
+void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *);
+EXPORT_SYMBOL(ip_nat_decode_session);
+
 /*
  * Extra routing may needed on local out, as the QUEUE target never
  * returns control to the table.
index b518697af4db5fe454e6ed5dc512173593352dcc..8b8a1f00bbf4407e2a8f09d17831dae33fcd656f 100644 (file)
                                 : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN"  \
                                    : "*ERROR*")))
 
+#ifdef CONFIG_XFRM
+static void nat_decode_session(struct sk_buff *skb, struct flowi *fl)
+{
+       struct ip_conntrack *ct;
+       struct ip_conntrack_tuple *t;
+       enum ip_conntrack_info ctinfo;
+       enum ip_conntrack_dir dir;
+       unsigned long statusbit;
+
+       ct = ip_conntrack_get(skb, &ctinfo);
+       if (ct == NULL)
+               return;
+       dir = CTINFO2DIR(ctinfo);
+       t = &ct->tuplehash[dir].tuple;
+
+       if (dir == IP_CT_DIR_ORIGINAL)
+               statusbit = IPS_DST_NAT;
+       else
+               statusbit = IPS_SRC_NAT;
+
+       if (ct->status & statusbit) {
+               fl->fl4_dst = t->dst.ip;
+               if (t->dst.protonum == IPPROTO_TCP ||
+                   t->dst.protonum == IPPROTO_UDP)
+                       fl->fl_ip_dport = t->dst.u.tcp.port;
+       }
+
+       statusbit ^= IPS_NAT_MASK;
+
+       if (ct->status & statusbit) {
+               fl->fl4_src = t->src.ip;
+               if (t->dst.protonum == IPPROTO_TCP ||
+                   t->dst.protonum == IPPROTO_UDP)
+                       fl->fl_ip_sport = t->src.u.tcp.port;
+       }
+}
+#endif
+               
 static unsigned int
 ip_nat_fn(unsigned int hooknum,
          struct sk_buff **pskb,
@@ -330,10 +368,14 @@ static int init_or_cleanup(int init)
 
        if (!init) goto cleanup;
 
+#ifdef CONFIG_XFRM
+       BUG_ON(ip_nat_decode_session != NULL);
+       ip_nat_decode_session = nat_decode_session;
+#endif
        ret = ip_nat_rule_init();
        if (ret < 0) {
                printk("ip_nat_init: can't setup rules.\n");
-               goto cleanup_nothing;
+               goto cleanup_decode_session;
        }
        ret = nf_register_hook(&ip_nat_in_ops);
        if (ret < 0) {
@@ -381,7 +423,11 @@ static int init_or_cleanup(int init)
        nf_unregister_hook(&ip_nat_in_ops);
  cleanup_rule_init:
        ip_nat_rule_cleanup();
- cleanup_nothing:
+ cleanup_decode_session:
+#ifdef CONFIG_XFRM
+       ip_nat_decode_session = NULL;
+       synchronize_net();
+#endif
        return ret;
 }
 
index f2edc9225b6a08a6fba45588d549b9cf48a3f197..59614a994b4e51c7384200134e08bba86190e82d 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/workqueue.h>
 #include <linux/notifier.h>
 #include <linux/netdevice.h>
+#include <linux/netfilter.h>
 #include <linux/module.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
@@ -985,6 +986,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
        if (xfrm_decode_session(skb, &fl, family) < 0)
                return 0;
+       nf_nat_decode_session(skb, &fl, family);
 
        sk_sid = security_sk_sid(sk, &fl, fl_dir);