124 lines
4.7 KiB
Diff
124 lines
4.7 KiB
Diff
From 84fa3efb7d8eeb1c3f2daf9a3034e2794b3294f9 Mon Sep 17 00:00:00 2001
|
|
From: Zhi Chen <zhichen@codeaurora.org>
|
|
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 <zhichen@codeaurora.org>
|
|
Signed-off-by: Murat Sezgin <msezgin@codeaurora.org>
|
|
---
|
|
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
|