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 /net/bridge/br_mdb.c | |
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>
Diffstat (limited to 'net/bridge/br_mdb.c')
-rw-r--r-- | net/bridge/br_mdb.c | 163 |
1 files changed, 163 insertions, 0 deletions
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 | } | ||