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