return skb->nf_bridge;
}
+static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
+{
+ struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+ if (atomic_read(&nf_bridge->use) > 1) {
+ struct nf_bridge_info *tmp = nf_bridge_alloc(skb);
+
+ if (tmp) {
+ memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info));
+ atomic_set(&tmp->use, 1);
+ nf_bridge_put(nf_bridge);
+ }
+ nf_bridge = tmp;
+ }
+ return nf_bridge;
+}
+
static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
{
unsigned int len = nf_bridge_encap_header_len(skb);
if (!skb->nf_bridge)
return NF_ACCEPT;
+ /* Need exclusive nf_bridge_info since we might have multiple
+ * different physoutdevs. */
+ if (!nf_bridge_unshare(skb))
+ return NF_DROP;
+
parent = bridge_parent(out);
if (!parent)
return NF_DROP;
if (!skb->nf_bridge)
return NF_ACCEPT;
+ /* Need exclusive nf_bridge_info since we might have multiple
+ * different physoutdevs. */
+ if (!nf_bridge_unshare(skb))
+ return NF_DROP;
+
nf_bridge = skb->nf_bridge;
if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
return NF_ACCEPT;