diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-02-09 20:09:38 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-02-09 20:09:38 -0500 |
commit | 5dce971acf2ae20c80d5e9d1f6bbf17376870911 (patch) | |
tree | 9d5e6809e839f18a2dfe51d2a1d0ff43a17c26bf | |
parent | b3f1be4b5412e34647764457bec901e06b03e624 (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>
-rw-r--r-- | net/bridge/br_netfilter.c | 53 |
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 |
58 | static struct ctl_table_header *brnf_sysctl_header; | 55 | static struct ctl_table_header *brnf_sysctl_header; |
59 | static int brnf_call_iptables = 1; | 56 | static 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 | ||
98 | static 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 */ |
273 | static void setup_pre_routing(struct sk_buff *skb) | 280 | static 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 |