xref: /aosp_15_r20/external/libnl/lib/route/mdb.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * lib/route/mdb.c		Multicast Database
4  */
5 
6 #include "nl-default.h"
7 
8 #include <linux/if_bridge.h>
9 
10 #include <netlink/netlink.h>
11 #include <netlink/route/mdb.h>
12 #include <netlink/route/nexthop.h>
13 #include <netlink/utils.h>
14 #include <netlink/route/rtnl.h>
15 
16 #include "nl-route.h"
17 #include "nl-aux-route/nl-route.h"
18 #include "nl-priv-dynamic-core/object-api.h"
19 #include "nl-priv-dynamic-core/cache-api.h"
20 
21 /** @cond SKIP */
22 #define MDB_ATTR_IFINDEX         0x000001
23 #define MDB_ATTR_ENTRIES         0x000002
24 
25 struct rtnl_mdb {
26 	NLHDR_COMMON
27 	uint32_t ifindex;
28 
29 	struct nl_list_head mdb_entry_list;
30 };
31 
32 struct rtnl_mdb_entry {
33 	struct nl_list_head mdb_list;
34 	struct nl_addr *addr;
35 	uint32_t ifindex;
36 	uint16_t vid;
37 	uint16_t proto;
38 	uint8_t state;
39 };
40 
41 static struct rtnl_mdb_entry *rtnl_mdb_entry_alloc(void);
42 static void rtnl_mdb_entry_free(struct rtnl_mdb_entry *mdb_entry);
43 
44 static struct nl_cache_ops rtnl_mdb_ops;
45 static struct nl_object_ops mdb_obj_ops;
46 /** @endcond */
47 
mdb_constructor(struct nl_object * obj)48 static void mdb_constructor(struct nl_object *obj)
49 {
50 	struct rtnl_mdb *_mdb = (struct rtnl_mdb *) obj;
51 
52 	nl_init_list_head(&_mdb->mdb_entry_list);
53 }
54 
mdb_free_data(struct nl_object * obj)55 static void mdb_free_data(struct nl_object *obj)
56 {
57 	struct rtnl_mdb *mdb = (struct rtnl_mdb *)obj;
58 	struct rtnl_mdb_entry *mdb_entry;
59 	struct rtnl_mdb_entry *mdb_entry_safe;
60 
61 	nl_list_for_each_entry_safe(mdb_entry, mdb_entry_safe,
62 				    &mdb->mdb_entry_list, mdb_list)
63 		rtnl_mdb_entry_free(mdb_entry);
64 }
65 
mdb_entry_equal(struct rtnl_mdb_entry * a,struct rtnl_mdb_entry * b)66 static int mdb_entry_equal(struct rtnl_mdb_entry *a, struct rtnl_mdb_entry *b)
67 {
68 	return    a->ifindex == b->ifindex
69 	       && a->vid == b->vid
70 	       && a->proto == b->proto
71 	       && a->state == b->state
72 	       && nl_addr_cmp(a->addr, b->addr) == 0;
73 }
74 
mdb_compare(struct nl_object * _a,struct nl_object * _b,uint64_t attrs,int flags)75 static uint64_t mdb_compare(struct nl_object *_a, struct nl_object *_b,
76 			    uint64_t attrs, int flags)
77 {
78 	struct rtnl_mdb *a = (struct rtnl_mdb *) _a;
79 	struct rtnl_mdb *b = (struct rtnl_mdb *) _b;
80 	struct rtnl_mdb_entry *a_entry, *b_entry;
81 	uint64_t diff = 0;
82 
83 #define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR)
84 	diff |= _DIFF(MDB_ATTR_IFINDEX, a->ifindex != b->ifindex);
85 #undef _DIFF
86 
87 	a_entry = nl_list_entry(a->mdb_entry_list.next, struct rtnl_mdb_entry, mdb_list);
88 	b_entry = nl_list_entry(b->mdb_entry_list.next, struct rtnl_mdb_entry, mdb_list);
89 	while (1) {
90 		if (   &a_entry->mdb_list == &a->mdb_entry_list
91 		    || &b_entry->mdb_list == &b->mdb_entry_list) {
92 			if (   &a_entry->mdb_list != &a->mdb_entry_list
93 			    || &b_entry->mdb_list != &b->mdb_entry_list)
94 				diff |= MDB_ATTR_ENTRIES;
95 			break;
96 		}
97 		if (!mdb_entry_equal(a_entry, b_entry)) {
98 			diff |= MDB_ATTR_ENTRIES;
99 			break;
100 		}
101 		a_entry = nl_list_entry(a_entry->mdb_list.next, struct rtnl_mdb_entry, mdb_list);
102 		b_entry = nl_list_entry(b_entry->mdb_list.next, struct rtnl_mdb_entry, mdb_list);
103 	}
104 
105 	return diff;
106 }
107 
mdb_entry_clone(struct rtnl_mdb_entry * src)108 static struct rtnl_mdb_entry *mdb_entry_clone(struct rtnl_mdb_entry *src)
109 {
110 	struct rtnl_mdb_entry *dst = rtnl_mdb_entry_alloc();
111 	if (!dst)
112 		return NULL;
113 
114 	dst->ifindex = src->ifindex;
115 	dst->state = src->state;
116 	dst->vid = src->vid;
117 	dst->proto = src->proto;
118 
119 	dst->addr = nl_addr_clone(src->addr);
120 	if (dst->addr == NULL) {
121 		free(dst);
122 		return NULL;
123 	}
124 
125 	return dst;
126 }
127 
mdb_clone(struct nl_object * _dst,struct nl_object * _src)128 static int mdb_clone(struct nl_object *_dst, struct nl_object *_src)
129 {
130 	struct rtnl_mdb *dst = nl_object_priv(_dst);
131 	struct rtnl_mdb *src = nl_object_priv(_src);
132 	struct rtnl_mdb_entry *entry;
133 
134 	nl_init_list_head(&dst->mdb_entry_list);
135 
136 	nl_list_for_each_entry(entry, &src->mdb_entry_list, mdb_list) {
137 		struct rtnl_mdb_entry *copy = mdb_entry_clone(entry);
138 
139 		if (!copy)
140 			return -NLE_NOMEM;
141 
142 		rtnl_mdb_add_entry(dst, copy);
143 	}
144 
145 	return 0;
146 }
147 
mdb_update(struct nl_object * old_obj,struct nl_object * new_obj)148 static int mdb_update(struct nl_object *old_obj, struct nl_object *new_obj)
149 {
150 	struct rtnl_mdb *old = (struct rtnl_mdb *) old_obj;
151 	struct rtnl_mdb *new = (struct rtnl_mdb *) new_obj;
152 	struct rtnl_mdb_entry *entry, *old_entry;
153 	int action = new_obj->ce_msgtype;
154 
155 	if (new->ifindex != old->ifindex)
156 		return -NLE_OPNOTSUPP;
157 
158 	switch (action) {
159 	case RTM_NEWMDB:
160 		nl_list_for_each_entry(entry, &new->mdb_entry_list, mdb_list) {
161 			struct rtnl_mdb_entry *copy = mdb_entry_clone(entry);
162 
163 			if (!copy)
164 				return -NLE_NOMEM;
165 
166 			rtnl_mdb_add_entry(old, copy);
167 		}
168 		break;
169 	case RTM_DELMDB:
170 		entry = nl_list_first_entry(&new->mdb_entry_list,
171 		                            struct rtnl_mdb_entry,
172 		                            mdb_list);
173 		nl_list_for_each_entry(old_entry, &old->mdb_entry_list, mdb_list) {
174 			if (   old_entry->ifindex == entry->ifindex
175 			    && !nl_addr_cmp(old_entry->addr, entry->addr)) {
176 				nl_list_del(&old_entry->mdb_list);
177 				break;
178 			}
179 		}
180 		break;
181 	}
182 
183 	return NLE_SUCCESS;
184 }
185 
186 static struct nla_policy mdb_policy[MDBA_MAX + 1] = {
187 	[MDBA_MDB] = {.type = NLA_NESTED},
188 };
189 
190 static struct nla_policy mdb_db_policy[MDBA_MDB_MAX + 1] = {
191 	[MDBA_MDB_ENTRY] = {.type = NLA_NESTED},
192 };
193 
mdb_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)194 static int mdb_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
195 			  struct nlmsghdr *nlh, struct nl_parser_param *pp)
196 {
197 	int err = 0;
198 	int rem = 0;
199 	struct nlattr *tb[MDBA_MAX + 1];
200 	struct br_port_msg *port;
201 	struct nlattr *nla;
202 	struct br_mdb_entry *e;
203 	_nl_auto_rtnl_mdb struct rtnl_mdb *mdb = rtnl_mdb_alloc();
204 
205 	if (!mdb)
206 		return -NLE_NOMEM;
207 
208 	err = nlmsg_parse(nlh, sizeof(struct br_port_msg), tb, MDBA_MAX,
209 	                  mdb_policy);
210 	if (err < 0)
211 		return err;
212 
213 	mdb->ce_msgtype = nlh->nlmsg_type;
214 
215 	port = nlmsg_data(nlh);
216 	mdb->ifindex = port->ifindex;
217 	mdb->ce_mask |= MDB_ATTR_IFINDEX;
218 
219 	if (tb[MDBA_MDB]) {
220 		struct nlattr *db_attr[MDBA_MDB_MAX+1];
221 
222 		err = nla_parse_nested(db_attr, MDBA_MDB_MAX, tb[MDBA_MDB],
223 				       mdb_db_policy);
224 		if (err < 0)
225 			return err;
226 		rem = nla_len(tb[MDBA_MDB]);
227 
228 		for (nla = nla_data(tb[MDBA_MDB]); nla_ok(nla, rem);
229 		     nla = nla_next(nla, &rem)) {
230 			int rm = nla_len(nla);
231 			struct nlattr *nla2;
232 
233 			for (nla2 = nla_data(nla); nla_ok(nla2, rm);
234 			     nla2 = nla_next(nla2, &rm)) {
235 				_nl_auto_nl_addr struct nl_addr *addr = NULL;
236 				struct rtnl_mdb_entry *entry;
237 				uint16_t proto;
238 
239 				e = nla_data(nla2);
240 
241 				proto = ntohs(e->addr.proto);
242 
243 				if (proto == ETH_P_IP) {
244 					addr = nl_addr_build(
245 						AF_INET, &e->addr.u.ip4,
246 						sizeof(e->addr.u.ip4));
247 				} else if (proto == ETH_P_IPV6) {
248 					addr = nl_addr_build(
249 						AF_INET6, &e->addr.u.ip6,
250 						sizeof(e->addr.u.ip6));
251 				} else {
252 					addr = nl_addr_build(
253 						AF_LLC, e->addr.u.mac_addr,
254 						sizeof(e->addr.u.mac_addr));
255 				}
256 				if (!addr)
257 					return -NLE_NOMEM;
258 
259 				entry = rtnl_mdb_entry_alloc();
260 				if (!entry)
261 					return -NLE_NOMEM;
262 
263 				mdb->ce_mask |= MDB_ATTR_ENTRIES;
264 
265 				entry->ifindex = e->ifindex;
266 				entry->vid = e->vid;
267 				entry->state = e->state;
268 				entry->proto = ntohs(e->addr.proto);
269 				entry->addr = _nl_steal_pointer(&addr);
270 				rtnl_mdb_add_entry(mdb, entry);
271 			}
272 		}
273 	}
274 
275 	return pp->pp_cb((struct nl_object *) mdb, pp);
276 }
277 
mdb_request_update(struct nl_cache * cache,struct nl_sock * sk)278 static int mdb_request_update(struct nl_cache *cache, struct nl_sock *sk)
279 {
280 	return nl_rtgen_request(sk, RTM_GETMDB, AF_BRIDGE, NLM_F_DUMP);
281 }
282 
mdb_entry_dump_line(struct rtnl_mdb_entry * entry,struct nl_dump_params * p)283 static void mdb_entry_dump_line(struct rtnl_mdb_entry *entry,
284                                 struct nl_dump_params *p)
285 {
286 	char buf[INET6_ADDRSTRLEN];
287 
288 	nl_dump(p, "port %d ", entry->ifindex);
289 	nl_dump(p, "vid %d ", entry->vid);
290 	nl_dump(p, "proto 0x%04x ", entry->proto);
291 	nl_dump(p, "address %s\n", nl_addr2str(entry->addr, buf, sizeof(buf)));
292 }
293 
mdb_dump_line(struct nl_object * obj,struct nl_dump_params * p)294 static void mdb_dump_line(struct nl_object *obj, struct nl_dump_params *p)
295 {
296 	struct rtnl_mdb *mdb = (struct rtnl_mdb *) obj;
297 	struct rtnl_mdb_entry *_mdb;
298 
299 	nl_dump(p, "dev %d \n", mdb->ifindex);
300 
301 	nl_list_for_each_entry(_mdb, &mdb->mdb_entry_list, mdb_list) {
302 		p->dp_ivar = NH_DUMP_FROM_ONELINE;
303 		mdb_entry_dump_line(_mdb, p);
304 	}
305 }
306 
mdb_dump_details(struct nl_object * obj,struct nl_dump_params * p)307 static void mdb_dump_details(struct nl_object *obj, struct nl_dump_params *p)
308 {
309 	mdb_dump_line(obj, p);
310 }
311 
mdb_dump_stats(struct nl_object * obj,struct nl_dump_params * p)312 static void mdb_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
313 {
314 	mdb_dump_details(obj, p);
315 }
316 
rtnl_mdb_put(struct rtnl_mdb * mdb)317 void rtnl_mdb_put(struct rtnl_mdb *mdb)
318 {
319 	nl_object_put((struct nl_object *) mdb);
320 }
321 
322 /** @} */
323 
324 /**
325  * @name Cache Management
326  * @{
327  */
rtnl_mdb_alloc_cache(struct nl_sock * sk,struct nl_cache ** result)328 int rtnl_mdb_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
329 {
330 	return nl_cache_alloc_and_fill(&rtnl_mdb_ops, sk, result);
331 }
332 
333 /**
334  * Build a neighbour cache including all MDB entries currently configured in the kernel.
335  * @arg sock		Netlink socket.
336  * @arg result		Pointer to store resulting cache.
337  * @arg flags		Flags to apply to cache before filling
338  *
339  * Allocates a new MDB cache, initializes it properly and updates it
340  * to include all Multicast Database entries currently configured in the kernel.
341  *
342  * @return 0 on success or a negative error code.
343  */
rtnl_mdb_alloc_cache_flags(struct nl_sock * sock,struct nl_cache ** result,unsigned int flags)344 int rtnl_mdb_alloc_cache_flags(struct nl_sock *sock, struct nl_cache **result,
345 			       unsigned int flags)
346 {
347 	struct nl_cache *cache;
348 	int err;
349 
350 	cache = nl_cache_alloc(&rtnl_mdb_ops);
351 	if (!cache)
352 		return -NLE_NOMEM;
353 
354 	nl_cache_set_flags(cache, flags);
355 
356 	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
357 		nl_cache_free(cache);
358 		return err;
359 	}
360 
361 	*result = cache;
362 	return 0;
363 }
364 
365 /** @} */
366 
367 /**
368  * @name Attributes
369  * @{
370  */
rtnl_mdb_get_ifindex(struct rtnl_mdb * mdb)371 uint32_t rtnl_mdb_get_ifindex(struct rtnl_mdb *mdb)
372 {
373 	return mdb->ifindex;
374 }
375 
rtnl_mdb_add_entry(struct rtnl_mdb * mdb,struct rtnl_mdb_entry * entry)376 void rtnl_mdb_add_entry(struct rtnl_mdb *mdb, struct rtnl_mdb_entry *entry)
377 {
378 	nl_list_add_tail(&entry->mdb_list, &mdb->mdb_entry_list);
379 }
380 
rtnl_mdb_foreach_entry(struct rtnl_mdb * mdb,void (* cb)(struct rtnl_mdb_entry *,void *),void * arg)381 void rtnl_mdb_foreach_entry(struct rtnl_mdb *mdb,
382                             void (*cb)(struct rtnl_mdb_entry *, void *),
383                             void *arg)
384 {
385 	struct rtnl_mdb_entry *entry;
386 
387 	nl_list_for_each_entry(entry, &mdb->mdb_entry_list, mdb_list) {
388 		cb(entry, arg);
389 	}
390 }
391 
rtnl_mdb_entry_get_ifindex(struct rtnl_mdb_entry * mdb_entry)392 int rtnl_mdb_entry_get_ifindex(struct rtnl_mdb_entry *mdb_entry)
393 {
394 	return mdb_entry->ifindex;
395 }
396 
rtnl_mdb_entry_get_vid(struct rtnl_mdb_entry * mdb_entry)397 int rtnl_mdb_entry_get_vid(struct rtnl_mdb_entry *mdb_entry)
398 {
399 	return mdb_entry->vid;
400 }
401 
rtnl_mdb_entry_get_state(struct rtnl_mdb_entry * mdb_entry)402 int rtnl_mdb_entry_get_state(struct rtnl_mdb_entry *mdb_entry)
403 {
404 	return mdb_entry->state;
405 }
406 
rtnl_mdb_entry_get_addr(struct rtnl_mdb_entry * mdb_entry)407 struct nl_addr *rtnl_mdb_entry_get_addr(struct rtnl_mdb_entry *mdb_entry)
408 {
409 	return mdb_entry->addr;
410 }
411 
rtnl_mdb_entry_get_proto(struct rtnl_mdb_entry * mdb_entry)412 uint16_t rtnl_mdb_entry_get_proto(struct rtnl_mdb_entry *mdb_entry)
413 {
414 	return mdb_entry->proto;
415 }
416 
417 /** @} */
418 
419 static struct nl_object_ops mdb_obj_ops = {
420 	.oo_name = "route/mdb",
421 	.oo_size = sizeof(struct rtnl_mdb),
422 	.oo_constructor = mdb_constructor,
423 	.oo_dump = {
424 	            [NL_DUMP_LINE]    = mdb_dump_line,
425 	            [NL_DUMP_DETAILS] = mdb_dump_details,
426 	            [NL_DUMP_STATS]   = mdb_dump_stats,
427 	},
428 	.oo_clone = mdb_clone,
429 	.oo_compare = mdb_compare,
430 	.oo_update = mdb_update,
431 	.oo_free_data = mdb_free_data,
432 };
433 
rtnl_mdb_alloc(void)434 struct rtnl_mdb *rtnl_mdb_alloc(void)
435 {
436 	return (struct rtnl_mdb *) nl_object_alloc(&mdb_obj_ops);
437 }
438 
rtnl_mdb_entry_alloc(void)439 static struct rtnl_mdb_entry *rtnl_mdb_entry_alloc(void)
440 {
441 	struct rtnl_mdb_entry *mdb;
442 
443 	mdb = calloc(1, sizeof(struct rtnl_mdb_entry));
444 	if (!mdb)
445 		return NULL;
446 
447 	nl_init_list_head(&mdb->mdb_list);
448 
449 	return mdb;
450 
451 }
452 
rtnl_mdb_entry_free(struct rtnl_mdb_entry * mdb_entry)453 static void rtnl_mdb_entry_free(struct rtnl_mdb_entry *mdb_entry)
454 {
455 	nl_list_del(&mdb_entry->mdb_list);
456 	nl_addr_put(mdb_entry->addr);
457 	free(mdb_entry);
458 }
459 
460 static struct nl_af_group mdb_groups[] = {
461 	{AF_BRIDGE, RTNLGRP_MDB},
462 	{END_OF_GROUP_LIST},
463 };
464 
465 static struct nl_cache_ops rtnl_mdb_ops = {
466 	.co_name = "route/mdb",
467 	.co_hdrsize = sizeof(struct br_port_msg),
468 	.co_msgtypes = {
469 	                { RTM_NEWMDB, NL_ACT_NEW, "new"},
470 	                { RTM_DELMDB, NL_ACT_DEL, "del"},
471 	                { RTM_GETMDB, NL_ACT_GET, "get"},
472 	                END_OF_MSGTYPES_LIST,
473 	                },
474 	.co_protocol = NETLINK_ROUTE,
475 	.co_groups = mdb_groups,
476 	.co_request_update = mdb_request_update,
477 	.co_msg_parser = mdb_msg_parser,
478 	.co_obj_ops = &mdb_obj_ops,
479 };
480 
mdb_init(void)481 static void _nl_init mdb_init(void)
482 {
483 	nl_cache_mngt_register(&rtnl_mdb_ops);
484 }
485 
mdb_exit(void)486 static void _nl_exit mdb_exit(void)
487 {
488 	nl_cache_mngt_unregister(&rtnl_mdb_ops);
489 }
490 
491 /** @} */
492