libnl 3.7.0
mdb.c
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
16static struct rtnl_mdb_entry *rtnl_mdb_entry_alloc(void);
17static void rtnl_mdb_entry_free(struct rtnl_mdb_entry *mdb_entry);
18
19static struct nl_cache_ops rtnl_mdb_ops;
20static struct nl_object_ops mdb_obj_ops;
21/** @endcond */
22
23static 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
30static 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
41static 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
50static 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
83static 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
103static 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
123static 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
161static struct nla_policy mdb_policy[MDBA_MAX + 1] = {
162 [MDBA_MDB] = {.type = NLA_NESTED},
163};
164
165static struct nla_policy mdb_db_policy[MDBA_MDB_MAX + 1] = {
166 [MDBA_MDB_ENTRY] = {.type = NLA_NESTED},
167};
168
169static 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
253static 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
258static 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
269static 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
282static void mdb_dump_details(struct nl_object *obj, struct nl_dump_params *p)
283{
284 mdb_dump_line(obj, p);
285}
286
287static void mdb_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
288{
289 mdb_dump_details(obj, p);
290}
291
292void 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 */
303int 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 */
319int 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 */
346uint32_t rtnl_mdb_get_ifindex(struct rtnl_mdb *mdb)
347{
348 return mdb->ifindex;
349}
350
351void 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
356void 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
367int rtnl_mdb_entry_get_ifindex(struct rtnl_mdb_entry *mdb_entry)
368{
369 return mdb_entry->ifindex;
370}
371
372int rtnl_mdb_entry_get_vid(struct rtnl_mdb_entry *mdb_entry)
373{
374 return mdb_entry->vid;
375}
376
377int rtnl_mdb_entry_get_state(struct rtnl_mdb_entry *mdb_entry)
378{
379 return mdb_entry->state;
380}
381
382struct nl_addr *rtnl_mdb_entry_get_addr(struct rtnl_mdb_entry *mdb_entry)
383{
384 return mdb_entry->addr;
385}
386
387uint16_t rtnl_mdb_entry_get_proto(struct rtnl_mdb_entry *mdb_entry)
388{
389 return mdb_entry->proto;
390}
391
392/** @} */
393
394static 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
409struct rtnl_mdb *rtnl_mdb_alloc(void)
410{
411 return (struct rtnl_mdb *) nl_object_alloc(&mdb_obj_ops);
412}
413
414static 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
428static 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
435static struct nl_af_group mdb_groups[] = {
436 {AF_BRIDGE, RTNLGRP_MDB},
437 {END_OF_GROUP_LIST},
438};
439
440static 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
456static void __init mdb_init(void)
457{
458 nl_cache_mngt_register(&rtnl_mdb_ops);
459}
460
461static void __exit mdb_exit(void)
462{
463 nl_cache_mngt_unregister(&rtnl_mdb_ops);
464}
465
466/** @} */
struct nl_addr * nl_addr_build(int family, const void *buf, size_t size)
Allocate abstract address based on a binary address.
Definition: addr.c:211
int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
Compare abstract addresses.
Definition: addr.c:584
struct nl_addr * nl_addr_clone(const struct nl_addr *addr)
Clone existing abstract address object.
Definition: addr.c:492
char * nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
Convert abstract address object to character string.
Definition: addr.c:998
void nl_addr_put(struct nl_addr *addr)
Decrease the reference counter of an abstract address.
Definition: addr.c:538
struct nlattr * nla_next(const struct nlattr *nla, int *remaining)
Return next attribute in a stream of attributes.
Definition: attr.c:165
int nla_ok(const struct nlattr *nla, int remaining)
Check if the attribute header and payload can be accessed safely.
Definition: attr.c:142
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition: attr.c:114
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, const struct nla_policy *policy)
Create attribute index based on nested attribute.
Definition: attr.c:1016
int nla_len(const struct nlattr *nla)
Return length of the payload .
Definition: attr.c:125
@ NLA_NESTED
Nested attributes.
Definition: attr.h:42
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
Unregister a set of cache operations.
Definition: cache_mngt.c:281
int nl_cache_mngt_register(struct nl_cache_ops *ops)
Register a set of cache operations.
Definition: cache_mngt.c:246
int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition: cache.c:1035
void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags)
Set cache flags.
Definition: cache.c:608
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition: cache.c:403
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate new cache.
Definition: cache.c:178
int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, struct nl_cache **result)
Allocate new cache and fill it.
Definition: cache.c:228
void * nlmsg_data(const struct nlmsghdr *nlh)
Return pointer to message payload.
Definition: msg.c:100
int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy)
parse attributes of a netlink message
Definition: msg.c:208
void nl_object_put(struct nl_object *obj)
Release a reference from an object.
Definition: object.c:214
struct nl_object * nl_object_alloc(struct nl_object_ops *ops)
Allocate a new object of kind specified by the operations handle.
Definition: object.c:48
int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags)
Send routing netlink request message.
Definition: rtnl.c:35
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:955
@ NL_DUMP_STATS
Dump all attributes including statistics.
Definition: types.h:18
@ NL_DUMP_LINE
Dump object briefly on one line.
Definition: types.h:16
@ NL_DUMP_DETAILS
Dump all attributes but no statistics.
Definition: types.h:17
Dumping parameters.
Definition: types.h:28
int dp_ivar
PRIVATE Owned by the current caller.
Definition: types.h:99
Attribute validation policy.
Definition: attr.h:63
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:65