diff options
author | Cong Wang <amwang@redhat.com> | 2012-12-06 19:04:48 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-12-07 14:32:52 -0500 |
commit | ee07c6e7a6f8a25c18f0a6b18152fbd7499245f6 (patch) | |
tree | 055d61934deeedf93eefbde3106f6a751c35d932 | |
parent | 5d248c491b38d4f1b2a0bd7721241d68cd0b3067 (diff) |
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 b3885791e11e..9a0f6ff0d7e7 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h | |||
@@ -116,4 +116,59 @@ enum { | |||
116 | __IFLA_BRIDGE_MAX, | 116 | __IFLA_BRIDGE_MAX, |
117 | }; | 117 | }; |
118 | #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) | 118 | #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) |
119 | |||
120 | /* Bridge multicast database attributes | ||
121 | * [MDBA_MDB] = { | ||
122 | * [MDBA_MDB_ENTRY] = { | ||
123 | * [MDBA_MDB_ENTRY_INFO] | ||
124 | * } | ||
125 | * } | ||
126 | * [MDBA_ROUTER] = { | ||
127 | * [MDBA_ROUTER_PORT] | ||
128 | * } | ||
129 | */ | ||
130 | enum { | ||
131 | MDBA_UNSPEC, | ||
132 | MDBA_MDB, | ||
133 | MDBA_ROUTER, | ||
134 | __MDBA_MAX, | ||
135 | }; | ||
136 | #define MDBA_MAX (__MDBA_MAX - 1) | ||
137 | |||
138 | enum { | ||
139 | MDBA_MDB_UNSPEC, | ||
140 | MDBA_MDB_ENTRY, | ||
141 | __MDBA_MDB_MAX, | ||
142 | }; | ||
143 | #define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1) | ||
144 | |||
145 | enum { | ||
146 | MDBA_MDB_ENTRY_UNSPEC, | ||
147 | MDBA_MDB_ENTRY_INFO, | ||
148 | __MDBA_MDB_ENTRY_MAX, | ||
149 | }; | ||
150 | #define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) | ||
151 | |||
152 | enum { | ||
153 | MDBA_ROUTER_UNSPEC, | ||
154 | MDBA_ROUTER_PORT, | ||
155 | __MDBA_ROUTER_MAX, | ||
156 | }; | ||
157 | #define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1) | ||
158 | |||
159 | struct br_port_msg { | ||
160 | __u32 ifindex; | ||
161 | }; | ||
162 | |||
163 | struct br_mdb_entry { | ||
164 | __u32 ifindex; | ||
165 | struct { | ||
166 | union { | ||
167 | __be32 ip4; | ||
168 | struct in6_addr ip6; | ||
169 | } u; | ||
170 | __be16 proto; | ||
171 | } addr; | ||
172 | }; | ||
173 | |||
119 | #endif /* _UAPI_LINUX_IF_BRIDGE_H */ | 174 | #endif /* _UAPI_LINUX_IF_BRIDGE_H */ |
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 33d29cea37ea..354a1e7d32a3 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h | |||
@@ -125,6 +125,9 @@ enum { | |||
125 | RTM_GETNETCONF = 82, | 125 | RTM_GETNETCONF = 82, |
126 | #define RTM_GETNETCONF RTM_GETNETCONF | 126 | #define RTM_GETNETCONF RTM_GETNETCONF |
127 | 127 | ||
128 | RTM_GETMDB = 86, | ||
129 | #define RTM_GETMDB RTM_GETMDB | ||
130 | |||
128 | __RTM_MAX, | 131 | __RTM_MAX, |
129 | #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) | 132 | #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) |
130 | }; | 133 | }; |
diff --git a/net/bridge/Makefile b/net/bridge/Makefile index d0359ea8ee79..e859098f5ee9 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile | |||
@@ -12,6 +12,6 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o | |||
12 | 12 | ||
13 | bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o | 13 | bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o |
14 | 14 | ||
15 | bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o | 15 | bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o |
16 | 16 | ||
17 | obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ | 17 | 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 000000000000..edc0d731f6b2 --- /dev/null +++ b/net/bridge/br_mdb.c | |||
@@ -0,0 +1,163 @@ | |||
1 | #include <linux/err.h> | ||
2 | #include <linux/igmp.h> | ||
3 | #include <linux/kernel.h> | ||
4 | #include <linux/netdevice.h> | ||
5 | #include <linux/rculist.h> | ||
6 | #include <linux/skbuff.h> | ||
7 | #include <net/ip.h> | ||
8 | #include <net/netlink.h> | ||
9 | #if IS_ENABLED(CONFIG_IPV6) | ||
10 | #include <net/ipv6.h> | ||
11 | #endif | ||
12 | |||
13 | #include "br_private.h" | ||
14 | |||
15 | static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb, | ||
16 | struct net_device *dev) | ||
17 | { | ||
18 | struct net_bridge *br = netdev_priv(dev); | ||
19 | struct net_bridge_port *p; | ||
20 | struct hlist_node *n; | ||
21 | struct nlattr *nest; | ||
22 | |||
23 | if (!br->multicast_router || hlist_empty(&br->router_list)) | ||
24 | return 0; | ||
25 | |||
26 | nest = nla_nest_start(skb, MDBA_ROUTER); | ||
27 | if (nest == NULL) | ||
28 | return -EMSGSIZE; | ||
29 | |||
30 | hlist_for_each_entry_rcu(p, n, &br->router_list, rlist) { | ||
31 | if (p && nla_put_u32(skb, MDBA_ROUTER_PORT, p->dev->ifindex)) | ||
32 | goto fail; | ||
33 | } | ||
34 | |||
35 | nla_nest_end(skb, nest); | ||
36 | return 0; | ||
37 | fail: | ||
38 | nla_nest_cancel(skb, nest); | ||
39 | return -EMSGSIZE; | ||
40 | } | ||
41 | |||
42 | static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, | ||
43 | struct net_device *dev) | ||
44 | { | ||
45 | struct net_bridge *br = netdev_priv(dev); | ||
46 | struct net_bridge_mdb_htable *mdb; | ||
47 | struct nlattr *nest, *nest2; | ||
48 | int i, err = 0; | ||
49 | int idx = 0, s_idx = cb->args[1]; | ||
50 | |||
51 | if (br->multicast_disabled) | ||
52 | return 0; | ||
53 | |||
54 | mdb = rcu_dereference(br->mdb); | ||
55 | if (!mdb) | ||
56 | return 0; | ||
57 | |||
58 | nest = nla_nest_start(skb, MDBA_MDB); | ||
59 | if (nest == NULL) | ||
60 | return -EMSGSIZE; | ||
61 | |||
62 | for (i = 0; i < mdb->max; i++) { | ||
63 | struct hlist_node *h; | ||
64 | struct net_bridge_mdb_entry *mp; | ||
65 | struct net_bridge_port_group *p, **pp; | ||
66 | struct net_bridge_port *port; | ||
67 | |||
68 | hlist_for_each_entry_rcu(mp, h, &mdb->mhash[i], hlist[mdb->ver]) { | ||
69 | if (idx < s_idx) | ||
70 | goto skip; | ||
71 | |||
72 | nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY); | ||
73 | if (nest2 == NULL) { | ||
74 | err = -EMSGSIZE; | ||
75 | goto out; | ||
76 | } | ||
77 | |||
78 | for (pp = &mp->ports; | ||
79 | (p = rcu_dereference(*pp)) != NULL; | ||
80 | pp = &p->next) { | ||
81 | port = p->port; | ||
82 | if (port) { | ||
83 | struct br_mdb_entry e; | ||
84 | e.ifindex = port->dev->ifindex; | ||
85 | e.addr.u.ip4 = p->addr.u.ip4; | ||
86 | #if IS_ENABLED(CONFIG_IPV6) | ||
87 | e.addr.u.ip6 = p->addr.u.ip6; | ||
88 | #endif | ||
89 | e.addr.proto = p->addr.proto; | ||
90 | if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(e), &e)) { | ||
91 | nla_nest_cancel(skb, nest2); | ||
92 | err = -EMSGSIZE; | ||
93 | goto out; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | nla_nest_end(skb, nest2); | ||
98 | skip: | ||
99 | idx++; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | out: | ||
104 | cb->args[1] = idx; | ||
105 | nla_nest_end(skb, nest); | ||
106 | return err; | ||
107 | } | ||
108 | |||
109 | static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
110 | { | ||
111 | struct net_device *dev; | ||
112 | struct net *net = sock_net(skb->sk); | ||
113 | struct nlmsghdr *nlh = NULL; | ||
114 | int idx = 0, s_idx; | ||
115 | |||
116 | s_idx = cb->args[0]; | ||
117 | |||
118 | rcu_read_lock(); | ||
119 | |||
120 | /* TODO: in case of rehashing, we need to check | ||
121 | * consistency for dumping. | ||
122 | */ | ||
123 | cb->seq = net->dev_base_seq; | ||
124 | |||
125 | for_each_netdev_rcu(net, dev) { | ||
126 | if (dev->priv_flags & IFF_EBRIDGE) { | ||
127 | struct br_port_msg *bpm; | ||
128 | |||
129 | if (idx < s_idx) | ||
130 | goto skip; | ||
131 | |||
132 | nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, | ||
133 | cb->nlh->nlmsg_seq, RTM_GETMDB, | ||
134 | sizeof(*bpm), NLM_F_MULTI); | ||
135 | if (nlh == NULL) | ||
136 | break; | ||
137 | |||
138 | bpm = nlmsg_data(nlh); | ||
139 | bpm->ifindex = dev->ifindex; | ||
140 | if (br_mdb_fill_info(skb, cb, dev) < 0) | ||
141 | goto out; | ||
142 | if (br_rports_fill_info(skb, cb, dev) < 0) | ||
143 | goto out; | ||
144 | |||
145 | cb->args[1] = 0; | ||
146 | nlmsg_end(skb, nlh); | ||
147 | skip: | ||
148 | idx++; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | out: | ||
153 | if (nlh) | ||
154 | nlmsg_end(skb, nlh); | ||
155 | rcu_read_unlock(); | ||
156 | cb->args[0] = idx; | ||
157 | return skb->len; | ||
158 | } | ||
159 | |||
160 | void br_mdb_init(void) | ||
161 | { | ||
162 | rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, NULL); | ||
163 | } | ||
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index a2a7a1a79081..68e375ac93bd 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) | |||
1605 | br_multicast_querier_expired, (unsigned long)br); | 1605 | br_multicast_querier_expired, (unsigned long)br); |
1606 | setup_timer(&br->multicast_query_timer, br_multicast_query_expired, | 1606 | setup_timer(&br->multicast_query_timer, br_multicast_query_expired, |
1607 | (unsigned long)br); | 1607 | (unsigned long)br); |
1608 | br_mdb_init(); | ||
1608 | } | 1609 | } |
1609 | 1610 | ||
1610 | void br_multicast_open(struct net_bridge *br) | 1611 | void br_multicast_open(struct net_bridge *br) |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index cd86222cf5e3..ae0a6ec0a702 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, | |||
433 | extern int br_multicast_toggle(struct net_bridge *br, unsigned long val); | 433 | extern int br_multicast_toggle(struct net_bridge *br, unsigned long val); |
434 | extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val); | 434 | extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val); |
435 | extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); | 435 | extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); |
436 | extern void br_mdb_init(void); | ||
436 | 437 | ||
437 | static inline bool br_multicast_is_router(struct net_bridge *br) | 438 | static inline bool br_multicast_is_router(struct net_bridge *br) |
438 | { | 439 | { |
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index d309e7f472d8..163aaa77d5aa 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c | |||
@@ -67,6 +67,7 @@ static struct nlmsg_perm nlmsg_route_perms[] = | |||
67 | { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | 67 | { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ }, |
68 | { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | 68 | { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, |
69 | { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | 69 | { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, |
70 | { RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
70 | }; | 71 | }; |
71 | 72 | ||
72 | static struct nlmsg_perm nlmsg_tcpdiag_perms[] = | 73 | static struct nlmsg_perm nlmsg_tcpdiag_perms[] = |