diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2010-02-27 19:41:46 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-28 00:48:45 -0800 |
commit | 5cb5e947d8f82011e3d7e7017e9d10db9a40ae9f (patch) | |
tree | 126e3b2112f780120bf9e95c67fdb1b81e73c314 /net/bridge/br_forward.c | |
parent | eb1d16414339a6e113d89e2cca2556005d7ce919 (diff) |
bridge: Add multicast forwarding functions
This patch adds code to perform selective multicast forwarding.
We forward multicast traffic to a set of ports plus all multicast
router ports. In order to avoid duplications among these two
sets of ports, we order all ports by the numeric value of their
pointers. The two lists are then walked in lock-step to eliminate
duplicates.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_forward.c')
-rw-r--r-- | net/bridge/br_forward.c | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 86cd0712d63e..d61e6f741125 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -186,3 +186,70 @@ void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, { br_flood(br, skb, skb2, __br_forward); } + +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING +/* called with rcu_read_lock */ +static void br_multicast_flood(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb, struct sk_buff *skb0, + void (*__packet_hook)( + const struct net_bridge_port *p, + struct sk_buff *skb)) +{ + struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; + struct net_bridge *br = netdev_priv(dev); + struct net_bridge_port *port; + struct net_bridge_port *lport, *rport; + struct net_bridge_port *prev; + struct net_bridge_port_group *p; + struct hlist_node *rp; + + prev = NULL; + + rp = br->router_list.first; + p = mdst ? mdst->ports : NULL; + while (p || rp) { + lport = p ? p->port : NULL; + rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) : + NULL; + + port = (unsigned long)lport > (unsigned long)rport ? + lport : rport; + + prev = maybe_deliver(prev, port, skb, __packet_hook); + if (IS_ERR(prev)) + goto out; + + if ((unsigned long)lport >= (unsigned long)port) + p = p->next; + if ((unsigned long)rport >= (unsigned long)port) + rp = rp->next; + } + + if (!prev) + goto out; + + if (skb0) + deliver_clone(prev, skb, __packet_hook); + else + __packet_hook(prev, skb); + return; + +out: + if (!skb0) + kfree_skb(skb); +} + +/* called with rcu_read_lock */ +void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb) +{ + br_multicast_flood(mdst, skb, NULL, __br_deliver); +} + +/* called with rcu_read_lock */ +void br_multicast_forward(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb, struct sk_buff *skb2) +{ + br_multicast_flood(mdst, skb, skb2, __br_forward); +} +#endif |