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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
282 static void mdb_dump_details(struct nl_object *obj, struct nl_dump_params *p)
283 {
284  mdb_dump_line(obj, p);
285 }
286 
287 static void mdb_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
288 {
289  mdb_dump_details(obj, p);
290 }
291 
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  */
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  */
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  */
346 uint32_t rtnl_mdb_get_ifindex(struct rtnl_mdb *mdb)
347 {
348  return mdb->ifindex;
349 }
350 
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 
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 
367 int rtnl_mdb_entry_get_ifindex(struct rtnl_mdb_entry *mdb_entry)
368 {
369  return mdb_entry->ifindex;
370 }
371 
372 int rtnl_mdb_entry_get_vid(struct rtnl_mdb_entry *mdb_entry)
373 {
374  return mdb_entry->vid;
375 }
376 
377 int rtnl_mdb_entry_get_state(struct rtnl_mdb_entry *mdb_entry)
378 {
379  return mdb_entry->state;
380 }
381 
382 struct nl_addr *rtnl_mdb_entry_get_addr(struct rtnl_mdb_entry *mdb_entry)
383 {
384  return mdb_entry->addr;
385 }
386 
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 
409 struct rtnl_mdb *rtnl_mdb_alloc(void)
410 {
411  return (struct rtnl_mdb *) nl_object_alloc(&mdb_obj_ops);
412 }
413 
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 
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 
456 static void __init mdb_init(void)
457 {
458  nl_cache_mngt_register(&rtnl_mdb_ops);
459 }
460 
461 static void __exit mdb_exit(void)
462 {
463  nl_cache_mngt_unregister(&rtnl_mdb_ops);
464 }
465 
466 /** @} */
char * nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
Convert abstract address object to character string.
Definition: addr.c:993
int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
Compare abstract addresses.
Definition: addr.c:579
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
struct nl_addr * nl_addr_clone(const struct nl_addr *addr)
Clone existing abstract address object.
Definition: addr.c:487
void nl_addr_put(struct nl_addr *addr)
Decrease the reference counter of an abstract address.
Definition: addr.c:533
int nla_ok(const struct nlattr *nla, int remaining)
Check if the attribute header and payload can be accessed safely.
Definition: attr.c:142
struct nlattr * nla_next(const struct nlattr *nla, int *remaining)
Return next attribute in a stream of attributes.
Definition: attr.c:165
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:1013
int nla_len(const struct nlattr *nla)
Return length of the payload .
Definition: attr.c:125
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition: attr.c:114
@ 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
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
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate new cache.
Definition: cache.c:178
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 * nlmsg_data(const struct nlmsghdr *nlh)
Return pointer to message payload.
Definition: msg.c:100
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