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