• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/rtnl.c

  1 /*
  2 Copyright 2021 Jo-Philipp Wich <jo@mein.io>
  3 
  4 Licensed under the Apache License, Version 2.0 (the "License");
  5 you may not use this file except in compliance with the License.
  6 You may obtain a copy of the License at
  7 
  8         http://www.apache.org/licenses/LICENSE-2.0
  9 
 10 Unless required by applicable law or agreed to in writing, software
 11 distributed under the License is distributed on an "AS IS" BASIS,
 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 See the License for the specific language governing permissions and
 14 limitations under the License.
 15 */
 16 
 17 /**
 18  * # Routing Netlink
 19  *
 20  * The `rtnl` module provides functions for interacting with the routing netlink interface.
 21  *
 22  * Functions can be individually imported and directly accessed using the
 23  * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import}
 24  * syntax:
 25  *
 26  *   ```javascript
 27  *   import { error, request, listener, RTM_GETROUTE, RTM_NEWROUTE, RTM_DELROUTE, AF_INET } from 'rtnl';
 28  *
 29  *   // Send a netlink request
 30  *   let response = request(RTM_GETROUTE, 0, { family: AF_INET });
 31  *
 32  *   // Create a listener for route changes
 33  *   let routeListener = listener((msg) => {
 34  *       print('Received route message:', msg, '\n');
 35  *   }, [RTM_NEWROUTE, RTM_DELROUTE]);
 36  *   ```
 37  *
 38  * Alternatively, the module namespace can be imported
 39  * using a wildcard import statement:
 40  *
 41  *   ```javascript
 42  *   import * as rtnl from 'rtnl';
 43  *
 44  *   // Send a netlink request
 45  *   let response = rtnl.request(rtnl.RTM_GETROUTE, 0, { family: rtnl.AF_INET });
 46  *
 47  *   // Create a listener for route changes
 48  *   let listener = rtnl.listener((msg) => {
 49  *       print('Received route message:', msg, '\n');
 50  *   }, [rtnl.RTM_NEWROUTE, rtnl.RTM_DELROUTE]);
 51  *   ```
 52  *
 53  * Additionally, the rtnl module namespace may also be imported by invoking
 54  * the `ucode` interpreter with the `-lrtnl` switch.
 55  *
 56  * @module rtnl
 57  */
 58 
 59 #include <stdio.h>
 60 #include <stdint.h>
 61 #include <stdbool.h>
 62 #include <stdarg.h>
 63 #include <unistd.h>
 64 #include <errno.h>
 65 #include <string.h>
 66 #include <limits.h>
 67 #include <math.h>
 68 #include <assert.h>
 69 
 70 #include <netinet/ether.h>
 71 #include <arpa/inet.h>
 72 #include <netlink/msg.h>
 73 #include <netlink/attr.h>
 74 #include <netlink/socket.h>
 75 
 76 #include <linux/rtnetlink.h>
 77 #include <linux/if_tunnel.h>
 78 #include <linux/ip6_tunnel.h>
 79 #include <linux/lwtunnel.h>
 80 #include <linux/mpls.h>
 81 #include <linux/mpls_iptunnel.h>
 82 #include <linux/seg6.h>
 83 #include <linux/seg6_iptunnel.h>
 84 #include <linux/seg6_hmac.h>
 85 #include <linux/veth.h>
 86 #include <linux/ila.h>
 87 #include <linux/fib_rules.h>
 88 #include <linux/if_addrlabel.h>
 89 #include <linux/if_bridge.h>
 90 #include <linux/netconf.h>
 91 #include <linux/ipv6.h>
 92 #include <linux/can/netlink.h>
 93 #include <linux/can/vxcan.h>
 94 
 95 #include <libubox/uloop.h>
 96 
 97 #include "ucode/module.h"
 98 #include "ucode/platform.h"
 99 
