From: Chris Wright Date: Wed, 8 Feb 2006 00:51:49 +0000 (-0800) Subject: Add bridge patches from shemminger X-Git-Tag: v2.6.15.5~52 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=16d42b5afd746179f6a5b5f00240b6f8df7308af;p=thirdparty%2Fkernel%2Fstable-queue.git Add bridge patches from shemminger --- diff --git a/queue/bridge-fix-rcu-race-on-device-removal.patch b/queue/bridge-fix-rcu-race-on-device-removal.patch new file mode 100644 index 00000000000..a786da4eb5c --- /dev/null +++ b/queue/bridge-fix-rcu-race-on-device-removal.patch @@ -0,0 +1,111 @@ +From stable-bounces@linux.kernel.org Mon Feb 6 15:50:01 2006 +Date: Mon, 6 Feb 2006 15:42:45 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Cc: +Subject: [PATCH] bridge: fix RCU race on device removal + +Patch to 2.6.15 stable kernel to fix race conditions on device +removal. These are reproducible by doing delif while packets are +in flight. + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + net/bridge/br_if.c | 7 ++----- + net/bridge/br_input.c | 10 ++++++++-- + net/bridge/br_stp_bpdu.c | 8 ++++++-- + 3 files changed, 16 insertions(+), 9 deletions(-) + +Index: linux-2.6.15.3/net/bridge/br_if.c +=================================================================== +--- linux-2.6.15.3.orig/net/bridge/br_if.c ++++ linux-2.6.15.3/net/bridge/br_if.c +@@ -99,7 +99,6 @@ static void del_nbp(struct net_bridge_po + struct net_bridge *br = p->br; + struct net_device *dev = p->dev; + +- dev->br_port = NULL; + dev_set_promiscuity(dev, -1); + + spin_lock_bh(&br->lock); +@@ -110,9 +109,7 @@ static void del_nbp(struct net_bridge_po + + list_del_rcu(&p->list); + +- del_timer_sync(&p->message_age_timer); +- del_timer_sync(&p->forward_delay_timer); +- del_timer_sync(&p->hold_timer); ++ rcu_assign_pointer(dev->br_port, NULL); + + call_rcu(&p->rcu, destroy_nbp_rcu); + } +@@ -217,7 +214,6 @@ static struct net_bridge_port *new_nbp(s + p->dev = dev; + p->path_cost = cost; + p->priority = 0x8000 >> BR_PORT_BITS; +- dev->br_port = p; + p->port_no = index; + br_init_port(p); + p->state = BR_STATE_DISABLED; +@@ -360,6 +356,7 @@ int br_add_if(struct net_bridge *br, str + else if ((err = br_sysfs_addif(p))) + del_nbp(p); + else { ++ rcu_assign_pointer(dev->br_port, p); + dev_set_promiscuity(dev, 1); + + list_add_rcu(&p->list, &br->port_list); +Index: linux-2.6.15.3/net/bridge/br_input.c +=================================================================== +--- linux-2.6.15.3.orig/net/bridge/br_input.c ++++ linux-2.6.15.3/net/bridge/br_input.c +@@ -45,11 +45,17 @@ static void br_pass_frame_up(struct net_ + int br_handle_frame_finish(struct sk_buff *skb) + { + const unsigned char *dest = eth_hdr(skb)->h_dest; +- struct net_bridge_port *p = skb->dev->br_port; +- struct net_bridge *br = p->br; ++ struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); ++ struct net_bridge *br; + struct net_bridge_fdb_entry *dst; + int passedup = 0; + ++ if (unlikely(!p || p->state == BR_STATE_DISABLED)) { ++ kfree_skb(skb); ++ return 0; ++ } ++ ++ br = p->br; + /* insert into forwarding database after filtering to avoid spoofing */ + br_fdb_update(p->br, p, eth_hdr(skb)->h_source); + +Index: linux-2.6.15.3/net/bridge/br_stp_bpdu.c +=================================================================== +--- linux-2.6.15.3.orig/net/bridge/br_stp_bpdu.c ++++ linux-2.6.15.3/net/bridge/br_stp_bpdu.c +@@ -136,10 +136,13 @@ static const unsigned char header[6] = { + /* NO locks */ + int br_stp_handle_bpdu(struct sk_buff *skb) + { +- struct net_bridge_port *p = skb->dev->br_port; +- struct net_bridge *br = p->br; ++ struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); ++ struct net_bridge *br; + unsigned char *buf; + ++ if (!p) ++ goto err; ++ + /* insert into forwarding database after filtering to avoid spoofing */ + br_fdb_update(p->br, p, eth_hdr(skb)->h_source); + +@@ -150,6 +153,7 @@ int br_stp_handle_bpdu(struct sk_buff *s + + buf = skb_pull(skb, sizeof(header)); + ++ br = p->br; + spin_lock_bh(&br->lock); + if (p->state == BR_STATE_DISABLED + || !(br->dev->flags & IFF_UP) diff --git a/queue/bridge-netfilter-races-on-device-removal.patch b/queue/bridge-netfilter-races-on-device-removal.patch new file mode 100644 index 00000000000..395a47fa369 --- /dev/null +++ b/queue/bridge-netfilter-races-on-device-removal.patch @@ -0,0 +1,182 @@ +From stable-bounces@linux.kernel.org Mon Feb 6 15:49:07 2006 +Date: Mon, 6 Feb 2006 15:41:15 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Cc: netdev@vger.kernel.org, David Miller +Subject: [PATCH] bridge: netfilter races on device removal + +Fix bridge netfilter to handle case where interface is deleted +from bridge while packet is being processed (on other CPU). + +Fixes: http://bugzilla.kernel.org/show_bug.cgi?id=5803 + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + net/bridge/br_netfilter.c | 55 +++++++++++++++++++++++++++++++--------------- + 1 files changed, 38 insertions(+), 17 deletions(-) + +Index: linux-2.6.15.3/net/bridge/br_netfilter.c +=================================================================== +--- linux-2.6.15.3.orig/net/bridge/br_netfilter.c ++++ linux-2.6.15.3/net/bridge/br_netfilter.c +@@ -47,9 +47,6 @@ + #define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr) + #define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr) + +-#define has_bridge_parent(device) ((device)->br_port != NULL) +-#define bridge_parent(device) ((device)->br_port->br->dev) +- + #ifdef CONFIG_SYSCTL + static struct ctl_table_header *brnf_sysctl_header; + static int brnf_call_iptables = 1; +@@ -94,6 +91,12 @@ static struct rtable __fake_rtable = { + .rt_flags = 0, + }; + ++static inline struct net_device *bridge_parent(const struct net_device *dev) ++{ ++ struct net_bridge_port *port = rcu_dereference(dev->br_port); ++ ++ return port ? port->br->dev : NULL; ++} + + /* PF_BRIDGE/PRE_ROUTING *********************************************/ + /* Undo the changes made for ip6tables PREROUTING and continue the +@@ -185,11 +188,15 @@ static int br_nf_pre_routing_finish_brid + skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; + + skb->dev = bridge_parent(skb->dev); +- if (skb->protocol == __constant_htons(ETH_P_8021Q)) { +- skb_pull(skb, VLAN_HLEN); +- skb->nh.raw += VLAN_HLEN; ++ if (!skb->dev) ++ kfree_skb(skb); ++ else { ++ if (skb->protocol == __constant_htons(ETH_P_8021Q)) { ++ skb_pull(skb, VLAN_HLEN); ++ skb->nh.raw += VLAN_HLEN; ++ } ++ skb->dst->output(skb); + } +- skb->dst->output(skb); + return 0; + } + +@@ -266,7 +273,7 @@ bridged_dnat: + } + + /* Some common code for IPv4/IPv6 */ +-static void setup_pre_routing(struct sk_buff *skb) ++static struct net_device *setup_pre_routing(struct sk_buff *skb) + { + struct nf_bridge_info *nf_bridge = skb->nf_bridge; + +@@ -278,6 +285,8 @@ static void setup_pre_routing(struct sk_ + nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING; + nf_bridge->physindev = skb->dev; + skb->dev = bridge_parent(skb->dev); ++ ++ return skb->dev; + } + + /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */ +@@ -372,7 +381,8 @@ static unsigned int br_nf_pre_routing_ip + nf_bridge_put(skb->nf_bridge); + if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) + return NF_DROP; +- setup_pre_routing(skb); ++ if (!setup_pre_routing(skb)) ++ return NF_DROP; + + NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, + br_nf_pre_routing_finish_ipv6); +@@ -409,7 +419,6 @@ static unsigned int br_nf_pre_routing(un + + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + skb_pull(skb, VLAN_HLEN); +- (skb)->nh.raw += VLAN_HLEN; + } + return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); + } +@@ -426,7 +435,6 @@ static unsigned int br_nf_pre_routing(un + + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + skb_pull(skb, VLAN_HLEN); +- (skb)->nh.raw += VLAN_HLEN; + } + + if (!pskb_may_pull(skb, sizeof(struct iphdr))) +@@ -456,7 +464,8 @@ static unsigned int br_nf_pre_routing(un + nf_bridge_put(skb->nf_bridge); + if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) + return NF_DROP; +- setup_pre_routing(skb); ++ if (!setup_pre_routing(skb)) ++ return NF_DROP; + store_orig_dstaddr(skb); + + NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, +@@ -530,11 +539,16 @@ static unsigned int br_nf_forward_ip(uns + struct sk_buff *skb = *pskb; + struct nf_bridge_info *nf_bridge; + struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); ++ struct net_device *parent; + int pf; + + if (!skb->nf_bridge) + return NF_ACCEPT; + ++ parent = bridge_parent(out); ++ if (!parent) ++ return NF_DROP; ++ + if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) + pf = PF_INET; + else +@@ -555,8 +569,8 @@ static unsigned int br_nf_forward_ip(uns + nf_bridge->mask |= BRNF_BRIDGED; + nf_bridge->physoutdev = skb->dev; + +- NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), +- bridge_parent(out), br_nf_forward_finish); ++ NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), parent, ++ br_nf_forward_finish); + + return NF_STOLEN; + } +@@ -679,6 +693,8 @@ static unsigned int br_nf_local_out(unsi + goto out; + } + realoutdev = bridge_parent(skb->dev); ++ if (!realoutdev) ++ return NF_DROP; + + #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) + /* iptables should match -o br0.x */ +@@ -692,9 +708,11 @@ static unsigned int br_nf_local_out(unsi + /* IP forwarded traffic has a physindev, locally + * generated traffic hasn't. */ + if (realindev != NULL) { +- if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) && +- has_bridge_parent(realindev)) +- realindev = bridge_parent(realindev); ++ if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) ) { ++ struct net_device *parent = bridge_parent(realindev); ++ if (parent) ++ realindev = parent; ++ } + + NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev, + realoutdev, br_nf_local_out_finish, +@@ -734,6 +752,9 @@ static unsigned int br_nf_post_routing(u + if (!nf_bridge) + return NF_ACCEPT; + ++ if (!realoutdev) ++ return NF_DROP; ++ + if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) + pf = PF_INET; + else diff --git a/queue/series b/queue/series index cc18735596b..48c634c5b90 100644 --- a/queue/series +++ b/queue/series @@ -16,3 +16,4 @@ selinux-fix-size-128-slab-leak.patch fixed-hardware-rx-checksum-handling.patch x86_64-let-impossible-cpus-point-to-reference-per-cpu-data.patch x86_64-clear-more-state-when-ignoring-empty-node-in-srat-parsing.patch +bridge-netfilter-races-on-device-removal.patch