blob: 4556d913955bf1933d558c0598cce41c475f8ea6 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Cong Wangee07c6e2012-12-07 00:04:48 +00002#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>
Cong Wangcfd56752012-12-11 22:23:08 +00008#include <linux/if_ether.h>
Cong Wangee07c6e2012-12-07 00:04:48 +00009#include <net/ip.h>
10#include <net/netlink.h>
Elad Razf1fecb12016-01-10 21:06:23 +010011#include <net/switchdev.h>
Cong Wangee07c6e2012-12-07 00:04:48 +000012#if IS_ENABLED(CONFIG_IPV6)
13#include <net/ipv6.h>
Linus Lüssing3c3769e2013-09-04 02:13:39 +020014#include <net/addrconf.h>
Cong Wangee07c6e2012-12-07 00:04:48 +000015#endif
16
17#include "br_private.h"
18
Linus Lüssingff391c52021-05-13 15:20:45 +020019static bool
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030020br_ip4_rports_get_timer(struct net_bridge_mcast_port *pmctx,
21 unsigned long *timer)
Linus Lüssingff391c52021-05-13 15:20:45 +020022{
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030023 *timer = br_timer_value(&pmctx->ip4_mc_router_timer);
24 return !hlist_unhashed(&pmctx->ip4_rlist);
Linus Lüssingff391c52021-05-13 15:20:45 +020025}
26
27static bool
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030028br_ip6_rports_get_timer(struct net_bridge_mcast_port *pmctx,
29 unsigned long *timer)
Linus Lüssingff391c52021-05-13 15:20:45 +020030{
Linus Lüssinga3c02e72021-05-13 15:20:51 +020031#if IS_ENABLED(CONFIG_IPV6)
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030032 *timer = br_timer_value(&pmctx->ip6_mc_router_timer);
33 return !hlist_unhashed(&pmctx->ip6_rlist);
Linus Lüssinga3c02e72021-05-13 15:20:51 +020034#else
Linus Lüssingff391c52021-05-13 15:20:45 +020035 *timer = 0;
36 return false;
Linus Lüssinga3c02e72021-05-13 15:20:51 +020037#endif
Linus Lüssingff391c52021-05-13 15:20:45 +020038}
39
Nikolay Aleksandrov05d6f382021-08-16 17:57:05 +030040static 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
50size_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
Nikolay Aleksandrovdc002872021-08-10 18:29:33 +030070int br_rports_fill_info(struct sk_buff *skb,
71 const struct net_bridge_mcast *brmctx)
Cong Wangee07c6e2012-12-07 00:04:48 +000072{
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030073 u16 vid = brmctx->vlan ? brmctx->vlan->vid : 0;
Linus Lüssingff391c52021-05-13 15:20:45 +020074 bool have_ip4_mc_rtr, have_ip6_mc_rtr;
75 unsigned long ip4_timer, ip6_timer;
Nikolay Aleksandrov59f78f92016-02-26 21:20:04 +010076 struct nlattr *nest, *port_nest;
Linus Lüssingff391c52021-05-13 15:20:45 +020077 struct net_bridge_port *p;
Cong Wangee07c6e2012-12-07 00:04:48 +000078
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030079 if (!brmctx->multicast_router || !br_rports_have_mc_router(brmctx))
Cong Wangee07c6e2012-12-07 00:04:48 +000080 return 0;
81
Michal Kubecekae0be8d2019-04-26 11:13:06 +020082 nest = nla_nest_start_noflag(skb, MDBA_ROUTER);
Cong Wangee07c6e2012-12-07 00:04:48 +000083 if (nest == NULL)
84 return -EMSGSIZE;
85
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +030086 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);
Linus Lüssingff391c52021-05-13 15:20:45 +0200102
103 if (!have_ip4_mc_rtr && !have_ip6_mc_rtr)
Nikolay Aleksandrov59f78f92016-02-26 21:20:04 +0100104 continue;
Linus Lüssingff391c52021-05-13 15:20:45 +0200105
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200106 port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT);
Nikolay Aleksandrov59f78f92016-02-26 21:20:04 +0100107 if (!port_nest)
Cong Wangee07c6e2012-12-07 00:04:48 +0000108 goto fail;
Linus Lüssingff391c52021-05-13 15:20:45 +0200109
Nikolay Aleksandrov59f78f92016-02-26 21:20:04 +0100110 if (nla_put_nohdr(skb, sizeof(u32), &p->dev->ifindex) ||
111 nla_put_u32(skb, MDBA_ROUTER_PATTR_TIMER,
Linus Lüssingff391c52021-05-13 15:20:45 +0200112 max(ip4_timer, ip6_timer)) ||
Nikolay Aleksandrov59f78f92016-02-26 21:20:04 +0100113 nla_put_u8(skb, MDBA_ROUTER_PATTR_TYPE,
Nikolay Aleksandrov96322332021-07-19 20:06:23 +0300114 p->multicast_ctx.multicast_router) ||
Linus Lüssingb7fb0912021-05-13 15:20:52 +0200115 (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,
Nikolay Aleksandrovdc002872021-08-10 18:29:33 +0300120 ip6_timer)) ||
121 (vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid))) {
Nikolay Aleksandrov59f78f92016-02-26 21:20:04 +0100122 nla_nest_cancel(skb, port_nest);
123 goto fail;
124 }
125 nla_nest_end(skb, port_nest);
Cong Wangee07c6e2012-12-07 00:04:48 +0000126 }
127
128 nla_nest_end(skb, nest);
129 return 0;
130fail:
131 nla_nest_cancel(skb, nest);
132 return -EMSGSIZE;
133}
134
Elad Raz9d06b6d2016-02-03 09:57:05 +0100135static 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;
Nikolay Aleksandrov3247b272019-07-30 15:20:41 +0300141 if (flags & MDB_PG_FLAGS_FAST_LEAVE)
142 e->flags |= MDB_FLAGS_FAST_LEAVE;
Nikolay Aleksandrov8266a042020-09-22 10:30:24 +0300143 if (flags & MDB_PG_FLAGS_STAR_EXCL)
144 e->flags |= MDB_FLAGS_STAR_EXCL;
Nikolay Aleksandrov9116ffb2020-09-22 10:30:25 +0300145 if (flags & MDB_PG_FLAGS_BLOCKED)
146 e->flags |= MDB_FLAGS_BLOCKED;
Elad Raz9d06b6d2016-02-03 09:57:05 +0100147}
148
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300149static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip,
150 struct nlattr **mdb_attrs)
Elad Raz6dd684c2016-04-21 12:52:44 +0200151{
152 memset(ip, 0, sizeof(struct br_ip));
153 ip->vid = entry->vid;
154 ip->proto = entry->addr.proto;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300155 switch (ip->proto) {
156 case htons(ETH_P_IP):
Nikolay Aleksandroveab32272020-09-22 10:30:17 +0300157 ip->dst.ip4 = entry->addr.u.ip4;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300158 if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE])
159 ip->src.ip4 = nla_get_in_addr(mdb_attrs[MDBE_ATTR_SOURCE]);
160 break;
Elad Raz6dd684c2016-04-21 12:52:44 +0200161#if IS_ENABLED(CONFIG_IPV6)
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300162 case htons(ETH_P_IPV6):
Nikolay Aleksandroveab32272020-09-22 10:30:17 +0300163 ip->dst.ip6 = entry->addr.u.ip6;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300164 if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE])
165 ip->src.ip6 = nla_get_in6_addr(mdb_attrs[MDBE_ATTR_SOURCE]);
166 break;
Elad Raz6dd684c2016-04-21 12:52:44 +0200167#endif
Nikolay Aleksandrov955062b2020-10-29 01:38:31 +0200168 default:
169 ether_addr_copy(ip->dst.mac_addr, entry->addr.u.mac_addr);
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300170 }
171
Elad Raz6dd684c2016-04-21 12:52:44 +0200172}
173
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300174static 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,
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300188 lockdep_is_held(&p->key.port->br->multicast_lock)) {
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300189 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,
Nikolay Aleksandrovdeb96562020-09-22 10:30:16 +0300195 ent->addr.src.ip4)) {
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300196 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,
Nikolay Aleksandrovdeb96562020-09-22 10:30:16 +0300203 &ent->addr.src.ip6)) {
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300204 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
225out_cancel_err:
226 nla_nest_cancel(skb, nest);
227 return -EMSGSIZE;
228}
229
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300230static int __mdb_fill_info(struct sk_buff *skb,
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300231 struct net_bridge_mdb_entry *mp,
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300232 struct net_bridge_port_group *p)
233{
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300234 bool dump_srcs_mode = false;
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300235 struct timer_list *mtimer;
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300236 struct nlattr *nest_ent;
237 struct br_mdb_entry e;
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300238 u8 flags = 0;
239 int ifindex;
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300240
241 memset(&e, 0, sizeof(e));
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300242 if (p) {
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300243 ifindex = p->key.port->dev->ifindex;
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300244 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;
254 if (mp->addr.proto == htons(ETH_P_IP))
Nikolay Aleksandroveab32272020-09-22 10:30:17 +0300255 e.addr.u.ip4 = mp->addr.dst.ip4;
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300256#if IS_ENABLED(CONFIG_IPV6)
Nikolay Aleksandrov955062b2020-10-29 01:38:31 +0200257 else if (mp->addr.proto == htons(ETH_P_IPV6))
Nikolay Aleksandroveab32272020-09-22 10:30:17 +0300258 e.addr.u.ip6 = mp->addr.dst.ip6;
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300259#endif
Nikolay Aleksandrov955062b2020-10-29 01:38:31 +0200260 else
261 ether_addr_copy(e.addr.u.mac_addr, mp->addr.dst.mac_addr);
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300262 e.addr.proto = mp->addr.proto;
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300263 nest_ent = nla_nest_start_noflag(skb,
264 MDBA_MDB_ENTRY_INFO);
265 if (!nest_ent)
266 return -EMSGSIZE;
267
268 if (nla_put_nohdr(skb, sizeof(e), &e) ||
269 nla_put_u32(skb,
270 MDBA_MDB_EATTR_TIMER,
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300271 br_timer_value(mtimer)))
272 goto nest_err;
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300273
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300274 switch (mp->addr.proto) {
275 case htons(ETH_P_IP):
Nikolay Aleksandrovd3d065c2021-07-19 20:06:24 +0300276 dump_srcs_mode = !!(mp->br->multicast_ctx.multicast_igmp_version == 3);
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300277 if (mp->addr.src.ip4) {
278 if (nla_put_in_addr(skb, MDBA_MDB_EATTR_SOURCE,
279 mp->addr.src.ip4))
280 goto nest_err;
281 break;
282 }
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300283 break;
284#if IS_ENABLED(CONFIG_IPV6)
285 case htons(ETH_P_IPV6):
Nikolay Aleksandrovd3d065c2021-07-19 20:06:24 +0300286 dump_srcs_mode = !!(mp->br->multicast_ctx.multicast_mld_version == 2);
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300287 if (!ipv6_addr_any(&mp->addr.src.ip6)) {
288 if (nla_put_in6_addr(skb, MDBA_MDB_EATTR_SOURCE,
289 &mp->addr.src.ip6))
290 goto nest_err;
291 break;
292 }
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300293 break;
294#endif
Nikolay Aleksandrov955062b2020-10-29 01:38:31 +0200295 default:
296 ether_addr_copy(e.addr.u.mac_addr, mp->addr.dst.mac_addr);
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300297 }
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300298 if (p) {
299 if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, p->rt_protocol))
300 goto nest_err;
301 if (dump_srcs_mode &&
302 (__mdb_fill_srcs(skb, p) ||
303 nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE,
304 p->filter_mode)))
305 goto nest_err;
306 }
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300307 nla_nest_end(skb, nest_ent);
308
309 return 0;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300310
311nest_err:
312 nla_nest_cancel(skb, nest_ent);
313 return -EMSGSIZE;
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300314}
315
Cong Wangee07c6e2012-12-07 00:04:48 +0000316static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
317 struct net_device *dev)
318{
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300319 int idx = 0, s_idx = cb->args[1], err = 0, pidx = 0, s_pidx = cb->args[2];
Cong Wangee07c6e2012-12-07 00:04:48 +0000320 struct net_bridge *br = netdev_priv(dev);
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200321 struct net_bridge_mdb_entry *mp;
Cong Wangee07c6e2012-12-07 00:04:48 +0000322 struct nlattr *nest, *nest2;
Cong Wangee07c6e2012-12-07 00:04:48 +0000323
Nikolay Aleksandrov13cefad2018-09-26 17:01:03 +0300324 if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
Cong Wangee07c6e2012-12-07 00:04:48 +0000325 return 0;
326
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200327 nest = nla_nest_start_noflag(skb, MDBA_MDB);
Cong Wangee07c6e2012-12-07 00:04:48 +0000328 if (nest == NULL)
329 return -EMSGSIZE;
330
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200331 hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
stephen hemminger762a3d82013-08-04 17:19:38 -0700332 struct net_bridge_port_group *p;
333 struct net_bridge_port_group __rcu **pp;
Cong Wangee07c6e2012-12-07 00:04:48 +0000334
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200335 if (idx < s_idx)
336 goto skip;
Cong Wangee07c6e2012-12-07 00:04:48 +0000337
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200338 nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200339 if (!nest2) {
340 err = -EMSGSIZE;
341 break;
342 }
343
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300344 if (!s_pidx && mp->host_joined) {
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300345 err = __mdb_fill_info(skb, mp, NULL);
346 if (err) {
347 nla_nest_cancel(skb, nest2);
348 break;
349 }
350 }
351
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200352 for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
353 pp = &p->next) {
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300354 if (!p->key.port)
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200355 continue;
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300356 if (pidx < s_pidx)
357 goto skip_pg;
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200358
Nikolay Aleksandrove77b0c82019-08-17 14:22:12 +0300359 err = __mdb_fill_info(skb, mp, p);
Nikolay Aleksandrov65459162019-08-17 14:22:11 +0300360 if (err) {
Ido Schimmel12913f72020-09-11 16:24:47 +0300361 nla_nest_end(skb, nest2);
Cong Wangee07c6e2012-12-07 00:04:48 +0000362 goto out;
363 }
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300364skip_pg:
365 pidx++;
Cong Wangee07c6e2012-12-07 00:04:48 +0000366 }
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300367 pidx = 0;
368 s_pidx = 0;
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200369 nla_nest_end(skb, nest2);
370skip:
371 idx++;
Cong Wangee07c6e2012-12-07 00:04:48 +0000372 }
373
374out:
375 cb->args[1] = idx;
Nikolay Aleksandrov5205e912020-09-07 12:56:08 +0300376 cb->args[2] = pidx;
Cong Wangee07c6e2012-12-07 00:04:48 +0000377 nla_nest_end(skb, nest);
378 return err;
379}
380
David Ahernc77b9362018-10-07 20:16:42 -0700381static int br_mdb_valid_dump_req(const struct nlmsghdr *nlh,
382 struct netlink_ext_ack *extack)
383{
384 struct br_port_msg *bpm;
385
386 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*bpm))) {
387 NL_SET_ERR_MSG_MOD(extack, "Invalid header for mdb dump request");
388 return -EINVAL;
389 }
390
391 bpm = nlmsg_data(nlh);
392 if (bpm->ifindex) {
393 NL_SET_ERR_MSG_MOD(extack, "Filtering by device index is not supported for mdb dump request");
394 return -EINVAL;
395 }
396 if (nlmsg_attrlen(nlh, sizeof(*bpm))) {
397 NL_SET_ERR_MSG(extack, "Invalid data after header in mdb dump request");
398 return -EINVAL;
399 }
400
401 return 0;
402}
403
Cong Wangee07c6e2012-12-07 00:04:48 +0000404static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
405{
406 struct net_device *dev;
407 struct net *net = sock_net(skb->sk);
408 struct nlmsghdr *nlh = NULL;
409 int idx = 0, s_idx;
410
David Ahernc77b9362018-10-07 20:16:42 -0700411 if (cb->strict_check) {
412 int err = br_mdb_valid_dump_req(cb->nlh, cb->extack);
413
414 if (err < 0)
415 return err;
416 }
417
Cong Wangee07c6e2012-12-07 00:04:48 +0000418 s_idx = cb->args[0];
419
420 rcu_read_lock();
421
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +0200422 cb->seq = net->dev_base_seq;
Cong Wangee07c6e2012-12-07 00:04:48 +0000423
424 for_each_netdev_rcu(net, dev) {
Kyungrok Chung254ec032021-10-16 20:21:36 +0900425 if (netif_is_bridge_master(dev)) {
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +0300426 struct net_bridge *br = netdev_priv(dev);
Cong Wangee07c6e2012-12-07 00:04:48 +0000427 struct br_port_msg *bpm;
428
429 if (idx < s_idx)
430 goto skip;
431
432 nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
433 cb->nlh->nlmsg_seq, RTM_GETMDB,
434 sizeof(*bpm), NLM_F_MULTI);
435 if (nlh == NULL)
436 break;
437
438 bpm = nlmsg_data(nlh);
Mathias Krausec085c492013-03-09 05:52:19 +0000439 memset(bpm, 0, sizeof(*bpm));
Cong Wangee07c6e2012-12-07 00:04:48 +0000440 bpm->ifindex = dev->ifindex;
441 if (br_mdb_fill_info(skb, cb, dev) < 0)
442 goto out;
Nikolay Aleksandrove04d3772021-08-10 18:29:32 +0300443 if (br_rports_fill_info(skb, &br->multicast_ctx) < 0)
Cong Wangee07c6e2012-12-07 00:04:48 +0000444 goto out;
445
446 cb->args[1] = 0;
447 nlmsg_end(skb, nlh);
448 skip:
449 idx++;
450 }
451 }
452
453out:
454 if (nlh)
455 nlmsg_end(skb, nlh);
456 rcu_read_unlock();
457 cb->args[0] = idx;
458 return skb->len;
459}
460
Cong Wang37a393b2012-12-11 22:23:07 +0000461static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
462 struct net_device *dev,
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300463 struct net_bridge_mdb_entry *mp,
464 struct net_bridge_port_group *pg,
465 int type)
Cong Wang37a393b2012-12-11 22:23:07 +0000466{
467 struct nlmsghdr *nlh;
468 struct br_port_msg *bpm;
469 struct nlattr *nest, *nest2;
470
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300471 nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0);
Cong Wang37a393b2012-12-11 22:23:07 +0000472 if (!nlh)
473 return -EMSGSIZE;
474
475 bpm = nlmsg_data(nlh);
Mathias Krausec085c492013-03-09 05:52:19 +0000476 memset(bpm, 0, sizeof(*bpm));
Cong Wang37a393b2012-12-11 22:23:07 +0000477 bpm->family = AF_BRIDGE;
478 bpm->ifindex = dev->ifindex;
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200479 nest = nla_nest_start_noflag(skb, MDBA_MDB);
Cong Wang37a393b2012-12-11 22:23:07 +0000480 if (nest == NULL)
481 goto cancel;
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200482 nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
Cong Wang37a393b2012-12-11 22:23:07 +0000483 if (nest2 == NULL)
484 goto end;
485
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300486 if (__mdb_fill_info(skb, mp, pg))
Cong Wang37a393b2012-12-11 22:23:07 +0000487 goto end;
488
489 nla_nest_end(skb, nest2);
490 nla_nest_end(skb, nest);
Johannes Berg053c0952015-01-16 22:09:00 +0100491 nlmsg_end(skb, nlh);
492 return 0;
Cong Wang37a393b2012-12-11 22:23:07 +0000493
494end:
495 nla_nest_end(skb, nest);
496cancel:
497 nlmsg_cancel(skb, nlh);
498 return -EMSGSIZE;
499}
500
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300501static size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg)
Cong Wang37a393b2012-12-11 22:23:07 +0000502{
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300503 size_t nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
504 nla_total_size(sizeof(struct br_mdb_entry)) +
505 nla_total_size(sizeof(u32));
506 struct net_bridge_group_src *ent;
507 size_t addr_size = 0;
508
509 if (!pg)
510 goto out;
511
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300512 /* MDBA_MDB_EATTR_RTPROT */
513 nlmsg_size += nla_total_size(sizeof(u8));
514
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300515 switch (pg->key.addr.proto) {
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300516 case htons(ETH_P_IP):
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300517 /* MDBA_MDB_EATTR_SOURCE */
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300518 if (pg->key.addr.src.ip4)
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300519 nlmsg_size += nla_total_size(sizeof(__be32));
Nikolay Aleksandrovd3d065c2021-07-19 20:06:24 +0300520 if (pg->key.port->br->multicast_ctx.multicast_igmp_version == 2)
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300521 goto out;
522 addr_size = sizeof(__be32);
523 break;
524#if IS_ENABLED(CONFIG_IPV6)
525 case htons(ETH_P_IPV6):
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300526 /* MDBA_MDB_EATTR_SOURCE */
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300527 if (!ipv6_addr_any(&pg->key.addr.src.ip6))
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300528 nlmsg_size += nla_total_size(sizeof(struct in6_addr));
Nikolay Aleksandrovd3d065c2021-07-19 20:06:24 +0300529 if (pg->key.port->br->multicast_ctx.multicast_mld_version == 1)
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300530 goto out;
531 addr_size = sizeof(struct in6_addr);
532 break;
533#endif
534 }
535
536 /* MDBA_MDB_EATTR_GROUP_MODE */
537 nlmsg_size += nla_total_size(sizeof(u8));
538
539 /* MDBA_MDB_EATTR_SRC_LIST nested attr */
540 if (!hlist_empty(&pg->src_list))
541 nlmsg_size += nla_total_size(0);
542
543 hlist_for_each_entry(ent, &pg->src_list, node) {
544 /* MDBA_MDB_SRCLIST_ENTRY nested attr +
545 * MDBA_MDB_SRCATTR_ADDRESS + MDBA_MDB_SRCATTR_TIMER
546 */
547 nlmsg_size += nla_total_size(0) +
548 nla_total_size(addr_size) +
549 nla_total_size(sizeof(u32));
550 }
551out:
552 return nlmsg_size;
Cong Wang37a393b2012-12-11 22:23:07 +0000553}
554
Vladimir Oltean9ae9ff92021-10-27 19:21:17 +0300555void br_mdb_notify(struct net_device *dev,
556 struct net_bridge_mdb_entry *mp,
557 struct net_bridge_port_group *pg,
558 int type)
559{
Cong Wang37a393b2012-12-11 22:23:07 +0000560 struct net *net = dev_net(dev);
561 struct sk_buff *skb;
562 int err = -ENOBUFS;
563
Vladimir Oltean9ae9ff92021-10-27 19:21:17 +0300564 br_switchdev_mdb_notify(dev, mp, pg, type);
Elad Razf1fecb12016-01-10 21:06:23 +0100565
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300566 skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
Cong Wang37a393b2012-12-11 22:23:07 +0000567 if (!skb)
568 goto errout;
569
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300570 err = nlmsg_populate_mdb_fill(skb, dev, mp, pg, type);
Cong Wang37a393b2012-12-11 22:23:07 +0000571 if (err < 0) {
572 kfree_skb(skb);
573 goto errout;
574 }
575
576 rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC);
577 return;
578errout:
579 rtnl_set_sk_err(net, RTNLGRP_MDB, err);
580}
581
Satish Ashok949f1e32015-07-23 05:00:53 -0700582static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
583 struct net_device *dev,
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300584 int ifindex, u16 vid, u32 pid,
Satish Ashok949f1e32015-07-23 05:00:53 -0700585 u32 seq, int type, unsigned int flags)
586{
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300587 struct nlattr *nest, *port_nest;
Satish Ashok949f1e32015-07-23 05:00:53 -0700588 struct br_port_msg *bpm;
589 struct nlmsghdr *nlh;
Satish Ashok949f1e32015-07-23 05:00:53 -0700590
Nicolas Dichtel94a72b32019-09-06 11:47:02 +0200591 nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), 0);
Satish Ashok949f1e32015-07-23 05:00:53 -0700592 if (!nlh)
593 return -EMSGSIZE;
594
595 bpm = nlmsg_data(nlh);
596 memset(bpm, 0, sizeof(*bpm));
597 bpm->family = AF_BRIDGE;
598 bpm->ifindex = dev->ifindex;
Michal Kubecekae0be8d2019-04-26 11:13:06 +0200599 nest = nla_nest_start_noflag(skb, MDBA_ROUTER);
Satish Ashok949f1e32015-07-23 05:00:53 -0700600 if (!nest)
601 goto cancel;
602
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300603 port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT);
604 if (!port_nest)
Satish Ashok949f1e32015-07-23 05:00:53 -0700605 goto end;
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300606 if (nla_put_nohdr(skb, sizeof(u32), &ifindex)) {
607 nla_nest_cancel(skb, port_nest);
608 goto end;
609 }
610 if (vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid)) {
611 nla_nest_cancel(skb, port_nest);
612 goto end;
613 }
614 nla_nest_end(skb, port_nest);
Satish Ashok949f1e32015-07-23 05:00:53 -0700615
616 nla_nest_end(skb, nest);
617 nlmsg_end(skb, nlh);
618 return 0;
619
620end:
621 nla_nest_end(skb, nest);
622cancel:
623 nlmsg_cancel(skb, nlh);
624 return -EMSGSIZE;
625}
626
627static inline size_t rtnl_rtr_nlmsg_size(void)
628{
629 return NLMSG_ALIGN(sizeof(struct br_port_msg))
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300630 + nla_total_size(sizeof(__u32))
631 + nla_total_size(sizeof(u16));
Satish Ashok949f1e32015-07-23 05:00:53 -0700632}
633
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300634void br_rtr_notify(struct net_device *dev, struct net_bridge_mcast_port *pmctx,
Satish Ashok949f1e32015-07-23 05:00:53 -0700635 int type)
636{
637 struct net *net = dev_net(dev);
638 struct sk_buff *skb;
639 int err = -ENOBUFS;
640 int ifindex;
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300641 u16 vid;
Satish Ashok949f1e32015-07-23 05:00:53 -0700642
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300643 ifindex = pmctx ? pmctx->port->dev->ifindex : 0;
644 vid = pmctx && br_multicast_port_ctx_is_vlan(pmctx) ? pmctx->vlan->vid :
645 0;
Satish Ashok949f1e32015-07-23 05:00:53 -0700646 skb = nlmsg_new(rtnl_rtr_nlmsg_size(), GFP_ATOMIC);
647 if (!skb)
648 goto errout;
649
Nikolay Aleksandrov1e9ca452021-07-19 20:06:33 +0300650 err = nlmsg_populate_rtr_fill(skb, dev, ifindex, vid, 0, 0, type,
651 NTF_SELF);
Satish Ashok949f1e32015-07-23 05:00:53 -0700652 if (err < 0) {
653 kfree_skb(skb);
654 goto errout;
655 }
656
657 rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC);
658 return;
659
660errout:
661 rtnl_set_sk_err(net, RTNLGRP_MDB, err);
662}
663
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300664static bool is_valid_mdb_entry(struct br_mdb_entry *entry,
665 struct netlink_ext_ack *extack)
Cong Wangcfd56752012-12-11 22:23:08 +0000666{
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300667 if (entry->ifindex == 0) {
668 NL_SET_ERR_MSG_MOD(extack, "Zero entry ifindex is not allowed");
Cong Wangcfd56752012-12-11 22:23:08 +0000669 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300670 }
Cong Wangcfd56752012-12-11 22:23:08 +0000671
672 if (entry->addr.proto == htons(ETH_P_IP)) {
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300673 if (!ipv4_is_multicast(entry->addr.u.ip4)) {
674 NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is not multicast");
Cong Wangcfd56752012-12-11 22:23:08 +0000675 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300676 }
677 if (ipv4_is_local_multicast(entry->addr.u.ip4)) {
678 NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is local multicast");
Cong Wangcfd56752012-12-11 22:23:08 +0000679 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300680 }
Cong Wangcfd56752012-12-11 22:23:08 +0000681#if IS_ENABLED(CONFIG_IPV6)
682 } else if (entry->addr.proto == htons(ETH_P_IPV6)) {
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300683 if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) {
684 NL_SET_ERR_MSG_MOD(extack, "IPv6 entry group address is link-local all nodes");
Cong Wangcfd56752012-12-11 22:23:08 +0000685 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300686 }
Cong Wangcfd56752012-12-11 22:23:08 +0000687#endif
Nikolay Aleksandrov955062b2020-10-29 01:38:31 +0200688 } else if (entry->addr.proto == 0) {
689 /* L2 mdb */
690 if (!is_multicast_ether_addr(entry->addr.u.mac_addr)) {
691 NL_SET_ERR_MSG_MOD(extack, "L2 entry group is not multicast");
692 return false;
693 }
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300694 } else {
695 NL_SET_ERR_MSG_MOD(extack, "Unknown entry protocol");
Cong Wangcfd56752012-12-11 22:23:08 +0000696 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300697 }
698
699 if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY) {
700 NL_SET_ERR_MSG_MOD(extack, "Unknown entry state");
Amerigo Wangccb1c312012-12-14 22:09:51 +0000701 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300702 }
703 if (entry->vid >= VLAN_VID_MASK) {
704 NL_SET_ERR_MSG_MOD(extack, "Invalid entry VLAN id");
Nikolay Aleksandrov74fe61f2015-07-10 08:02:08 -0700705 return false;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300706 }
Cong Wangcfd56752012-12-11 22:23:08 +0000707
708 return true;
709}
710
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300711static bool is_valid_mdb_source(struct nlattr *attr, __be16 proto,
712 struct netlink_ext_ack *extack)
713{
714 switch (proto) {
715 case htons(ETH_P_IP):
716 if (nla_len(attr) != sizeof(struct in_addr)) {
717 NL_SET_ERR_MSG_MOD(extack, "IPv4 invalid source address length");
718 return false;
719 }
720 if (ipv4_is_multicast(nla_get_in_addr(attr))) {
721 NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed");
722 return false;
723 }
724 break;
725#if IS_ENABLED(CONFIG_IPV6)
726 case htons(ETH_P_IPV6): {
727 struct in6_addr src;
728
729 if (nla_len(attr) != sizeof(struct in6_addr)) {
730 NL_SET_ERR_MSG_MOD(extack, "IPv6 invalid source address length");
731 return false;
732 }
733 src = nla_get_in6_addr(attr);
734 if (ipv6_addr_is_multicast(&src)) {
735 NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed");
736 return false;
737 }
738 break;
739 }
740#endif
741 default:
742 NL_SET_ERR_MSG_MOD(extack, "Invalid protocol used with source address");
743 return false;
744 }
745
746 return true;
747}
748
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300749static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = {
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300750 [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY,
751 sizeof(struct in_addr),
752 sizeof(struct in6_addr)),
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300753};
754
Cong Wangcfd56752012-12-11 22:23:08 +0000755static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300756 struct net_device **pdev, struct br_mdb_entry **pentry,
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300757 struct nlattr **mdb_attrs, struct netlink_ext_ack *extack)
Cong Wangcfd56752012-12-11 22:23:08 +0000758{
759 struct net *net = sock_net(skb->sk);
760 struct br_mdb_entry *entry;
761 struct br_port_msg *bpm;
762 struct nlattr *tb[MDBA_SET_ENTRY_MAX+1];
763 struct net_device *dev;
764 int err;
765
Johannes Berg8cb08172019-04-26 14:07:28 +0200766 err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
767 MDBA_SET_ENTRY_MAX, NULL, NULL);
Cong Wangcfd56752012-12-11 22:23:08 +0000768 if (err < 0)
769 return err;
770
771 bpm = nlmsg_data(nlh);
772 if (bpm->ifindex == 0) {
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300773 NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex");
Cong Wangcfd56752012-12-11 22:23:08 +0000774 return -EINVAL;
775 }
776
777 dev = __dev_get_by_index(net, bpm->ifindex);
778 if (dev == NULL) {
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300779 NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist");
Cong Wangcfd56752012-12-11 22:23:08 +0000780 return -ENODEV;
781 }
782
Kyungrok Chung254ec032021-10-16 20:21:36 +0900783 if (!netif_is_bridge_master(dev)) {
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300784 NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge");
Cong Wangcfd56752012-12-11 22:23:08 +0000785 return -EOPNOTSUPP;
786 }
787
788 *pdev = dev;
789
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300790 if (!tb[MDBA_SET_ENTRY]) {
791 NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute");
792 return -EINVAL;
793 }
794 if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) {
795 NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length");
Cong Wangcfd56752012-12-11 22:23:08 +0000796 return -EINVAL;
797 }
798
799 entry = nla_data(tb[MDBA_SET_ENTRY]);
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300800 if (!is_valid_mdb_entry(entry, extack))
Cong Wangcfd56752012-12-11 22:23:08 +0000801 return -EINVAL;
Cong Wangcfd56752012-12-11 22:23:08 +0000802 *pentry = entry;
Nikolay Aleksandrov2ac95df2020-09-22 10:30:12 +0300803
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300804 if (tb[MDBA_SET_ENTRY_ATTRS]) {
805 err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX,
806 tb[MDBA_SET_ENTRY_ATTRS],
807 br_mdbe_attrs_pol, extack);
808 if (err)
809 return err;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300810 if (mdb_attrs[MDBE_ATTR_SOURCE] &&
811 !is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE],
812 entry->addr.proto, extack))
813 return -EINVAL;
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300814 } else {
815 memset(mdb_attrs, 0,
816 sizeof(struct nlattr *) * (MDBE_ATTR_MAX + 1));
817 }
818
Cong Wangcfd56752012-12-11 22:23:08 +0000819 return 0;
820}
821
Nikolay Aleksandrov6567cb42021-07-21 17:01:26 +0300822static struct net_bridge_mcast *
823__br_mdb_choose_context(struct net_bridge *br,
824 const struct br_mdb_entry *entry,
825 struct netlink_ext_ack *extack)
826{
827 struct net_bridge_mcast *brmctx = NULL;
828 struct net_bridge_vlan *v;
829
830 if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
831 brmctx = &br->multicast_ctx;
832 goto out;
833 }
834
835 if (!entry->vid) {
836 NL_SET_ERR_MSG_MOD(extack, "Cannot add an entry without a vlan when vlan snooping is enabled");
837 goto out;
838 }
839
840 v = br_vlan_find(br_vlan_group(br), entry->vid);
841 if (!v) {
842 NL_SET_ERR_MSG_MOD(extack, "Vlan is not configured");
843 goto out;
844 }
845 if (br_multicast_ctx_vlan_global_disabled(&v->br_mcast_ctx)) {
846 NL_SET_ERR_MSG_MOD(extack, "Vlan's multicast processing is disabled");
847 goto out;
848 }
849 brmctx = &v->br_mcast_ctx;
850out:
851 return brmctx;
852}
853
Cong Wangcfd56752012-12-11 22:23:08 +0000854static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300855 struct br_mdb_entry *entry,
856 struct nlattr **mdb_attrs,
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300857 struct netlink_ext_ack *extack)
Cong Wangcfd56752012-12-11 22:23:08 +0000858{
Nikolay Aleksandrov8266a042020-09-22 10:30:24 +0300859 struct net_bridge_mdb_entry *mp, *star_mp;
Cong Wangcfd56752012-12-11 22:23:08 +0000860 struct net_bridge_port_group __rcu **pp;
Nikolay Aleksandrov6567cb42021-07-21 17:01:26 +0300861 struct net_bridge_port_group *p;
862 struct net_bridge_mcast *brmctx;
Nikolay Aleksandrov8266a042020-09-22 10:30:24 +0300863 struct br_ip group, star_group;
Satish Ashokf7e29652015-07-06 05:53:35 -0700864 unsigned long now = jiffies;
Vladimir Oltean0e761ac2020-10-29 01:48:15 +0200865 unsigned char flags = 0;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300866 u8 filter_mode;
Cong Wangcfd56752012-12-11 22:23:08 +0000867 int err;
868
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300869 __mdb_entry_to_br_ip(entry, &group, mdb_attrs);
870
Nikolay Aleksandrov6567cb42021-07-21 17:01:26 +0300871 brmctx = __br_mdb_choose_context(br, entry, extack);
872 if (!brmctx)
873 return -EINVAL;
874
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300875 /* host join errors which can happen before creating the group */
876 if (!port) {
877 /* don't allow any flags for host-joined groups */
878 if (entry->state) {
879 NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups");
880 return -EINVAL;
881 }
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300882 if (!br_multicast_is_star_g(&group)) {
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300883 NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined");
884 return -EINVAL;
885 }
886 }
887
Nikolay Aleksandrov955062b2020-10-29 01:38:31 +0200888 if (br_group_is_l2(&group) && entry->state != MDB_PERMANENT) {
889 NL_SET_ERR_MSG_MOD(extack, "Only permanent L2 entries allowed");
890 return -EINVAL;
891 }
892
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300893 mp = br_mdb_ip_get(br, &group);
Cong Wangcfd56752012-12-11 22:23:08 +0000894 if (!mp) {
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300895 mp = br_multicast_new_group(br, &group);
Arnd Bergmann56bb7fd2016-02-10 16:09:02 +0100896 err = PTR_ERR_OR_ZERO(mp);
897 if (err)
Cong Wangcfd56752012-12-11 22:23:08 +0000898 return err;
899 }
900
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +0300901 /* host join */
902 if (!port) {
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300903 if (mp->host_joined) {
904 NL_SET_ERR_MSG_MOD(extack, "Group is already joined by host");
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +0300905 return -EEXIST;
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300906 }
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +0300907
Nikolay Aleksandrov58d913a2021-07-21 17:01:27 +0300908 br_multicast_host_join(brmctx, mp, false);
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300909 br_mdb_notify(br->dev, mp, NULL, RTM_NEWMDB);
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +0300910
911 return 0;
912 }
913
Cong Wangcfd56752012-12-11 22:23:08 +0000914 for (pp = &mp->ports;
915 (p = mlock_dereference(*pp, br)) != NULL;
916 pp = &p->next) {
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300917 if (p->key.port == port) {
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300918 NL_SET_ERR_MSG_MOD(extack, "Group is already joined by port");
Cong Wangcfd56752012-12-11 22:23:08 +0000919 return -EEXIST;
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300920 }
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +0300921 if ((unsigned long)p->key.port < (unsigned long)port)
Cong Wangcfd56752012-12-11 22:23:08 +0000922 break;
923 }
924
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300925 filter_mode = br_multicast_is_star_g(&group) ? MCAST_EXCLUDE :
926 MCAST_INCLUDE;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300927
Vladimir Oltean0e761ac2020-10-29 01:48:15 +0200928 if (entry->state == MDB_PERMANENT)
929 flags |= MDB_PG_FLAGS_PERMANENT;
930
931 p = br_multicast_new_port_group(port, &group, *pp, flags, NULL,
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300932 filter_mode, RTPROT_STATIC);
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300933 if (unlikely(!p)) {
934 NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new port group");
Cong Wangcfd56752012-12-11 22:23:08 +0000935 return -ENOMEM;
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300936 }
Cong Wangcfd56752012-12-11 22:23:08 +0000937 rcu_assign_pointer(*pp, p);
Nikolay Aleksandrov79abc872020-09-07 12:56:11 +0300938 if (entry->state == MDB_TEMPORARY)
Nikolay Aleksandrovd3d065c2021-07-19 20:06:24 +0300939 mod_timer(&p->timer,
Nikolay Aleksandrov6567cb42021-07-21 17:01:26 +0300940 now + brmctx->multicast_membership_interval);
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +0300941 br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);
Nikolay Aleksandrov8266a042020-09-22 10:30:24 +0300942 /* if we are adding a new EXCLUDE port group (*,G) it needs to be also
943 * added to all S,G entries for proper replication, if we are adding
944 * a new INCLUDE port (S,G) then all of *,G EXCLUDE ports need to be
945 * added to it for proper replication
946 */
Nikolay Aleksandrov6567cb42021-07-21 17:01:26 +0300947 if (br_multicast_should_handle_mode(brmctx, group.proto)) {
Nikolay Aleksandrov8266a042020-09-22 10:30:24 +0300948 switch (filter_mode) {
949 case MCAST_EXCLUDE:
950 br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE);
951 break;
952 case MCAST_INCLUDE:
953 star_group = p->key.addr;
954 memset(&star_group.src, 0, sizeof(star_group.src));
955 star_mp = br_mdb_ip_get(br, &star_group);
956 if (star_mp)
957 br_multicast_sg_add_exclude_ports(star_mp, p);
958 break;
959 }
960 }
Cong Wangcfd56752012-12-11 22:23:08 +0000961
Cong Wangcfd56752012-12-11 22:23:08 +0000962 return 0;
963}
964
965static int __br_mdb_add(struct net *net, struct net_bridge *br,
Nikolay Aleksandrov7eea6292020-09-22 10:30:13 +0300966 struct net_bridge_port *p,
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300967 struct br_mdb_entry *entry,
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +0300968 struct nlattr **mdb_attrs,
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300969 struct netlink_ext_ack *extack)
Cong Wangcfd56752012-12-11 22:23:08 +0000970{
Cong Wangcfd56752012-12-11 22:23:08 +0000971 int ret;
972
Cong Wangcfd56752012-12-11 22:23:08 +0000973 spin_lock_bh(&br->multicast_lock);
Nikolay Aleksandrov8f8cb772020-09-22 10:30:21 +0300974 ret = br_mdb_add_group(br, p, entry, mdb_attrs, extack);
Cong Wangcfd56752012-12-11 22:23:08 +0000975 spin_unlock_bh(&br->multicast_lock);
Nikolay Aleksandrov7eea6292020-09-22 10:30:13 +0300976
Cong Wangcfd56752012-12-11 22:23:08 +0000977 return ret;
978}
979
David Ahernc21ef3e2017-04-16 09:48:24 -0700980static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
981 struct netlink_ext_ack *extack)
Cong Wangcfd56752012-12-11 22:23:08 +0000982{
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300983 struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1];
Cong Wangcfd56752012-12-11 22:23:08 +0000984 struct net *net = sock_net(skb->sk);
Nikolay Aleksandrov2594e9062015-09-25 19:00:11 +0200985 struct net_bridge_vlan_group *vg;
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +0300986 struct net_bridge_port *p = NULL;
Satish Ashoke44deb22015-08-03 13:29:16 +0200987 struct net_device *dev, *pdev;
Cong Wangcfd56752012-12-11 22:23:08 +0000988 struct br_mdb_entry *entry;
Nikolay Aleksandrov2594e9062015-09-25 19:00:11 +0200989 struct net_bridge_vlan *v;
Cong Wangcfd56752012-12-11 22:23:08 +0000990 struct net_bridge *br;
991 int err;
992
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +0300993 err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
Cong Wangcfd56752012-12-11 22:23:08 +0000994 if (err < 0)
995 return err;
996
997 br = netdev_priv(dev);
998
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +0300999 if (!netif_running(br->dev)) {
1000 NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running");
Nikolay Aleksandrov7eea6292020-09-22 10:30:13 +03001001 return -EINVAL;
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +03001002 }
1003
1004 if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
1005 NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled");
1006 return -EINVAL;
1007 }
Nikolay Aleksandrov7eea6292020-09-22 10:30:13 +03001008
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001009 if (entry->ifindex != br->dev->ifindex) {
1010 pdev = __dev_get_by_index(net, entry->ifindex);
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +03001011 if (!pdev) {
1012 NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist");
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001013 return -ENODEV;
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +03001014 }
Satish Ashoke44deb22015-08-03 13:29:16 +02001015
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001016 p = br_port_get_rtnl(pdev);
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +03001017 if (!p) {
1018 NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port");
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001019 return -EINVAL;
Nikolay Aleksandrov83f73982020-09-22 10:30:14 +03001020 }
1021
1022 if (p->br != br) {
1023 NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
1024 return -EINVAL;
1025 }
1026 if (p->state == BR_STATE_DISABLED) {
1027 NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state");
1028 return -EINVAL;
1029 }
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001030 vg = nbp_vlan_group(p);
1031 } else {
1032 vg = br_vlan_group(br);
1033 }
Satish Ashoke44deb22015-08-03 13:29:16 +02001034
Nikolay Aleksandrovf59783f2019-08-17 14:22:10 +03001035 /* If vlan filtering is enabled and VLAN is not specified
1036 * install mdb entry on all vlans configured on the port.
1037 */
Ido Schimmel1f514452017-05-26 08:37:23 +02001038 if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
Nikolay Aleksandrov2594e9062015-09-25 19:00:11 +02001039 list_for_each_entry(v, &vg->vlan_list, vlist) {
1040 entry->vid = v->vid;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +03001041 err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack);
Satish Ashoke44deb22015-08-03 13:29:16 +02001042 if (err)
1043 break;
Satish Ashoke44deb22015-08-03 13:29:16 +02001044 }
1045 } else {
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +03001046 err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack);
Satish Ashoke44deb22015-08-03 13:29:16 +02001047 }
1048
Cong Wangcfd56752012-12-11 22:23:08 +00001049 return err;
1050}
1051
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +03001052static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry,
1053 struct nlattr **mdb_attrs)
Cong Wangcfd56752012-12-11 22:23:08 +00001054{
Cong Wangcfd56752012-12-11 22:23:08 +00001055 struct net_bridge_mdb_entry *mp;
1056 struct net_bridge_port_group *p;
1057 struct net_bridge_port_group __rcu **pp;
1058 struct br_ip ip;
1059 int err = -EINVAL;
1060
Nikolay Aleksandrov13cefad2018-09-26 17:01:03 +03001061 if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
Cong Wangcfd56752012-12-11 22:23:08 +00001062 return -EINVAL;
1063
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +03001064 __mdb_entry_to_br_ip(entry, &ip, mdb_attrs);
Cong Wangcfd56752012-12-11 22:23:08 +00001065
1066 spin_lock_bh(&br->multicast_lock);
Nikolay Aleksandrov19e3a9c2018-12-05 15:14:24 +02001067 mp = br_mdb_ip_get(br, &ip);
Cong Wangcfd56752012-12-11 22:23:08 +00001068 if (!mp)
1069 goto unlock;
1070
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001071 /* host leave */
1072 if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
1073 br_multicast_host_leave(mp, false);
1074 err = 0;
Nikolay Aleksandrov81f19832020-09-07 12:56:12 +03001075 br_mdb_notify(br->dev, mp, NULL, RTM_DELMDB);
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001076 if (!mp->ports && netif_running(br->dev))
1077 mod_timer(&mp->timer, jiffies);
1078 goto unlock;
1079 }
1080
Cong Wangcfd56752012-12-11 22:23:08 +00001081 for (pp = &mp->ports;
1082 (p = mlock_dereference(*pp, br)) != NULL;
1083 pp = &p->next) {
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +03001084 if (!p->key.port || p->key.port->dev->ifindex != entry->ifindex)
Cong Wangcfd56752012-12-11 22:23:08 +00001085 continue;
1086
Nikolay Aleksandrov085b53c2020-09-22 10:30:22 +03001087 if (p->key.port->state == BR_STATE_DISABLED)
Cong Wangcfd56752012-12-11 22:23:08 +00001088 goto unlock;
1089
Nikolay Aleksandrov681590b2020-09-07 12:56:06 +03001090 br_multicast_del_pg(mp, p, pp);
Cong Wangcfd56752012-12-11 22:23:08 +00001091 err = 0;
Cong Wangcfd56752012-12-11 22:23:08 +00001092 break;
1093 }
1094
1095unlock:
1096 spin_unlock_bh(&br->multicast_lock);
1097 return err;
1098}
1099
David Ahernc21ef3e2017-04-16 09:48:24 -07001100static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
1101 struct netlink_ext_ack *extack)
Cong Wangcfd56752012-12-11 22:23:08 +00001102{
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +03001103 struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1];
Satish Ashoke44deb22015-08-03 13:29:16 +02001104 struct net *net = sock_net(skb->sk);
Nikolay Aleksandrov2594e9062015-09-25 19:00:11 +02001105 struct net_bridge_vlan_group *vg;
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001106 struct net_bridge_port *p = NULL;
Satish Ashoke44deb22015-08-03 13:29:16 +02001107 struct net_device *dev, *pdev;
Cong Wangcfd56752012-12-11 22:23:08 +00001108 struct br_mdb_entry *entry;
Nikolay Aleksandrov2594e9062015-09-25 19:00:11 +02001109 struct net_bridge_vlan *v;
Cong Wangcfd56752012-12-11 22:23:08 +00001110 struct net_bridge *br;
1111 int err;
1112
Nikolay Aleksandrov9c4258c2020-09-22 10:30:18 +03001113 err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
Cong Wangcfd56752012-12-11 22:23:08 +00001114 if (err < 0)
1115 return err;
1116
1117 br = netdev_priv(dev);
1118
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001119 if (entry->ifindex != br->dev->ifindex) {
1120 pdev = __dev_get_by_index(net, entry->ifindex);
1121 if (!pdev)
1122 return -ENODEV;
Satish Ashoke44deb22015-08-03 13:29:16 +02001123
Nikolay Aleksandrov1bc844e2019-08-17 14:22:13 +03001124 p = br_port_get_rtnl(pdev);
1125 if (!p || p->br != br || p->state == BR_STATE_DISABLED)
1126 return -EINVAL;
1127 vg = nbp_vlan_group(p);
1128 } else {
1129 vg = br_vlan_group(br);
1130 }
Satish Ashoke44deb22015-08-03 13:29:16 +02001131
Nikolay Aleksandrovf59783f2019-08-17 14:22:10 +03001132 /* If vlan filtering is enabled and VLAN is not specified
1133 * delete mdb entry on all vlans configured on the port.
1134 */
Ido Schimmel1f514452017-05-26 08:37:23 +02001135 if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
Nikolay Aleksandrov2594e9062015-09-25 19:00:11 +02001136 list_for_each_entry(v, &vg->vlan_list, vlist) {
1137 entry->vid = v->vid;
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +03001138 err = __br_mdb_del(br, entry, mdb_attrs);
Satish Ashoke44deb22015-08-03 13:29:16 +02001139 }
1140 } else {
Nikolay Aleksandrov88d4bd12020-09-22 10:30:19 +03001141 err = __br_mdb_del(br, entry, mdb_attrs);
Satish Ashoke44deb22015-08-03 13:29:16 +02001142 }
1143
Cong Wangcfd56752012-12-11 22:23:08 +00001144 return err;
1145}
1146
Cong Wangee07c6e2012-12-07 00:04:48 +00001147void br_mdb_init(void)
1148{
Florian Westphalc1c502b2017-12-02 21:44:07 +01001149 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);
Cong Wangee07c6e2012-12-07 00:04:48 +00001152}
Vlad Yasevich63233152012-12-19 09:13:48 +00001153
1154void 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}