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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt