aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-09 20:09:38 -0500
committerDavid S. Miller <davem@davemloft.net>2006-02-09 20:09:38 -0500
commit5dce971acf2ae20c80d5e9d1f6bbf17376870911 (patch)
tree9d5e6809e839f18a2dfe51d2a1d0ff43a17c26bf /net/bridge
parentb3f1be4b5412e34647764457bec901e06b03e624 (diff)
[BRIDGE]: netfilter handle RCU during removal
Bridge netfilter code needs to handle the case where device is removed from bridge while packet in process. In these cases the bridge_parent can become null while processing. This should fix: http://bugzilla.kernel.org/show_bug.cgi?id=5803 Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_netfilter.c53
1 files changed, 38 insertions, 15 deletions
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 7cac3fb9f809..b5018166b0e5 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -51,9 +51,6 @@
51#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr) 51#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
52#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr) 52#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
53 53
54#define has_bridge_parent(device) ((device)->br_port != NULL)
55#define bridge_parent(device) ((device)->br_port->br->dev)
56
57#ifdef CONFIG_SYSCTL 54#ifdef CONFIG_SYSCTL
58static struct ctl_table_header *brnf_sysctl_header; 55static struct ctl_table_header *brnf_sysctl_header;
59static int brnf_call_iptables = 1; 56static int brnf_call_iptables = 1;
@@ -98,6 +95,12 @@ static struct rtable __fake_rtable = {
98 .rt_flags = 0, 95 .rt_flags = 0,
99}; 96};
100 97
98static inline struct net_device *bridge_parent(const struct net_device *dev)
99{
100 struct net_bridge_port *port = rcu_dereference(dev->br_port);
101
102 return port ? port->br->dev : NULL;
103}
101 104
102/* PF_BRIDGE/PRE_ROUTING *********************************************/ 105/* PF_BRIDGE/PRE_ROUTING *********************************************/
103/* Undo the changes made for ip6tables PREROUTING and continue the 106/* Undo the changes made for ip6tables PREROUTING and continue the
@@ -189,11 +192,15 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
189 skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; 192 skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
190 193
191 skb->dev = bridge_parent(skb->dev); 194 skb->dev = bridge_parent(skb->dev);
192 if (skb->protocol == __constant_htons(ETH_P_8021Q)) { 195 if (!skb->dev)
193 skb_pull(skb, VLAN_HLEN); 196 kfree_skb(skb);
194 skb->nh.raw += VLAN_HLEN; 197 else {
198 if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
199 skb_pull(skb, VLAN_HLEN);
200 skb->nh.raw += VLAN_HLEN;
201 }
202 skb->dst->output(skb);
195 } 203 }
196 skb->dst->output(skb);
197 return 0; 204 return 0;
198} 205}
199 206
@@ -270,7 +277,7 @@ bridged_dnat:
270} 277}
271 278
272/* Some common code for IPv4/IPv6 */ 279/* Some common code for IPv4/IPv6 */
273static void setup_pre_routing(struct sk_buff *skb) 280static struct net_device *setup_pre_routing(struct sk_buff *skb)
274{ 281{
275 struct nf_bridge_info *nf_bridge = skb->nf_bridge; 282 struct nf_bridge_info *nf_bridge = skb->nf_bridge;
276 283
@@ -282,6 +289,8 @@ static void setup_pre_routing(struct sk_buff *skb)
282 nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING; 289 nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
283 nf_bridge->physindev = skb->dev; 290 nf_bridge->physindev = skb->dev;
284 skb->dev = bridge_parent(skb->dev); 291 skb->dev = bridge_parent(skb->dev);
292
293 return skb->dev;
285} 294}
286 295
287/* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */ 296/* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
@@ -376,7 +385,8 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
376 nf_bridge_put(skb->nf_bridge); 385 nf_bridge_put(skb->nf_bridge);
377 if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) 386 if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
378 return NF_DROP; 387 return NF_DROP;
379 setup_pre_routing(skb); 388 if (!setup_pre_routing(skb))
389 return NF_DROP;
380 390
381 NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, 391 NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
382 br_nf_pre_routing_finish_ipv6); 392 br_nf_pre_routing_finish_ipv6);
@@ -465,7 +475,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
465 nf_bridge_put(skb->nf_bridge); 475 nf_bridge_put(skb->nf_bridge);
466 if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) 476 if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
467 return NF_DROP; 477 return NF_DROP;
468 setup_pre_routing(skb); 478 if (!setup_pre_routing(skb))
479 return NF_DROP;
469 store_orig_dstaddr(skb); 480 store_orig_dstaddr(skb);
470 481
471 NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, 482 NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
@@ -539,11 +550,16 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
539 struct sk_buff *skb = *pskb; 550 struct sk_buff *skb = *pskb;
540 struct nf_bridge_info *nf_bridge; 551 struct nf_bridge_info *nf_bridge;
541 struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); 552 struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
553 struct net_device *parent;
542 int pf; 554 int pf;
543 555
544 if (!skb->nf_bridge) 556 if (!skb->nf_bridge)
545 return NF_ACCEPT; 557 return NF_ACCEPT;
546 558
559 parent = bridge_parent(out);
560 if (!parent)
561 return NF_DROP;
562
547 if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) 563 if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
548 pf = PF_INET; 564 pf = PF_INET;
549 else 565 else
@@ -564,8 +580,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
564 nf_bridge->mask |= BRNF_BRIDGED; 580 nf_bridge->mask |= BRNF_BRIDGED;
565 nf_bridge->physoutdev = skb->dev; 581 nf_bridge->physoutdev = skb->dev;
566 582
567 NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), 583 NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), parent,
568 bridge_parent(out), br_nf_forward_finish); 584 br_nf_forward_finish);
569 585
570 return NF_STOLEN; 586 return NF_STOLEN;
571} 587}
@@ -688,6 +704,8 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
688 goto out; 704 goto out;
689 } 705 }
690 realoutdev = bridge_parent(skb->dev); 706 realoutdev = bridge_parent(skb->dev);
707 if (!realoutdev)
708 return NF_DROP;
691 709
692#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) 710#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
693 /* iptables should match -o br0.x */ 711 /* iptables should match -o br0.x */
@@ -701,9 +719,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
701 /* IP forwarded traffic has a physindev, locally 719 /* IP forwarded traffic has a physindev, locally
702 * generated traffic hasn't. */ 720 * generated traffic hasn't. */
703 if (realindev != NULL) { 721 if (realindev != NULL) {
704 if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) && 722 if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) ) {
705 has_bridge_parent(realindev)) 723 struct net_device *parent = bridge_parent(realindev);
706 realindev = bridge_parent(realindev); 724 if (parent)
725 realindev = parent;
726 }
707 727
708 NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev, 728 NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev,
709 realoutdev, br_nf_local_out_finish, 729 realoutdev, br_nf_local_out_finish,
@@ -743,6 +763,9 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
743 if (!nf_bridge) 763 if (!nf_bridge)
744 return NF_ACCEPT; 764 return NF_ACCEPT;
745 765
766 if (!realoutdev)
767 return NF_DROP;
768
746 if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) 769 if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
747 pf = PF_INET; 770 pf = PF_INET;
748 else 771 else