diff options
Diffstat (limited to 'net/bridge')
-rw-r--r-- | net/bridge/br_netfilter.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 5d8b939eded1..9f78a69d6b8b 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c | |||
@@ -142,6 +142,23 @@ static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb) | |||
142 | return skb->nf_bridge; | 142 | return skb->nf_bridge; |
143 | } | 143 | } |
144 | 144 | ||
145 | static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb) | ||
146 | { | ||
147 | struct nf_bridge_info *nf_bridge = skb->nf_bridge; | ||
148 | |||
149 | if (atomic_read(&nf_bridge->use) > 1) { | ||
150 | struct nf_bridge_info *tmp = nf_bridge_alloc(skb); | ||
151 | |||
152 | if (tmp) { | ||
153 | memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info)); | ||
154 | atomic_set(&tmp->use, 1); | ||
155 | nf_bridge_put(nf_bridge); | ||
156 | } | ||
157 | nf_bridge = tmp; | ||
158 | } | ||
159 | return nf_bridge; | ||
160 | } | ||
161 | |||
145 | static inline void nf_bridge_push_encap_header(struct sk_buff *skb) | 162 | static inline void nf_bridge_push_encap_header(struct sk_buff *skb) |
146 | { | 163 | { |
147 | unsigned int len = nf_bridge_encap_header_len(skb); | 164 | unsigned int len = nf_bridge_encap_header_len(skb); |
@@ -637,6 +654,11 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, | |||
637 | if (!skb->nf_bridge) | 654 | if (!skb->nf_bridge) |
638 | return NF_ACCEPT; | 655 | return NF_ACCEPT; |
639 | 656 | ||
657 | /* Need exclusive nf_bridge_info since we might have multiple | ||
658 | * different physoutdevs. */ | ||
659 | if (!nf_bridge_unshare(skb)) | ||
660 | return NF_DROP; | ||
661 | |||
640 | parent = bridge_parent(out); | 662 | parent = bridge_parent(out); |
641 | if (!parent) | 663 | if (!parent) |
642 | return NF_DROP; | 664 | return NF_DROP; |
@@ -718,6 +740,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff *skb, | |||
718 | if (!skb->nf_bridge) | 740 | if (!skb->nf_bridge) |
719 | return NF_ACCEPT; | 741 | return NF_ACCEPT; |
720 | 742 | ||
743 | /* Need exclusive nf_bridge_info since we might have multiple | ||
744 | * different physoutdevs. */ | ||
745 | if (!nf_bridge_unshare(skb)) | ||
746 | return NF_DROP; | ||
747 | |||
721 | nf_bridge = skb->nf_bridge; | 748 | nf_bridge = skb->nf_bridge; |
722 | if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT)) | 749 | if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT)) |
723 | return NF_ACCEPT; | 750 | return NF_ACCEPT; |