diff options
| author | Cong Wang <amwang@redhat.com> | 2012-12-07 00:04:48 +0000 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2012-12-07 14:32:52 -0500 | 
| commit | ee07c6e7a6f8a25c18f0a6b18152fbd7499245f6 (patch) | |
| tree | 055d61934deeedf93eefbde3106f6a751c35d932 | |
| parent | 5d248c491b38d4f1b2a0bd7721241d68cd0b3067 (diff) | |
| download | olio-linux-3.10-ee07c6e7a6f8a25c18f0a6b18152fbd7499245f6.tar.xz olio-linux-3.10-ee07c6e7a6f8a25c18f0a6b18152fbd7499245f6.zip  | |
bridge: export multicast database via netlink
V5: fix two bugs pointed out by Thomas
    remove seq check for now, mark it as TODO
V4: remove some useless #include
    some coding style fix
V3: drop debugging printk's
    update selinux perm table as well
V2: drop patch 1/2, export ifindex directly
    Redesign netlink attributes
    Improve netlink seq check
    Handle IPv6 addr as well
This patch exports bridge multicast database via netlink
message type RTM_GETMDB. Similar to fdb, but currently bridge-specific.
We may need to support modify multicast database too (RTM_{ADD,DEL}MDB).
(Thanks to Thomas for patient reviews)
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Graf <tgraf@suug.ch>
Cc: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Cong Wang <amwang@redhat.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/uapi/linux/if_bridge.h | 55 | ||||
| -rw-r--r-- | include/uapi/linux/rtnetlink.h | 3 | ||||
| -rw-r--r-- | net/bridge/Makefile | 2 | ||||
| -rw-r--r-- | net/bridge/br_mdb.c | 163 | ||||
| -rw-r--r-- | net/bridge/br_multicast.c | 1 | ||||
| -rw-r--r-- | net/bridge/br_private.h | 1 | ||||
| -rw-r--r-- | security/selinux/nlmsgtab.c | 1 | 
7 files changed, 225 insertions, 1 deletions
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index b3885791e11..9a0f6ff0d7e 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -116,4 +116,59 @@ enum {  	__IFLA_BRIDGE_MAX,  };  #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) + +/* Bridge multicast database attributes + * [MDBA_MDB] = { + *     [MDBA_MDB_ENTRY] = { + *         [MDBA_MDB_ENTRY_INFO] + *     } + * } + * [MDBA_ROUTER] = { + *    [MDBA_ROUTER_PORT] + * } + */ +enum { +	MDBA_UNSPEC, +	MDBA_MDB, +	MDBA_ROUTER, +	__MDBA_MAX, +}; +#define MDBA_MAX (__MDBA_MAX - 1) + +enum { +	MDBA_MDB_UNSPEC, +	MDBA_MDB_ENTRY, +	__MDBA_MDB_MAX, +}; +#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1) + +enum { +	MDBA_MDB_ENTRY_UNSPEC, +	MDBA_MDB_ENTRY_INFO, +	__MDBA_MDB_ENTRY_MAX, +}; +#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) + +enum { +	MDBA_ROUTER_UNSPEC, +	MDBA_ROUTER_PORT, +	__MDBA_ROUTER_MAX, +}; +#define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1) + +struct br_port_msg { +	__u32 ifindex; +}; + +struct br_mdb_entry { +	__u32 ifindex; +	struct { +		union { +			__be32	ip4; +			struct in6_addr ip6; +		} u; +		__be16		proto; +	} addr; +}; +  #endif /* _UAPI_LINUX_IF_BRIDGE_H */ diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 33d29cea37e..354a1e7d32a 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -125,6 +125,9 @@ enum {  	RTM_GETNETCONF = 82,  #define RTM_GETNETCONF RTM_GETNETCONF +	RTM_GETMDB = 86, +#define RTM_GETMDB RTM_GETMDB +  	__RTM_MAX,  #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)  }; diff --git a/net/bridge/Makefile b/net/bridge/Makefile index d0359ea8ee7..e859098f5ee 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -12,6 +12,6 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o  bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o -bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o +bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o  obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c new file mode 100644 index 00000000000..edc0d731f6b --- /dev/null +++ b/net/bridge/br_mdb.c @@ -0,0 +1,163 @@ +#include <linux/err.h> +#include <linux/igmp.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/rculist.h> +#include <linux/skbuff.h> +#include <net/ip.h> +#include <net/netlink.h> +#if IS_ENABLED(CONFIG_IPV6) +#include <net/ipv6.h> +#endif + +#include "br_private.h" + +static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb, +			       struct net_device *dev) +{ +	struct net_bridge *br = netdev_priv(dev); +	struct net_bridge_port *p; +	struct hlist_node *n; +	struct nlattr *nest; + +	if (!br->multicast_router || hlist_empty(&br->router_list)) +		return 0; + +	nest = nla_nest_start(skb, MDBA_ROUTER); +	if (nest == NULL) +		return -EMSGSIZE; + +	hlist_for_each_entry_rcu(p, n, &br->router_list, rlist) { +		if (p && nla_put_u32(skb, MDBA_ROUTER_PORT, p->dev->ifindex)) +			goto fail; +	} + +	nla_nest_end(skb, nest); +	return 0; +fail: +	nla_nest_cancel(skb, nest); +	return -EMSGSIZE; +} + +static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, +			    struct net_device *dev) +{ +	struct net_bridge *br = netdev_priv(dev); +	struct net_bridge_mdb_htable *mdb; +	struct nlattr *nest, *nest2; +	int i, err = 0; +	int idx = 0, s_idx = cb->args[1]; + +	if (br->multicast_disabled) +		return 0; + +	mdb = rcu_dereference(br->mdb); +	if (!mdb) +		return 0; + +	nest = nla_nest_start(skb, MDBA_MDB); +	if (nest == NULL) +		return -EMSGSIZE; + +	for (i = 0; i < mdb->max; i++) { +		struct hlist_node *h; +		struct net_bridge_mdb_entry *mp; +		struct net_bridge_port_group *p, **pp; +		struct net_bridge_port *port; + +		hlist_for_each_entry_rcu(mp, h, &mdb->mhash[i], hlist[mdb->ver]) { +			if (idx < s_idx) +				goto skip; + +			nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY); +			if (nest2 == NULL) { +				err = -EMSGSIZE; +				goto out; +			} + +			for (pp = &mp->ports; +			     (p = rcu_dereference(*pp)) != NULL; +			      pp = &p->next) { +				port = p->port; +				if (port) { +					struct br_mdb_entry e; +					e.ifindex = port->dev->ifindex; +					e.addr.u.ip4 = p->addr.u.ip4; +#if IS_ENABLED(CONFIG_IPV6) +					e.addr.u.ip6 = p->addr.u.ip6; +#endif +					e.addr.proto = p->addr.proto; +					if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(e), &e)) { +						nla_nest_cancel(skb, nest2); +						err = -EMSGSIZE; +						goto out; +					} +				} +			} +			nla_nest_end(skb, nest2); +		skip: +			idx++; +		} +	} + +out: +	cb->args[1] = idx; +	nla_nest_end(skb, nest); +	return err; +} + +static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ +	struct net_device *dev; +	struct net *net = sock_net(skb->sk); +	struct nlmsghdr *nlh = NULL; +	int idx = 0, s_idx; + +	s_idx = cb->args[0]; + +	rcu_read_lock(); + +	/* TODO: in case of rehashing, we need to check +	 * consistency for dumping. +	 */ +	cb->seq = net->dev_base_seq; + +	for_each_netdev_rcu(net, dev) { +		if (dev->priv_flags & IFF_EBRIDGE) { +			struct br_port_msg *bpm; + +			if (idx < s_idx) +				goto skip; + +			nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, +					cb->nlh->nlmsg_seq, RTM_GETMDB, +					sizeof(*bpm), NLM_F_MULTI); +			if (nlh == NULL) +				break; + +			bpm = nlmsg_data(nlh); +			bpm->ifindex = dev->ifindex; +			if (br_mdb_fill_info(skb, cb, dev) < 0) +				goto out; +			if (br_rports_fill_info(skb, cb, dev) < 0) +				goto out; + +			cb->args[1] = 0; +			nlmsg_end(skb, nlh); +		skip: +			idx++; +		} +	} + +out: +	if (nlh) +		nlmsg_end(skb, nlh); +	rcu_read_unlock(); +	cb->args[0] = idx; +	return skb->len; +} + +void br_mdb_init(void) +{ +	rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, NULL); +} diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index a2a7a1a7908..68e375ac93b 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1605,6 +1605,7 @@ void br_multicast_init(struct net_bridge *br)  		    br_multicast_querier_expired, (unsigned long)br);  	setup_timer(&br->multicast_query_timer, br_multicast_query_expired,  		    (unsigned long)br); +	br_mdb_init();  }  void br_multicast_open(struct net_bridge *br) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index cd86222cf5e..ae0a6ec0a70 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -433,6 +433,7 @@ extern int br_multicast_set_port_router(struct net_bridge_port *p,  extern int br_multicast_toggle(struct net_bridge *br, unsigned long val);  extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val);  extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); +extern void br_mdb_init(void);  static inline bool br_multicast_is_router(struct net_bridge *br)  { diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index d309e7f472d..163aaa77d5a 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -67,6 +67,7 @@ static struct nlmsg_perm nlmsg_route_perms[] =  	{ RTM_GETADDRLABEL,	NETLINK_ROUTE_SOCKET__NLMSG_READ  },  	{ RTM_GETDCB,		NETLINK_ROUTE_SOCKET__NLMSG_READ  },  	{ RTM_SETDCB,		NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, +	{ RTM_GETMDB,		NETLINK_ROUTE_SOCKET__NLMSG_READ  },  };  static struct nlmsg_perm nlmsg_tcpdiag_perms[] =  |