Commit 8fa9ff68 authored by Patrick McHardy's avatar Patrick McHardy

netfilter: fix crashes in bridge netfilter caused by fragment jumps

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=14805Reported-and-Tested-by: default avatarChong Qiao <qiaochong@loongson.cn>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 0b5ccb2e
...@@ -337,6 +337,7 @@ enum ip_defrag_users { ...@@ -337,6 +337,7 @@ enum ip_defrag_users {
IP_DEFRAG_CALL_RA_CHAIN, IP_DEFRAG_CALL_RA_CHAIN,
IP_DEFRAG_CONNTRACK_IN, IP_DEFRAG_CONNTRACK_IN,
IP_DEFRAG_CONNTRACK_OUT, IP_DEFRAG_CONNTRACK_OUT,
IP_DEFRAG_CONNTRACK_BRIDGE_IN,
IP_DEFRAG_VS_IN, IP_DEFRAG_VS_IN,
IP_DEFRAG_VS_OUT, IP_DEFRAG_VS_OUT,
IP_DEFRAG_VS_FWD IP_DEFRAG_VS_FWD
......
...@@ -354,6 +354,7 @@ enum ip6_defrag_users { ...@@ -354,6 +354,7 @@ enum ip6_defrag_users {
IP6_DEFRAG_LOCAL_DELIVER, IP6_DEFRAG_LOCAL_DELIVER,
IP6_DEFRAG_CONNTRACK_IN, IP6_DEFRAG_CONNTRACK_IN,
IP6_DEFRAG_CONNTRACK_OUT, IP6_DEFRAG_CONNTRACK_OUT,
IP6_DEFRAG_CONNTRACK_BRIDGE_IN,
}; };
struct ip6_create_arg { struct ip6_create_arg {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <net/route.h> #include <net/route.h>
#include <net/ip.h> #include <net/ip.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <net/netfilter/ipv4/nf_defrag_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) ...@@ -34,6 +35,20 @@ static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
return err; 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, static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
struct sk_buff *skb, struct sk_buff *skb,
const struct net_device *in, const struct net_device *in,
...@@ -50,10 +65,8 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, ...@@ -50,10 +65,8 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
#endif #endif
/* Gather fragments. */ /* Gather fragments. */
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
if (nf_ct_ipv4_gather_frags(skb, enum ip_defrag_users user = nf_ct_defrag_user(hooknum, skb);
hooknum == NF_INET_PRE_ROUTING ? if (nf_ct_ipv4_gather_frags(skb, user))
IP_DEFRAG_CONNTRACK_IN :
IP_DEFRAG_CONNTRACK_OUT))
return NF_STOLEN; return NF_STOLEN;
} }
return NF_ACCEPT; return NF_ACCEPT;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/inet_frag.h> #include <net/inet_frag.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
...@@ -190,6 +191,11 @@ static unsigned int ipv6_confirm(unsigned int hooknum, ...@@ -190,6 +191,11 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
struct sk_buff *skb) 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) if (hooknum == NF_INET_PRE_ROUTING)
return IP6_DEFRAG_CONNTRACK_IN; return IP6_DEFRAG_CONNTRACK_IN;
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment