Hi,
Currently, net/bridge/br_input.c has some bugs with respect to port state
handling. It will happily pass multicast packets received on non-forwarding
ports up the stack, and will happily call the PRE_ROUTING netfilter hook on
such packets. Both of these are Wrong.
The attached patch fixes br_input.c up to the point of being readable (if
I only knew what I know now two years ago..), fixing these bugs in the
process. Please apply to 2.4 and 2.5. This patch has received quite some
local testing, but as it's quite large I'd like to wait for 2.4.18pre for
inclusion into 2.4. For 2.5, I don't care if it breaks :)
There's still a small race condition in reading dev->br_port and tearing
down a bridge device, but the old code had the same problem, and I've never
had reports of it biting anyone. This patch makes the race window even
smaller than it was. A proper patch to fix it will be sent later on.
cheers,
Lennert
P.S. I keep track of merged/submitted/pending patches at
http://bridge.sf.net/patchtracker.html
diff -urN linux-2.4.16/net/bridge/br_input.c linux-2.4.16-brfix/net/bridge/br_input.c
--- linux-2.4.16/net/bridge/br_input.c Wed Aug 15 10:54:39 2001
+++ linux-2.4.16-brfix/net/bridge/br_input.c Sun Dec 9 02:41:02 2001
@@ -46,7 +46,7 @@
br_pass_frame_up_finish);
}
-static void __br_handle_frame(struct sk_buff *skb)
+static int br_handle_frame_finish(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
@@ -57,103 +57,112 @@
dest = skb->mac.ethernet->h_dest;
p = skb->dev->br_port;
- br = p->br;
- passedup = 0;
+ if (p == NULL)
+ goto err_nolock;
- if (!(br->dev.flags & IFF_UP) ||
- p->state == BR_STATE_DISABLED)
- goto freeandout;
+ br = p->br;
+ read_lock(&br->lock);
+ if (skb->dev->br_port == NULL)
+ goto err;
+ passedup = 0;
if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2) {
+ if (skb2 != NULL) {
passedup = 1;
br_pass_frame_up(br, skb2);
}
}
- if (skb->mac.ethernet->h_source[0] & 1)
- goto freeandout;
-
- if (!passedup &&
- (dest[0] & 1) &&
- (br->dev.flags & IFF_ALLMULTI || br->dev.mc_list != NULL)) {
- struct sk_buff *skb2;
-
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2) {
- passedup = 1;
- br_pass_frame_up(br, skb2);
- }
- }
-
- if (br->stp_enabled &&
- !memcmp(dest, bridge_ula, 5) &&
- !(dest[5] & 0xF0))
- goto handle_special_frame;
-
- if (p->state == BR_STATE_LEARNING ||
- p->state == BR_STATE_FORWARDING)
- br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
-
- if (p->state != BR_STATE_FORWARDING)
- goto freeandout;
-
if (dest[0] & 1) {
- br_flood_forward(br, skb, 1);
+ br_flood_forward(br, skb, !passedup);
if (!passedup)
br_pass_frame_up(br, skb);
- else
- kfree_skb(skb);
- return;
+ goto out;
}
dst = br_fdb_get(br, dest);
-
if (dst != NULL && dst->is_local) {
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
br_fdb_put(dst);
- return;
+ goto out;
}
if (dst != NULL) {
br_forward(dst->dst, skb);
br_fdb_put(dst);
- return;
+ goto out;
}
br_flood_forward(br, skb, 0);
- return;
- handle_special_frame:
- if (!dest[5]) {
- br_stp_handle_bpdu(skb);
- return;
- }
+out:
+ read_unlock(&br->lock);
+ return 0;
- freeandout:
+err:
+ read_unlock(&br->lock);
+err_nolock:
kfree_skb(skb);
+ return 0;
}
-static int br_handle_frame_finish(struct sk_buff *skb)
+void br_handle_frame(struct sk_buff *skb)
{
struct net_bridge *br;
+ unsigned char *dest;
+ struct net_bridge_port *p;
+
+ dest = skb->mac.ethernet->h_dest;
+
+ p = skb->dev->br_port;
+ if (p == NULL)
+ goto err_nolock;
- br = skb->dev->br_port->br;
+ br = p->br;
read_lock(&br->lock);
- __br_handle_frame(skb);
- read_unlock(&br->lock);
+ if (skb->dev->br_port == NULL)
+ goto err;
- return 0;
-}
+ if (!(br->dev.flags & IFF_UP) ||
+ p->state == BR_STATE_DISABLED)
+ goto err;
-void br_handle_frame(struct sk_buff *skb)
-{
- NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ if (skb->mac.ethernet->h_source[0] & 1)
+ goto err;
+
+ if (p->state == BR_STATE_LEARNING ||
+ p->state == BR_STATE_FORWARDING)
+ br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+
+ if (br->stp_enabled &&
+ !memcmp(dest, bridge_ula, 5) &&
+ !(dest[5] & 0xF0))
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
+ read_unlock(&br->lock);
+ return;
+ }
+
+err:
+ read_unlock(&br->lock);
+err_nolock:
+ kfree_skb(skb);
+ return;
+
+handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+ return;
+ }
+
+ kfree_skb(skb);
}
_______________________________________________
Bridge mailing list
[EMAIL PROTECTED]
http://www.math.leidenuniv.nl/mailman/listinfo/bridge