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

Sources/ucode/lib/rtnl.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt