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

Sources/ucode/lib/rtnl.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt