From 84fa3efb7d8eeb1c3f2daf9a3034e2794b3294f9 Mon Sep 17 00:00:00 2001 From: Zhi Chen Date: Mon, 13 Apr 2015 16:39:06 -0700 Subject: [PATCH 223/281] linux: bridge: add patch for IGMP/MLD snooping Kernel patch for the IGMP/MLD snooping module, which is a replacement and enhancement of Linux bridge multicast snooping. With the IGMP/MLD snooping module, we can support IGMPv3/MLDv2 on Linux bridge. Furthermore, with additional plugins, we can support switch port-based snooping and WiFi conversion from multicast to unicast. This patch exported a couple of bridge APIs and added two packet hooks on the input/output path. Change-Id: I3507f2b793155856b4dac8ad5937a9fc6d28551d Signed-off-by: Zhi Chen Signed-off-by: Murat Sezgin --- include/linux/if_bridge.h | 6 ++++++ net/bridge/br_device.c | 5 +++++ net/bridge/br_input.c | 12 +++++++++++- net/bridge/br_private.h | 5 +++++ 4 files changed, 27 insertions(+), 1 deletion(-) --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -66,11 +66,13 @@ struct br_ip_list { #define BR_DEFAULT_AGEING_TIME (300 * HZ) struct net_bridge; +struct net_bridge_port; void brioctl_set(int (*hook)(struct net *net, struct net_bridge *br, unsigned int cmd, struct ifreq *ifr, void __user *uarg)); int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, struct ifreq *ifr, void __user *uarg); + extern struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, struct sk_buff *skb); @@ -230,4 +232,8 @@ extern br_port_dev_get_hook_t __rcu *br_ typedef void (br_notify_hook_t)(int group, int event, const void *ptr); extern br_notify_hook_t __rcu *br_notify_hook; +typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, + struct sk_buff *skb); +extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; + #endif --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -89,6 +89,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff * if (is_broadcast_ether_addr(dest)) { br_flood(br, skb, BR_PKT_BROADCAST, false, true, vid); } else if (is_multicast_ether_addr(dest)) { + br_multicast_handle_hook_t *multicast_handle_hook = + rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, NULL, skb)) + goto out; + if (unlikely(netpoll_tx_running(dev))) { br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid); goto out; --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -30,7 +30,11 @@ br_netif_receive_skb(struct net *net, st return netif_receive_skb(skb); } -static int br_pass_frame_up(struct sk_buff *skb, bool promisc) +/* Hook for external Multicast handler */ +br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_multicast_handle_hook); + +int br_pass_frame_up(struct sk_buff *skb, bool promisc) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(brdev); @@ -71,6 +75,7 @@ static int br_pass_frame_up(struct sk_bu dev_net(indev), NULL, skb, indev, NULL, br_netif_receive_skb); } +EXPORT_SYMBOL_GPL(br_pass_frame_up); /* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) @@ -85,6 +90,7 @@ int br_handle_frame_finish(struct net *n struct net_bridge_vlan *vlan; struct net_bridge *br; bool promisc; + br_multicast_handle_hook_t *multicast_handle_hook; u16 vid = 0; u8 state; @@ -182,6 +188,10 @@ int br_handle_frame_finish(struct net *n switch (pkt_type) { case BR_PKT_MULTICAST: + multicast_handle_hook = rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, p, skb)) + goto out; + mdst = br_mdb_get(brmctx, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -909,6 +909,7 @@ void br_manage_promisc(struct net_bridge int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); /* br_input.c */ +int br_pass_frame_up(struct sk_buff *skb, bool promisc); int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); @@ -2292,4 +2293,8 @@ void br_do_suppress_nd(struct sk_buff *s u16 vid, struct net_bridge_port *p, struct nd_msg *msg); struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid); + +#define __br_get(__hook, __default, __args ...) \ + (__hook ? (__hook(__args)) : (__default)) + #endif