libnl  3.6.0
netconf.c
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2017 David Ahern <dsa@cumulusnetworks.com>
4  */
5 
6 /**
7  * @ingroup rtnl
8  * @defgroup netconf Netconf
9  * @brief
10  *
11  * @{
12  */
13 
14 #include <netlink-private/netlink.h>
15 #include <netlink/netlink.h>
16 #include <netlink/utils.h>
17 #include <netlink/route/netconf.h>
18 #include <linux/netconf.h>
19 #include <linux/socket.h>
20 #include <netlink/hashtable.h>
21 
22 /** @cond SKIP */
23 #define NETCONF_ATTR_FAMILY 0x0001
24 #define NETCONF_ATTR_IFINDEX 0x0002
25 #define NETCONF_ATTR_RP_FILTER 0x0004
26 #define NETCONF_ATTR_FWDING 0x0008
27 #define NETCONF_ATTR_MC_FWDING 0x0010
28 #define NETCONF_ATTR_PROXY_NEIGH 0x0020
29 #define NETCONF_ATTR_IGNORE_RT_LINKDWN 0x0040
30 #define NETCONF_ATTR_INPUT 0x0080
31 
32 struct rtnl_netconf
33 {
34  NLHDR_COMMON
35 
36  int family;
37  int ifindex;
38  int rp_filter;
39  int forwarding;
40  int mc_forwarding;
41  int proxy_neigh;
42  int ignore_routes_linkdown;
43  int input;
44 };
45 
46 static struct nl_cache_ops rtnl_netconf_ops;
47 static struct nl_object_ops netconf_obj_ops;
48 /** @endcond */
49 
50 static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
51  [NETCONFA_IFINDEX] = { .type = NLA_S32 },
52  [NETCONFA_FORWARDING] = { .type = NLA_S32 },
53  [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
54  [NETCONFA_RP_FILTER] = { .type = NLA_S32 },
55  [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 },
56  [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 },
57 };
58 
59 static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
60  [NETCONFA_IFINDEX] = { .type = NLA_S32 },
61  [NETCONFA_FORWARDING] = { .type = NLA_S32 },
62  [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
63  [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 },
64  [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 },
65 };
66 
67 static struct nla_policy devconf_mpls_policy[NETCONFA_MAX+1] = {
68  [NETCONFA_IFINDEX] = { .type = NLA_S32 },
69  [NETCONFA_INPUT] = { .type = NLA_S32 },
70 };
71 
72 static struct rtnl_netconf *rtnl_netconf_alloc(void)
73 {
74  return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops);
75 }
76 
77 static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
78  struct nlmsghdr *nlh, struct nl_parser_param *pp)
79 {
80  struct nlattr *tb[NETCONFA_MAX+1], *attr;
81  struct rtnl_netconf *nc;
82  struct netconfmsg *ncm;
83  int err;
84 
85  ncm = nlmsg_data(nlh);
86  switch (ncm->ncm_family) {
87  case AF_INET:
88  err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
89  devconf_ipv4_policy);
90  if (err < 0)
91  return err;
92  break;
93  case AF_INET6:
94  err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
95  devconf_ipv6_policy);
96  if (err < 0)
97  return err;
98  break;
99  case AF_MPLS:
100  err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
101  devconf_mpls_policy);
102  if (err < 0)
103  return err;
104  break;
105  default:
106  printf("unexpected netconf family: %d\n", ncm->ncm_family);
107  return -1;
108  }
109 
110  if (!tb[NETCONFA_IFINDEX])
111  return -1;
112 
113  nc = rtnl_netconf_alloc();
114  if (!nc)
115  return -NLE_NOMEM;
116 
117  nc->ce_msgtype = nlh->nlmsg_type;
118  nc->family = ncm->ncm_family;
119  nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
120 
121  nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX;
122 
123 
124  if (tb[NETCONFA_RP_FILTER]) {
125  attr = tb[NETCONFA_RP_FILTER];
126  nc->rp_filter = nla_get_s32(attr);
127  nc->ce_mask |= NETCONF_ATTR_RP_FILTER;
128  }
129 
130  if (tb[NETCONFA_FORWARDING]) {
131  attr = tb[NETCONFA_FORWARDING];
132  nc->forwarding = nla_get_s32(attr);
133  nc->ce_mask |= NETCONF_ATTR_FWDING;
134  }
135 
136  if (tb[NETCONFA_MC_FORWARDING]) {
137  attr = tb[NETCONFA_MC_FORWARDING];
138  nc->mc_forwarding = nla_get_s32(attr);
139  nc->ce_mask |= NETCONF_ATTR_MC_FWDING;
140  }
141 
142  if (tb[NETCONFA_PROXY_NEIGH]) {
143  attr = tb[NETCONFA_PROXY_NEIGH];
144  nc->proxy_neigh = nla_get_s32(attr);
145  nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH;
146  }
147 
148  if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) {
149  attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN];
150  nc->ignore_routes_linkdown = nla_get_s32(attr);
151  nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN;
152  }
153 
154  if (tb[NETCONFA_INPUT]) {
155  attr = tb[NETCONFA_INPUT];
156  nc->input = nla_get_s32(attr);
157  nc->ce_mask |= NETCONF_ATTR_INPUT;
158  }
159 
160  err = pp->pp_cb((struct nl_object *) nc, pp);
161 
162  rtnl_netconf_put(nc);
163  return err;
164 }
165 
166 static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk)
167 {
168  struct netconfmsg nc = {
169  .ncm_family = cache->c_iarg1,
170  };
171 
172  return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc));
173 }
174 
175 static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p)
176 {
177  struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
178  struct nl_cache *link_cache;
179  char buf[64];
180 
181  switch(nc->family) {
182  case AF_INET:
183  nl_dump(p, "ipv4 ");
184  break;
185  case AF_INET6:
186  nl_dump(p, "ipv6 ");
187  break;
188  case AF_MPLS:
189  nl_dump(p, "mpls ");
190  break;
191  default:
192  return;
193  }
194 
195  switch(nc->ifindex) {
196  case NETCONFA_IFINDEX_ALL:
197  nl_dump(p, "all ");
198  break;
199  case NETCONFA_IFINDEX_DEFAULT:
200  nl_dump(p, "default ");
201  break;
202  default:
203  link_cache = nl_cache_mngt_require_safe("route/link");
204  if (link_cache) {
205  nl_dump(p, "dev %s ",
206  rtnl_link_i2name(link_cache, nc->ifindex,
207  buf, sizeof(buf)));
208  nl_cache_put(link_cache);
209  } else
210  nl_dump(p, "dev %d ", nc->ifindex);
211  }
212 
213  if (nc->ce_mask & NETCONF_ATTR_FWDING) {
214  nl_dump(p, "forwarding %s ",
215  nc->forwarding ? "on" : "off");
216  }
217 
218  if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) {
219  if (nc->rp_filter == 0)
220  nl_dump(p, "rp_filter off ");
221  else if (nc->rp_filter == 1)
222  nl_dump(p, "rp_filter strict ");
223  else if (nc->rp_filter == 2)
224  nl_dump(p, "rp_filter loose ");
225  else
226  nl_dump(p, "rp_filter unknown-mode ");
227  }
228 
229  if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) {
230  nl_dump(p, "mc_forwarding %s ",
231  nc->mc_forwarding ? "on" : "off");
232  }
233 
234  if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
235  nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh);
236 
237  if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) {
238  nl_dump(p, "ignore_routes_with_linkdown %s ",
239  nc->ignore_routes_linkdown ? "on" : "off");
240  }
241 
242  if (nc->ce_mask & NETCONF_ATTR_INPUT)
243  nl_dump(p, "input %s ", nc->input ? "on" : "off");
244 
245  nl_dump(p, "\n");
246 }
247 
248 static const struct trans_tbl netconf_attrs[] = {
249  __ADD(NETCONF_ATTR_FAMILY, family),
250  __ADD(NETCONF_ATTR_IFINDEX, ifindex),
251  __ADD(NETCONF_ATTR_RP_FILTER, rp_filter),
252  __ADD(NETCONF_ATTR_FWDING, forwarding),
253  __ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding),
254  __ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh),
255  __ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown),
256  __ADD(NETCONF_ATTR_INPUT, input),
257 };
258 
259 static char *netconf_attrs2str(int attrs, char *buf, size_t len)
260 {
261  return __flags2str(attrs, buf, len, netconf_attrs,
262  ARRAY_SIZE(netconf_attrs));
263 }
264 
265 static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey,
266  uint32_t table_sz)
267 {
268  struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
269  unsigned int nckey_sz;
270  struct nc_hash_key {
271  int nc_family;
272  int nc_index;
273  } __attribute__((packed)) nckey;
274 
275  nckey_sz = sizeof(nckey);
276  nckey.nc_family = nc->family;
277  nckey.nc_index = nc->ifindex;
278 
279  *hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz;
280 
281  NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n",
282  nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey);
283 }
284 
285 static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b,
286  uint64_t attrs, int flags)
287 {
288  struct rtnl_netconf *a = (struct rtnl_netconf *) _a;
289  struct rtnl_netconf *b = (struct rtnl_netconf *) _b;
290  uint64_t diff = 0;
291 
292 #define NETCONF_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NETCONF_ATTR_##ATTR, a, b, EXPR)
293 
294  diff |= NETCONF_DIFF(FAMILY, a->family != b->family);
295  diff |= NETCONF_DIFF(IFINDEX, a->ifindex != b->ifindex);
296  diff |= NETCONF_DIFF(RP_FILTER, a->rp_filter != b->rp_filter);
297  diff |= NETCONF_DIFF(FWDING, a->forwarding != b->forwarding);
298  diff |= NETCONF_DIFF(MC_FWDING, a->mc_forwarding != b->mc_forwarding);
299  diff |= NETCONF_DIFF(PROXY_NEIGH, a->proxy_neigh != b->proxy_neigh);
300  diff |= NETCONF_DIFF(IGNORE_RT_LINKDWN,
301  a->ignore_routes_linkdown != b->ignore_routes_linkdown);
302  diff |= NETCONF_DIFF(INPUT, a->input != b->input);
303 
304 #undef NETCONF_DIFF
305 
306  return diff;
307 }
308 
309 static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj)
310 {
311  struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj;
312  struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj;
313  int action = new_obj->ce_msgtype;
314 
315  switch(action) {
316  case RTM_NEWNETCONF:
317  if (new_nc->family != old_nc->family ||
318  new_nc->ifindex != old_nc->ifindex)
319  return -NLE_OPNOTSUPP;
320 
321  if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER)
322  old_nc->rp_filter = new_nc->rp_filter;
323  if (new_nc->ce_mask & NETCONF_ATTR_FWDING)
324  old_nc->forwarding = new_nc->forwarding;
325  if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING)
326  old_nc->mc_forwarding = new_nc->mc_forwarding;
327  if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
328  old_nc->proxy_neigh = new_nc->proxy_neigh;
329  if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)
330  old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown;
331 
332  break;
333  default:
334  return -NLE_OPNOTSUPP;
335  }
336 
337  return NLE_SUCCESS;
338 }
339 
340 /**
341  * @name Cache Management
342  * @{
343  */
344 
345 int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
346 {
347  return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result);
348 }
349 
350 /**
351  * Search netconf in cache
352  * @arg cache netconf cache
353  * @arg family Address family of interest
354  * @arg ifindex Interface index of interest
355  *
356  * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
357  * for given index and family
358  *
359  * The reference counter is incremented before returning the netconf entry,
360  * therefore the reference must be given back with rtnl_netconf_put() after
361  * usage.
362  *
363  * @return netconf object or NULL if no match was found.
364  */
365 struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family,
366  int ifindex)
367 {
368  struct rtnl_netconf *nc;
369 
370  if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops)
371  return NULL;
372 
373  nl_list_for_each_entry(nc, &cache->c_items, ce_list) {
374  if (nc->ifindex == ifindex &&
375  nc->family == family) {
376  nl_object_get((struct nl_object *) nc);
377  return nc;
378  }
379  }
380 
381  return NULL;
382 }
383 
384 void rtnl_netconf_put(struct rtnl_netconf *nc)
385 {
386  nl_object_put((struct nl_object *) nc);
387 }
388 
389 /**
390  * Search netconf in cache
391  * @arg cache netconf cache
392  * @arg family Address family of interest
393  *
394  * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
395  * for "all" netconf settings for given family
396  *
397  * The reference counter is incremented before returning the netconf entry,
398  * therefore the reference must be given back with rtnl_netconf_put() after
399  * usage.
400  *
401  * @return netconf object or NULL if no match was found.
402  */
403 struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family)
404 {
405  return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL);
406 }
407 
408 /**
409  * Search netconf in cache
410  * @arg cache netconf cache
411  * @arg family Address family of interest
412  *
413  * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
414  * for "default" netconf settings for given family
415  *
416  * The reference counter is incremented before returning the netconf entry,
417  * therefore the reference must be given back with rtnl_netconf_put() after
418  * usage.
419  *
420  * @return netconf object or NULL if no match was found.
421  */
422 struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family)
423 {
424  return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT);
425 }
426 
427 /** @} */
428 
429 /**
430  * @name Attributes
431  * @{
432  */
433 
434 int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val)
435 {
436  if (!nc)
437  return -NLE_INVAL;
438  if (!(nc->ce_mask & NETCONF_ATTR_FAMILY))
439  return -NLE_MISSING_ATTR;
440  if (val)
441  *val = nc->family;
442  return 0;
443 }
444 int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val)
445 {
446  if (!nc)
447  return -NLE_INVAL;
448  if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX))
449  return -NLE_MISSING_ATTR;
450  if (val)
451  *val = nc->ifindex;
452  return 0;
453 }
454 int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val)
455 {
456  if (!nc)
457  return -NLE_INVAL;
458  if (!(nc->ce_mask & NETCONF_ATTR_FWDING))
459  return -NLE_MISSING_ATTR;
460  if (val)
461  *val = nc->forwarding;
462  return 0;
463 }
464 int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val)
465 {
466  if (!nc)
467  return -NLE_INVAL;
468  if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING))
469  return -NLE_MISSING_ATTR;
470  if (val)
471  *val = nc->mc_forwarding;
472  return 0;
473 }
474 int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val)
475 {
476  if (!nc)
477  return -NLE_INVAL;
478  if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER))
479  return -NLE_MISSING_ATTR;
480  if (val)
481  *val = nc->rp_filter;
482  return 0;
483 }
484 int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val)
485 {
486  if (!nc)
487  return -NLE_INVAL;
488  if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH))
489  return -NLE_MISSING_ATTR;
490  if (val)
491  *val = nc->proxy_neigh;
492  return 0;
493 }
494 int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val)
495 {
496  if (!nc)
497  return -NLE_INVAL;
498  if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN))
499  return -NLE_MISSING_ATTR;
500  if (val)
501  *val = nc->ignore_routes_linkdown;
502  return 0;
503 }
504 int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val)
505 {
506  if (!nc)
507  return -NLE_INVAL;
508  if (!(nc->ce_mask & NETCONF_ATTR_INPUT))
509  return -NLE_MISSING_ATTR;
510  if (val)
511  *val = nc->input;
512  return 0;
513 }
514 
515 
516 /** @} */
517 
518 static struct nl_object_ops netconf_obj_ops = {
519  .oo_name = "route/netconf",
520  .oo_size = sizeof(struct rtnl_netconf),
521  .oo_dump = {
522  [NL_DUMP_LINE] = netconf_dump_line,
523  [NL_DUMP_DETAILS] = netconf_dump_line,
524  },
525  .oo_compare = netconf_compare,
526  .oo_keygen = netconf_keygen,
527  .oo_update = netconf_update,
528  .oo_attrs2str = netconf_attrs2str,
529  .oo_id_attrs = (NETCONF_ATTR_FAMILY |
530  NETCONF_ATTR_IFINDEX)
531 };
532 
533 static struct nl_af_group netconf_groups[] = {
534  { AF_INET, RTNLGRP_IPV4_NETCONF },
535  { AF_INET6, RTNLGRP_IPV6_NETCONF },
536  { AF_MPLS, RTNLGRP_MPLS_NETCONF },
537  { END_OF_GROUP_LIST },
538 };
539 
540 static struct nl_cache_ops rtnl_netconf_ops = {
541  .co_name = "route/netconf",
542  .co_hdrsize = sizeof(struct netconfmsg),
543  .co_msgtypes = {
544  { RTM_NEWNETCONF, NL_ACT_NEW, "new" },
545  { RTM_DELNETCONF, NL_ACT_DEL, "del" },
546  { RTM_GETNETCONF, NL_ACT_GET, "get" },
547  END_OF_MSGTYPES_LIST,
548  },
549  .co_protocol = NETLINK_ROUTE,
550  .co_groups = netconf_groups,
551  .co_request_update = netconf_request_update,
552  .co_msg_parser = netconf_msg_parser,
553  .co_obj_ops = &netconf_obj_ops,
554 };
555 
556 static void __init netconf_init(void)
557 {
558  nl_cache_mngt_register(&rtnl_netconf_ops);
559 }
560 
561 static void __exit netconf_exit(void)
562 {
563  nl_cache_mngt_unregister(&rtnl_netconf_ops);
564 }
565 
566 /** @} */
int32_t nla_get_s32(const struct nlattr *nla)
Return payload of 32 bit signed integer attribute.
Definition: attr.c:674
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
struct nl_cache * nl_cache_mngt_require_safe(const char *name)
Return cache previously provided via nl_cache_mngt_provide()
Definition: cache_mngt.c:424
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
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
struct rtnl_netconf * rtnl_netconf_get_all(struct nl_cache *cache, int family)
Search netconf in cache.
Definition: netconf.c:403
struct rtnl_netconf * rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, int ifindex)
Search netconf in cache.
Definition: netconf.c:365
struct rtnl_netconf * rtnl_netconf_get_default(struct nl_cache *cache, int family)
Search netconf in cache.
Definition: netconf.c:422
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
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition: object.c:203
int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, size_t size)
Construct and transmit a Netlink message.
Definition: nl.c:574
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:955
@ 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
Attribute validation policy.
Definition: attr.h:63
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:65