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