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