100 #define DIV_ROUND_UP(n, d)      (((n) + (d) - 1) / (d))
101 
102 #define err_return(code, ...) do { set_error(code, __VA_ARGS__); return NULL; } while(0)
103 
104 #define NLM_F_STRICT_CHK (1 << 15)
105 
106 #define RTNL_CMDS_BITMAP_SIZE   DIV_ROUND_UP(__RTM_MAX, 32)
107 #define RTNL_GRPS_BITMAP_SIZE   DIV_ROUND_UP(__RTNLGRP_MAX, 32)
108 
109 /* Can't use net/if.h for declarations as it clashes with linux/if.h
110  * on certain musl versions.
111  * Ref: https://www.openwall.com/lists/musl/2017/04/16/1 */
112 extern unsigned int if_nametoindex (const char *);
113 extern char *if_indextoname (unsigned int ifindex, char *ifname);
114 
115 static struct {
116         int code;
117         char *msg;
118 } last_error;
119 
120 __attribute__((format(printf, 2, 3))) static void
121 set_error(int errcode, const char *fmt, ...) {
122         va_list ap;
123 
124         free(last_error.msg);
125 
126         last_error.code = errcode;
127         last_error.msg = NULL;
128 
129         if (fmt) {
130                 va_start(ap, fmt);
131                 xvasprintf(&last_error.msg, fmt, ap);
132                 va_end(ap);
133         }
134 }
135 
136 static uc_resource_type_t *listener_type;
137 static uc_value_t *listener_registry;
138 static uc_vm_t *listener_vm;
139 
140 typedef struct {
141         uint32_t cmds[RTNL_CMDS_BITMAP_SIZE];
142         size_t index;
143 } uc_nl_listener_t;
144 
145 typedef struct {
146         uint8_t family;
147         uint8_t mask;
148         uint8_t alen;
149         uint8_t bitlen;
150         union {
151                 struct in_addr in;
152                 struct in6_addr in6;
153                 struct mpls_label mpls[16];
154         } addr;
155 } uc_nl_cidr_t;
156 
157 static bool
158 uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
159 {
160         uint64_t u;
161 
162         u = ucv_to_unsigned(val);
163 
164         if (errno != 0 || u > UINT32_MAX)
165                 return false;
166 
167         *n = (uint32_t)u;
168 
169         return true;
170 }
171 
172 static bool
173 uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
174 {
175         int64_t i;
176 
177         i = ucv_to_integer(val);
178 
179         if (errno != 0 || i < INT32_MIN || i > INT32_MAX)
180                 return false;
181 
182         *n = (uint32_t)i;
183 
184         return true;
185 }
186 
187 static bool
188 uc_nl_parse_u64(uc_value_t *val, uint64_t *n)
189 {
190         *n = ucv_to_unsigned(val);
191 
192         return (errno == 0);
193 }
194 
195 static const char *
196 addr64_ntop(const void *addr, char *buf, size_t buflen)
197 {
198         const union { uint64_t u64; uint16_t u16[4]; } *a64 = addr;
199         int len;
200 
201         errno = 0;
202 
203         len = snprintf(buf, buflen, "%04x:%04x:%04x:%04x",
204                        ntohs(a64->u16[0]), ntohs(a64->u16[1]),
205                        ntohs(a64->u16[2]), ntohs(a64->u16[3]));
206 
207         if ((size_t)len >= buflen) {
208                 errno = ENOSPC;
209 
210                 return NULL;
211         }
212 
213         return buf;
214 }
215 
216 static int
217 addr64_pton(const char *src, void *dst)
218 {
219         union { uint64_t u64; uint16_t u16[4]; } *a64 = dst;
220         unsigned long n;
221         size_t i;
222         char *e;
223 
224         for (i = 0; i < ARRAY_SIZE(a64->u16); i++) {
225                 n = strtoul(src, &e, 16);
226 
227                 if (e == src || n > 0xffff)
228                         return -1;
229 
230                 a64->u16[i] = htons(n);
231 
232                 if (*e == 0)
233                         break;
234 
235                 if (i >= 3 || *e != ':')
236                         return -1;
237 
238                 src += (e - src) + 1;
239         }
240 
241         return 0;
242 }
243 
244 static const char *
245 mpls_ntop(const void *addr, size_t addrlen, char *buf, size_t buflen)
246 {
247         const struct mpls_label *p = addr;
248         size_t remlen = buflen;
249         uint32_t entry, label;
250         char *s = buf;
251         int len;
252 
253         errno = 0;
254 
255         while (addrlen >= sizeof(*p)) {
256                 entry = ntohl(p->entry);
257                 label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT;
258 
259                 len = snprintf(s, remlen, "%u", label);
260 
261                 if ((size_t)len >= remlen)
262                         break;
263 
264                 if (entry & MPLS_LS_S_MASK)
265                         return buf;
266 
267                 s += len;
268                 remlen -= len;
269 
270                 if (remlen) {
271                         *s++ = '/';
272                         remlen--;
273                 }
274 
275                 p++;
276 
277                 addrlen -= sizeof(*p);
278         }
279 
280         errno = ENOSPC;
281 
282         return NULL;
283 }
284 
285 static int
286 mpls_pton(int af, const char *src, void *dst, size_t dstlen)
287 {
288         size_t max = dstlen / sizeof(struct mpls_label);
289         struct mpls_label *p = dst;
290         uint32_t label;
291         char *e;
292 
293         errno = 0;
294 
295         if (af != AF_MPLS) {
296                 errno = EAFNOSUPPORT;
297 
298                 return -1;
299         }
300 
301         while (max > 0) {
302                 label = strtoul(src, &e, 0);
303 
304                 if (label >= (1 << 20))
305                         return 0;
306 
307                 if (e == src)
308                         return 0;
309 
310                 p->entry = htonl(label << MPLS_LS_LABEL_SHIFT);
311 
312                 if (*e == 0) {
313                         p->entry |= htonl(1 << MPLS_LS_S_SHIFT);
314 
315                         return 1;
316                 }
317 
318                 if (*e != '/')
319                         return 0;
320 
321                 src += (e - src) + 1;
322                 max--;
323                 p++;
324         }
325 
326         errno = ENOSPC;
327 
328         return -1;
329 }
330 
331 static bool
332 uc_nl_parse_cidr(uc_vm_t *vm, uc_value_t *val, uc_nl_cidr_t *p)
333 {
334         char *s = ucv_to_string(vm, val);
335         struct in6_addr mask6 = { 0 };
336         struct in_addr mask = { 0 };
337         bool valid = true;
338         char *m, *e;
339         long n = 0;
340         size_t i;
341 
342         if (!s)
343                 return false;
344 
345         m = strchr(s, '/');
346 
347         if (m)
348                 *m++ = '\0';
349 
350         if (inet_pton(AF_INET6, s, &p->addr.in6) == 1) {
351                 if (m) {
352                         if (inet_pton(AF_INET6, m, &mask6) == 1) {
353                                 while (n < 128 && (mask6.s6_addr[n / 8] << (n % 8)) & 128)
354                                         n++;
355                         }
356                         else {
357                                 n = strtol(m, &e, 10);
358 
359                                 if (e == m || *e || n < 0 || n > 128)
360                                         valid = false;
361                         }
362 
363                         p->mask = (uint8_t)n;
364                 }
365                 else {
366                         p->mask = 128;
367                 }
368 
369                 p->family = AF_INET6;
370                 p->alen = sizeof(mask6);
371                 p->bitlen = p->alen * 8;
372         }
373         else if (strchr(s, '.') && inet_pton(AF_INET, s, &p->addr.in) == 1) {
374                 if (m) {
375                         if (inet_pton(AF_INET, m, &mask) == 1) {
376                                 mask.s_addr = ntohl(mask.s_addr);
377 
378                                 while (n < 32 && (mask.s_addr << n) & 0x80000000)
379                                         n++;
380                         }
381                         else {
382                                 n = strtol(m, &e, 10);
383 
384                                 if (e == m || *e || n < 0 || n > 32)
385                                         valid = false;
386                         }
387 
388                         p->mask = (uint8_t)n;
389                 }
390                 else {
391                         p->mask = 32;
392                 }
393 
394                 p->family = AF_INET;
395                 p->alen = sizeof(mask);
396                 p->bitlen = p->alen * 8;
397         }
398         else {
399                 if (m)
400                         m[-1] = '/';
401 
402                 if (mpls_pton(AF_MPLS, s, &p->addr.mpls, sizeof(p->addr.mpls)) == 1) {
403                         p->family = AF_MPLS;
404                         p->alen = 0;
405 
406                         for (i = 0; i < ARRAY_SIZE(p->addr.mpls); i++) {
407                                 p->alen += sizeof(struct mpls_label);
408 
409                                 if (ntohl(p->addr.mpls[i].entry) & MPLS_LS_S_MASK)
410                                         break;
411                         }
412 
413                         p->bitlen = p->alen * 8;
414                         p->mask = p->bitlen;
415                 }
416                 else {
417                         valid = false;
418                 }
419         }
420 
421         free(s);
422 
423         return valid;
424 }
425 
426 typedef enum {
427         DT_FLAG,
428         DT_BOOL,
429         DT_U8,
430         DT_U16,
431         DT_U32,
432         DT_S32,
433         DT_U64,
434         DT_STRING,
435         DT_NETDEV,
436         DT_LLADDR,
437         DT_INADDR,
438         DT_IN6ADDR,
439         DT_U64ADDR,
440         DT_MPLSADDR,
441         DT_ANYADDR,
442         DT_BRIDGEID,
443         DT_LINKINFO,
444         DT_MULTIPATH,
445         DT_NUMRANGE,
446         DT_AFSPEC,
447         DT_FLAGS,
448         DT_ENCAP,
449         DT_SRH,
450         DT_IPOPTS,
451         DT_U32_OR_MEMBER,
452         DT_NESTED,
453 } uc_nl_attr_datatype_t;
454 
455 enum {
456         DF_NO_SET = (1 << 0),
457         DF_NO_GET = (1 << 1),
458         DF_ALLOW_NONE = (1 << 2),
459         DF_BYTESWAP = (1 << 3),
460         DF_MAX_1 = (1 << 4),
461         DF_MAX_255 = (1 << 5),
462         DF_MAX_65535 = (1 << 6),
463         DF_MAX_16777215 = (1 << 7),
464         DF_STORE_MASK = (1 << 8),
465         DF_MULTIPLE = (1 << 9),
466         DF_FLAT = (1 << 10),
467         DF_FAMILY_HINT = (1 << 11),
468 };
469 
470 typedef struct uc_nl_attr_spec {
471         size_t attr;
472         const char *key;
473         uc_nl_attr_datatype_t type;
474         uint32_t flags;
475         const void *auxdata;
476 } uc_nl_attr_spec_t;
477 
478 typedef struct uc_nl_nested_spec {
479         size_t headsize;
480         size_t nattrs;
481         const uc_nl_attr_spec_t attrs[];
482 } uc_nl_nested_spec_t;
483 
484 #define SIZE(type) (void *)(uintptr_t)sizeof(struct type)
485 #define MEMBER(type, field) (void *)(uintptr_t)offsetof(struct type, field)
486 
487 static const uc_nl_nested_spec_t route_cacheinfo_rta = {
488         .headsize = NLA_ALIGN(sizeof(struct rta_cacheinfo)),
489         .nattrs = 8,
490         .attrs = {
491                 { RTA_UNSPEC, "clntref", DT_U32, 0, MEMBER(rta_cacheinfo, rta_clntref) },
492                 { RTA_UNSPEC, "lastuse", DT_U32, 0, MEMBER(rta_cacheinfo, rta_lastuse) },
493                 { RTA_UNSPEC, "expires", DT_S32, 0, MEMBER(rta_cacheinfo, rta_expires) },
494                 { RTA_UNSPEC, "error", DT_U32, 0, MEMBER(rta_cacheinfo, rta_error) },
495                 { RTA_UNSPEC, "used", DT_U32, 0, MEMBER(rta_cacheinfo, rta_used) },
496                 { RTA_UNSPEC, "id", DT_U32, 0, MEMBER(rta_cacheinfo, rta_id) },
497                 { RTA_UNSPEC, "ts", DT_U32, 0, MEMBER(rta_cacheinfo, rta_ts) },
498                 { RTA_UNSPEC, "tsage", DT_U32, 0, MEMBER(rta_cacheinfo, rta_tsage) },
499         }
500 };
501 
502 static const uc_nl_nested_spec_t route_metrics_rta = {
503         .headsize = 0,
504         .nattrs = 16,
505         .attrs = {
506                 { RTAX_MTU, "mtu", DT_U32, 0, NULL },
507                 { RTAX_HOPLIMIT, "hoplimit", DT_U32, DF_MAX_255, NULL },
508                 { RTAX_ADVMSS, "advmss", DT_U32, 0, NULL },
509                 { RTAX_REORDERING, "reordering", DT_U32, 0, NULL },
510                 { RTAX_RTT, "rtt", DT_U32, 0, NULL },
511                 { RTAX_WINDOW, "window", DT_U32, 0, NULL },
512                 { RTAX_CWND, "cwnd", DT_U32, 0, NULL },
513                 { RTAX_INITCWND, "initcwnd", DT_U32, 0, NULL },
514                 { RTAX_INITRWND, "initrwnd", DT_U32, 0, NULL },
515                 { RTAX_FEATURES, "ecn", DT_U32, DF_MAX_1, NULL },
516                 { RTAX_QUICKACK, "quickack", DT_U32, DF_MAX_1, NULL },
517                 { RTAX_CC_ALGO, "cc_algo", DT_STRING, 0, NULL },
518                 { RTAX_RTTVAR, "rttvar", DT_U32, 0, NULL },
519                 { RTAX_SSTHRESH, "ssthresh", DT_U32, 0, NULL },
520                 { RTAX_FASTOPEN_NO_COOKIE, "fastopen_no_cookie", DT_U32, DF_MAX_1, NULL },
521                 { RTAX_LOCK, "lock", DT_U32, 0, NULL },
522         }
523 };
524 
525 static const uc_nl_nested_spec_t route_msg = {
526         .headsize = NLA_ALIGN(sizeof(struct rtmsg)),
527         .nattrs = 28,
528         .attrs = {
529                 { RTA_UNSPEC, "family", DT_U8, 0, MEMBER(rtmsg, rtm_family) },
530                 { RTA_UNSPEC, "tos", DT_U8, 0, MEMBER(rtmsg, rtm_tos) },
531                 { RTA_UNSPEC, "protocol", DT_U8, 0, MEMBER(rtmsg, rtm_protocol) },
532                 { RTA_UNSPEC, "scope", DT_U8, 0, MEMBER(rtmsg, rtm_scope) },
533                 { RTA_UNSPEC, "type", DT_U8, 0, MEMBER(rtmsg, rtm_type) },
534                 { RTA_UNSPEC, "flags", DT_U32, 0, MEMBER(rtmsg, rtm_flags) },
535                 { RTA_SRC, "src", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(rtmsg, rtm_src_len) },
536                 { RTA_DST, "dst", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(rtmsg, rtm_dst_len) },
537                 { RTA_IIF, "iif", DT_NETDEV, 0, NULL },
538                 { RTA_OIF, "oif", DT_NETDEV, 0, NULL },
539                 { RTA_GATEWAY, "gateway", DT_ANYADDR, DF_FAMILY_HINT, NULL },
540                 { RTA_PRIORITY, "priority", DT_U32, 0, NULL },
541                 { RTA_PREFSRC, "prefsrc", DT_ANYADDR, DF_FAMILY_HINT, NULL },
542                 { RTA_METRICS, "metrics", DT_NESTED, 0, &route_metrics_rta },
543                 { RTA_MULTIPATH, "multipath", DT_MULTIPATH, 0, NULL },
544                 { RTA_FLOW, "flow", DT_U32, 0, NULL },
545                 { RTA_CACHEINFO, "cacheinfo", DT_NESTED, DF_NO_SET, &route_cacheinfo_rta },
546                 { RTA_TABLE, "table", DT_U32_OR_MEMBER, DF_MAX_255, MEMBER(rtmsg, rtm_table) },
547                 { RTA_MARK, "mark", DT_U32, 0, NULL },
548                 //RTA_MFC_STATS,
549                 { RTA_PREF, "pref", DT_U8, 0, NULL },
550                 { RTA_ENCAP, "encap", DT_ENCAP, 0, NULL },
551                 { RTA_EXPIRES, "expires", DT_U32, 0, NULL },
552                 { RTA_UID, "uid", DT_U32, 0, NULL },
553                 { RTA_TTL_PROPAGATE, "ttl_propagate", DT_BOOL, 0, NULL },
554                 { RTA_IP_PROTO, "ip_proto", DT_U8, 0, NULL },
555                 { RTA_SPORT, "sport", DT_U16, DF_BYTESWAP, NULL },
556                 { RTA_DPORT, "dport", DT_U16, DF_BYTESWAP, NULL },
557                 { RTA_NH_ID, "nh_id", DT_U32, 0, NULL },
558         }
559 };
560 
561 static const uc_nl_attr_spec_t route_encap_mpls_attrs[] = {
562         { MPLS_IPTUNNEL_DST, "dst", DT_MPLSADDR, 0, NULL },
563         { MPLS_IPTUNNEL_TTL, "ttl", DT_U8, 0, NULL },
564 };
565 
566 static const uc_nl_attr_spec_t route_encap_ip_attrs[] = {
567         { LWTUNNEL_IP_ID, "id", DT_U64, DF_BYTESWAP, NULL },
568         { LWTUNNEL_IP_DST, "dst", DT_INADDR, 0, NULL },
569         { LWTUNNEL_IP_SRC, "src", DT_INADDR, 0, NULL },
570         { LWTUNNEL_IP_TOS, "tos", DT_U8, 0, NULL },
571         { LWTUNNEL_IP_TTL, "ttl", DT_U8, 0, NULL },
572         { LWTUNNEL_IP_OPTS, "opts", DT_IPOPTS, 0, NULL },
573         { LWTUNNEL_IP_FLAGS, "flags", DT_U16, 0, NULL },
574 };
575 
576 static const uc_nl_attr_spec_t route_encap_ila_attrs[] = {
577         { ILA_ATTR_LOCATOR, "locator", DT_U64ADDR, 0, NULL },
578         { ILA_ATTR_CSUM_MODE, "csum_mode", DT_U8, 0, NULL },
579         { ILA_ATTR_IDENT_TYPE, "ident_type", DT_U8, 0, NULL },
580         { ILA_ATTR_HOOK_TYPE, "hook_type", DT_U8, 0, NULL },
581 };
582 
583 static const uc_nl_attr_spec_t route_encap_ip6_attrs[] = {
584         { LWTUNNEL_IP6_ID, "id", DT_U64, DF_BYTESWAP, NULL },
585         { LWTUNNEL_IP6_DST, "dst", DT_IN6ADDR, 0, NULL },
586         { LWTUNNEL_IP6_SRC, "src", DT_IN6ADDR, 0, NULL },
587         { LWTUNNEL_IP6_TC, "tc", DT_U32, 0, NULL },
588         { LWTUNNEL_IP6_HOPLIMIT, "hoplimit", DT_U8, 0, NULL },
589         { LWTUNNEL_IP6_OPTS, "opts", DT_IPOPTS, 0, NULL },
590         { LWTUNNEL_IP6_FLAGS, "flags", DT_U16, 0, NULL },
591 };
592 
593 static const uc_nl_attr_spec_t route_encap_seg6_attrs[] = {
594         { SEG6_IPTUNNEL_SRH, "srh", DT_SRH, 0, NULL },
595 };
596 
597 #define IPV4_DEVCONF_ENTRY(name) ((void *)((IPV4_DEVCONF_##name - 1) * sizeof(uint32_t)))
598 
599 static const uc_nl_nested_spec_t link_attrs_af_spec_inet_devconf_rta = {
600         .headsize = NLA_ALIGN(IPV4_DEVCONF_MAX * sizeof(uint32_t)),
601         .nattrs = 32,
602         .attrs = {
603                 { 0, "forwarding", DT_U32, 0, IPV4_DEVCONF_ENTRY(FORWARDING) },
604                 { 0, "mc_forwarding", DT_U32, 0, IPV4_DEVCONF_ENTRY(MC_FORWARDING) },
605                 { 0, "proxy_arp", DT_U32, 0, IPV4_DEVCONF_ENTRY(PROXY_ARP) },
606                 { 0, "accept_redirects", DT_U32, 0, IPV4_DEVCONF_ENTRY(ACCEPT_REDIRECTS) },
607                 { 0, "secure_redirects", DT_U32, 0, IPV4_DEVCONF_ENTRY(SECURE_REDIRECTS) },
608                 { 0, "send_redirects", DT_U32, 0, IPV4_DEVCONF_ENTRY(SEND_REDIRECTS) },
609                 { 0, "shared_media", DT_U32, 0, IPV4_DEVCONF_ENTRY(SHARED_MEDIA) },
610                 { 0, "rp_filter", DT_U32, 0, IPV4_DEVCONF_ENTRY(RP_FILTER) },
611                 { 0, "accept_source_route", DT_U32, 0, IPV4_DEVCONF_ENTRY(ACCEPT_SOURCE_ROUTE) },
612                 { 0, "bootp_relay", DT_U32, 0, IPV4_DEVCONF_ENTRY(BOOTP_RELAY) },
613                 { 0, "log_martians", DT_U32, 0, IPV4_DEVCONF_ENTRY(LOG_MARTIANS) },
614                 { 0, "tag", DT_U32, 0, IPV4_DEVCONF_ENTRY(TAG) },
615                 { 0, "arpfilter", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARPFILTER) },
616                 { 0, "medium_id", DT_U32, 0, IPV4_DEVCONF_ENTRY(MEDIUM_ID) },
617                 { 0, "noxfrm", DT_U32, 0, IPV4_DEVCONF_ENTRY(NOXFRM) },
618                 { 0, "nopolicy", DT_U32, 0, IPV4_DEVCONF_ENTRY(NOPOLICY) },
619                 { 0, "force_igmp_version", DT_U32, 0, IPV4_DEVCONF_ENTRY(FORCE_IGMP_VERSION) },
620                 { 0, "arp_announce", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_ANNOUNCE) },
621                 { 0, "arp_ignore", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_IGNORE) },
622                 { 0, "promote_secondaries", DT_U32, 0, IPV4_DEVCONF_ENTRY(PROMOTE_SECONDARIES) },
623                 { 0, "arp_accept", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_ACCEPT) },
624                 { 0, "arp_notify", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_NOTIFY) },
625                 { 0, "accept_local", DT_U32, 0, IPV4_DEVCONF_ENTRY(ACCEPT_LOCAL) },
626                 { 0, "src_vmark", DT_U32, 0, IPV4_DEVCONF_ENTRY(SRC_VMARK) },
627                 { 0, "proxy_arp_pvlan", DT_U32, 0, IPV4_DEVCONF_ENTRY(PROXY_ARP_PVLAN) },
628                 { 0, "route_localnet", DT_U32, 0, IPV4_DEVCONF_ENTRY(ROUTE_LOCALNET) },
629                 { 0, "igmpv2_unsolicited_report_interval", DT_U32, 0, IPV4_DEVCONF_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL) },
630                 { 0, "igmpv3_unsolicited_report_interval", DT_U32, 0, IPV4_DEVCONF_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL) },
631                 { 0, "ignore_routes_with_linkdown", DT_U32, 0, IPV4_DEVCONF_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN) },
632                 { 0, "drop_unicast_in_l2_multicast", DT_U32, 0, IPV4_DEVCONF_ENTRY(DROP_UNICAST_IN_L2_MULTICAST) },
633                 { 0, "drop_gratuitous_arp", DT_U32, 0, IPV4_DEVCONF_ENTRY(DROP_GRATUITOUS_ARP) },
634                 { 0, "bc_forwarding", DT_U32, 0, IPV4_DEVCONF_ENTRY(BC_FORWARDING) },
635         }
636 };
637 
638 static const uc_nl_nested_spec_t link_attrs_af_spec_inet_rta = {
639         .headsize = 0,
640         .nattrs = 1,
641         .attrs = {
642                 { IFLA_INET_CONF, "conf", DT_NESTED, 0, &link_attrs_af_spec_inet_devconf_rta },
643         }
644 };
645 
646 #define IPV6_DEVCONF_ENTRY(name) ((void *)(DEVCONF_##name * sizeof(uint32_t)))
647 
648 static const uc_nl_nested_spec_t link_attrs_af_spec_inet6_devconf_rta = {
649         .headsize = NLA_ALIGN(DEVCONF_MAX * sizeof(uint32_t)),
650         .nattrs = 53,
651         .attrs = {
652                 { 0, "forwarding", DT_S32, 0, IPV6_DEVCONF_ENTRY(FORWARDING) },
653                 { 0, "hoplimit", DT_S32, 0, IPV6_DEVCONF_ENTRY(HOPLIMIT) },
654                 { 0, "mtu6", DT_S32, 0, IPV6_DEVCONF_ENTRY(MTU6) },
655                 { 0, "accept_ra", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA) },
656                 { 0, "accept_redirects", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_REDIRECTS) },
657                 { 0, "autoconf", DT_S32, 0, IPV6_DEVCONF_ENTRY(AUTOCONF) },
658                 { 0, "dad_transmits", DT_S32, 0, IPV6_DEVCONF_ENTRY(DAD_TRANSMITS) },
659                 { 0, "rtr_solicits", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICITS) },
660                 { 0, "rtr_solicit_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICIT_INTERVAL) },
661                 { 0, "rtr_solicit_delay", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICIT_DELAY) },
662                 { 0, "use_tempaddr", DT_S32, 0, IPV6_DEVCONF_ENTRY(USE_TEMPADDR) },
663                 { 0, "temp_valid_lft", DT_S32, 0, IPV6_DEVCONF_ENTRY(TEMP_VALID_LFT) },
664                 { 0, "temp_prefered_lft", DT_S32, 0, IPV6_DEVCONF_ENTRY(TEMP_PREFERED_LFT) },
665                 { 0, "regen_max_retry", DT_S32, 0, IPV6_DEVCONF_ENTRY(REGEN_MAX_RETRY) },
666                 { 0, "max_desync_factor", DT_S32, 0, IPV6_DEVCONF_ENTRY(MAX_DESYNC_FACTOR) },
667                 { 0, "max_addresses", DT_S32, 0, IPV6_DEVCONF_ENTRY(MAX_ADDRESSES) },
668                 { 0, "force_mld_version", DT_S32, 0, IPV6_DEVCONF_ENTRY(FORCE_MLD_VERSION) },
669                 { 0, "accept_ra_defrtr", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_DEFRTR) },
670                 { 0, "accept_ra_pinfo", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_PINFO) },
671                 { 0, "accept_ra_rtr_pref", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_RTR_PREF) },
672                 { 0, "rtr_probe_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_PROBE_INTERVAL) },
673                 { 0, "accept_ra_rt_info_max_plen", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_RT_INFO_MAX_PLEN) },
674                 { 0, "proxy_ndp", DT_S32, 0, IPV6_DEVCONF_ENTRY(PROXY_NDP) },
675                 { 0, "optimistic_dad", DT_S32, 0, IPV6_DEVCONF_ENTRY(OPTIMISTIC_DAD) },
676                 { 0, "accept_source_route", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_SOURCE_ROUTE) },
677                 { 0, "mc_forwarding", DT_S32, 0, IPV6_DEVCONF_ENTRY(MC_FORWARDING) },
678                 { 0, "disable_ipv6", DT_S32, 0, IPV6_DEVCONF_ENTRY(DISABLE_IPV6) },
679                 { 0, "accept_dad", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_DAD) },
680                 { 0, "force_tllao", DT_S32, 0, IPV6_DEVCONF_ENTRY(FORCE_TLLAO) },
681                 { 0, "ndisc_notify", DT_S32, 0, IPV6_DEVCONF_ENTRY(NDISC_NOTIFY) },
682                 { 0, "mldv1_unsolicited_report_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(MLDV1_UNSOLICITED_REPORT_INTERVAL) },
683                 { 0, "mldv2_unsolicited_report_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(MLDV2_UNSOLICITED_REPORT_INTERVAL) },
684                 { 0, "suppress_frag_ndisc", DT_S32, 0, IPV6_DEVCONF_ENTRY(SUPPRESS_FRAG_NDISC) },
685                 { 0, "accept_ra_from_local", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_FROM_LOCAL) },
686                 { 0, "use_optimistic", DT_S32, 0, IPV6_DEVCONF_ENTRY(USE_OPTIMISTIC) },
687                 { 0, "accept_ra_mtu", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_MTU) },
688                 { 0, "stable_secret", DT_S32, 0, IPV6_DEVCONF_ENTRY(STABLE_SECRET) },
689                 { 0, "use_oif_addrs_only", DT_S32, 0, IPV6_DEVCONF_ENTRY(USE_OIF_ADDRS_ONLY) },
690                 { 0, "accept_ra_min_hop_limit", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_MIN_HOP_LIMIT) },
691                 { 0, "ignore_routes_with_linkdown", DT_S32, 0, IPV6_DEVCONF_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN) },
692                 { 0, "drop_unicast_in_l2_multicast", DT_S32, 0, IPV6_DEVCONF_ENTRY(DROP_UNICAST_IN_L2_MULTICAST) },
693                 { 0, "drop_unsolicited_na", DT_S32, 0, IPV6_DEVCONF_ENTRY(DROP_UNSOLICITED_NA) },
694                 { 0, "keep_addr_on_down", DT_S32, 0, IPV6_DEVCONF_ENTRY(KEEP_ADDR_ON_DOWN) },
695                 { 0, "rtr_solicit_max_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICIT_MAX_INTERVAL) },
696                 { 0, "seg6_enabled", DT_S32, 0, IPV6_DEVCONF_ENTRY(SEG6_ENABLED) },
697                 { 0, "seg6_require_hmac", DT_S32, 0, IPV6_DEVCONF_ENTRY(SEG6_REQUIRE_HMAC) },
698                 { 0, "enhanced_dad", DT_S32, 0, IPV6_DEVCONF_ENTRY(ENHANCED_DAD) },
699                 { 0, "addr_gen_mode", DT_S32, 0, IPV6_DEVCONF_ENTRY(ADDR_GEN_MODE) },
700                 { 0, "disable_policy", DT_S32, 0, IPV6_DEVCONF_ENTRY(DISABLE_POLICY) },
701                 { 0, "accept_ra_rt_info_min_plen", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_RT_INFO_MIN_PLEN) },
702                 { 0, "ndisc_tclass", DT_S32, 0, IPV6_DEVCONF_ENTRY(NDISC_TCLASS) },
703                 { 0, "rpl_seg_enabled", DT_S32, 0, IPV6_DEVCONF_ENTRY(RPL_SEG_ENABLED) },
704                 { 0, "ra_defrtr_metric", DT_S32, 0, IPV6_DEVCONF_ENTRY(RA_DEFRTR_METRIC) },
705         }
706 };
707 
708 static const uc_nl_nested_spec_t link_attrs_af_spec_inet6_rta = {
709         .headsize = 0,
710         .nattrs = 3,
711         .attrs = {
712                 { IFLA_INET6_ADDR_GEN_MODE, "mode", DT_U8, 0, NULL },
713                 { IFLA_INET6_FLAGS, "flags", DT_U32, DF_NO_SET, NULL },
714                 { IFLA_INET6_CONF, "conf", DT_NESTED, DF_NO_SET, &link_attrs_af_spec_inet6_devconf_rta },
715         }
716 };
717 
718 static const uc_nl_nested_spec_t link_attrs_af_spec_rta = {
719         .headsize = 0,
720         .nattrs = 2,
721         .attrs = {
722                 { AF_INET, "inet", DT_NESTED, DF_NO_SET, &link_attrs_af_spec_inet_rta },
723                 { AF_INET6, "inet6", DT_NESTED, 0, &link_attrs_af_spec_inet6_rta },
724         }
725 };
726 
727 static const uc_nl_nested_spec_t link_attrs_stats64_rta = {
728         .headsize = NLA_ALIGN(sizeof(struct rtnl_link_stats64)),
729         .nattrs = 24,
730         .attrs = {
731                 { IFLA_UNSPEC, "rx_packets", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_packets) },
732                 { IFLA_UNSPEC, "tx_packets", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_packets) },
733                 { IFLA_UNSPEC, "rx_bytes", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_bytes) },
734                 { IFLA_UNSPEC, "tx_bytes", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_bytes) },
735                 { IFLA_UNSPEC, "rx_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_errors) },
736                 { IFLA_UNSPEC, "tx_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_errors) },
737                 { IFLA_UNSPEC, "rx_dropped", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_dropped) },
738                 { IFLA_UNSPEC, "tx_dropped", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_dropped) },
739                 { IFLA_UNSPEC, "multicast", DT_U64, 0, MEMBER(rtnl_link_stats64, multicast) },
740                 { IFLA_UNSPEC, "collisions", DT_U64, 0, MEMBER(rtnl_link_stats64, collisions) },
741                 { IFLA_UNSPEC, "rx_length_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_length_errors) },
742                 { IFLA_UNSPEC, "rx_over_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_over_errors) },
743                 { IFLA_UNSPEC, "rx_crc_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_crc_errors) },
744                 { IFLA_UNSPEC, "rx_frame_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_frame_errors) },
745                 { IFLA_UNSPEC, "rx_fifo_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_fifo_errors) },
746                 { IFLA_UNSPEC, "rx_missed_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_missed_errors) },
747                 { IFLA_UNSPEC, "tx_aborted_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_aborted_errors) },
748                 { IFLA_UNSPEC, "tx_carrier_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_carrier_errors) },
749                 { IFLA_UNSPEC, "tx_fifo_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_fifo_errors) },
750                 { IFLA_UNSPEC, "tx_heartbeat_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_heartbeat_errors) },
751                 { IFLA_UNSPEC, "tx_window_errors", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_window_errors) },
752                 { IFLA_UNSPEC, "rx_compressed", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_compressed) },
753                 { IFLA_UNSPEC, "tx_compressed", DT_U64, 0, MEMBER(rtnl_link_stats64, tx_compressed) },
754                 { IFLA_UNSPEC, "rx_nohandler", DT_U64, 0, MEMBER(rtnl_link_stats64, rx_nohandler) },
755         }
756 };
757 
758 static const uc_nl_nested_spec_t link_msg = {
759         .headsize = NLA_ALIGN(sizeof(struct ifinfomsg)),
760         .nattrs = 26,
761         .attrs = {
762                 { IFLA_UNSPEC, "family", DT_U8, 0, MEMBER(ifinfomsg, ifi_family) },
763                 { IFLA_UNSPEC, "type", DT_U16, 0, MEMBER(ifinfomsg, ifi_type) },
764                 { IFLA_UNSPEC, "dev", DT_NETDEV, 0, MEMBER(ifinfomsg, ifi_index) },
765                 { IFLA_UNSPEC, "flags", DT_FLAGS, 0, MEMBER(ifinfomsg, ifi_flags) },
766                 { IFLA_UNSPEC, "change", DT_FLAGS, 0, MEMBER(ifinfomsg, ifi_change) },
767                 { IFLA_ADDRESS, "address", DT_LLADDR, 0, NULL },
768                 { IFLA_BROADCAST, "broadcast", DT_LLADDR, 0, NULL },
769                 { IFLA_TXQLEN, "txqlen", DT_U32, 0, NULL },
770                 { IFLA_MTU, "mtu", DT_U32, 0, NULL },
771                 { IFLA_CARRIER, "carrier", DT_BOOL, 0, NULL },
772                 { IFLA_MASTER, "master", DT_NETDEV, DF_ALLOW_NONE, NULL },
773                 { IFLA_IFALIAS, "ifalias", DT_STRING, 0, NULL },
774                 { IFLA_LINKMODE, "linkmode", DT_U8, 0, NULL },
775                 { IFLA_OPERSTATE, "operstate", DT_U8, 0, NULL },
776                 { IFLA_NUM_TX_QUEUES, "num_tx_queues", DT_U32, 0, NULL },
777                 { IFLA_NUM_RX_QUEUES, "num_rx_queues", DT_U32, 0, NULL },
778                 { IFLA_AF_SPEC, "af_spec", DT_AFSPEC, 0, NULL },
779                 { IFLA_LINK_NETNSID, "link_netnsid", DT_U32, 0, NULL },
780                 { IFLA_TARGET_NETNSID, "target_netnsid", DT_S32, 0, NULL },
781                 { IFLA_PROTO_DOWN, "proto_down", DT_BOOL, 0, NULL },
782                 { IFLA_GROUP, "group", DT_U32, 0, NULL },
783                 { IFLA_LINK, "link", DT_NETDEV, 0, NULL },
784                 { IFLA_IFNAME, "ifname", DT_STRING, 0, NULL },
785                 { IFLA_LINKINFO, "linkinfo", DT_LINKINFO, 0, NULL }, /* XXX: DF_NO_GET ? */
786                 { IFLA_EXT_MASK, "ext_mask", DT_U32, 0, NULL },
787                 { IFLA_STATS64, "stats64", DT_NESTED, DF_NO_SET, &link_attrs_stats64_rta },
788                 /* TODO: IFLA_VFINFO_LIST */
789                 /* TODO: the following two should be straightforward, just uncomment and test */
790                 /* { IFLA_NET_NS_PID, "net_ns_pid", DT_S32, 0, NULL }, */
791                 /* { IFLA_NET_NS_FD, "net_ns_fd", DT_S32, 0, NULL }, */
792         }
793 };
794 
795 static const uc_nl_attr_spec_t link_bareudp_attrs[] = {
796         { IFLA_BAREUDP_ETHERTYPE, "ethertype", DT_U16, 0, NULL },
797         { IFLA_BAREUDP_MULTIPROTO_MODE, "multiproto_mode", DT_FLAG, 0, NULL },
798         { IFLA_BAREUDP_PORT, "port", DT_U16, 0, NULL },
799         { IFLA_BAREUDP_SRCPORT_MIN, "srcport_min", DT_U16, 0, NULL },
800 };
801 
802 static const uc_nl_nested_spec_t link_bond_ad_info_rta = {
803         .headsize = 0,
804         .nattrs = 5,
805         .attrs = {
806                 { IFLA_BOND_AD_INFO_ACTOR_KEY, "ad_info_actor_key", DT_U16, DF_NO_SET, NULL },
807                 { IFLA_BOND_AD_INFO_AGGREGATOR, "ad_info_aggregator", DT_U16, DF_NO_SET, NULL },
808                 { IFLA_BOND_AD_INFO_NUM_PORTS, "ad_info_num_ports", DT_U16, DF_NO_SET, NULL },
809                 { IFLA_BOND_AD_INFO_PARTNER_KEY, "ad_info_partner_key", DT_U16, DF_NO_SET, NULL },
810                 { IFLA_BOND_AD_INFO_PARTNER_MAC, "ad_info_partner_mac", DT_LLADDR, DF_NO_SET, NULL },
811         }
812 };
813 
814 static const uc_nl_attr_spec_t link_bond_attrs[] = {
815         { IFLA_BOND_ACTIVE_SLAVE, "active_slave", DT_NETDEV, DF_ALLOW_NONE, NULL },
816         { IFLA_BOND_AD_ACTOR_SYSTEM, "ad_actor_system", DT_LLADDR, 0, NULL },
817         { IFLA_BOND_AD_ACTOR_SYS_PRIO, "ad_actor_sys_prio", DT_U16, 0, NULL },
818         { IFLA_BOND_AD_INFO, "ad_info", DT_NESTED, DF_NO_SET, &link_bond_ad_info_rta },
819         { IFLA_BOND_AD_LACP_RATE, "ad_lacp_rate", DT_U8, 0, NULL },
820         { IFLA_BOND_AD_SELECT, "ad_select", DT_U8, 0, NULL },
821         { IFLA_BOND_AD_USER_PORT_KEY, "ad_user_port_key", DT_U16, 0, NULL },
822         { IFLA_BOND_ALL_SLAVES_ACTIVE, "all_slaves_active", DT_U8, 0, NULL },
823         { IFLA_BOND_ARP_ALL_TARGETS, "arp_all_targets", DT_U32, 0, NULL },
824         { IFLA_BOND_ARP_INTERVAL, "arp_interval", DT_U32, 0, NULL },
825         { IFLA_BOND_ARP_IP_TARGET, "arp_ip_target", DT_INADDR, DF_MULTIPLE, NULL },
826         { IFLA_BOND_ARP_VALIDATE, "arp_validate", DT_U32, 0, NULL },
827         { IFLA_BOND_DOWNDELAY, "downdelay", DT_U32, 0, NULL },
828         { IFLA_BOND_FAIL_OVER_MAC, "fail_over_mac", DT_U8, 0, NULL },
829         { IFLA_BOND_LP_INTERVAL, "lp_interval", DT_U32, 0, NULL },
830         { IFLA_BOND_MIIMON, "miimon", DT_U32, 0, NULL },
831         { IFLA_BOND_MIN_LINKS, "min_links", DT_U32, 0, NULL },
832         { IFLA_BOND_MODE, "mode", DT_U8, 0, NULL },
833         { IFLA_BOND_NUM_PEER_NOTIF, "num_peer_notif", DT_U8, 0, NULL },
834         { IFLA_BOND_PACKETS_PER_SLAVE, "packets_per_slave", DT_U32, 0, NULL },
835         { IFLA_BOND_PRIMARY, "primary", DT_NETDEV, 0, NULL },
836         { IFLA_BOND_PRIMARY_RESELECT, "primary_reselect", DT_U8, 0, NULL },
837         { IFLA_BOND_RESEND_IGMP, "resend_igmp", DT_U32, 0, NULL },
838         { IFLA_BOND_TLB_DYNAMIC_LB, "tlb_dynamic_lb", DT_U8, 0, NULL },
839         { IFLA_BOND_UPDELAY, "updelay", DT_U32, 0, NULL },
840         { IFLA_BOND_USE_CARRIER, "use_carrier", DT_U8, 0, NULL },
841         { IFLA_BOND_XMIT_HASH_POLICY, "xmit_hash_policy", DT_U8, 0, NULL },
842 };
843 
844 static const uc_nl_attr_spec_t link_bond_slave_attrs[] = {
845         { IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, "ad_actor_oper_port_state", DT_U8, DF_NO_SET, NULL },
846         { IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, "ad_aggregator_id", DT_U16, DF_NO_SET, NULL },
847         { IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, "ad_partner_oper_port_state", DT_U8, DF_NO_SET, NULL },
848         { IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, "link_failure_count", DT_U32, DF_NO_SET, NULL },
849         { IFLA_BOND_SLAVE_MII_STATUS, "mii_status", DT_U8, DF_NO_SET, NULL },
850         { IFLA_BOND_SLAVE_PERM_HWADDR, "perm_hwaddr", DT_LLADDR, DF_NO_SET, NULL },
851         { IFLA_BOND_SLAVE_QUEUE_ID, "queue_id", DT_U16, 0, NULL },
852         { IFLA_BOND_SLAVE_STATE, "state", DT_U8, DF_NO_SET, NULL },
853 };
854 
855 static const uc_nl_attr_spec_t link_bridge_attrs[] = {
856         { IFLA_BR_AGEING_TIME, "ageing_time", DT_U32, 0, NULL },
857         { IFLA_BR_BRIDGE_ID, "bridge_id", DT_BRIDGEID, DF_NO_SET, NULL },
858         { IFLA_BR_FDB_FLUSH, "fdb_flush", DT_FLAG, DF_NO_GET, NULL },
859         { IFLA_BR_FORWARD_DELAY, "forward_delay", DT_U32, 0, NULL },
860         { IFLA_BR_GC_TIMER, "gc_timer", DT_U64, DF_NO_SET, NULL },
861         { IFLA_BR_GROUP_ADDR, "group_addr", DT_LLADDR, 0, NULL },
862         { IFLA_BR_GROUP_FWD_MASK, "group_fwd_mask", DT_U16, 0, NULL },
863         { IFLA_BR_HELLO_TIME, "hello_time", DT_U32, 0, NULL },
864         { IFLA_BR_HELLO_TIMER, "hello_timer", DT_U64, DF_NO_SET, NULL },
865         { IFLA_BR_MAX_AGE, "max_age", DT_U32, 0, NULL },
866         { IFLA_BR_MCAST_HASH_ELASTICITY, "mcast_hash_elasticity", DT_U32, 0, NULL },
867         { IFLA_BR_MCAST_HASH_MAX, "mcast_hash_max", DT_U32, 0, NULL },
868         { IFLA_BR_MCAST_IGMP_VERSION, "mcast_igmp_version", DT_U8, 0, NULL },
869         { IFLA_BR_MCAST_LAST_MEMBER_CNT, "mcast_last_member_cnt", DT_U32, 0, NULL },
870         { IFLA_BR_MCAST_LAST_MEMBER_INTVL, "mcast_last_member_intvl", DT_U64, 0, NULL },
871         { IFLA_BR_MCAST_MEMBERSHIP_INTVL, "mcast_membership_intvl", DT_U64, 0, NULL },
872         { IFLA_BR_MCAST_MLD_VERSION, "mcast_mld_version", DT_U8, 0, NULL },
873         { IFLA_BR_MCAST_QUERIER, "mcast_querier", DT_U8, 0, NULL },
874         { IFLA_BR_MCAST_QUERIER_INTVL, "mcast_querier_intvl", DT_U64, 0, NULL },
875         { IFLA_BR_MCAST_QUERY_INTVL, "mcast_query_intvl", DT_U64, 0, NULL },
876         { IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, "mcast_query_response_intvl", DT_U64, 0, NULL },
877         { IFLA_BR_MCAST_QUERY_USE_IFADDR, "mcast_query_use_ifaddr", DT_U8, 0, NULL },
878         { IFLA_BR_MCAST_ROUTER, "mcast_router", DT_U8, 0, NULL },
879         { IFLA_BR_MCAST_SNOOPING, "mcast_snooping", DT_U8, 0, NULL },
880         { IFLA_BR_MCAST_STARTUP_QUERY_CNT, "mcast_startup_query_cnt", DT_U32, 0, NULL },
881         { IFLA_BR_MCAST_STARTUP_QUERY_INTVL, "mcast_startup_query_intvl", DT_U64, 0, NULL },
882         { IFLA_BR_MCAST_STATS_ENABLED, "mcast_stats_enabled", DT_U8, 0, NULL },
883         { IFLA_BR_NF_CALL_ARPTABLES, "nf_call_arptables", DT_U8, 0, NULL },
884         { IFLA_BR_NF_CALL_IP6TABLES, "nf_call_ip6tables", DT_U8, 0, NULL },
885         { IFLA_BR_NF_CALL_IPTABLES, "nf_call_iptables", DT_U8, 0, NULL },
886         { IFLA_BR_PRIORITY, "priority", DT_U16, 0, NULL },
887         { IFLA_BR_ROOT_ID, "root_id", DT_BRIDGEID, DF_NO_SET, NULL },
888         { IFLA_BR_ROOT_PATH_COST, "root_path_cost", DT_U32, DF_NO_SET, NULL },
889         { IFLA_BR_ROOT_PORT, "root_port", DT_U16, DF_NO_SET, NULL },
890         { IFLA_BR_STP_STATE, "stp_state", DT_U32, 0, NULL },
891         { IFLA_BR_TCN_TIMER, "tcn_timer", DT_U64, DF_NO_SET, NULL },
892         { IFLA_BR_TOPOLOGY_CHANGE, "topology_change", DT_U8, DF_NO_SET, NULL },
893         { IFLA_BR_TOPOLOGY_CHANGE_DETECTED, "topology_change_detected", DT_U8, DF_NO_SET, NULL },
894         { IFLA_BR_TOPOLOGY_CHANGE_TIMER, "topology_change_timer", DT_U64, DF_NO_SET, NULL },
895         { IFLA_BR_VLAN_DEFAULT_PVID, "vlan_default_pvid", DT_U16, 0, NULL },
896         { IFLA_BR_VLAN_FILTERING, "vlan_filtering", DT_U8, 0, NULL },
897         { IFLA_BR_VLAN_PROTOCOL, "vlan_protocol", DT_U16, 0, NULL },
898         { IFLA_BR_VLAN_STATS_ENABLED, "vlan_stats_enabled", DT_U8, 0, NULL },
899 };
900 
901 static const uc_nl_attr_spec_t link_bridge_slave_attrs[] = {
902         { IFLA_BRPORT_BACKUP_PORT, "backup_port", DT_NETDEV, 0, NULL },
903         //{ IFLA_BRPORT_BCAST_FLOOD, "bcast-flood", DT_??, 0, NULL },
904         { IFLA_BRPORT_BRIDGE_ID, "bridge_id", DT_BRIDGEID, DF_NO_SET, NULL },
905         { IFLA_BRPORT_CONFIG_PENDING, "config_pending", DT_U8, DF_NO_SET, NULL },
906         { IFLA_BRPORT_COST, "cost", DT_U32, 0, NULL },
907         { IFLA_BRPORT_DESIGNATED_COST, "designated_cost", DT_U16, DF_NO_SET, NULL },
908         { IFLA_BRPORT_DESIGNATED_PORT, "designated_port", DT_U16, DF_NO_SET, NULL },
909         { IFLA_BRPORT_FAST_LEAVE, "fast_leave", DT_U8, 0, NULL },
910         { IFLA_BRPORT_FLUSH, "flush", DT_FLAG, DF_NO_GET, NULL },
911         { IFLA_BRPORT_FORWARD_DELAY_TIMER, "forward_delay_timer", DT_U64, DF_NO_SET, NULL },
912         { IFLA_BRPORT_GROUP_FWD_MASK, "group_fwd_mask", DT_U16, 0, NULL },
913         { IFLA_BRPORT_GUARD, "guard", DT_U8, 0, NULL },
914         { IFLA_BRPORT_HOLD_TIMER, "hold_timer", DT_U64, DF_NO_SET, NULL },
915         { IFLA_BRPORT_ID, "id", DT_U16, DF_NO_SET, NULL },
916         { IFLA_BRPORT_ISOLATED, "isolated", DT_U8, 0, NULL },
917         { IFLA_BRPORT_LEARNING, "learning", DT_U8, 0, NULL },
918         { IFLA_BRPORT_LEARNING_SYNC, "learning_sync", DT_U8, 0, NULL },
919         { IFLA_BRPORT_MCAST_FLOOD, "mcast_flood", DT_U8, 0, NULL },
920         { IFLA_BRPORT_MCAST_TO_UCAST, "mcast_to_ucast", DT_U8, 0, NULL },
921         { IFLA_BRPORT_MESSAGE_AGE_TIMER, "message_age_timer", DT_U64, DF_NO_SET, NULL },
922         { IFLA_BRPORT_MODE, "mode", DT_U8, 0, NULL },
923         { IFLA_BRPORT_MULTICAST_ROUTER, "multicast_router", DT_U8, 0, NULL },
924         { IFLA_BRPORT_NEIGH_SUPPRESS, "neigh_suppress", DT_U8, 0, NULL },
925         { IFLA_BRPORT_NO, "no", DT_U16, DF_NO_SET, NULL },
926         { IFLA_BRPORT_PRIORITY, "priority", DT_U16, 0, NULL },
927         { IFLA_BRPORT_PROTECT, "protect", DT_U8, 0, NULL },
928         { IFLA_BRPORT_PROXYARP, "proxyarp", DT_U8, DF_NO_SET, NULL },
929         { IFLA_BRPORT_PROXYARP_WIFI, "proxyarp_wifi", DT_U8, DF_NO_SET, NULL },
930         { IFLA_BRPORT_ROOT_ID, "root_id", DT_BRIDGEID, DF_NO_SET, NULL },
931         { IFLA_BRPORT_STATE, "state", DT_U8, 0, NULL },
932         { IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, "topology_change_ack", DT_U8, DF_NO_SET, NULL },
933         { IFLA_BRPORT_UNICAST_FLOOD, "unicast_flood", DT_U8, 0, NULL },
934         { IFLA_BRPORT_VLAN_TUNNEL, "vlan_tunnel", DT_U8, 0, NULL },
935 };
936 
937 static const uc_nl_nested_spec_t link_can_bittiming_rta = {
938         .headsize = NLA_ALIGN(sizeof(struct can_bittiming)),
939         .nattrs = 8,
940         .attrs = {
941                 { RTA_UNSPEC, "bitrate", DT_U32, 0, MEMBER(can_bittiming, bitrate) },
942                 { RTA_UNSPEC, "sample_point", DT_U32, 0, MEMBER(can_bittiming, sample_point) },
943                 { RTA_UNSPEC, "tq", DT_U32, 0, MEMBER(can_bittiming, tq) },
944                 { RTA_UNSPEC, "prop_seg", DT_U32, 0, MEMBER(can_bittiming, prop_seg) },
945                 { RTA_UNSPEC, "phase_seg1", DT_U32, 0, MEMBER(can_bittiming, phase_seg1) },
946                 { RTA_UNSPEC, "phase_seg2", DT_U32, 0, MEMBER(can_bittiming, phase_seg2) },
947                 { RTA_UNSPEC, "sjw", DT_U32, 0, MEMBER(can_bittiming, sjw) },
948                 { RTA_UNSPEC, "brp", DT_U32, 0, MEMBER(can_bittiming, brp) },
949         }
950 };
951 
952 static const uc_nl_nested_spec_t link_can_bittiming_const_rta = {
953         .headsize = NLA_ALIGN(sizeof(struct can_bittiming_const)),
954         .nattrs = 8,
955         .attrs = {
956                 { RTA_UNSPEC, "tseg1_min", DT_U32, 0, MEMBER(can_bittiming_const, tseg1_min) },
957                 { RTA_UNSPEC, "tseg1_max", DT_U32, 0, MEMBER(can_bittiming_const, tseg1_max) },
958                 { RTA_UNSPEC, "tseg2_min", DT_U32, 0, MEMBER(can_bittiming_const, tseg2_min) },
959                 { RTA_UNSPEC, "tseg2_max", DT_U32, 0, MEMBER(can_bittiming_const, tseg2_max) },
960                 { RTA_UNSPEC, "sjw_max", DT_U32, 0, MEMBER(can_bittiming_const, sjw_max) },
961                 { RTA_UNSPEC, "brp_min", DT_U32, 0, MEMBER(can_bittiming_const, brp_min) },
962                 { RTA_UNSPEC, "brp_max", DT_U32, 0, MEMBER(can_bittiming_const, brp_max) },
963                 { RTA_UNSPEC, "brp_inc", DT_U32, 0, MEMBER(can_bittiming_const, brp_inc) },
964         }
965 };
966 
967 static const uc_nl_nested_spec_t link_can_clock_rta = {
968         .headsize = NLA_ALIGN(sizeof(struct can_clock)),
969         .nattrs = 1,
970         .attrs = {
971                 { RTA_UNSPEC, "freq", DT_U32, 0, MEMBER(can_clock, freq) },
972         }
973 };
974 
975 static const uc_nl_nested_spec_t link_can_ctrlmode_rta = {
976         .headsize = NLA_ALIGN(sizeof(struct can_ctrlmode)),
977         .nattrs = 2,
978         .attrs = {
979                 { RTA_UNSPEC, "mask", DT_U32, 0, MEMBER(can_ctrlmode, mask) },
980                 { RTA_UNSPEC, "flags", DT_U32, 0, MEMBER(can_ctrlmode, flags) },
981         }
982 };
983 
984 static const uc_nl_nested_spec_t link_can_berr_counter_rta = {
985         .headsize = NLA_ALIGN(sizeof(struct can_berr_counter)),
986         .nattrs = 2,
987         .attrs = {
988                 { RTA_UNSPEC, "txerr", DT_U16, 0, MEMBER(can_berr_counter, txerr) },
989                 { RTA_UNSPEC, "rxerr", DT_U16, 0, MEMBER(can_berr_counter, rxerr) },
990         }
991 };
992 
993 static const uc_nl_attr_spec_t link_can_attrs[] = {
994         { IFLA_CAN_BERR_COUNTER, "berr_counter", DT_NESTED, DF_NO_SET, &link_can_berr_counter_rta },
995         { IFLA_CAN_BITTIMING, "bittiming", DT_NESTED, 0, &link_can_bittiming_rta },
996         { IFLA_CAN_BITTIMING_CONST, "bittiming_const", DT_NESTED, DF_NO_SET, &link_can_bittiming_const_rta },
997         { IFLA_CAN_CLOCK, "clock", DT_NESTED, DF_NO_SET, &link_can_clock_rta },
998         { IFLA_CAN_CTRLMODE, "ctrlmode", DT_NESTED, 0, &link_can_ctrlmode_rta },
999         { IFLA_CAN_DATA_BITTIMING, "data_bittiming", DT_NESTED, 0, &link_can_bittiming_rta },
1000         { IFLA_CAN_DATA_BITTIMING_CONST, "data_bittiming_const", DT_NESTED, DF_NO_SET, &link_can_bittiming_const_rta },
1001         { IFLA_CAN_RESTART, "restart", DT_U32, DF_NO_GET, NULL },
1002         { IFLA_CAN_RESTART_MS, "restart_ms", DT_U32, 0, NULL },
1003         { IFLA_CAN_STATE, "state", DT_U32, DF_NO_SET, NULL },
1004         { IFLA_CAN_TERMINATION, "termination", DT_U16, 0, NULL },
1005 };
1006 
1007 static const uc_nl_attr_spec_t link_geneve_attrs[] = {
1008         { IFLA_GENEVE_COLLECT_METADATA, "collect_metadata", DT_FLAG, DF_NO_GET, NULL },
1009         { IFLA_GENEVE_ID, "id", DT_U32, 0, NULL },
1010         { IFLA_GENEVE_LABEL, "label", DT_U32, 0, NULL },
1011         { IFLA_GENEVE_PORT, "port", DT_U16, 0, NULL },
1012         { IFLA_GENEVE_REMOTE, "remote", DT_INADDR, 0, NULL },
1013         { IFLA_GENEVE_REMOTE6, "remote6", DT_IN6ADDR, 0, NULL },
1014         { IFLA_GENEVE_TOS, "tos", DT_U8, 0, NULL },
1015         { IFLA_GENEVE_TTL, "ttl", DT_U8, 0, NULL },
1016         { IFLA_GENEVE_UDP_CSUM, "udp_csum", DT_U8, 0, NULL },
1017         { IFLA_GENEVE_UDP_ZERO_CSUM6_RX, "udp_zero_csum6_rx", DT_U8, 0, NULL },
1018         { IFLA_GENEVE_UDP_ZERO_CSUM6_TX, "udp_zero_csum6_tx", DT_U8, 0, NULL },
1019 };
1020 
1021 static const uc_nl_attr_spec_t link_hsr_attrs[] = {
1022         { IFLA_HSR_MULTICAST_SPEC, "multicast_spec", DT_STRING, DF_NO_GET, NULL },
1023         { IFLA_HSR_SEQ_NR, "seq_nr", DT_U16, DF_NO_SET, NULL },
1024         { IFLA_HSR_SLAVE1, "slave1", DT_NETDEV, 0, NULL },
1025         { IFLA_HSR_SLAVE2, "slave2", DT_NETDEV, 0, NULL },
1026         { IFLA_HSR_SUPERVISION_ADDR, "supervision_addr", DT_LLADDR, DF_NO_SET, NULL },
1027         { IFLA_HSR_VERSION, "version", DT_STRING, DF_NO_GET, NULL },
1028 };
1029 
1030 static const uc_nl_attr_spec_t link_ipoib_attrs[] = {
1031         { IFLA_IPOIB_MODE, "mode", DT_U16, 0, NULL },
1032         { IFLA_IPOIB_PKEY, "pkey", DT_U16, 0, NULL },
1033         { IFLA_IPOIB_UMCAST, "umcast", DT_U16, 0, NULL },
1034 };
1035 
1036 static const uc_nl_attr_spec_t link_ipvlan_attrs[] = {
1037         { IFLA_IPVLAN_FLAGS, "flags", DT_U16, 0, NULL },
1038         { IFLA_IPVLAN_MODE, "mode", DT_U16, 0, NULL },
1039 };
1040 
1041 static const uc_nl_attr_spec_t link_macvlan_attrs[] = {
1042         { IFLA_MACVLAN_FLAGS, "flags", DT_U16, 0, NULL },
1043         { IFLA_MACVLAN_MACADDR, "macaddr", DT_LLADDR, DF_NO_GET, NULL },
1044         { IFLA_MACVLAN_MACADDR_COUNT, "macaddr_count", DT_U32, DF_NO_SET, NULL },
1045         { IFLA_MACVLAN_MACADDR_DATA, "macaddr_data", DT_LLADDR, DF_MULTIPLE, (void *)IFLA_MACVLAN_MACADDR },
1046         { IFLA_MACVLAN_MACADDR_MODE, "macaddr_mode", DT_U32, DF_NO_GET, NULL },
1047         { IFLA_MACVLAN_MODE, "mode", DT_U32, 0, NULL },
1048 };
1049 
1050 static const uc_nl_attr_spec_t link_rmnet_attrs[] = {
1051         //{ IFLA_RMNET_FLAGS, "flags", DT_??, 0, NULL },
1052         { IFLA_RMNET_MUX_ID, "mux_id", DT_U16, 0, NULL },
1053 };
1054 
1055 
1056 static const uc_nl_attr_spec_t link_vlan_attrs[] = {
1057         { IFLA_VLAN_EGRESS_QOS, "egress_qos_map", DT_NUMRANGE, DF_MULTIPLE, (void *)IFLA_VLAN_QOS_MAPPING },
1058         { IFLA_VLAN_FLAGS, "flags", DT_FLAGS, 0, NULL },
1059         { IFLA_VLAN_ID, "id", DT_U16, 0, NULL },
1060         { IFLA_VLAN_INGRESS_QOS, "ingress_qos_map", DT_NUMRANGE, DF_MULTIPLE, (void *)IFLA_VLAN_QOS_MAPPING },
1061         { IFLA_VLAN_PROTOCOL, "protocol", DT_U16, 0, NULL },
1062 };
1063 
1064 static const uc_nl_attr_spec_t link_vrf_attrs[] = {
1065         { IFLA_VRF_PORT_TABLE, "port_table", DT_U32, DF_NO_SET, NULL },
1066         { IFLA_VRF_TABLE, "table", DT_U32, 0, NULL },
1067 };
1068 
1069 static const uc_nl_attr_spec_t link_vxcan_attrs[] = {
1070         { VXCAN_INFO_PEER, "info_peer", DT_NESTED, 0, &link_msg },
1071 };
1072 
1073 static const uc_nl_attr_spec_t link_vxlan_attrs[] = {
1074         { IFLA_VXLAN_AGEING, "ageing", DT_U32, 0, NULL },
1075         { IFLA_VXLAN_COLLECT_METADATA, "collect_metadata", DT_U8, 0, NULL },
1076         { IFLA_VXLAN_GBP, "gbp", DT_FLAG, 0, NULL },
1077         { IFLA_VXLAN_GPE, "gpe", DT_FLAG, 0, NULL },
1078         { IFLA_VXLAN_GROUP, "group", DT_INADDR, 0, NULL },
1079         { IFLA_VXLAN_GROUP6, "group6", DT_IN6ADDR, 0, NULL },
1080         { IFLA_VXLAN_ID, "id", DT_U32, 0, NULL },
1081         { IFLA_VXLAN_L2MISS, "l2miss", DT_U8, 0, NULL },
1082         { IFLA_VXLAN_L3MISS, "l3miss", DT_U8, 0, NULL },
1083         { IFLA_VXLAN_LABEL, "label", DT_U32, 0, NULL },
1084         { IFLA_VXLAN_LEARNING, "learning", DT_U8, 0, NULL },
1085         { IFLA_VXLAN_LIMIT, "limit", DT_U32, 0, NULL },
1086         { IFLA_VXLAN_LINK, "link", DT_U32, 0, NULL },
1087         { IFLA_VXLAN_LOCAL, "local", DT_INADDR, 0, NULL },
1088         { IFLA_VXLAN_LOCAL6, "local6", DT_IN6ADDR, 0, NULL },
1089         { IFLA_VXLAN_PORT, "port", DT_U16, DF_BYTESWAP, NULL },
1090         { IFLA_VXLAN_PORT_RANGE, "port_range", DT_NUMRANGE, DF_MAX_65535|DF_BYTESWAP, NULL },
1091         { IFLA_VXLAN_PROXY, "proxy", DT_U8, 0, NULL },
1092         //{ IFLA_VXLAN_REMCSUM_NOPARTIAL, "remcsum-nopartial", DT_??, 0, NULL },
1093         { IFLA_VXLAN_REMCSUM_RX, "remcsum_rx", DT_BOOL, 0, NULL },
1094         { IFLA_VXLAN_REMCSUM_TX, "remcsum_tx", DT_BOOL, 0, NULL },
1095         { IFLA_VXLAN_RSC, "rsc", DT_BOOL, 0, NULL },
1096         { IFLA_VXLAN_TOS, "tos", DT_U8, 0, NULL },
1097         { IFLA_VXLAN_TTL, "ttl", DT_U8, 0, NULL },
1098         { IFLA_VXLAN_TTL_INHERIT, "ttl_inherit", DT_FLAG, 0, NULL },
1099         { IFLA_VXLAN_UDP_CSUM, "udp_csum", DT_BOOL, 0, NULL },
1100         { IFLA_VXLAN_UDP_ZERO_CSUM6_RX, "udp_zero_csum6_rx", DT_BOOL, 0, NULL },
1101         { IFLA_VXLAN_UDP_ZERO_CSUM6_TX, "udp_zero_csum6_tx", DT_BOOL, 0, NULL },
1102 };
1103 
1104 static const uc_nl_attr_spec_t link_gre_attrs[] = {
1105         { IFLA_GRE_COLLECT_METADATA, "collect_metadata", DT_FLAG, 0, NULL },
1106         { IFLA_GRE_ENCAP_DPORT, "encap_dport", DT_U16, DF_BYTESWAP, NULL },
1107         { IFLA_GRE_ENCAP_FLAGS, "encap_flags", DT_U16, 0, NULL },
1108         { IFLA_GRE_ENCAP_LIMIT, "encap_limit", DT_U8, 0, NULL },
1109         { IFLA_GRE_ENCAP_SPORT, "encap_sport", DT_U16, DF_BYTESWAP, NULL },
1110         { IFLA_GRE_ENCAP_TYPE, "encap_type", DT_U16, 0, NULL },
1111         { IFLA_GRE_ERSPAN_DIR, "erspan_dir", DT_U8, 0, NULL },
1112         { IFLA_GRE_ERSPAN_HWID, "erspan_hwid", DT_U16, 0, NULL },
1113         { IFLA_GRE_ERSPAN_INDEX, "erspan_index", DT_U32, 0, NULL },
1114         { IFLA_GRE_ERSPAN_VER, "erspan_ver", DT_U8, 0, NULL },
1115         { IFLA_GRE_FLAGS, "flags", DT_U32, 0, NULL },
1116         { IFLA_GRE_FLOWINFO, "flowinfo", DT_U32, DF_BYTESWAP, NULL },
1117         { IFLA_GRE_FWMARK, "fwmark", DT_U32, 0, NULL },
1118         { IFLA_GRE_IFLAGS, "iflags", DT_U16, 0, NULL },
1119         { IFLA_GRE_IGNORE_DF, "ignore_df", DT_BOOL, 0, NULL },
1120         { IFLA_GRE_IKEY, "ikey", DT_U32, 0, NULL },
1121         { IFLA_GRE_LINK, "link", DT_NETDEV, 0, NULL },
1122         { IFLA_GRE_LOCAL, "local", DT_ANYADDR, 0, NULL },
1123         { IFLA_GRE_OFLAGS, "oflags", DT_U16, 0, NULL },
1124         { IFLA_GRE_OKEY, "okey", DT_U32, 0, NULL },
1125         { IFLA_GRE_PMTUDISC, "pmtudisc", DT_BOOL, 0, NULL },
1126         { IFLA_GRE_REMOTE, "remote", DT_ANYADDR, 0, NULL },
1127         { IFLA_GRE_TOS, "tos", DT_U8, 0, NULL },
1128         { IFLA_GRE_TTL, "ttl", DT_U8, 0, NULL },
1129 };
1130 
1131 #define link_gretap_attrs link_gre_attrs
1132 #define link_erspan_attrs link_gre_attrs
1133 #define link_ip6gre_attrs link_gre_attrs
1134 #define link_ip6gretap_attrs link_gre_attrs
1135 #define link_ip6erspan_attrs link_gre_attrs
1136 
1137 static const uc_nl_attr_spec_t link_ip6tnl_attrs[] = {
1138         { IFLA_IPTUN_6RD_PREFIX, "6rd_prefix", DT_IN6ADDR, 0, NULL },
1139         { IFLA_IPTUN_6RD_PREFIXLEN, "6rd_prefixlen", DT_U16, 0, NULL },
1140         { IFLA_IPTUN_6RD_RELAY_PREFIX, "6rd_relay_prefix", DT_INADDR, 0, NULL },
1141         { IFLA_IPTUN_6RD_RELAY_PREFIXLEN, "6rd_relay_prefixlen", DT_U16, 0, NULL },
1142         { IFLA_IPTUN_COLLECT_METADATA, "collect_metadata", DT_BOOL, 0, NULL },
1143         { IFLA_IPTUN_ENCAP_DPORT, "encap_dport", DT_U16, DF_BYTESWAP, NULL },
1144         { IFLA_IPTUN_ENCAP_FLAGS, "encap_flags", DT_U16, 0, NULL },
1145         { IFLA_IPTUN_ENCAP_LIMIT, "encap_limit", DT_U8, 0, NULL },
1146         { IFLA_IPTUN_ENCAP_SPORT, "encap_sport", DT_U16, DF_BYTESWAP, NULL },
1147         { IFLA_IPTUN_ENCAP_TYPE, "encap_type", DT_U16, 0, NULL },
1148         { IFLA_IPTUN_FLAGS, "flags", DT_U16, 0, NULL },
1149         { IFLA_IPTUN_FLOWINFO, "flowinfo", DT_U32, DF_BYTESWAP, NULL },
1150         { IFLA_IPTUN_FWMARK, "fwmark", DT_U32, 0, NULL },
1151         { IFLA_IPTUN_LINK, "link", DT_NETDEV, 0, NULL },
1152         { IFLA_IPTUN_LOCAL, "local", DT_ANYADDR, 0, NULL },
1153         { IFLA_IPTUN_PMTUDISC, "pmtudisc", DT_BOOL, 0, NULL },
1154         { IFLA_IPTUN_PROTO, "proto", DT_U8, 0, NULL },
1155         { IFLA_IPTUN_REMOTE, "remote", DT_ANYADDR, 0, NULL },
1156         { IFLA_IPTUN_TOS, "tos", DT_U8, 0, NULL },
1157         { IFLA_IPTUN_TTL, "ttl", DT_U8, 0, NULL },
1158 };
1159 
1160 #define link_ipip_attrs link_ip6tnl_attrs
1161 #define link_sit_attrs link_ip6tnl_attrs
1162 
1163 static const uc_nl_attr_spec_t link_veth_attrs[] = {
1164         { VETH_INFO_PEER, "info_peer", DT_NESTED, 0, &link_msg },
1165 };
1166 
1167 static const uc_nl_attr_spec_t link_vti_attrs[] = {
1168         { IFLA_VTI_FWMARK, "fwmark", DT_U32, 0, NULL },
1169         { IFLA_VTI_IKEY, "ikey", DT_U32, 0, NULL },
1170         { IFLA_VTI_LINK, "link", DT_U32, 0, NULL },
1171         { IFLA_VTI_LOCAL, "local", DT_ANYADDR, 0, NULL },
1172         { IFLA_VTI_OKEY, "okey", DT_U32, 0, NULL },
1173         { IFLA_VTI_REMOTE, "remote", DT_ANYADDR, 0, NULL },
1174 };
1175 
1176 #define link_vti6_attrs link_vti_attrs
1177 
1178 static const uc_nl_attr_spec_t link_xfrm_attrs[] = {
1179         { IFLA_XFRM_IF_ID, "if_id", DT_U32, 0, NULL },
1180         { IFLA_XFRM_LINK, "link", DT_NETDEV, 0, NULL },
1181 };
1182 
1183 static const uc_nl_attr_spec_t lwtipopt_erspan_attrs[] = {
1184         { LWTUNNEL_IP_OPT_ERSPAN_VER, "ver", DT_U8, 0, NULL },
1185         { LWTUNNEL_IP_OPT_ERSPAN_INDEX, "index", DT_U16, DF_BYTESWAP, NULL },
1186         { LWTUNNEL_IP_OPT_ERSPAN_DIR, "dir", DT_U8, 0, NULL },
1187         { LWTUNNEL_IP_OPT_ERSPAN_HWID, "hwid", DT_U8, 0, NULL },
1188 };
1189 
1190 static const uc_nl_attr_spec_t lwtipopt_geneve_attrs[] = {
1191         { LWTUNNEL_IP_OPT_GENEVE_CLASS, "class", DT_U16, DF_BYTESWAP, NULL },
1192         { LWTUNNEL_IP_OPT_GENEVE_TYPE, "type", DT_U8, 0, NULL },
1193         { LWTUNNEL_IP_OPT_GENEVE_DATA, "data", DT_STRING, 0, NULL },
1194 };
1195 
1196 static const uc_nl_attr_spec_t lwtipopt_vxlan_attrs[] = {
1197         { LWTUNNEL_IP_OPT_VXLAN_GBP, "gbp", DT_U32, 0, NULL },
1198 };
1199 
1200 static const uc_nl_nested_spec_t neigh_cacheinfo_rta = {
1201         .headsize = NLA_ALIGN(sizeof(struct nda_cacheinfo)),
1202         .nattrs = 4,
1203         .attrs = {
1204                 { NDA_UNSPEC, "confirmed", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_confirmed) },
1205                 { NDA_UNSPEC, "used", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_used) },
1206                 { NDA_UNSPEC, "updated", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_updated) },
1207                 { NDA_UNSPEC, "refcnt", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_refcnt) },
1208         }
1209 };
1210 
1211 static const uc_nl_nested_spec_t neigh_msg = {
1212         .headsize = NLA_ALIGN(sizeof(struct ndmsg)),
1213         .nattrs = 16,
1214         .attrs = {
1215                 { NDA_UNSPEC, "family", DT_U8, 0, MEMBER(ndmsg, ndm_family) },
1216                 { NDA_UNSPEC, "dev" /* actually ifindex, but avoid clash with NDA_IFINDEX */, DT_NETDEV, DF_ALLOW_NONE, MEMBER(ndmsg, ndm_ifindex) },
1217                 { NDA_UNSPEC, "state", DT_U16, 0, MEMBER(ndmsg, ndm_state) },
1218                 { NDA_UNSPEC, "flags", DT_U8, 0, MEMBER(ndmsg, ndm_flags) },
1219                 { NDA_UNSPEC, "type", DT_U8, 0, MEMBER(ndmsg, ndm_type) },
1220                 { NDA_CACHEINFO, "cacheinfo", DT_NESTED, DF_NO_SET, &neigh_cacheinfo_rta },
1221                 { NDA_DST, "dst", DT_ANYADDR, 0, NULL },
1222                 { NDA_IFINDEX, "ifindex", DT_NETDEV, 0, NULL },
1223                 { NDA_LINK_NETNSID, "link_netnsid", DT_U32, DF_NO_SET, NULL },
1224                 { NDA_LLADDR, "lladdr", DT_LLADDR, 0, NULL },
1225                 { NDA_MASTER, "master", DT_NETDEV, 0, NULL },
1226                 { NDA_PORT, "port", DT_U16, DF_BYTESWAP, NULL },
1227                 { NDA_PROBES, "probes", DT_U32, DF_NO_SET, NULL },
1228                 { NDA_SRC_VNI, "src_vni", DT_U32, DF_NO_SET, NULL },
1229                 { NDA_VLAN, "vlan", DT_U16, 0, NULL },
1230                 { NDA_VNI, "vni", DT_U32, DF_MAX_16777215, NULL },
1231         }
1232 };
1233 
1234 static const uc_nl_nested_spec_t addr_cacheinfo_rta = {
1235         .headsize = NLA_ALIGN(sizeof(struct ifa_cacheinfo)),
1236         .nattrs = 4,
1237         .attrs = {
1238                 { IFA_UNSPEC, "preferred", DT_U32, 0, MEMBER(ifa_cacheinfo, ifa_prefered) },
1239                 { IFA_UNSPEC, "valid", DT_U32, 0, MEMBER(ifa_cacheinfo, ifa_valid) },
1240                 { IFA_UNSPEC, "cstamp", DT_U32, 0, MEMBER(ifa_cacheinfo, cstamp) },
1241                 { IFA_UNSPEC, "tstamp", DT_U32, 0, MEMBER(ifa_cacheinfo, tstamp) },
1242         }
1243 };
1244 
1245 static const uc_nl_nested_spec_t addr_msg = {
1246         .headsize = NLA_ALIGN(sizeof(struct ifaddrmsg)),
1247         .nattrs = 11,
1248         .attrs = {
1249                 { IFA_UNSPEC, "family", DT_U8, 0, MEMBER(ifaddrmsg, ifa_family) },
1250                 { IFA_FLAGS, "flags", DT_U32_OR_MEMBER, DF_MAX_255, MEMBER(ifaddrmsg, ifa_flags) },
1251                 { IFA_UNSPEC, "scope", DT_U8, 0, MEMBER(ifaddrmsg, ifa_scope) },
1252                 { IFA_UNSPEC, "dev", DT_NETDEV, 0, MEMBER(ifaddrmsg, ifa_index) },
1253                 { IFA_ADDRESS, "address", DT_ANYADDR, DF_STORE_MASK, MEMBER(ifaddrmsg, ifa_prefixlen) },
1254                 { IFA_LOCAL, "local", DT_ANYADDR, 0, NULL },
1255                 { IFA_LABEL, "label", DT_STRING, 0, NULL },
1256                 { IFA_BROADCAST, "broadcast", DT_ANYADDR, 0, NULL },
1257                 { IFA_ANYCAST, "anycast", DT_ANYADDR, 0, NULL },
1258                 { IFA_CACHEINFO, "cacheinfo", DT_NESTED, DF_NO_SET, &addr_cacheinfo_rta },
1259                 { IFA_RT_PRIORITY, "metric", DT_U32, 0, NULL },
1260         }
1261 };
1262 
1263 static const uc_nl_nested_spec_t rule_msg = {
1264         .headsize = NLA_ALIGN(sizeof(struct fib_rule_hdr)),
1265         .nattrs = 23,
1266         .attrs = {
1267                 { FRA_UNSPEC, "family", DT_U8, 0, MEMBER(fib_rule_hdr, family) },
1268                 { FRA_UNSPEC, "tos", DT_U8, 0, MEMBER(fib_rule_hdr, tos) },
1269                 { FRA_UNSPEC, "action", DT_U8, 0, MEMBER(fib_rule_hdr, action) },
1270                 { FRA_UNSPEC, "flags", DT_U32, 0, MEMBER(fib_rule_hdr, flags) },
1271                 { FRA_PRIORITY, "priority", DT_U32, 0, NULL },
1272                 { FRA_SRC, "src", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(fib_rule_hdr, src_len) },
1273                 { FRA_DST, "dst", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(fib_rule_hdr, dst_len) },
1274                 { FRA_FWMARK, "fwmark", DT_U32, 0, NULL },
1275                 { FRA_FWMASK, "fwmask", DT_U32, 0, NULL },
1276                 { FRA_IFNAME, "iif", DT_NETDEV, 0, NULL },
1277                 { FRA_OIFNAME, "oif", DT_NETDEV, 0, NULL },
1278                 { FRA_L3MDEV, "l3mdev", DT_U8, 0, NULL },
1279                 { FRA_UID_RANGE, "uid_range", DT_NUMRANGE, 0, NULL },
1280                 { FRA_IP_PROTO, "ip_proto", DT_U8, 0, NULL },
1281                 { FRA_SPORT_RANGE, "sport_range", DT_NUMRANGE, DF_MAX_65535, NULL },
1282                 { FRA_DPORT_RANGE, "dport_range", DT_NUMRANGE, DF_MAX_65535, NULL },
1283                 { FRA_TABLE, "table", DT_U32_OR_MEMBER, DF_MAX_255, MEMBER(fib_rule_hdr, table) },
1284                 { FRA_SUPPRESS_PREFIXLEN, "suppress_prefixlen", DT_S32, 0, NULL },
1285                 { FRA_SUPPRESS_IFGROUP, "suppress_ifgroup", DT_U32, 0, NULL },
1286                 { FRA_FLOW, "flow", DT_U32, 0, NULL },
1287                 { RTA_GATEWAY, "gateway", DT_ANYADDR, DF_FAMILY_HINT, NULL },
1288                 { FRA_GOTO, "goto", DT_U32, 0, NULL },
1289                 { FRA_PROTOCOL, "protocol", DT_U8, 0, NULL },
1290         }
1291 };
1292 
1293 #define IFAL_UNSPEC 0
1294 
1295 static const uc_nl_nested_spec_t addrlabel_msg = {
1296         .headsize = NLA_ALIGN(sizeof(struct ifaddrlblmsg)),
1297         .nattrs = 6,
1298         .attrs = {
1299                 { IFAL_UNSPEC, "family", DT_U8, 0, MEMBER(ifaddrlblmsg, ifal_family) },
1300                 { IFAL_UNSPEC, "flags", DT_U8, 0, MEMBER(ifaddrlblmsg, ifal_flags) },
1301                 { IFAL_UNSPEC, "dev", DT_NETDEV, 0, MEMBER(ifaddrlblmsg, ifal_index) },
1302                 { IFAL_UNSPEC, "seq", DT_U32, 0, MEMBER(ifaddrlblmsg, ifal_seq) },
1303                 { IFAL_ADDRESS, "address", DT_ANYADDR, DF_STORE_MASK, MEMBER(ifaddrlblmsg, ifal_prefixlen) },
1304                 { IFAL_LABEL, "label", DT_U32, 0, NULL },
1305         }
1306 };
1307 
1308 static const uc_nl_nested_spec_t neightbl_params_rta = {
1309         .headsize = 0,
1310         .nattrs = 13,
1311         .attrs = {
1312                 { NDTPA_IFINDEX, "dev", DT_NETDEV, 0, NULL },
1313                 { NDTPA_BASE_REACHABLE_TIME, "base_reachable_time", DT_U64, 0, NULL },
1314                 { NDTPA_RETRANS_TIME, "retrans_time", DT_U64, 0, NULL },
1315                 { NDTPA_GC_STALETIME, "gc_staletime", DT_U64, 0, NULL },
1316                 { NDTPA_DELAY_PROBE_TIME, "delay_probe_time", DT_U64, 0, NULL },
1317                 { NDTPA_QUEUE_LEN, "queue_len", DT_U32, 0, NULL },
1318                 { NDTPA_APP_PROBES, "app_probes", DT_U32, 0, NULL },
1319                 { NDTPA_UCAST_PROBES, "ucast_probes", DT_U32, 0, NULL },
1320                 { NDTPA_MCAST_PROBES, "mcast_probes", DT_U32, 0, NULL },
1321                 { NDTPA_ANYCAST_DELAY, "anycast_delay", DT_U64, 0, NULL },
1322                 { NDTPA_PROXY_DELAY, "proxy_delay", DT_U64, 0, NULL },
1323                 { NDTPA_PROXY_QLEN, "proxy_qlen", DT_U32, 0, NULL },
1324                 { NDTPA_LOCKTIME, "locktime", DT_U64, 0, NULL },
1325         }
1326 };
1327 
1328 static const uc_nl_nested_spec_t neightbl_config_rta = {
1329         .headsize = NLA_ALIGN(sizeof(struct ndt_config)),
1330         .nattrs = 9,
1331         .attrs = {
1332                 { NDTA_UNSPEC, "key_len", DT_U16, 0, MEMBER(ndt_config, ndtc_key_len) },
1333                 { NDTA_UNSPEC, "entry_size", DT_U16, 0, MEMBER(ndt_config, ndtc_entry_size) },
1334                 { NDTA_UNSPEC, "entries", DT_U32, 0, MEMBER(ndt_config, ndtc_entries) },
1335                 { NDTA_UNSPEC, "last_flush", DT_U32, 0, MEMBER(ndt_config, ndtc_last_flush) },
1336                 { NDTA_UNSPEC, "last_rand", DT_U32, 0, MEMBER(ndt_config, ndtc_last_rand) },
1337                 { NDTA_UNSPEC, "hash_rnd", DT_U32, 0, MEMBER(ndt_config, ndtc_hash_rnd) },
1338                 { NDTA_UNSPEC, "hash_mask", DT_U32, 0, MEMBER(ndt_config, ndtc_hash_mask) },
1339                 { NDTA_UNSPEC, "hash_chain_gc", DT_U32, 0, MEMBER(ndt_config, ndtc_hash_chain_gc) },
1340                 { NDTA_UNSPEC, "proxy_qlen", DT_U32, 0, MEMBER(ndt_config, ndtc_proxy_qlen) },
1341         }
1342 };
1343 
1344 static const uc_nl_nested_spec_t neightbl_stats_rta = {
1345         .headsize = NLA_ALIGN(sizeof(struct ndt_stats)),
1346         .nattrs = 10,
1347         .attrs = {
1348                 { NDTA_UNSPEC, "allocs", DT_U64, 0, MEMBER(ndt_stats, ndts_allocs) },
1349                 { NDTA_UNSPEC, "destroys", DT_U64, 0, MEMBER(ndt_stats, ndts_destroys) },
1350                 { NDTA_UNSPEC, "hash_grows", DT_U64, 0, MEMBER(ndt_stats, ndts_hash_grows) },
1351                 { NDTA_UNSPEC, "res_failed", DT_U64, 0, MEMBER(ndt_stats, ndts_res_failed) },
1352                 { NDTA_UNSPEC, "lookups", DT_U64, 0, MEMBER(ndt_stats, ndts_lookups) },
1353                 { NDTA_UNSPEC, "hits", DT_U64, 0, MEMBER(ndt_stats, ndts_hits) },
1354                 { NDTA_UNSPEC, "rcv_probes_mcast", DT_U64, 0, MEMBER(ndt_stats, ndts_rcv_probes_mcast) },
1355                 { NDTA_UNSPEC, "rcv_probes_ucast", DT_U64, 0, MEMBER(ndt_stats, ndts_rcv_probes_ucast) },
1356                 { NDTA_UNSPEC, "periodic_gc_runs", DT_U64, 0, MEMBER(ndt_stats, ndts_periodic_gc_runs) },
1357                 { NDTA_UNSPEC, "forced_gc_runs", DT_U64, 0, MEMBER(ndt_stats, ndts_forced_gc_runs) },
1358         }
1359 };
1360 
1361 static const uc_nl_nested_spec_t neightbl_msg = {
1362         .headsize = NLA_ALIGN(sizeof(struct ndtmsg)),
1363         .nattrs = 9,
1364         .attrs = {
1365                 { NDTA_UNSPEC, "family", DT_U8, 0, MEMBER(ndtmsg, ndtm_family) },
1366                 { NDTA_NAME, "name", DT_STRING, 0, NULL },
1367                 { NDTA_THRESH1, "thresh1", DT_U32, 0, NULL },
1368                 { NDTA_THRESH2, "thresh2", DT_U32, 0, NULL },
1369                 { NDTA_THRESH3, "thresh3", DT_U32, 0, NULL },
1370                 { NDTA_GC_INTERVAL, "gc_interval", DT_U64, 0, NULL },
1371                 { NDTA_PARMS, "params", DT_NESTED, 0, &neightbl_params_rta },
1372                 { NDTA_CONFIG, "config", DT_NESTED, DF_NO_SET, &neightbl_config_rta },
1373                 { NDTA_STATS, "stats", DT_NESTED, DF_NO_SET, &neightbl_stats_rta },
1374         }
1375 };
1376 
1377 static const uc_nl_nested_spec_t netconf_msg = {
1378         .headsize = NLA_ALIGN(sizeof(struct netconfmsg)),
1379         .nattrs = 8,
1380         .attrs = {
1381                 { NETCONFA_UNSPEC, "family", DT_U8, 0, MEMBER(netconfmsg, ncm_family) },
1382                 { NETCONFA_IFINDEX, "dev", DT_NETDEV, 0, NULL },
1383                 { NETCONFA_FORWARDING, "forwarding", DT_U32, DF_NO_SET, NULL },
1384                 { NETCONFA_RP_FILTER, "rp_filter", DT_U32, DF_NO_SET, NULL },
1385                 { NETCONFA_MC_FORWARDING, "mc_forwarding", DT_U32, DF_NO_SET, NULL },
1386                 { NETCONFA_PROXY_NEIGH, "proxy_neigh", DT_U32, DF_NO_SET, NULL },
1387                 { NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, "ignore_routes_with_linkdown", DT_U32, DF_NO_SET, NULL },
1388                 { NETCONFA_INPUT, "input", DT_U32, DF_NO_SET, NULL },
1389         }
1390 };
1391 
1392 
1393 static bool
1394 nla_check_len(struct nlattr *nla, size_t sz)
1395 {
1396         return (nla && nla_len(nla) >= (ssize_t)sz);
1397 }
1398 
1399 static bool
1400 nla_parse_error(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *v, const char *msg)
1401 {
1402         char *s;
1403 
1404         s = ucv_to_string(vm, v);
1405 
1406         set_error(NLE_INVAL, "%s `%s` has invalid value `%s`: %s",
1407                 spec->attr ? "attribute" : "field",
1408                 spec->key,
1409                 s,
1410                 msg);
1411 
1412         free(s);
1413 
1414         return false;
1415 }
1416 
1417 static void
1418 uc_nl_put_struct_member(char *base, const void *offset, size_t datalen, void *data)
1419 {
1420         memcpy(base + (uintptr_t)offset, data, datalen);
1421 }
1422 
1423 static void
1424 uc_nl_put_struct_member_u8(char *base, const void *offset, uint8_t u8)
1425 {
1426         base[(uintptr_t)offset] = u8;
1427 }
1428 
1429 static void
1430 uc_nl_put_struct_member_u16(char *base, const void *offset, uint16_t u16)
1431 {
1432         uc_nl_put_struct_member(base, offset, sizeof(u16), &u16);
1433 }
1434 
1435 static void
1436 uc_nl_put_struct_member_u32(char *base, const void *offset, uint32_t u32)
1437 {
1438         uc_nl_put_struct_member(base, offset, sizeof(u32), &u32);
1439 }
1440 
1441 static void *
1442 uc_nl_get_struct_member(char *base, const void *offset, size_t datalen, void *data)
1443 {
1444         memcpy(data, base + (uintptr_t)offset, datalen);
1445 
1446         return data;
1447 }
1448 
1449 static uint8_t
1450 uc_nl_get_struct_member_u8(char *base, const void *offset)
1451 {
1452         return (uint8_t)base[(uintptr_t)offset];
1453 }
1454 
1455 static uint16_t
1456 uc_nl_get_struct_member_u16(char *base, const void *offset)
1457 {
1458         uint16_t u16;
1459 
1460         uc_nl_get_struct_member(base, offset, sizeof(u16), &u16);
1461 
1462         return u16;
1463 }
1464 
1465 static uint32_t
1466 uc_nl_get_struct_member_u32(char *base, const void *offset)
1467 {
1468         uint32_t u32;
1469 
1470         uc_nl_get_struct_member(base, offset, sizeof(u32), &u32);
1471 
1472         return u32;
1473 }
1474 
1475 static uint64_t
1476 uc_nl_get_struct_member_u64(char *base, const void *offset)
1477 {
1478         uint64_t u64;
1479 
1480         uc_nl_get_struct_member(base, offset, sizeof(u64), &u64);
1481 
1482         return u64;
1483 }
1484 
1485 static bool
1486 uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx);
1487 
1488 static uc_value_t *
1489 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm);
1490 
1491 static bool
1492 uc_nl_convert_attrs(struct nl_msg *msg, void *buf, size_t buflen, size_t headsize, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj)
1493 {
1494         size_t i, maxattr = 0, structlen = headsize;
1495         struct nlattr **tb, *nla, *nla_nest;
1496         uc_value_t *v, *arr;
1497         int rem;
1498 
1499         for (i = 0; i < nattrs; i++)
1500                 if (attrs[i].attr > maxattr)
1501                         maxattr = attrs[i].attr;
1502 
1503         tb = calloc(maxattr + 1, sizeof(struct nlattr *));
1504 
1505         if (!tb)
1506                 return false;
1507 
1508         if (buflen > headsize) {
1509                 if (maxattr)
1510                         nla_parse(tb, maxattr, buf + headsize, buflen - headsize, NULL);
1511         }
1512         else {
1513                 structlen = buflen;
1514         }
1515 
1516         for (i = 0; i < nattrs; i++) {
1517                 if (attrs[i].attr == 0 && (uintptr_t)attrs[i].auxdata >= structlen)
1518                         continue;
1519 
1520                 if (attrs[i].attr != 0 && !tb[attrs[i].attr])
1521                         continue;
1522 
1523                 if (attrs[i].flags & DF_NO_GET)
1524                         continue;
1525 
1526                 if (attrs[i].flags & DF_MULTIPLE) {
1527                         /* can't happen, but needed to nudge clang-analyzer */
1528                         if (!tb[attrs[i].attr])
1529                                 continue;
1530 
1531                         arr = ucv_array_new(vm);
1532                         nla_nest = tb[attrs[i].attr];
1533 
1534                         nla_for_each_attr(nla, nla_data(nla_nest), nla_len(nla_nest), rem) {
1535                                 if (attrs[i].auxdata && nla_type(nla) != (intptr_t)attrs[i].auxdata)
1536                                         continue;
1537 
1538                                 tb[attrs[i].attr] = nla;
1539 
1540                                 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm);
1541 
1542                                 if (!v)
1543                                         continue;
1544 
1545                                 ucv_array_push(arr, v);
1546                         }
1547 
1548                         if (!ucv_array_length(arr)) {
1549                                 ucv_put(arr);
1550 
1551                                 continue;
1552                         }
1553 
1554                         v = arr;
1555                 }
1556                 else {
1557                         v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm);
1558 
1559                         if (!v)
1560                                 continue;
1561                 }
1562 
1563                 ucv_object_add(obj, attrs[i].key, v);
1564         }
1565 
1566         free(tb);
1567 
1568         return true;
1569 }
1570 
1571 static bool
1572 uc_nl_parse_attrs(struct nl_msg *msg, char *base, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj)
1573 {
1574         struct nlattr *nla_nest = NULL;
1575         size_t i, j, idx;
1576         uc_value_t *v;
1577         bool exists;
1578 
1579         for (i = 0; i < nattrs; i++) {
1580                 v = ucv_object_get(obj, attrs[i].key, &exists);
1581 
1582                 if (!exists)
1583                         continue;
1584 
1585                 if (attrs[i].flags & DF_MULTIPLE) {
1586                         if (!(attrs[i].flags & DF_FLAT))
1587                                 nla_nest = nla_nest_start(msg, attrs[i].attr);
1588 
1589                         if (ucv_type(v) == UC_ARRAY) {
1590                                 for (j = 0; j < ucv_array_length(v); j++) {
1591                                         if (attrs[i].flags & DF_FLAT)
1592                                                 idx = attrs[i].attr;
1593                                         else if (attrs[i].auxdata)
1594                                                 idx = (uintptr_t)attrs[i].auxdata;
1595                                         else
1596                                                 idx = j;
1597 
1598                                         if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, ucv_array_get(v, j), idx))
1599                                                 return false;
1600                                 }
1601                         }
1602                         else {
1603                                 if (attrs[i].flags & DF_FLAT)
1604                                         idx = attrs[i].attr;
1605                                 else if (attrs[i].auxdata)
1606                                         idx = (uintptr_t)attrs[i].auxdata;
1607                                 else
1608                                         idx = 0;
1609 
1610                                 if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, idx))
1611                                         return false;
1612                         }
1613 
1614                         if (nla_nest)
1615                                 nla_nest_end(msg, nla_nest);
1616                 }
1617                 else if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, 0)) {
1618                         return false;
1619                 }
1620         }
1621 
1622         return true;
1623 }
1624 
1625 static bool
1626 uc_nl_parse_rta_nexthop(struct nl_msg *msg, uc_vm_t *vm, uc_value_t *val)
1627 {
1628         struct { uint16_t family; char addr[sizeof(struct in6_addr)]; } via;
1629         struct nlmsghdr *hdr = nlmsg_hdr(msg);
1630         struct rtmsg *rtm = NLMSG_DATA(hdr);
1631         struct nlattr *rta_gateway;
1632         struct rtnexthop *rtnh;
1633         uc_nl_cidr_t cidr = { 0 };
1634         uc_value_t *v;
1635         uint32_t u;
1636         int aflen;
1637         char *s;
1638 
1639         if (ucv_type(val) != UC_OBJECT)
1640                 return false;
1641 
1642         if (!uc_nl_parse_cidr(vm, ucv_object_get(val, "via", NULL), &cidr))
1643                 return false;
1644 
1645         aflen = (cidr.family == AF_INET6 ? sizeof(cidr.addr.in6) : sizeof(cidr.addr.in));
1646 
1647         if (cidr.mask != (aflen * 8))
1648                 return false;
1649 
1650         rta_gateway = nla_reserve(msg, RTA_GATEWAY, sizeof(*rtnh));
1651 
1652         rtnh = nla_data(rta_gateway);
1653         rtnh->rtnh_len = sizeof(*rtnh);
1654 
1655         if (rtm->rtm_family == AF_UNSPEC)
1656                 rtm->rtm_family = cidr.family;
1657 
1658         if (cidr.family == rtm->rtm_family) {
1659                 nla_put(msg, RTA_GATEWAY, aflen, &cidr.addr.in6);
1660                 rtnh->rtnh_len += nla_total_size(aflen);
1661         }
1662         else {
1663                 via.family = cidr.family;
1664                 memcpy(via.addr, &cidr.addr.in6, aflen);
1665                 nla_put(msg, RTA_VIA, sizeof(via.family) + aflen, &via);
1666                 rtnh->rtnh_len += nla_total_size(sizeof(via.family) + aflen);
1667         }
1668 
1669         v = ucv_object_get(val, "dev", NULL);
1670         s = ucv_string_get(v);
1671 
1672         if (s) {
1673                 rtnh->rtnh_ifindex = if_nametoindex(s);
1674 
1675                 if (rtnh->rtnh_ifindex == 0)
1676                         return false;
1677         }
1678 
1679         v = ucv_object_get(val, "weight", NULL);
1680 
1681         if (v) {
1682                 if (!uc_nl_parse_u32(v, &u) || u == 0 || u > 256)
1683                         return false;
1684 
1685                 rtnh->rtnh_hops = u - 1;
1686         }
1687 
1688         if (ucv_is_truish(ucv_object_get(val, "onlink", NULL)))
1689                 rtnh->rtnh_flags |= RTNH_F_ONLINK;
1690 
1691         v = ucv_object_get(val, "realm", NULL);
1692 
1693         if (v) {
1694                 if (!uc_nl_parse_u32(v, &u))
1695                         return false;
1696 
1697                 nla_put_u32(msg, RTA_FLOW, u);
1698                 rtnh->rtnh_len += nla_total_size(sizeof(uint32_t));
1699         }
1700 
1701         v = ucv_object_get(val, "as", NULL);
1702 
1703         if (v) {
1704                 if (!uc_nl_parse_cidr(vm, v, &cidr) || cidr.family != rtm->rtm_family)
1705                         return false;
1706 
1707                 if (cidr.mask != cidr.bitlen)
1708                         return false;
1709 
1710                 nla_put(msg, RTA_NEWDST, cidr.alen, &cidr.addr.in6);
1711                 rtnh->rtnh_len += nla_total_size(cidr.alen);
1712         }
1713 
1714         /* XXX: nla_nest_end(rta_gateway) ? */
1715 
1716         return true;
1717 }
1718 
1719 static bool
1720 uc_nl_parse_rta_multipath(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
1721 {
1722         struct nlattr *rta_multipath = nla_nest_start(msg, spec->attr);
1723         size_t i;
1724 
1725         for (i = 0; i < ucv_array_length(val); i++)
1726                 if (!uc_nl_parse_rta_nexthop(msg, vm, ucv_array_get(val, i)))
1727                         return false;
1728 
1729         nla_nest_end(msg, rta_multipath);
1730 
1731         return true;
1732 }
1733 
1734 static uc_value_t *
1735 uc_nl_convert_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm);
1736 
1737 static uc_value_t *
1738 uc_nl_convert_rta_multipath(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1739 {
1740         uc_nl_attr_spec_t encap_spec = { .attr = RTA_ENCAP };
1741         struct rtnexthop *nh = nla_data(tb[spec->attr]);
1742         struct nlattr *multipath_tb[RTA_MAX + 1];
1743         size_t len = nla_len(tb[spec->attr]);
1744         uc_value_t *nh_obj, *nh_arr;
1745         char buf[INET6_ADDRSTRLEN];
1746         struct rtvia *via;
1747         int af;
1748 
1749         nh_arr = ucv_array_new(vm);
1750 
1751         while (len >= sizeof(*nh)) {
1752                 if ((size_t)NLA_ALIGN(nh->rtnh_len) > len)
1753                         break;
1754 
1755                 nh_obj = ucv_object_new(vm);
1756                 ucv_array_push(nh_arr, nh_obj);
1757 
1758                 nla_parse(multipath_tb, RTA_MAX, (struct nlattr *)RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh), NULL);
1759 
1760                 if (multipath_tb[RTA_GATEWAY]) {
1761                         switch (nla_len(multipath_tb[RTA_GATEWAY])) {
1762                         case 4: af = AF_INET; break;
1763                         case 16: af = AF_INET6; break;
1764                         default: af = AF_UNSPEC; break;
1765                         }
1766 
1767                         if (inet_ntop(af, nla_data(multipath_tb[RTA_GATEWAY]), buf, sizeof(buf)))
1768                                 ucv_object_add(nh_obj, "via", ucv_string_new(buf));
1769                 }
1770 
1771                 if (multipath_tb[RTA_VIA]) {
1772                         if (nla_len(multipath_tb[RTA_VIA]) > (ssize_t)sizeof(*via)) {
1773                                 via = nla_data(multipath_tb[RTA_VIA]);
1774                                 af = via->rtvia_family;
1775 
1776                                 if ((af == AF_INET &&
1777                                      nla_len(multipath_tb[RTA_VIA]) == sizeof(*via) + sizeof(struct in_addr)) ||
1778                                         (af == AF_INET6 &&
1779                                      nla_len(multipath_tb[RTA_VIA]) == sizeof(*via) + sizeof(struct in6_addr))) {
1780                                         if (inet_ntop(af, via->rtvia_addr, buf, sizeof(buf)))
1781                                                 ucv_object_add(nh_obj, "via", ucv_string_new(buf));
1782                                 }
1783                         }
1784                 }
1785 
1786                 if (if_indextoname(nh->rtnh_ifindex, buf))
1787                         ucv_object_add(nh_obj, "dev", ucv_string_new(buf));
1788 
1789                 ucv_object_add(nh_obj, "weight", ucv_int64_new(nh->rtnh_hops + 1));
1790                 ucv_object_add(nh_obj, "onlink", ucv_boolean_new(nh->rtnh_flags & RTNH_F_ONLINK));
1791 
1792                 if (multipath_tb[RTA_FLOW] && nla_len(multipath_tb[RTA_FLOW]) == sizeof(uint32_t))
1793                         ucv_object_add(nh_obj, "realm", ucv_int64_new(nla_get_u32(multipath_tb[RTA_FLOW])));
1794 
1795                 if (multipath_tb[RTA_ENCAP])
1796                         ucv_object_add(nh_obj, "encap",
1797                                 uc_nl_convert_rta_encap(&encap_spec, msg, multipath_tb, vm));
1798 
1799                 if (multipath_tb[RTA_NEWDST]) {
1800                         switch (nla_len(multipath_tb[RTA_NEWDST])) {
1801                         case 4: af = AF_INET; break;
1802                         case 16: af = AF_INET6; break;
1803                         default: af = AF_UNSPEC; break;
1804                         }
1805 
1806                         if (inet_ntop(af, nla_data(multipath_tb[RTA_NEWDST]), buf, sizeof(buf)))
1807                                 ucv_object_add(nh_obj, "as", ucv_string_new(buf));
1808                 }
1809 
1810                 len -= NLA_ALIGN(nh->rtnh_len);
1811                 nh = RTNH_NEXT(nh);
1812         }
1813 
1814         return nh_arr;
1815 }
1816 
1817 static bool
1818 parse_num(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *val, void *dst)
1819 {
1820         int64_t n = ucv_int64_get(val);
1821         uint32_t *u32;
1822         uint16_t *u16;
1823         uint8_t *u8;
1824 
1825         if (spec->flags & DF_MAX_255) {
1826                 if (n < 0 || n > 255)
1827                         return nla_parse_error(spec, vm, val, "number out of range 0-255");
1828 
1829                 u8 = dst; *u8 = n;
1830         }
1831         else if (spec->flags & DF_MAX_65535) {
1832                 if (n < 0 || n > 65535)
1833                         return nla_parse_error(spec, vm, val, "number out of range 0-65535");
1834 
1835                 u16 = dst; *u16 = n;
1836 
1837                 if (spec->flags & DF_BYTESWAP)
1838                         *u16 = htons(*u16);
1839         }
1840         else if (spec->flags & DF_MAX_16777215) {
1841                 if (n < 0 || n > 16777215)
1842                         return nla_parse_error(spec, vm, val, "number out of range 0-16777215");
1843 
1844                 u32 = dst; *u32 = n;
1845 
1846                 if (spec->flags & DF_BYTESWAP)
1847                         *u32 = htonl(*u32);
1848         }
1849         else {
1850                 if (n < 0 || n > 4294967295)
1851                         return nla_parse_error(spec, vm, val, "number out of range 0-4294967295");
1852 
1853                 u32 = dst; *u32 = n;
1854 
1855                 if (spec->flags & DF_BYTESWAP)
1856                         *u32 = htonl(*u32);
1857         }
1858 
1859         return true;
1860 }
1861 
1862 static bool
1863 uc_nl_parse_rta_numrange(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
1864 {
1865         union {
1866                 struct { uint8_t low; uint8_t high; } u8;
1867                 struct { uint16_t low; uint16_t high; } u16;
1868                 struct { uint32_t low; uint32_t high; } u32;
1869         } ranges = { 0 };
1870 
1871         void *d1, *d2;
1872         size_t len;
1873 
1874         if (ucv_array_length(val) != 2 ||
1875             ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
1876             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
1877                 return nla_parse_error(spec, vm, val, "not a two-element array of numbers");
1878 
1879         if (spec->flags & DF_MAX_255) {
1880                 len = sizeof(ranges.u8);
1881                 d1 = &ranges.u8.low;
1882                 d2 = &ranges.u8.high;
1883         }
1884         else if (spec->flags & DF_MAX_65535) {
1885                 len = sizeof(ranges.u16);
1886                 d1 = &ranges.u16.low;
1887                 d2 = &ranges.u16.high;
1888         }
1889         else {
1890                 len = sizeof(ranges.u32);
1891                 d1 = &ranges.u32.low;
1892                 d2 = &ranges.u32.high;
1893         }
1894 
1895         if (!parse_num(spec, vm, ucv_array_get(val, 0), d1) ||
1896             !parse_num(spec, vm, ucv_array_get(val, 1), d2))
1897             return false;
1898 
1899         nla_put(msg, spec->attr, len, d1);
1900 
1901         return true;
1902 }
1903 
1904 static uc_value_t *
1905 uc_nl_convert_rta_numrange(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1906 {
1907         union {
1908                 struct { uint8_t low; uint8_t high; } *u8;
1909                 struct { uint16_t low; uint16_t high; } *u16;
1910                 struct { uint32_t low; uint32_t high; } *u32;
1911         } ranges = { 0 };
1912 
1913         bool swap = (spec->flags & DF_BYTESWAP);
1914         uc_value_t *arr, *n1, *n2;
1915 
1916         if (spec->flags & DF_MAX_255) {
1917                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u8)))
1918                         return NULL;
1919 
1920                 ranges.u8 = nla_data(tb[spec->attr]);
1921                 n1 = ucv_int64_new(ranges.u8->low);
1922                 n2 = ucv_int64_new(ranges.u8->high);
1923         }
1924         else if (spec->flags & DF_MAX_65535) {
1925                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u16)))
1926                         return NULL;
1927 
1928                 ranges.u16 = nla_data(tb[spec->attr]);
1929                 n1 = ucv_int64_new(swap ? ntohs(ranges.u16->low) : ranges.u16->low);
1930                 n2 = ucv_int64_new(swap ? ntohs(ranges.u16->high) : ranges.u16->high);
1931         }
1932         else {
1933                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u32)))
1934                         return NULL;
1935 
1936                 ranges.u32 = nla_data(tb[spec->attr]);
1937                 n1 = ucv_int64_new(swap ? ntohl(ranges.u32->low) : ranges.u32->low);
1938                 n2 = ucv_int64_new(swap ? ntohl(ranges.u32->high) : ranges.u32->high);
1939         }
1940 
1941         arr = ucv_array_new(vm);
1942 
1943         ucv_array_push(arr, n1);
1944         ucv_array_push(arr, n2);
1945 
1946         return arr;
1947 }
1948 
1949 
1950 #define LINK_TYPE(name) \
1951         { #name, link_##name##_attrs, ARRAY_SIZE(link_##name##_attrs) }
1952 
1953 static const struct {
1954         const char *name;
1955         const uc_nl_attr_spec_t *attrs;
1956         size_t nattrs;
1957 } link_types[] = {
1958         LINK_TYPE(bareudp),
1959         LINK_TYPE(bond),
1960         LINK_TYPE(bond_slave),
1961         LINK_TYPE(bridge),
1962         LINK_TYPE(bridge_slave),
1963         LINK_TYPE(can),
1964         LINK_TYPE(geneve),
1965         LINK_TYPE(hsr),
1966         LINK_TYPE(ipoib),
1967         LINK_TYPE(ipvlan),
1968         LINK_TYPE(macvlan),
1969         LINK_TYPE(rmnet),
1970         LINK_TYPE(vlan),
1971         LINK_TYPE(vrf),
1972         LINK_TYPE(vxcan),
1973         LINK_TYPE(vxlan),
1974         //LINK_TYPE(xdp),
1975         //LINK_TYPE(xstats),
1976         LINK_TYPE(gre),
1977         LINK_TYPE(gretap),
1978         LINK_TYPE(erspan),
1979         LINK_TYPE(ip6gre),
1980         LINK_TYPE(ip6gretap),
1981         LINK_TYPE(ip6erspan),
1982         LINK_TYPE(ip6tnl),
1983         LINK_TYPE(ipip),
1984         LINK_TYPE(sit),
1985         LINK_TYPE(veth),
1986         LINK_TYPE(vti),
1987         LINK_TYPE(vti6),
1988         LINK_TYPE(xfrm),
1989 };
1990 
1991 static bool
1992 uc_nl_parse_rta_linkinfo(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
1993 {
1994         const uc_nl_attr_spec_t *attrs = NULL;
1995         struct nlattr *li_nla, *info_nla;
1996         size_t i, nattrs = 0;
1997         char *kind, *p;
1998         uc_value_t *k;
1999 
2000         k = ucv_object_get(val, "type", NULL);
2001         kind = ucv_string_get(k);
2002 
2003         if (!kind)
2004                 return nla_parse_error(spec, vm, val, "linkinfo does not specify kind");
2005 
2006         li_nla = nla_nest_start(msg, spec->attr);
2007 
2008         nla_put_string(msg, IFLA_INFO_KIND, kind);
2009 
2010         for (i = 0; i < ARRAY_SIZE(link_types); i++) {
2011                 if (!strcmp(link_types[i].name, kind)) {
2012                         attrs = link_types[i].attrs;
2013                         nattrs = link_types[i].nattrs;
2014                         break;
2015                 }
2016         }
2017 
2018         p = strchr(kind, '_');
2019 
2020         if (!p || strcmp(p, "_slave"))
2021                 info_nla = nla_nest_start(msg, IFLA_INFO_DATA);
2022         else
2023                 info_nla = nla_nest_start(msg, IFLA_INFO_SLAVE_DATA);
2024 
2025         if (!uc_nl_parse_attrs(msg, base, attrs, nattrs, vm, val))
2026                 return false;
2027 
2028         nla_nest_end(msg, info_nla);
2029         nla_nest_end(msg, li_nla);
2030 
2031         return true;
2032 }
2033 
2034 static uc_value_t *
2035 uc_nl_convert_rta_linkinfo_data(uc_value_t *obj, size_t attr, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2036 {
2037         const uc_nl_attr_spec_t *attrs = NULL;
2038         size_t i, nattrs = 0;
2039         uc_value_t *v;
2040         bool rv;
2041 
2042         if (!tb[attr] || nla_len(tb[attr]) < 1)
2043                 return NULL;
2044 
2045         v = ucv_string_new_length(nla_data(tb[attr]), nla_len(tb[attr]) - 1);
2046 
2047         ucv_object_add(obj, "type", v);
2048 
2049         for (i = 0; i < ARRAY_SIZE(link_types); i++) {
2050                 if (!strcmp(link_types[i].name, ucv_string_get(v))) {
2051                         attrs = link_types[i].attrs;
2052                         nattrs = link_types[i].nattrs;
2053                         break;
2054                 }
2055         }
2056 
2057         attr = (attr == IFLA_INFO_KIND) ? IFLA_INFO_DATA : IFLA_INFO_SLAVE_DATA;
2058 
2059         if (nattrs > 0 && tb[attr]) {
2060                 rv = uc_nl_convert_attrs(msg, nla_data(tb[attr]), nla_len(tb[attr]), 0, attrs, nattrs, vm, obj);
2061 
2062                 if (!rv)
2063                         return NULL;
2064         }
2065 
2066         return obj;
2067 }
2068 
2069 static uc_value_t *
2070 uc_nl_convert_rta_linkinfo(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2071 {
2072         struct nlattr *linkinfo_tb[IFLA_INFO_MAX + 1];
2073         uc_value_t *info_obj, *slave_obj;
2074 
2075         if (!tb[spec->attr])
2076                 return NULL;
2077 
2078         nla_parse(linkinfo_tb, IFLA_INFO_MAX, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), NULL);
2079 
2080         info_obj = ucv_object_new(vm);
2081 
2082         if (linkinfo_tb[IFLA_INFO_KIND]) {
2083                 if (!uc_nl_convert_rta_linkinfo_data(info_obj, IFLA_INFO_KIND, msg, linkinfo_tb, vm)) {
2084                         ucv_put(info_obj);
2085 
2086                         return NULL;
2087                 }
2088         }
2089 
2090         if (linkinfo_tb[IFLA_INFO_SLAVE_KIND]) {
2091                 slave_obj = ucv_object_new(vm);
2092 
2093                 if (!uc_nl_convert_rta_linkinfo_data(slave_obj, IFLA_INFO_SLAVE_KIND, msg, linkinfo_tb, vm)) {
2094                         ucv_put(info_obj);
2095                         ucv_put(slave_obj);
2096 
2097                         return NULL;
2098                 }
2099 
2100                 ucv_object_add(info_obj, "slave", slave_obj);
2101         }
2102 
2103         return info_obj;
2104 }
2105 
2106 static uc_value_t *
2107 uc_nl_convert_rta_bridgeid(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2108 {
2109         char buf[sizeof("ffff.ff:ff:ff:ff:ff:ff")];
2110         struct ifla_bridge_id *id;
2111 
2112         if (!nla_check_len(tb[spec->attr], sizeof(*id)))
2113                 return NULL;
2114 
2115         id = nla_data(tb[spec->attr]);
2116 
2117         snprintf(buf, sizeof(buf), "%02x%02x.%02x:%02x:%02x:%02x:%02x:%02x",
2118                 id->prio[0], id->prio[1],
2119                 id->addr[0], id->addr[1],
2120                 id->addr[2], id->addr[3],
2121                 id->addr[4], id->addr[5]);
2122 
2123         return ucv_string_new(buf);
2124 }
2125 
2126 static bool
2127 uc_nl_parse_rta_srh(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
2128 {
2129         uc_value_t *mode, *hmac, *segs, *seg;
2130         struct seg6_iptunnel_encap *tun;
2131         struct sr6_tlv_hmac *tlv;
2132         struct ipv6_sr_hdr *srh;
2133         size_t i, nsegs, srhlen;
2134         char *s;
2135 
2136         mode = ucv_object_get(val, "mode", NULL);
2137         hmac = ucv_object_get(val, "hmac", NULL);
2138         segs = ucv_object_get(val, "segs", NULL);
2139 
2140         if (mode != NULL &&
2141             (ucv_type(mode) != UC_INTEGER ||
2142              ucv_int64_get(mode) < 0 ||
2143              ucv_int64_get(mode) > UINT32_MAX))
2144                 return nla_parse_error(spec, vm, val, "srh mode not an integer in range 0-4294967295");
2145 
2146         if (hmac != NULL &&
2147             (ucv_type(hmac) != UC_INTEGER ||
2148              ucv_int64_get(hmac) < 0 ||
2149              ucv_int64_get(hmac) > UINT32_MAX))
2150                 return nla_parse_error(spec, vm, val, "srh hmac not an integer in range 0-4294967295");
2151 
2152         if (ucv_type(segs) != UC_ARRAY ||
2153             ucv_array_length(segs) == 0)
2154                 return nla_parse_error(spec, vm, val, "srh segs array missing or empty");
2155 
2156         nsegs = ucv_array_length(segs);
2157 
2158         if (!mode || !ucv_int64_get(mode))
2159                 nsegs++;
2160 
2161         srhlen = 8 + 16 * nsegs;
2162 
2163         if (hmac && ucv_int64_get(hmac))
2164                 srhlen += 40;
2165 
2166 
2167         tun = calloc(1, sizeof(*tun) + srhlen);
2168 
2169         if (!tun)
2170                 return nla_parse_error(spec, vm, val, "cannot allocate srh header");
2171 
2172         tun->mode = (int)ucv_int64_get(mode);
2173 
2174         srh = tun->srh;
2175         srh->hdrlen = (srhlen >> 3) - 1;
2176         srh->type = 4;
2177         srh->segments_left = nsegs - 1;
2178         srh->first_segment = nsegs - 1;
2179 
2180         if (hmac && ucv_int64_get(hmac))
2181                 srh->flags |= SR6_FLAG1_HMAC;
2182 
2183         for (i = 0; i < ucv_array_length(segs); i++) {
2184                 seg = ucv_array_get(segs, i);
2185                 s = ucv_string_get(seg);
2186 
2187                 if (!s || inet_pton(AF_INET6, s, &srh->segments[--nsegs]) != 1) {
2188                         free(tun);
2189 
2190                         return nla_parse_error(spec, vm, val, "srh segs array contains invalid IPv6 address");
2191                 }
2192         }
2193 
2194         if (hmac && ucv_int64_get(hmac)) {
2195                 tlv = (struct sr6_tlv_hmac *)((char *)srh + srhlen - 40);
2196                 tlv->tlvhdr.type = SR6_TLV_HMAC;
2197                 tlv->tlvhdr.len = 38;
2198                 tlv->hmackeyid = htonl((uint32_t)ucv_int64_get(hmac));
2199         }
2200 
2201         nla_put(msg, spec->attr, sizeof(*tun) + srhlen, tun);
2202         free(tun);
2203 
2204         return true;
2205 }
2206 
2207 static uc_value_t *
2208 uc_nl_convert_rta_srh(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2209 {
2210         char buf[INET6_ADDRSTRLEN], *p, *e;
2211         struct seg6_iptunnel_encap *tun;
2212         uc_value_t *tun_obj, *seg_arr;
2213         struct sr6_tlv_hmac *tlv;
2214         size_t i;
2215 
2216         if (!nla_check_len(tb[spec->attr], sizeof(*tun)))
2217                 return NULL;
2218 
2219         tun = nla_data(tb[spec->attr]);
2220         tun_obj = ucv_object_new(vm);
2221 
2222         ucv_object_add(tun_obj, "mode", ucv_int64_new(tun->mode));
2223 
2224         seg_arr = ucv_array_new(vm);
2225 
2226         p = (char *)tun->srh->segments;
2227         e = (char *)tun + nla_len(tb[spec->attr]);
2228 
2229         for (i = tun->srh->first_segment + 1;
2230              p + sizeof(struct in6_addr) <= e && i > 0;
2231              i--, p += sizeof(struct in6_addr)) {
2232                 if (inet_ntop(AF_INET6, p, buf, sizeof(buf)))
2233                         ucv_array_push(seg_arr, ucv_string_new(buf));
2234                 else
2235                         ucv_array_push(seg_arr, NULL);
2236         }
2237 
2238         ucv_object_add(tun_obj, "segs", seg_arr);
2239 
2240         if (sr_has_hmac(tun->srh)) {
2241                 i = ((tun->srh->hdrlen + 1) << 3) - 40;
2242                 tlv = (struct sr6_tlv_hmac *)((char *)tun->srh + i);
2243 
2244                 ucv_object_add(tun_obj, "hmac", ucv_int64_new(ntohl(tlv->hmackeyid)));
2245         }
2246 
2247         return tun_obj;
2248 }
2249 
2250 #define ENCAP_TYPE(name, type) \
2251         { #name, LWTUNNEL_ENCAP_##type, route_encap_##name##_attrs, ARRAY_SIZE(route_encap_##name##_attrs) }
2252 
2253 static const struct {
2254         const char *name;
2255         uint16_t type;
2256         const uc_nl_attr_spec_t *attrs;
2257         size_t nattrs;
2258 } encap_types[] = {
2259         ENCAP_TYPE(mpls, MPLS),
2260         ENCAP_TYPE(ip, IP),
2261         ENCAP_TYPE(ip6, IP6),
2262         ENCAP_TYPE(ila, ILA),
2263         //ENCAP_TYPE(bpf, BPF),
2264         ENCAP_TYPE(seg6, SEG6),
2265         //ENCAP_TYPE(seg6local, SEG6_LOCAL),
2266         //ENCAP_TYPE(rpl, RPL),
2267 };
2268 
2269 static bool
2270 uc_nl_parse_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
2271 {
2272         const uc_nl_attr_spec_t *attrs = NULL;
2273         struct nlattr *enc_nla;
2274         size_t i, nattrs = 0;
2275         uint16_t ntype = 0;
2276         uc_value_t *t;
2277         char *type;
2278 
2279         t = ucv_object_get(val, "type", NULL);
2280         type = ucv_string_get(t);
2281 
2282         if (!type)
2283                 return nla_parse_error(spec, vm, val, "encap does not specify type");
2284 
2285         for (i = 0; i < ARRAY_SIZE(encap_types); i++) {
2286                 if (!strcmp(encap_types[i].name, type)) {
2287                         ntype = encap_types[i].type;
2288                         attrs = encap_types[i].attrs;
2289                         nattrs = encap_types[i].nattrs;
2290                         break;
2291                 }
2292         }
2293 
2294         if (!ntype)
2295                 return nla_parse_error(spec, vm, val, "encap specifies unknown type");
2296 
2297         nla_put_u16(msg, RTA_ENCAP_TYPE, ntype);
2298 
2299         enc_nla = nla_nest_start(msg, spec->attr);
2300 
2301         if (!uc_nl_parse_attrs(msg, base, attrs, nattrs, vm, val))
2302                 return false;
2303 
2304         nla_nest_end(msg, enc_nla);
2305 
2306         return true;
2307 }
2308 
2309 static uc_value_t *
2310 uc_nl_convert_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2311 {
2312         const uc_nl_attr_spec_t *attrs = NULL;
2313         const char *name = NULL;
2314         uc_value_t *encap_obj;
2315         size_t i, nattrs = 0;
2316         bool rv;
2317 
2318         if (!tb[spec->attr] ||
2319             !nla_check_len(tb[RTA_ENCAP_TYPE], sizeof(uint16_t)))
2320                 return NULL;
2321 
2322         for (i = 0; i < ARRAY_SIZE(encap_types); i++) {
2323                 if (encap_types[i].type != nla_get_u16(tb[RTA_ENCAP_TYPE]))
2324                         continue;
2325 
2326                 name = encap_types[i].name;
2327                 attrs = encap_types[i].attrs;
2328                 nattrs = encap_types[i].nattrs;
2329 
2330                 break;
2331         }
2332 
2333         if (!name)
2334                 return NULL;
2335 
2336         encap_obj = ucv_object_new(vm);
2337 
2338         rv = uc_nl_convert_attrs(msg,
2339                 nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), 0,
2340                 attrs, nattrs, vm, encap_obj);
2341 
2342         if (!rv) {
2343                 ucv_put(encap_obj);
2344 
2345                 return NULL;
2346         }
2347 
2348         ucv_object_add(encap_obj, "type", ucv_string_new(name));
2349 
2350         return encap_obj;
2351 }
2352 
2353 #define IPOPTS_TYPE(name, type, multiple) \
2354         { #name, LWTUNNEL_IP_OPTS_##type, multiple, lwtipopt_##name##_attrs, ARRAY_SIZE(lwtipopt_##name##_attrs) }
2355 
2356 static const struct {
2357         const char *name;
2358         uint16_t type;
2359         bool multiple;
2360         const uc_nl_attr_spec_t *attrs;
2361         size_t nattrs;
2362 } lwtipopt_types[] = {
2363         IPOPTS_TYPE(erspan, ERSPAN, false),
2364         IPOPTS_TYPE(geneve, GENEVE, true),
2365         IPOPTS_TYPE(vxlan, VXLAN, false),
2366 };
2367 
2368 static bool
2369 uc_nl_parse_rta_ipopts(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
2370 {
2371         const uc_nl_attr_spec_t *attrs = NULL;
2372         struct nlattr *opt_nla, *type_nla;
2373         bool exists, multiple = false;
2374         size_t i, j, nattrs = 0;
2375         uint16_t ntype = 0;
2376         uc_value_t *item;
2377 
2378         ucv_object_foreach(val, type, v) {
2379                 for (i = 0; i < ARRAY_SIZE(lwtipopt_types); i++) {
2380                         if (!strcmp(lwtipopt_types[i].name, type)) {
2381                                 val = v;
2382                                 ntype = lwtipopt_types[i].type;
2383                                 attrs = lwtipopt_types[i].attrs;
2384                                 nattrs = lwtipopt_types[i].nattrs;
2385                                 multiple = lwtipopt_types[i].multiple;
2386                                 break;
2387                         }
2388                 }
2389         }
2390 
2391         if (!ntype)
2392                 return nla_parse_error(spec, vm, val, "unknown IP options type specified");
2393 
2394         opt_nla = nla_nest_start(msg, spec->attr);
2395 
2396         j = 0;
2397         item = (ucv_type(val) == UC_ARRAY) ? ucv_array_get(val, j++) : val;
2398 
2399         while (true) {
2400                 type_nla = nla_nest_start(msg, ntype);
2401 
2402                 for (i = 0; i < nattrs; i++) {
2403                         v = ucv_object_get(item, attrs[i].key, &exists);
2404 
2405                         if (!exists)
2406                                 continue;
2407 
2408                         if (!uc_nl_parse_attr(&attrs[i], msg, nla_data(type_nla), vm, v, 0))
2409                                 return false;
2410                 }
2411 
2412                 nla_nest_end(msg, type_nla);
2413 
2414                 if (!multiple || ucv_type(val) != UC_ARRAY || j >= ucv_array_length(val))
2415                         break;
2416 
2417                 item = ucv_array_get(val, j++);
2418         }
2419 
2420         nla_nest_end(msg, opt_nla);
2421 
2422         return true;
2423 }
2424 
2425 static uc_value_t *
2426 uc_nl_convert_rta_ipopts(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2427 {
2428         struct nlattr *opt_tb[LWTUNNEL_IP_OPTS_MAX + 1];
2429         const uc_nl_attr_spec_t *attrs = NULL;
2430         uc_value_t *opt_obj, *type_obj;
2431         const char *name = NULL;
2432         size_t i, nattrs = 0;
2433         uint16_t type = 0;
2434         bool rv;
2435 
2436         if (!tb[spec->attr] ||
2437                 !nla_parse(opt_tb, LWTUNNEL_IP_OPTS_MAX, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), NULL))
2438                 return NULL;
2439 
2440         for (i = 0; i < ARRAY_SIZE(lwtipopt_types); i++) {
2441                 if (!opt_tb[lwtipopt_types[i].type])
2442                         continue;
2443 
2444                 type = lwtipopt_types[i].type;
2445                 name = lwtipopt_types[i].name;
2446                 attrs = lwtipopt_types[i].attrs;
2447                 nattrs = lwtipopt_types[i].nattrs;
2448 
2449                 break;
2450         }
2451 
2452         if (!name)
2453                 return NULL;
2454 
2455         type_obj = ucv_object_new(vm);
2456 
2457         rv = uc_nl_convert_attrs(msg,
2458                 nla_data(opt_tb[type]), nla_len(opt_tb[type]), 0,
2459                 attrs, nattrs, vm, type_obj);
2460 
2461         if (!rv) {
2462                 ucv_put(type_obj);
2463 
2464                 return NULL;
2465         }
2466 
2467         opt_obj = ucv_object_new(vm);
2468 
2469         ucv_object_add(opt_obj, name, type_obj);
2470 
2471         return opt_obj;
2472 }
2473 
2474 static bool
2475 uc_nl_parse_rta_afspec(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
2476 {
2477         struct rtgenmsg *rtg = nlmsg_data(nlmsg_hdr(msg));
2478         struct bridge_vlan_info vinfo = { 0 };
2479         uc_value_t *vlans, *vlan, *vv;
2480         struct nlattr *nla, *af_nla;
2481         uint32_t num;
2482         size_t i;
2483 
2484         nla = nla_reserve(msg, spec->attr, 0);
2485 
2486         ucv_object_foreach(val, type, v) {
2487                 if (!strcmp(type, "bridge")) {
2488                         if (rtg->rtgen_family == AF_UNSPEC)
2489                                 rtg->rtgen_family = AF_BRIDGE;
2490 
2491                         vv = ucv_object_get(v, "bridge_flags", NULL);
2492 
2493                         if (vv) {
2494                                 if (!uc_nl_parse_u32(vv, &num) || num > 0xffff)
2495                                         return nla_parse_error(spec, vm, vv, "field bridge.bridge_flags not an integer or out of range 0-65535");
2496 
2497                                 nla_put_u16(msg, IFLA_BRIDGE_FLAGS, num);
2498                         }
2499 
2500                         vv = ucv_object_get(v, "bridge_mode", NULL);
2501 
2502                         if (vv) {
2503                                 if (!uc_nl_parse_u32(vv, &num) || num > 0xffff)
2504                                         return nla_parse_error(spec, vm, vv, "field bridge.bridge_mode not an integer or out of range 0-65535");
2505 
2506                                 nla_put_u16(msg, IFLA_BRIDGE_MODE, num);
2507                         }
2508 
2509                         vlans = ucv_object_get(v, "bridge_vlan_info", NULL);
2510 
2511                         for (vlan = (ucv_type(vlans) == UC_ARRAY) ? ucv_array_get(vlans, 0) : vlans, i = 0;
2512                              ucv_type(vlan) == UC_OBJECT;
2513                              vlan = (ucv_type(vlans) == UC_ARRAY) ? ucv_array_get(vlans, ++i) : NULL) {
2514 
2515                                 vinfo.vid = 0;
2516                                 vinfo.flags = 0;
2517 
2518                                 vv = ucv_object_get(vlan, "flags", NULL);
2519 
2520                                 if (vv) {
2521                                         if (!uc_nl_parse_u32(vv, &num) || num > 0xffff)
2522                                                 return nla_parse_error(spec, vm, vv, "field bridge.bridge_vlan_info.flags not an integer or out of range 0-65535");
2523 
2524                                         vinfo.flags = num;
2525                                 }
2526 
2527                                 vv = ucv_object_get(vlan, "vid", NULL);
2528 
2529                                 if (!uc_nl_parse_u32(vv, &num) || num > 0xfff)
2530                                         return nla_parse_error(spec, vm, vv, "field bridge.bridge_vlan_info.vid not an integer or out of range 0-4095");
2531 
2532                                 vinfo.vid = num;
2533 
2534                                 vv = ucv_object_get(vlan, "vid_end", NULL);
2535 
2536                                 if (vv) {
2537                                         if (!uc_nl_parse_u32(vv, &num) || num > 0xfff)
2538                                                 return nla_parse_error(spec, vm, vv, "field bridge.bridge_vlan_info.vid_end not an integer or out of range 0-4095");
2539 
2540                                         vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_END;
2541                                         vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
2542                                         nla_put(msg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
2543 
2544                                         vinfo.vid = num;
2545                                         vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
2546                                         vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
2547                                         nla_put(msg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
2548                                 }
2549                                 else {
2550                                         vinfo.flags &= ~(BRIDGE_VLAN_INFO_RANGE_BEGIN|BRIDGE_VLAN_INFO_RANGE_END);
2551                                         nla_put(msg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo);
2552                                 }
2553                         }
2554                 }
2555                 else if (!strcmp(type, "inet")) {
2556                         af_nla = nla_reserve(msg, AF_INET, link_attrs_af_spec_inet_rta.headsize);
2557 
2558                         if (!uc_nl_parse_attrs(msg, nla_data(af_nla),
2559                                                link_attrs_af_spec_inet_rta.attrs,
2560                                                link_attrs_af_spec_inet_rta.nattrs,
2561                                                vm, v))
2562                                 return false;
2563 
2564                         nla_nest_end(msg, af_nla);
2565                 }
2566                 else if (!strcmp(type, "inet6")) {
2567                         af_nla = nla_reserve(msg, AF_INET6, link_attrs_af_spec_inet6_rta.headsize);
2568 
2569                         if (!uc_nl_parse_attrs(msg, nla_data(af_nla),
2570                                                link_attrs_af_spec_inet6_rta.attrs,
2571                                                link_attrs_af_spec_inet6_rta.nattrs,
2572                                                vm, v))
2573                                 return false;
2574 
2575                         nla_nest_end(msg, af_nla);
2576                 }
2577                 else {
2578                         return nla_parse_error(spec, vm, val, "unknown address family specified");
2579                 }
2580         }
2581 
2582         nla_nest_end(msg, nla);
2583 
2584         return true;
2585 }
2586 
2587 static uc_value_t *
2588 uc_nl_convert_rta_afspec(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2589 {
2590         struct rtgenmsg *rtg = nlmsg_data(nlmsg_hdr(msg));
2591         uc_value_t *obj, *bridge, *vlans = NULL, *vlan;
2592         struct bridge_vlan_info vinfo;
2593         struct nlattr *nla;
2594         uint16_t vid = 0;
2595         int rem;
2596 
2597         if (!tb[spec->attr])
2598                 return NULL;
2599 
2600         obj = ucv_object_new(vm);
2601 
2602         if (rtg->rtgen_family == AF_BRIDGE) {
2603                 bridge = ucv_object_new(vm);
2604 
2605                 nla_for_each_attr(nla, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), rem) {
2606                         switch (nla_type(nla)) {
2607                         case IFLA_BRIDGE_FLAGS:
2608                                 if (nla_check_len(nla, sizeof(uint16_t)))
2609                                         ucv_object_add(bridge, "bridge_flags", ucv_uint64_new(nla_get_u16(nla)));
2610 
2611                                 break;
2612 
2613                         case IFLA_BRIDGE_MODE:
2614                                 if (nla_check_len(nla, sizeof(uint16_t)))
2615                                         ucv_object_add(bridge, "bridge_mode", ucv_uint64_new(nla_get_u16(nla)));
2616 
2617                                 break;
2618 
2619                         case IFLA_BRIDGE_VLAN_INFO:
2620                                 if (nla_check_len(nla, sizeof(vinfo))) {
2621                                         memcpy(&vinfo, nla_data(nla), sizeof(vinfo));
2622 
2623                                         if (!(vinfo.flags & BRIDGE_VLAN_INFO_RANGE_END))
2624                                                 vid = vinfo.vid;
2625 
2626                                         if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
2627                                                 continue;
2628 
2629                                         if (!vlans) {
2630                                                 vlans = ucv_array_new(vm);
2631                                                 ucv_object_add(bridge, "bridge_vlan_info", vlans);
2632                                         }
2633 
2634                                         vlan = ucv_object_new(vm);
2635 
2636                                         ucv_object_add(vlan, "vid", ucv_uint64_new(vid));
2637 
2638                                         if (vid != vinfo.vid)
2639                                                 ucv_object_add(vlan, "vid_end", ucv_uint64_new(vinfo.vid));
2640 
2641                                         ucv_object_add(vlan, "flags", ucv_uint64_new(vinfo.flags & ~BRIDGE_VLAN_INFO_RANGE_END));
2642 
2643                                         ucv_array_push(vlans, vlan);
2644                                 }
2645 
2646                                 break;
2647                         }
2648                 }
2649 
2650                 ucv_object_add(obj, "bridge", bridge);
2651         }
2652         else {
2653                 if (!uc_nl_convert_attrs(msg, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]),
2654                                          link_attrs_af_spec_rta.headsize, link_attrs_af_spec_rta.attrs,
2655                                          link_attrs_af_spec_rta.nattrs, vm, obj)) {
2656                         ucv_put(obj);
2657 
2658                         return NULL;
2659                 }
2660         }
2661 
2662         return obj;
2663 }
2664 
2665 static bool
2666 uc_nl_parse_rta_u32_or_member(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
2667 {
2668         uint32_t u32;
2669 
2670         if (!uc_nl_parse_u32(val, &u32))
2671                 return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
2672 
2673         if (spec->flags & DF_MAX_255) {
2674                 if (u32 <= 255) {
2675                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2676 
2677                         return true;
2678                 }
2679 
2680                 uc_nl_put_struct_member_u8(base, spec->auxdata, 0);
2681         }
2682         else if (spec->flags & DF_MAX_65535) {
2683                 if (u32 <= 65535) {
2684                         uc_nl_put_struct_member_u16(base, spec->auxdata,
2685                                 (spec->flags & DF_BYTESWAP) ? htons((uint16_t)u32) : (uint16_t)u32);
2686 
2687                         return true;
2688                 }
2689 
2690                 uc_nl_put_struct_member_u16(base, spec->auxdata, 0);
2691         }
2692         else if (spec->flags & DF_MAX_16777215) {
2693                 if (u32 <= 16777215) {
2694                         uc_nl_put_struct_member_u32(base, spec->auxdata,
2695                                 (spec->flags & DF_BYTESWAP) ? htonl(u32) : u32);
2696 
2697                         return true;
2698                 }
2699 
2700                 uc_nl_put_struct_member_u32(base, spec->auxdata, 0);
2701         }
2702 
2703         nla_put_u32(msg, spec->attr,
2704                 (spec->flags & DF_BYTESWAP) ? htonl(u32) : u32);
2705 
2706         return true;
2707 }
2708 
2709 static uc_value_t *
2710 uc_nl_convert_rta_u32_or_member(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm)
2711 {
2712         uint32_t u32 = 0;
2713 
2714         if (nla_check_len(tb[spec->attr], sizeof(uint32_t))) {
2715                 if (spec->flags & DF_BYTESWAP)
2716                         u32 = ntohl(nla_get_u32(tb[spec->attr]));
2717                 else
2718                         u32 = nla_get_u32(tb[spec->attr]);
2719         }
2720         else if (spec->flags & DF_MAX_255) {
2721                 u32 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2722         }
2723         else if (spec->flags & DF_MAX_65535) {
2724                 if (spec->flags & DF_BYTESWAP)
2725                         u32 = ntohs(uc_nl_get_struct_member_u16(base, spec->auxdata));
2726                 else
2727                         u32 = uc_nl_get_struct_member_u16(base, spec->auxdata);
2728         }
2729         else if (spec->flags & DF_MAX_16777215) {
2730                 if (spec->flags & DF_BYTESWAP)
2731                         u32 = ntohl(uc_nl_get_struct_member_u32(base, spec->auxdata));
2732                 else
2733                         u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2734         }
2735         else {
2736                 return NULL;
2737         }
2738 
2739         return ucv_uint64_new(u32);
2740 }
2741 
2742 static bool
2743 uc_nl_parse_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
2744 {
2745         const uc_nl_nested_spec_t *nest = spec->auxdata;
2746         struct nlattr *nested_nla;
2747 
2748         nested_nla = nla_reserve(msg, spec->attr, nest->headsize);
2749 
2750         if (!nested_nla)
2751                 return false;
2752 
2753         /* nla_reserve() only zeroes the padding, not the payload, so struct
2754          * headers would otherwise carry heap garbage into the kernel */
2755         if (nest->headsize)
2756                 memset(nla_data(nested_nla), 0, nest->headsize);
2757 
2758         if (!uc_nl_parse_attrs(msg, nla_data(nested_nla), nest->attrs, nest->nattrs, vm, val))
2759                 return false;
2760 
2761         nla_nest_end(msg, nested_nla);
2762 
2763         return true;
2764 }
2765 
2766 static uc_value_t *
2767 uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2768 {
2769         const uc_nl_nested_spec_t *nest = spec->auxdata;
2770         uc_value_t *nested_obj;
2771         bool rv;
2772 
2773         nested_obj = ucv_object_new(vm);
2774 
2775         rv = uc_nl_convert_attrs(msg,
2776                 nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), nest->headsize,
2777                 nest->attrs, nest->nattrs,
2778                 vm, nested_obj);
2779 
2780         if (!rv) {
2781                 ucv_put(nested_obj);
2782 
2783                 return NULL;
2784         }
2785 
2786         return nested_obj;
2787 }
2788 
2789 
2790 static bool
2791 uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx)
2792 {
2793         uc_nl_cidr_t cidr = { 0 };
2794         struct ether_addr *ea;
2795         struct rtgenmsg *rtg;
2796         uint64_t u64;
2797         uint32_t u32;
2798         uint16_t u16;
2799         size_t attr;
2800         char *s;
2801 
2802         if (spec->flags & DF_MULTIPLE)
2803                 attr = idx;
2804         else
2805                 attr = spec->attr;
2806 
2807         switch (spec->type) {
2808         case DT_U8:
2809                 if (!uc_nl_parse_u32(val, &u32) || u32 > 255)
2810                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-255");
2811 
2812                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2813                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2814 
2815                 if (spec->attr == 0)
2816                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2817                 else
2818                         nla_put_u8(msg, attr, u32);
2819 
2820                 break;
2821 
2822         case DT_U16:
2823                 if (!uc_nl_parse_u32(val, &u32) || u32 > 65535)
2824                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-65535");
2825 
2826                 u16 = (uint16_t)u32;
2827 
2828                 if (spec->flags & DF_BYTESWAP)
2829                         u16 = htons(u16);
2830 
2831                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2832                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2833                 else if ((spec->flags & DF_MAX_255) && u32 > 255)
2834                         return nla_parse_error(spec, vm, val, "integer out of range 0-255");
2835 
2836                 if (spec->attr == 0)
2837                         uc_nl_put_struct_member_u16(base, spec->auxdata, u16);
2838                 else
2839                         nla_put_u16(msg, attr, u16);
2840 
2841                 break;
2842 
2843         case DT_S32:
2844         case DT_U32:
2845                 if (spec->type == DT_S32 && !uc_nl_parse_s32(val, &u32))
2846                         return nla_parse_error(spec, vm, val, "not an integer or out of range -2147483648-2147483647");
2847                 else if (spec->type == DT_U32 && !uc_nl_parse_u32(val, &u32))
2848                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
2849 
2850                 if (spec->flags & DF_BYTESWAP)
2851                         u32 = htonl(u32);
2852 
2853                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2854                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2855                 else if ((spec->flags & DF_MAX_255) && u32 > 255)
2856                         return nla_parse_error(spec, vm, val, "integer out of range 0-255");
2857                 else if ((spec->flags & DF_MAX_65535) && u32 > 65535)
2858                         return nla_parse_error(spec, vm, val, "integer out of range 0-65535");
2859                 else if ((spec->flags & DF_MAX_16777215) && u32 > 16777215)
2860                         return nla_parse_error(spec, vm, val, "integer out of range 0-16777215");
2861 
2862                 if (spec->attr == 0)
2863                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2864                 else
2865                         nla_put_u32(msg, attr, u32);
2866 
2867                 break;
2868 
2869         case DT_U64:
2870                 assert(spec->attr != 0);
2871 
2872                 if (!uc_nl_parse_u64(val, &u64))
2873                         return nla_parse_error(spec, vm, val, "not an integer or negative");
2874 
2875                 if (spec->flags & DF_BYTESWAP)
2876                         u64 = htobe64(u64);
2877 
2878                 nla_put_u64(msg, attr, u64);
2879                 break;
2880 
2881         case DT_BOOL:
2882                 u32 = (uint32_t)ucv_is_truish(val);
2883 
2884                 if (spec->attr == 0)
2885                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2886                 else
2887                         nla_put_u8(msg, attr, u32);
2888 
2889                 break;
2890 
2891         case DT_FLAG:
2892                 u32 = (uint32_t)ucv_is_truish(val);
2893 
2894                 if (spec->attr == 0)
2895                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2896                 else if (u32 == 1)
2897                         nla_put_flag(msg, attr);
2898 
2899                 break;
2900 
2901         case DT_STRING:
2902                 assert(spec->attr != 0);
2903 
2904                 if (ucv_type(val) == UC_STRING) {
2905                         nla_put(msg, attr, ucv_string_length(val), ucv_string_get(val));
2906                 }
2907                 else {
2908                         s = ucv_to_string(vm, val);
2909 
2910                         if (!s)
2911                                 return nla_parse_error(spec, vm, val, "out of memory");
2912 
2913                         nla_put_string(msg, attr, s);
2914                         free(s);
2915                 }
2916 
2917                 break;
2918 
2919         case DT_NETDEV:
2920                 if (ucv_type(val) == UC_INTEGER) {
2921                         if (ucv_int64_get(val) < 0 ||
2922                             ucv_int64_get(val) > UINT32_MAX)
2923                                 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295");
2924 
2925                         u32 = (uint32_t)ucv_int64_get(val);
2926                 }
2927                 else {
2928                         s = ucv_to_string(vm, val);
2929 
2930                         if (!s)
2931                                 return nla_parse_error(spec, vm, val, "out of memory");
2932 
2933                         u32 = if_nametoindex(s);
2934 
2935                         free(s);
2936                 }
2937 
2938                 if (u32 == 0 && !(spec->flags & DF_ALLOW_NONE))
2939                         return nla_parse_error(spec, vm, val, "interface not found");
2940 
2941                 if (spec->attr == 0)
2942                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2943                 else
2944                         nla_put_u32(msg, attr, u32);
2945 
2946                 break;
2947 
2948         case DT_LLADDR:
2949                 assert(spec->attr != 0);
2950 
2951                 s = ucv_to_string(vm, val);
2952 
2953                 if (!s)
2954                         return nla_parse_error(spec, vm, val, "out of memory");
2955 
2956                 ea = ether_aton(s);
2957 
2958                 free(s);
2959 
2960                 if (!ea)
2961                         return nla_parse_error(spec, vm, val, "invalid MAC address");
2962 
2963                 nla_put(msg, attr, sizeof(*ea), ea);
2964 
2965                 break;
2966 
2967         case DT_U64ADDR:
2968                 assert(spec->attr != 0);
2969 
2970                 if (ucv_type(val) == UC_INTEGER) {
2971                         u64 = ucv_uint64_get(val);
2972                 }
2973                 else {
2974                         s = ucv_to_string(vm, val);
2975 
2976                         if (!s)
2977                                 return nla_parse_error(spec, vm, val, "out of memory");
2978 
2979                         u16 = addr64_pton(s, &u64);
2980 
2981                         free(s);
2982 
2983                         if (u16 != 1)
2984                                 return nla_parse_error(spec, vm, val, "invalid address");
2985                 }
2986 
2987                 nla_put_u64(msg, attr, u64);
2988 
2989                 break;
2990 
2991         case DT_INADDR:
2992         case DT_IN6ADDR:
2993         case DT_MPLSADDR:
2994         case DT_ANYADDR:
2995                 assert(spec->attr != 0);
2996 
2997                 rtg = nlmsg_data(nlmsg_hdr(msg));
2998 
2999                 if (!uc_nl_parse_cidr(vm, val, &cidr))
3000                         return nla_parse_error(spec, vm, val, "invalid IP address");
3001 
3002                 if ((spec->type == DT_INADDR && cidr.family != AF_INET) ||
3003                     (spec->type == DT_IN6ADDR && cidr.family != AF_INET6) ||
3004                     (spec->type == DT_MPLSADDR && cidr.family != AF_MPLS))
3005                     return nla_parse_error(spec, vm, val, "wrong address family");
3006 
3007                 if (spec->flags & DF_STORE_MASK)
3008                         uc_nl_put_struct_member_u8(base, spec->auxdata, cidr.mask);
3009                 else if (cidr.mask != cidr.bitlen)
3010                         return nla_parse_error(spec, vm, val, "address range given but single address expected");
3011 
3012                 nla_put(msg, attr, cidr.alen, &cidr.addr.in6);
3013 
3014                 if ((rtg->rtgen_family == AF_UNSPEC) && (spec->flags & DF_FAMILY_HINT))
3015                         rtg->rtgen_family = cidr.family;
3016 
3017                 break;
3018 
3019         case DT_MULTIPATH:
3020                 if (!uc_nl_parse_rta_multipath(spec, msg, base, vm, val))
3021                         return nla_parse_error(spec, vm, val, "invalid nexthop data");
3022 
3023                 break;
3024 
3025         case DT_NUMRANGE:
3026                 if (!uc_nl_parse_rta_numrange(spec, msg, base, vm, val))
3027                         return false;
3028 
3029                 break;
3030 
3031         case DT_FLAGS:
3032                 if (ucv_array_length(val) == 2) {
3033                         if (ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
3034                             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
3035                                 return nla_parse_error(spec, vm, val, "flag or mask value not an integer");
3036 
3037                         if (!uc_nl_parse_u32(ucv_array_get(val, 0), &u32))
3038                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
3039 
3040                         memcpy(&u64, &u32, sizeof(u32));
3041 
3042                         if (!uc_nl_parse_u32(ucv_array_get(val, 1), &u32))
3043                                 return nla_parse_error(spec, vm, val, "mask value not an integer or out of range 0-4294967295");
3044 
3045                         memcpy((char *)&u64 + sizeof(u32), &u32, sizeof(u32));
3046                 }
3047                 else if (ucv_type(val) == UC_INTEGER) {
3048                         if (!uc_nl_parse_u32(val, &u32))
3049                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
3050 
3051                         memcpy(&u64, &u32, sizeof(u32));
3052                         memset((char *)&u64 + sizeof(u32), 0xff, sizeof(u32));
3053                 }
3054                 else {
3055                         return nla_parse_error(spec, vm, val, "value neither an array of flags, mask nor an integer");
3056                 }
3057 
3058                 if (spec->attr == 0)
3059                         uc_nl_put_struct_member(base, spec->auxdata, sizeof(u64), &u64);
3060                 else
3061                         nla_put_u64(msg, attr, u64);
3062 
3063                 break;
3064 
3065         case DT_LINKINFO:
3066                 if (!uc_nl_parse_rta_linkinfo(spec, msg, base, vm, val))
3067                         return false;
3068 
3069                 break;
3070 
3071         case DT_SRH:
3072                 if (!uc_nl_parse_rta_srh(spec, msg, base, vm, val))
3073                         return false;
3074 
3075                 break;
3076 
3077         case DT_ENCAP:
3078                 if (!uc_nl_parse_rta_encap(spec, msg, base, vm, val))
3079                         return false;
3080 
3081                 break;
3082 
3083         case DT_IPOPTS:
3084                 if (!uc_nl_parse_rta_ipopts(spec, msg, base, vm, val))
3085                         return false;
3086 
3087                 break;
3088 
3089         case DT_AFSPEC:
3090                 if (!uc_nl_parse_rta_afspec(spec, msg, base, vm, val))
3091                         return false;
3092 
3093                 break;
3094 
3095         case DT_U32_OR_MEMBER:
3096                 if (!uc_nl_parse_rta_u32_or_member(spec, msg, base, vm, val))
3097                         return false;
3098 
3099                 break;
3100 
3101         case DT_NESTED:
3102                 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val))
3103                         return false;
3104 
3105                 break;
3106 
3107         default:
3108                 assert(0);
3109         }
3110 
3111         return true;
3112 }
3113 
3114 static uc_value_t *
3115 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm)
3116 {
3117         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 };
3118         struct { uint32_t flags; uint32_t mask; } flags;
3119         char buf[sizeof(struct mpls_label) * 16];
3120         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3121         struct rtgenmsg *rtg = nlmsg_data(hdr);
3122         struct ether_addr *ea;
3123         uc_value_t *v;
3124         char *s;
3125 
3126         switch (spec->type) {
3127         case DT_U8:
3128                 if (spec->attr == 0)
3129                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
3130                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
3131                         t.u8 = nla_get_u8(tb[spec->attr]);
3132 
3133                 return ucv_uint64_new(t.u8);
3134 
3135         case DT_U16:
3136                 if (spec->attr == 0)
3137                         t.u16 = uc_nl_get_struct_member_u16(base, spec->auxdata);
3138                 else if (nla_check_len(tb[spec->attr], sizeof(t.u16)))
3139                         t.u16 = nla_get_u16(tb[spec->attr]);
3140 
3141                 if (spec->flags & DF_BYTESWAP)
3142                         t.u16 = ntohs(t.u16);
3143 
3144                 return ucv_uint64_new(t.u16);
3145 
3146         case DT_U32:
3147         case DT_S32:
3148                 if (spec->attr == 0)
3149                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
3150                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
3151                         t.u32 = nla_get_u32(tb[spec->attr]);
3152 
3153                 if (spec->flags & DF_BYTESWAP)
3154                         t.u32 = ntohl(t.u32);
3155 
3156                 if (spec->type == DT_S32)
3157                         return ucv_int64_new((int32_t)t.u32);
3158 
3159                 return ucv_uint64_new(t.u32);
3160 
3161         case DT_U64:
3162                 if (spec->attr == 0)
3163                         t.u64 = uc_nl_get_struct_member_u64(base, spec->auxdata);
3164                 else if (nla_check_len(tb[spec->attr], sizeof(t.u64)))
3165                         memcpy(&t.u64, nla_data(tb[spec->attr]), sizeof(t.u64));
3166 
3167                 return ucv_uint64_new(t.u64);
3168 
3169         case DT_BOOL:
3170                 if (spec->attr == 0)
3171                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
3172                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
3173                         t.u8 = nla_get_u8(tb[spec->attr]);
3174 
3175                 return ucv_boolean_new(t.u8 != 0);
3176 
3177         case DT_FLAG:
3178                 if (spec->attr == 0)
3179                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
3180                 else if (tb[spec->attr] != NULL)
3181                         t.u8 = 1;
3182 
3183                 return ucv_boolean_new(t.u8 != 0);
3184 
3185         case DT_STRING:
3186                 assert(spec->attr != 0);
3187 
3188                 if (!nla_check_len(tb[spec->attr], 1))
3189                         return NULL;
3190 
3191                 return ucv_string_new_length(
3192                         nla_data(tb[spec->attr]), nla_len(tb[spec->attr]) - 1);
3193 
3194         case DT_NETDEV:
3195                 if (spec->attr == 0)
3196                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
3197                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
3198                         t.u32 = nla_get_u32(tb[spec->attr]);
3199 
3200                 if (if_indextoname(t.u32, buf))
3201                         return ucv_string_new(buf);
3202                 else if (spec->flags & DF_ALLOW_NONE)
3203                         return ucv_int64_new(0);
3204 
3205                 return NULL;
3206 
3207         case DT_LLADDR:
3208                 assert(spec->attr != 0);
3209 
3210                 if (!nla_check_len(tb[spec->attr], sizeof(*ea)))
3211                         return NULL;
3212 
3213                 ea = nla_data(tb[spec->attr]);
3214 
3215                 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
3216                         ea->ether_addr_octet[0], ea->ether_addr_octet[1],
3217                         ea->ether_addr_octet[2], ea->ether_addr_octet[3],
3218                         ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
3219 
3220                 return ucv_string_new(buf);
3221 
3222         case DT_U64ADDR:
3223                 assert(spec->attr != 0);
3224 
3225                 if (!nla_check_len(tb[spec->attr], sizeof(uint64_t)) ||
3226                     !addr64_ntop(nla_data(tb[spec->attr]), buf, sizeof(buf)))
3227                         return NULL;
3228 
3229                 return ucv_string_new(buf);
3230 
3231         case DT_INADDR:
3232         case DT_IN6ADDR:
3233         case DT_MPLSADDR:
3234         case DT_ANYADDR:
3235                 assert(spec->attr != 0);
3236 
3237                 t.sz = (size_t)nla_len(tb[spec->attr]);
3238 
3239                 switch (spec->type) {
3240                 case DT_INADDR:
3241                         if (t.sz < sizeof(struct in_addr) ||
3242                             !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3243                                 return NULL;
3244 
3245                         break;
3246 
3247                 case DT_IN6ADDR:
3248                         if (t.sz < sizeof(struct in6_addr) ||
3249                             !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3250                                 return NULL;
3251 
3252                         break;
3253 
3254                 case DT_MPLSADDR:
3255                         if (t.sz < sizeof(struct mpls_label) ||
3256                             !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
3257                                 return NULL;
3258 
3259                         break;
3260 
3261                 default:
3262                         switch (rtg->rtgen_family) {
3263                         case AF_MPLS:
3264                                 if (t.sz < sizeof(struct mpls_label) ||
3265                                     !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
3266                                         return NULL;
3267 
3268                                 break;
3269 
3270                         case AF_INET6:
3271                                 if (t.sz < sizeof(struct in6_addr) ||
3272                                     !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3273                                         return NULL;
3274 
3275                                 break;
3276 
3277                         case AF_INET:
3278                                 if (t.sz < sizeof(struct in_addr) ||
3279                                     !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3280                                         return NULL;
3281 
3282                                 break;
3283 
3284                         default:
3285                                 return NULL;
3286                         }
3287 
3288                         break;
3289                 }
3290 
3291                 if (spec->flags & DF_STORE_MASK) {
3292                         s = buf + strlen(buf);
3293                         snprintf(s, buf + sizeof(buf) - s, "/%hhu",
3294                                 uc_nl_get_struct_member_u8(base, spec->auxdata));
3295                 }
3296 
3297                 return ucv_string_new(buf);
3298 
3299         case DT_MULTIPATH:
3300                 return uc_nl_convert_rta_multipath(spec, msg, tb, vm);
3301 
3302         case DT_NUMRANGE:
3303                 return uc_nl_convert_rta_numrange(spec, msg, tb, vm);
3304 
3305         case DT_FLAGS:
3306                 if (spec->attr == 0)
3307                         uc_nl_get_struct_member(base, spec->auxdata, sizeof(flags), &flags);
3308                 else if (nla_check_len(tb[spec->attr], sizeof(flags)))
3309                         memcpy(&flags, nla_data(tb[spec->attr]), sizeof(flags));
3310                 else
3311                         return NULL;
3312 
3313                 if (flags.mask == 0)
3314                         return ucv_uint64_new(flags.flags);
3315 
3316                 v = ucv_array_new(vm);
3317 
3318                 ucv_array_push(v, ucv_uint64_new(flags.flags));
3319                 ucv_array_push(v, ucv_uint64_new(flags.mask));
3320 
3321                 return v;
3322 
3323         case DT_LINKINFO:
3324                 return uc_nl_convert_rta_linkinfo(spec, msg, tb, vm);
3325 
3326         case DT_BRIDGEID:
3327                 return uc_nl_convert_rta_bridgeid(spec, msg, tb, vm);
3328 
3329         case DT_SRH:
3330                 return uc_nl_convert_rta_srh(spec, msg, tb, vm);
3331 
3332         case DT_ENCAP:
3333                 return uc_nl_convert_rta_encap(spec, msg, tb, vm);
3334 
3335         case DT_IPOPTS:
3336                 return uc_nl_convert_rta_ipopts(spec, msg, tb, vm);
3337 
3338         case DT_AFSPEC:
3339                 return uc_nl_convert_rta_afspec(spec, msg, tb, vm);
3340 
3341         case DT_U32_OR_MEMBER:
3342                 return uc_nl_convert_rta_u32_or_member(spec, msg, base, tb, vm);
3343 
3344         case DT_NESTED:
3345                 return uc_nl_convert_rta_nested(spec, msg, tb, vm);
3346 
3347         default:
3348                 assert(0);
3349         }
3350 
3351         return NULL;
3352 }
3353 
3354 
3355 static struct nl_sock *sock = NULL;
3356 static struct {
3357         struct nl_sock *evsock;
3358         struct uloop_fd evsock_fd;
3359         uint32_t groups[RTNL_GRPS_BITMAP_SIZE];
3360 } nl_conn;
3361 
3362 typedef enum {
3363         STATE_UNREPLIED,
3364         STATE_CONTINUE,
3365         STATE_REPLIED,
3366         STATE_ERROR
3367 } reply_state_t;
3368 
3369 typedef struct {
3370         reply_state_t state;
3371         uc_vm_t *vm;
3372         uc_value_t *res;
3373         int family;
3374         const uc_nl_nested_spec_t *spec;
3375 } request_state_t;
3376 
3377 
3378 /**
3379  * Query error information.
3380  *
3381  * Returns a string containing a description of the last occurred error or
3382  * `null` if there is no error information.
3383  *
3384  * @function module:rtnl#error
3385  *
3386  * @returns {?string}
3387  *
3388  * @example
3389  * // Trigger rtnl error
3390  * request('invalid_command', {}, {});
3391  *
3392  * // Print error (should yield error description)
3393  * print(error(), "\n");
3394  */
3395 static uc_value_t *
3396 uc_nl_error(uc_vm_t *vm, size_t nargs)
3397 {
3398         uc_stringbuf_t *buf;
3399         const char *s;
3400 
3401         if (last_error.code == 0)
3402                 return NULL;
3403 
3404         buf = ucv_stringbuf_new();
3405 
3406         if (last_error.code == NLE_FAILURE && last_error.msg) {
3407                 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
3408         }
3409         else {
3410                 s = nl_geterror(last_error.code);
3411 
3412                 ucv_stringbuf_addstr(buf, s, strlen(s));
3413 
3414                 if (last_error.msg)
3415                         ucv_stringbuf_printf(buf, ": %s", last_error.msg);
3416         }
3417 
3418         set_error(0, NULL);
3419 
3420         return ucv_stringbuf_finish(buf);
3421 }
3422 
3423 /*
3424  * route functions
3425  */
3426 
3427 static int
3428 cb_done(struct nl_msg *msg, void *arg)
3429 {
3430         request_state_t *s = arg;
3431 
3432         s->state = STATE_REPLIED;
3433 
3434         return NL_STOP;
3435 }
3436 
3437 static int
3438 cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
3439 {
3440         request_state_t *s = arg;
3441         int errnum = err->error;
3442 
3443         set_error(NLE_FAILURE, "RTNETLINK answers: %s",
3444                   strerror(errnum < 0 ? -errnum : errnum));
3445 
3446         s->state = STATE_ERROR;
3447 
3448         return NL_STOP;
3449 }
3450 
3451 static int
3452 cb_reply(struct nl_msg *msg, void *arg)
3453 {
3454         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3455         request_state_t *s = arg;
3456         uc_value_t *o;
3457         bool rv;
3458 
3459         if (RTM_FAM(hdr->nlmsg_type) != s->family)
3460                 return NL_SKIP;
3461 
3462         if (s->spec) {
3463                 if (nlmsg_attrlen(hdr, 0) < (ssize_t)s->spec->headsize)
3464                         return NL_SKIP;
3465 
3466                 o = ucv_object_new(s->vm);
3467 
3468                 rv = uc_nl_convert_attrs(msg,
3469                         nlmsg_attrdata(hdr, 0),
3470                         nlmsg_attrlen(hdr, 0),
3471                         s->spec->headsize,
3472                         s->spec->attrs, s->spec->nattrs, s->vm, o);
3473 
3474                 if (rv) {
3475                         if (hdr->nlmsg_flags & NLM_F_MULTI) {
3476                                 if (!s->res)
3477                                         s->res = ucv_array_new(s->vm);
3478 
3479                                 ucv_array_push(s->res, o);
3480                         }
3481                         else {
3482                                 s->res = o;
3483                         }
3484                 }
3485                 else {
3486                         ucv_put(o);
3487                 }
3488         }
3489 
3490         s->state = STATE_CONTINUE;
3491 
3492         return NL_SKIP;
3493 }
3494 
3495 
3496 static const struct {
3497         int family;
3498         const uc_nl_nested_spec_t *spec;
3499 } rtm_families[] = {
3500         { RTM_FAM(RTM_GETLINK), &link_msg },
3501         { RTM_FAM(RTM_GETROUTE), &route_msg },
3502         { RTM_FAM(RTM_GETNEIGH), &neigh_msg },
3503         { RTM_FAM(RTM_GETADDR), &addr_msg },
3504         { RTM_FAM(RTM_GETRULE), &rule_msg },
3505         { RTM_FAM(RTM_GETADDRLABEL), &addrlabel_msg },
3506         { RTM_FAM(RTM_GETNEIGHTBL), &neightbl_msg },
3507         { RTM_FAM(RTM_GETNETCONF), &netconf_msg },
3508 };
3509 
3510 /**
3511  * Send a netlink request.
3512  *
3513  * Sends a netlink request with the specified command, flags, and payload.
3514  *
3515  * @function module:rtnl#request
3516  *
3517  * @param {string} cmd - The netlink command to send
3518  * @param {number} flags - The netlink flags for the request
3519  * @param {*} payload - The payload data for the request
3520  *
3521  * @returns {?*} - The response data or null on error
3522  *
3523  * @example
3524  * // Send a route request
3525  * let response = request('RTM_GETROUTE', 0, { family: AF_INET });
3526  */
3527 static uc_value_t *
3528 uc_nl_request(uc_vm_t *vm, size_t nargs)
3529 {
3530         uc_value_t *cmd = uc_fn_arg(0);
3531         uc_value_t *flags = uc_fn_arg(1);
3532         uc_value_t *payload = uc_fn_arg(2);
3533         request_state_t st = { .vm = vm };
3534         uint16_t flagval = 0;
3535         struct nl_msg *msg;
3536         struct nl_cb *cb;
3537         socklen_t optlen;
3538         int enable, err;
3539         void *buf;
3540         size_t i;
3541 
3542         if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 ||
3543             (flags != NULL && ucv_type(flags) != UC_INTEGER) ||
3544             (payload != NULL && ucv_type(payload) != UC_OBJECT))
3545                 err_return(NLE_INVAL, NULL);
3546 
3547         if (flags) {
3548                 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff)
3549                         err_return(NLE_INVAL, NULL);
3550                 else
3551                         flagval = (uint16_t)ucv_int64_get(flags);
3552         }
3553 
3554         for (i = 0; i < ARRAY_SIZE(rtm_families); i++) {
3555                 if (rtm_families[i].family == RTM_FAM(ucv_int64_get(cmd))) {
3556                         st.spec = rtm_families[i].spec;
3557                         st.family = rtm_families[i].family;
3558                         break;
3559                 }
3560         }
3561 
3562         if (!sock) {
3563                 sock = nl_socket_alloc();
3564 
3565                 if (!sock)
3566                         err_return(NLE_NOMEM, NULL);
3567 
3568                 err = nl_connect(sock, NETLINK_ROUTE);
3569 
3570                 if (err != 0)
3571                         err_return(err, NULL);
3572         }
3573 
3574         optlen = sizeof(enable);
3575 
3576         if (getsockopt(sock->s_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &enable, &optlen) < 0)
3577                 enable = 0;
3578 
3579         if (!!(flagval & NLM_F_STRICT_CHK) != enable) {
3580                 enable = !!(flagval & NLM_F_STRICT_CHK);
3581 
3582                 if (setsockopt(sock->s_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &enable, sizeof(enable)) < 0)
3583                         err_return(nl_syserr2nlerr(errno), "Unable to toggle NETLINK_GET_STRICT_CHK");
3584         }
3585 
3586         msg = nlmsg_alloc_simple(ucv_int64_get(cmd), NLM_F_REQUEST | (flagval & ~NLM_F_STRICT_CHK));
3587 
3588         if (!msg)
3589                 err_return(NLE_NOMEM, NULL);
3590 
3591         if (st.spec) {
3592                 if (st.spec->headsize) {
3593                         buf = nlmsg_reserve(msg, st.spec->headsize, 0);
3594 
3595                         if (!buf) {
3596                                 nlmsg_free(msg);
3597 
3598                                 return NULL;
3599                         }
3600 
3601                         memset(buf, 0, st.spec->headsize);
3602                 }
3603 
3604                 if (!uc_nl_parse_attrs(msg, NLMSG_DATA(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) {
3605                         nlmsg_free(msg);
3606 
3607                         return NULL;
3608                 }
3609         }
3610 
3611         cb = nl_cb_alloc(NL_CB_DEFAULT);
3612 
3613         if (!cb) {
3614                 nlmsg_free(msg);
3615                 err_return(NLE_NOMEM, NULL);
3616         }
3617 
3618         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st);
3619         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
3620         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_done, &st);
3621         nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
3622 
3623         nl_send_auto_complete(sock, msg);
3624 
3625         do {
3626                 err = nl_recvmsgs(sock, cb);
3627 
3628                 if (err && st.state != STATE_ERROR) {
3629                         set_error(err, NULL);
3630 
3631                         st.state = STATE_ERROR;
3632                 }
3633         }
3634         while (st.state < STATE_REPLIED);
3635 
3636         nlmsg_free(msg);
3637         nl_cb_put(cb);
3638 
3639         switch (st.state) {
3640         case STATE_REPLIED:
3641                 return st.res;
3642 
3643         case STATE_UNREPLIED:
3644                 return ucv_boolean_new(true);
3645 
3646         case STATE_ERROR:
3647                 return ucv_boolean_new(false);
3648 
3649         default:
3650                 set_error(NLE_FAILURE, "Interrupted reply");
3651 
3652                 return ucv_boolean_new(false);
3653         }
3654 }
3655 
3656 static const uc_nl_nested_spec_t *
3657 uc_nl_msg_spec(int type)
3658 {
3659         switch (type) {
3660         case RTM_NEWLINK:
3661         case RTM_DELLINK:
3662                 return &link_msg;
3663         case RTM_NEWROUTE:
3664         case RTM_DELROUTE:
3665                 return &route_msg;
3666         case RTM_NEWNEIGH:
3667         case RTM_DELNEIGH:
3668                 return &neigh_msg;
3669         case RTM_NEWADDR:
3670         case RTM_DELADDR:
3671                 return &addr_msg;
3672         case RTM_NEWRULE:
3673         case RTM_DELRULE:
3674                 return &rule_msg;
3675         case RTM_NEWADDRLABEL:
3676         case RTM_DELADDRLABEL:
3677                 return &addrlabel_msg;
3678         case RTM_NEWNEIGHTBL:
3679                 return &neightbl_msg;
3680         case RTM_NEWNETCONF:
3681         case RTM_DELNETCONF:
3682                 return &netconf_msg;
3683         default:
3684                 return NULL;
3685         }
3686 }
3687 
3688 static void
3689 uc_nl_prepare_event(uc_vm_t *vm, uc_value_t *dest, struct nl_msg *msg)
3690 {
3691         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3692         const uc_nl_nested_spec_t *spec;
3693         const uc_nl_attr_spec_t *attrs = NULL;
3694         size_t nattrs = 0, headsize = 0;
3695         uc_value_t *o;
3696 
3697         spec = uc_nl_msg_spec(hdr->nlmsg_type);
3698         if (spec) {
3699                 attrs = spec->attrs;
3700                 nattrs = spec->nattrs;
3701                 headsize = spec->headsize;
3702         }
3703 
3704         o = ucv_object_new(vm);
3705         if (!uc_nl_convert_attrs(msg, nlmsg_attrdata(hdr, 0),
3706                 nlmsg_attrlen(hdr, 0), headsize, attrs, nattrs, vm, o)) {
3707                 ucv_put(o);
3708                 return;
3709         }
3710 
3711         ucv_object_add(dest, "msg", o);
3712         if (headsize)
3713                 ucv_object_add(dest, "head", ucv_string_new_length(NLMSG_DATA(hdr), headsize));
3714 }
3715 
3716 static bool
3717 uc_nl_fill_cmds(uint32_t *cmd_bits, uc_value_t *cmds)
3718 {
3719         if (ucv_type(cmds) == UC_ARRAY) {
3720                 for (size_t i = 0; i < ucv_array_length(cmds); i++) {
3721                         int64_t n = ucv_int64_get(ucv_array_get(cmds, i));
3722 
3723                         if (errno || n < 0 || n >= __RTM_MAX)
3724                                 return false;
3725 
3726                         cmd_bits[n / 32] |= (1 << (n % 32));
3727                 }
3728         }
3729         else if (ucv_type(cmds) == UC_INTEGER) {
3730                 int64_t n = ucv_int64_get(cmds);
3731 
3732                 if (errno || n < 0 || n > 255)
3733                         return false;
3734 
3735                 cmd_bits[n / 32] |= (1 << (n % 32));
3736         }
3737         else if (!cmds)
3738                 memset(cmd_bits, 0xff, RTNL_CMDS_BITMAP_SIZE * sizeof(*cmd_bits));
3739         else
3740                 return false;
3741 
3742         return true;
3743 }
3744 
3745 static int
3746 cb_listener_event(struct nl_msg *msg, void *arg)
3747 {
3748         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3749         uc_vm_t *vm = listener_vm;
3750         int cmd = hdr->nlmsg_type;
3751 
3752         if (!nl_conn.evsock_fd.registered || !vm)
3753                 return NL_SKIP;
3754 
3755         for (size_t i = 0; i < ucv_array_length(listener_registry); i += 2) {
3756                 uc_value_t *this = ucv_array_get(listener_registry, i);
3757                 uc_value_t *func = ucv_array_get(listener_registry, i + 1);
3758                 uc_nl_listener_t *l;
3759                 uc_value_t *o;
3760 
3761                 l = ucv_resource_data(this, "rtnl.listener");
3762                 if (!l)
3763                         continue;
3764 
3765                 if (cmd > __RTM_MAX || !(l->cmds[cmd / 32] & (1 << (cmd % 32))))
3766                         continue;
3767 
3768                 if (!ucv_is_callable(func))
3769                         continue;
3770 
3771                 o = ucv_object_new(vm);
3772                 uc_nl_prepare_event(vm, o, msg);
3773                 ucv_object_add(o, "cmd", ucv_int64_new(cmd));
3774 
3775                 uc_vm_stack_push(vm, ucv_get(this));
3776                 uc_vm_stack_push(vm, ucv_get(func));
3777                 uc_vm_stack_push(vm, o);
3778 
3779                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
3780                         uloop_end();
3781                         set_error(NLE_FAILURE, "Runtime exception in callback");
3782 
3783                         errno = EINVAL;
3784 
3785                         return NL_STOP;
3786                 }
3787 
3788                 ucv_put(uc_vm_stack_pop(vm));
3789         }
3790 
3791         errno = 0;
3792 
3793         return NL_SKIP;
3794 }
3795 
3796 static void
3797 uc_nl_listener_cb(struct uloop_fd *fd, unsigned int events)
3798 {
3799         while (true) {
3800                 errno = 0;
3801 
3802                 nl_recvmsgs_default(nl_conn.evsock);
3803 
3804                 if (errno != 0)
3805                         break;
3806         }
3807 }
3808 
3809 static void
3810 uc_nl_add_group(unsigned int idx)
3811 {
3812         if (idx >= __RTNLGRP_MAX)
3813                 return;
3814 
3815         if (nl_conn.groups[idx / 32] & (1 << (idx % 32)))
3816                 return;
3817 
3818         nl_conn.groups[idx / 32] |= (1 << (idx % 32));
3819         nl_socket_add_membership(nl_conn.evsock, idx);
3820 }
3821 
3822 static bool
3823 uc_nl_evsock_init(void)
3824 {
3825         struct uloop_fd *fd = &nl_conn.evsock_fd;
3826         struct nl_sock *sock;
3827 
3828         if (nl_conn.evsock)
3829                 return true;
3830 
3831         sock = nl_socket_alloc();
3832 
3833         if (nl_connect(sock, NETLINK_ROUTE))
3834                 goto free;
3835 
3836         fd->fd = nl_socket_get_fd(sock);
3837         fd->cb = uc_nl_listener_cb;
3838         uloop_fd_add(fd, ULOOP_READ);
3839 
3840         nl_socket_set_buffer_size(sock, 1024 * 1024, 0);
3841         nl_socket_disable_seq_check(sock);
3842         nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, cb_listener_event, NULL);
3843 
3844         nl_conn.evsock = sock;
3845 
3846         return true;
3847 
3848 free:
3849         nl_socket_free(sock);
3850         return false;
3851 }
3852 
3853 /**
3854  * Represents a netlink listener resource.
3855  *
3856  * @class module:rtnl.listener
3857  * @hideconstructor
3858  *
3859  * @see {@link module:rtnl#listener|listener()}
3860  *
3861  * @example
3862  * const nlListener = listener((msg) => {
3863  *     print('Received netlink message:', msg, '\n');
3864  * }, [RTM_NEWROUTE, RTM_DELROUTE]);
3865  *
3866  * nlListener.set_commands([RTM_GETLINK, RTM_SETLINK]);
3867  *
3868  * nlListener.close();
3869  */
3870 
3871 /**
3872  * Create a netlink listener.
3873  *
3874  * Creates a new netlink listener that will receive messages matching the specified
3875  * commands and multicast groups.
3876  *
3877  * @function module:rtnl#listener
3878  *
3879  * @param {function} callback - The callback function to invoke when a message is received
3880  * @param {Array<string>} [commands] - Array of netlink commands to listen for (optional)
3881  * @param {Array<number>} [groups] - Array of multicast groups to join (optional)
3882  *
3883  * @returns {module:rtnl.listener} - A listener object with methods to control the listener
3884  *
3885  * @example
3886  * // Create a listener for route changes
3887  * let routeListener = listener((msg) => {
3888  *     print('Received route message:', msg, '\n');
3889  * }, [RTM_NEWROUTE, RTM_DELROUTE]);
3890  */
3891 static uc_value_t *
3892 uc_nl_listener(uc_vm_t *vm, size_t nargs)
3893 {
3894         uc_nl_listener_t *l;
3895         uc_value_t *cb_func = uc_fn_arg(0);
3896         uc_value_t *cmds = uc_fn_arg(1);
3897         uc_value_t *groups = uc_fn_arg(2);
3898         uc_value_t *rv;
3899         size_t i;
3900 
3901         if (!ucv_is_callable(cb_func)) {
3902                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid callback");
3903                 return NULL;
3904         }
3905 
3906         if (!uc_nl_evsock_init())
3907                 return NULL;
3908 
3909         if (ucv_type(groups) == UC_ARRAY) {
3910                 for (i = 0; i < ucv_array_length(groups); i++) {
3911                         int64_t n = ucv_int64_get(ucv_array_get(groups, i));
3912 
3913                         if (errno || n < 0 || n >= __RTNLGRP_MAX)
3914                                 err_return(NLE_INVAL, NULL);
3915 
3916                         uc_nl_add_group(n);
3917                 }
3918         } else {
3919                 uc_nl_add_group(RTNLGRP_LINK);
3920         }
3921 
3922         for (i = 0; i < ucv_array_length(listener_registry); i += 2) {
3923                 if (!ucv_array_get(listener_registry, i))
3924                         break;
3925         }
3926 
3927         l = xalloc(sizeof(*l));
3928         l->index = i;
3929 
3930         if (!uc_nl_fill_cmds(l->cmds, cmds)) {
3931                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
3932                 free(l);
3933                 return NULL;
3934         }
3935 
3936         rv = uc_resource_new(listener_type, l);
3937 
3938         ucv_array_set(listener_registry, i, ucv_get(rv));
3939         ucv_array_set(listener_registry, i + 1, ucv_get(cb_func));
3940 
3941         listener_vm = vm;
3942 
3943         return rv;
3944 }
3945 
3946 static void
3947 uc_nl_listener_free(void *arg)
3948 {
3949         uc_nl_listener_t *l = arg;
3950 
3951         if (!l)
3952                 return;
3953 
3954         ucv_array_set(listener_registry, l->index, NULL);
3955         ucv_array_set(listener_registry, l->index + 1, NULL);
3956         free(l);
3957 }
3958 
3959 /**
3960  * Set the commands for a netlink listener.
3961  *
3962  * Updates the set of netlink commands that the listener will receive.
3963  *
3964  * @function module:rtnl.listener#set_commands
3965  *
3966  * @param {Array<string>} commands - Array of netlink commands to listen for
3967  *
3968  * @returns {boolean} - true if successful, false on error
3969  *
3970  * @example
3971  * // Update listener to only receive route messages
3972  * listener.set_commands([RTM_NEWROUTE, RTM_DELROUTE]);
3973  */
3974 static uc_value_t *
3975 uc_nl_listener_set_commands(uc_vm_t *vm, size_t nargs)
3976 {
3977         uc_nl_listener_t *l = uc_fn_thisval("rtnl.listener");
3978         uc_value_t *cmds = uc_fn_arg(0);
3979 
3980         if (!l)
3981                 return NULL;
3982 
3983         memset(l->cmds, 0, sizeof(l->cmds));
3984         if (!uc_nl_fill_cmds(l->cmds, cmds))
3985                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
3986 
3987         return NULL;
3988 }
3989 
3990 /**
3991  * Close a netlink listener.
3992  *
3993  * Closes the netlink listener and stops receiving messages.
3994  *
3995  * @function module:rtnl.listener#close
3996  *
3997  * @returns {boolean} - true if successful, false on error
3998  *
3999  * @example
4000  * // Close the listener
4001  * listener.close();
4002  */
4003 static uc_value_t *
4004 uc_nl_listener_close(uc_vm_t *vm, size_t nargs)
4005 {
4006         uc_nl_listener_t **lptr = uc_fn_this("rtnl.listener");
4007         uc_nl_listener_t *l;
4008 
4009         if (!lptr)
4010                 return NULL;
4011 
4012         l = *lptr;
4013         if (!l)
4014                 return NULL;
4015 
4016         *lptr = NULL;
4017         uc_nl_listener_free(l);
4018 
4019         return NULL;
4020 }
4021 
4022 
4023 static void
4024 register_constants(uc_vm_t *vm, uc_value_t *scope)
4025 {
4026         uc_value_t *c = ucv_object_new(vm);
4027 
4028 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x))
4029 
4030         /**
4031          * @typedef
4032          * @name Netlink message flags
4033          * @property {number} NLM_F_ACK - Request for acknowledgment
4034          * @property {number} NLM_F_ACK_TLVS - Request for acknowledgment with TLVs
4035          * @property {number} NLM_F_APPEND - Append to existing list
4036          * @property {number} NLM_F_ATOMIC - Atomic operation
4037          * @property {number} NLM_F_CAPPED - Request capped
4038          * @property {number} NLM_F_CREATE - Create if not exists
4039          * @property {number} NLM_F_DUMP - Dump request
4040          * @property {number} NLM_F_DUMP_FILTERED - Dump filtered request
4041          * @property {number} NLM_F_DUMP_INTR - Dump interrupted
4042          * @property {number} NLM_F_ECHO - Echo request
4043          * @property {number} NLM_F_EXCL - Exclusive creation
4044          * @property {number} NLM_F_MATCH - Match request
4045          * @property {number} NLM_F_MULTI - Multi-part message
4046          * @property {number} NLM_F_NONREC - Non-recursive operation
4047          * @property {number} NLM_F_REPLACE - Replace existing
4048          * @property {number} NLM_F_REQUEST - Request message
4049          * @property {number} NLM_F_ROOT - Root operation
4050          * @property {number} NLM_F_STRICT_CHK - Strict checking
4051          */
4052         ADD_CONST(NLM_F_ACK);
4053         ADD_CONST(NLM_F_ACK);
4054         ADD_CONST(NLM_F_ACK_TLVS);
4055         ADD_CONST(NLM_F_APPEND);
4056         ADD_CONST(NLM_F_ATOMIC);
4057         ADD_CONST(NLM_F_CAPPED);
4058         ADD_CONST(NLM_F_CREATE);
4059         ADD_CONST(NLM_F_DUMP);
4060         ADD_CONST(NLM_F_DUMP_FILTERED);
4061         ADD_CONST(NLM_F_DUMP_INTR);
4062         ADD_CONST(NLM_F_ECHO);
4063         ADD_CONST(NLM_F_EXCL);
4064         ADD_CONST(NLM_F_MATCH);
4065         ADD_CONST(NLM_F_MULTI);
4066         ADD_CONST(NLM_F_NONREC);
4067         ADD_CONST(NLM_F_REPLACE);
4068         ADD_CONST(NLM_F_REQUEST);
4069         ADD_CONST(NLM_F_ROOT);
4070         ADD_CONST(NLM_F_STRICT_CHK); /* custom */
4071 
4072         /**
4073          * @typedef
4074          * @name IPv6 address generation modes
4075          * @property {number} IN6_ADDR_GEN_MODE_EUI64 - EUI-64 mode
4076          * @property {number} IN6_ADDR_GEN_MODE_NONE - No mode
4077          * @property {number} IN6_ADDR_GEN_MODE_STABLE_PRIVACY - Stable privacy mode
4078          * @property {number} IN6_ADDR_GEN_MODE_RANDOM - Random mode
4079          */
4080         ADD_CONST(IN6_ADDR_GEN_MODE_EUI64);
4081         ADD_CONST(IN6_ADDR_GEN_MODE_NONE);
4082         ADD_CONST(IN6_ADDR_GEN_MODE_STABLE_PRIVACY);
4083         ADD_CONST(IN6_ADDR_GEN_MODE_RANDOM);
4084 
4085         /**
4086          * @typedef
4087          * @name MACVLAN modes
4088          * @property {number} MACVLAN_MODE_PRIVATE - Private mode
4089          * @property {number} MACVLAN_MODE_VEPA - VEPA mode
4090          * @property {number} MACVLAN_MODE_BRIDGE - Bridge mode
4091          * @property {number} MACVLAN_MODE_PASSTHRU - Pass-through mode
4092          * @property {number} MACVLAN_MODE_SOURCE - Source mode
4093          */
4094         ADD_CONST(MACVLAN_MODE_PRIVATE);
4095         ADD_CONST(MACVLAN_MODE_VEPA);
4096         ADD_CONST(MACVLAN_MODE_BRIDGE);
4097         ADD_CONST(MACVLAN_MODE_PASSTHRU);
4098         ADD_CONST(MACVLAN_MODE_SOURCE);
4099 
4100         /**
4101          * @typedef
4102          * @name MACVLAN MAC address commands
4103          * @property {number} MACVLAN_MACADDR_ADD - Add MAC address
4104          * @property {number} MACVLAN_MACADDR_DEL - Delete MAC address
4105          * @property {number} MACVLAN_MACADDR_FLUSH - Flush MAC addresses
4106          * @property {number} MACVLAN_MACADDR_SET - Set MAC address
4107          */
4108         ADD_CONST(MACVLAN_MACADDR_ADD);
4109         ADD_CONST(MACVLAN_MACADDR_DEL);
4110         ADD_CONST(MACVLAN_MACADDR_FLUSH);
4111         ADD_CONST(MACVLAN_MACADDR_SET);
4112 
4113         /**
4114          * @typedef
4115          * @name MACsec validation levels
4116          * @property {number} MACSEC_VALIDATE_DISABLED - Disabled validation
4117          * @property {number} MACSEC_VALIDATE_CHECK - Check validation
4118          * @property {number} MACSEC_VALIDATE_STRICT - Strict validation
4119          * @property {number} MACSEC_VALIDATE_MAX - Maximum validation
4120          */
4121         ADD_CONST(MACSEC_VALIDATE_DISABLED);
4122         ADD_CONST(MACSEC_VALIDATE_CHECK);
4123         ADD_CONST(MACSEC_VALIDATE_STRICT);
4124         ADD_CONST(MACSEC_VALIDATE_MAX);
4125 
4126         /**
4127          * @typedef
4128          * @name MACsec offload modes
4129          * @property {number} MACSEC_OFFLOAD_OFF - Offload off
4130          * @property {number} MACSEC_OFFLOAD_PHY - Physical offload
4131          * @property {number} MACSEC_OFFLOAD_MAC - MAC offload
4132          * @property {number} MACSEC_OFFLOAD_MAX - Maximum offload
4133          */
4134         ADD_CONST(MACSEC_OFFLOAD_OFF);
4135         ADD_CONST(MACSEC_OFFLOAD_PHY);
4136         ADD_CONST(MACSEC_OFFLOAD_MAC);
4137         ADD_CONST(MACSEC_OFFLOAD_MAX);
4138 
4139         /**
4140          * @typedef
4141          * @name IPVLAN modes
4142          * @property {number} IPVLAN_MODE_L2 - Layer 2 mode
4143          * @property {number} IPVLAN_MODE_L3 - Layer 3 mode
4144          * @property {number} IPVLAN_MODE_L3S - Layer 3 symmetric mode
4145          */
4146         ADD_CONST(IPVLAN_MODE_L2);
4147         ADD_CONST(IPVLAN_MODE_L3);
4148         ADD_CONST(IPVLAN_MODE_L3S);
4149 
4150         /**
4151          * @typedef
4152          * @name VXLAN data frame flags
4153          * @property {number} VXLAN_DF_UNSET - Data frame unset
4154          * @property {number} VXLAN_DF_SET - Data frame set
4155          * @property {number} VXLAN_DF_INHERIT - Data frame inherit
4156          * @property {number} VXLAN_DF_MAX - Maximum data frame
4157          */
4158         ADD_CONST(VXLAN_DF_UNSET);
4159         ADD_CONST(VXLAN_DF_SET);
4160         ADD_CONST(VXLAN_DF_INHERIT);
4161         ADD_CONST(VXLAN_DF_MAX);
4162 
4163         /**
4164          * @typedef
4165          * @name Geneve data frame flags
4166          * @property {number} GENEVE_DF_UNSET - Data frame unset
4167          * @property {number} GENEVE_DF_SET - Data frame set
4168          * @property {number} GENEVE_DF_INHERIT - Data frame inherit
4169          * @property {number} GENEVE_DF_MAX - Maximum data frame
4170          */
4171         ADD_CONST(GENEVE_DF_UNSET);
4172         ADD_CONST(GENEVE_DF_SET);
4173         ADD_CONST(GENEVE_DF_INHERIT);
4174         ADD_CONST(GENEVE_DF_MAX);
4175 
4176         /**
4177          * @typedef
4178          * @name GTP roles
4179          * @property {number} GTP_ROLE_GGSN - GGSN role
4180          * @property {number} GTP_ROLE_SGSN - SGSN role
4181          */
4182         ADD_CONST(GTP_ROLE_GGSN);
4183         ADD_CONST(GTP_ROLE_SGSN);
4184 
4185         /**
4186          * @typedef
4187          * @name Port request types
4188          * @property {number} PORT_REQUEST_PREASSOCIATE - Pre-associate request
4189          * @property {number} PORT_REQUEST_PREASSOCIATE_RR - Pre-associate round-robin request
4190          * @property {number} PORT_REQUEST_ASSOCIATE - Associate request
4191          * @property {number} PORT_REQUEST_DISASSOCIATE - Disassociate request
4192          */
4193         ADD_CONST(PORT_REQUEST_PREASSOCIATE);
4194         ADD_CONST(PORT_REQUEST_PREASSOCIATE_RR);
4195         ADD_CONST(PORT_REQUEST_ASSOCIATE);
4196         ADD_CONST(PORT_REQUEST_DISASSOCIATE);
4197 
4198         /**
4199          * @typedef
4200          * @name Port VDP responses
4201          * @property {number} PORT_VDP_RESPONSE_SUCCESS - Success response
4202          * @property {number} PORT_VDP_RESPONSE_INVALID_FORMAT - Invalid format response
4203          * @property {number} PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES - Insufficient resources response
4204          * @property {number} PORT_VDP_RESPONSE_UNUSED_VTID - Unused VTID response
4205          * @property {number} PORT_VDP_RESPONSE_VTID_VIOLATION - VTID violation response
4206          * @property {number} PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION - VTID version violation response
4207          * @property {number} PORT_VDP_RESPONSE_OUT_OF_SYNC - Out of sync response
4208          */
4209         ADD_CONST(PORT_VDP_RESPONSE_SUCCESS);
4210         ADD_CONST(PORT_VDP_RESPONSE_INVALID_FORMAT);
4211         ADD_CONST(PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES);
4212         ADD_CONST(PORT_VDP_RESPONSE_UNUSED_VTID);
4213         ADD_CONST(PORT_VDP_RESPONSE_VTID_VIOLATION);
4214         ADD_CONST(PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION);
4215         ADD_CONST(PORT_VDP_RESPONSE_OUT_OF_SYNC);
4216 
4217         /**
4218          * @typedef
4219          * @name Port profile responses
4220          * @property {number} PORT_PROFILE_RESPONSE_SUCCESS - Success response
4221          * @property {number} PORT_PROFILE_RESPONSE_INPROGRESS - In progress response
4222          * @property {number} PORT_PROFILE_RESPONSE_INVALID - Invalid response
4223          * @property {number} PORT_PROFILE_RESPONSE_BADSTATE - Bad state response
4224          * @property {number} PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES - Insufficient resources response
4225          * @property {number} PORT_PROFILE_RESPONSE_ERROR - Error response
4226          */
4227         ADD_CONST(PORT_PROFILE_RESPONSE_SUCCESS);
4228         ADD_CONST(PORT_PROFILE_RESPONSE_INPROGRESS);
4229         ADD_CONST(PORT_PROFILE_RESPONSE_INVALID);
4230         ADD_CONST(PORT_PROFILE_RESPONSE_BADSTATE);
4231         ADD_CONST(PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES);
4232         ADD_CONST(PORT_PROFILE_RESPONSE_ERROR);
4233 
4234         /**
4235          * @typedef
4236          * @name IPoIB modes
4237          * @property {number} IPOIB_MODE_DATAGRAM - Datagram mode
4238          * @property {number} IPOIB_MODE_CONNECTED - Connected mode
4239          */
4240         ADD_CONST(IPOIB_MODE_DATAGRAM);
4241         ADD_CONST(IPOIB_MODE_CONNECTED);
4242 
4243         /**
4244          * @typedef
4245          * @name HSR protocols
4246          * @property {number} HSR_PROTOCOL_HSR - HSR protocol
4247          * @property {number} HSR_PROTOCOL_PRP - PRP protocol
4248          */
4249         ADD_CONST(HSR_PROTOCOL_HSR);
4250         ADD_CONST(HSR_PROTOCOL_PRP);
4251 
4252         /**
4253          * @typedef
4254          * @name CAN controller modes
4255          * @description
4256          * Flag bits used in the `mask` and `flags` members of the `ctrlmode`
4257          * attribute of `can` type links.
4258          * @property {number} CAN_CTRLMODE_LOOPBACK - Loopback mode
4259          * @property {number} CAN_CTRLMODE_LISTENONLY - Listen-only mode
4260          * @property {number} CAN_CTRLMODE_3_SAMPLES - Triple sampling mode
4261          * @property {number} CAN_CTRLMODE_ONE_SHOT - One-shot mode
4262          * @property {number} CAN_CTRLMODE_BERR_REPORTING - Bus error reporting
4263          * @property {number} CAN_CTRLMODE_FD - CAN FD mode
4264          * @property {number} CAN_CTRLMODE_PRESUME_ACK - Ignore missing CAN ACKs
4265          * @property {number} CAN_CTRLMODE_FD_NON_ISO - CAN FD in non-ISO mode
4266          * @property {number} CAN_CTRLMODE_CC_LEN8_DLC - Classic CAN DLC option
4267          */
4268         ADD_CONST(CAN_CTRLMODE_LOOPBACK);
4269         ADD_CONST(CAN_CTRLMODE_LISTENONLY);
4270         ADD_CONST(CAN_CTRLMODE_3_SAMPLES);
4271         ADD_CONST(CAN_CTRLMODE_ONE_SHOT);
4272         ADD_CONST(CAN_CTRLMODE_BERR_REPORTING);
4273         ADD_CONST(CAN_CTRLMODE_FD);
4274         ADD_CONST(CAN_CTRLMODE_PRESUME_ACK);
4275         ADD_CONST(CAN_CTRLMODE_FD_NON_ISO);
4276 #ifdef CAN_CTRLMODE_CC_LEN8_DLC
4277         ADD_CONST(CAN_CTRLMODE_CC_LEN8_DLC);
4278 #endif
4279 #ifdef CAN_CTRLMODE_TDC_AUTO
4280         ADD_CONST(CAN_CTRLMODE_TDC_AUTO);
4281         ADD_CONST(CAN_CTRLMODE_TDC_MANUAL);
4282 #endif
4283 
4284         /**
4285          * @typedef
4286          * @name CAN operational states
4287          * @description
4288          * Values of the `state` attribute of `can` type links.
4289          * @property {number} CAN_STATE_ERROR_ACTIVE - RX/TX error count < 96
4290          * @property {number} CAN_STATE_ERROR_WARNING - RX/TX error count < 128
4291          * @property {number} CAN_STATE_ERROR_PASSIVE - RX/TX error count < 256
4292          * @property {number} CAN_STATE_BUS_OFF - RX/TX error count >= 256
4293          * @property {number} CAN_STATE_STOPPED - Device is stopped
4294          * @property {number} CAN_STATE_SLEEPING - Device is sleeping
4295          */
4296         ADD_CONST(CAN_STATE_ERROR_ACTIVE);
4297         ADD_CONST(CAN_STATE_ERROR_WARNING);
4298         ADD_CONST(CAN_STATE_ERROR_PASSIVE);
4299         ADD_CONST(CAN_STATE_BUS_OFF);
4300         ADD_CONST(CAN_STATE_STOPPED);
4301         ADD_CONST(CAN_STATE_SLEEPING);
4302 
4303         /**
4304          * @typedef
4305          * @name Link extended statistics types
4306          * @property {number} LINK_XSTATS_TYPE_UNSPEC - Unspecified type
4307          * @property {number} LINK_XSTATS_TYPE_BRIDGE - Bridge type
4308          * @property {number} LINK_XSTATS_TYPE_BOND - Bond type
4309          */
4310         ADD_CONST(LINK_XSTATS_TYPE_UNSPEC);
4311         ADD_CONST(LINK_XSTATS_TYPE_BRIDGE);
4312         ADD_CONST(LINK_XSTATS_TYPE_BOND);
4313 
4314         /**
4315          * @typedef
4316          * @name XDP attach types
4317          * @property {number} XDP_ATTACHED_NONE - Not attached
4318          * @property {number} XDP_ATTACHED_DRV - Driver attached
4319          * @property {number} XDP_ATTACHED_SKB - SKB attached
4320          * @property {number} XDP_ATTACHED_HW - Hardware attached
4321          * @property {number} XDP_ATTACHED_MULTI - Multi attached
4322          */
4323         ADD_CONST(XDP_ATTACHED_NONE);
4324         ADD_CONST(XDP_ATTACHED_DRV);
4325         ADD_CONST(XDP_ATTACHED_SKB);
4326         ADD_CONST(XDP_ATTACHED_HW);
4327         ADD_CONST(XDP_ATTACHED_MULTI);
4328 
4329         /**
4330          * @typedef
4331          * @name FDB notification bits
4332          * @property {number} FDB_NOTIFY_BIT - Notify bit
4333          * @property {number} FDB_NOTIFY_INACTIVE_BIT - Inactive notify bit
4334          */
4335 
4336         ADD_CONST(FDB_NOTIFY_BIT);
4337         ADD_CONST(FDB_NOTIFY_INACTIVE_BIT);
4338 
4339         /**
4340          * @typedef
4341          * @name Route commands
4342          * @property {number} RTM_BASE - Base command
4343          * @property {number} RTM_NEWLINK - New link
4344          * @property {number} RTM_DELLINK - Delete link
4345          * @property {number} RTM_GETLINK - Get link
4346          * @property {number} RTM_SETLINK - Set link
4347          * @property {number} RTM_NEWADDR - New address
4348          * @property {number} RTM_DELADDR - Delete address
4349          * @property {number} RTM_GETADDR - Get address
4350          * @property {number} RTM_NEWROUTE - New route
4351          * @property {number} RTM_DELROUTE - Delete route
4352          * @property {number} RTM_GETROUTE - Get route
4353          * @property {number} RTM_NEWRULE - New rule
4354          * @property {number} RTM_DELRULE - Delete rule
4355          * @property {number} RTM_GETRULE - Get rule
4356          * @property {number} RTM_NEWQDISC - New queue discipline
4357          * @property {number} RTM_DELQDISC - Delete queue discipline
4358          * @property {number} RTM_GETQDISC - Get queue discipline
4359          * @property {number} RTM_NEWTCLASS - New traffic class
4360          * @property {number} RTM_DELTCLASS - Delete traffic class
4361          * @property {number} RTM_GETTCLASS - Get traffic class
4362          * @property {number} RTM_NEWTFILTER - New traffic filter
4363          * @property {number} RTM_DELTFILTER - Delete traffic filter
4364          * @property {number} RTM_GETTFILTER - Get traffic filter
4365          * @property {number} RTM_NEWACTION - New action
4366          * @property {number} RTM_DELACTION - Delete action
4367          * @property {number} RTM_GETACTION - Get action
4368          * @property {number} RTM_NEWPREFIX - New prefix
4369          * @property {number} RTM_GETMULTICAST - Get multicast
4370          * @property {number} RTM_GETANYCAST - Get anycast
4371          * @property {number} RTM_NEWNEIGHTBL - New neighbor table
4372          * @property {number} RTM_GETNEIGHTBL - Get neighbor table
4373          * @property {number} RTM_SETNEIGHTBL - Set neighbor table
4374          * @property {number} RTM_NEWNDUSEROPT - New neighbor user option
4375          * @property {number} RTM_NEWADDRLABEL - New address label
4376          * @property {number} RTM_DELADDRLABEL - Delete address label
4377          * @property {number} RTM_GETADDRLABEL - Get address label
4378          * @property {number} RTM_GETDCB - Get DCB
4379          * @property {number} RTM_SETDCB - Set DCB
4380          * @property {number} RTM_NEWNETCONF - New network configuration
4381          * @property {number} RTM_DELNETCONF - Delete network configuration
4382          * @property {number} RTM_GETNETCONF - Get network configuration
4383          * @property {number} RTM_NEWMDB - New multicast database
4384          * @property {number} RTM_DELMDB - Delete multicast database
4385          * @property {number} RTM_GETMDB - Get multicast database
4386          * @property {number} RTM_NEWNSID - New network namespace ID
4387          * @property {number} RTM_DELNSID - Delete network namespace ID
4388          * @property {number} RTM_GETNSID - Get network namespace ID
4389          * @property {number} RTM_NEWSTATS - New statistics
4390          * @property {number} RTM_GETSTATS - Get statistics
4391          * @property {number} RTM_NEWCACHEREPORT - New cache report
4392          * @property {number} RTM_NEWCHAIN - New chain
4393          * @property {number} RTM_DELCHAIN - Delete chain
4394          * @property {number} RTM_GETCHAIN - Get chain
4395          * @property {number} RTM_NEWNEXTHOP - New next hop
4396          * @property {number} RTM_DELNEXTHOP - Delete next hop
4397          * @property {number} RTM_GETNEXTHOP - Get next hop
4398          * @property {number} RTM_NEWLINKPROP - New link property
4399          * @property {number} RTM_DELLINKPROP - Delete link property
4400          * @property {number} RTM_GETLINKPROP - Get link property
4401          * @property {number} RTM_NEWVLAN - New VLAN
4402          * @property {number} RTM_DELVLAN - Delete VLAN
4403          * @property {number} RTM_GETVLAN - Get VLAN
4404          */
4405         ADD_CONST(RTM_BASE);
4406         ADD_CONST(RTM_NEWLINK);
4407         ADD_CONST(RTM_DELLINK);
4408         ADD_CONST(RTM_GETLINK);
4409         ADD_CONST(RTM_SETLINK);
4410         ADD_CONST(RTM_NEWADDR);
4411         ADD_CONST(RTM_DELADDR);
4412         ADD_CONST(RTM_GETADDR);
4413         ADD_CONST(RTM_NEWROUTE);
4414         ADD_CONST(RTM_DELROUTE);
4415         ADD_CONST(RTM_GETROUTE);
4416         ADD_CONST(RTM_NEWNEIGH);
4417         ADD_CONST(RTM_DELNEIGH);
4418         ADD_CONST(RTM_GETNEIGH);
4419         ADD_CONST(RTM_NEWRULE);
4420         ADD_CONST(RTM_DELRULE);
4421         ADD_CONST(RTM_GETRULE);
4422         ADD_CONST(RTM_NEWQDISC);
4423         ADD_CONST(RTM_DELQDISC);
4424         ADD_CONST(RTM_GETQDISC);
4425         ADD_CONST(RTM_NEWTCLASS);
4426         ADD_CONST(RTM_DELTCLASS);
4427         ADD_CONST(RTM_GETTCLASS);
4428         ADD_CONST(RTM_NEWTFILTER);
4429         ADD_CONST(RTM_DELTFILTER);
4430         ADD_CONST(RTM_GETTFILTER);
4431         ADD_CONST(RTM_NEWACTION);
4432         ADD_CONST(RTM_DELACTION);
4433         ADD_CONST(RTM_GETACTION);
4434         ADD_CONST(RTM_NEWPREFIX);
4435         ADD_CONST(RTM_GETMULTICAST);
4436         ADD_CONST(RTM_GETANYCAST);
4437         ADD_CONST(RTM_NEWNEIGHTBL);
4438         ADD_CONST(RTM_GETNEIGHTBL);
4439         ADD_CONST(RTM_SETNEIGHTBL);
4440         ADD_CONST(RTM_NEWNDUSEROPT);
4441         ADD_CONST(RTM_NEWADDRLABEL);
4442         ADD_CONST(RTM_DELADDRLABEL);
4443         ADD_CONST(RTM_GETADDRLABEL);
4444         ADD_CONST(RTM_GETDCB);
4445         ADD_CONST(RTM_SETDCB);
4446         ADD_CONST(RTM_NEWNETCONF);
4447         ADD_CONST(RTM_DELNETCONF);
4448         ADD_CONST(RTM_GETNETCONF);
4449         ADD_CONST(RTM_NEWMDB);
4450         ADD_CONST(RTM_DELMDB);
4451         ADD_CONST(RTM_GETMDB);
4452         ADD_CONST(RTM_NEWNSID);
4453         ADD_CONST(RTM_DELNSID);
4454         ADD_CONST(RTM_GETNSID);
4455         ADD_CONST(RTM_NEWSTATS);
4456         ADD_CONST(RTM_GETSTATS);
4457         ADD_CONST(RTM_NEWCACHEREPORT);
4458         ADD_CONST(RTM_NEWCHAIN);
4459         ADD_CONST(RTM_DELCHAIN);
4460         ADD_CONST(RTM_GETCHAIN);
4461         ADD_CONST(RTM_NEWNEXTHOP);
4462         ADD_CONST(RTM_DELNEXTHOP);
4463         ADD_CONST(RTM_GETNEXTHOP);
4464         ADD_CONST(RTM_NEWLINKPROP);
4465         ADD_CONST(RTM_DELLINKPROP);
4466         ADD_CONST(RTM_GETLINKPROP);
4467         ADD_CONST(RTM_NEWVLAN);
4468         ADD_CONST(RTM_DELVLAN);
4469         ADD_CONST(RTM_GETVLAN);
4470 
4471         /**
4472          * @typedef
4473          * @name Route types
4474          * @property {number} RTN_UNSPEC - Unspecified route
4475          * @property {number} RTN_UNICAST - Unicast route
4476          * @property {number} RTN_LOCAL - Local route
4477          * @property {number} RTN_BROADCAST - Broadcast route
4478          * @property {number} RTN_ANYCAST - Anycast route
4479          * @property {number} RTN_MULTICAST - Multicast route
4480          * @property {number} RTN_BLACKHOLE - Blackhole route
4481          * @property {number} RTN_UNREACHABLE - Unreachable route
4482          * @property {number} RTN_PROHIBIT - Prohibited route
4483          * @property {number} RTN_THROW - Throw route
4484          * @property {number} RTN_NAT - NAT route
4485          * @property {number} RTN_XRESOLVE - External resolve route
4486          */
4487         ADD_CONST(RTN_UNSPEC);
4488         ADD_CONST(RTN_UNICAST);
4489         ADD_CONST(RTN_LOCAL);
4490         ADD_CONST(RTN_BROADCAST);
4491         ADD_CONST(RTN_ANYCAST);
4492         ADD_CONST(RTN_MULTICAST);
4493         ADD_CONST(RTN_BLACKHOLE);
4494         ADD_CONST(RTN_UNREACHABLE);
4495         ADD_CONST(RTN_PROHIBIT);
4496         ADD_CONST(RTN_THROW);
4497         ADD_CONST(RTN_NAT);
4498         ADD_CONST(RTN_XRESOLVE);
4499 
4500         /**
4501          * @typedef
4502          * @name Route scopes
4503          * @property {number} RT_SCOPE_UNIVERSE - Universe scope
4504          * @property {number} RT_SCOPE_SITE - Site scope
4505          * @property {number} RT_SCOPE_LINK - Link scope
4506          * @property {number} RT_SCOPE_HOST - Host scope
4507          * @property {number} RT_SCOPE_NOWHERE - Nowhere scope
4508          */
4509         ADD_CONST(RT_SCOPE_UNIVERSE);
4510         ADD_CONST(RT_SCOPE_SITE);
4511         ADD_CONST(RT_SCOPE_LINK);
4512         ADD_CONST(RT_SCOPE_HOST);
4513         ADD_CONST(RT_SCOPE_NOWHERE);
4514 
4515         /**
4516          * @typedef
4517          * @name Route tables
4518          * @property {number} RT_TABLE_UNSPEC - Unspecified table
4519          * @property {number} RT_TABLE_COMPAT - Compatibility table
4520          * @property {number} RT_TABLE_DEFAULT - Default table
4521          * @property {number} RT_TABLE_MAIN - Main table
4522          * @property {number} RT_TABLE_LOCAL - Local table
4523          * @property {number} RT_TABLE_MAX - Maximum table
4524          */
4525         ADD_CONST(RT_TABLE_UNSPEC);
4526         ADD_CONST(RT_TABLE_COMPAT);
4527         ADD_CONST(RT_TABLE_DEFAULT);
4528         ADD_CONST(RT_TABLE_MAIN);
4529         ADD_CONST(RT_TABLE_LOCAL);
4530         ADD_CONST(RT_TABLE_MAX);
4531 
4532         /**
4533          * @typedef
4534          * @name Route metrics
4535          * @property {number} RTAX_MTU - Maximum transmission unit
4536          * @property {number} RTAX_HOPLIMIT - Hop limit
4537          * @property {number} RTAX_ADVMSS - Advertised MSS
4538          * @property {number} RTAX_REORDERING - Reordering
4539          * @property {number} RTAX_RTT - Round trip time
4540          * @property {number} RTAX_WINDOW - Window size
4541          * @property {number} RTAX_CWND - Congestion window
4542          * @property {number} RTAX_INITCWND - Initial congestion window
4543          * @property {number} RTAX_INITRWND - Initial receive window
4544          * @property {number} RTAX_FEATURES - Features
4545          * @property {number} RTAX_QUICKACK - Quick acknowledgment
4546          * @property {number} RTAX_CC_ALGO - Congestion control algorithm
4547          * @property {number} RTAX_RTTVAR - RTT variance
4548          * @property {number} RTAX_SSTHRESH - Slow start threshold
4549          * @property {number} RTAX_FASTOPEN_NO_COOKIE - Fast open no cookie
4550          */
4551         /* required to construct RTAX_LOCK */
4552         ADD_CONST(RTAX_MTU);
4553         ADD_CONST(RTAX_HOPLIMIT);
4554         ADD_CONST(RTAX_ADVMSS);
4555         ADD_CONST(RTAX_REORDERING);
4556         ADD_CONST(RTAX_RTT);
4557         ADD_CONST(RTAX_WINDOW);
4558         ADD_CONST(RTAX_CWND);
4559         ADD_CONST(RTAX_INITCWND);
4560         ADD_CONST(RTAX_INITRWND);
4561         ADD_CONST(RTAX_FEATURES);
4562         ADD_CONST(RTAX_QUICKACK);
4563         ADD_CONST(RTAX_CC_ALGO);
4564         ADD_CONST(RTAX_RTTVAR);
4565         ADD_CONST(RTAX_SSTHRESH);
4566         ADD_CONST(RTAX_FASTOPEN_NO_COOKIE);
4567 
4568         /**
4569          * @typedef
4570          * @name Prefix types
4571          * @property {number} PREFIX_UNSPEC - Unspecified prefix
4572          * @property {number} PREFIX_ADDRESS - Address prefix
4573          * @property {number} PREFIX_CACHEINFO - Cache info prefix
4574          */
4575         ADD_CONST(PREFIX_UNSPEC);
4576         ADD_CONST(PREFIX_ADDRESS);
4577         ADD_CONST(PREFIX_CACHEINFO);
4578 
4579         /**
4580          * @typedef
4581          * @name Neighbor discovery user option types
4582          * @property {number} NDUSEROPT_UNSPEC - Unspecified option
4583          * @property {number} NDUSEROPT_SRCADDR - Source address option
4584          */
4585         ADD_CONST(NDUSEROPT_UNSPEC);
4586         ADD_CONST(NDUSEROPT_SRCADDR);
4587 
4588         /**
4589          * @typedef
4590          * @name Multicast groups
4591          * @property {number} RTNLGRP_NONE - No group
4592          * @property {number} RTNLGRP_LINK - Link group
4593          * @property {number} RTNLGRP_NOTIFY - Notify group
4594          * @property {number} RTNLGRP_NEIGH - Neighbor group
4595          * @property {number} RTNLGRP_TC - Traffic control group
4596          * @property {number} RTNLGRP_IPV4_IFADDR - IPv4 interface address group
4597          * @property {number} RTNLGRP_IPV4_MROUTE - IPv4 multicast route group
4598          * @property {number} RTNLGRP_IPV4_ROUTE - IPv4 route group
4599          * @property {number} RTNLGRP_IPV4_RULE - IPv4 rule group
4600          * @property {number} RTNLGRP_IPV6_IFADDR - IPv6 interface address group
4601          * @property {number} RTNLGRP_IPV6_MROUTE - IPv6 multicast route group
4602          * @property {number} RTNLGRP_IPV6_ROUTE - IPv6 route group
4603          * @property {number} RTNLGRP_IPV6_IFINFO - IPv6 interface info group
4604          * @property {number} RTNLGRP_DECnet_IFADDR - DECnet interface address group
4605          * @property {number} RTNLGRP_NOP2 - No operation 2
4606          * @property {number} RTNLGRP_DECnet_ROUTE - DECnet route group
4607          * @property {number} RTNLGRP_DECnet_RULE - DECnet rule group
4608          * @property {number} RTNLGRP_NOP4 - No operation 4
4609          * @property {number} RTNLGRP_IPV6_PREFIX - IPv6 prefix group
4610          * @property {number} RTNLGRP_IPV6_RULE - IPv6 rule group
4611          * @property {number} RTNLGRP_ND_USEROPT - Neighbor discovery user option group
4612          * @property {number} RTNLGRP_PHONET_IFADDR - Phonet interface address group
4613          * @property {number} RTNLGRP_PHONET_ROUTE - Phonet route group
4614          * @property {number} RTNLGRP_DCB - Data Center Bridging group
4615          * @property {number} RTNLGRP_IPV4_NETCONF - IPv4 network configuration group
4616          * @property {number} RTNLGRP_IPV6_NETCONF - IPv6 network configuration group
4617          * @property {number} RTNLGRP_MDB - Multicast database group
4618          * @property {number} RTNLGRP_MPLS_ROUTE - MPLS route group
4619          * @property {number} RTNLGRP_NSID - Network namespace ID group
4620          * @property {number} RTNLGRP_MPLS_NETCONF - MPLS network configuration group
4621          * @property {number} RTNLGRP_IPV4_MROUTE_R - IPv4 multicast route reverse group
4622          * @property {number} RTNLGRP_IPV6_MROUTE_R - IPv6 multicast route reverse group
4623          * @property {number} RTNLGRP_NEXTHOP - Next hop group
4624          * @property {number} RTNLGRP_BRVLAN - Bridge VLAN group
4625          */
4626         ADD_CONST(RTNLGRP_NONE);
4627         ADD_CONST(RTNLGRP_LINK);
4628         ADD_CONST(RTNLGRP_NOTIFY);
4629         ADD_CONST(RTNLGRP_NEIGH);
4630         ADD_CONST(RTNLGRP_TC);
4631         ADD_CONST(RTNLGRP_IPV4_IFADDR);
4632         ADD_CONST(RTNLGRP_IPV4_MROUTE);
4633         ADD_CONST(RTNLGRP_IPV4_ROUTE);
4634         ADD_CONST(RTNLGRP_IPV4_RULE);
4635         ADD_CONST(RTNLGRP_IPV6_IFADDR);
4636         ADD_CONST(RTNLGRP_IPV6_MROUTE);
4637         ADD_CONST(RTNLGRP_IPV6_ROUTE);
4638         ADD_CONST(RTNLGRP_IPV6_IFINFO);
4639         ADD_CONST(RTNLGRP_DECnet_IFADDR);
4640         ADD_CONST(RTNLGRP_NOP2);
4641         ADD_CONST(RTNLGRP_DECnet_ROUTE);
4642         ADD_CONST(RTNLGRP_DECnet_RULE);
4643         ADD_CONST(RTNLGRP_NOP4);
4644         ADD_CONST(RTNLGRP_IPV6_PREFIX);
4645         ADD_CONST(RTNLGRP_IPV6_RULE);
4646         ADD_CONST(RTNLGRP_ND_USEROPT);
4647         ADD_CONST(RTNLGRP_PHONET_IFADDR);
4648         ADD_CONST(RTNLGRP_PHONET_ROUTE);
4649         ADD_CONST(RTNLGRP_DCB);
4650         ADD_CONST(RTNLGRP_IPV4_NETCONF);
4651         ADD_CONST(RTNLGRP_IPV6_NETCONF);
4652         ADD_CONST(RTNLGRP_MDB);
4653         ADD_CONST(RTNLGRP_MPLS_ROUTE);
4654         ADD_CONST(RTNLGRP_NSID);
4655         ADD_CONST(RTNLGRP_MPLS_NETCONF);
4656         ADD_CONST(RTNLGRP_IPV4_MROUTE_R);
4657         ADD_CONST(RTNLGRP_IPV6_MROUTE_R);
4658         ADD_CONST(RTNLGRP_NEXTHOP);
4659         ADD_CONST(RTNLGRP_BRVLAN);
4660 
4661         /**
4662          * @typedef
4663          * @name Route flags
4664          * @property {number} RTM_F_CLONED - Cloned route
4665          * @property {number} RTM_F_EQUALIZE - Equalize route
4666          * @property {number} RTM_F_FIB_MATCH - FIB match
4667          * @property {number} RTM_F_LOOKUP_TABLE - Lookup table
4668          * @property {number} RTM_F_NOTIFY - Notify
4669          * @property {number} RTM_F_PREFIX - Prefix
4670          */
4671         ADD_CONST(RTM_F_CLONED);
4672         ADD_CONST(RTM_F_EQUALIZE);
4673         ADD_CONST(RTM_F_FIB_MATCH);
4674         ADD_CONST(RTM_F_LOOKUP_TABLE);
4675         ADD_CONST(RTM_F_NOTIFY);
4676         ADD_CONST(RTM_F_PREFIX);
4677 
4678         /**
4679          * @typedef
4680          * @name Address families
4681          * @property {number} AF_UNSPEC - Unspecified address family
4682          * @property {number} AF_INET - IPv4 address family
4683          * @property {number} AF_INET6 - IPv6 address family
4684          * @property {number} AF_MPLS - MPLS address family
4685          * @property {number} AF_BRIDGE - Bridge address family
4686          */
4687         ADD_CONST(AF_UNSPEC);
4688         ADD_CONST(AF_INET);
4689         ADD_CONST(AF_INET6);
4690         ADD_CONST(AF_MPLS);
4691         ADD_CONST(AF_BRIDGE);
4692 
4693         /**
4694          * @typedef
4695          * @name Generic Routing Encapsulation flags
4696          * @property {number} GRE_CSUM - Checksum flag
4697          * @property {number} GRE_ROUTING - Routing flag
4698          * @property {number} GRE_KEY - Key flag
4699          * @property {number} GRE_SEQ - Sequence flag
4700          * @property {number} GRE_STRICT - Strict flag
4701          * @property {number} GRE_REC - Record flag
4702          * @property {number} GRE_ACK - Acknowledgment flag
4703          */
4704         ADD_CONST(GRE_CSUM);
4705         ADD_CONST(GRE_ROUTING);
4706         ADD_CONST(GRE_KEY);
4707         ADD_CONST(GRE_SEQ);
4708         ADD_CONST(GRE_STRICT);
4709         ADD_CONST(GRE_REC);
4710         ADD_CONST(GRE_ACK);
4711 
4712         /**
4713          * @typedef
4714          * @name Tunnel encapsulation types
4715          * @property {number} TUNNEL_ENCAP_NONE - No encapsulation
4716          * @property {number} TUNNEL_ENCAP_FOU - Foo over UDP
4717          * @property {number} TUNNEL_ENCAP_GUE - Generic UDP Encapsulation
4718          * @property {number} TUNNEL_ENCAP_MPLS - MPLS encapsulation
4719          */
4720         ADD_CONST(TUNNEL_ENCAP_NONE);
4721         ADD_CONST(TUNNEL_ENCAP_FOU);
4722         ADD_CONST(TUNNEL_ENCAP_GUE);
4723         ADD_CONST(TUNNEL_ENCAP_MPLS);
4724 
4725         /**
4726          * @typedef
4727          * @name Tunnel encapsulation flags
4728          * @property {number} TUNNEL_ENCAP_FLAG_CSUM - Checksum flag
4729          * @property {number} TUNNEL_ENCAP_FLAG_CSUM6 - IPv6 checksum flag
4730          * @property {number} TUNNEL_ENCAP_FLAG_REMCSUM - Remote checksum flag
4731          */
4732         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM);
4733         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM6);
4734         ADD_CONST(TUNNEL_ENCAP_FLAG_REMCSUM);
4735 
4736         /**
4737          * @typedef
4738          * @name IPv6 tunnel flags
4739          * @property {number} IP6_TNL_F_ALLOW_LOCAL_REMOTE - Allow local remote
4740          * @property {number} IP6_TNL_F_IGN_ENCAP_LIMIT - Ignore encapsulation limit
4741          * @property {number} IP6_TNL_F_MIP6_DEV - Mobile IPv6 device
4742          * @property {number} IP6_TNL_F_RCV_DSCP_COPY - Receive DSCP copy
4743          * @property {number} IP6_TNL_F_USE_ORIG_FLOWLABEL - Use original flow label
4744          * @property {number} IP6_TNL_F_USE_ORIG_FWMARK - Use original firewall mark
4745          * @property {number} IP6_TNL_F_USE_ORIG_TCLASS - Use original traffic class
4746          */
4747         ADD_CONST(IP6_TNL_F_ALLOW_LOCAL_REMOTE);
4748         ADD_CONST(IP6_TNL_F_IGN_ENCAP_LIMIT);
4749         ADD_CONST(IP6_TNL_F_MIP6_DEV);
4750         ADD_CONST(IP6_TNL_F_RCV_DSCP_COPY);
4751         ADD_CONST(IP6_TNL_F_USE_ORIG_FLOWLABEL);
4752         ADD_CONST(IP6_TNL_F_USE_ORIG_FWMARK);
4753         ADD_CONST(IP6_TNL_F_USE_ORIG_TCLASS);
4754 
4755         /**
4756          * @typedef
4757          * @name Interface flags
4758          * @property {number} NTF_EXT_LEARNED - Externally learned
4759          * @property {number} NTF_MASTER - Master interface
4760          * @property {number} NTF_OFFLOADED - Offloaded
4761          * @property {number} NTF_PROXY - Proxy
4762          * @property {number} NTF_ROUTER - Router
4763          * @property {number} NTF_SELF - Self
4764          * @property {number} NTF_STICKY - Sticky
4765          * @property {number} NTF_USE - Use
4766          */
4767         ADD_CONST(NTF_EXT_LEARNED);
4768         ADD_CONST(NTF_MASTER);
4769         ADD_CONST(NTF_OFFLOADED);
4770         ADD_CONST(NTF_PROXY);
4771         ADD_CONST(NTF_ROUTER);
4772         ADD_CONST(NTF_SELF);
4773         ADD_CONST(NTF_STICKY);
4774         ADD_CONST(NTF_USE);
4775 
4776         /**
4777          * @typedef
4778          * @name Neighbor states
4779          * @property {number} NUD_DELAY - Delay state
4780          * @property {number} NUD_FAILED - Failed state
4781          * @property {number} NUD_INCOMPLETE - Incomplete state
4782          * @property {number} NUD_NOARP - No ARP
4783          * @property {number} NUD_NONE - No state
4784          * @property {number} NUD_PERMANENT - Permanent state
4785          * @property {number} NUD_PROBE - Probe state
4786          * @property {number} NUD_REACHABLE - Reachable state
4787          * @property {number} NUD_STALE - Stale state
4788          */
4789         ADD_CONST(NUD_DELAY);
4790         ADD_CONST(NUD_FAILED);
4791         ADD_CONST(NUD_INCOMPLETE);
4792         ADD_CONST(NUD_NOARP);
4793         ADD_CONST(NUD_NONE);
4794         ADD_CONST(NUD_PERMANENT);
4795         ADD_CONST(NUD_PROBE);
4796         ADD_CONST(NUD_REACHABLE);
4797         ADD_CONST(NUD_STALE);
4798 
4799         /**
4800          * @typedef
4801          * @name Address flags
4802          * @property {number} IFA_F_DADFAILED - DAD failed
4803          * @property {number} IFA_F_DEPRECATED - Deprecated
4804          * @property {number} IFA_F_HOMEADDRESS - Home address
4805          * @property {number} IFA_F_MANAGETEMPADDR - Manage temporary address
4806          * @property {number} IFA_F_MCAUTOJOIN - Multicast auto join
4807          * @property {number} IFA_F_NODAD - No DAD
4808          * @property {number} IFA_F_NOPREFIXROUTE - No prefix route
4809          * @property {number} IFA_F_OPTIMISTIC - Optimistic
4810          * @property {number} IFA_F_PERMANENT - Permanent
4811          * @property {number} IFA_F_SECONDARY - Secondary
4812          * @property {number} IFA_F_STABLE_PRIVACY - Stable privacy
4813          * @property {number} IFA_F_TEMPORARY - Temporary
4814          * @property {number} IFA_F_TENTATIVE - Tentative
4815          */
4816         ADD_CONST(IFA_F_DADFAILED);
4817         ADD_CONST(IFA_F_DEPRECATED);
4818         ADD_CONST(IFA_F_HOMEADDRESS);
4819         ADD_CONST(IFA_F_MANAGETEMPADDR);
4820         ADD_CONST(IFA_F_MCAUTOJOIN);
4821         ADD_CONST(IFA_F_NODAD);
4822         ADD_CONST(IFA_F_NOPREFIXROUTE);
4823         ADD_CONST(IFA_F_OPTIMISTIC);
4824         ADD_CONST(IFA_F_PERMANENT);
4825         ADD_CONST(IFA_F_SECONDARY);
4826         ADD_CONST(IFA_F_STABLE_PRIVACY);
4827         ADD_CONST(IFA_F_TEMPORARY);
4828         ADD_CONST(IFA_F_TENTATIVE);
4829 
4830         /**
4831          * @typedef
4832          * @name FIB rule flags
4833          * @property {number} FIB_RULE_PERMANENT - Permanent rule
4834          * @property {number} FIB_RULE_INVERT - Invert rule
4835          * @property {number} FIB_RULE_UNRESOLVED - Unresolved rule
4836          * @property {number} FIB_RULE_IIF_DETACHED - Interface detached
4837          * @property {number} FIB_RULE_DEV_DETACHED - Device detached
4838          * @property {number} FIB_RULE_OIF_DETACHED - Output interface detached
4839          */
4840         ADD_CONST(FIB_RULE_PERMANENT);
4841         ADD_CONST(FIB_RULE_INVERT);
4842         ADD_CONST(FIB_RULE_UNRESOLVED);
4843         ADD_CONST(FIB_RULE_IIF_DETACHED);
4844         ADD_CONST(FIB_RULE_DEV_DETACHED);
4845         ADD_CONST(FIB_RULE_OIF_DETACHED);
4846 
4847         /**
4848          * @typedef
4849          * @name FIB rule actions
4850          * @property {number} FR_ACT_TO_TBL - To table action
4851          * @property {number} FR_ACT_GOTO - Goto action
4852          * @property {number} FR_ACT_NOP - No operation action
4853          * @property {number} FR_ACT_BLACKHOLE - Blackhole action
4854          * @property {number} FR_ACT_UNREACHABLE - Unreachable action
4855          * @property {number} FR_ACT_PROHIBIT - Prohibit action
4856          */
4857         ADD_CONST(FR_ACT_TO_TBL);
4858         ADD_CONST(FR_ACT_GOTO);
4859         ADD_CONST(FR_ACT_NOP);
4860         ADD_CONST(FR_ACT_BLACKHOLE);
4861         ADD_CONST(FR_ACT_UNREACHABLE);
4862         ADD_CONST(FR_ACT_PROHIBIT);
4863 
4864         /**
4865          * @typedef
4866          * @name Network configuration indices
4867          * @property {number} NETCONFA_IFINDEX_ALL - All interfaces
4868          * @property {number} NETCONFA_IFINDEX_DEFAULT - Default interface
4869          */
4870         ADD_CONST(NETCONFA_IFINDEX_ALL);
4871         ADD_CONST(NETCONFA_IFINDEX_DEFAULT);
4872 
4873         /**
4874          * @typedef
4875          * @name Bridge flags
4876          * @property {number} BRIDGE_FLAGS_MASTER - Master flag
4877          * @property {number} BRIDGE_FLAGS_SELF - Self flag
4878          */
4879         ADD_CONST(BRIDGE_FLAGS_MASTER);
4880         ADD_CONST(BRIDGE_FLAGS_SELF);
4881 
4882         /**
4883          * @typedef
4884          * @name Bridge modes
4885          * @property {number} BRIDGE_MODE_VEB - Virtual Ethernet Bridge mode
4886          * @property {number} BRIDGE_MODE_VEPA - Virtual Ethernet Port Aggregator mode
4887          * @property {number} BRIDGE_MODE_UNDEF - Undefined mode
4888          * @property {number} BRIDGE_MODE_UNSPEC - Unspecified mode
4889          * @property {number} BRIDGE_MODE_HAIRPIN - Hairpin mode
4890          */
4891         ADD_CONST(BRIDGE_MODE_VEB);
4892         ADD_CONST(BRIDGE_MODE_VEPA);
4893         ADD_CONST(BRIDGE_MODE_UNDEF);
4894         ADD_CONST(BRIDGE_MODE_UNSPEC);
4895         ADD_CONST(BRIDGE_MODE_HAIRPIN);
4896 
4897         /**
4898          * @typedef
4899          * @name Bridge VLAN information flags
4900          * @property {number} BRIDGE_VLAN_INFO_MASTER - Master VLAN info
4901          * @property {number} BRIDGE_VLAN_INFO_PVID - Primary VLAN ID
4902          * @property {number} BRIDGE_VLAN_INFO_UNTAGGED - Untagged VLAN
4903          * @property {number} BRIDGE_VLAN_INFO_RANGE_BEGIN - Range begin
4904          * @property {number} BRIDGE_VLAN_INFO_RANGE_END - Range end
4905          * @property {number} BRIDGE_VLAN_INFO_BRENTRY - Bridge entry
4906          */
4907         ADD_CONST(BRIDGE_VLAN_INFO_MASTER);
4908         ADD_CONST(BRIDGE_VLAN_INFO_PVID);
4909         ADD_CONST(BRIDGE_VLAN_INFO_UNTAGGED);
4910         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_BEGIN);
4911         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_END);
4912         ADD_CONST(BRIDGE_VLAN_INFO_BRENTRY);
4913 
4914         ucv_object_add(scope, "const", c);
4915 };
4916 
4917 static const uc_function_list_t global_fns[] = {
4918         { "error",              uc_nl_error },
4919         { "request",    uc_nl_request },
4920         { "listener",   uc_nl_listener },
4921 };
4922 
4923 static const uc_function_list_t listener_fns[] = {
4924         { "set_commands",       uc_nl_listener_set_commands },
4925         { "close",                      uc_nl_listener_close },
4926 };
4927 
4928 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
4929 {
4930         uc_function_list_register(scope, global_fns);
4931 
4932         listener_type = uc_type_declare(vm, "rtnl.listener", listener_fns, uc_nl_listener_free);
4933         listener_registry = ucv_array_new(vm);
4934 
4935         uc_vm_registry_set(vm, "rtnl.registry", listener_registry);
4936 
4937         register_constants(vm, scope);
4938 }
4939 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt