Skip to content

Commit

Permalink
netfilter: fix crashes in bridge netfilter caused by fragment jumps
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Patrick McHardy committed Dec 15, 2009
1 parent 0b5ccb2 commit 8fa9ff6
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 4 deletions.
1 change: 1 addition & 0 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,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
Expand Down
1 change: 1 addition & 0 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,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 {
Expand Down
21 changes: 17 additions & 4 deletions net/ipv4/netfilter/nf_defrag_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -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,
Expand All @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -190,6 +191,11 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
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
Expand Down

0 comments on commit 8fa9ff6

Please sign in to comment.