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