]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/bridge/br_netfilter.c
[NETFILTER]: bridge-netfilter: fix net_device refcnt leaks
[net-next-2.6.git] / net / bridge / br_netfilter.c
index 5d8b939eded1937b8a2ced738a69add839b0c3d0..9f78a69d6b8b570ff20e0be70d7a68471cc45d4c 100644 (file)
@@ -142,6 +142,23 @@ static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
        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);
@@ -637,6 +654,11 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *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;
@@ -718,6 +740,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff *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;
+
        nf_bridge = skb->nf_bridge;
        if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
                return NF_ACCEPT;