• 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 #include <stdio.h>
 18 #include <stdint.h>
 19 #include <stdbool.h>
 20 #include <stdarg.h>
 21 #include <unistd.h>
 22 #include <errno.h>
 23 #include <string.h>
 24 #include <limits.h>
 25 #include <math.h>
 26 #include <assert.h>
 27 #include <endian.h>
 28 
 29 #include <netinet/ether.h>
 30 #include <arpa/inet.h>
 31 #include <netlink/msg.h>
 32 #include <netlink/attr.h>
 33 #include <netlink/socket.h>
 34 
 35 #include <linux/rtnetlink.h>
 36 #include <linux/if_tunnel.h>
 37 #include <linux/ip6_tunnel.h>
 38 #include <linux/lwtunnel.h>
 39 #include <linux/mpls.h>
 40 #include <linux/mpls_iptunnel.h>
 41 #include <linux/seg6.h>
 42 #include <linux/seg6_iptunnel.h>
 43 #include <linux/seg6_hmac.h>
 44 #include <linux/veth.h>
 45 #include <linux/ila.h>
 46 #include <linux/fib_rules.h>
 47 #include <linux/if_addrlabel.h>
 48 #include <linux/if_bridge.h>
 49 #include <linux/netconf.h>
 50 #include <linux/ipv6.h>
 51 
 52 #include "ucode/module.h"
 53 
 54 #define err_return(code, ...) do { set_error(code, __VA_ARGS__); return NULL; } while(0)
 55 
 56 #define NLM_F_STRICT_CHK (1 << 15)
 57 
 58 /* Can't use net/if.h for declarations as it clashes with linux/if.h
 59  * on certain musl versions.
 60  * Ref: https://www.openwall.com/lists/musl/2017/04/16/1 */
 61 extern unsigned int if_nametoindex (const char *);
 62 extern char *if_indextoname (unsigned int ifindex, char *ifname);
 63 
 64 static struct {
 65         int code;
 66         char *msg;
 67 } last_error;
 68 
 69 __attribute__((format(printf, 2, 3))) static void
 70 set_error(int errcode, const char *fmt, ...) {
 71         va_list ap;
 72 
 73         free(last_error.msg);
 74 
 75         last_error.code = errcode;
 76         last_error.msg = NULL;
 77 
 78         if (fmt) {
 79                 va_start(ap, fmt);
 80                 xvasprintf(&last_error.msg, fmt, ap);
 81                 va_end(ap);
 82         }
 83 }
 84 
 85 typedef struct {
 86         uint8_t family;
 87         uint8_t mask;
 88         uint8_t alen;
 89         uint8_t bitlen;
 90         union {
 91                 struct in_addr in;
 92                 struct in6_addr in6;
 93                 struct mpls_label mpls[16];
 94         } addr;
 95 } uc_nl_cidr_t;
 96 
 97 static bool
 98 uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
 99 {
100         uint64_t u;
101 
102         u = ucv_to_unsigned(val);
103 
104         if (errno != 0 || u > UINT32_MAX)
105                 return false;
106 
107         *n = (uint32_t)u;
108 
109         return true;
110 }
111 
112 static bool
113 uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
114 {
115         int64_t i;
116 
117         i = ucv_to_integer(val);
118 
119         if (errno != 0 || i < INT32_MIN || i > INT32_MAX)
120                 return false;
121 
122         *n = (uint32_t)i;
123 
124         return true;
125 }
126 
127 static bool
128 uc_nl_parse_u64(uc_value_t *val, uint64_t *n)
129 {
130         *n = ucv_to_unsigned(val);
131 
132         return (errno == 0);
133 }
134 
135 static const char *
136 addr64_ntop(const void *addr, char *buf, size_t buflen)
137 {
138         const union { uint64_t u64; uint16_t u16[4]; } *a64 = addr;
139         int len;
140 
141         errno = 0;
142 
143         len = snprintf(buf, buflen, "%04x:%04x:%04x:%04x",
144                        ntohs(a64->u16[0]), ntohs(a64->u16[1]),
145                        ntohs(a64->u16[2]), ntohs(a64->u16[3]));
146 
147         if ((size_t)len >= buflen) {
148                 errno = ENOSPC;
149 
150                 return NULL;
151         }
152 
153         return buf;
154 }
155 
156 static int
157 addr64_pton(const char *src, void *dst)
158 {
159         union { uint64_t u64; uint16_t u16[4]; } *a64 = dst;
160         unsigned long n;
161         size_t i;
162         char *e;
163 
164         for (i = 0; i < ARRAY_SIZE(a64->u16); i++) {
165                 n = strtoul(src, &e, 16);
166 
167                 if (e == src || n > 0xffff)
168                         return -1;
169 
170                 a64->u16[i] = htons(n);
171 
172                 if (*e == 0)
173                         break;
174 
175                 if (i >= 3 || *e != ':')
176                         return -1;
177 
178                 src += (e - src) + 1;
179         }
180 
181         return 0;
182 }
183 
184 static const char *
185 mpls_ntop(const void *addr, size_t addrlen, char *buf, size_t buflen)
186 {
187         const struct mpls_label *p = addr;
188         size_t remlen = buflen;
189         uint32_t entry, label;
190         char *s = buf;
191         int len;
192 
193         errno = 0;
194 
195         while (addrlen >= sizeof(*p)) {
196                 entry = ntohl(p->entry);
197                 label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT;
198 
199                 len = snprintf(s, remlen, "%u", label);
200 
201                 if ((size_t)len >= remlen)
202                         break;
203 
204                 if (entry & MPLS_LS_S_MASK)
205                         return buf;
206 
207                 s += len;
208                 remlen -= len;
209 
210                 if (remlen) {
211                         *s++ = '/';
212                         remlen--;
213                 }
214 
215                 p++;
216 
217                 addrlen -= sizeof(*p);
218         }
219 
220         errno = ENOSPC;
221 
222         return NULL;
223 }
224 
225 static int
226 mpls_pton(int af, const char *src, void *dst, size_t dstlen)
227 {
228         size_t max = dstlen / sizeof(struct mpls_label);
229         struct mpls_label *p = dst;
230         uint32_t label;
231         char *e;
232 
233         errno = 0;
234 
235         if (af != AF_MPLS) {
236                 errno = EAFNOSUPPORT;
237 
238                 return -1;
239         }
240 
241         while (max > 0) {
242                 label = strtoul(src, &e, 0);
243 
244                 if (label >= (1 << 20))
245                         return 0;
246 
247                 if (e == src)
248                         return 0;
249 
250                 p->entry = htonl(label << MPLS_LS_LABEL_SHIFT);
251 
252                 if (*e == 0) {
253                         p->entry |= htonl(1 << MPLS_LS_S_SHIFT);
254 
255                         return 1;
256                 }
257 
258                 if (*e != '/')
259                         return 0;
260 
261                 src += (e - src) + 1;
262                 max--;
263                 p++;
264         }
265 
266         errno = ENOSPC;
267 
268         return -1;
269 }
270 
271 static bool
272 uc_nl_parse_cidr(uc_vm_t *vm, uc_value_t *val, uc_nl_cidr_t *p)
273 {
274         char *s = ucv_to_string(vm, val);
275         struct in6_addr mask6 = { 0 };
276         struct in_addr mask = { 0 };
277         bool valid = true;
278         char *m, *e;
279         long n = 0;
280         size_t i;
281 
282         if (!s)
283                 return false;
284 
285         m = strchr(s, '/');
286 
287         if (m)
288                 *m++ = '\0';
289 
290         if (inet_pton(AF_INET6, s, &p->addr.in6) == 1) {
291                 if (m) {
292                         if (inet_pton(AF_INET6, m, &mask6) == 1) {
293                                 while (n < 128 && (mask6.s6_addr[n / 8] << (n % 8)) & 128)
294                                         n++;
295                         }
296                         else {
297                                 n = strtol(m, &e, 10);
298 
299                                 if (e == m || *e || n < 0 || n > 128)
300                                         valid = false;
301                         }
302 
303                         p->mask = (uint8_t)n;
304                 }
305                 else {
306                         p->mask = 128;
307                 }
308 
309                 p->family = AF_INET6;
310                 p->alen = sizeof(mask6);
311                 p->bitlen = p->alen * 8;
312         }
313         else if (strchr(s, '.') && inet_pton(AF_INET, s, &p->addr.in) == 1) {
314                 if (m) {
315                         if (inet_pton(AF_INET, m, &mask) == 1) {
316                                 mask.s_addr = ntohl(mask.s_addr);
317 
318                                 while (n < 32 && (mask.s_addr << n) & 0x80000000)
319                                         n++;
320                         }
321                         else {
322                                 n = strtol(m, &e, 10);
323 
324                                 if (e == m || *e || n < 0 || n > 32)
325                                         valid = false;
326                         }
327 
328                         p->mask = (uint8_t)n;
329                 }
330                 else {
331                         p->mask = 32;
332                 }
333 
334                 p->family = AF_INET;
335                 p->alen = sizeof(mask);
336                 p->bitlen = p->alen * 8;
337         }
338         else {
339                 if (m)
340                         m[-1] = '/';
341 
342                 if (mpls_pton(AF_MPLS, s, &p->addr.mpls, sizeof(p->addr.mpls)) == 1) {
343                         p->family = AF_MPLS;
344                         p->alen = 0;
345 
346                         for (i = 0; i < ARRAY_SIZE(p->addr.mpls); i++) {
347                                 p->alen += sizeof(struct mpls_label);
348 
349                                 if (ntohl(p->addr.mpls[i].entry) & MPLS_LS_S_MASK)
350                                         break;
351                         }
352 
353                         p->bitlen = p->alen * 8;
354                         p->mask = p->bitlen;
355                 }
356                 else {
357                         valid = false;
358                 }
359         }
360 
361         free(s);
362 
363         return valid;
364 }
365 
366 typedef enum {
367         DT_FLAG,
368         DT_BOOL,
369         DT_U8,
370         DT_U16,
371         DT_U32,
372         DT_S32,
373         DT_U64,
374         DT_STRING,
375         DT_NETDEV,
376         DT_LLADDR,
377         DT_INADDR,
378         DT_IN6ADDR,
379         DT_U64ADDR,
380         DT_MPLSADDR,
381         DT_ANYADDR,
382         DT_BRIDGEID,
383         DT_LINKINFO,
384         DT_MULTIPATH,
385         DT_NUMRANGE,
386         DT_FLAGS,
387         DT_ENCAP,
388         DT_SRH,
389         DT_IPOPTS,
390         DT_U32_OR_MEMBER,
391         DT_NESTED,
392 } uc_nl_attr_datatype_t;
393 
394 enum {
395         DF_NO_SET = (1 << 0),
396         DF_NO_GET = (1 << 1),
397         DF_ALLOW_NONE = (1 << 2),
398         DF_BYTESWAP = (1 << 3),
399         DF_MAX_1 = (1 << 4),
400         DF_MAX_255 = (1 << 5),
401         DF_MAX_65535 = (1 << 6),
402         DF_MAX_16777215 = (1 << 7),
403         DF_STORE_MASK = (1 << 8),
404         DF_MULTIPLE = (1 << 9),
405         DF_FLAT = (1 << 10),
406         DF_FAMILY_HINT = (1 << 11),
407 };
408 
409 typedef struct uc_nl_attr_spec {
410         size_t attr;
411         const char *key;
412         uc_nl_attr_datatype_t type;
413         uint32_t flags;
414         const void *auxdata;
415 } uc_nl_attr_spec_t;
416 
417 typedef struct uc_nl_nested_spec {
418         size_t headsize;
419         size_t nattrs;
420         const uc_nl_attr_spec_t attrs[];
421 } uc_nl_nested_spec_t;
422 
423 #define SIZE(type) (void *)(uintptr_t)sizeof(struct type)
424 #define MEMBER(type, field) (void *)(uintptr_t)offsetof(struct type, field)
425 
426 static const uc_nl_nested_spec_t route_cacheinfo_rta = {
427         .headsize = NLA_ALIGN(sizeof(struct rta_cacheinfo)),
428         .nattrs = 8,
429         .attrs = {
430                 { RTA_UNSPEC, "clntref", DT_U32, 0, MEMBER(rta_cacheinfo, rta_clntref) },
431                 { RTA_UNSPEC, "lastuse", DT_U32, 0, MEMBER(rta_cacheinfo, rta_lastuse) },
432                 { RTA_UNSPEC, "expires", DT_S32, 0, MEMBER(rta_cacheinfo, rta_expires) },
433                 { RTA_UNSPEC, "error", DT_U32, 0, MEMBER(rta_cacheinfo, rta_error) },
434                 { RTA_UNSPEC, "used", DT_U32, 0, MEMBER(rta_cacheinfo, rta_used) },
435                 { RTA_UNSPEC, "id", DT_U32, 0, MEMBER(rta_cacheinfo, rta_id) },
436                 { RTA_UNSPEC, "ts", DT_U32, 0, MEMBER(rta_cacheinfo, rta_ts) },
437                 { RTA_UNSPEC, "tsage", DT_U32, 0, MEMBER(rta_cacheinfo, rta_tsage) },
438         }
439 };
440 
441 static const uc_nl_nested_spec_t route_metrics_rta = {
442         .headsize = 0,
443         .nattrs = 16,
444         .attrs = {
445                 { RTAX_MTU, "mtu", DT_U32, 0, NULL },
446                 { RTAX_HOPLIMIT, "hoplimit", DT_U32, DF_MAX_255, NULL },
447                 { RTAX_ADVMSS, "advmss", DT_U32, 0, NULL },
448                 { RTAX_REORDERING, "reordering", DT_U32, 0, NULL },
449                 { RTAX_RTT, "rtt", DT_U32, 0, NULL },
450                 { RTAX_WINDOW, "window", DT_U32, 0, NULL },
451                 { RTAX_CWND, "cwnd", DT_U32, 0, NULL },
452                 { RTAX_INITCWND, "initcwnd", DT_U32, 0, NULL },
453                 { RTAX_INITRWND, "initrwnd", DT_U32, 0, NULL },
454                 { RTAX_FEATURES, "ecn", DT_U32, DF_MAX_1, NULL },
455                 { RTAX_QUICKACK, "quickack", DT_U32, DF_MAX_1, NULL },
456                 { RTAX_CC_ALGO, "cc_algo", DT_STRING, 0, NULL },
457                 { RTAX_RTTVAR, "rttvar", DT_U32, 0, NULL },
458                 { RTAX_SSTHRESH, "ssthresh", DT_U32, 0, NULL },
459                 { RTAX_FASTOPEN_NO_COOKIE, "fastopen_no_cookie", DT_U32, DF_MAX_1, NULL },
460                 { RTAX_LOCK, "lock", DT_U32, 0, NULL },
461         }
462 };
463 
464 static const uc_nl_nested_spec_t route_msg = {
465         .headsize = NLA_ALIGN(sizeof(struct rtmsg)),
466         .nattrs = 28,
467         .attrs = {
468                 { RTA_UNSPEC, "family", DT_U8, 0, MEMBER(rtmsg, rtm_family) },
469                 { RTA_UNSPEC, "tos", DT_U8, 0, MEMBER(rtmsg, rtm_tos) },
470                 { RTA_UNSPEC, "protocol", DT_U8, 0, MEMBER(rtmsg, rtm_protocol) },
471                 { RTA_UNSPEC, "scope", DT_U8, 0, MEMBER(rtmsg, rtm_scope) },
472                 { RTA_UNSPEC, "type", DT_U8, 0, MEMBER(rtmsg, rtm_type) },
473                 { RTA_UNSPEC, "flags", DT_U32, 0, MEMBER(rtmsg, rtm_flags) },
474                 { RTA_SRC, "src", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(rtmsg, rtm_src_len) },
475                 { RTA_DST, "dst", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(rtmsg, rtm_dst_len) },
476                 { RTA_IIF, "iif", DT_NETDEV, 0, NULL },
477                 { RTA_OIF, "oif", DT_NETDEV, 0, NULL },
478                 { RTA_GATEWAY, "gateway", DT_ANYADDR, DF_FAMILY_HINT, NULL },
479                 { RTA_PRIORITY, "priority", DT_U32, 0, NULL },
480                 { RTA_PREFSRC, "prefsrc", DT_ANYADDR, DF_FAMILY_HINT, NULL },
481                 { RTA_METRICS, "metrics", DT_NESTED, 0, &route_metrics_rta },
482                 { RTA_MULTIPATH, "multipath", DT_MULTIPATH, 0, NULL },
483                 { RTA_FLOW, "flow", DT_U32, 0, NULL },
484                 { RTA_CACHEINFO, "cacheinfo", DT_NESTED, DF_NO_SET, &route_cacheinfo_rta },
485                 { RTA_TABLE, "table", DT_U32_OR_MEMBER, DF_MAX_255, MEMBER(rtmsg, rtm_table) },
486                 { RTA_MARK, "mark", DT_U32, 0, NULL },
487                 //RTA_MFC_STATS,
488                 { RTA_PREF, "pref", DT_U8, 0, NULL },
489                 { RTA_ENCAP, "encap", DT_ENCAP, 0, NULL },
490                 { RTA_EXPIRES, "expires", DT_U32, 0, NULL },
491                 { RTA_UID, "uid", DT_U32, 0, NULL },
492                 { RTA_TTL_PROPAGATE, "ttl_propagate", DT_BOOL, 0, NULL },
493                 { RTA_IP_PROTO, "ip_proto", DT_U8, 0, NULL },
494                 { RTA_SPORT, "sport", DT_U16, DF_BYTESWAP, NULL },
495                 { RTA_DPORT, "dport", DT_U16, DF_BYTESWAP, NULL },
496                 { RTA_NH_ID, "nh_id", DT_U32, 0, NULL },
497         }
498 };
499 
500 static const uc_nl_attr_spec_t route_encap_mpls_attrs[] = {
501         { MPLS_IPTUNNEL_DST, "dst", DT_MPLSADDR, 0, NULL },
502         { MPLS_IPTUNNEL_TTL, "ttl", DT_U8, 0, NULL },
503 };
504 
505 static const uc_nl_attr_spec_t route_encap_ip_attrs[] = {
506         { LWTUNNEL_IP_ID, "id", DT_U64, DF_BYTESWAP, NULL },
507         { LWTUNNEL_IP_DST, "dst", DT_INADDR, 0, NULL },
508         { LWTUNNEL_IP_SRC, "src", DT_INADDR, 0, NULL },
509         { LWTUNNEL_IP_TOS, "tos", DT_U8, 0, NULL },
510         { LWTUNNEL_IP_TTL, "ttl", DT_U8, 0, NULL },
511         { LWTUNNEL_IP_OPTS, "opts", DT_IPOPTS, 0, NULL },
512         { LWTUNNEL_IP_FLAGS, "flags", DT_U16, 0, NULL },
513 };
514 
515 static const uc_nl_attr_spec_t route_encap_ila_attrs[] = {
516         { ILA_ATTR_LOCATOR, "locator", DT_U64ADDR, 0, NULL },
517         { ILA_ATTR_CSUM_MODE, "csum_mode", DT_U8, 0, NULL },
518         { ILA_ATTR_IDENT_TYPE, "ident_type", DT_U8, 0, NULL },
519         { ILA_ATTR_HOOK_TYPE, "hook_type", DT_U8, 0, NULL },
520 };
521 
522 static const uc_nl_attr_spec_t route_encap_ip6_attrs[] = {
523         { LWTUNNEL_IP6_ID, "id", DT_U64, DF_BYTESWAP, NULL },
524         { LWTUNNEL_IP6_DST, "dst", DT_IN6ADDR, 0, NULL },
525         { LWTUNNEL_IP6_SRC, "src", DT_IN6ADDR, 0, NULL },
526         { LWTUNNEL_IP6_TC, "tc", DT_U32, 0, NULL },
527         { LWTUNNEL_IP6_HOPLIMIT, "hoplimit", DT_U8, 0, NULL },
528         { LWTUNNEL_IP6_OPTS, "opts", DT_IPOPTS, 0, NULL },
529         { LWTUNNEL_IP6_FLAGS, "flags", DT_U16, 0, NULL },
530 };
531 
532 static const uc_nl_attr_spec_t route_encap_seg6_attrs[] = {
533         { SEG6_IPTUNNEL_SRH, "srh", DT_SRH, 0, NULL },
534 };
535 
536 #define IPV4_DEVCONF_ENTRY(name) ((void *)((IPV4_DEVCONF_##name - 1) * sizeof(uint32_t)))
537 
538 static const uc_nl_nested_spec_t link_attrs_af_spec_inet_devconf_rta = {
539         .headsize = NLA_ALIGN(IPV4_DEVCONF_MAX * sizeof(uint32_t)),
540         .nattrs = 32,
541         .attrs = {
542                 { 0, "forwarding", DT_U32, 0, IPV4_DEVCONF_ENTRY(FORWARDING) },
543                 { 0, "mc_forwarding", DT_U32, 0, IPV4_DEVCONF_ENTRY(MC_FORWARDING) },
544                 { 0, "proxy_arp", DT_U32, 0, IPV4_DEVCONF_ENTRY(PROXY_ARP) },
545                 { 0, "accept_redirects", DT_U32, 0, IPV4_DEVCONF_ENTRY(ACCEPT_REDIRECTS) },
546                 { 0, "secure_redirects", DT_U32, 0, IPV4_DEVCONF_ENTRY(SECURE_REDIRECTS) },
547                 { 0, "send_redirects", DT_U32, 0, IPV4_DEVCONF_ENTRY(SEND_REDIRECTS) },
548                 { 0, "shared_media", DT_U32, 0, IPV4_DEVCONF_ENTRY(SHARED_MEDIA) },
549                 { 0, "rp_filter", DT_U32, 0, IPV4_DEVCONF_ENTRY(RP_FILTER) },
550                 { 0, "accept_source_route", DT_U32, 0, IPV4_DEVCONF_ENTRY(ACCEPT_SOURCE_ROUTE) },
551                 { 0, "bootp_relay", DT_U32, 0, IPV4_DEVCONF_ENTRY(BOOTP_RELAY) },
552                 { 0, "log_martians", DT_U32, 0, IPV4_DEVCONF_ENTRY(LOG_MARTIANS) },
553                 { 0, "tag", DT_U32, 0, IPV4_DEVCONF_ENTRY(TAG) },
554                 { 0, "arpfilter", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARPFILTER) },
555                 { 0, "medium_id", DT_U32, 0, IPV4_DEVCONF_ENTRY(MEDIUM_ID) },
556                 { 0, "noxfrm", DT_U32, 0, IPV4_DEVCONF_ENTRY(NOXFRM) },
557                 { 0, "nopolicy", DT_U32, 0, IPV4_DEVCONF_ENTRY(NOPOLICY) },
558                 { 0, "force_igmp_version", DT_U32, 0, IPV4_DEVCONF_ENTRY(FORCE_IGMP_VERSION) },
559                 { 0, "arp_announce", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_ANNOUNCE) },
560                 { 0, "arp_ignore", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_IGNORE) },
561                 { 0, "promote_secondaries", DT_U32, 0, IPV4_DEVCONF_ENTRY(PROMOTE_SECONDARIES) },
562                 { 0, "arp_accept", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_ACCEPT) },
563                 { 0, "arp_notify", DT_U32, 0, IPV4_DEVCONF_ENTRY(ARP_NOTIFY) },
564                 { 0, "accept_local", DT_U32, 0, IPV4_DEVCONF_ENTRY(ACCEPT_LOCAL) },
565                 { 0, "src_vmark", DT_U32, 0, IPV4_DEVCONF_ENTRY(SRC_VMARK) },
566                 { 0, "proxy_arp_pvlan", DT_U32, 0, IPV4_DEVCONF_ENTRY(PROXY_ARP_PVLAN) },
567                 { 0, "route_localnet", DT_U32, 0, IPV4_DEVCONF_ENTRY(ROUTE_LOCALNET) },
568                 { 0, "igmpv2_unsolicited_report_interval", DT_U32, 0, IPV4_DEVCONF_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL) },
569                 { 0, "igmpv3_unsolicited_report_interval", DT_U32, 0, IPV4_DEVCONF_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL) },
570                 { 0, "ignore_routes_with_linkdown", DT_U32, 0, IPV4_DEVCONF_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN) },
571                 { 0, "drop_unicast_in_l2_multicast", DT_U32, 0, IPV4_DEVCONF_ENTRY(DROP_UNICAST_IN_L2_MULTICAST) },
572                 { 0, "drop_gratuitous_arp", DT_U32, 0, IPV4_DEVCONF_ENTRY(DROP_GRATUITOUS_ARP) },
573                 { 0, "bc_forwarding", DT_U32, 0, IPV4_DEVCONF_ENTRY(BC_FORWARDING) },
574         }
575 };
576 
577 static const uc_nl_nested_spec_t link_attrs_af_spec_inet_rta = {
578         .headsize = 0,
579         .nattrs = 1,
580         .attrs = {
581                 { IFLA_INET_CONF, "conf", DT_NESTED, 0, &link_attrs_af_spec_inet_devconf_rta },
582         }
583 };
584 
585 #define IPV6_DEVCONF_ENTRY(name) ((void *)(DEVCONF_##name * sizeof(uint32_t)))
586 
587 static const uc_nl_nested_spec_t link_attrs_af_spec_inet6_devconf_rta = {
588         .headsize = NLA_ALIGN(DEVCONF_MAX * sizeof(uint32_t)),
589         .nattrs = 53,
590         .attrs = {
591                 { 0, "forwarding", DT_S32, 0, IPV6_DEVCONF_ENTRY(FORWARDING) },
592                 { 0, "hoplimit", DT_S32, 0, IPV6_DEVCONF_ENTRY(HOPLIMIT) },
593                 { 0, "mtu6", DT_S32, 0, IPV6_DEVCONF_ENTRY(MTU6) },
594                 { 0, "accept_ra", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA) },
595                 { 0, "accept_redirects", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_REDIRECTS) },
596                 { 0, "autoconf", DT_S32, 0, IPV6_DEVCONF_ENTRY(AUTOCONF) },
597                 { 0, "dad_transmits", DT_S32, 0, IPV6_DEVCONF_ENTRY(DAD_TRANSMITS) },
598                 { 0, "rtr_solicits", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICITS) },
599                 { 0, "rtr_solicit_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICIT_INTERVAL) },
600                 { 0, "rtr_solicit_delay", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICIT_DELAY) },
601                 { 0, "use_tempaddr", DT_S32, 0, IPV6_DEVCONF_ENTRY(USE_TEMPADDR) },
602                 { 0, "temp_valid_lft", DT_S32, 0, IPV6_DEVCONF_ENTRY(TEMP_VALID_LFT) },
603                 { 0, "temp_prefered_lft", DT_S32, 0, IPV6_DEVCONF_ENTRY(TEMP_PREFERED_LFT) },
604                 { 0, "regen_max_retry", DT_S32, 0, IPV6_DEVCONF_ENTRY(REGEN_MAX_RETRY) },
605                 { 0, "max_desync_factor", DT_S32, 0, IPV6_DEVCONF_ENTRY(MAX_DESYNC_FACTOR) },
606                 { 0, "max_addresses", DT_S32, 0, IPV6_DEVCONF_ENTRY(MAX_ADDRESSES) },
607                 { 0, "force_mld_version", DT_S32, 0, IPV6_DEVCONF_ENTRY(FORCE_MLD_VERSION) },
608                 { 0, "accept_ra_defrtr", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_DEFRTR) },
609                 { 0, "accept_ra_pinfo", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_PINFO) },
610                 { 0, "accept_ra_rtr_pref", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_RTR_PREF) },
611                 { 0, "rtr_probe_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_PROBE_INTERVAL) },
612                 { 0, "accept_ra_rt_info_max_plen", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_RT_INFO_MAX_PLEN) },
613                 { 0, "proxy_ndp", DT_S32, 0, IPV6_DEVCONF_ENTRY(PROXY_NDP) },
614                 { 0, "optimistic_dad", DT_S32, 0, IPV6_DEVCONF_ENTRY(OPTIMISTIC_DAD) },
615                 { 0, "accept_source_route", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_SOURCE_ROUTE) },
616                 { 0, "mc_forwarding", DT_S32, 0, IPV6_DEVCONF_ENTRY(MC_FORWARDING) },
617                 { 0, "disable_ipv6", DT_S32, 0, IPV6_DEVCONF_ENTRY(DISABLE_IPV6) },
618                 { 0, "accept_dad", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_DAD) },
619                 { 0, "force_tllao", DT_S32, 0, IPV6_DEVCONF_ENTRY(FORCE_TLLAO) },
620                 { 0, "ndisc_notify", DT_S32, 0, IPV6_DEVCONF_ENTRY(NDISC_NOTIFY) },
621                 { 0, "mldv1_unsolicited_report_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(MLDV1_UNSOLICITED_REPORT_INTERVAL) },
622                 { 0, "mldv2_unsolicited_report_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(MLDV2_UNSOLICITED_REPORT_INTERVAL) },
623                 { 0, "suppress_frag_ndisc", DT_S32, 0, IPV6_DEVCONF_ENTRY(SUPPRESS_FRAG_NDISC) },
624                 { 0, "accept_ra_from_local", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_FROM_LOCAL) },
625                 { 0, "use_optimistic", DT_S32, 0, IPV6_DEVCONF_ENTRY(USE_OPTIMISTIC) },
626                 { 0, "accept_ra_mtu", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_MTU) },
627                 { 0, "stable_secret", DT_S32, 0, IPV6_DEVCONF_ENTRY(STABLE_SECRET) },
628                 { 0, "use_oif_addrs_only", DT_S32, 0, IPV6_DEVCONF_ENTRY(USE_OIF_ADDRS_ONLY) },
629                 { 0, "accept_ra_min_hop_limit", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_MIN_HOP_LIMIT) },
630                 { 0, "ignore_routes_with_linkdown", DT_S32, 0, IPV6_DEVCONF_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN) },
631                 { 0, "drop_unicast_in_l2_multicast", DT_S32, 0, IPV6_DEVCONF_ENTRY(DROP_UNICAST_IN_L2_MULTICAST) },
632                 { 0, "drop_unsolicited_na", DT_S32, 0, IPV6_DEVCONF_ENTRY(DROP_UNSOLICITED_NA) },
633                 { 0, "keep_addr_on_down", DT_S32, 0, IPV6_DEVCONF_ENTRY(KEEP_ADDR_ON_DOWN) },
634                 { 0, "rtr_solicit_max_interval", DT_S32, 0, IPV6_DEVCONF_ENTRY(RTR_SOLICIT_MAX_INTERVAL) },
635                 { 0, "seg6_enabled", DT_S32, 0, IPV6_DEVCONF_ENTRY(SEG6_ENABLED) },
636                 { 0, "seg6_require_hmac", DT_S32, 0, IPV6_DEVCONF_ENTRY(SEG6_REQUIRE_HMAC) },
637                 { 0, "enhanced_dad", DT_S32, 0, IPV6_DEVCONF_ENTRY(ENHANCED_DAD) },
638                 { 0, "addr_gen_mode", DT_S32, 0, IPV6_DEVCONF_ENTRY(ADDR_GEN_MODE) },
639                 { 0, "disable_policy", DT_S32, 0, IPV6_DEVCONF_ENTRY(DISABLE_POLICY) },
640                 { 0, "accept_ra_rt_info_min_plen", DT_S32, 0, IPV6_DEVCONF_ENTRY(ACCEPT_RA_RT_INFO_MIN_PLEN) },
641                 { 0, "ndisc_tclass", DT_S32, 0, IPV6_DEVCONF_ENTRY(NDISC_TCLASS) },
642                 { 0, "rpl_seg_enabled", DT_S32, 0, IPV6_DEVCONF_ENTRY(RPL_SEG_ENABLED) },
643                 { 0, "ra_defrtr_metric", DT_S32, 0, IPV6_DEVCONF_ENTRY(RA_DEFRTR_METRIC) },
644         }
645 };
646 
647 static const uc_nl_nested_spec_t link_attrs_af_spec_inet6_rta = {
648         .headsize = 0,
649         .nattrs = 3,
650         .attrs = {
651                 { IFLA_INET6_ADDR_GEN_MODE, "mode", DT_U8, 0, NULL },
652                 { IFLA_INET6_FLAGS, "flags", DT_U32, DF_NO_SET, NULL },
653                 { IFLA_INET6_CONF, "conf", DT_NESTED, DF_NO_SET, &link_attrs_af_spec_inet6_devconf_rta },
654         }
655 };
656 
657 static const uc_nl_nested_spec_t link_attrs_bridge_vinfo_rta = {
658         .headsize = sizeof(struct  bridge_vlan_info),
659         .nattrs = 2,
660         .attrs = {
661                 { IFLA_UNSPEC, "flags", DT_U16, 0, MEMBER(bridge_vlan_info, flags) },
662                 { IFLA_UNSPEC, "vid", DT_U16, 0, MEMBER(bridge_vlan_info, vid) },
663         }
664 };
665 
666 static const uc_nl_nested_spec_t link_attrs_af_spec_rta = {
667         .headsize = 0,
668         .nattrs = 4,
669         .attrs = {
670                 { AF_INET, "inet", DT_NESTED, DF_NO_SET, &link_attrs_af_spec_inet_rta },
671                 { AF_INET6, "inet6", DT_NESTED, 0, &link_attrs_af_spec_inet6_rta },
672                 { IFLA_BRIDGE_FLAGS, "bridge_flags", DT_U16, 0, NULL },
673                 { IFLA_BRIDGE_VLAN_INFO, "bridge_vlan_info", DT_NESTED, DF_MULTIPLE|DF_FLAT, &link_attrs_bridge_vinfo_rta },
674         }
675 };
676 
677 static const uc_nl_nested_spec_t link_msg = {
678         .headsize = NLA_ALIGN(sizeof(struct ifinfomsg)),
679         .nattrs = 23,
680         .attrs = {
681                 { IFLA_UNSPEC, "family", DT_U8, 0, MEMBER(ifinfomsg, ifi_family) },
682                 { IFLA_UNSPEC, "type", DT_U16, 0, MEMBER(ifinfomsg, ifi_type) },
683                 { IFLA_UNSPEC, "dev", DT_NETDEV, 0, MEMBER(ifinfomsg, ifi_index) },
684                 { IFLA_UNSPEC, "flags", DT_FLAGS, 0, MEMBER(ifinfomsg, ifi_flags) },
685                 { IFLA_ADDRESS, "address", DT_LLADDR, 0, NULL },
686                 { IFLA_BROADCAST, "broadcast", DT_LLADDR, 0, NULL },
687                 { IFLA_TXQLEN, "txqlen", DT_U32, 0, NULL },
688                 { IFLA_MTU, "mtu", DT_U32, 0, NULL },
689                 /* { IFLA_NETNS_PID, "netns", DT_U32, 0, NULL }, */
690                 { IFLA_CARRIER, "carrier", DT_BOOL, 0, NULL },
691                 /* IFLA_VFINFO_LIST */
692                 { IFLA_MASTER, "master", DT_NETDEV, DF_ALLOW_NONE, NULL },
693                 { IFLA_IFALIAS, "ifalias", DT_STRING, 0, NULL },
694                 { IFLA_LINKMODE, "linkmode", DT_U8, 0, NULL },
695                 { IFLA_OPERSTATE, "operstate", DT_U8, 0, NULL },
696                 { IFLA_NUM_TX_QUEUES, "num_tx_queues", DT_U32, 0, NULL },
697                 { IFLA_NUM_RX_QUEUES, "num_rx_queues", DT_U32, 0, NULL },
698                 { IFLA_AF_SPEC, "af_spec", DT_NESTED, 0, &link_attrs_af_spec_rta },
699                 { IFLA_LINK_NETNSID, "link_netnsid", DT_U32, 0, NULL },
700                 { IFLA_PROTO_DOWN, "proto_down", DT_BOOL, 0, NULL },
701                 { IFLA_GROUP, "group", DT_U32, 0, NULL },
702                 { IFLA_LINK, "link", DT_NETDEV, 0, NULL },
703                 { IFLA_IFNAME, "ifname", DT_STRING, 0, NULL },
704                 { IFLA_LINKINFO, "linkinfo", DT_LINKINFO, 0, NULL }, /* XXX: DF_NO_GET ? */
705                 { IFLA_EXT_MASK, "ext_mask", DT_U32, 0, NULL },
706         }
707 };
708 
709 static const uc_nl_attr_spec_t link_bareudp_attrs[] = {
710         { IFLA_BAREUDP_ETHERTYPE, "ethertype", DT_U16, 0, NULL },
711         { IFLA_BAREUDP_MULTIPROTO_MODE, "multiproto_mode", DT_FLAG, 0, NULL },
712         { IFLA_BAREUDP_PORT, "port", DT_U16, 0, NULL },
713         { IFLA_BAREUDP_SRCPORT_MIN, "srcport_min", DT_U16, 0, NULL },
714 };
715 
716 static const uc_nl_nested_spec_t link_bond_ad_info_rta = {
717         .headsize = 0,
718         .nattrs = 5,
719         .attrs = {
720                 { IFLA_BOND_AD_INFO_ACTOR_KEY, "ad_info_actor_key", DT_U16, DF_NO_SET, NULL },
721                 { IFLA_BOND_AD_INFO_AGGREGATOR, "ad_info_aggregator", DT_U16, DF_NO_SET, NULL },
722                 { IFLA_BOND_AD_INFO_NUM_PORTS, "ad_info_num_ports", DT_U16, DF_NO_SET, NULL },
723                 { IFLA_BOND_AD_INFO_PARTNER_KEY, "ad_info_partner_key", DT_U16, DF_NO_SET, NULL },
724                 { IFLA_BOND_AD_INFO_PARTNER_MAC, "ad_info_partner_mac", DT_LLADDR, DF_NO_SET, NULL },
725         }
726 };
727 
728 static const uc_nl_attr_spec_t link_bond_attrs[] = {
729         { IFLA_BOND_ACTIVE_SLAVE, "active_slave", DT_NETDEV, DF_ALLOW_NONE, NULL },
730         { IFLA_BOND_AD_ACTOR_SYSTEM, "ad_actor_system", DT_LLADDR, 0, NULL },
731         { IFLA_BOND_AD_ACTOR_SYS_PRIO, "ad_actor_sys_prio", DT_U16, 0, NULL },
732         { IFLA_BOND_AD_INFO, "ad_info", DT_NESTED, DF_NO_SET, &link_bond_ad_info_rta },
733         { IFLA_BOND_AD_LACP_RATE, "ad_lacp_rate", DT_U8, 0, NULL },
734         { IFLA_BOND_AD_SELECT, "ad_select", DT_U8, 0, NULL },
735         { IFLA_BOND_AD_USER_PORT_KEY, "ad_user_port_key", DT_U16, 0, NULL },
736         { IFLA_BOND_ALL_SLAVES_ACTIVE, "all_slaves_active", DT_U8, 0, NULL },
737         { IFLA_BOND_ARP_ALL_TARGETS, "arp_all_targets", DT_U32, 0, NULL },
738         { IFLA_BOND_ARP_INTERVAL, "arp_interval", DT_U32, 0, NULL },
739         { IFLA_BOND_ARP_IP_TARGET, "arp_ip_target", DT_INADDR, DF_MULTIPLE, NULL },
740         { IFLA_BOND_ARP_VALIDATE, "arp_validate", DT_U32, 0, NULL },
741         { IFLA_BOND_DOWNDELAY, "downdelay", DT_U32, 0, NULL },
742         { IFLA_BOND_FAIL_OVER_MAC, "fail_over_mac", DT_U8, 0, NULL },
743         { IFLA_BOND_LP_INTERVAL, "lp_interval", DT_U32, 0, NULL },
744         { IFLA_BOND_MIIMON, "miimon", DT_U32, 0, NULL },
745         { IFLA_BOND_MIN_LINKS, "min_links", DT_U32, 0, NULL },
746         { IFLA_BOND_MODE, "mode", DT_U8, 0, NULL },
747         { IFLA_BOND_NUM_PEER_NOTIF, "num_peer_notif", DT_U8, 0, NULL },
748         { IFLA_BOND_PACKETS_PER_SLAVE, "packets_per_slave", DT_U32, 0, NULL },
749         { IFLA_BOND_PRIMARY, "primary", DT_NETDEV, 0, NULL },
750         { IFLA_BOND_PRIMARY_RESELECT, "primary_reselect", DT_U8, 0, NULL },
751         { IFLA_BOND_RESEND_IGMP, "resend_igmp", DT_U32, 0, NULL },
752         { IFLA_BOND_TLB_DYNAMIC_LB, "tlb_dynamic_lb", DT_U8, 0, NULL },
753         { IFLA_BOND_UPDELAY, "updelay", DT_U32, 0, NULL },
754         { IFLA_BOND_USE_CARRIER, "use_carrier", DT_U8, 0, NULL },
755         { IFLA_BOND_XMIT_HASH_POLICY, "xmit_hash_policy", DT_U8, 0, NULL },
756 };
757 
758 static const uc_nl_attr_spec_t link_bond_slave_attrs[] = {
759         { IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, "ad_actor_oper_port_state", DT_U8, DF_NO_SET, NULL },
760         { IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, "ad_aggregator_id", DT_U16, DF_NO_SET, NULL },
761         { IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, "ad_partner_oper_port_state", DT_U8, DF_NO_SET, NULL },
762         { IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, "link_failure_count", DT_U32, DF_NO_SET, NULL },
763         { IFLA_BOND_SLAVE_MII_STATUS, "mii_status", DT_U8, DF_NO_SET, NULL },
764         { IFLA_BOND_SLAVE_PERM_HWADDR, "perm_hwaddr", DT_LLADDR, DF_NO_SET, NULL },
765         { IFLA_BOND_SLAVE_QUEUE_ID, "queue_id", DT_U16, 0, NULL },
766         { IFLA_BOND_SLAVE_STATE, "state", DT_U8, DF_NO_SET, NULL },
767 };
768 
769 static const uc_nl_attr_spec_t link_bridge_attrs[] = {
770         { IFLA_BR_AGEING_TIME, "ageing_time", DT_U32, 0, NULL },
771         { IFLA_BR_BRIDGE_ID, "bridge_id", DT_BRIDGEID, DF_NO_SET, NULL },
772         { IFLA_BR_FDB_FLUSH, "fdb_flush", DT_FLAG, DF_NO_GET, NULL },
773         { IFLA_BR_FORWARD_DELAY, "forward_delay", DT_U32, 0, NULL },
774         { IFLA_BR_GC_TIMER, "gc_timer", DT_U64, DF_NO_SET, NULL },
775         { IFLA_BR_GROUP_ADDR, "group_addr", DT_LLADDR, 0, NULL },
776         { IFLA_BR_GROUP_FWD_MASK, "group_fwd_mask", DT_U16, 0, NULL },
777         { IFLA_BR_HELLO_TIME, "hello_time", DT_U32, 0, NULL },
778         { IFLA_BR_HELLO_TIMER, "hello_timer", DT_U64, DF_NO_SET, NULL },
779         { IFLA_BR_MAX_AGE, "max_age", DT_U32, 0, NULL },
780         { IFLA_BR_MCAST_HASH_ELASTICITY, "mcast_hash_elasticity", DT_U32, 0, NULL },
781         { IFLA_BR_MCAST_HASH_MAX, "mcast_hash_max", DT_U32, 0, NULL },
782         { IFLA_BR_MCAST_IGMP_VERSION, "mcast_igmp_version", DT_U8, 0, NULL },
783         { IFLA_BR_MCAST_LAST_MEMBER_CNT, "mcast_last_member_cnt", DT_U32, 0, NULL },
784         { IFLA_BR_MCAST_LAST_MEMBER_INTVL, "mcast_last_member_intvl", DT_U64, 0, NULL },
785         { IFLA_BR_MCAST_MEMBERSHIP_INTVL, "mcast_membership_intvl", DT_U64, 0, NULL },
786         { IFLA_BR_MCAST_MLD_VERSION, "mcast_mld_version", DT_U8, 0, NULL },
787         { IFLA_BR_MCAST_QUERIER, "mcast_querier", DT_U8, 0, NULL },
788         { IFLA_BR_MCAST_QUERIER_INTVL, "mcast_querier_intvl", DT_U64, 0, NULL },
789         { IFLA_BR_MCAST_QUERY_INTVL, "mcast_query_intvl", DT_U64, 0, NULL },
790         { IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, "mcast_query_response_intvl", DT_U64, 0, NULL },
791         { IFLA_BR_MCAST_QUERY_USE_IFADDR, "mcast_query_use_ifaddr", DT_U8, 0, NULL },
792         { IFLA_BR_MCAST_ROUTER, "mcast_router", DT_U8, 0, NULL },
793         { IFLA_BR_MCAST_SNOOPING, "mcast_snooping", DT_U8, 0, NULL },
794         { IFLA_BR_MCAST_STARTUP_QUERY_CNT, "mcast_startup_query_cnt", DT_U32, 0, NULL },
795         { IFLA_BR_MCAST_STARTUP_QUERY_INTVL, "mcast_startup_query_intvl", DT_U64, 0, NULL },
796         { IFLA_BR_MCAST_STATS_ENABLED, "mcast_stats_enabled", DT_U8, 0, NULL },
797         { IFLA_BR_NF_CALL_ARPTABLES, "nf_call_arptables", DT_U8, 0, NULL },
798         { IFLA_BR_NF_CALL_IP6TABLES, "nf_call_ip6tables", DT_U8, 0, NULL },
799         { IFLA_BR_NF_CALL_IPTABLES, "nf_call_iptables", DT_U8, 0, NULL },
800         { IFLA_BR_PRIORITY, "priority", DT_U16, 0, NULL },
801         { IFLA_BR_ROOT_ID, "root_id", DT_BRIDGEID, DF_NO_SET, NULL },
802         { IFLA_BR_ROOT_PATH_COST, "root_path_cost", DT_U32, DF_NO_SET, NULL },
803         { IFLA_BR_ROOT_PORT, "root_port", DT_U16, DF_NO_SET, NULL },
804         { IFLA_BR_STP_STATE, "stp_state", DT_U32, 0, NULL },
805         { IFLA_BR_TCN_TIMER, "tcn_timer", DT_U64, DF_NO_SET, NULL },
806         { IFLA_BR_TOPOLOGY_CHANGE, "topology_change", DT_U8, DF_NO_SET, NULL },
807         { IFLA_BR_TOPOLOGY_CHANGE_DETECTED, "topology_change_detected", DT_U8, DF_NO_SET, NULL },
808         { IFLA_BR_TOPOLOGY_CHANGE_TIMER, "topology_change_timer", DT_U64, DF_NO_SET, NULL },
809         { IFLA_BR_VLAN_DEFAULT_PVID, "vlan_default_pvid", DT_U16, 0, NULL },
810         { IFLA_BR_VLAN_FILTERING, "vlan_filtering", DT_U8, 0, NULL },
811         { IFLA_BR_VLAN_PROTOCOL, "vlan_protocol", DT_U16, 0, NULL },
812         { IFLA_BR_VLAN_STATS_ENABLED, "vlan_stats_enabled", DT_U8, 0, NULL },
813 };
814 
815 static const uc_nl_attr_spec_t link_bridge_slave_attrs[] = {
816         { IFLA_BRPORT_BACKUP_PORT, "backup_port", DT_NETDEV, 0, NULL },
817         //{ IFLA_BRPORT_BCAST_FLOOD, "bcast-flood", DT_??, 0, NULL },
818         { IFLA_BRPORT_BRIDGE_ID, "bridge_id", DT_BRIDGEID, DF_NO_SET, NULL },
819         { IFLA_BRPORT_CONFIG_PENDING, "config_pending", DT_U8, DF_NO_SET, NULL },
820         { IFLA_BRPORT_COST, "cost", DT_U32, 0, NULL },
821         { IFLA_BRPORT_DESIGNATED_COST, "designated_cost", DT_U16, DF_NO_SET, NULL },
822         { IFLA_BRPORT_DESIGNATED_PORT, "designated_port", DT_U16, DF_NO_SET, NULL },
823         { IFLA_BRPORT_FAST_LEAVE, "fast_leave", DT_U8, 0, NULL },
824         { IFLA_BRPORT_FLUSH, "flush", DT_FLAG, DF_NO_GET, NULL },
825         { IFLA_BRPORT_FORWARD_DELAY_TIMER, "forward_delay_timer", DT_U64, DF_NO_SET, NULL },
826         { IFLA_BRPORT_GROUP_FWD_MASK, "group_fwd_mask", DT_U16, 0, NULL },
827         { IFLA_BRPORT_GUARD, "guard", DT_U8, 0, NULL },
828         { IFLA_BRPORT_HOLD_TIMER, "hold_timer", DT_U64, DF_NO_SET, NULL },
829         { IFLA_BRPORT_ID, "id", DT_U16, DF_NO_SET, NULL },
830         { IFLA_BRPORT_ISOLATED, "isolated", DT_U8, 0, NULL },
831         { IFLA_BRPORT_LEARNING, "learning", DT_U8, 0, NULL },
832         { IFLA_BRPORT_LEARNING_SYNC, "learning_sync", DT_U8, 0, NULL },
833         { IFLA_BRPORT_MCAST_FLOOD, "mcast_flood", DT_U8, 0, NULL },
834         { IFLA_BRPORT_MCAST_TO_UCAST, "mcast_to_ucast", DT_U8, 0, NULL },
835         { IFLA_BRPORT_MESSAGE_AGE_TIMER, "message_age_timer", DT_U64, DF_NO_SET, NULL },
836         { IFLA_BRPORT_MODE, "mode", DT_U8, 0, NULL },
837         { IFLA_BRPORT_MULTICAST_ROUTER, "multicast_router", DT_U8, 0, NULL },
838         { IFLA_BRPORT_NEIGH_SUPPRESS, "neigh_suppress", DT_U8, 0, NULL },
839         { IFLA_BRPORT_NO, "no", DT_U16, DF_NO_SET, NULL },
840         { IFLA_BRPORT_PRIORITY, "priority", DT_U16, 0, NULL },
841         { IFLA_BRPORT_PROTECT, "protect", DT_U8, 0, NULL },
842         { IFLA_BRPORT_PROXYARP, "proxyarp", DT_U8, DF_NO_SET, NULL },
843         { IFLA_BRPORT_PROXYARP_WIFI, "proxyarp_wifi", DT_U8, DF_NO_SET, NULL },
844         { IFLA_BRPORT_ROOT_ID, "root_id", DT_BRIDGEID, DF_NO_SET, NULL },
845         { IFLA_BRPORT_STATE, "state", DT_U8, 0, NULL },
846         { IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, "topology_change_ack", DT_U8, DF_NO_SET, NULL },
847         { IFLA_BRPORT_UNICAST_FLOOD, "unicast_flood", DT_U8, 0, NULL },
848         { IFLA_BRPORT_VLAN_TUNNEL, "vlan_tunnel", DT_U8, 0, NULL },
849 };
850 
851 static const uc_nl_attr_spec_t link_geneve_attrs[] = {
852         { IFLA_GENEVE_COLLECT_METADATA, "collect_metadata", DT_FLAG, DF_NO_GET, NULL },
853         { IFLA_GENEVE_ID, "id", DT_U32, 0, NULL },
854         { IFLA_GENEVE_LABEL, "label", DT_U32, 0, NULL },
855         { IFLA_GENEVE_PORT, "port", DT_U16, 0, NULL },
856         { IFLA_GENEVE_REMOTE, "remote", DT_INADDR, 0, NULL },
857         { IFLA_GENEVE_REMOTE6, "remote6", DT_IN6ADDR, 0, NULL },
858         { IFLA_GENEVE_TOS, "tos", DT_U8, 0, NULL },
859         { IFLA_GENEVE_TTL, "ttl", DT_U8, 0, NULL },
860         { IFLA_GENEVE_UDP_CSUM, "udp_csum", DT_U8, 0, NULL },
861         { IFLA_GENEVE_UDP_ZERO_CSUM6_RX, "udp_zero_csum6_rx", DT_U8, 0, NULL },
862         { IFLA_GENEVE_UDP_ZERO_CSUM6_TX, "udp_zero_csum6_tx", DT_U8, 0, NULL },
863 };
864 
865 static const uc_nl_attr_spec_t link_hsr_attrs[] = {
866         { IFLA_HSR_MULTICAST_SPEC, "multicast_spec", DT_STRING, DF_NO_GET, NULL },
867         { IFLA_HSR_SEQ_NR, "seq_nr", DT_U16, DF_NO_SET, NULL },
868         { IFLA_HSR_SLAVE1, "slave1", DT_NETDEV, 0, NULL },
869         { IFLA_HSR_SLAVE2, "slave2", DT_NETDEV, 0, NULL },
870         { IFLA_HSR_SUPERVISION_ADDR, "supervision_addr", DT_LLADDR, DF_NO_SET, NULL },
871         { IFLA_HSR_VERSION, "version", DT_STRING, DF_NO_GET, NULL },
872 };
873 
874 static const uc_nl_attr_spec_t link_ipoib_attrs[] = {
875         { IFLA_IPOIB_MODE, "mode", DT_U16, 0, NULL },
876         { IFLA_IPOIB_PKEY, "pkey", DT_U16, 0, NULL },
877         { IFLA_IPOIB_UMCAST, "umcast", DT_U16, 0, NULL },
878 };
879 
880 static const uc_nl_attr_spec_t link_ipvlan_attrs[] = {
881         { IFLA_IPVLAN_FLAGS, "flags", DT_U16, 0, NULL },
882         { IFLA_IPVLAN_MODE, "mode", DT_U16, 0, NULL },
883 };
884 
885 static const uc_nl_attr_spec_t link_macvlan_attrs[] = {
886         { IFLA_MACVLAN_FLAGS, "flags", DT_U16, 0, NULL },
887         { IFLA_MACVLAN_MACADDR, "macaddr", DT_LLADDR, DF_NO_GET, NULL },
888         { IFLA_MACVLAN_MACADDR_COUNT, "macaddr_count", DT_U32, DF_NO_SET, NULL },
889         { IFLA_MACVLAN_MACADDR_DATA, "macaddr_data", DT_LLADDR, DF_MULTIPLE, (void *)IFLA_MACVLAN_MACADDR },
890         { IFLA_MACVLAN_MACADDR_MODE, "macaddr_mode", DT_U32, DF_NO_GET, NULL },
891         { IFLA_MACVLAN_MODE, "mode", DT_U32, 0, NULL },
892 };
893 
894 static const uc_nl_attr_spec_t link_rmnet_attrs[] = {
895         //{ IFLA_RMNET_FLAGS, "flags", DT_??, 0, NULL },
896         { IFLA_RMNET_MUX_ID, "mux_id", DT_U16, 0, NULL },
897 };
898 
899 
900 static const uc_nl_attr_spec_t link_vlan_attrs[] = {
901         { IFLA_VLAN_EGRESS_QOS, "egress_qos_map", DT_NUMRANGE, DF_MULTIPLE, (void *)IFLA_VLAN_QOS_MAPPING },
902         { IFLA_VLAN_FLAGS, "flags", DT_FLAGS, 0, NULL },
903         { IFLA_VLAN_ID, "id", DT_U16, 0, NULL },
904         { IFLA_VLAN_INGRESS_QOS, "ingress_qos_map", DT_NUMRANGE, DF_MULTIPLE, (void *)IFLA_VLAN_QOS_MAPPING },
905         { IFLA_VLAN_PROTOCOL, "protocol", DT_U16, 0, NULL },
906 };
907 
908 static const uc_nl_attr_spec_t link_vrf_attrs[] = {
909         { IFLA_VRF_PORT_TABLE, "port_table", DT_U32, DF_NO_SET, NULL },
910         { IFLA_VRF_TABLE, "table", DT_U32, 0, NULL },
911 };
912 
913 static const uc_nl_attr_spec_t link_vxlan_attrs[] = {
914         { IFLA_VXLAN_AGEING, "ageing", DT_U32, 0, NULL },
915         { IFLA_VXLAN_COLLECT_METADATA, "collect_metadata", DT_U8, 0, NULL },
916         { IFLA_VXLAN_GBP, "gbp", DT_FLAG, 0, NULL },
917         { IFLA_VXLAN_GPE, "gpe", DT_FLAG, 0, NULL },
918         { IFLA_VXLAN_GROUP, "group", DT_INADDR, 0, NULL },
919         { IFLA_VXLAN_GROUP6, "group6", DT_IN6ADDR, 0, NULL },
920         { IFLA_VXLAN_ID, "id", DT_U32, 0, NULL },
921         { IFLA_VXLAN_L2MISS, "l2miss", DT_U8, 0, NULL },
922         { IFLA_VXLAN_L3MISS, "l3miss", DT_U8, 0, NULL },
923         { IFLA_VXLAN_LABEL, "label", DT_U32, 0, NULL },
924         { IFLA_VXLAN_LEARNING, "learning", DT_U8, 0, NULL },
925         { IFLA_VXLAN_LIMIT, "limit", DT_U32, 0, NULL },
926         { IFLA_VXLAN_LINK, "link", DT_U32, 0, NULL },
927         { IFLA_VXLAN_LOCAL, "local", DT_INADDR, 0, NULL },
928         { IFLA_VXLAN_LOCAL6, "local6", DT_IN6ADDR, 0, NULL },
929         { IFLA_VXLAN_PORT, "port", DT_U16, DF_BYTESWAP, NULL },
930         { IFLA_VXLAN_PORT_RANGE, "port_range", DT_NUMRANGE, DF_MAX_65535|DF_BYTESWAP, NULL },
931         { IFLA_VXLAN_PROXY, "proxy", DT_U8, 0, NULL },
932         //{ IFLA_VXLAN_REMCSUM_NOPARTIAL, "remcsum-nopartial", DT_??, 0, NULL },
933         { IFLA_VXLAN_REMCSUM_RX, "remcsum_rx", DT_BOOL, 0, NULL },
934         { IFLA_VXLAN_REMCSUM_TX, "remcsum_tx", DT_BOOL, 0, NULL },
935         { IFLA_VXLAN_RSC, "rsc", DT_BOOL, 0, NULL },
936         { IFLA_VXLAN_TOS, "tos", DT_U8, 0, NULL },
937         { IFLA_VXLAN_TTL, "ttl", DT_U8, 0, NULL },
938         { IFLA_VXLAN_TTL_INHERIT, "ttl_inherit", DT_FLAG, 0, NULL },
939         { IFLA_VXLAN_UDP_CSUM, "udp_csum", DT_BOOL, 0, NULL },
940         { IFLA_VXLAN_UDP_ZERO_CSUM6_RX, "udp_zero_csum6_rx", DT_BOOL, 0, NULL },
941         { IFLA_VXLAN_UDP_ZERO_CSUM6_TX, "udp_zero_csum6_tx", DT_BOOL, 0, NULL },
942 };
943 
944 static const uc_nl_attr_spec_t link_gre_attrs[] = {
945         { IFLA_GRE_COLLECT_METADATA, "collect_metadata", DT_FLAG, 0, NULL },
946         { IFLA_GRE_ENCAP_DPORT, "encap_dport", DT_U16, DF_BYTESWAP, NULL },
947         { IFLA_GRE_ENCAP_FLAGS, "encap_flags", DT_U16, 0, NULL },
948         { IFLA_GRE_ENCAP_LIMIT, "encap_limit", DT_U8, 0, NULL },
949         { IFLA_GRE_ENCAP_SPORT, "encap_sport", DT_U16, DF_BYTESWAP, NULL },
950         { IFLA_GRE_ENCAP_TYPE, "encap_type", DT_U16, 0, NULL },
951         { IFLA_GRE_ERSPAN_DIR, "erspan_dir", DT_U8, 0, NULL },
952         { IFLA_GRE_ERSPAN_HWID, "erspan_hwid", DT_U16, 0, NULL },
953         { IFLA_GRE_ERSPAN_INDEX, "erspan_index", DT_U32, 0, NULL },
954         { IFLA_GRE_ERSPAN_VER, "erspan_ver", DT_U8, 0, NULL },
955         { IFLA_GRE_FLAGS, "flags", DT_U32, 0, NULL },
956         { IFLA_GRE_FLOWINFO, "flowinfo", DT_U32, DF_BYTESWAP, NULL },
957         { IFLA_GRE_FWMARK, "fwmark", DT_U32, 0, NULL },
958         { IFLA_GRE_IFLAGS, "iflags", DT_U16, 0, NULL },
959         { IFLA_GRE_IGNORE_DF, "ignore_df", DT_BOOL, 0, NULL },
960         { IFLA_GRE_IKEY, "ikey", DT_U32, 0, NULL },
961         { IFLA_GRE_LINK, "link", DT_NETDEV, 0, NULL },
962         { IFLA_GRE_LOCAL, "local", DT_ANYADDR, 0, NULL },
963         { IFLA_GRE_OFLAGS, "oflags", DT_U16, 0, NULL },
964         { IFLA_GRE_OKEY, "okey", DT_U32, 0, NULL },
965         { IFLA_GRE_PMTUDISC, "pmtudisc", DT_BOOL, 0, NULL },
966         { IFLA_GRE_REMOTE, "remote", DT_ANYADDR, 0, NULL },
967         { IFLA_GRE_TOS, "tos", DT_U8, 0, NULL },
968         { IFLA_GRE_TTL, "ttl", DT_U8, 0, NULL },
969 };
970 
971 #define link_gretap_attrs link_gre_attrs
972 #define link_erspan_attrs link_gre_attrs
973 #define link_ip6gre_attrs link_gre_attrs
974 #define link_ip6gretap_attrs link_gre_attrs
975 #define link_ip6erspan_attrs link_gre_attrs
976 
977 static const uc_nl_attr_spec_t link_ip6tnl_attrs[] = {
978         { IFLA_IPTUN_6RD_PREFIX, "6rd_prefix", DT_IN6ADDR, 0, NULL },
979         { IFLA_IPTUN_6RD_PREFIXLEN, "6rd_prefixlen", DT_U16, 0, NULL },
980         { IFLA_IPTUN_6RD_RELAY_PREFIX, "6rd_relay_prefix", DT_INADDR, 0, NULL },
981         { IFLA_IPTUN_6RD_RELAY_PREFIXLEN, "6rd_relay_prefixlen", DT_U16, 0, NULL },
982         { IFLA_IPTUN_COLLECT_METADATA, "collect_metadata", DT_BOOL, 0, NULL },
983         { IFLA_IPTUN_ENCAP_DPORT, "encap_dport", DT_U16, DF_BYTESWAP, NULL },
984         { IFLA_IPTUN_ENCAP_FLAGS, "encap_flags", DT_U16, 0, NULL },
985         { IFLA_IPTUN_ENCAP_LIMIT, "encap_limit", DT_U8, 0, NULL },
986         { IFLA_IPTUN_ENCAP_SPORT, "encap_sport", DT_U16, DF_BYTESWAP, NULL },
987         { IFLA_IPTUN_ENCAP_TYPE, "encap_type", DT_U16, 0, NULL },
988         { IFLA_IPTUN_FLAGS, "flags", DT_U16, 0, NULL },
989         { IFLA_IPTUN_FLOWINFO, "flowinfo", DT_U32, DF_BYTESWAP, NULL },
990         { IFLA_IPTUN_FWMARK, "fwmark", DT_U32, 0, NULL },
991         { IFLA_IPTUN_LINK, "link", DT_NETDEV, 0, NULL },
992         { IFLA_IPTUN_LOCAL, "local", DT_ANYADDR, 0, NULL },
993         { IFLA_IPTUN_PMTUDISC, "pmtudisc", DT_BOOL, 0, NULL },
994         { IFLA_IPTUN_PROTO, "proto", DT_U8, 0, NULL },
995         { IFLA_IPTUN_REMOTE, "remote", DT_ANYADDR, 0, NULL },
996         { IFLA_IPTUN_TOS, "tos", DT_U8, 0, NULL },
997         { IFLA_IPTUN_TTL, "ttl", DT_U8, 0, NULL },
998 };
999 
1000 #define link_ipip_attrs link_ip6tnl_attrs
1001 #define link_sit_attrs link_ip6tnl_attrs
1002 
1003 static const uc_nl_attr_spec_t link_veth_attrs[] = {
1004         { VETH_INFO_PEER, "info_peer", DT_NESTED, 0, &link_msg },
1005 };
1006 
1007 static const uc_nl_attr_spec_t link_vti_attrs[] = {
1008         { IFLA_VTI_FWMARK, "fwmark", DT_U32, 0, NULL },
1009         { IFLA_VTI_IKEY, "ikey", DT_U32, 0, NULL },
1010         { IFLA_VTI_LINK, "link", DT_U32, 0, NULL },
1011         { IFLA_VTI_LOCAL, "local", DT_ANYADDR, 0, NULL },
1012         { IFLA_VTI_OKEY, "okey", DT_U32, 0, NULL },
1013         { IFLA_VTI_REMOTE, "remote", DT_ANYADDR, 0, NULL },
1014 };
1015 
1016 #define link_vti6_attrs link_vti_attrs
1017 
1018 static const uc_nl_attr_spec_t link_xfrm_attrs[] = {
1019         { IFLA_XFRM_IF_ID, "if_id", DT_U32, 0, NULL },
1020         { IFLA_XFRM_LINK, "link", DT_NETDEV, 0, NULL },
1021 };
1022 
1023 static const uc_nl_attr_spec_t lwtipopt_erspan_attrs[] = {
1024         { LWTUNNEL_IP_OPT_ERSPAN_VER, "ver", DT_U8, 0, NULL },
1025         { LWTUNNEL_IP_OPT_ERSPAN_INDEX, "index", DT_U16, DF_BYTESWAP, NULL },
1026         { LWTUNNEL_IP_OPT_ERSPAN_DIR, "dir", DT_U8, 0, NULL },
1027         { LWTUNNEL_IP_OPT_ERSPAN_HWID, "hwid", DT_U8, 0, NULL },
1028 };
1029 
1030 static const uc_nl_attr_spec_t lwtipopt_geneve_attrs[] = {
1031         { LWTUNNEL_IP_OPT_GENEVE_CLASS, "class", DT_U16, DF_BYTESWAP, NULL },
1032         { LWTUNNEL_IP_OPT_GENEVE_TYPE, "type", DT_U8, 0, NULL },
1033         { LWTUNNEL_IP_OPT_GENEVE_DATA, "data", DT_STRING, 0, NULL },
1034 };
1035 
1036 static const uc_nl_attr_spec_t lwtipopt_vxlan_attrs[] = {
1037         { LWTUNNEL_IP_OPT_VXLAN_GBP, "gbp", DT_U32, 0, NULL },
1038 };
1039 
1040 static const uc_nl_nested_spec_t neigh_cacheinfo_rta = {
1041         .headsize = NLA_ALIGN(sizeof(struct nda_cacheinfo)),
1042         .nattrs = 4,
1043         .attrs = {
1044                 { NDA_UNSPEC, "confirmed", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_confirmed) },
1045                 { NDA_UNSPEC, "used", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_used) },
1046                 { NDA_UNSPEC, "updated", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_updated) },
1047                 { NDA_UNSPEC, "refcnt", DT_U32, 0, MEMBER(nda_cacheinfo, ndm_refcnt) },
1048         }
1049 };
1050 
1051 static const uc_nl_nested_spec_t neigh_msg = {
1052         .headsize = NLA_ALIGN(sizeof(struct ndmsg)),
1053         .nattrs = 16,
1054         .attrs = {
1055                 { NDA_UNSPEC, "family", DT_U8, 0, MEMBER(ndmsg, ndm_family) },
1056                 { NDA_UNSPEC, "dev" /* actually ifindex, but avoid clash with NDA_IFINDEX */, DT_NETDEV, DF_ALLOW_NONE, MEMBER(ndmsg, ndm_ifindex) },
1057                 { NDA_UNSPEC, "state", DT_U16, 0, MEMBER(ndmsg, ndm_state) },
1058                 { NDA_UNSPEC, "flags", DT_U8, 0, MEMBER(ndmsg, ndm_flags) },
1059                 { NDA_UNSPEC, "type", DT_U8, 0, MEMBER(ndmsg, ndm_type) },
1060                 { NDA_CACHEINFO, "cacheinfo", DT_NESTED, DF_NO_SET, &neigh_cacheinfo_rta },
1061                 { NDA_DST, "dst", DT_ANYADDR, 0, NULL },
1062                 { NDA_IFINDEX, "ifindex", DT_NETDEV, 0, NULL },
1063                 { NDA_LINK_NETNSID, "link_netnsid", DT_U32, DF_NO_SET, NULL },
1064                 { NDA_LLADDR, "lladdr", DT_LLADDR, 0, NULL },
1065                 { NDA_MASTER, "master", DT_NETDEV, 0, NULL },
1066                 { NDA_PORT, "port", DT_U16, DF_BYTESWAP, NULL },
1067                 { NDA_PROBES, "probes", DT_U32, DF_NO_SET, NULL },
1068                 { NDA_SRC_VNI, "src_vni", DT_U32, DF_NO_SET, NULL },
1069                 { NDA_VLAN, "vlan", DT_U16, 0, NULL },
1070                 { NDA_VNI, "vni", DT_U32, DF_MAX_16777215, NULL },
1071         }
1072 };
1073 
1074 static const uc_nl_nested_spec_t addr_cacheinfo_rta = {
1075         .headsize = NLA_ALIGN(sizeof(struct ifa_cacheinfo)),
1076         .nattrs = 4,
1077         .attrs = {
1078                 { IFA_UNSPEC, "preferred", DT_U32, 0, MEMBER(ifa_cacheinfo, ifa_prefered) },
1079                 { IFA_UNSPEC, "valid", DT_U32, 0, MEMBER(ifa_cacheinfo, ifa_valid) },
1080                 { IFA_UNSPEC, "cstamp", DT_U32, 0, MEMBER(ifa_cacheinfo, cstamp) },
1081                 { IFA_UNSPEC, "tstamp", DT_U32, 0, MEMBER(ifa_cacheinfo, tstamp) },
1082         }
1083 };
1084 
1085 static const uc_nl_nested_spec_t addr_msg = {
1086         .headsize = NLA_ALIGN(sizeof(struct ifaddrmsg)),
1087         .nattrs = 11,
1088         .attrs = {
1089                 { IFA_UNSPEC, "family", DT_U8, 0, MEMBER(ifaddrmsg, ifa_family) },
1090                 { IFA_FLAGS, "flags", DT_U32_OR_MEMBER, DF_MAX_255, MEMBER(ifaddrmsg, ifa_flags) },
1091                 { IFA_UNSPEC, "scope", DT_U8, 0, MEMBER(ifaddrmsg, ifa_scope) },
1092                 { IFA_UNSPEC, "dev", DT_NETDEV, 0, MEMBER(ifaddrmsg, ifa_index) },
1093                 { IFA_ADDRESS, "address", DT_ANYADDR, DF_STORE_MASK, MEMBER(ifaddrmsg, ifa_prefixlen) },
1094                 { IFA_LOCAL, "local", DT_ANYADDR, 0, NULL },
1095                 { IFA_LABEL, "label", DT_STRING, 0, NULL },
1096                 { IFA_BROADCAST, "broadcast", DT_ANYADDR, 0, NULL },
1097                 { IFA_ANYCAST, "anycast", DT_ANYADDR, 0, NULL },
1098                 { IFA_CACHEINFO, "cacheinfo", DT_NESTED, DF_NO_SET, &addr_cacheinfo_rta },
1099                 { IFA_RT_PRIORITY, "metric", DT_U32, 0, NULL },
1100         }
1101 };
1102 
1103 static const uc_nl_nested_spec_t rule_msg = {
1104         .headsize = NLA_ALIGN(sizeof(struct fib_rule_hdr)),
1105         .nattrs = 23,
1106         .attrs = {
1107                 { FRA_UNSPEC, "family", DT_U8, 0, MEMBER(fib_rule_hdr, family) },
1108                 { FRA_UNSPEC, "tos", DT_U8, 0, MEMBER(fib_rule_hdr, tos) },
1109                 { FRA_UNSPEC, "action", DT_U8, 0, MEMBER(fib_rule_hdr, action) },
1110                 { FRA_UNSPEC, "flags", DT_U32, 0, MEMBER(fib_rule_hdr, flags) },
1111                 { FRA_PRIORITY, "priority", DT_U32, 0, NULL },
1112                 { FRA_SRC, "src", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(fib_rule_hdr, src_len) },
1113                 { FRA_DST, "dst", DT_ANYADDR, DF_STORE_MASK|DF_FAMILY_HINT, MEMBER(fib_rule_hdr, dst_len) },
1114                 { FRA_FWMARK, "fwmark", DT_U32, 0, NULL },
1115                 { FRA_FWMASK, "fwmask", DT_U32, 0, NULL },
1116                 { FRA_IFNAME, "iif", DT_NETDEV, 0, NULL },
1117                 { FRA_OIFNAME, "oif", DT_NETDEV, 0, NULL },
1118                 { FRA_L3MDEV, "l3mdev", DT_U8, 0, NULL },
1119                 { FRA_UID_RANGE, "uid_range", DT_NUMRANGE, 0, NULL },
1120                 { FRA_IP_PROTO, "ip_proto", DT_U8, 0, NULL },
1121                 { FRA_SPORT_RANGE, "sport_range", DT_NUMRANGE, DF_MAX_65535, NULL },
1122                 { FRA_DPORT_RANGE, "dport_range", DT_NUMRANGE, DF_MAX_65535, NULL },
1123                 { FRA_TABLE, "table", DT_U32_OR_MEMBER, DF_MAX_255, MEMBER(fib_rule_hdr, table) },
1124                 { FRA_SUPPRESS_PREFIXLEN, "suppress_prefixlen", DT_S32, 0, NULL },
1125                 { FRA_SUPPRESS_IFGROUP, "suppress_ifgroup", DT_U32, 0, NULL },
1126                 { FRA_FLOW, "flow", DT_U32, 0, NULL },
1127                 { RTA_GATEWAY, "gateway", DT_ANYADDR, DF_FAMILY_HINT, NULL },
1128                 { FRA_GOTO, "goto", DT_U32, 0, NULL },
1129                 { FRA_PROTOCOL, "protocol", DT_U8, 0, NULL },
1130         }
1131 };
1132 
1133 #define IFAL_UNSPEC 0
1134 
1135 static const uc_nl_nested_spec_t addrlabel_msg = {
1136         .headsize = NLA_ALIGN(sizeof(struct ifaddrlblmsg)),
1137         .nattrs = 6,
1138         .attrs = {
1139                 { IFAL_UNSPEC, "family", DT_U8, 0, MEMBER(ifaddrlblmsg, ifal_family) },
1140                 { IFAL_UNSPEC, "flags", DT_U8, 0, MEMBER(ifaddrlblmsg, ifal_flags) },
1141                 { IFAL_UNSPEC, "dev", DT_NETDEV, 0, MEMBER(ifaddrlblmsg, ifal_index) },
1142                 { IFAL_UNSPEC, "seq", DT_U32, 0, MEMBER(ifaddrlblmsg, ifal_seq) },
1143                 { IFAL_ADDRESS, "address", DT_ANYADDR, DF_STORE_MASK, MEMBER(ifaddrlblmsg, ifal_prefixlen) },
1144                 { IFAL_LABEL, "label", DT_U32, 0, NULL },
1145         }
1146 };
1147 
1148 static const uc_nl_nested_spec_t neightbl_params_rta = {
1149         .headsize = 0,
1150         .nattrs = 13,
1151         .attrs = {
1152                 { NDTPA_IFINDEX, "dev", DT_NETDEV, 0, NULL },
1153                 { NDTPA_BASE_REACHABLE_TIME, "base_reachable_time", DT_U64, 0, NULL },
1154                 { NDTPA_RETRANS_TIME, "retrans_time", DT_U64, 0, NULL },
1155                 { NDTPA_GC_STALETIME, "gc_staletime", DT_U64, 0, NULL },
1156                 { NDTPA_DELAY_PROBE_TIME, "delay_probe_time", DT_U64, 0, NULL },
1157                 { NDTPA_QUEUE_LEN, "queue_len", DT_U32, 0, NULL },
1158                 { NDTPA_APP_PROBES, "app_probes", DT_U32, 0, NULL },
1159                 { NDTPA_UCAST_PROBES, "ucast_probes", DT_U32, 0, NULL },
1160                 { NDTPA_MCAST_PROBES, "mcast_probes", DT_U32, 0, NULL },
1161                 { NDTPA_ANYCAST_DELAY, "anycast_delay", DT_U64, 0, NULL },
1162                 { NDTPA_PROXY_DELAY, "proxy_delay", DT_U64, 0, NULL },
1163                 { NDTPA_PROXY_QLEN, "proxy_qlen", DT_U32, 0, NULL },
1164                 { NDTPA_LOCKTIME, "locktime", DT_U64, 0, NULL },
1165         }
1166 };
1167 
1168 static const uc_nl_nested_spec_t neightbl_config_rta = {
1169         .headsize = NLA_ALIGN(sizeof(struct ndt_config)),
1170         .nattrs = 9,
1171         .attrs = {
1172                 { NDTA_UNSPEC, "key_len", DT_U16, 0, MEMBER(ndt_config, ndtc_key_len) },
1173                 { NDTA_UNSPEC, "entry_size", DT_U16, 0, MEMBER(ndt_config, ndtc_entry_size) },
1174                 { NDTA_UNSPEC, "entries", DT_U32, 0, MEMBER(ndt_config, ndtc_entries) },
1175                 { NDTA_UNSPEC, "last_flush", DT_U32, 0, MEMBER(ndt_config, ndtc_last_flush) },
1176                 { NDTA_UNSPEC, "last_rand", DT_U32, 0, MEMBER(ndt_config, ndtc_last_rand) },
1177                 { NDTA_UNSPEC, "hash_rnd", DT_U32, 0, MEMBER(ndt_config, ndtc_hash_rnd) },
1178                 { NDTA_UNSPEC, "hash_mask", DT_U32, 0, MEMBER(ndt_config, ndtc_hash_mask) },
1179                 { NDTA_UNSPEC, "hash_chain_gc", DT_U32, 0, MEMBER(ndt_config, ndtc_hash_chain_gc) },
1180                 { NDTA_UNSPEC, "proxy_qlen", DT_U32, 0, MEMBER(ndt_config, ndtc_proxy_qlen) },
1181         }
1182 };
1183 
1184 static const uc_nl_nested_spec_t neightbl_stats_rta = {
1185         .headsize = NLA_ALIGN(sizeof(struct ndt_stats)),
1186         .nattrs = 10,
1187         .attrs = {
1188                 { NDTA_UNSPEC, "allocs", DT_U64, 0, MEMBER(ndt_stats, ndts_allocs) },
1189                 { NDTA_UNSPEC, "destroys", DT_U64, 0, MEMBER(ndt_stats, ndts_destroys) },
1190                 { NDTA_UNSPEC, "hash_grows", DT_U64, 0, MEMBER(ndt_stats, ndts_hash_grows) },
1191                 { NDTA_UNSPEC, "res_failed", DT_U64, 0, MEMBER(ndt_stats, ndts_res_failed) },
1192                 { NDTA_UNSPEC, "lookups", DT_U64, 0, MEMBER(ndt_stats, ndts_lookups) },
1193                 { NDTA_UNSPEC, "hits", DT_U64, 0, MEMBER(ndt_stats, ndts_hits) },
1194                 { NDTA_UNSPEC, "rcv_probes_mcast", DT_U64, 0, MEMBER(ndt_stats, ndts_rcv_probes_mcast) },
1195                 { NDTA_UNSPEC, "rcv_probes_ucast", DT_U64, 0, MEMBER(ndt_stats, ndts_rcv_probes_ucast) },
1196                 { NDTA_UNSPEC, "periodic_gc_runs", DT_U64, 0, MEMBER(ndt_stats, ndts_periodic_gc_runs) },
1197                 { NDTA_UNSPEC, "forced_gc_runs", DT_U64, 0, MEMBER(ndt_stats, ndts_forced_gc_runs) },
1198         }
1199 };
1200 
1201 static const uc_nl_nested_spec_t neightbl_msg = {
1202         .headsize = NLA_ALIGN(sizeof(struct ndtmsg)),
1203         .nattrs = 9,
1204         .attrs = {
1205                 { NDTA_UNSPEC, "family", DT_U8, 0, MEMBER(ndtmsg, ndtm_family) },
1206                 { NDTA_NAME, "name", DT_STRING, 0, NULL },
1207                 { NDTA_THRESH1, "thresh1", DT_U32, 0, NULL },
1208                 { NDTA_THRESH2, "thresh2", DT_U32, 0, NULL },
1209                 { NDTA_THRESH3, "thresh3", DT_U32, 0, NULL },
1210                 { NDTA_GC_INTERVAL, "gc_interval", DT_U64, 0, NULL },
1211                 { NDTA_PARMS, "params", DT_NESTED, 0, &neightbl_params_rta },
1212                 { NDTA_CONFIG, "config", DT_NESTED, DF_NO_SET, &neightbl_config_rta },
1213                 { NDTA_STATS, "stats", DT_NESTED, DF_NO_SET, &neightbl_stats_rta },
1214         }
1215 };
1216 
1217 static const uc_nl_nested_spec_t netconf_msg = {
1218         .headsize = NLA_ALIGN(sizeof(struct netconfmsg)),
1219         .nattrs = 8,
1220         .attrs = {
1221                 { NETCONFA_UNSPEC, "family", DT_U8, 0, MEMBER(netconfmsg, ncm_family) },
1222                 { NETCONFA_IFINDEX, "dev", DT_NETDEV, 0, NULL },
1223                 { NETCONFA_FORWARDING, "forwarding", DT_U32, DF_NO_SET, NULL },
1224                 { NETCONFA_RP_FILTER, "rp_filter", DT_U32, DF_NO_SET, NULL },
1225                 { NETCONFA_MC_FORWARDING, "mc_forwarding", DT_U32, DF_NO_SET, NULL },
1226                 { NETCONFA_PROXY_NEIGH, "proxy_neigh", DT_U32, DF_NO_SET, NULL },
1227                 { NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, "ignore_routes_with_linkdown", DT_U32, DF_NO_SET, NULL },
1228                 { NETCONFA_INPUT, "input", DT_U32, DF_NO_SET, NULL },
1229         }
1230 };
1231 
1232 
1233 static bool
1234 nla_check_len(struct nlattr *nla, size_t sz)
1235 {
1236         return (nla && nla_len(nla) >= (ssize_t)sz);
1237 }
1238 
1239 static bool
1240 nla_parse_error(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *v, const char *msg)
1241 {
1242         char *s;
1243 
1244         s = ucv_to_string(vm, v);
1245 
1246         set_error(NLE_INVAL, "%s `%s` has invalid value `%s`: %s",
1247                 spec->attr ? "attribute" : "field",
1248                 spec->key,
1249                 s,
1250                 msg);
1251 
1252         free(s);
1253 
1254         return false;
1255 }
1256 
1257 static void
1258 uc_nl_put_struct_member(char *base, const void *offset, size_t datalen, void *data)
1259 {
1260         memcpy(base + (uintptr_t)offset, data, datalen);
1261 }
1262 
1263 static void
1264 uc_nl_put_struct_member_u8(char *base, const void *offset, uint8_t u8)
1265 {
1266         base[(uintptr_t)offset] = u8;
1267 }
1268 
1269 static void
1270 uc_nl_put_struct_member_u16(char *base, const void *offset, uint16_t u16)
1271 {
1272         uc_nl_put_struct_member(base, offset, sizeof(u16), &u16);
1273 }
1274 
1275 static void
1276 uc_nl_put_struct_member_u32(char *base, const void *offset, uint32_t u32)
1277 {
1278         uc_nl_put_struct_member(base, offset, sizeof(u32), &u32);
1279 }
1280 
1281 static void *
1282 uc_nl_get_struct_member(char *base, const void *offset, size_t datalen, void *data)
1283 {
1284         memcpy(data, base + (uintptr_t)offset, datalen);
1285 
1286         return data;
1287 }
1288 
1289 static uint8_t
1290 uc_nl_get_struct_member_u8(char *base, const void *offset)
1291 {
1292         return (uint8_t)base[(uintptr_t)offset];
1293 }
1294 
1295 static uint16_t
1296 uc_nl_get_struct_member_u16(char *base, const void *offset)
1297 {
1298         uint16_t u16;
1299 
1300         uc_nl_get_struct_member(base, offset, sizeof(u16), &u16);
1301 
1302         return u16;
1303 }
1304 
1305 static uint32_t
1306 uc_nl_get_struct_member_u32(char *base, const void *offset)
1307 {
1308         uint32_t u32;
1309 
1310         uc_nl_get_struct_member(base, offset, sizeof(u32), &u32);
1311 
1312         return u32;
1313 }
1314 
1315 static uint64_t
1316 uc_nl_get_struct_member_u64(char *base, const void *offset)
1317 {
1318         uint64_t u64;
1319 
1320         uc_nl_get_struct_member(base, offset, sizeof(u64), &u64);
1321 
1322         return u64;
1323 }
1324 
1325 static bool
1326 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);
1327 
1328 static uc_value_t *
1329 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm);
1330 
1331 static bool
1332 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)
1333 {
1334         size_t i, maxattr = 0, structlen = headsize;
1335         struct nlattr **tb, *nla, *nla_nest;
1336         uc_value_t *v, *arr;
1337         int rem;
1338 
1339         for (i = 0; i < nattrs; i++)
1340                 if (attrs[i].attr > maxattr)
1341                         maxattr = attrs[i].attr;
1342 
1343         tb = calloc(maxattr + 1, sizeof(struct nlattr *));
1344 
1345         if (!tb)
1346                 return false;
1347 
1348         if (buflen > headsize) {
1349                 if (maxattr)
1350                         nla_parse(tb, maxattr, buf + headsize, buflen - headsize, NULL);
1351         }
1352         else {
1353                 structlen = buflen;
1354         }
1355 
1356         for (i = 0; i < nattrs; i++) {
1357                 if (attrs[i].attr == 0 && (uintptr_t)attrs[i].auxdata >= structlen)
1358                         continue;
1359 
1360                 if (attrs[i].attr != 0 && !tb[attrs[i].attr])
1361                         continue;
1362 
1363                 if (attrs[i].flags & DF_NO_GET)
1364                         continue;
1365 
1366                 if (attrs[i].flags & DF_MULTIPLE) {
1367                         /* can't happen, but needed to nudge clang-analyzer */
1368                         if (!tb[attrs[i].attr])
1369                                 continue;
1370 
1371                         arr = ucv_array_new(vm);
1372                         nla_nest = tb[attrs[i].attr];
1373 
1374                         nla_for_each_attr(nla, nla_data(nla_nest), nla_len(nla_nest), rem) {
1375                                 if (attrs[i].auxdata && nla_type(nla) != (intptr_t)attrs[i].auxdata)
1376                                         continue;
1377 
1378                                 tb[attrs[i].attr] = nla;
1379 
1380                                 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm);
1381 
1382                                 if (!v)
1383                                         continue;
1384 
1385                                 ucv_array_push(arr, v);
1386                         }
1387 
1388                         if (!ucv_array_length(arr)) {
1389                                 ucv_put(arr);
1390 
1391                                 continue;
1392                         }
1393 
1394                         v = arr;
1395                 }
1396                 else {
1397                         v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm);
1398 
1399                         if (!v)
1400                                 continue;
1401                 }
1402 
1403                 ucv_object_add(obj, attrs[i].key, v);
1404         }
1405 
1406         free(tb);
1407 
1408         return true;
1409 }
1410 
1411 static bool
1412 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)
1413 {
1414         struct nlattr *nla_nest = NULL;
1415         size_t i, j, idx;
1416         uc_value_t *v;
1417         bool exists;
1418 
1419         for (i = 0; i < nattrs; i++) {
1420                 v = ucv_object_get(obj, attrs[i].key, &exists);
1421 
1422                 if (!exists)
1423                         continue;
1424 
1425                 if (attrs[i].flags & DF_MULTIPLE) {
1426                         if (!(attrs[i].flags & DF_FLAT))
1427                                 nla_nest = nla_nest_start(msg, attrs[i].attr);
1428 
1429                         if (ucv_type(v) == UC_ARRAY) {
1430                                 for (j = 0; j < ucv_array_length(v); j++) {
1431                                         if (attrs[i].flags & DF_FLAT)
1432                                                 idx = attrs[i].attr;
1433                                         else if (attrs[i].auxdata)
1434                                                 idx = (uintptr_t)attrs[i].auxdata;
1435                                         else
1436                                                 idx = j;
1437 
1438                                         if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, ucv_array_get(v, j), idx))
1439                                                 return false;
1440                                 }
1441                         }
1442                         else {
1443                                 if (attrs[i].flags & DF_FLAT)
1444                                         idx = attrs[i].attr;
1445                                 else if (attrs[i].auxdata)
1446                                         idx = (uintptr_t)attrs[i].auxdata;
1447                                 else
1448                                         idx = 0;
1449 
1450                                 if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, idx))
1451                                         return false;
1452                         }
1453 
1454                         if (nla_nest)
1455                                 nla_nest_end(msg, nla_nest);
1456                 }
1457                 else if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, 0)) {
1458                         return false;
1459                 }
1460         }
1461 
1462         return true;
1463 }
1464 
1465 static bool
1466 uc_nl_parse_rta_nexthop(struct nl_msg *msg, uc_vm_t *vm, uc_value_t *val)
1467 {
1468         struct { uint16_t family; char addr[sizeof(struct in6_addr)]; } via;
1469         struct nlmsghdr *hdr = nlmsg_hdr(msg);
1470         struct rtmsg *rtm = NLMSG_DATA(hdr);
1471         struct nlattr *rta_gateway;
1472         struct rtnexthop *rtnh;
1473         uc_nl_cidr_t cidr = { 0 };
1474         uc_value_t *v;
1475         uint32_t u;
1476         int aflen;
1477         char *s;
1478 
1479         if (ucv_type(val) != UC_OBJECT)
1480                 return false;
1481 
1482         if (uc_nl_parse_cidr(vm, ucv_object_get(val, "via", NULL), &cidr))
1483                 return false;
1484 
1485         aflen = (cidr.family == AF_INET6 ? sizeof(cidr.addr.in6) : sizeof(cidr.addr.in));
1486 
1487         if (cidr.mask != (aflen * 8))
1488                 return false;
1489 
1490         rta_gateway = nla_reserve(msg, RTA_GATEWAY, sizeof(*rtnh));
1491 
1492         rtnh = nla_data(rta_gateway);
1493         rtnh->rtnh_len = sizeof(*rtnh);
1494 
1495         if (rtm->rtm_family == AF_UNSPEC)
1496                 rtm->rtm_family = cidr.family;
1497 
1498         if (cidr.family == rtm->rtm_family) {
1499                 nla_put(msg, RTA_GATEWAY, aflen, &cidr.addr.in6);
1500                 rtnh->rtnh_len += nla_total_size(aflen);
1501         }
1502         else {
1503                 via.family = cidr.family;
1504                 memcpy(via.addr, &cidr.addr.in6, aflen);
1505                 nla_put(msg, RTA_VIA, sizeof(via.family) + aflen, &via);
1506                 rtnh->rtnh_len += nla_total_size(sizeof(via.family) + aflen);
1507         }
1508 
1509         v = ucv_object_get(val, "dev", NULL);
1510         s = ucv_string_get(v);
1511 
1512         if (s) {
1513                 rtnh->rtnh_ifindex = if_nametoindex(s);
1514 
1515                 if (rtnh->rtnh_ifindex == 0)
1516                         return false;
1517         }
1518 
1519         v = ucv_object_get(val, "weight", NULL);
1520 
1521         if (v) {
1522                 if (!uc_nl_parse_u32(v, &u) || u == 0 || u > 256)
1523                         return false;
1524 
1525                 rtnh->rtnh_hops = u - 1;
1526         }
1527 
1528         if (ucv_is_truish(ucv_object_get(val, "onlink", NULL)))
1529                 rtnh->rtnh_flags |= RTNH_F_ONLINK;
1530 
1531         v = ucv_object_get(val, "realm", NULL);
1532 
1533         if (v) {
1534                 if (!uc_nl_parse_u32(v, &u))
1535                         return false;
1536 
1537                 nla_put_u32(msg, RTA_FLOW, u);
1538                 rtnh->rtnh_len += nla_total_size(sizeof(uint32_t));
1539         }
1540 
1541         v = ucv_object_get(val, "as", NULL);
1542 
1543         if (v) {
1544                 if (!uc_nl_parse_cidr(vm, v, &cidr) || cidr.family != rtm->rtm_family)
1545                         return false;
1546 
1547                 if (cidr.mask != cidr.bitlen)
1548                         return false;
1549 
1550                 nla_put(msg, RTA_NEWDST, cidr.alen, &cidr.addr.in6);
1551                 rtnh->rtnh_len += nla_total_size(cidr.alen);
1552         }
1553 
1554         /* XXX: nla_nest_end(rta_gateway) ? */
1555 
1556         return true;
1557 }
1558 
1559 static bool
1560 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)
1561 {
1562         struct nlattr *rta_multipath = nla_nest_start(msg, spec->attr);
1563         size_t i;
1564 
1565         for (i = 0; i < ucv_array_length(val); i++)
1566                 if (!uc_nl_parse_rta_nexthop(msg, vm, ucv_array_get(val, i)))
1567                         return false;
1568 
1569         nla_nest_end(msg, rta_multipath);
1570 
1571         return true;
1572 }
1573 
1574 static uc_value_t *
1575 uc_nl_convert_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm);
1576 
1577 static uc_value_t *
1578 uc_nl_convert_rta_multipath(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1579 {
1580         uc_nl_attr_spec_t encap_spec = { .attr = RTA_ENCAP };
1581         struct rtnexthop *nh = nla_data(tb[spec->attr]);
1582         struct nlattr *multipath_tb[RTA_MAX + 1];
1583         size_t len = nla_len(tb[spec->attr]);
1584         uc_value_t *nh_obj, *nh_arr;
1585         char buf[INET6_ADDRSTRLEN];
1586         struct rtvia *via;
1587         int af;
1588 
1589         nh_arr = ucv_array_new(vm);
1590 
1591         while (len >= sizeof(*nh)) {
1592                 if ((size_t)NLA_ALIGN(nh->rtnh_len) > len)
1593                         break;
1594 
1595                 nh_obj = ucv_object_new(vm);
1596                 ucv_array_push(nh_arr, nh_obj);
1597 
1598                 nla_parse(multipath_tb, RTA_MAX + 1, (struct nlattr *)RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh), NULL);
1599 
1600                 if (multipath_tb[RTA_GATEWAY]) {
1601                         switch (nla_len(multipath_tb[RTA_GATEWAY])) {
1602                         case 4: af = AF_INET; break;
1603                         case 16: af = AF_INET6; break;
1604                         default: af = AF_UNSPEC; break;
1605                         }
1606 
1607                         if (inet_ntop(af, nla_data(multipath_tb[RTA_GATEWAY]), buf, sizeof(buf)))
1608                                 ucv_object_add(nh_obj, "via", ucv_string_new(buf));
1609                 }
1610 
1611                 if (multipath_tb[RTA_VIA]) {
1612                         if (nla_len(multipath_tb[RTA_VIA]) > (ssize_t)sizeof(*via)) {
1613                                 via = nla_data(multipath_tb[RTA_VIA]);
1614                                 af = via->rtvia_family;
1615 
1616                                 if ((af == AF_INET &&
1617                                      nla_len(multipath_tb[RTA_VIA]) == sizeof(*via) + sizeof(struct in_addr)) ||
1618                                         (af == AF_INET6 &&
1619                                      nla_len(multipath_tb[RTA_VIA]) == sizeof(*via) + sizeof(struct in6_addr))) {
1620                                         if (inet_ntop(af, via->rtvia_addr, buf, sizeof(buf)))
1621                                                 ucv_object_add(nh_obj, "via", ucv_string_new(buf));
1622                                 }
1623                         }
1624                 }
1625 
1626                 if (if_indextoname(nh->rtnh_ifindex, buf))
1627                         ucv_object_add(nh_obj, "dev", ucv_string_new(buf));
1628 
1629                 ucv_object_add(nh_obj, "weight", ucv_int64_new(nh->rtnh_hops + 1));
1630                 ucv_object_add(nh_obj, "onlink", ucv_boolean_new(nh->rtnh_flags & RTNH_F_ONLINK));
1631 
1632                 if (multipath_tb[RTA_FLOW] && nla_len(multipath_tb[RTA_FLOW]) == sizeof(uint32_t))
1633                         ucv_object_add(nh_obj, "realm", ucv_int64_new(nla_get_u32(multipath_tb[RTA_FLOW])));
1634 
1635                 if (multipath_tb[RTA_ENCAP])
1636                         ucv_object_add(nh_obj, "encap",
1637                                 uc_nl_convert_rta_encap(&encap_spec, msg, multipath_tb, vm));
1638 
1639                 if (multipath_tb[RTA_NEWDST]) {
1640                         switch (nla_len(multipath_tb[RTA_NEWDST])) {
1641                         case 4: af = AF_INET; break;
1642                         case 16: af = AF_INET6; break;
1643                         default: af = AF_UNSPEC; break;
1644                         }
1645 
1646                         if (inet_ntop(af, nla_data(multipath_tb[RTA_NEWDST]), buf, sizeof(buf)))
1647                                 ucv_object_add(nh_obj, "as", ucv_string_new(buf));
1648                 }
1649 
1650                 len -= NLA_ALIGN(nh->rtnh_len);
1651                 nh = RTNH_NEXT(nh);
1652         }
1653 
1654         return nh_arr;
1655 }
1656 
1657 static bool
1658 parse_num(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *val, void *dst)
1659 {
1660         int64_t n = ucv_int64_get(val);
1661         uint32_t *u32;
1662         uint16_t *u16;
1663         uint8_t *u8;
1664 
1665         if (spec->flags & DF_MAX_255) {
1666                 if (n < 0 || n > 255)
1667                         return nla_parse_error(spec, vm, val, "number out of range 0-255");
1668 
1669                 u8 = dst; *u8 = n;
1670         }
1671         else if (spec->flags & DF_MAX_65535) {
1672                 if (n < 0 || n > 65535)
1673                         return nla_parse_error(spec, vm, val, "number out of range 0-65535");
1674 
1675                 u16 = dst; *u16 = n;
1676 
1677                 if (spec->flags & DF_BYTESWAP)
1678                         *u16 = htons(*u16);
1679         }
1680         else if (spec->flags & DF_MAX_16777215) {
1681                 if (n < 0 || n > 16777215)
1682                         return nla_parse_error(spec, vm, val, "number out of range 0-16777215");
1683 
1684                 u32 = dst; *u32 = n;
1685 
1686                 if (spec->flags & DF_BYTESWAP)
1687                         *u32 = htonl(*u32);
1688         }
1689         else {
1690                 if (n < 0 || n > 4294967295)
1691                         return nla_parse_error(spec, vm, val, "number out of range 0-4294967295");
1692 
1693                 u32 = dst; *u32 = n;
1694 
1695                 if (spec->flags & DF_BYTESWAP)
1696                         *u32 = htonl(*u32);
1697         }
1698 
1699         return true;
1700 }
1701 
1702 static bool
1703 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)
1704 {
1705         union {
1706                 struct { uint8_t low; uint8_t high; } u8;
1707                 struct { uint16_t low; uint16_t high; } u16;
1708                 struct { uint32_t low; uint32_t high; } u32;
1709         } ranges = { 0 };
1710 
1711         void *d1, *d2;
1712         size_t len;
1713 
1714         if (ucv_array_length(val) != 2 ||
1715             ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
1716             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
1717                 return nla_parse_error(spec, vm, val, "not a two-element array of numbers");
1718 
1719         if (spec->flags & DF_MAX_255) {
1720                 len = sizeof(ranges.u8);
1721                 d1 = &ranges.u8.low;
1722                 d2 = &ranges.u8.high;
1723         }
1724         else if (spec->flags & DF_MAX_65535) {
1725                 len = sizeof(ranges.u16);
1726                 d1 = &ranges.u16.low;
1727                 d2 = &ranges.u16.high;
1728         }
1729         else {
1730                 len = sizeof(ranges.u32);
1731                 d1 = &ranges.u32.low;
1732                 d2 = &ranges.u32.high;
1733         }
1734 
1735         if (!parse_num(spec, vm, ucv_array_get(val, 0), d1) ||
1736             !parse_num(spec, vm, ucv_array_get(val, 1), d2))
1737             return false;
1738 
1739         nla_put(msg, spec->attr, len, d1);
1740 
1741         return true;
1742 }
1743 
1744 static uc_value_t *
1745 uc_nl_convert_rta_numrange(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1746 {
1747         union {
1748                 struct { uint8_t low; uint8_t high; } *u8;
1749                 struct { uint16_t low; uint16_t high; } *u16;
1750                 struct { uint32_t low; uint32_t high; } *u32;
1751         } ranges = { 0 };
1752 
1753         bool swap = (spec->flags & DF_BYTESWAP);
1754         uc_value_t *arr, *n1, *n2;
1755 
1756         if (spec->flags & DF_MAX_255) {
1757                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u8)))
1758                         return NULL;
1759 
1760                 ranges.u8 = nla_data(tb[spec->attr]);
1761                 n1 = ucv_int64_new(ranges.u8->low);
1762                 n2 = ucv_int64_new(ranges.u8->high);
1763         }
1764         else if (spec->flags & DF_MAX_65535) {
1765                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u16)))
1766                         return NULL;
1767 
1768                 ranges.u16 = nla_data(tb[spec->attr]);
1769                 n1 = ucv_int64_new(swap ? ntohs(ranges.u16->low) : ranges.u16->low);
1770                 n2 = ucv_int64_new(swap ? ntohs(ranges.u16->high) : ranges.u16->high);
1771         }
1772         else {
1773                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u32)))
1774                         return NULL;
1775 
1776                 ranges.u32 = nla_data(tb[spec->attr]);
1777                 n1 = ucv_int64_new(swap ? ntohl(ranges.u32->low) : ranges.u32->low);
1778                 n2 = ucv_int64_new(swap ? ntohl(ranges.u32->high) : ranges.u32->high);
1779         }
1780 
1781         arr = ucv_array_new(vm);
1782 
1783         ucv_array_push(arr, n1);
1784         ucv_array_push(arr, n2);
1785 
1786         return arr;
1787 }
1788 
1789 
1790 #define LINK_TYPE(name) \
1791         { #name, link_##name##_attrs, ARRAY_SIZE(link_##name##_attrs) }
1792 
1793 static const struct {
1794         const char *name;
1795         const uc_nl_attr_spec_t *attrs;
1796         size_t nattrs;
1797 } link_types[] = {
1798         LINK_TYPE(bareudp),
1799         LINK_TYPE(bond),
1800         LINK_TYPE(bond_slave),
1801         LINK_TYPE(bridge),
1802         LINK_TYPE(bridge_slave),
1803         LINK_TYPE(geneve),
1804         LINK_TYPE(hsr),
1805         LINK_TYPE(ipoib),
1806         LINK_TYPE(ipvlan),
1807         LINK_TYPE(macvlan),
1808         LINK_TYPE(rmnet),
1809         LINK_TYPE(vlan),
1810         LINK_TYPE(vrf),
1811         //LINK_TYPE(vxcan),
1812         LINK_TYPE(vxlan),
1813         //LINK_TYPE(xdp),
1814         //LINK_TYPE(xstats),
1815         LINK_TYPE(gre),
1816         LINK_TYPE(gretap),
1817         LINK_TYPE(erspan),
1818         LINK_TYPE(ip6gre),
1819         LINK_TYPE(ip6gretap),
1820         LINK_TYPE(ip6erspan),
1821         LINK_TYPE(ip6tnl),
1822         LINK_TYPE(ipip),
1823         LINK_TYPE(sit),
1824         LINK_TYPE(veth),
1825         LINK_TYPE(vti),
1826         LINK_TYPE(vti6),
1827         LINK_TYPE(xfrm),
1828 };
1829 
1830 static bool
1831 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)
1832 {
1833         const uc_nl_attr_spec_t *attrs = NULL;
1834         struct nlattr *li_nla, *info_nla;
1835         size_t i, nattrs = 0;
1836         char *kind, *p;
1837         uc_value_t *k;
1838 
1839         k = ucv_object_get(val, "type", NULL);
1840         kind = ucv_string_get(k);
1841 
1842         if (!kind)
1843                 return nla_parse_error(spec, vm, val, "linkinfo does not specify kind");
1844 
1845         li_nla = nla_nest_start(msg, spec->attr);
1846 
1847         nla_put_string(msg, IFLA_INFO_KIND, kind);
1848 
1849         for (i = 0; i < ARRAY_SIZE(link_types); i++) {
1850                 if (!strcmp(link_types[i].name, kind)) {
1851                         attrs = link_types[i].attrs;
1852                         nattrs = link_types[i].nattrs;
1853                         break;
1854                 }
1855         }
1856 
1857         p = strchr(kind, '_');
1858 
1859         if (!p || strcmp(p, "_slave"))
1860                 info_nla = nla_nest_start(msg, IFLA_INFO_DATA);
1861         else
1862                 info_nla = nla_nest_start(msg, IFLA_INFO_SLAVE_DATA);
1863 
1864         if (!uc_nl_parse_attrs(msg, base, attrs, nattrs, vm, val))
1865                 return false;
1866 
1867         nla_nest_end(msg, info_nla);
1868         nla_nest_end(msg, li_nla);
1869 
1870         return true;
1871 }
1872 
1873 static uc_value_t *
1874 uc_nl_convert_rta_linkinfo_data(uc_value_t *obj, size_t attr, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1875 {
1876         const uc_nl_attr_spec_t *attrs = NULL;
1877         size_t i, nattrs = 0;
1878         uc_value_t *v;
1879         bool rv;
1880 
1881         if (!tb[attr] || nla_len(tb[attr]) < 1)
1882                 return NULL;
1883 
1884         v = ucv_string_new_length(nla_data(tb[attr]), nla_len(tb[attr]) - 1);
1885 
1886         ucv_object_add(obj, "type", v);
1887 
1888         for (i = 0; i < ARRAY_SIZE(link_types); i++) {
1889                 if (!strcmp(link_types[i].name, ucv_string_get(v))) {
1890                         attrs = link_types[i].attrs;
1891                         nattrs = link_types[i].nattrs;
1892                         break;
1893                 }
1894         }
1895 
1896         attr = (attr == IFLA_INFO_KIND) ? IFLA_INFO_DATA : IFLA_INFO_SLAVE_DATA;
1897 
1898         if (nattrs > 0 && tb[attr]) {
1899                 rv = uc_nl_convert_attrs(msg, nla_data(tb[attr]), nla_len(tb[attr]), 0, attrs, nattrs, vm, obj);
1900 
1901                 if (!rv)
1902                         return NULL;
1903         }
1904 
1905         return obj;
1906 }
1907 
1908 static uc_value_t *
1909 uc_nl_convert_rta_linkinfo(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1910 {
1911         struct nlattr *linkinfo_tb[IFLA_INFO_MAX];
1912         uc_value_t *info_obj, *slave_obj;
1913 
1914         if (!tb[spec->attr])
1915                 return NULL;
1916 
1917         nla_parse(linkinfo_tb, IFLA_INFO_MAX, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), NULL);
1918 
1919         info_obj = ucv_object_new(vm);
1920 
1921         if (linkinfo_tb[IFLA_INFO_KIND]) {
1922                 if (!uc_nl_convert_rta_linkinfo_data(info_obj, IFLA_INFO_KIND, msg, linkinfo_tb, vm)) {
1923                         ucv_put(info_obj);
1924 
1925                         return NULL;
1926                 }
1927         }
1928 
1929         if (linkinfo_tb[IFLA_INFO_SLAVE_KIND]) {
1930                 slave_obj = ucv_object_new(vm);
1931 
1932                 if (!uc_nl_convert_rta_linkinfo_data(slave_obj, IFLA_INFO_SLAVE_KIND, msg, linkinfo_tb, vm)) {
1933                         ucv_put(info_obj);
1934                         ucv_put(slave_obj);
1935 
1936                         return NULL;
1937                 }
1938 
1939                 ucv_object_add(info_obj, "slave", slave_obj);
1940         }
1941 
1942         return info_obj;
1943 }
1944 
1945 static uc_value_t *
1946 uc_nl_convert_rta_bridgeid(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1947 {
1948         char buf[sizeof("ffff.ff:ff:ff:ff:ff:ff")];
1949         struct ifla_bridge_id *id;
1950 
1951         if (!nla_check_len(tb[spec->attr], sizeof(*id)))
1952                 return NULL;
1953 
1954         id = nla_data(tb[spec->attr]);
1955 
1956         snprintf(buf, sizeof(buf), "%02x%02x.%02x:%02x:%02x:%02x:%02x:%02x",
1957                 id->prio[0], id->prio[1],
1958                 id->addr[0], id->addr[1],
1959                 id->addr[2], id->addr[3],
1960                 id->addr[4], id->addr[5]);
1961 
1962         return ucv_string_new(buf);
1963 }
1964 
1965 static bool
1966 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)
1967 {
1968         uc_value_t *mode, *hmac, *segs, *seg;
1969         struct seg6_iptunnel_encap *tun;
1970         struct sr6_tlv_hmac *tlv;
1971         struct ipv6_sr_hdr *srh;
1972         size_t i, nsegs, srhlen;
1973         char *s;
1974 
1975         mode = ucv_object_get(val, "mode", NULL);
1976         hmac = ucv_object_get(val, "hmac", NULL);
1977         segs = ucv_object_get(val, "segs", NULL);
1978 
1979         if (mode != NULL &&
1980             (ucv_type(mode) != UC_INTEGER ||
1981              ucv_int64_get(mode) < 0 ||
1982              ucv_int64_get(mode) > UINT32_MAX))
1983                 return nla_parse_error(spec, vm, val, "srh mode not an integer in range 0-4294967295");
1984 
1985         if (hmac != NULL &&
1986             (ucv_type(hmac) != UC_INTEGER ||
1987              ucv_int64_get(hmac) < 0 ||
1988              ucv_int64_get(hmac) > UINT32_MAX))
1989                 return nla_parse_error(spec, vm, val, "srh hmac not an integer in range 0-4294967295");
1990 
1991         if (ucv_type(segs) != UC_ARRAY ||
1992             ucv_array_length(segs) == 0)
1993                 return nla_parse_error(spec, vm, val, "srh segs array missing or empty");
1994 
1995         nsegs = ucv_array_length(segs);
1996 
1997         if (!mode || !ucv_int64_get(mode))
1998                 nsegs++;
1999 
2000         srhlen = 8 + 16 * nsegs;
2001 
2002         if (hmac && ucv_int64_get(hmac))
2003                 srhlen += 40;
2004 
2005 
2006         tun = calloc(1, sizeof(*tun) + srhlen);
2007 
2008         if (!tun)
2009                 return nla_parse_error(spec, vm, val, "cannot allocate srh header");
2010 
2011         tun->mode = (int)ucv_int64_get(mode);
2012 
2013         srh = tun->srh;
2014         srh->hdrlen = (srhlen >> 3) - 1;
2015         srh->type = 4;
2016         srh->segments_left = nsegs - 1;
2017         srh->first_segment = nsegs - 1;
2018 
2019         if (hmac && ucv_int64_get(hmac))
2020                 srh->flags |= SR6_FLAG1_HMAC;
2021 
2022         for (i = 0; i < ucv_array_length(segs); i++) {
2023                 seg = ucv_array_get(segs, i);
2024                 s = ucv_string_get(seg);
2025 
2026                 if (!s || inet_pton(AF_INET6, s, &srh->segments[--nsegs]) != 1) {
2027                         free(tun);
2028 
2029                         return nla_parse_error(spec, vm, val, "srh segs array contains invalid IPv6 address");
2030                 }
2031         }
2032 
2033         if (hmac && ucv_int64_get(hmac)) {
2034                 tlv = (struct sr6_tlv_hmac *)((char *)srh + srhlen - 40);
2035                 tlv->tlvhdr.type = SR6_TLV_HMAC;
2036                 tlv->tlvhdr.len = 38;
2037                 tlv->hmackeyid = htonl((uint32_t)ucv_int64_get(hmac));
2038         }
2039 
2040         nla_put(msg, spec->attr, sizeof(*tun) + srhlen, tun);
2041         free(tun);
2042 
2043         return true;
2044 }
2045 
2046 static uc_value_t *
2047 uc_nl_convert_rta_srh(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2048 {
2049         char buf[INET6_ADDRSTRLEN], *p, *e;
2050         struct seg6_iptunnel_encap *tun;
2051         uc_value_t *tun_obj, *seg_arr;
2052         struct sr6_tlv_hmac *tlv;
2053         size_t i;
2054 
2055         if (!nla_check_len(tb[spec->attr], sizeof(*tun)))
2056                 return NULL;
2057 
2058         tun = nla_data(tb[spec->attr]);
2059         tun_obj = ucv_object_new(vm);
2060 
2061         ucv_object_add(tun_obj, "mode", ucv_int64_new(tun->mode));
2062 
2063         seg_arr = ucv_array_new(vm);
2064 
2065         p = (char *)tun->srh->segments;
2066         e = (char *)tun + nla_len(tb[spec->attr]);
2067 
2068         for (i = tun->srh->first_segment + 1;
2069              p + sizeof(struct in6_addr) <= e && i > 0;
2070              i--, p += sizeof(struct in6_addr)) {
2071                 if (inet_ntop(AF_INET6, p, buf, sizeof(buf)))
2072                         ucv_array_push(seg_arr, ucv_string_new(buf));
2073                 else
2074                         ucv_array_push(seg_arr, NULL);
2075         }
2076 
2077         ucv_object_add(tun_obj, "segs", seg_arr);
2078 
2079         if (sr_has_hmac(tun->srh)) {
2080                 i = ((tun->srh->hdrlen + 1) << 3) - 40;
2081                 tlv = (struct sr6_tlv_hmac *)((char *)tun->srh + i);
2082 
2083                 ucv_object_add(tun_obj, "hmac", ucv_int64_new(ntohl(tlv->hmackeyid)));
2084         }
2085 
2086         return tun_obj;
2087 }
2088 
2089 #define ENCAP_TYPE(name, type) \
2090         { #name, LWTUNNEL_ENCAP_##type, route_encap_##name##_attrs, ARRAY_SIZE(route_encap_##name##_attrs) }
2091 
2092 static const struct {
2093         const char *name;
2094         uint16_t type;
2095         const uc_nl_attr_spec_t *attrs;
2096         size_t nattrs;
2097 } encap_types[] = {
2098         ENCAP_TYPE(mpls, MPLS),
2099         ENCAP_TYPE(ip, IP),
2100         ENCAP_TYPE(ip6, IP6),
2101         ENCAP_TYPE(ila, ILA),
2102         //ENCAP_TYPE(bpf, BPF),
2103         ENCAP_TYPE(seg6, SEG6),
2104         //ENCAP_TYPE(seg6local, SEG6_LOCAL),
2105         //ENCAP_TYPE(rpl, RPL),
2106 };
2107 
2108 static bool
2109 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)
2110 {
2111         const uc_nl_attr_spec_t *attrs = NULL;
2112         struct nlattr *enc_nla;
2113         size_t i, nattrs = 0;
2114         uint16_t ntype = 0;
2115         uc_value_t *t;
2116         char *type;
2117 
2118         t = ucv_object_get(val, "type", NULL);
2119         type = ucv_string_get(t);
2120 
2121         if (!type)
2122                 return nla_parse_error(spec, vm, val, "encap does not specify type");
2123 
2124         for (i = 0; i < ARRAY_SIZE(encap_types); i++) {
2125                 if (!strcmp(encap_types[i].name, type)) {
2126                         ntype = encap_types[i].type;
2127                         attrs = encap_types[i].attrs;
2128                         nattrs = encap_types[i].nattrs;
2129                         break;
2130                 }
2131         }
2132 
2133         if (!ntype)
2134                 return nla_parse_error(spec, vm, val, "encap specifies unknown type");
2135 
2136         nla_put_u16(msg, RTA_ENCAP_TYPE, ntype);
2137 
2138         enc_nla = nla_nest_start(msg, spec->attr);
2139 
2140         if (!uc_nl_parse_attrs(msg, base, attrs, nattrs, vm, val))
2141                 return false;
2142 
2143         nla_nest_end(msg, enc_nla);
2144 
2145         return true;
2146 }
2147 
2148 static uc_value_t *
2149 uc_nl_convert_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2150 {
2151         const uc_nl_attr_spec_t *attrs = NULL;
2152         const char *name = NULL;
2153         uc_value_t *encap_obj;
2154         size_t i, nattrs = 0;
2155         bool rv;
2156 
2157         if (!tb[spec->attr] ||
2158             !nla_check_len(tb[RTA_ENCAP_TYPE], sizeof(uint16_t)))
2159                 return NULL;
2160 
2161         for (i = 0; i < ARRAY_SIZE(encap_types); i++) {
2162                 if (encap_types[i].type != nla_get_u16(tb[RTA_ENCAP_TYPE]))
2163                         continue;
2164 
2165                 name = encap_types[i].name;
2166                 attrs = encap_types[i].attrs;
2167                 nattrs = encap_types[i].nattrs;
2168 
2169                 break;
2170         }
2171 
2172         if (!name)
2173                 return NULL;
2174 
2175         encap_obj = ucv_object_new(vm);
2176 
2177         rv = uc_nl_convert_attrs(msg,
2178                 nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), 0,
2179                 attrs, nattrs, vm, encap_obj);
2180 
2181         if (!rv) {
2182                 ucv_put(encap_obj);
2183 
2184                 return NULL;
2185         }
2186 
2187         ucv_object_add(encap_obj, "type", ucv_string_new(name));
2188 
2189         return encap_obj;
2190 }
2191 
2192 #define IPOPTS_TYPE(name, type, multiple) \
2193         { #name, LWTUNNEL_IP_OPTS_##type, multiple, lwtipopt_##name##_attrs, ARRAY_SIZE(lwtipopt_##name##_attrs) }
2194 
2195 static const struct {
2196         const char *name;
2197         uint16_t type;
2198         bool multiple;
2199         const uc_nl_attr_spec_t *attrs;
2200         size_t nattrs;
2201 } lwtipopt_types[] = {
2202         IPOPTS_TYPE(erspan, ERSPAN, false),
2203         IPOPTS_TYPE(geneve, GENEVE, true),
2204         IPOPTS_TYPE(vxlan, VXLAN, false),
2205 };
2206 
2207 static bool
2208 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)
2209 {
2210         const uc_nl_attr_spec_t *attrs = NULL;
2211         struct nlattr *opt_nla, *type_nla;
2212         bool exists, multiple = false;
2213         size_t i, j, nattrs = 0;
2214         uint16_t ntype = 0;
2215         uc_value_t *item;
2216 
2217         ucv_object_foreach(val, type, v) {
2218                 for (i = 0; i < ARRAY_SIZE(lwtipopt_types); i++) {
2219                         if (!strcmp(lwtipopt_types[i].name, type)) {
2220                                 val = v;
2221                                 ntype = lwtipopt_types[i].type;
2222                                 attrs = lwtipopt_types[i].attrs;
2223                                 nattrs = lwtipopt_types[i].nattrs;
2224                                 multiple = lwtipopt_types[i].multiple;
2225                                 break;
2226                         }
2227                 }
2228         }
2229 
2230         if (!ntype)
2231                 return nla_parse_error(spec, vm, val, "unknown IP options type specified");
2232 
2233         opt_nla = nla_nest_start(msg, spec->attr);
2234 
2235         j = 0;
2236         item = (ucv_type(val) == UC_ARRAY) ? ucv_array_get(val, j++) : val;
2237 
2238         while (true) {
2239                 type_nla = nla_nest_start(msg, ntype);
2240 
2241                 for (i = 0; i < nattrs; i++) {
2242                         v = ucv_object_get(item, attrs[i].key, &exists);
2243 
2244                         if (!exists)
2245                                 continue;
2246 
2247                         if (!uc_nl_parse_attr(&attrs[i], msg, nla_data(type_nla), vm, v, 0))
2248                                 return false;
2249                 }
2250 
2251                 nla_nest_end(msg, type_nla);
2252 
2253                 if (!multiple || ucv_type(val) != UC_ARRAY || j >= ucv_array_length(val))
2254                         break;
2255 
2256                 item = ucv_array_get(val, j++);
2257         }
2258 
2259         nla_nest_end(msg, opt_nla);
2260 
2261         return true;
2262 }
2263 
2264 static uc_value_t *
2265 uc_nl_convert_rta_ipopts(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2266 {
2267         struct nlattr *opt_tb[LWTUNNEL_IP_OPTS_MAX + 1];
2268         const uc_nl_attr_spec_t *attrs = NULL;
2269         uc_value_t *opt_obj, *type_obj;
2270         const char *name = NULL;
2271         size_t i, nattrs = 0;
2272         uint16_t type = 0;
2273         bool rv;
2274 
2275         if (!tb[spec->attr] ||
2276                 !nla_parse(opt_tb, LWTUNNEL_IP_OPTS_MAX, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), NULL))
2277                 return NULL;
2278 
2279         for (i = 0; i < ARRAY_SIZE(lwtipopt_types); i++) {
2280                 if (!opt_tb[lwtipopt_types[i].type])
2281                         continue;
2282 
2283                 type = lwtipopt_types[i].type;
2284                 name = lwtipopt_types[i].name;
2285                 attrs = lwtipopt_types[i].attrs;
2286                 nattrs = lwtipopt_types[i].nattrs;
2287 
2288                 break;
2289         }
2290 
2291         if (!name)
2292                 return NULL;
2293 
2294         type_obj = ucv_object_new(vm);
2295 
2296         rv = uc_nl_convert_attrs(msg,
2297                 nla_data(opt_tb[type]), nla_len(opt_tb[type]), 0,
2298                 attrs, nattrs, vm, type_obj);
2299 
2300         if (!rv) {
2301                 ucv_put(type_obj);
2302 
2303                 return NULL;
2304         }
2305 
2306         opt_obj = ucv_object_new(vm);
2307 
2308         ucv_object_add(opt_obj, name, type_obj);
2309 
2310         return opt_obj;
2311 }
2312 
2313 static bool
2314 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)
2315 {
2316         uint32_t u32;
2317 
2318         if (!uc_nl_parse_u32(val, &u32))
2319                 return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
2320 
2321         if (spec->flags & DF_MAX_255) {
2322                 if (u32 <= 255) {
2323                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2324 
2325                         return true;
2326                 }
2327 
2328                 uc_nl_put_struct_member_u8(base, spec->auxdata, 0);
2329         }
2330         else if (spec->flags & DF_MAX_65535) {
2331                 if (u32 <= 65535) {
2332                         uc_nl_put_struct_member_u16(base, spec->auxdata,
2333                                 (spec->flags & DF_BYTESWAP) ? htons((uint16_t)u32) : (uint16_t)u32);
2334 
2335                         return true;
2336                 }
2337 
2338                 uc_nl_put_struct_member_u16(base, spec->auxdata, 0);
2339         }
2340         else if (spec->flags & DF_MAX_16777215) {
2341                 if (u32 <= 16777215) {
2342                         uc_nl_put_struct_member_u32(base, spec->auxdata,
2343                                 (spec->flags & DF_BYTESWAP) ? htonl(u32) : u32);
2344 
2345                         return true;
2346                 }
2347 
2348                 uc_nl_put_struct_member_u32(base, spec->auxdata, 0);
2349         }
2350 
2351         nla_put_u32(msg, spec->attr,
2352                 (spec->flags & DF_BYTESWAP) ? htonl(u32) : u32);
2353 
2354         return true;
2355 }
2356 
2357 static uc_value_t *
2358 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)
2359 {
2360         uint32_t u32 = 0;
2361 
2362         if (nla_check_len(tb[spec->attr], sizeof(uint32_t))) {
2363                 if (spec->flags & DF_BYTESWAP)
2364                         u32 = ntohl(nla_get_u32(tb[spec->attr]));
2365                 else
2366                         u32 = nla_get_u32(tb[spec->attr]);
2367         }
2368         else if (spec->flags & DF_MAX_255) {
2369                 u32 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2370         }
2371         else if (spec->flags & DF_MAX_65535) {
2372                 if (spec->flags & DF_BYTESWAP)
2373                         u32 = ntohs(uc_nl_get_struct_member_u16(base, spec->auxdata));
2374                 else
2375                         u32 = uc_nl_get_struct_member_u16(base, spec->auxdata);
2376         }
2377         else if (spec->flags & DF_MAX_16777215) {
2378                 if (spec->flags & DF_BYTESWAP)
2379                         u32 = ntohl(uc_nl_get_struct_member_u32(base, spec->auxdata));
2380                 else
2381                         u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2382         }
2383         else {
2384                 return NULL;
2385         }
2386 
2387         return ucv_uint64_new(u32);
2388 }
2389 
2390 static bool
2391 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)
2392 {
2393         const uc_nl_nested_spec_t *nest = spec->auxdata;
2394         struct nlattr *nested_nla;
2395 
2396         nested_nla = nla_reserve(msg, spec->attr, nest->headsize);
2397 
2398         if (!uc_nl_parse_attrs(msg, nla_data(nested_nla), nest->attrs, nest->nattrs, vm, val))
2399                 return false;
2400 
2401         nla_nest_end(msg, nested_nla);
2402 
2403         return true;
2404 }
2405 
2406 static uc_value_t *
2407 uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2408 {
2409         const uc_nl_nested_spec_t *nest = spec->auxdata;
2410         uc_value_t *nested_obj;
2411         bool rv;
2412 
2413         nested_obj = ucv_object_new(vm);
2414 
2415         rv = uc_nl_convert_attrs(msg,
2416                 nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), nest->headsize,
2417                 nest->attrs, nest->nattrs,
2418                 vm, nested_obj);
2419 
2420         if (!rv) {
2421                 ucv_put(nested_obj);
2422 
2423                 return NULL;
2424         }
2425 
2426         return nested_obj;
2427 }
2428 
2429 
2430 static bool
2431 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)
2432 {
2433         uc_nl_cidr_t cidr = { 0 };
2434         struct ether_addr *ea;
2435         struct rtgenmsg *rtg;
2436         uint64_t u64;
2437         uint32_t u32;
2438         uint16_t u16;
2439         size_t attr;
2440         char *s;
2441 
2442         if (spec->flags & DF_MULTIPLE)
2443                 attr = idx;
2444         else
2445                 attr = spec->attr;
2446 
2447         switch (spec->type) {
2448         case DT_U8:
2449                 if (!uc_nl_parse_u32(val, &u32) || u32 > 255)
2450                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-255");
2451 
2452                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2453                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2454 
2455                 if (spec->attr == 0)
2456                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2457                 else
2458                         nla_put_u8(msg, attr, u32);
2459 
2460                 break;
2461 
2462         case DT_U16:
2463                 if (!uc_nl_parse_u32(val, &u32) || u32 > 65535)
2464                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-65535");
2465 
2466                 u16 = (uint16_t)u32;
2467 
2468                 if (spec->flags & DF_BYTESWAP)
2469                         u16 = htons(u16);
2470 
2471                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2472                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2473                 else if ((spec->flags & DF_MAX_255) && u32 > 255)
2474                         return nla_parse_error(spec, vm, val, "integer out of range 0-255");
2475 
2476                 if (spec->attr == 0)
2477                         uc_nl_put_struct_member_u16(base, spec->auxdata, u16);
2478                 else
2479                         nla_put_u16(msg, attr, u16);
2480 
2481                 break;
2482 
2483         case DT_S32:
2484         case DT_U32:
2485                 if (spec->type == DT_S32 && !uc_nl_parse_s32(val, &u32))
2486                         return nla_parse_error(spec, vm, val, "not an integer or out of range -2147483648-2147483647");
2487                 else if (spec->type == DT_U32 && !uc_nl_parse_u32(val, &u32))
2488                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
2489 
2490                 if (spec->flags & DF_BYTESWAP)
2491                         u32 = htonl(u32);
2492 
2493                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2494                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2495                 else if ((spec->flags & DF_MAX_255) && u32 > 255)
2496                         return nla_parse_error(spec, vm, val, "integer out of range 0-255");
2497                 else if ((spec->flags & DF_MAX_65535) && u32 > 65535)
2498                         return nla_parse_error(spec, vm, val, "integer out of range 0-65535");
2499                 else if ((spec->flags & DF_MAX_16777215) && u32 > 16777215)
2500                         return nla_parse_error(spec, vm, val, "integer out of range 0-16777215");
2501 
2502                 if (spec->attr == 0)
2503                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2504                 else
2505                         nla_put_u32(msg, attr, u32);
2506 
2507                 break;
2508 
2509         case DT_U64:
2510                 assert(spec->attr != 0);
2511 
2512                 if (!uc_nl_parse_u64(val, &u64))
2513                         return nla_parse_error(spec, vm, val, "not an integer or negative");
2514 
2515                 if (spec->flags & DF_BYTESWAP)
2516                         u64 = htobe64(u64);
2517 
2518                 nla_put_u64(msg, attr, u64);
2519                 break;
2520 
2521         case DT_BOOL:
2522                 u32 = (uint32_t)ucv_is_truish(val);
2523 
2524                 if (spec->attr == 0)
2525                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2526                 else
2527                         nla_put_u8(msg, attr, u32);
2528 
2529                 break;
2530 
2531         case DT_FLAG:
2532                 u32 = (uint32_t)ucv_is_truish(val);
2533 
2534                 if (spec->attr == 0)
2535                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2536                 else if (u32 == 1)
2537                         nla_put_flag(msg, attr);
2538 
2539                 break;
2540 
2541         case DT_STRING:
2542                 assert(spec->attr != 0);
2543 
2544                 s = ucv_to_string(vm, val);
2545 
2546                 if (!s)
2547                         return nla_parse_error(spec, vm, val, "out of memory");
2548 
2549                 nla_put_string(msg, attr, s);
2550                 free(s);
2551 
2552                 break;
2553 
2554         case DT_NETDEV:
2555                 if (ucv_type(val) == UC_INTEGER) {
2556                         if (ucv_int64_get(val) < 0 ||
2557                             ucv_int64_get(val) > UINT32_MAX)
2558                                 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295");
2559 
2560                         u32 = (uint32_t)ucv_int64_get(val);
2561                 }
2562                 else {
2563                         s = ucv_to_string(vm, val);
2564 
2565                         if (!s)
2566                                 return nla_parse_error(spec, vm, val, "out of memory");
2567 
2568                         u32 = if_nametoindex(s);
2569 
2570                         free(s);
2571                 }
2572 
2573                 if (u32 == 0 && !(spec->flags & DF_ALLOW_NONE))
2574                         return nla_parse_error(spec, vm, val, "interface not found");
2575 
2576                 if (spec->attr == 0)
2577                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2578                 else
2579                         nla_put_u32(msg, attr, u32);
2580 
2581                 break;
2582 
2583         case DT_LLADDR:
2584                 assert(spec->attr != 0);
2585 
2586                 s = ucv_to_string(vm, val);
2587 
2588                 if (!s)
2589                         return nla_parse_error(spec, vm, val, "out of memory");
2590 
2591                 ea = ether_aton(s);
2592 
2593                 free(s);
2594 
2595                 if (!ea)
2596                         return nla_parse_error(spec, vm, val, "invalid MAC address");
2597 
2598                 nla_put(msg, attr, sizeof(*ea), ea);
2599 
2600                 break;
2601 
2602         case DT_U64ADDR:
2603                 assert(spec->attr != 0);
2604 
2605                 if (ucv_type(val) == UC_INTEGER) {
2606                         u64 = ucv_uint64_get(val);
2607                 }
2608                 else {
2609                         s = ucv_to_string(vm, val);
2610 
2611                         if (!s)
2612                                 return nla_parse_error(spec, vm, val, "out of memory");
2613 
2614                         u16 = addr64_pton(s, &u64);
2615 
2616                         free(s);
2617 
2618                         if (u16 != 1)
2619                                 return nla_parse_error(spec, vm, val, "invalid address");
2620                 }
2621 
2622                 nla_put_u64(msg, attr, u64);
2623 
2624                 break;
2625 
2626         case DT_INADDR:
2627         case DT_IN6ADDR:
2628         case DT_MPLSADDR:
2629         case DT_ANYADDR:
2630                 assert(spec->attr != 0);
2631 
2632                 rtg = nlmsg_data(nlmsg_hdr(msg));
2633 
2634                 if (!uc_nl_parse_cidr(vm, val, &cidr))
2635                         return nla_parse_error(spec, vm, val, "invalid IP address");
2636 
2637                 if ((spec->type == DT_INADDR && cidr.family != AF_INET) ||
2638                     (spec->type == DT_IN6ADDR && cidr.family != AF_INET6) ||
2639                     (spec->type == DT_MPLSADDR && cidr.family != AF_MPLS))
2640                     return nla_parse_error(spec, vm, val, "wrong address family");
2641 
2642                 if (spec->flags & DF_STORE_MASK)
2643                         uc_nl_put_struct_member_u8(base, spec->auxdata, cidr.mask);
2644                 else if (cidr.mask != cidr.bitlen)
2645                         return nla_parse_error(spec, vm, val, "address range given but single address expected");
2646 
2647                 nla_put(msg, attr, cidr.alen, &cidr.addr.in6);
2648 
2649                 if ((rtg->rtgen_family == AF_UNSPEC) && (spec->flags & DF_FAMILY_HINT))
2650                         rtg->rtgen_family = cidr.family;
2651 
2652                 break;
2653 
2654         case DT_MULTIPATH:
2655                 if (!uc_nl_parse_rta_multipath(spec, msg, base, vm, val))
2656                         return nla_parse_error(spec, vm, val, "invalid nexthop data");
2657 
2658                 break;
2659 
2660         case DT_NUMRANGE:
2661                 if (!uc_nl_parse_rta_numrange(spec, msg, base, vm, val))
2662                         return false;
2663 
2664                 break;
2665 
2666         case DT_FLAGS:
2667                 if (ucv_array_length(val) == 2) {
2668                         if (ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
2669                             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
2670                                 return nla_parse_error(spec, vm, val, "flag or mask value not an integer");
2671 
2672                         if (!uc_nl_parse_u32(ucv_array_get(val, 0), &u32))
2673                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
2674 
2675                         memcpy(&u64, &u32, sizeof(u32));
2676 
2677                         if (!uc_nl_parse_u32(ucv_array_get(val, 1), &u32))
2678                                 return nla_parse_error(spec, vm, val, "mask value not an integer or out of range 0-4294967295");
2679 
2680                         memcpy((char *)&u64 + sizeof(u32), &u32, sizeof(u32));
2681                 }
2682                 else if (ucv_type(val) == UC_INTEGER) {
2683                         if (!uc_nl_parse_u32(val, &u32))
2684                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
2685 
2686                         memcpy(&u64, &u32, sizeof(u32));
2687                         memset((char *)&u64 + sizeof(u32), 0xff, sizeof(u32));
2688                 }
2689                 else {
2690                         return nla_parse_error(spec, vm, val, "value neither an array of flags, mask nor an integer");
2691                 }
2692 
2693                 if (spec->attr == 0)
2694                         uc_nl_put_struct_member(base, spec->auxdata, sizeof(u64), &u64);
2695                 else
2696                         nla_put_u64(msg, attr, u64);
2697 
2698                 break;
2699 
2700         case DT_LINKINFO:
2701                 if (!uc_nl_parse_rta_linkinfo(spec, msg, base, vm, val))
2702                         return false;
2703 
2704                 break;
2705 
2706         case DT_SRH:
2707                 if (!uc_nl_parse_rta_srh(spec, msg, base, vm, val))
2708                         return false;
2709 
2710                 break;
2711 
2712         case DT_ENCAP:
2713                 if (!uc_nl_parse_rta_encap(spec, msg, base, vm, val))
2714                         return false;
2715 
2716                 break;
2717 
2718         case DT_IPOPTS:
2719                 if (!uc_nl_parse_rta_ipopts(spec, msg, base, vm, val))
2720                         return false;
2721 
2722                 break;
2723 
2724         case DT_U32_OR_MEMBER:
2725                 if (!uc_nl_parse_rta_u32_or_member(spec, msg, base, vm, val))
2726                         return false;
2727 
2728                 break;
2729 
2730         case DT_NESTED:
2731                 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val))
2732                         return false;
2733 
2734                 break;
2735 
2736         default:
2737                 assert(0);
2738         }
2739 
2740         return true;
2741 }
2742 
2743 static uc_value_t *
2744 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm)
2745 {
2746         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 };
2747         struct { uint32_t flags; uint32_t mask; } flags;
2748         char buf[sizeof(struct mpls_label) * 16];
2749         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2750         struct rtgenmsg *rtg = nlmsg_data(hdr);
2751         struct ether_addr *ea;
2752         uc_value_t *v;
2753         char *s;
2754 
2755         switch (spec->type) {
2756         case DT_U8:
2757                 if (spec->attr == 0)
2758                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2759                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
2760                         t.u8 = nla_get_u8(tb[spec->attr]);
2761 
2762                 return ucv_uint64_new(t.u8);
2763 
2764         case DT_U16:
2765                 if (spec->attr == 0)
2766                         t.u16 = uc_nl_get_struct_member_u16(base, spec->auxdata);
2767                 else if (nla_check_len(tb[spec->attr], sizeof(t.u16)))
2768                         t.u16 = nla_get_u16(tb[spec->attr]);
2769 
2770                 if (spec->flags & DF_BYTESWAP)
2771                         t.u16 = ntohs(t.u16);
2772 
2773                 return ucv_uint64_new(t.u16);
2774 
2775         case DT_U32:
2776         case DT_S32:
2777                 if (spec->attr == 0)
2778                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2779                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
2780                         t.u32 = nla_get_u32(tb[spec->attr]);
2781 
2782                 if (spec->flags & DF_BYTESWAP)
2783                         t.u32 = ntohl(t.u32);
2784 
2785                 if (spec->type == DT_S32)
2786                         return ucv_int64_new((int32_t)t.u32);
2787 
2788                 return ucv_uint64_new(t.u32);
2789 
2790         case DT_U64:
2791                 if (spec->attr == 0)
2792                         t.u64 = uc_nl_get_struct_member_u64(base, spec->auxdata);
2793                 else if (nla_check_len(tb[spec->attr], sizeof(t.u64)))
2794                         memcpy(&t.u64, nla_data(tb[spec->attr]), sizeof(t.u64));
2795 
2796                 return ucv_uint64_new(t.u64);
2797 
2798         case DT_BOOL:
2799                 if (spec->attr == 0)
2800                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2801                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
2802                         t.u8 = nla_get_u8(tb[spec->attr]);
2803 
2804                 return ucv_boolean_new(t.u8 != 0);
2805 
2806         case DT_FLAG:
2807                 if (spec->attr == 0)
2808                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2809                 else if (tb[spec->attr] != NULL)
2810                         t.u8 = 1;
2811 
2812                 return ucv_boolean_new(t.u8 != 0);
2813 
2814         case DT_STRING:
2815                 assert(spec->attr != 0);
2816 
2817                 if (!nla_check_len(tb[spec->attr], 1))
2818                         return NULL;
2819 
2820                 return ucv_string_new_length(
2821                         nla_data(tb[spec->attr]), nla_len(tb[spec->attr]) - 1);
2822 
2823         case DT_NETDEV:
2824                 if (spec->attr == 0)
2825                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2826                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
2827                         t.u32 = nla_get_u32(tb[spec->attr]);
2828 
2829                 if (if_indextoname(t.u32, buf))
2830                         return ucv_string_new(buf);
2831                 else if (spec->flags & DF_ALLOW_NONE)
2832                         return ucv_int64_new(0);
2833 
2834                 return NULL;
2835 
2836         case DT_LLADDR:
2837                 assert(spec->attr != 0);
2838 
2839                 if (!nla_check_len(tb[spec->attr], sizeof(*ea)))
2840                         return NULL;
2841 
2842                 ea = nla_data(tb[spec->attr]);
2843 
2844                 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
2845                         ea->ether_addr_octet[0], ea->ether_addr_octet[1],
2846                         ea->ether_addr_octet[2], ea->ether_addr_octet[3],
2847                         ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
2848 
2849                 return ucv_string_new(buf);
2850 
2851         case DT_U64ADDR:
2852                 assert(spec->attr != 0);
2853 
2854                 if (!nla_check_len(tb[spec->attr], sizeof(uint64_t)) ||
2855                     !addr64_ntop(nla_data(tb[spec->attr]), buf, sizeof(buf)))
2856                         return NULL;
2857 
2858                 return ucv_string_new(buf);
2859 
2860         case DT_INADDR:
2861         case DT_IN6ADDR:
2862         case DT_MPLSADDR:
2863         case DT_ANYADDR:
2864                 assert(spec->attr != 0);
2865 
2866                 t.sz = (size_t)nla_len(tb[spec->attr]);
2867 
2868                 switch (spec->type) {
2869                 case DT_INADDR:
2870                         if (t.sz < sizeof(struct in_addr) ||
2871                             !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2872                                 return NULL;
2873 
2874                         break;
2875 
2876                 case DT_IN6ADDR:
2877                         if (t.sz < sizeof(struct in6_addr) ||
2878                             !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2879                                 return NULL;
2880 
2881                         break;
2882 
2883                 case DT_MPLSADDR:
2884                         if (t.sz < sizeof(struct mpls_label) ||
2885                             !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
2886                                 return NULL;
2887 
2888                         break;
2889 
2890                 default:
2891                         switch (rtg->rtgen_family) {
2892                         case AF_MPLS:
2893                                 if (t.sz < sizeof(struct mpls_label) ||
2894                                     !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
2895                                         return NULL;
2896 
2897                                 break;
2898 
2899                         case AF_INET6:
2900                                 if (t.sz < sizeof(struct in6_addr) ||
2901                                     !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2902                                         return NULL;
2903 
2904                                 break;
2905 
2906                         case AF_INET:
2907                                 if (t.sz < sizeof(struct in_addr) ||
2908                                     !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2909                                         return NULL;
2910 
2911                                 break;
2912 
2913                         default:
2914                                 return NULL;
2915                         }
2916 
2917                         break;
2918                 }
2919 
2920                 if (spec->flags & DF_STORE_MASK) {
2921                         s = buf + strlen(buf);
2922                         snprintf(s, buf + sizeof(buf) - s, "/%hhu",
2923                                 uc_nl_get_struct_member_u8(base, spec->auxdata));
2924                 }
2925 
2926                 return ucv_string_new(buf);
2927 
2928         case DT_MULTIPATH:
2929                 return uc_nl_convert_rta_multipath(spec, msg, tb, vm);
2930 
2931         case DT_NUMRANGE:
2932                 return uc_nl_convert_rta_numrange(spec, msg, tb, vm);
2933 
2934         case DT_FLAGS:
2935                 if (spec->attr == 0)
2936                         uc_nl_get_struct_member(base, spec->auxdata, sizeof(flags), &flags);
2937                 else if (nla_check_len(tb[spec->attr], sizeof(flags)))
2938                         memcpy(&flags, nla_data(tb[spec->attr]), sizeof(flags));
2939                 else
2940                         return NULL;
2941 
2942                 if (flags.mask == 0)
2943                         return ucv_uint64_new(flags.flags);
2944 
2945                 v = ucv_array_new(vm);
2946 
2947                 ucv_array_push(v, ucv_uint64_new(flags.flags));
2948                 ucv_array_push(v, ucv_uint64_new(flags.mask));
2949 
2950                 return v;
2951 
2952         case DT_LINKINFO:
2953                 return uc_nl_convert_rta_linkinfo(spec, msg, tb, vm);
2954 
2955         case DT_BRIDGEID:
2956                 return uc_nl_convert_rta_bridgeid(spec, msg, tb, vm);
2957 
2958         case DT_SRH:
2959                 return uc_nl_convert_rta_srh(spec, msg, tb, vm);
2960 
2961         case DT_ENCAP:
2962                 return uc_nl_convert_rta_encap(spec, msg, tb, vm);
2963 
2964         case DT_IPOPTS:
2965                 return uc_nl_convert_rta_ipopts(spec, msg, tb, vm);
2966 
2967         case DT_U32_OR_MEMBER:
2968                 return uc_nl_convert_rta_u32_or_member(spec, msg, base, tb, vm);
2969 
2970         case DT_NESTED:
2971                 return uc_nl_convert_rta_nested(spec, msg, tb, vm);
2972 
2973         default:
2974                 assert(0);
2975         }
2976 
2977         return NULL;
2978 }
2979 
2980 
2981 static struct nl_sock *sock = NULL;
2982 
2983 typedef enum {
2984         STATE_UNREPLIED,
2985         STATE_CONTINUE,
2986         STATE_REPLIED,
2987         STATE_ERROR
2988 } reply_state_t;
2989 
2990 typedef struct {
2991         reply_state_t state;
2992         uc_vm_t *vm;
2993         uc_value_t *res;
2994         int family;
2995         const uc_nl_nested_spec_t *spec;
2996 } request_state_t;
2997 
2998 
2999 static uc_value_t *
3000 uc_nl_error(uc_vm_t *vm, size_t nargs)
3001 {
3002         uc_stringbuf_t *buf;
3003         const char *s;
3004 
3005         if (last_error.code == 0)
3006                 return NULL;
3007 
3008         buf = ucv_stringbuf_new();
3009 
3010         if (last_error.code == NLE_FAILURE && last_error.msg) {
3011                 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
3012         }
3013         else {
3014                 s = nl_geterror(last_error.code);
3015 
3016                 ucv_stringbuf_addstr(buf, s, strlen(s));
3017 
3018                 if (last_error.msg)
3019                         ucv_stringbuf_printf(buf, ": %s", last_error.msg);
3020         }
3021 
3022         set_error(0, NULL);
3023 
3024         return ucv_stringbuf_finish(buf);
3025 }
3026 
3027 /*
3028  * route functions
3029  */
3030 
3031 static int
3032 cb_done(struct nl_msg *msg, void *arg)
3033 {
3034         request_state_t *s = arg;
3035 
3036         s->state = STATE_REPLIED;
3037 
3038         return NL_STOP;
3039 }
3040 
3041 static int
3042 cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
3043 {
3044         request_state_t *s = arg;
3045         int errnum = err->error;
3046 
3047         set_error(NLE_FAILURE, "RTNETLINK answers: %s",
3048                   strerror(errnum < 0 ? -errnum : errnum));
3049 
3050         s->state = STATE_ERROR;
3051 
3052         return NL_STOP;
3053 }
3054 
3055 static int
3056 cb_reply(struct nl_msg *msg, void *arg)
3057 {
3058         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3059         request_state_t *s = arg;
3060         uc_value_t *o;
3061         bool rv;
3062 
3063         if (RTM_FAM(hdr->nlmsg_type) != s->family)
3064                 return NL_SKIP;
3065 
3066         if (s->spec) {
3067                 if (nlmsg_attrlen(hdr, 0) < (ssize_t)s->spec->headsize)
3068                         return NL_SKIP;
3069 
3070                 o = ucv_object_new(s->vm);
3071 
3072                 rv = uc_nl_convert_attrs(msg,
3073                         nlmsg_attrdata(hdr, 0),
3074                         nlmsg_attrlen(hdr, 0),
3075                         s->spec->headsize,
3076                         s->spec->attrs, s->spec->nattrs, s->vm, o);
3077 
3078                 if (rv) {
3079                         if (hdr->nlmsg_flags & NLM_F_MULTI) {
3080                                 if (!s->res)
3081                                         s->res = ucv_array_new(s->vm);
3082 
3083                                 ucv_array_push(s->res, o);
3084                         }
3085                         else {
3086                                 s->res = o;
3087                         }
3088                 }
3089                 else {
3090                         ucv_put(o);
3091                 }
3092         }
3093 
3094         s->state = STATE_CONTINUE;
3095 
3096         return NL_SKIP;
3097 }
3098 
3099 
3100 static const struct {
3101         int family;
3102         const uc_nl_nested_spec_t *spec;
3103 } rtm_families[] = {
3104         { RTM_FAM(RTM_GETLINK), &link_msg },
3105         { RTM_FAM(RTM_GETROUTE), &route_msg },
3106         { RTM_FAM(RTM_GETNEIGH), &neigh_msg },
3107         { RTM_FAM(RTM_GETADDR), &addr_msg },
3108         { RTM_FAM(RTM_GETRULE), &rule_msg },
3109         { RTM_FAM(RTM_GETADDRLABEL), &addrlabel_msg },
3110         { RTM_FAM(RTM_GETNEIGHTBL), &neightbl_msg },
3111         { RTM_FAM(RTM_GETNETCONF), &netconf_msg },
3112 };
3113 
3114 static uc_value_t *
3115 uc_nl_request(uc_vm_t *vm, size_t nargs)
3116 {
3117         uc_value_t *cmd = uc_fn_arg(0);
3118         uc_value_t *flags = uc_fn_arg(1);
3119         uc_value_t *payload = uc_fn_arg(2);
3120         request_state_t st = { .vm = vm };
3121         uint16_t flagval = 0;
3122         int enable = 1, err;
3123         struct nl_msg *msg;
3124         struct nl_cb *cb;
3125         void *buf;
3126         size_t i;
3127 
3128         if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 ||
3129             (flags != NULL && ucv_type(flags) != UC_INTEGER) ||
3130             (payload != NULL && ucv_type(payload) != UC_OBJECT))
3131                 err_return(NLE_INVAL, NULL);
3132 
3133         if (flags) {
3134                 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff)
3135                         err_return(NLE_INVAL, NULL);
3136                 else
3137                         flagval = (uint16_t)ucv_int64_get(flags);
3138         }
3139 
3140         for (i = 0; i < ARRAY_SIZE(rtm_families); i++) {
3141                 if (rtm_families[i].family == RTM_FAM(ucv_int64_get(cmd))) {
3142                         st.spec = rtm_families[i].spec;
3143                         st.family = rtm_families[i].family;
3144                         break;
3145                 }
3146         }
3147 
3148         if (!sock) {
3149                 sock = nl_socket_alloc();
3150 
3151                 if (!sock)
3152                         err_return(NLE_NOMEM, NULL);
3153 
3154                 err = nl_connect(sock, NETLINK_ROUTE);
3155 
3156                 if (err != 0)
3157                         err_return(err, NULL);
3158 
3159                 if (flagval & NLM_F_STRICT_CHK) {
3160                         if (setsockopt(sock->s_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &enable, sizeof(enable)) < 0)
3161                                 err_return(nl_syserr2nlerr(errno), "Unable to enable NETLINK_GET_STRICT_CHK");
3162 
3163                         flagval &= ~NLM_F_STRICT_CHK;
3164                 }
3165         }
3166 
3167         msg = nlmsg_alloc_simple(ucv_int64_get(cmd), NLM_F_REQUEST | flagval);
3168 
3169         if (!msg)
3170                 err_return(NLE_NOMEM, NULL);
3171 
3172         if (st.spec) {
3173                 if (st.spec->headsize) {
3174                         buf = nlmsg_reserve(msg, st.spec->headsize, 0);
3175 
3176                         if (!buf) {
3177                                 nlmsg_free(msg);
3178 
3179                                 return NULL;
3180                         }
3181 
3182                         memset(buf, 0, st.spec->headsize);
3183                 }
3184 
3185                 if (!uc_nl_parse_attrs(msg, NLMSG_DATA(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) {
3186                         nlmsg_free(msg);
3187 
3188                         return NULL;
3189                 }
3190         }
3191 
3192         cb = nl_cb_alloc(NL_CB_DEFAULT);
3193 
3194         if (!cb) {
3195                 nlmsg_free(msg);
3196                 err_return(NLE_NOMEM, NULL);
3197         }
3198 
3199         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st);
3200         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
3201         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_done, &st);
3202         nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
3203 
3204         nl_send_auto_complete(sock, msg);
3205 
3206         do {
3207                 err = nl_recvmsgs(sock, cb);
3208 
3209                 if (err && st.state != STATE_ERROR) {
3210                         set_error(err, NULL);
3211 
3212                         st.state = STATE_ERROR;
3213                 }
3214         }
3215         while (st.state < STATE_REPLIED);
3216 
3217         nlmsg_free(msg);
3218         nl_cb_put(cb);
3219 
3220         switch (st.state) {
3221         case STATE_REPLIED:
3222                 return st.res;
3223 
3224         case STATE_UNREPLIED:
3225                 return ucv_boolean_new(true);
3226 
3227         case STATE_ERROR:
3228                 return ucv_boolean_new(false);
3229 
3230         default:
3231                 set_error(NLE_FAILURE, "Interrupted reply");
3232 
3233                 return ucv_boolean_new(false);
3234         }
3235 }
3236 
3237 
3238 static void
3239 register_constants(uc_vm_t *vm, uc_value_t *scope)
3240 {
3241         uc_value_t *c = ucv_object_new(vm);
3242 
3243 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x))
3244 
3245         ADD_CONST(NLM_F_ACK);
3246         ADD_CONST(NLM_F_ACK_TLVS);
3247         ADD_CONST(NLM_F_APPEND);
3248         ADD_CONST(NLM_F_ATOMIC);
3249         ADD_CONST(NLM_F_CAPPED);
3250         ADD_CONST(NLM_F_CREATE);
3251         ADD_CONST(NLM_F_DUMP);
3252         ADD_CONST(NLM_F_DUMP_FILTERED);
3253         ADD_CONST(NLM_F_DUMP_INTR);
3254         ADD_CONST(NLM_F_ECHO);
3255         ADD_CONST(NLM_F_EXCL);
3256         ADD_CONST(NLM_F_MATCH);
3257         ADD_CONST(NLM_F_MULTI);
3258         ADD_CONST(NLM_F_NONREC);
3259         ADD_CONST(NLM_F_REPLACE);
3260         ADD_CONST(NLM_F_REQUEST);
3261         ADD_CONST(NLM_F_ROOT);
3262         ADD_CONST(NLM_F_STRICT_CHK); /* custom */
3263 
3264         ADD_CONST(IN6_ADDR_GEN_MODE_EUI64);
3265         ADD_CONST(IN6_ADDR_GEN_MODE_NONE);
3266         ADD_CONST(IN6_ADDR_GEN_MODE_STABLE_PRIVACY);
3267         ADD_CONST(IN6_ADDR_GEN_MODE_RANDOM);
3268 
3269         ADD_CONST(BRIDGE_MODE_UNSPEC);
3270         ADD_CONST(BRIDGE_MODE_HAIRPIN);
3271 
3272         ADD_CONST(MACVLAN_MODE_PRIVATE);
3273         ADD_CONST(MACVLAN_MODE_VEPA);
3274         ADD_CONST(MACVLAN_MODE_BRIDGE);
3275         ADD_CONST(MACVLAN_MODE_PASSTHRU);
3276         ADD_CONST(MACVLAN_MODE_SOURCE);
3277 
3278         ADD_CONST(MACVLAN_MACADDR_ADD);
3279         ADD_CONST(MACVLAN_MACADDR_DEL);
3280         ADD_CONST(MACVLAN_MACADDR_FLUSH);
3281         ADD_CONST(MACVLAN_MACADDR_SET);
3282 
3283         ADD_CONST(MACSEC_VALIDATE_DISABLED);
3284         ADD_CONST(MACSEC_VALIDATE_CHECK);
3285         ADD_CONST(MACSEC_VALIDATE_STRICT);
3286         ADD_CONST(MACSEC_VALIDATE_MAX);
3287 
3288         ADD_CONST(MACSEC_OFFLOAD_OFF);
3289         ADD_CONST(MACSEC_OFFLOAD_PHY);
3290         ADD_CONST(MACSEC_OFFLOAD_MAC);
3291         ADD_CONST(MACSEC_OFFLOAD_MAX);
3292 
3293         ADD_CONST(IPVLAN_MODE_L2);
3294         ADD_CONST(IPVLAN_MODE_L3);
3295         ADD_CONST(IPVLAN_MODE_L3S);
3296 
3297         ADD_CONST(VXLAN_DF_UNSET);
3298         ADD_CONST(VXLAN_DF_SET);
3299         ADD_CONST(VXLAN_DF_INHERIT);
3300         ADD_CONST(VXLAN_DF_MAX);
3301 
3302         ADD_CONST(GENEVE_DF_UNSET);
3303         ADD_CONST(GENEVE_DF_SET);
3304         ADD_CONST(GENEVE_DF_INHERIT);
3305         ADD_CONST(GENEVE_DF_MAX);
3306 
3307         ADD_CONST(GTP_ROLE_GGSN);
3308         ADD_CONST(GTP_ROLE_SGSN);
3309 
3310         ADD_CONST(PORT_REQUEST_PREASSOCIATE);
3311         ADD_CONST(PORT_REQUEST_PREASSOCIATE_RR);
3312         ADD_CONST(PORT_REQUEST_ASSOCIATE);
3313         ADD_CONST(PORT_REQUEST_DISASSOCIATE);
3314 
3315         ADD_CONST(PORT_VDP_RESPONSE_SUCCESS);
3316         ADD_CONST(PORT_VDP_RESPONSE_INVALID_FORMAT);
3317         ADD_CONST(PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES);
3318         ADD_CONST(PORT_VDP_RESPONSE_UNUSED_VTID);
3319         ADD_CONST(PORT_VDP_RESPONSE_VTID_VIOLATION);
3320         ADD_CONST(PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION);
3321         ADD_CONST(PORT_VDP_RESPONSE_OUT_OF_SYNC);
3322         ADD_CONST(PORT_PROFILE_RESPONSE_SUCCESS);
3323         ADD_CONST(PORT_PROFILE_RESPONSE_INPROGRESS);
3324         ADD_CONST(PORT_PROFILE_RESPONSE_INVALID);
3325         ADD_CONST(PORT_PROFILE_RESPONSE_BADSTATE);
3326         ADD_CONST(PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES);
3327         ADD_CONST(PORT_PROFILE_RESPONSE_ERROR);
3328 
3329         ADD_CONST(IPOIB_MODE_DATAGRAM);
3330         ADD_CONST(IPOIB_MODE_CONNECTED);
3331 
3332         ADD_CONST(HSR_PROTOCOL_HSR);
3333         ADD_CONST(HSR_PROTOCOL_PRP);
3334 
3335         ADD_CONST(LINK_XSTATS_TYPE_UNSPEC);
3336         ADD_CONST(LINK_XSTATS_TYPE_BRIDGE);
3337         ADD_CONST(LINK_XSTATS_TYPE_BOND);
3338 
3339         ADD_CONST(XDP_ATTACHED_NONE);
3340         ADD_CONST(XDP_ATTACHED_DRV);
3341         ADD_CONST(XDP_ATTACHED_SKB);
3342         ADD_CONST(XDP_ATTACHED_HW);
3343         ADD_CONST(XDP_ATTACHED_MULTI);
3344 
3345         ADD_CONST(FDB_NOTIFY_BIT);
3346         ADD_CONST(FDB_NOTIFY_INACTIVE_BIT);
3347 
3348         ADD_CONST(RTM_BASE);
3349         ADD_CONST(RTM_NEWLINK);
3350         ADD_CONST(RTM_DELLINK);
3351         ADD_CONST(RTM_GETLINK);
3352         ADD_CONST(RTM_SETLINK);
3353         ADD_CONST(RTM_NEWADDR);
3354         ADD_CONST(RTM_DELADDR);
3355         ADD_CONST(RTM_GETADDR);
3356         ADD_CONST(RTM_NEWROUTE);
3357         ADD_CONST(RTM_DELROUTE);
3358         ADD_CONST(RTM_GETROUTE);
3359         ADD_CONST(RTM_NEWNEIGH);
3360         ADD_CONST(RTM_DELNEIGH);
3361         ADD_CONST(RTM_GETNEIGH);
3362         ADD_CONST(RTM_NEWRULE);
3363         ADD_CONST(RTM_DELRULE);
3364         ADD_CONST(RTM_GETRULE);
3365         ADD_CONST(RTM_NEWQDISC);
3366         ADD_CONST(RTM_DELQDISC);
3367         ADD_CONST(RTM_GETQDISC);
3368         ADD_CONST(RTM_NEWTCLASS);
3369         ADD_CONST(RTM_DELTCLASS);
3370         ADD_CONST(RTM_GETTCLASS);
3371         ADD_CONST(RTM_NEWTFILTER);
3372         ADD_CONST(RTM_DELTFILTER);
3373         ADD_CONST(RTM_GETTFILTER);
3374         ADD_CONST(RTM_NEWACTION);
3375         ADD_CONST(RTM_DELACTION);
3376         ADD_CONST(RTM_GETACTION);
3377         ADD_CONST(RTM_NEWPREFIX);
3378         ADD_CONST(RTM_GETMULTICAST);
3379         ADD_CONST(RTM_GETANYCAST);
3380         ADD_CONST(RTM_NEWNEIGHTBL);
3381         ADD_CONST(RTM_GETNEIGHTBL);
3382         ADD_CONST(RTM_SETNEIGHTBL);
3383         ADD_CONST(RTM_NEWNDUSEROPT);
3384         ADD_CONST(RTM_NEWADDRLABEL);
3385         ADD_CONST(RTM_DELADDRLABEL);
3386         ADD_CONST(RTM_GETADDRLABEL);
3387         ADD_CONST(RTM_GETDCB);
3388         ADD_CONST(RTM_SETDCB);
3389         ADD_CONST(RTM_NEWNETCONF);
3390         ADD_CONST(RTM_DELNETCONF);
3391         ADD_CONST(RTM_GETNETCONF);
3392         ADD_CONST(RTM_NEWMDB);
3393         ADD_CONST(RTM_DELMDB);
3394         ADD_CONST(RTM_GETMDB);
3395         ADD_CONST(RTM_NEWNSID);
3396         ADD_CONST(RTM_DELNSID);
3397         ADD_CONST(RTM_GETNSID);
3398         ADD_CONST(RTM_NEWSTATS);
3399         ADD_CONST(RTM_GETSTATS);
3400         ADD_CONST(RTM_NEWCACHEREPORT);
3401         ADD_CONST(RTM_NEWCHAIN);
3402         ADD_CONST(RTM_DELCHAIN);
3403         ADD_CONST(RTM_GETCHAIN);
3404         ADD_CONST(RTM_NEWNEXTHOP);
3405         ADD_CONST(RTM_DELNEXTHOP);
3406         ADD_CONST(RTM_GETNEXTHOP);
3407         ADD_CONST(RTM_NEWLINKPROP);
3408         ADD_CONST(RTM_DELLINKPROP);
3409         ADD_CONST(RTM_GETLINKPROP);
3410         ADD_CONST(RTM_NEWVLAN);
3411         ADD_CONST(RTM_DELVLAN);
3412         ADD_CONST(RTM_GETVLAN);
3413 
3414         ADD_CONST(RTN_UNSPEC);
3415         ADD_CONST(RTN_UNICAST);
3416         ADD_CONST(RTN_LOCAL);
3417         ADD_CONST(RTN_BROADCAST);
3418         ADD_CONST(RTN_ANYCAST);
3419         ADD_CONST(RTN_MULTICAST);
3420         ADD_CONST(RTN_BLACKHOLE);
3421         ADD_CONST(RTN_UNREACHABLE);
3422         ADD_CONST(RTN_PROHIBIT);
3423         ADD_CONST(RTN_THROW);
3424         ADD_CONST(RTN_NAT);
3425         ADD_CONST(RTN_XRESOLVE);
3426 
3427         ADD_CONST(RT_SCOPE_UNIVERSE);
3428         ADD_CONST(RT_SCOPE_SITE);
3429         ADD_CONST(RT_SCOPE_LINK);
3430         ADD_CONST(RT_SCOPE_HOST);
3431         ADD_CONST(RT_SCOPE_NOWHERE);
3432 
3433         ADD_CONST(RT_TABLE_UNSPEC);
3434         ADD_CONST(RT_TABLE_COMPAT);
3435         ADD_CONST(RT_TABLE_DEFAULT);
3436         ADD_CONST(RT_TABLE_MAIN);
3437         ADD_CONST(RT_TABLE_LOCAL);
3438         ADD_CONST(RT_TABLE_MAX);
3439 
3440         /* required to construct RTAX_LOCK */
3441         ADD_CONST(RTAX_MTU);
3442         ADD_CONST(RTAX_HOPLIMIT);
3443         ADD_CONST(RTAX_ADVMSS);
3444         ADD_CONST(RTAX_REORDERING);
3445         ADD_CONST(RTAX_RTT);
3446         ADD_CONST(RTAX_WINDOW);
3447         ADD_CONST(RTAX_CWND);
3448         ADD_CONST(RTAX_INITCWND);
3449         ADD_CONST(RTAX_INITRWND);
3450         ADD_CONST(RTAX_FEATURES);
3451         ADD_CONST(RTAX_QUICKACK);
3452         ADD_CONST(RTAX_CC_ALGO);
3453         ADD_CONST(RTAX_RTTVAR);
3454         ADD_CONST(RTAX_SSTHRESH);
3455         ADD_CONST(RTAX_FASTOPEN_NO_COOKIE);
3456 
3457         ADD_CONST(PREFIX_UNSPEC);
3458         ADD_CONST(PREFIX_ADDRESS);
3459         ADD_CONST(PREFIX_CACHEINFO);
3460 
3461         ADD_CONST(NDUSEROPT_UNSPEC);
3462         ADD_CONST(NDUSEROPT_SRCADDR);
3463 
3464         ADD_CONST(RTNLGRP_NONE);
3465         ADD_CONST(RTNLGRP_LINK);
3466         ADD_CONST(RTNLGRP_NOTIFY);
3467         ADD_CONST(RTNLGRP_NEIGH);
3468         ADD_CONST(RTNLGRP_TC);
3469         ADD_CONST(RTNLGRP_IPV4_IFADDR);
3470         ADD_CONST(RTNLGRP_IPV4_MROUTE);
3471         ADD_CONST(RTNLGRP_IPV4_ROUTE);
3472         ADD_CONST(RTNLGRP_IPV4_RULE);
3473         ADD_CONST(RTNLGRP_IPV6_IFADDR);
3474         ADD_CONST(RTNLGRP_IPV6_MROUTE);
3475         ADD_CONST(RTNLGRP_IPV6_ROUTE);
3476         ADD_CONST(RTNLGRP_IPV6_IFINFO);
3477         ADD_CONST(RTNLGRP_DECnet_IFADDR);
3478         ADD_CONST(RTNLGRP_NOP2);
3479         ADD_CONST(RTNLGRP_DECnet_ROUTE);
3480         ADD_CONST(RTNLGRP_DECnet_RULE);
3481         ADD_CONST(RTNLGRP_NOP4);
3482         ADD_CONST(RTNLGRP_IPV6_PREFIX);
3483         ADD_CONST(RTNLGRP_IPV6_RULE);
3484         ADD_CONST(RTNLGRP_ND_USEROPT);
3485         ADD_CONST(RTNLGRP_PHONET_IFADDR);
3486         ADD_CONST(RTNLGRP_PHONET_ROUTE);
3487         ADD_CONST(RTNLGRP_DCB);
3488         ADD_CONST(RTNLGRP_IPV4_NETCONF);
3489         ADD_CONST(RTNLGRP_IPV6_NETCONF);
3490         ADD_CONST(RTNLGRP_MDB);
3491         ADD_CONST(RTNLGRP_MPLS_ROUTE);
3492         ADD_CONST(RTNLGRP_NSID);
3493         ADD_CONST(RTNLGRP_MPLS_NETCONF);
3494         ADD_CONST(RTNLGRP_IPV4_MROUTE_R);
3495         ADD_CONST(RTNLGRP_IPV6_MROUTE_R);
3496         ADD_CONST(RTNLGRP_NEXTHOP);
3497         ADD_CONST(RTNLGRP_BRVLAN);
3498 
3499         ADD_CONST(RTM_F_CLONED);
3500         ADD_CONST(RTM_F_EQUALIZE);
3501         ADD_CONST(RTM_F_FIB_MATCH);
3502         ADD_CONST(RTM_F_LOOKUP_TABLE);
3503         ADD_CONST(RTM_F_NOTIFY);
3504         ADD_CONST(RTM_F_PREFIX);
3505 
3506         ADD_CONST(AF_UNSPEC);
3507         ADD_CONST(AF_INET);
3508         ADD_CONST(AF_INET6);
3509         ADD_CONST(AF_MPLS);
3510         ADD_CONST(AF_BRIDGE);
3511 
3512         ADD_CONST(GRE_CSUM);
3513         ADD_CONST(GRE_ROUTING);
3514         ADD_CONST(GRE_KEY);
3515         ADD_CONST(GRE_SEQ);
3516         ADD_CONST(GRE_STRICT);
3517         ADD_CONST(GRE_REC);
3518         ADD_CONST(GRE_ACK);
3519 
3520         ADD_CONST(TUNNEL_ENCAP_NONE);
3521         ADD_CONST(TUNNEL_ENCAP_FOU);
3522         ADD_CONST(TUNNEL_ENCAP_GUE);
3523         ADD_CONST(TUNNEL_ENCAP_MPLS);
3524 
3525         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM);
3526         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM6);
3527         ADD_CONST(TUNNEL_ENCAP_FLAG_REMCSUM);
3528 
3529         ADD_CONST(IP6_TNL_F_ALLOW_LOCAL_REMOTE);
3530         ADD_CONST(IP6_TNL_F_IGN_ENCAP_LIMIT);
3531         ADD_CONST(IP6_TNL_F_MIP6_DEV);
3532         ADD_CONST(IP6_TNL_F_RCV_DSCP_COPY);
3533         ADD_CONST(IP6_TNL_F_USE_ORIG_FLOWLABEL);
3534         ADD_CONST(IP6_TNL_F_USE_ORIG_FWMARK);
3535         ADD_CONST(IP6_TNL_F_USE_ORIG_TCLASS);
3536 
3537         ADD_CONST(NTF_EXT_LEARNED);
3538         ADD_CONST(NTF_MASTER);
3539         ADD_CONST(NTF_OFFLOADED);
3540         ADD_CONST(NTF_PROXY);
3541         ADD_CONST(NTF_ROUTER);
3542         ADD_CONST(NTF_SELF);
3543         ADD_CONST(NTF_STICKY);
3544         ADD_CONST(NTF_USE);
3545 
3546         ADD_CONST(NUD_DELAY);
3547         ADD_CONST(NUD_FAILED);
3548         ADD_CONST(NUD_INCOMPLETE);
3549         ADD_CONST(NUD_NOARP);
3550         ADD_CONST(NUD_NONE);
3551         ADD_CONST(NUD_PERMANENT);
3552         ADD_CONST(NUD_PROBE);
3553         ADD_CONST(NUD_REACHABLE);
3554         ADD_CONST(NUD_STALE);
3555 
3556         ADD_CONST(IFA_F_DADFAILED);
3557         ADD_CONST(IFA_F_DEPRECATED);
3558         ADD_CONST(IFA_F_HOMEADDRESS);
3559         ADD_CONST(IFA_F_MANAGETEMPADDR);
3560         ADD_CONST(IFA_F_MCAUTOJOIN);
3561         ADD_CONST(IFA_F_NODAD);
3562         ADD_CONST(IFA_F_NOPREFIXROUTE);
3563         ADD_CONST(IFA_F_OPTIMISTIC);
3564         ADD_CONST(IFA_F_PERMANENT);
3565         ADD_CONST(IFA_F_SECONDARY);
3566         ADD_CONST(IFA_F_STABLE_PRIVACY);
3567         ADD_CONST(IFA_F_TEMPORARY);
3568         ADD_CONST(IFA_F_TENTATIVE);
3569 
3570         ADD_CONST(FIB_RULE_PERMANENT);
3571         ADD_CONST(FIB_RULE_INVERT);
3572         ADD_CONST(FIB_RULE_UNRESOLVED);
3573         ADD_CONST(FIB_RULE_IIF_DETACHED);
3574         ADD_CONST(FIB_RULE_DEV_DETACHED);
3575         ADD_CONST(FIB_RULE_OIF_DETACHED);
3576 
3577         ADD_CONST(FR_ACT_TO_TBL);
3578         ADD_CONST(FR_ACT_GOTO);
3579         ADD_CONST(FR_ACT_NOP);
3580         ADD_CONST(FR_ACT_BLACKHOLE);
3581         ADD_CONST(FR_ACT_UNREACHABLE);
3582         ADD_CONST(FR_ACT_PROHIBIT);
3583 
3584         ADD_CONST(NETCONFA_IFINDEX_ALL);
3585         ADD_CONST(NETCONFA_IFINDEX_DEFAULT);
3586 
3587         ADD_CONST(BRIDGE_VLAN_INFO_MASTER);
3588         ADD_CONST(BRIDGE_VLAN_INFO_PVID);
3589         ADD_CONST(BRIDGE_VLAN_INFO_UNTAGGED);
3590         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_BEGIN);
3591         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_END);
3592         ADD_CONST(BRIDGE_VLAN_INFO_BRENTRY);
3593 
3594         ucv_object_add(scope, "const", c);
3595 };
3596 
3597 static const uc_function_list_t global_fns[] = {
3598         { "error",              uc_nl_error },
3599         { "request",    uc_nl_request },
3600 };
3601 
3602 
3603 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
3604 {
3605         uc_function_list_register(scope, global_fns);
3606 
3607         register_constants(vm, scope);
3608 }
3609 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt