netfilter: fix crashes in bridge netfilter caused by fragment jumps
authorPatrick McHardy <kaber@trash.net>
Tue, 15 Dec 2009 15:59:59 +0000 (16:59 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 6 Jan 2010 23:04:40 +0000 (15:04 -0800)
commit 8fa9ff6849bb86c59cc2ea9faadf3cb2d5223497 upstream.

When fragments from bridge netfilter are passed to IPv4 or IPv6 conntrack
and a reassembly queue with the same fragment key already exists from
reassembling a similar packet received on a different device (f.i. with
multicasted fragments), the reassembled packet might continue on a different
codepath than where the head fragment originated. This can cause crashes
in bridge netfilter when a fragment received on a non-bridge device (and
thus with skb->nf_bridge == NULL) continues through the bridge netfilter
code.

Add a new reassembly identifier for packets originating from bridge
netfilter and use it to put those packets in insolated queues.

Fixes http://bugzilla.kernel.org/show_bug.cgi?id=14805

Reported-and-Tested-by: Chong Qiao <qiaochong@loongson.cn>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
include/net/ip.h
include/net/ipv6.h
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c

index 2f47e5482b552a1d5bf95a54d5101ab80ca0899a..69db943058aca158f8b3e889e06905a55d42b43b 100644 (file)
@@ -342,6 +342,7 @@ enum ip_defrag_users
        IP_DEFRAG_CALL_RA_CHAIN,
        IP_DEFRAG_CONNTRACK_IN,
        IP_DEFRAG_CONNTRACK_OUT,
+       IP_DEFRAG_CONNTRACK_BRIDGE_IN,
        IP_DEFRAG_VS_IN,
        IP_DEFRAG_VS_OUT,
        IP_DEFRAG_VS_FWD
index a49649906052900ed2954ae5f12fa64dba056dea..639bbf06d378cdbb0c480ac9a564add0235ca688 100644 (file)
@@ -358,6 +358,7 @@ enum ip6_defrag_users {
        IP6_DEFRAG_LOCAL_DELIVER,
        IP6_DEFRAG_CONNTRACK_IN,
        IP6_DEFRAG_CONNTRACK_OUT,
+       IP6_DEFRAG_CONNTRACK_BRIDGE_IN,
 };
 
 struct ip6_create_arg {
index fa2d6b6fc3e581524181d88a1402df6289eb42c8..331ead3ebd1be98c93ec5bd15cfcb68171c080a7 100644 (file)
@@ -14,6 +14,7 @@
 #include <net/route.h>
 #include <net/ip.h>
 
+#include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv4.h>
 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
 
@@ -34,6 +35,20 @@ static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
        return err;
 }
 
+static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum,
+                                             struct sk_buff *skb)
+{
+#ifdef CONFIG_BRIDGE_NETFILTER
+       if (skb->nf_bridge &&
+           skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)
+               return IP_DEFRAG_CONNTRACK_BRIDGE_IN;
+#endif
+       if (hooknum == NF_INET_PRE_ROUTING)
+               return IP_DEFRAG_CONNTRACK_IN;
+       else
+               return IP_DEFRAG_CONNTRACK_OUT;
+}
+
 static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
                                          struct sk_buff *skb,
                                          const struct net_device *in,
@@ -50,10 +65,8 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
 #endif
        /* Gather fragments. */
        if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
-               if (nf_ct_ipv4_gather_frags(skb,
-                                           hooknum == NF_INET_PRE_ROUTING ?
-                                           IP_DEFRAG_CONNTRACK_IN :
-                                           IP_DEFRAG_CONNTRACK_OUT))
+               enum ip_defrag_users user = nf_ct_defrag_user(hooknum, skb);
+               if (nf_ct_ipv4_gather_frags(skb, user))
                        return NF_STOLEN;
        }
        return NF_ACCEPT;
index c0a82fe783210b3d76a637ee4f59efae542bed6d..0956ebabbff2a604d809678074ce6e338c00d501 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/ipv6.h>
 #include <net/inet_frag.h>
 
+#include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv6.h>
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_helper.h>
@@ -190,6 +191,11 @@ out:
 static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
                                                struct sk_buff *skb)
 {
+#ifdef CONFIG_BRIDGE_NETFILTER
+       if (skb->nf_bridge &&
+           skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)
+               return IP6_DEFRAG_CONNTRACK_BRIDGE_IN;
+#endif
        if (hooknum == NF_INET_PRE_ROUTING)
                return IP6_DEFRAG_CONNTRACK_IN;
        else