Record the parent device since it may be removed (by RCU)
in the course of processing packets via netfilter.

Signed-off-by: Stephen Hemminger <[EMAIL PROTECTED]>


--- br-fix.orig/include/linux/skbuff.h
+++ br-fix/include/linux/skbuff.h
@@ -96,6 +96,7 @@ struct nf_bridge_info {
        atomic_t use;
        struct net_device *physindev;
        struct net_device *physoutdev;
+       struct net_device *parentdev;
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
        struct net_device *netoutdev;
 #endif
--- br-fix.orig/net/bridge/br_netfilter.c
+++ br-fix/net/bridge/br_netfilter.c
@@ -51,9 +51,6 @@
 #define store_orig_dstaddr(skb)         (skb_origaddr(skb) = 
(skb)->nh.iph->daddr)
 #define dnat_took_place(skb)    (skb_origaddr(skb) != (skb)->nh.iph->daddr)
 
-#define has_bridge_parent(device)      ((device)->br_port != NULL)
-#define bridge_parent(device)          ((device)->br_port->br->dev)
-
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_header *brnf_sysctl_header;
 static int brnf_call_iptables = 1;
@@ -98,13 +95,15 @@ static struct rtable __fake_rtable = {
        .rt_flags       = 0,
 };
 
-static struct nf_bridge_info *nf_bridge_alloc(void)
+static struct nf_bridge_info *nf_bridge_alloc(struct net_device *parent)
 {
        struct nf_bridge_info *nf_bridge;
 
        nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC);
-       if (nf_bridge)
+       if (nf_bridge) {
                atomic_set(&(nf_bridge->use), 1);
+               nf_bridge->parentdev = parent;
+       }
 
        return nf_bridge;
 }
@@ -209,7 +208,7 @@ static int br_nf_pre_routing_finish_brid
        }
        skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
 
-       skb->dev = bridge_parent(skb->dev);
+       skb->dev = skb->nf_bridge->parentdev;
        if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
                skb_pull(skb, VLAN_HLEN);
                skb->nh.raw += VLAN_HLEN;
@@ -302,7 +301,7 @@ static void setup_pre_routing(struct sk_
 
        nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
        nf_bridge->physindev = skb->dev;
-       skb->dev = bridge_parent(skb->dev);
+       skb->dev = nf_bridge->parentdev;
 }
 
 /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway 
*/
@@ -364,6 +363,7 @@ static unsigned int br_nf_pre_routing_ip
    struct sk_buff *skb, const struct net_device *in,
    const struct net_device *out, int (*okfn)(struct sk_buff *))
 {
+       struct net_bridge_port *brport;
        struct ipv6hdr *hdr;
        u32 pkt_len;
 
@@ -391,21 +391,29 @@ static unsigned int br_nf_pre_routing_ip
                }
        }
        if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb))
-                       goto inhdr_error;
+               goto inhdr_error;
 
        nf_bridge_put(skb->nf_bridge);
 
-       skb->nf_bridge = nf_bridge_alloc();
+       rcu_read_lock();
+       brport = rcu_dereference(in->br_port);
+       if (!brport)
+               goto drop;
+
+       skb->nf_bridge = nf_bridge_alloc(brport->br->dev);
        if (!skb->nf_bridge)
-               return NF_DROP;
+               goto drop;
 
        setup_pre_routing(skb);
 
        NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
                br_nf_pre_routing_finish_ipv6);
+       rcu_read_unlock();
 
        return NF_STOLEN;
 
+drop:
+       rcu_read_unlock();
 inhdr_error:
        return NF_DROP;
 }
@@ -425,6 +433,7 @@ static unsigned int br_nf_pre_routing(un
        __u32 len;
        struct sk_buff *skb = *pskb;
        struct vlan_ethhdr *hdr = vlan_eth_hdr(*pskb);
+       struct net_bridge_port *brport;
 
        if (skb->protocol == __constant_htons(ETH_P_IPV6) || IS_VLAN_IPV6) {
 #ifdef CONFIG_SYSCTL
@@ -485,18 +494,28 @@ static unsigned int br_nf_pre_routing(un
        }
 
        nf_bridge_put(skb->nf_bridge);
-       skb->nf_bridge = nf_bridge_alloc();
+
+       rcu_read_lock();
+       brport = rcu_dereference(in->br_port);
+       if (!brport)
+               goto drop_unlock;
+
+       skb->nf_bridge = nf_bridge_alloc(brport->br->dev);
        if (!skb->nf_bridge)
-               return NF_DROP;
+               goto drop_unlock;
 
        setup_pre_routing(skb);
        store_orig_dstaddr(skb);
 
        NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
                br_nf_pre_routing_finish);
+       rcu_read_unlock();
 
        return NF_STOLEN;
 
+drop_unlock:
+       rcu_read_unlock();
+
 inhdr_error:
 //     IP_INC_STATS_BH(IpInHdrErrors);
 out:
@@ -588,8 +607,8 @@ static unsigned int br_nf_forward_ip(uns
        nf_bridge->mask |= BRNF_BRIDGED;
        nf_bridge->physoutdev = skb->dev;
 
-       NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in),
-               bridge_parent(out), br_nf_forward_finish);
+       NF_HOOK(pf, NF_IP_FORWARD, skb, nf_bridge->parentdev, 
nf_bridge->parentdev,
+               br_nf_forward_finish);
 
        return NF_STOLEN;
 }
@@ -711,7 +730,7 @@ static unsigned int br_nf_local_out(unsi
                        skb->dev, br_forward_finish);
                goto out;
        }
-       realoutdev = bridge_parent(skb->dev);
+       realoutdev = nf_bridge->parentdev;
 
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
        /* iptables should match -o br0.x */
@@ -725,9 +744,8 @@ static unsigned int br_nf_local_out(unsi
        /* IP forwarded traffic has a physindev, locally
         * generated traffic hasn't. */
        if (realindev != NULL) {
-               if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) &&
-                   has_bridge_parent(realindev))
-                       realindev = bridge_parent(realindev);
+               if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) )
+                       realindev = nf_bridge->parentdev;
 
                NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev,
                               realoutdev, br_nf_local_out_finish,
@@ -751,7 +769,7 @@ static unsigned int br_nf_post_routing(u
        struct sk_buff *skb = *pskb;
        struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
        struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
-       struct net_device *realoutdev = bridge_parent(skb->dev);
+       struct net_device *realoutdev = nf_bridge->parentdev;
        int pf;
 
 #ifdef CONFIG_NETFILTER_DEBUG
@@ -806,8 +824,7 @@ static unsigned int br_nf_post_routing(u
 print_error:
        if (skb->dev != NULL) {
                printk("[%s]", skb->dev->name);
-               if (has_bridge_parent(skb->dev))
-                       printk("[%s]", bridge_parent(skb->dev)->name);
+               printk("[%s]", nf_bridge->parentdev->name);
        }
        printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw,
                                              skb->data);
@@ -866,7 +883,8 @@ static unsigned int ip_sabotage_out(unsi
 #endif
                        if (hook == NF_IP_POST_ROUTING)
                                return NF_ACCEPT;
-                       skb->nf_bridge = nf_bridge_alloc();
+
+                       skb->nf_bridge = nf_bridge_alloc((struct net_device *) 
out);
                        if (!skb->nf_bridge)
                                return NF_DROP;
                }

--
Stephen Hemminger <[EMAIL PROTECTED]>
OSDL http://developer.osdl.org/~shemminger

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to