Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
ee07c6e7 CW |
2 | #include <linux/err.h> |
3 | #include <linux/igmp.h> | |
4 | #include <linux/kernel.h> | |
5 | #include <linux/netdevice.h> | |
6 | #include <linux/rculist.h> | |
7 | #include <linux/skbuff.h> | |
cfd56754 | 8 | #include <linux/if_ether.h> |
ee07c6e7 CW |
9 | #include <net/ip.h> |
10 | #include <net/netlink.h> | |
f1fecb1d | 11 | #include <net/switchdev.h> |
ee07c6e7 CW |
12 | #if IS_ENABLED(CONFIG_IPV6) |
13 | #include <net/ipv6.h> | |
3c3769e6 | 14 | #include <net/addrconf.h> |
ee07c6e7 CW |
15 | #endif |
16 | ||
17 | #include "br_private.h" | |
18 | ||
ff391c5d | 19 | static bool |
e04d377f NA |
20 | br_ip4_rports_get_timer(struct net_bridge_mcast_port *pmctx, |
21 | unsigned long *timer) | |
ff391c5d | 22 | { |
e04d377f NA |
23 | *timer = br_timer_value(&pmctx->ip4_mc_router_timer); |
24 | return !hlist_unhashed(&pmctx->ip4_rlist); | |
ff391c5d LL |
25 | } |
26 | ||
27 | static bool | |
e04d377f NA |
28 | br_ip6_rports_get_timer(struct net_bridge_mcast_port *pmctx, |
29 | unsigned long *timer) | |
ff391c5d | 30 | { |
a3c02e76 | 31 | #if IS_ENABLED(CONFIG_IPV6) |
e04d377f NA |
32 | *timer = br_timer_value(&pmctx->ip6_mc_router_timer); |
33 | return !hlist_unhashed(&pmctx->ip6_rlist); | |
a3c02e76 | 34 | #else |
ff391c5d LL |
35 | *timer = 0; |
36 | return false; | |
a3c02e76 | 37 | #endif |
ff391c5d LL |
38 | } |
39 | ||
05d6f38e NA |
40 | static size_t __br_rports_one_size(void) |
41 | { | |
42 | return nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PORT */ | |
43 | nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_TIMER */ | |
44 | nla_total_size(sizeof(u8)) + /* MDBA_ROUTER_PATTR_TYPE */ | |
45 | nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_INET_TIMER */ | |
46 | nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_INET6_TIMER */ | |
47 | nla_total_size(sizeof(u32)); /* MDBA_ROUTER_PATTR_VID */ | |
48 | } | |
49 | ||
50 | size_t br_rports_size(const struct net_bridge_mcast *brmctx) | |
51 | { | |
52 | struct net_bridge_mcast_port *pmctx; | |
53 | size_t size = nla_total_size(0); /* MDBA_ROUTER */ | |
54 | ||
55 | rcu_read_lock(); | |
56 | hlist_for_each_entry_rcu(pmctx, &brmctx->ip4_mc_router_list, | |
57 | ip4_rlist) | |
58 | size += __br_rports_one_size(); | |
59 | ||
60 | #if IS_ENABLED(CONFIG_IPV6) | |
61 | hlist_for_each_entry_rcu(pmctx, &brmctx->ip6_mc_router_list, | |
62 | ip6_rlist) | |
63 | size += __br_rports_one_size(); | |
64 | #endif | |
65 | rcu_read_unlock(); | |
66 | ||
67 | return size; | |
68 | } | |
69 | ||
dc002875 NA |
70 | int br_rports_fill_info(struct sk_buff *skb, |
71 | const struct net_bridge_mcast *brmctx) | |
ee07c6e7 | 72 | { |
e04d377f | 73 | u16 vid = brmctx->vlan ? brmctx->vlan->vid : 0; |
ff391c5d LL |
74 | bool have_ip4_mc_rtr, have_ip6_mc_rtr; |
75 | unsigned long ip4_timer, ip6_timer; | |
59f78f9f | 76 | struct nlattr *nest, *port_nest; |
ff391c5d LL |
77 | struct net_bridge_port *p; |
78 | ||
e04d377f | 79 | if (!brmctx->multicast_router || !br_rports_have_mc_router(brmctx)) |
ee07c6e7 CW |
80 | return 0; |
81 | ||
ae0be8de | 82 | nest = nla_nest_start_noflag(skb, MDBA_ROUTER); |
ee07c6e7 CW |
83 | if (nest == NULL) |
84 | return -EMSGSIZE; | |
85 | ||
e04d377f NA |
86 | list_for_each_entry_rcu(p, &brmctx->br->port_list, list) { |
87 | struct net_bridge_mcast_port *pmctx; | |
88 | ||
89 | if (vid) { | |
90 | struct net_bridge_vlan *v; | |
91 | ||
92 | v = br_vlan_find(nbp_vlan_group(p), vid); | |
93 | if (!v) | |
94 | continue; | |
95 | pmctx = &v->port_mcast_ctx; | |
96 | } else { | |
97 | pmctx = &p->multicast_ctx; | |
98 | } | |
99 | ||
100 | have_ip4_mc_rtr = br_ip4_rports_get_timer(pmctx, &ip4_timer); | |
101 | have_ip6_mc_rtr = br_ip6_rports_get_timer(pmctx, &ip6_timer); | |
ff391c5d LL |
102 | |
103 | if (!have_ip4_mc_rtr && !have_ip6_mc_rtr) | |
59f78f9f | 104 | continue; |
ff391c5d | 105 | |
ae0be8de | 106 | port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT); |
59f78f9f | 107 | if (!port_nest) |
ee07c6e7 | 108 | goto fail; |
ff391c5d | 109 | |
59f78f9f NA |
110 | if (nla_put_nohdr(skb, sizeof(u32), &p->dev->ifindex) || |
111 | nla_put_u32(skb, MDBA_ROUTER_PATTR_TIMER, | |
ff391c5d | 112 | max(ip4_timer, ip6_timer)) || |
59f78f9f | 113 | nla_put_u8(skb, MDBA_ROUTER_PATTR_TYPE, |
9632233e | 114 | p->multicast_ctx.multicast_router) || |
b7fb0916 LL |
115 | (have_ip4_mc_rtr && |
116 | nla_put_u32(skb, MDBA_ROUTER_PATTR_INET_TIMER, | |
117 | ip4_timer)) || | |
118 | (have_ip6_mc_rtr && | |
119 | nla_put_u32(skb, MDBA_ROUTER_PATTR_INET6_TIMER, | |
dc002875 NA |
120 | ip6_timer)) || |
121 | (vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid))) { | |
59f78f9f NA |
122 | nla_nest_cancel(skb, port_nest); |
123 | goto fail; | |
124 | } | |
125 | nla_nest_end(skb, port_nest); | |
ee07c6e7 CW |
126 | } |
127 | ||
128 | nla_nest_end(skb, nest); | |
129 | return 0; | |
130 | fail: | |
131 | nla_nest_cancel(skb, nest); | |
132 | return -EMSGSIZE; | |
133 | } | |
134 | ||
9d06b6d8 ER |
135 | static void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags) |
136 | { | |
137 | e->state = flags & MDB_PG_FLAGS_PERMANENT; | |
138 | e->flags = 0; | |
139 | if (flags & MDB_PG_FLAGS_OFFLOAD) | |
140 | e->flags |= MDB_FLAGS_OFFLOAD; | |
3247b272 NA |
141 | if (flags & MDB_PG_FLAGS_FAST_LEAVE) |
142 | e->flags |= MDB_FLAGS_FAST_LEAVE; | |
8266a049 NA |
143 | if (flags & MDB_PG_FLAGS_STAR_EXCL) |
144 | e->flags |= MDB_FLAGS_STAR_EXCL; | |
9116ffbf NA |
145 | if (flags & MDB_PG_FLAGS_BLOCKED) |
146 | e->flags |= MDB_FLAGS_BLOCKED; | |
9d06b6d8 ER |
147 | } |
148 | ||
88d4bd18 NA |
149 | static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip, |
150 | struct nlattr **mdb_attrs) | |
6dd684c0 ER |
151 | { |
152 | memset(ip, 0, sizeof(struct br_ip)); | |
153 | ip->vid = entry->vid; | |
154 | ip->proto = entry->addr.proto; | |
88d4bd18 NA |
155 | switch (ip->proto) { |
156 | case htons(ETH_P_IP): | |
eab3227b | 157 | ip->dst.ip4 = entry->addr.u.ip4; |
88d4bd18 NA |
158 | if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE]) |
159 | ip->src.ip4 = nla_get_in_addr(mdb_attrs[MDBE_ATTR_SOURCE]); | |
160 | break; | |
6dd684c0 | 161 | #if IS_ENABLED(CONFIG_IPV6) |
88d4bd18 | 162 | case htons(ETH_P_IPV6): |
eab3227b | 163 | ip->dst.ip6 = entry->addr.u.ip6; |
88d4bd18 NA |
164 | if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE]) |
165 | ip->src.ip6 = nla_get_in6_addr(mdb_attrs[MDBE_ATTR_SOURCE]); | |
166 | break; | |
6dd684c0 | 167 | #endif |
955062b0 NA |
168 | default: |
169 | ether_addr_copy(ip->dst.mac_addr, entry->addr.u.mac_addr); | |
88d4bd18 NA |
170 | } |
171 | ||
6dd684c0 ER |
172 | } |
173 | ||
5205e919 NA |
174 | static int __mdb_fill_srcs(struct sk_buff *skb, |
175 | struct net_bridge_port_group *p) | |
176 | { | |
177 | struct net_bridge_group_src *ent; | |
178 | struct nlattr *nest, *nest_ent; | |
179 | ||
180 | if (hlist_empty(&p->src_list)) | |
181 | return 0; | |
182 | ||
183 | nest = nla_nest_start(skb, MDBA_MDB_EATTR_SRC_LIST); | |
184 | if (!nest) | |
185 | return -EMSGSIZE; | |
186 | ||
187 | hlist_for_each_entry_rcu(ent, &p->src_list, node, | |
085b53c8 | 188 | lockdep_is_held(&p->key.port->br->multicast_lock)) { |
5205e919 NA |
189 | nest_ent = nla_nest_start(skb, MDBA_MDB_SRCLIST_ENTRY); |
190 | if (!nest_ent) | |
191 | goto out_cancel_err; | |
192 | switch (ent->addr.proto) { | |
193 | case htons(ETH_P_IP): | |
194 | if (nla_put_in_addr(skb, MDBA_MDB_SRCATTR_ADDRESS, | |
deb96566 | 195 | ent->addr.src.ip4)) { |
5205e919 NA |
196 | nla_nest_cancel(skb, nest_ent); |
197 | goto out_cancel_err; | |
198 | } | |
199 | break; | |
200 | #if IS_ENABLED(CONFIG_IPV6) | |
201 | case htons(ETH_P_IPV6): | |
202 | if (nla_put_in6_addr(skb, MDBA_MDB_SRCATTR_ADDRESS, | |
deb96566 | 203 | &ent->addr.src.ip6)) { |
5205e919 NA |
204 | nla_nest_cancel(skb, nest_ent); |
205 | goto out_cancel_err; | |
206 | } | |
207 | break; | |
208 | #endif | |
209 | default: | |
210 | nla_nest_cancel(skb, nest_ent); | |
211 | continue; | |
212 | } | |
213 | if (nla_put_u32(skb, MDBA_MDB_SRCATTR_TIMER, | |
214 | br_timer_value(&ent->timer))) { | |
215 | nla_nest_cancel(skb, nest_ent); | |
216 | goto out_cancel_err; | |
217 | } | |
218 | nla_nest_end(skb, nest_ent); | |
219 | } | |
220 | ||
221 | nla_nest_end(skb, nest); | |
222 | ||
223 | return 0; | |
224 | ||
225 | out_cancel_err: | |
226 | nla_nest_cancel(skb, nest); | |
227 | return -EMSGSIZE; | |
228 | } | |
229 | ||
6545916e | 230 | static int __mdb_fill_info(struct sk_buff *skb, |
e77b0c84 | 231 | struct net_bridge_mdb_entry *mp, |
6545916e NA |
232 | struct net_bridge_port_group *p) |
233 | { | |
5205e919 | 234 | bool dump_srcs_mode = false; |
e77b0c84 | 235 | struct timer_list *mtimer; |
6545916e NA |
236 | struct nlattr *nest_ent; |
237 | struct br_mdb_entry e; | |
e77b0c84 NA |
238 | u8 flags = 0; |
239 | int ifindex; | |
6545916e NA |
240 | |
241 | memset(&e, 0, sizeof(e)); | |
e77b0c84 | 242 | if (p) { |
085b53c8 | 243 | ifindex = p->key.port->dev->ifindex; |
e77b0c84 NA |
244 | mtimer = &p->timer; |
245 | flags = p->flags; | |
246 | } else { | |
247 | ifindex = mp->br->dev->ifindex; | |
248 | mtimer = &mp->timer; | |
249 | } | |
250 | ||
251 | __mdb_entry_fill_flags(&e, flags); | |
252 | e.ifindex = ifindex; | |
253 | e.vid = mp->addr.vid; | |
e65693b0 | 254 | if (mp->addr.proto == htons(ETH_P_IP)) { |
eab3227b | 255 | e.addr.u.ip4 = mp->addr.dst.ip4; |
6545916e | 256 | #if IS_ENABLED(CONFIG_IPV6) |
e65693b0 | 257 | } else if (mp->addr.proto == htons(ETH_P_IPV6)) { |
eab3227b | 258 | e.addr.u.ip6 = mp->addr.dst.ip6; |
6545916e | 259 | #endif |
e65693b0 | 260 | } else { |
955062b0 | 261 | ether_addr_copy(e.addr.u.mac_addr, mp->addr.dst.mac_addr); |
e65693b0 JW |
262 | e.state = MDB_PG_FLAGS_PERMANENT; |
263 | } | |
e77b0c84 | 264 | e.addr.proto = mp->addr.proto; |
6545916e NA |
265 | nest_ent = nla_nest_start_noflag(skb, |
266 | MDBA_MDB_ENTRY_INFO); | |
267 | if (!nest_ent) | |
268 | return -EMSGSIZE; | |
269 | ||
270 | if (nla_put_nohdr(skb, sizeof(e), &e) || | |
271 | nla_put_u32(skb, | |
272 | MDBA_MDB_EATTR_TIMER, | |
88d4bd18 NA |
273 | br_timer_value(mtimer))) |
274 | goto nest_err; | |
8f8cb77e | 275 | |
5205e919 NA |
276 | switch (mp->addr.proto) { |
277 | case htons(ETH_P_IP): | |
d3d065c0 | 278 | dump_srcs_mode = !!(mp->br->multicast_ctx.multicast_igmp_version == 3); |
88d4bd18 NA |
279 | if (mp->addr.src.ip4) { |
280 | if (nla_put_in_addr(skb, MDBA_MDB_EATTR_SOURCE, | |
281 | mp->addr.src.ip4)) | |
282 | goto nest_err; | |
283 | break; | |
284 | } | |
5205e919 NA |
285 | break; |
286 | #if IS_ENABLED(CONFIG_IPV6) | |
287 | case htons(ETH_P_IPV6): | |
d3d065c0 | 288 | dump_srcs_mode = !!(mp->br->multicast_ctx.multicast_mld_version == 2); |
88d4bd18 NA |
289 | if (!ipv6_addr_any(&mp->addr.src.ip6)) { |
290 | if (nla_put_in6_addr(skb, MDBA_MDB_EATTR_SOURCE, | |
291 | &mp->addr.src.ip6)) | |
292 | goto nest_err; | |
293 | break; | |
294 | } | |
5205e919 NA |
295 | break; |
296 | #endif | |
955062b0 NA |
297 | default: |
298 | ether_addr_copy(e.addr.u.mac_addr, mp->addr.dst.mac_addr); | |
5205e919 | 299 | } |
8f8cb77e NA |
300 | if (p) { |
301 | if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, p->rt_protocol)) | |
302 | goto nest_err; | |
303 | if (dump_srcs_mode && | |
304 | (__mdb_fill_srcs(skb, p) || | |
305 | nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE, | |
306 | p->filter_mode))) | |
307 | goto nest_err; | |
308 | } | |
6545916e NA |
309 | nla_nest_end(skb, nest_ent); |
310 | ||
311 | return 0; | |
88d4bd18 NA |
312 | |
313 | nest_err: | |
314 | nla_nest_cancel(skb, nest_ent); | |
315 | return -EMSGSIZE; | |
6545916e NA |
316 | } |
317 | ||
ee07c6e7 CW |
318 | static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, |
319 | struct net_device *dev) | |
320 | { | |
5205e919 | 321 | int idx = 0, s_idx = cb->args[1], err = 0, pidx = 0, s_pidx = cb->args[2]; |
ee07c6e7 | 322 | struct net_bridge *br = netdev_priv(dev); |
19e3a9c9 | 323 | struct net_bridge_mdb_entry *mp; |
ee07c6e7 | 324 | struct nlattr *nest, *nest2; |
ee07c6e7 | 325 | |
13cefad2 | 326 | if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) |
ee07c6e7 CW |
327 | return 0; |
328 | ||
ae0be8de | 329 | nest = nla_nest_start_noflag(skb, MDBA_MDB); |
ee07c6e7 CW |
330 | if (nest == NULL) |
331 | return -EMSGSIZE; | |
332 | ||
19e3a9c9 | 333 | hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { |
762a3d89 | 334 | struct net_bridge_port_group *p; |
335 | struct net_bridge_port_group __rcu **pp; | |
ee07c6e7 | 336 | |
19e3a9c9 NA |
337 | if (idx < s_idx) |
338 | goto skip; | |
ee07c6e7 | 339 | |
ae0be8de | 340 | nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY); |
19e3a9c9 NA |
341 | if (!nest2) { |
342 | err = -EMSGSIZE; | |
343 | break; | |
344 | } | |
ee07c6e7 | 345 | |
5205e919 | 346 | if (!s_pidx && mp->host_joined) { |
e77b0c84 NA |
347 | err = __mdb_fill_info(skb, mp, NULL); |
348 | if (err) { | |
349 | nla_nest_cancel(skb, nest2); | |
350 | break; | |
351 | } | |
352 | } | |
353 | ||
19e3a9c9 NA |
354 | for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL; |
355 | pp = &p->next) { | |
085b53c8 | 356 | if (!p->key.port) |
19e3a9c9 | 357 | continue; |
5205e919 NA |
358 | if (pidx < s_pidx) |
359 | goto skip_pg; | |
19e3a9c9 | 360 | |
e77b0c84 | 361 | err = __mdb_fill_info(skb, mp, p); |
6545916e | 362 | if (err) { |
12913f74 | 363 | nla_nest_end(skb, nest2); |
19e3a9c9 NA |
364 | goto out; |
365 | } | |
5205e919 NA |
366 | skip_pg: |
367 | pidx++; | |
ee07c6e7 | 368 | } |
5205e919 NA |
369 | pidx = 0; |
370 | s_pidx = 0; | |
19e3a9c9 NA |
371 | nla_nest_end(skb, nest2); |
372 | skip: | |
373 | idx++; | |
ee07c6e7 CW |
374 | } |
375 | ||
376 | out: | |
377 | cb->args[1] = idx; | |
5205e919 | 378 | cb->args[2] = pidx; |
ee07c6e7 CW |
379 | nla_nest_end(skb, nest); |
380 | return err; | |
381 | } | |
382 | ||
c77b9364 DA |
383 | static int br_mdb_valid_dump_req(const struct nlmsghdr *nlh, |
384 | struct netlink_ext_ack *extack) | |
385 | { | |
386 | struct br_port_msg *bpm; | |
387 | ||
388 | if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*bpm))) { | |
389 | NL_SET_ERR_MSG_MOD(extack, "Invalid header for mdb dump request"); | |
390 | return -EINVAL; | |
391 | } | |
392 | ||
393 | bpm = nlmsg_data(nlh); | |
394 | if (bpm->ifindex) { | |
395 | NL_SET_ERR_MSG_MOD(extack, "Filtering by device index is not supported for mdb dump request"); | |
396 | return -EINVAL; | |
397 | } | |
398 | if (nlmsg_attrlen(nlh, sizeof(*bpm))) { | |
399 | NL_SET_ERR_MSG(extack, "Invalid data after header in mdb dump request"); | |
400 | return -EINVAL; | |
401 | } | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
ee07c6e7 CW |
406 | static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb) |
407 | { | |
408 | struct net_device *dev; | |
409 | struct net *net = sock_net(skb->sk); | |
410 | struct nlmsghdr *nlh = NULL; | |
411 | int idx = 0, s_idx; | |
412 | ||
c77b9364 DA |
413 | if (cb->strict_check) { |
414 | int err = br_mdb_valid_dump_req(cb->nlh, cb->extack); | |
415 | ||
416 | if (err < 0) | |
417 | return err; | |
418 | } | |
419 | ||
ee07c6e7 CW |
420 | s_idx = cb->args[0]; |
421 | ||
422 | rcu_read_lock(); | |
423 | ||
19e3a9c9 | 424 | cb->seq = net->dev_base_seq; |
ee07c6e7 CW |
425 | |
426 | for_each_netdev_rcu(net, dev) { | |
254ec036 | 427 | if (netif_is_bridge_master(dev)) { |
e04d377f | 428 | struct net_bridge *br = netdev_priv(dev); |
ee07c6e7 CW |
429 | struct br_port_msg *bpm; |
430 | ||
431 | if (idx < s_idx) | |
432 | goto skip; | |
433 | ||
434 | nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, | |
435 | cb->nlh->nlmsg_seq, RTM_GETMDB, | |
436 | sizeof(*bpm), NLM_F_MULTI); | |
437 | if (nlh == NULL) | |
438 | break; | |
439 | ||
440 | bpm = nlmsg_data(nlh); | |
c085c499 | 441 | memset(bpm, 0, sizeof(*bpm)); |
ee07c6e7 CW |
442 | bpm->ifindex = dev->ifindex; |
443 | if (br_mdb_fill_info(skb, cb, dev) < 0) | |
444 | goto out; | |
e04d377f | 445 | if (br_rports_fill_info(skb, &br->multicast_ctx) < 0) |
ee07c6e7 CW |
446 | goto out; |
447 | ||
448 | cb->args[1] = 0; | |
449 | nlmsg_end(skb, nlh); | |
450 | skip: | |
451 | idx++; | |
452 | } | |
453 | } | |
454 | ||
455 | out: | |
456 | if (nlh) | |
457 | nlmsg_end(skb, nlh); | |
458 | rcu_read_unlock(); | |
459 | cb->args[0] = idx; | |
460 | return skb->len; | |
461 | } | |
462 | ||
37a393bc CW |
463 | static int nlmsg_populate_mdb_fill(struct sk_buff *skb, |
464 | struct net_device *dev, | |
81f19838 NA |
465 | struct net_bridge_mdb_entry *mp, |
466 | struct net_bridge_port_group *pg, | |
467 | int type) | |
37a393bc CW |
468 | { |
469 | struct nlmsghdr *nlh; | |
470 | struct br_port_msg *bpm; | |
471 | struct nlattr *nest, *nest2; | |
472 | ||
81f19838 | 473 | nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0); |
37a393bc CW |
474 | if (!nlh) |
475 | return -EMSGSIZE; | |
476 | ||
477 | bpm = nlmsg_data(nlh); | |
c085c499 | 478 | memset(bpm, 0, sizeof(*bpm)); |
37a393bc CW |
479 | bpm->family = AF_BRIDGE; |
480 | bpm->ifindex = dev->ifindex; | |
ae0be8de | 481 | nest = nla_nest_start_noflag(skb, MDBA_MDB); |
37a393bc CW |
482 | if (nest == NULL) |
483 | goto cancel; | |
ae0be8de | 484 | nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY); |
37a393bc CW |
485 | if (nest2 == NULL) |
486 | goto end; | |
487 | ||
81f19838 | 488 | if (__mdb_fill_info(skb, mp, pg)) |
37a393bc CW |
489 | goto end; |
490 | ||
491 | nla_nest_end(skb, nest2); | |
492 | nla_nest_end(skb, nest); | |
053c095a JB |
493 | nlmsg_end(skb, nlh); |
494 | return 0; | |
37a393bc CW |
495 | |
496 | end: | |
497 | nla_nest_end(skb, nest); | |
498 | cancel: | |
499 | nlmsg_cancel(skb, nlh); | |
500 | return -EMSGSIZE; | |
501 | } | |
502 | ||
81f19838 | 503 | static size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg) |
37a393bc | 504 | { |
81f19838 NA |
505 | size_t nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) + |
506 | nla_total_size(sizeof(struct br_mdb_entry)) + | |
507 | nla_total_size(sizeof(u32)); | |
508 | struct net_bridge_group_src *ent; | |
509 | size_t addr_size = 0; | |
510 | ||
511 | if (!pg) | |
512 | goto out; | |
513 | ||
8f8cb77e NA |
514 | /* MDBA_MDB_EATTR_RTPROT */ |
515 | nlmsg_size += nla_total_size(sizeof(u8)); | |
516 | ||
085b53c8 | 517 | switch (pg->key.addr.proto) { |
81f19838 | 518 | case htons(ETH_P_IP): |
88d4bd18 | 519 | /* MDBA_MDB_EATTR_SOURCE */ |
085b53c8 | 520 | if (pg->key.addr.src.ip4) |
88d4bd18 | 521 | nlmsg_size += nla_total_size(sizeof(__be32)); |
d3d065c0 | 522 | if (pg->key.port->br->multicast_ctx.multicast_igmp_version == 2) |
81f19838 NA |
523 | goto out; |
524 | addr_size = sizeof(__be32); | |
525 | break; | |
526 | #if IS_ENABLED(CONFIG_IPV6) | |
527 | case htons(ETH_P_IPV6): | |
88d4bd18 | 528 | /* MDBA_MDB_EATTR_SOURCE */ |
085b53c8 | 529 | if (!ipv6_addr_any(&pg->key.addr.src.ip6)) |
88d4bd18 | 530 | nlmsg_size += nla_total_size(sizeof(struct in6_addr)); |
d3d065c0 | 531 | if (pg->key.port->br->multicast_ctx.multicast_mld_version == 1) |
81f19838 NA |
532 | goto out; |
533 | addr_size = sizeof(struct in6_addr); | |
534 | break; | |
535 | #endif | |
536 | } | |
537 | ||
538 | /* MDBA_MDB_EATTR_GROUP_MODE */ | |
539 | nlmsg_size += nla_total_size(sizeof(u8)); | |
540 | ||
541 | /* MDBA_MDB_EATTR_SRC_LIST nested attr */ | |
542 | if (!hlist_empty(&pg->src_list)) | |
543 | nlmsg_size += nla_total_size(0); | |
544 | ||
545 | hlist_for_each_entry(ent, &pg->src_list, node) { | |
546 | /* MDBA_MDB_SRCLIST_ENTRY nested attr + | |
547 | * MDBA_MDB_SRCATTR_ADDRESS + MDBA_MDB_SRCATTR_TIMER | |
548 | */ | |
549 | nlmsg_size += nla_total_size(0) + | |
550 | nla_total_size(addr_size) + | |
551 | nla_total_size(sizeof(u32)); | |
552 | } | |
553 | out: | |
554 | return nlmsg_size; | |
37a393bc CW |
555 | } |
556 | ||
9ae9ff99 VO |
557 | void br_mdb_notify(struct net_device *dev, |
558 | struct net_bridge_mdb_entry *mp, | |
559 | struct net_bridge_port_group *pg, | |
560 | int type) | |
561 | { | |
562 | struct net *net = dev_net(dev); | |
563 | struct sk_buff *skb; | |
564 | int err = -ENOBUFS; | |
565 | ||
566 | br_switchdev_mdb_notify(dev, mp, pg, type); | |
f1fecb1d | 567 | |
81f19838 | 568 | skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC); |
37a393bc CW |
569 | if (!skb) |
570 | goto errout; | |
571 | ||
81f19838 | 572 | err = nlmsg_populate_mdb_fill(skb, dev, mp, pg, type); |
37a393bc CW |
573 | if (err < 0) { |
574 | kfree_skb(skb); | |
575 | goto errout; | |
576 | } | |
577 | ||
578 | rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC); | |
579 | return; | |
580 | errout: | |
581 | rtnl_set_sk_err(net, RTNLGRP_MDB, err); | |
582 | } | |
583 | ||
949f1e39 SA |
584 | static int nlmsg_populate_rtr_fill(struct sk_buff *skb, |
585 | struct net_device *dev, | |
1e9ca456 | 586 | int ifindex, u16 vid, u32 pid, |
949f1e39 SA |
587 | u32 seq, int type, unsigned int flags) |
588 | { | |
1e9ca456 | 589 | struct nlattr *nest, *port_nest; |
949f1e39 SA |
590 | struct br_port_msg *bpm; |
591 | struct nlmsghdr *nlh; | |
949f1e39 | 592 | |
94a72b3f | 593 | nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), 0); |
949f1e39 SA |
594 | if (!nlh) |
595 | return -EMSGSIZE; | |
596 | ||
597 | bpm = nlmsg_data(nlh); | |
598 | memset(bpm, 0, sizeof(*bpm)); | |
599 | bpm->family = AF_BRIDGE; | |
600 | bpm->ifindex = dev->ifindex; | |
ae0be8de | 601 | nest = nla_nest_start_noflag(skb, MDBA_ROUTER); |
949f1e39 SA |
602 | if (!nest) |
603 | goto cancel; | |
604 | ||
1e9ca456 NA |
605 | port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT); |
606 | if (!port_nest) | |
607 | goto end; | |
608 | if (nla_put_nohdr(skb, sizeof(u32), &ifindex)) { | |
609 | nla_nest_cancel(skb, port_nest); | |
949f1e39 | 610 | goto end; |
1e9ca456 NA |
611 | } |
612 | if (vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid)) { | |
613 | nla_nest_cancel(skb, port_nest); | |
614 | goto end; | |
615 | } | |
616 | nla_nest_end(skb, port_nest); | |
949f1e39 SA |
617 | |
618 | nla_nest_end(skb, nest); | |
619 | nlmsg_end(skb, nlh); | |
620 | return 0; | |
621 | ||
622 | end: | |
623 | nla_nest_end(skb, nest); | |
624 | cancel: | |
625 | nlmsg_cancel(skb, nlh); | |
626 | return -EMSGSIZE; | |
627 | } | |
628 | ||
629 | static inline size_t rtnl_rtr_nlmsg_size(void) | |
630 | { | |
631 | return NLMSG_ALIGN(sizeof(struct br_port_msg)) | |
1e9ca456 NA |
632 | + nla_total_size(sizeof(__u32)) |
633 | + nla_total_size(sizeof(u16)); | |
949f1e39 SA |
634 | } |
635 | ||
1e9ca456 | 636 | void br_rtr_notify(struct net_device *dev, struct net_bridge_mcast_port *pmctx, |
949f1e39 SA |
637 | int type) |
638 | { | |
639 | struct net *net = dev_net(dev); | |
640 | struct sk_buff *skb; | |
641 | int err = -ENOBUFS; | |
642 | int ifindex; | |
1e9ca456 | 643 | u16 vid; |
949f1e39 | 644 | |
1e9ca456 NA |
645 | ifindex = pmctx ? pmctx->port->dev->ifindex : 0; |
646 | vid = pmctx && br_multicast_port_ctx_is_vlan(pmctx) ? pmctx->vlan->vid : | |
647 | 0; | |
949f1e39 SA |
648 | skb = nlmsg_new(rtnl_rtr_nlmsg_size(), GFP_ATOMIC); |
649 | if (!skb) | |
650 | goto errout; | |
651 | ||
1e9ca456 NA |
652 | err = nlmsg_populate_rtr_fill(skb, dev, ifindex, vid, 0, 0, type, |
653 | NTF_SELF); | |
949f1e39 SA |
654 | if (err < 0) { |
655 | kfree_skb(skb); | |
656 | goto errout; | |
657 | } | |
658 | ||
659 | rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC); | |
660 | return; | |
661 | ||
662 | errout: | |
663 | rtnl_set_sk_err(net, RTNLGRP_MDB, err); | |
664 | } | |
665 | ||
2ac95dfe NA |
666 | static bool is_valid_mdb_entry(struct br_mdb_entry *entry, |
667 | struct netlink_ext_ack *extack) | |
cfd56754 | 668 | { |
2ac95dfe NA |
669 | if (entry->ifindex == 0) { |
670 | NL_SET_ERR_MSG_MOD(extack, "Zero entry ifindex is not allowed"); | |
cfd56754 | 671 | return false; |
2ac95dfe | 672 | } |
cfd56754 CW |
673 | |
674 | if (entry->addr.proto == htons(ETH_P_IP)) { | |
2ac95dfe NA |
675 | if (!ipv4_is_multicast(entry->addr.u.ip4)) { |
676 | NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is not multicast"); | |
cfd56754 | 677 | return false; |
2ac95dfe NA |
678 | } |
679 | if (ipv4_is_local_multicast(entry->addr.u.ip4)) { | |
680 | NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is local multicast"); | |
cfd56754 | 681 | return false; |
2ac95dfe | 682 | } |
cfd56754 CW |
683 | #if IS_ENABLED(CONFIG_IPV6) |
684 | } else if (entry->addr.proto == htons(ETH_P_IPV6)) { | |
2ac95dfe NA |
685 | if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) { |
686 | NL_SET_ERR_MSG_MOD(extack, "IPv6 entry group address is link-local all nodes"); | |
cfd56754 | 687 | return false; |
2ac95dfe | 688 | } |
cfd56754 | 689 | #endif |
955062b0 NA |
690 | } else if (entry->addr.proto == 0) { |
691 | /* L2 mdb */ | |
692 | if (!is_multicast_ether_addr(entry->addr.u.mac_addr)) { | |
693 | NL_SET_ERR_MSG_MOD(extack, "L2 entry group is not multicast"); | |
694 | return false; | |
695 | } | |
2ac95dfe NA |
696 | } else { |
697 | NL_SET_ERR_MSG_MOD(extack, "Unknown entry protocol"); | |
cfd56754 | 698 | return false; |
2ac95dfe NA |
699 | } |
700 | ||
701 | if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY) { | |
702 | NL_SET_ERR_MSG_MOD(extack, "Unknown entry state"); | |
ccb1c31a | 703 | return false; |
2ac95dfe NA |
704 | } |
705 | if (entry->vid >= VLAN_VID_MASK) { | |
706 | NL_SET_ERR_MSG_MOD(extack, "Invalid entry VLAN id"); | |
74fe61f1 | 707 | return false; |
2ac95dfe | 708 | } |
cfd56754 CW |
709 | |
710 | return true; | |
711 | } | |
712 | ||
88d4bd18 NA |
713 | static bool is_valid_mdb_source(struct nlattr *attr, __be16 proto, |
714 | struct netlink_ext_ack *extack) | |
715 | { | |
716 | switch (proto) { | |
717 | case htons(ETH_P_IP): | |
718 | if (nla_len(attr) != sizeof(struct in_addr)) { | |
719 | NL_SET_ERR_MSG_MOD(extack, "IPv4 invalid source address length"); | |
720 | return false; | |
721 | } | |
722 | if (ipv4_is_multicast(nla_get_in_addr(attr))) { | |
723 | NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed"); | |
724 | return false; | |
725 | } | |
726 | break; | |
727 | #if IS_ENABLED(CONFIG_IPV6) | |
728 | case htons(ETH_P_IPV6): { | |
729 | struct in6_addr src; | |
730 | ||
731 | if (nla_len(attr) != sizeof(struct in6_addr)) { | |
732 | NL_SET_ERR_MSG_MOD(extack, "IPv6 invalid source address length"); | |
733 | return false; | |
734 | } | |
735 | src = nla_get_in6_addr(attr); | |
736 | if (ipv6_addr_is_multicast(&src)) { | |
737 | NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed"); | |
738 | return false; | |
739 | } | |
740 | break; | |
741 | } | |
742 | #endif | |
743 | default: | |
744 | NL_SET_ERR_MSG_MOD(extack, "Invalid protocol used with source address"); | |
745 | return false; | |
746 | } | |
747 | ||
748 | return true; | |
749 | } | |
750 | ||
9c4258c7 | 751 | static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { |
88d4bd18 NA |
752 | [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, |
753 | sizeof(struct in_addr), | |
754 | sizeof(struct in6_addr)), | |
9c4258c7 NA |
755 | }; |
756 | ||
cfd56754 | 757 | static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh, |
2ac95dfe | 758 | struct net_device **pdev, struct br_mdb_entry **pentry, |
9c4258c7 | 759 | struct nlattr **mdb_attrs, struct netlink_ext_ack *extack) |
cfd56754 CW |
760 | { |
761 | struct net *net = sock_net(skb->sk); | |
762 | struct br_mdb_entry *entry; | |
763 | struct br_port_msg *bpm; | |
764 | struct nlattr *tb[MDBA_SET_ENTRY_MAX+1]; | |
765 | struct net_device *dev; | |
766 | int err; | |
767 | ||
8cb08174 JB |
768 | err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb, |
769 | MDBA_SET_ENTRY_MAX, NULL, NULL); | |
cfd56754 CW |
770 | if (err < 0) |
771 | return err; | |
772 | ||
773 | bpm = nlmsg_data(nlh); | |
774 | if (bpm->ifindex == 0) { | |
2ac95dfe | 775 | NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex"); |
cfd56754 CW |
776 | return -EINVAL; |
777 | } | |
778 | ||
779 | dev = __dev_get_by_index(net, bpm->ifindex); | |
780 | if (dev == NULL) { | |
2ac95dfe | 781 | NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist"); |
cfd56754 CW |
782 | return -ENODEV; |
783 | } | |
784 | ||
254ec036 | 785 | if (!netif_is_bridge_master(dev)) { |
2ac95dfe | 786 | NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge"); |
cfd56754 CW |
787 | return -EOPNOTSUPP; |
788 | } | |
789 | ||
790 | *pdev = dev; | |
791 | ||
2ac95dfe NA |
792 | if (!tb[MDBA_SET_ENTRY]) { |
793 | NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute"); | |
cfd56754 CW |
794 | return -EINVAL; |
795 | } | |
2ac95dfe NA |
796 | if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) { |
797 | NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length"); | |
cfd56754 CW |
798 | return -EINVAL; |
799 | } | |
800 | ||
2ac95dfe NA |
801 | entry = nla_data(tb[MDBA_SET_ENTRY]); |
802 | if (!is_valid_mdb_entry(entry, extack)) | |
803 | return -EINVAL; | |
cfd56754 | 804 | *pentry = entry; |
2ac95dfe | 805 | |
9c4258c7 NA |
806 | if (tb[MDBA_SET_ENTRY_ATTRS]) { |
807 | err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX, | |
808 | tb[MDBA_SET_ENTRY_ATTRS], | |
809 | br_mdbe_attrs_pol, extack); | |
810 | if (err) | |
811 | return err; | |
88d4bd18 NA |
812 | if (mdb_attrs[MDBE_ATTR_SOURCE] && |
813 | !is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE], | |
814 | entry->addr.proto, extack)) | |
815 | return -EINVAL; | |
9c4258c7 NA |
816 | } else { |
817 | memset(mdb_attrs, 0, | |
818 | sizeof(struct nlattr *) * (MDBE_ATTR_MAX + 1)); | |
819 | } | |
820 | ||
cfd56754 CW |
821 | return 0; |
822 | } | |
823 | ||
6567cb43 NA |
824 | static struct net_bridge_mcast * |
825 | __br_mdb_choose_context(struct net_bridge *br, | |
826 | const struct br_mdb_entry *entry, | |
827 | struct netlink_ext_ack *extack) | |
828 | { | |
829 | struct net_bridge_mcast *brmctx = NULL; | |
830 | struct net_bridge_vlan *v; | |
831 | ||
832 | if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) { | |
833 | brmctx = &br->multicast_ctx; | |
834 | goto out; | |
835 | } | |
836 | ||
837 | if (!entry->vid) { | |
838 | NL_SET_ERR_MSG_MOD(extack, "Cannot add an entry without a vlan when vlan snooping is enabled"); | |
839 | goto out; | |
840 | } | |
841 | ||
842 | v = br_vlan_find(br_vlan_group(br), entry->vid); | |
843 | if (!v) { | |
844 | NL_SET_ERR_MSG_MOD(extack, "Vlan is not configured"); | |
845 | goto out; | |
846 | } | |
847 | if (br_multicast_ctx_vlan_global_disabled(&v->br_mcast_ctx)) { | |
848 | NL_SET_ERR_MSG_MOD(extack, "Vlan's multicast processing is disabled"); | |
849 | goto out; | |
850 | } | |
851 | brmctx = &v->br_mcast_ctx; | |
852 | out: | |
853 | return brmctx; | |
854 | } | |
855 | ||
cfd56754 | 856 | static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, |
8f8cb77e NA |
857 | struct br_mdb_entry *entry, |
858 | struct nlattr **mdb_attrs, | |
83f7398e | 859 | struct netlink_ext_ack *extack) |
cfd56754 | 860 | { |
8266a049 | 861 | struct net_bridge_mdb_entry *mp, *star_mp; |
cfd56754 | 862 | struct net_bridge_port_group __rcu **pp; |
6567cb43 NA |
863 | struct net_bridge_port_group *p; |
864 | struct net_bridge_mcast *brmctx; | |
8266a049 | 865 | struct br_ip group, star_group; |
f7e2965d | 866 | unsigned long now = jiffies; |
0e761ac0 | 867 | unsigned char flags = 0; |
88d4bd18 | 868 | u8 filter_mode; |
cfd56754 | 869 | |
8f8cb77e NA |
870 | __mdb_entry_to_br_ip(entry, &group, mdb_attrs); |
871 | ||
6567cb43 NA |
872 | brmctx = __br_mdb_choose_context(br, entry, extack); |
873 | if (!brmctx) | |
874 | return -EINVAL; | |
875 | ||
88d4bd18 | 876 | /* host join errors which can happen before creating the group */ |
e65693b0 JW |
877 | if (!port && !br_group_is_l2(&group)) { |
878 | /* don't allow any flags for host-joined IP groups */ | |
88d4bd18 NA |
879 | if (entry->state) { |
880 | NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups"); | |
881 | return -EINVAL; | |
882 | } | |
8f8cb77e | 883 | if (!br_multicast_is_star_g(&group)) { |
88d4bd18 NA |
884 | NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined"); |
885 | return -EINVAL; | |
886 | } | |
887 | } | |
888 | ||
955062b0 NA |
889 | if (br_group_is_l2(&group) && entry->state != MDB_PERMANENT) { |
890 | NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed"); | |
891 | return -EINVAL; | |
892 | } | |
893 | ||
d1942cd4 IS |
894 | mp = br_multicast_new_group(br, &group); |
895 | if (IS_ERR(mp)) | |
896 | return PTR_ERR(mp); | |
cfd56754 | 897 | |
1bc844ee NA |
898 | /* host join */ |
899 | if (!port) { | |
83f7398e NA |
900 | if (mp->host_joined) { |
901 | NL_SET_ERR_MSG_MOD(extack, "Group is already joined by host"); | |
1bc844ee | 902 | return -EEXIST; |
83f7398e | 903 | } |
1bc844ee | 904 | |
58d913a3 | 905 | br_multicast_host_join(brmctx, mp, false); |
81f19838 | 906 | br_mdb_notify(br->dev, mp, NULL, RTM_NEWMDB); |
1bc844ee NA |
907 | |
908 | return 0; | |
909 | } | |
910 | ||
cfd56754 CW |
911 | for (pp = &mp->ports; |
912 | (p = mlock_dereference(*pp, br)) != NULL; | |
913 | pp = &p->next) { | |
085b53c8 | 914 | if (p->key.port == port) { |
83f7398e | 915 | NL_SET_ERR_MSG_MOD(extack, "Group is already joined by port"); |
cfd56754 | 916 | return -EEXIST; |
83f7398e | 917 | } |
085b53c8 | 918 | if ((unsigned long)p->key.port < (unsigned long)port) |
cfd56754 CW |
919 | break; |
920 | } | |
921 | ||
8f8cb77e NA |
922 | filter_mode = br_multicast_is_star_g(&group) ? MCAST_EXCLUDE : |
923 | MCAST_INCLUDE; | |
88d4bd18 | 924 | |
0e761ac0 VO |
925 | if (entry->state == MDB_PERMANENT) |
926 | flags |= MDB_PG_FLAGS_PERMANENT; | |
927 | ||
928 | p = br_multicast_new_port_group(port, &group, *pp, flags, NULL, | |
8f8cb77e | 929 | filter_mode, RTPROT_STATIC); |
83f7398e NA |
930 | if (unlikely(!p)) { |
931 | NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new port group"); | |
cfd56754 | 932 | return -ENOMEM; |
83f7398e | 933 | } |
cfd56754 | 934 | rcu_assign_pointer(*pp, p); |
79abc875 | 935 | if (entry->state == MDB_TEMPORARY) |
d3d065c0 | 936 | mod_timer(&p->timer, |
6567cb43 | 937 | now + brmctx->multicast_membership_interval); |
81f19838 | 938 | br_mdb_notify(br->dev, mp, p, RTM_NEWMDB); |
8266a049 NA |
939 | /* if we are adding a new EXCLUDE port group (*,G) it needs to be also |
940 | * added to all S,G entries for proper replication, if we are adding | |
941 | * a new INCLUDE port (S,G) then all of *,G EXCLUDE ports need to be | |
942 | * added to it for proper replication | |
943 | */ | |
6567cb43 | 944 | if (br_multicast_should_handle_mode(brmctx, group.proto)) { |
8266a049 NA |
945 | switch (filter_mode) { |
946 | case MCAST_EXCLUDE: | |
947 | br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE); | |
948 | break; | |
949 | case MCAST_INCLUDE: | |
950 | star_group = p->key.addr; | |
951 | memset(&star_group.src, 0, sizeof(star_group.src)); | |
952 | star_mp = br_mdb_ip_get(br, &star_group); | |
953 | if (star_mp) | |
954 | br_multicast_sg_add_exclude_ports(star_mp, p); | |
955 | break; | |
956 | } | |
957 | } | |
cfd56754 | 958 | |
cfd56754 CW |
959 | return 0; |
960 | } | |
961 | ||
962 | static int __br_mdb_add(struct net *net, struct net_bridge *br, | |
7eea629d | 963 | struct net_bridge_port *p, |
83f7398e | 964 | struct br_mdb_entry *entry, |
88d4bd18 | 965 | struct nlattr **mdb_attrs, |
83f7398e | 966 | struct netlink_ext_ack *extack) |
cfd56754 | 967 | { |
cfd56754 CW |
968 | int ret; |
969 | ||
cfd56754 | 970 | spin_lock_bh(&br->multicast_lock); |
8f8cb77e | 971 | ret = br_mdb_add_group(br, p, entry, mdb_attrs, extack); |
cfd56754 | 972 | spin_unlock_bh(&br->multicast_lock); |
7eea629d | 973 | |
cfd56754 CW |
974 | return ret; |
975 | } | |
976 | ||
c21ef3e3 DA |
977 | static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, |
978 | struct netlink_ext_ack *extack) | |
cfd56754 | 979 | { |
9c4258c7 | 980 | struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; |
cfd56754 | 981 | struct net *net = sock_net(skb->sk); |
2594e906 | 982 | struct net_bridge_vlan_group *vg; |
1bc844ee | 983 | struct net_bridge_port *p = NULL; |
e44deb2f | 984 | struct net_device *dev, *pdev; |
cfd56754 | 985 | struct br_mdb_entry *entry; |
2594e906 | 986 | struct net_bridge_vlan *v; |
cfd56754 CW |
987 | struct net_bridge *br; |
988 | int err; | |
989 | ||
9c4258c7 | 990 | err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); |
cfd56754 CW |
991 | if (err < 0) |
992 | return err; | |
993 | ||
994 | br = netdev_priv(dev); | |
995 | ||
83f7398e NA |
996 | if (!netif_running(br->dev)) { |
997 | NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running"); | |
7eea629d | 998 | return -EINVAL; |
83f7398e NA |
999 | } |
1000 | ||
1001 | if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { | |
1002 | NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled"); | |
1003 | return -EINVAL; | |
1004 | } | |
7eea629d | 1005 | |
1bc844ee NA |
1006 | if (entry->ifindex != br->dev->ifindex) { |
1007 | pdev = __dev_get_by_index(net, entry->ifindex); | |
83f7398e NA |
1008 | if (!pdev) { |
1009 | NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist"); | |
1bc844ee | 1010 | return -ENODEV; |
83f7398e | 1011 | } |
e44deb2f | 1012 | |
1bc844ee | 1013 | p = br_port_get_rtnl(pdev); |
83f7398e NA |
1014 | if (!p) { |
1015 | NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port"); | |
1016 | return -EINVAL; | |
1017 | } | |
1018 | ||
1019 | if (p->br != br) { | |
1020 | NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); | |
1021 | return -EINVAL; | |
1022 | } | |
2aa4abed CA |
1023 | if (p->state == BR_STATE_DISABLED && entry->state != MDB_PERMANENT) { |
1024 | NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state and entry is not permanent"); | |
1bc844ee | 1025 | return -EINVAL; |
83f7398e | 1026 | } |
1bc844ee NA |
1027 | vg = nbp_vlan_group(p); |
1028 | } else { | |
1029 | vg = br_vlan_group(br); | |
1030 | } | |
e44deb2f | 1031 | |
f59783f5 NA |
1032 | /* If vlan filtering is enabled and VLAN is not specified |
1033 | * install mdb entry on all vlans configured on the port. | |
1034 | */ | |
1f51445a | 1035 | if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { |
2594e906 NA |
1036 | list_for_each_entry(v, &vg->vlan_list, vlist) { |
1037 | entry->vid = v->vid; | |
88d4bd18 | 1038 | err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack); |
e44deb2f SA |
1039 | if (err) |
1040 | break; | |
e44deb2f SA |
1041 | } |
1042 | } else { | |
88d4bd18 | 1043 | err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack); |
e44deb2f SA |
1044 | } |
1045 | ||
cfd56754 CW |
1046 | return err; |
1047 | } | |
1048 | ||
88d4bd18 NA |
1049 | static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry, |
1050 | struct nlattr **mdb_attrs) | |
cfd56754 | 1051 | { |
cfd56754 CW |
1052 | struct net_bridge_mdb_entry *mp; |
1053 | struct net_bridge_port_group *p; | |
1054 | struct net_bridge_port_group __rcu **pp; | |
1055 | struct br_ip ip; | |
1056 | int err = -EINVAL; | |
1057 | ||
13cefad2 | 1058 | if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED)) |
cfd56754 CW |
1059 | return -EINVAL; |
1060 | ||
88d4bd18 | 1061 | __mdb_entry_to_br_ip(entry, &ip, mdb_attrs); |
cfd56754 CW |
1062 | |
1063 | spin_lock_bh(&br->multicast_lock); | |
19e3a9c9 | 1064 | mp = br_mdb_ip_get(br, &ip); |
cfd56754 CW |
1065 | if (!mp) |
1066 | goto unlock; | |
1067 | ||
1bc844ee NA |
1068 | /* host leave */ |
1069 | if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) { | |
1070 | br_multicast_host_leave(mp, false); | |
1071 | err = 0; | |
81f19838 | 1072 | br_mdb_notify(br->dev, mp, NULL, RTM_DELMDB); |
1bc844ee NA |
1073 | if (!mp->ports && netif_running(br->dev)) |
1074 | mod_timer(&mp->timer, jiffies); | |
1075 | goto unlock; | |
1076 | } | |
1077 | ||
cfd56754 CW |
1078 | for (pp = &mp->ports; |
1079 | (p = mlock_dereference(*pp, br)) != NULL; | |
1080 | pp = &p->next) { | |
085b53c8 | 1081 | if (!p->key.port || p->key.port->dev->ifindex != entry->ifindex) |
cfd56754 CW |
1082 | continue; |
1083 | ||
681590bd | 1084 | br_multicast_del_pg(mp, p, pp); |
cfd56754 | 1085 | err = 0; |
cfd56754 CW |
1086 | break; |
1087 | } | |
1088 | ||
1089 | unlock: | |
1090 | spin_unlock_bh(&br->multicast_lock); | |
1091 | return err; | |
1092 | } | |
1093 | ||
c21ef3e3 DA |
1094 | static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, |
1095 | struct netlink_ext_ack *extack) | |
cfd56754 | 1096 | { |
9c4258c7 | 1097 | struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1]; |
e44deb2f | 1098 | struct net *net = sock_net(skb->sk); |
2594e906 | 1099 | struct net_bridge_vlan_group *vg; |
1bc844ee | 1100 | struct net_bridge_port *p = NULL; |
e44deb2f | 1101 | struct net_device *dev, *pdev; |
cfd56754 | 1102 | struct br_mdb_entry *entry; |
2594e906 | 1103 | struct net_bridge_vlan *v; |
cfd56754 CW |
1104 | struct net_bridge *br; |
1105 | int err; | |
1106 | ||
9c4258c7 | 1107 | err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack); |
cfd56754 CW |
1108 | if (err < 0) |
1109 | return err; | |
1110 | ||
1111 | br = netdev_priv(dev); | |
1112 | ||
1bc844ee NA |
1113 | if (entry->ifindex != br->dev->ifindex) { |
1114 | pdev = __dev_get_by_index(net, entry->ifindex); | |
1115 | if (!pdev) | |
1116 | return -ENODEV; | |
e44deb2f | 1117 | |
1bc844ee | 1118 | p = br_port_get_rtnl(pdev); |
2aa4abed CA |
1119 | if (!p) { |
1120 | NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port"); | |
1121 | return -EINVAL; | |
1122 | } | |
1123 | if (p->br != br) { | |
1124 | NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device"); | |
1bc844ee | 1125 | return -EINVAL; |
2aa4abed | 1126 | } |
1bc844ee NA |
1127 | vg = nbp_vlan_group(p); |
1128 | } else { | |
1129 | vg = br_vlan_group(br); | |
1130 | } | |
e44deb2f | 1131 | |
f59783f5 NA |
1132 | /* If vlan filtering is enabled and VLAN is not specified |
1133 | * delete mdb entry on all vlans configured on the port. | |
1134 | */ | |
1f51445a | 1135 | if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { |
2594e906 NA |
1136 | list_for_each_entry(v, &vg->vlan_list, vlist) { |
1137 | entry->vid = v->vid; | |
88d4bd18 | 1138 | err = __br_mdb_del(br, entry, mdb_attrs); |
e44deb2f SA |
1139 | } |
1140 | } else { | |
88d4bd18 | 1141 | err = __br_mdb_del(br, entry, mdb_attrs); |
e44deb2f SA |
1142 | } |
1143 | ||
cfd56754 CW |
1144 | return err; |
1145 | } | |
1146 | ||
ee07c6e7 CW |
1147 | void br_mdb_init(void) |
1148 | { | |
c1c502b5 FW |
1149 | rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0); |
1150 | rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, 0); | |
1151 | rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, 0); | |
ee07c6e7 | 1152 | } |
63233159 VY |
1153 | |
1154 | void br_mdb_uninit(void) | |
1155 | { | |
1156 | rtnl_unregister(PF_BRIDGE, RTM_GETMDB); | |
1157 | rtnl_unregister(PF_BRIDGE, RTM_NEWMDB); | |
1158 | rtnl_unregister(PF_BRIDGE, RTM_DELMDB); | |
1159 | } |