• 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                 nla_parse(tb, maxattr, buf + headsize, buflen - headsize, NULL);
1350         else
1351                 structlen = buflen;
1352 
1353         for (i = 0; i < nattrs; i++) {
1354                 if (attrs[i].attr == 0 && (uintptr_t)attrs[i].auxdata >= structlen)
1355                         continue;
1356 
1357                 if (attrs[i].attr != 0 && !tb[attrs[i].attr])
1358                         continue;
1359 
1360                 if (attrs[i].flags & DF_NO_GET)
1361                         continue;
1362 
1363                 if (attrs[i].flags & DF_MULTIPLE) {
1364                         /* can't happen, but needed to nudge clang-analyzer */
1365                         if (!tb[attrs[i].attr])
1366                                 continue;
1367 
1368                         arr = ucv_array_new(vm);
1369                         nla_nest = tb[attrs[i].attr];
1370 
1371                         nla_for_each_attr(nla, nla_data(nla_nest), nla_len(nla_nest), rem) {
1372                                 if (attrs[i].auxdata && nla_type(nla) != (intptr_t)attrs[i].auxdata)
1373                                         continue;
1374 
1375                                 tb[attrs[i].attr] = nla;
1376 
1377                                 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm);
1378 
1379                                 if (!v)
1380                                         continue;
1381 
1382                                 ucv_array_push(arr, v);
1383                         }
1384 
1385                         if (!ucv_array_length(arr)) {
1386                                 ucv_put(arr);
1387 
1388                                 continue;
1389                         }
1390 
1391                         v = arr;
1392                 }
1393                 else {
1394                         v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm);
1395 
1396                         if (!v)
1397                                 continue;
1398                 }
1399 
1400                 ucv_object_add(obj, attrs[i].key, v);
1401         }
1402 
1403         free(tb);
1404 
1405         return true;
1406 }
1407 
1408 static bool
1409 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)
1410 {
1411         struct nlattr *nla_nest = NULL;
1412         size_t i, j, idx;
1413         uc_value_t *v;
1414         bool exists;
1415 
1416         for (i = 0; i < nattrs; i++) {
1417                 v = ucv_object_get(obj, attrs[i].key, &exists);
1418 
1419                 if (!exists)
1420                         continue;
1421 
1422                 if (attrs[i].flags & DF_MULTIPLE) {
1423                         if (!(attrs[i].flags & DF_FLAT))
1424                                 nla_nest = nla_nest_start(msg, attrs[i].attr);
1425 
1426                         if (ucv_type(v) == UC_ARRAY) {
1427                                 for (j = 0; j < ucv_array_length(v); j++) {
1428                                         if (attrs[i].flags & DF_FLAT)
1429                                                 idx = attrs[i].attr;
1430                                         else if (attrs[i].auxdata)
1431                                                 idx = (uintptr_t)attrs[i].auxdata;
1432                                         else
1433                                                 idx = j;
1434 
1435                                         if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, ucv_array_get(v, j), idx))
1436                                                 return false;
1437                                 }
1438                         }
1439                         else {
1440                                 if (attrs[i].flags & DF_FLAT)
1441                                         idx = attrs[i].attr;
1442                                 else if (attrs[i].auxdata)
1443                                         idx = (uintptr_t)attrs[i].auxdata;
1444                                 else
1445                                         idx = 0;
1446 
1447                                 if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, idx))
1448                                         return false;
1449                         }
1450 
1451                         if (nla_nest)
1452                                 nla_nest_end(msg, nla_nest);
1453                 }
1454                 else if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, 0)) {
1455                         return false;
1456                 }
1457         }
1458 
1459         return true;
1460 }
1461 
1462 static bool
1463 uc_nl_parse_rta_nexthop(struct nl_msg *msg, uc_vm_t *vm, uc_value_t *val)
1464 {
1465         struct { uint16_t family; char addr[sizeof(struct in6_addr)]; } via;
1466         struct nlmsghdr *hdr = nlmsg_hdr(msg);
1467         struct rtmsg *rtm = NLMSG_DATA(hdr);
1468         struct nlattr *rta_gateway;
1469         struct rtnexthop *rtnh;
1470         uc_nl_cidr_t cidr = { 0 };
1471         uc_value_t *v;
1472         uint32_t u;
1473         int aflen;
1474         char *s;
1475 
1476         if (ucv_type(val) != UC_OBJECT)
1477                 return false;
1478 
1479         if (uc_nl_parse_cidr(vm, ucv_object_get(val, "via", NULL), &cidr))
1480                 return false;
1481 
1482         aflen = (cidr.family == AF_INET6 ? sizeof(cidr.addr.in6) : sizeof(cidr.addr.in));
1483 
1484         if (cidr.mask != (aflen * 8))
1485                 return false;
1486 
1487         rta_gateway = nla_reserve(msg, RTA_GATEWAY, sizeof(*rtnh));
1488 
1489         rtnh = nla_data(rta_gateway);
1490         rtnh->rtnh_len = sizeof(*rtnh);
1491 
1492         if (rtm->rtm_family == AF_UNSPEC)
1493                 rtm->rtm_family = cidr.family;
1494 
1495         if (cidr.family == rtm->rtm_family) {
1496                 nla_put(msg, RTA_GATEWAY, aflen, &cidr.addr.in6);
1497                 rtnh->rtnh_len += nla_total_size(aflen);
1498         }
1499         else {
1500                 via.family = cidr.family;
1501                 memcpy(via.addr, &cidr.addr.in6, aflen);
1502                 nla_put(msg, RTA_VIA, sizeof(via.family) + aflen, &via);
1503                 rtnh->rtnh_len += nla_total_size(sizeof(via.family) + aflen);
1504         }
1505 
1506         v = ucv_object_get(val, "dev", NULL);
1507         s = ucv_string_get(v);
1508 
1509         if (s) {
1510                 rtnh->rtnh_ifindex = if_nametoindex(s);
1511 
1512                 if (rtnh->rtnh_ifindex == 0)
1513                         return false;
1514         }
1515 
1516         v = ucv_object_get(val, "weight", NULL);
1517 
1518         if (v) {
1519                 if (!uc_nl_parse_u32(v, &u) || u == 0 || u > 256)
1520                         return false;
1521 
1522                 rtnh->rtnh_hops = u - 1;
1523         }
1524 
1525         if (ucv_is_truish(ucv_object_get(val, "onlink", NULL)))
1526                 rtnh->rtnh_flags |= RTNH_F_ONLINK;
1527 
1528         v = ucv_object_get(val, "realm", NULL);
1529 
1530         if (v) {
1531                 if (!uc_nl_parse_u32(v, &u))
1532                         return false;
1533 
1534                 nla_put_u32(msg, RTA_FLOW, u);
1535                 rtnh->rtnh_len += nla_total_size(sizeof(uint32_t));
1536         }
1537 
1538         v = ucv_object_get(val, "as", NULL);
1539 
1540         if (v) {
1541                 if (!uc_nl_parse_cidr(vm, v, &cidr) || cidr.family != rtm->rtm_family)
1542                         return false;
1543 
1544                 if (cidr.mask != cidr.bitlen)
1545                         return false;
1546 
1547                 nla_put(msg, RTA_NEWDST, cidr.alen, &cidr.addr.in6);
1548                 rtnh->rtnh_len += nla_total_size(cidr.alen);
1549         }
1550 
1551         /* XXX: nla_nest_end(rta_gateway) ? */
1552 
1553         return true;
1554 }
1555 
1556 static bool
1557 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)
1558 {
1559         struct nlattr *rta_multipath = nla_nest_start(msg, spec->attr);
1560         size_t i;
1561 
1562         for (i = 0; i < ucv_array_length(val); i++)
1563                 if (!uc_nl_parse_rta_nexthop(msg, vm, ucv_array_get(val, i)))
1564                         return false;
1565 
1566         nla_nest_end(msg, rta_multipath);
1567 
1568         return true;
1569 }
1570 
1571 static uc_value_t *
1572 uc_nl_convert_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm);
1573 
1574 static uc_value_t *
1575 uc_nl_convert_rta_multipath(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1576 {
1577         uc_nl_attr_spec_t encap_spec = { .attr = RTA_ENCAP };
1578         struct rtnexthop *nh = nla_data(tb[spec->attr]);
1579         struct nlattr *multipath_tb[RTA_MAX + 1];
1580         size_t len = nla_len(tb[spec->attr]);
1581         uc_value_t *nh_obj, *nh_arr;
1582         char buf[INET6_ADDRSTRLEN];
1583         struct rtvia *via;
1584         int af;
1585 
1586         nh_arr = ucv_array_new(vm);
1587 
1588         while (len >= sizeof(*nh)) {
1589                 if ((size_t)NLA_ALIGN(nh->rtnh_len) > len)
1590                         break;
1591 
1592                 nh_obj = ucv_object_new(vm);
1593                 ucv_array_push(nh_arr, nh_obj);
1594 
1595                 nla_parse(multipath_tb, RTA_MAX + 1, (struct nlattr *)RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh), NULL);
1596 
1597                 if (multipath_tb[RTA_GATEWAY]) {
1598                         switch (nla_len(multipath_tb[RTA_GATEWAY])) {
1599                         case 4: af = AF_INET; break;
1600                         case 16: af = AF_INET6; break;
1601                         default: af = AF_UNSPEC; break;
1602                         }
1603 
1604                         if (inet_ntop(af, nla_data(multipath_tb[RTA_GATEWAY]), buf, sizeof(buf)))
1605                                 ucv_object_add(nh_obj, "via", ucv_string_new(buf));
1606                 }
1607 
1608                 if (multipath_tb[RTA_VIA]) {
1609                         if (nla_len(multipath_tb[RTA_VIA]) > (ssize_t)sizeof(*via)) {
1610                                 via = nla_data(multipath_tb[RTA_VIA]);
1611                                 af = via->rtvia_family;
1612 
1613                                 if ((af == AF_INET &&
1614                                      nla_len(multipath_tb[RTA_VIA]) == sizeof(*via) + sizeof(struct in_addr)) ||
1615                                         (af == AF_INET6 &&
1616                                      nla_len(multipath_tb[RTA_VIA]) == sizeof(*via) + sizeof(struct in6_addr))) {
1617                                         if (inet_ntop(af, via->rtvia_addr, buf, sizeof(buf)))
1618                                                 ucv_object_add(nh_obj, "via", ucv_string_new(buf));
1619                                 }
1620                         }
1621                 }
1622 
1623                 if (if_indextoname(nh->rtnh_ifindex, buf))
1624                         ucv_object_add(nh_obj, "dev", ucv_string_new(buf));
1625 
1626                 ucv_object_add(nh_obj, "weight", ucv_int64_new(nh->rtnh_hops + 1));
1627                 ucv_object_add(nh_obj, "onlink", ucv_boolean_new(nh->rtnh_flags & RTNH_F_ONLINK));
1628 
1629                 if (multipath_tb[RTA_FLOW] && nla_len(multipath_tb[RTA_FLOW]) == sizeof(uint32_t))
1630                         ucv_object_add(nh_obj, "realm", ucv_int64_new(nla_get_u32(multipath_tb[RTA_FLOW])));
1631 
1632                 if (multipath_tb[RTA_ENCAP])
1633                         ucv_object_add(nh_obj, "encap",
1634                                 uc_nl_convert_rta_encap(&encap_spec, msg, multipath_tb, vm));
1635 
1636                 if (multipath_tb[RTA_NEWDST]) {
1637                         switch (nla_len(multipath_tb[RTA_NEWDST])) {
1638                         case 4: af = AF_INET; break;
1639                         case 16: af = AF_INET6; break;
1640                         default: af = AF_UNSPEC; break;
1641                         }
1642 
1643                         if (inet_ntop(af, nla_data(multipath_tb[RTA_NEWDST]), buf, sizeof(buf)))
1644                                 ucv_object_add(nh_obj, "as", ucv_string_new(buf));
1645                 }
1646 
1647                 len -= NLA_ALIGN(nh->rtnh_len);
1648                 nh = RTNH_NEXT(nh);
1649         }
1650 
1651         return nh_arr;
1652 }
1653 
1654 static bool
1655 parse_num(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *val, void *dst)
1656 {
1657         int64_t n = ucv_int64_get(val);
1658         uint32_t *u32;
1659         uint16_t *u16;
1660         uint8_t *u8;
1661 
1662         if (spec->flags & DF_MAX_255) {
1663                 if (n < 0 || n > 255)
1664                         return nla_parse_error(spec, vm, val, "number out of range 0-255");
1665 
1666                 u8 = dst; *u8 = n;
1667         }
1668         else if (spec->flags & DF_MAX_65535) {
1669                 if (n < 0 || n > 65535)
1670                         return nla_parse_error(spec, vm, val, "number out of range 0-65535");
1671 
1672                 u16 = dst; *u16 = n;
1673 
1674                 if (spec->flags & DF_BYTESWAP)
1675                         *u16 = htons(*u16);
1676         }
1677         else if (spec->flags & DF_MAX_16777215) {
1678                 if (n < 0 || n > 16777215)
1679                         return nla_parse_error(spec, vm, val, "number out of range 0-16777215");
1680 
1681                 u32 = dst; *u32 = n;
1682 
1683                 if (spec->flags & DF_BYTESWAP)
1684                         *u32 = htonl(*u32);
1685         }
1686         else {
1687                 if (n < 0 || n > 4294967295)
1688                         return nla_parse_error(spec, vm, val, "number out of range 0-4294967295");
1689 
1690                 u32 = dst; *u32 = n;
1691 
1692                 if (spec->flags & DF_BYTESWAP)
1693                         *u32 = htonl(*u32);
1694         }
1695 
1696         return true;
1697 }
1698 
1699 static bool
1700 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)
1701 {
1702         union {
1703                 struct { uint8_t low; uint8_t high; } u8;
1704                 struct { uint16_t low; uint16_t high; } u16;
1705                 struct { uint32_t low; uint32_t high; } u32;
1706         } ranges = { 0 };
1707 
1708         void *d1, *d2;
1709         size_t len;
1710 
1711         if (ucv_array_length(val) != 2 ||
1712             ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
1713             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
1714                 return nla_parse_error(spec, vm, val, "not a two-element array of numbers");
1715 
1716         if (spec->flags & DF_MAX_255) {
1717                 len = sizeof(ranges.u8);
1718                 d1 = &ranges.u8.low;
1719                 d2 = &ranges.u8.high;
1720         }
1721         else if (spec->flags & DF_MAX_65535) {
1722                 len = sizeof(ranges.u16);
1723                 d1 = &ranges.u16.low;
1724                 d2 = &ranges.u16.high;
1725         }
1726         else {
1727                 len = sizeof(ranges.u32);
1728                 d1 = &ranges.u32.low;
1729                 d2 = &ranges.u32.high;
1730         }
1731 
1732         if (!parse_num(spec, vm, ucv_array_get(val, 0), d1) ||
1733             !parse_num(spec, vm, ucv_array_get(val, 1), d2))
1734             return false;
1735 
1736         nla_put(msg, spec->attr, len, d1);
1737 
1738         return true;
1739 }
1740 
1741 static uc_value_t *
1742 uc_nl_convert_rta_numrange(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1743 {
1744         union {
1745                 struct { uint8_t low; uint8_t high; } *u8;
1746                 struct { uint16_t low; uint16_t high; } *u16;
1747                 struct { uint32_t low; uint32_t high; } *u32;
1748         } ranges = { 0 };
1749 
1750         bool swap = (spec->flags & DF_BYTESWAP);
1751         uc_value_t *arr, *n1, *n2;
1752 
1753         if (spec->flags & DF_MAX_255) {
1754                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u8)))
1755                         return NULL;
1756 
1757                 ranges.u8 = nla_data(tb[spec->attr]);
1758                 n1 = ucv_int64_new(ranges.u8->low);
1759                 n2 = ucv_int64_new(ranges.u8->high);
1760         }
1761         else if (spec->flags & DF_MAX_65535) {
1762                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u16)))
1763                         return NULL;
1764 
1765                 ranges.u16 = nla_data(tb[spec->attr]);
1766                 n1 = ucv_int64_new(swap ? ntohs(ranges.u16->low) : ranges.u16->low);
1767                 n2 = ucv_int64_new(swap ? ntohs(ranges.u16->high) : ranges.u16->high);
1768         }
1769         else {
1770                 if (!nla_check_len(tb[spec->attr], sizeof(*ranges.u32)))
1771                         return NULL;
1772 
1773                 ranges.u32 = nla_data(tb[spec->attr]);
1774                 n1 = ucv_int64_new(swap ? ntohl(ranges.u32->low) : ranges.u32->low);
1775                 n2 = ucv_int64_new(swap ? ntohl(ranges.u32->high) : ranges.u32->high);
1776         }
1777 
1778         arr = ucv_array_new(vm);
1779 
1780         ucv_array_push(arr, n1);
1781         ucv_array_push(arr, n2);
1782 
1783         return arr;
1784 }
1785 
1786 
1787 #define LINK_TYPE(name) \
1788         { #name, link_##name##_attrs, ARRAY_SIZE(link_##name##_attrs) }
1789 
1790 static const struct {
1791         const char *name;
1792         const uc_nl_attr_spec_t *attrs;
1793         size_t nattrs;
1794 } link_types[] = {
1795         LINK_TYPE(bareudp),
1796         LINK_TYPE(bond),
1797         LINK_TYPE(bond_slave),
1798         LINK_TYPE(bridge),
1799         LINK_TYPE(bridge_slave),
1800         LINK_TYPE(geneve),
1801         LINK_TYPE(hsr),
1802         LINK_TYPE(ipoib),
1803         LINK_TYPE(ipvlan),
1804         LINK_TYPE(macvlan),
1805         LINK_TYPE(rmnet),
1806         LINK_TYPE(vlan),
1807         LINK_TYPE(vrf),
1808         //LINK_TYPE(vxcan),
1809         LINK_TYPE(vxlan),
1810         //LINK_TYPE(xdp),
1811         //LINK_TYPE(xstats),
1812         LINK_TYPE(gre),
1813         LINK_TYPE(gretap),
1814         LINK_TYPE(erspan),
1815         LINK_TYPE(ip6gre),
1816         LINK_TYPE(ip6gretap),
1817         LINK_TYPE(ip6erspan),
1818         LINK_TYPE(ip6tnl),
1819         LINK_TYPE(ipip),
1820         LINK_TYPE(sit),
1821         LINK_TYPE(veth),
1822         LINK_TYPE(vti),
1823         LINK_TYPE(vti6),
1824         LINK_TYPE(xfrm),
1825 };
1826 
1827 static bool
1828 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)
1829 {
1830         const uc_nl_attr_spec_t *attrs = NULL;
1831         struct nlattr *li_nla, *info_nla;
1832         size_t i, nattrs = 0;
1833         char *kind, *p;
1834         uc_value_t *k;
1835 
1836         k = ucv_object_get(val, "type", NULL);
1837         kind = ucv_string_get(k);
1838 
1839         if (!kind)
1840                 return nla_parse_error(spec, vm, val, "linkinfo does not specify kind");
1841 
1842         li_nla = nla_nest_start(msg, spec->attr);
1843 
1844         nla_put_string(msg, IFLA_INFO_KIND, kind);
1845 
1846         for (i = 0; i < ARRAY_SIZE(link_types); i++) {
1847                 if (!strcmp(link_types[i].name, kind)) {
1848                         attrs = link_types[i].attrs;
1849                         nattrs = link_types[i].nattrs;
1850                         break;
1851                 }
1852         }
1853 
1854         p = strchr(kind, '_');
1855 
1856         if (!p || strcmp(p, "_slave"))
1857                 info_nla = nla_nest_start(msg, IFLA_INFO_DATA);
1858         else
1859                 info_nla = nla_nest_start(msg, IFLA_INFO_SLAVE_DATA);
1860 
1861         if (!uc_nl_parse_attrs(msg, base, attrs, nattrs, vm, val))
1862                 return false;
1863 
1864         nla_nest_end(msg, info_nla);
1865         nla_nest_end(msg, li_nla);
1866 
1867         return true;
1868 }
1869 
1870 static uc_value_t *
1871 uc_nl_convert_rta_linkinfo_data(uc_value_t *obj, size_t attr, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1872 {
1873         const uc_nl_attr_spec_t *attrs = NULL;
1874         size_t i, nattrs = 0;
1875         uc_value_t *v;
1876         bool rv;
1877 
1878         if (!tb[attr] || nla_len(tb[attr]) < 1)
1879                 return NULL;
1880 
1881         v = ucv_string_new_length(nla_data(tb[attr]), nla_len(tb[attr]) - 1);
1882 
1883         ucv_object_add(obj, "type", v);
1884 
1885         for (i = 0; i < ARRAY_SIZE(link_types); i++) {
1886                 if (!strcmp(link_types[i].name, ucv_string_get(v))) {
1887                         attrs = link_types[i].attrs;
1888                         nattrs = link_types[i].nattrs;
1889                         break;
1890                 }
1891         }
1892 
1893         if (nattrs > 0) {
1894                 attr = (attr == IFLA_INFO_KIND) ? IFLA_INFO_DATA : IFLA_INFO_SLAVE_DATA;
1895                 rv = uc_nl_convert_attrs(msg, nla_data(tb[attr]), nla_len(tb[attr]), 0, attrs, nattrs, vm, obj);
1896 
1897                 if (!rv)
1898                         return NULL;
1899         }
1900 
1901         return obj;
1902 }
1903 
1904 static uc_value_t *
1905 uc_nl_convert_rta_linkinfo(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1906 {
1907         struct nlattr *linkinfo_tb[IFLA_INFO_MAX];
1908         uc_value_t *info_obj, *slave_obj;
1909 
1910         if (!tb[spec->attr])
1911                 return NULL;
1912 
1913         nla_parse(linkinfo_tb, IFLA_INFO_MAX, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), NULL);
1914 
1915         info_obj = ucv_object_new(vm);
1916 
1917         if (linkinfo_tb[IFLA_INFO_KIND]) {
1918                 if (!uc_nl_convert_rta_linkinfo_data(info_obj, IFLA_INFO_KIND, msg, linkinfo_tb, vm)) {
1919                         ucv_put(info_obj);
1920 
1921                         return NULL;
1922                 }
1923         }
1924 
1925         if (linkinfo_tb[IFLA_INFO_SLAVE_KIND]) {
1926                 slave_obj = ucv_object_new(vm);
1927 
1928                 if (!uc_nl_convert_rta_linkinfo_data(slave_obj, IFLA_INFO_SLAVE_KIND, msg, linkinfo_tb, vm)) {
1929                         ucv_put(info_obj);
1930                         ucv_put(slave_obj);
1931 
1932                         return NULL;
1933                 }
1934 
1935                 ucv_object_add(info_obj, "slave", slave_obj);
1936         }
1937 
1938         return info_obj;
1939 }
1940 
1941 static uc_value_t *
1942 uc_nl_convert_rta_bridgeid(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
1943 {
1944         char buf[sizeof("ffff.ff:ff:ff:ff:ff:ff")];
1945         struct ifla_bridge_id *id;
1946 
1947         if (!nla_check_len(tb[spec->attr], sizeof(*id)))
1948                 return NULL;
1949 
1950         id = nla_data(tb[spec->attr]);
1951 
1952         snprintf(buf, sizeof(buf), "%02x%02x.%02x:%02x:%02x:%02x:%02x:%02x",
1953                 id->prio[0], id->prio[1],
1954                 id->addr[0], id->addr[1],
1955                 id->addr[2], id->addr[3],
1956                 id->addr[4], id->addr[5]);
1957 
1958         return ucv_string_new(buf);
1959 }
1960 
1961 static bool
1962 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)
1963 {
1964         uc_value_t *mode, *hmac, *segs, *seg;
1965         struct seg6_iptunnel_encap *tun;
1966         struct sr6_tlv_hmac *tlv;
1967         struct ipv6_sr_hdr *srh;
1968         size_t i, nsegs, srhlen;
1969         char *s;
1970 
1971         mode = ucv_object_get(val, "mode", NULL);
1972         hmac = ucv_object_get(val, "hmac", NULL);
1973         segs = ucv_object_get(val, "segs", NULL);
1974 
1975         if (mode != NULL &&
1976             (ucv_type(mode) != UC_INTEGER ||
1977              ucv_int64_get(mode) < 0 ||
1978              ucv_int64_get(mode) > UINT32_MAX))
1979                 return nla_parse_error(spec, vm, val, "srh mode not an integer in range 0-4294967295");
1980 
1981         if (hmac != NULL &&
1982             (ucv_type(hmac) != UC_INTEGER ||
1983              ucv_int64_get(hmac) < 0 ||
1984              ucv_int64_get(hmac) > UINT32_MAX))
1985                 return nla_parse_error(spec, vm, val, "srh hmac not an integer in range 0-4294967295");
1986 
1987         if (ucv_type(segs) != UC_ARRAY ||
1988             ucv_array_length(segs) == 0)
1989                 return nla_parse_error(spec, vm, val, "srh segs array missing or empty");
1990 
1991         nsegs = ucv_array_length(segs);
1992 
1993         if (!mode || !ucv_int64_get(mode))
1994                 nsegs++;
1995 
1996         srhlen = 8 + 16 * nsegs;
1997 
1998         if (hmac && ucv_int64_get(hmac))
1999                 srhlen += 40;
2000 
2001 
2002         tun = calloc(1, sizeof(*tun) + srhlen);
2003 
2004         if (!tun)
2005                 return nla_parse_error(spec, vm, val, "cannot allocate srh header");
2006 
2007         tun->mode = (int)ucv_int64_get(mode);
2008 
2009         srh = tun->srh;
2010         srh->hdrlen = (srhlen >> 3) - 1;
2011         srh->type = 4;
2012         srh->segments_left = nsegs - 1;
2013         srh->first_segment = nsegs - 1;
2014 
2015         if (hmac && ucv_int64_get(hmac))
2016                 srh->flags |= SR6_FLAG1_HMAC;
2017 
2018         for (i = 0; i < ucv_array_length(segs); i++) {
2019                 seg = ucv_array_get(segs, i);
2020                 s = ucv_string_get(seg);
2021 
2022                 if (!s || inet_pton(AF_INET6, s, &srh->segments[--nsegs]) != 1) {
2023                         free(tun);
2024 
2025                         return nla_parse_error(spec, vm, val, "srh segs array contains invalid IPv6 address");
2026                 }
2027         }
2028 
2029         if (hmac && ucv_int64_get(hmac)) {
2030                 tlv = (struct sr6_tlv_hmac *)((char *)srh + srhlen - 40);
2031                 tlv->tlvhdr.type = SR6_TLV_HMAC;
2032                 tlv->tlvhdr.len = 38;
2033                 tlv->hmackeyid = htonl((uint32_t)ucv_int64_get(hmac));
2034         }
2035 
2036         nla_put(msg, spec->attr, sizeof(*tun) + srhlen, tun);
2037         free(tun);
2038 
2039         return true;
2040 }
2041 
2042 static uc_value_t *
2043 uc_nl_convert_rta_srh(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2044 {
2045         char buf[INET6_ADDRSTRLEN], *p, *e;
2046         struct seg6_iptunnel_encap *tun;
2047         uc_value_t *tun_obj, *seg_arr;
2048         struct sr6_tlv_hmac *tlv;
2049         size_t i;
2050 
2051         if (!nla_check_len(tb[spec->attr], sizeof(*tun)))
2052                 return NULL;
2053 
2054         tun = nla_data(tb[spec->attr]);
2055         tun_obj = ucv_object_new(vm);
2056 
2057         ucv_object_add(tun_obj, "mode", ucv_int64_new(tun->mode));
2058 
2059         seg_arr = ucv_array_new(vm);
2060 
2061         p = (char *)tun->srh->segments;
2062         e = (char *)tun + nla_len(tb[spec->attr]);
2063 
2064         for (i = tun->srh->first_segment + 1;
2065              p + sizeof(struct in6_addr) <= e && i > 0;
2066              i--, p += sizeof(struct in6_addr)) {
2067                 if (inet_ntop(AF_INET6, p, buf, sizeof(buf)))
2068                         ucv_array_push(seg_arr, ucv_string_new(buf));
2069                 else
2070                         ucv_array_push(seg_arr, NULL);
2071         }
2072 
2073         ucv_object_add(tun_obj, "segs", seg_arr);
2074 
2075         if (sr_has_hmac(tun->srh)) {
2076                 i = ((tun->srh->hdrlen + 1) << 3) - 40;
2077                 tlv = (struct sr6_tlv_hmac *)((char *)tun->srh + i);
2078 
2079                 ucv_object_add(tun_obj, "hmac", ucv_int64_new(ntohl(tlv->hmackeyid)));
2080         }
2081 
2082         return tun_obj;
2083 }
2084 
2085 #define ENCAP_TYPE(name, type) \
2086         { #name, LWTUNNEL_ENCAP_##type, route_encap_##name##_attrs, ARRAY_SIZE(route_encap_##name##_attrs) }
2087 
2088 static const struct {
2089         const char *name;
2090         uint16_t type;
2091         const uc_nl_attr_spec_t *attrs;
2092         size_t nattrs;
2093 } encap_types[] = {
2094         ENCAP_TYPE(mpls, MPLS),
2095         ENCAP_TYPE(ip, IP),
2096         ENCAP_TYPE(ip6, IP6),
2097         ENCAP_TYPE(ila, ILA),
2098         //ENCAP_TYPE(bpf, BPF),
2099         ENCAP_TYPE(seg6, SEG6),
2100         //ENCAP_TYPE(seg6local, SEG6_LOCAL),
2101         //ENCAP_TYPE(rpl, RPL),
2102 };
2103 
2104 static bool
2105 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)
2106 {
2107         const uc_nl_attr_spec_t *attrs = NULL;
2108         struct nlattr *enc_nla;
2109         size_t i, nattrs = 0;
2110         uint16_t ntype = 0;
2111         uc_value_t *t;
2112         char *type;
2113 
2114         t = ucv_object_get(val, "type", NULL);
2115         type = ucv_string_get(t);
2116 
2117         if (!type)
2118                 return nla_parse_error(spec, vm, val, "encap does not specify type");
2119 
2120         for (i = 0; i < ARRAY_SIZE(encap_types); i++) {
2121                 if (!strcmp(encap_types[i].name, type)) {
2122                         ntype = encap_types[i].type;
2123                         attrs = encap_types[i].attrs;
2124                         nattrs = encap_types[i].nattrs;
2125                         break;
2126                 }
2127         }
2128 
2129         if (!ntype)
2130                 return nla_parse_error(spec, vm, val, "encap specifies unknown type");
2131 
2132         nla_put_u16(msg, RTA_ENCAP_TYPE, ntype);
2133 
2134         enc_nla = nla_nest_start(msg, spec->attr);
2135 
2136         if (!uc_nl_parse_attrs(msg, base, attrs, nattrs, vm, val))
2137                 return false;
2138 
2139         nla_nest_end(msg, enc_nla);
2140 
2141         return true;
2142 }
2143 
2144 static uc_value_t *
2145 uc_nl_convert_rta_encap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2146 {
2147         const uc_nl_attr_spec_t *attrs = NULL;
2148         const char *name = NULL;
2149         uc_value_t *encap_obj;
2150         size_t i, nattrs = 0;
2151         bool rv;
2152 
2153         if (!tb[spec->attr] ||
2154             !nla_check_len(tb[RTA_ENCAP_TYPE], sizeof(uint16_t)))
2155                 return NULL;
2156 
2157         for (i = 0; i < ARRAY_SIZE(encap_types); i++) {
2158                 if (encap_types[i].type != nla_get_u16(tb[RTA_ENCAP_TYPE]))
2159                         continue;
2160 
2161                 name = encap_types[i].name;
2162                 attrs = encap_types[i].attrs;
2163                 nattrs = encap_types[i].nattrs;
2164 
2165                 break;
2166         }
2167 
2168         if (!name)
2169                 return NULL;
2170 
2171         encap_obj = ucv_object_new(vm);
2172 
2173         rv = uc_nl_convert_attrs(msg,
2174                 nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), 0,
2175                 attrs, nattrs, vm, encap_obj);
2176 
2177         if (!rv) {
2178                 ucv_put(encap_obj);
2179 
2180                 return NULL;
2181         }
2182 
2183         ucv_object_add(encap_obj, "type", ucv_string_new(name));
2184 
2185         return encap_obj;
2186 }
2187 
2188 #define IPOPTS_TYPE(name, type, multiple) \
2189         { #name, LWTUNNEL_IP_OPTS_##type, multiple, lwtipopt_##name##_attrs, ARRAY_SIZE(lwtipopt_##name##_attrs) }
2190 
2191 static const struct {
2192         const char *name;
2193         uint16_t type;
2194         bool multiple;
2195         const uc_nl_attr_spec_t *attrs;
2196         size_t nattrs;
2197 } lwtipopt_types[] = {
2198         IPOPTS_TYPE(erspan, ERSPAN, false),
2199         IPOPTS_TYPE(geneve, GENEVE, true),
2200         IPOPTS_TYPE(vxlan, VXLAN, false),
2201 };
2202 
2203 static bool
2204 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)
2205 {
2206         const uc_nl_attr_spec_t *attrs = NULL;
2207         struct nlattr *opt_nla, *type_nla;
2208         bool exists, multiple = false;
2209         size_t i, j, nattrs = 0;
2210         uint16_t ntype = 0;
2211         uc_value_t *item;
2212 
2213         ucv_object_foreach(val, type, v) {
2214                 for (i = 0; i < ARRAY_SIZE(lwtipopt_types); i++) {
2215                         if (!strcmp(lwtipopt_types[i].name, type)) {
2216                                 val = v;
2217                                 ntype = lwtipopt_types[i].type;
2218                                 attrs = lwtipopt_types[i].attrs;
2219                                 nattrs = lwtipopt_types[i].nattrs;
2220                                 multiple = lwtipopt_types[i].multiple;
2221                                 break;
2222                         }
2223                 }
2224         }
2225 
2226         if (!ntype)
2227                 return nla_parse_error(spec, vm, val, "unknown IP options type specified");
2228 
2229         opt_nla = nla_nest_start(msg, spec->attr);
2230 
2231         j = 0;
2232         item = (ucv_type(val) == UC_ARRAY) ? ucv_array_get(val, j++) : val;
2233 
2234         while (true) {
2235                 type_nla = nla_nest_start(msg, ntype);
2236 
2237                 for (i = 0; i < nattrs; i++) {
2238                         v = ucv_object_get(item, attrs[i].key, &exists);
2239 
2240                         if (!exists)
2241                                 continue;
2242 
2243                         if (!uc_nl_parse_attr(&attrs[i], msg, nla_data(type_nla), vm, v, 0))
2244                                 return false;
2245                 }
2246 
2247                 nla_nest_end(msg, type_nla);
2248 
2249                 if (!multiple || ucv_type(val) != UC_ARRAY || j >= ucv_array_length(val))
2250                         break;
2251 
2252                 item = ucv_array_get(val, j++);
2253         }
2254 
2255         nla_nest_end(msg, opt_nla);
2256 
2257         return true;
2258 }
2259 
2260 static uc_value_t *
2261 uc_nl_convert_rta_ipopts(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2262 {
2263         struct nlattr *opt_tb[LWTUNNEL_IP_OPTS_MAX + 1];
2264         const uc_nl_attr_spec_t *attrs = NULL;
2265         uc_value_t *opt_obj, *type_obj;
2266         const char *name = NULL;
2267         size_t i, nattrs = 0;
2268         uint16_t type = 0;
2269         bool rv;
2270 
2271         if (!tb[spec->attr] ||
2272                 !nla_parse(opt_tb, LWTUNNEL_IP_OPTS_MAX, nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), NULL))
2273                 return NULL;
2274 
2275         for (i = 0; i < ARRAY_SIZE(lwtipopt_types); i++) {
2276                 if (!opt_tb[lwtipopt_types[i].type])
2277                         continue;
2278 
2279                 type = lwtipopt_types[i].type;
2280                 name = lwtipopt_types[i].name;
2281                 attrs = lwtipopt_types[i].attrs;
2282                 nattrs = lwtipopt_types[i].nattrs;
2283 
2284                 break;
2285         }
2286 
2287         if (!name)
2288                 return NULL;
2289 
2290         type_obj = ucv_object_new(vm);
2291 
2292         rv = uc_nl_convert_attrs(msg,
2293                 nla_data(opt_tb[type]), nla_len(opt_tb[type]), 0,
2294                 attrs, nattrs, vm, type_obj);
2295 
2296         if (!rv) {
2297                 ucv_put(type_obj);
2298 
2299                 return NULL;
2300         }
2301 
2302         opt_obj = ucv_object_new(vm);
2303 
2304         ucv_object_add(opt_obj, name, type_obj);
2305 
2306         return opt_obj;
2307 }
2308 
2309 static bool
2310 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)
2311 {
2312         uint32_t u32;
2313 
2314         if (!uc_nl_parse_u32(val, &u32))
2315                 return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
2316 
2317         if (spec->flags & DF_MAX_255) {
2318                 if (u32 <= 255) {
2319                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2320 
2321                         return true;
2322                 }
2323 
2324                 uc_nl_put_struct_member_u8(base, spec->auxdata, 0);
2325         }
2326         else if (spec->flags & DF_MAX_65535) {
2327                 if (u32 <= 65535) {
2328                         uc_nl_put_struct_member_u16(base, spec->auxdata,
2329                                 (spec->flags & DF_BYTESWAP) ? htons((uint16_t)u32) : (uint16_t)u32);
2330 
2331                         return true;
2332                 }
2333 
2334                 uc_nl_put_struct_member_u16(base, spec->auxdata, 0);
2335         }
2336         else if (spec->flags & DF_MAX_16777215) {
2337                 if (u32 <= 16777215) {
2338                         uc_nl_put_struct_member_u32(base, spec->auxdata,
2339                                 (spec->flags & DF_BYTESWAP) ? htonl(u32) : u32);
2340 
2341                         return true;
2342                 }
2343 
2344                 uc_nl_put_struct_member_u32(base, spec->auxdata, 0);
2345         }
2346 
2347         nla_put_u32(msg, spec->attr,
2348                 (spec->flags & DF_BYTESWAP) ? htonl(u32) : u32);
2349 
2350         return true;
2351 }
2352 
2353 static uc_value_t *
2354 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)
2355 {
2356         uint32_t u32 = 0;
2357 
2358         if (nla_check_len(tb[spec->attr], sizeof(uint32_t))) {
2359                 if (spec->flags & DF_BYTESWAP)
2360                         u32 = ntohl(nla_get_u32(tb[spec->attr]));
2361                 else
2362                         u32 = nla_get_u32(tb[spec->attr]);
2363         }
2364         else if (spec->flags & DF_MAX_255) {
2365                 u32 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2366         }
2367         else if (spec->flags & DF_MAX_65535) {
2368                 if (spec->flags & DF_BYTESWAP)
2369                         u32 = ntohs(uc_nl_get_struct_member_u16(base, spec->auxdata));
2370                 else
2371                         u32 = uc_nl_get_struct_member_u16(base, spec->auxdata);
2372         }
2373         else if (spec->flags & DF_MAX_16777215) {
2374                 if (spec->flags & DF_BYTESWAP)
2375                         u32 = ntohl(uc_nl_get_struct_member_u32(base, spec->auxdata));
2376                 else
2377                         u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2378         }
2379         else {
2380                 return NULL;
2381         }
2382 
2383         return ucv_uint64_new(u32);
2384 }
2385 
2386 static bool
2387 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)
2388 {
2389         const uc_nl_nested_spec_t *nest = spec->auxdata;
2390         struct nlattr *nested_nla;
2391 
2392         nested_nla = nla_reserve(msg, spec->attr, nest->headsize);
2393 
2394         if (!uc_nl_parse_attrs(msg, nla_data(nested_nla), nest->attrs, nest->nattrs, vm, val))
2395                 return false;
2396 
2397         nla_nest_end(msg, nested_nla);
2398 
2399         return true;
2400 }
2401 
2402 static uc_value_t *
2403 uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm)
2404 {
2405         const uc_nl_nested_spec_t *nest = spec->auxdata;
2406         uc_value_t *nested_obj;
2407         bool rv;
2408 
2409         nested_obj = ucv_object_new(vm);
2410 
2411         rv = uc_nl_convert_attrs(msg,
2412                 nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), nest->headsize,
2413                 nest->attrs, nest->nattrs,
2414                 vm, nested_obj);
2415 
2416         if (!rv) {
2417                 ucv_put(nested_obj);
2418 
2419                 return NULL;
2420         }
2421 
2422         return nested_obj;
2423 }
2424 
2425 
2426 static bool
2427 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)
2428 {
2429         uc_nl_cidr_t cidr = { 0 };
2430         struct ether_addr *ea;
2431         struct rtgenmsg *rtg;
2432         uint64_t u64;
2433         uint32_t u32;
2434         uint16_t u16;
2435         size_t attr;
2436         char *s;
2437 
2438         if (spec->flags & DF_MULTIPLE)
2439                 attr = idx;
2440         else
2441                 attr = spec->attr;
2442 
2443         switch (spec->type) {
2444         case DT_U8:
2445                 if (!uc_nl_parse_u32(val, &u32) || u32 > 255)
2446                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-255");
2447 
2448                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2449                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2450 
2451                 if (spec->attr == 0)
2452                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2453                 else
2454                         nla_put_u8(msg, attr, u32);
2455 
2456                 break;
2457 
2458         case DT_U16:
2459                 if (!uc_nl_parse_u32(val, &u32) || u32 > 65535)
2460                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-65535");
2461 
2462                 u16 = (uint16_t)u32;
2463 
2464                 if (spec->flags & DF_BYTESWAP)
2465                         u16 = htons(u16);
2466 
2467                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2468                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2469                 else if ((spec->flags & DF_MAX_255) && u32 > 255)
2470                         return nla_parse_error(spec, vm, val, "integer out of range 0-255");
2471 
2472                 if (spec->attr == 0)
2473                         uc_nl_put_struct_member_u16(base, spec->auxdata, u16);
2474                 else
2475                         nla_put_u16(msg, attr, u16);
2476 
2477                 break;
2478 
2479         case DT_S32:
2480         case DT_U32:
2481                 if (spec->type == DT_S32 && !uc_nl_parse_s32(val, &u32))
2482                         return nla_parse_error(spec, vm, val, "not an integer or out of range -2147483648-2147483647");
2483                 else if (spec->type == DT_U32 && !uc_nl_parse_u32(val, &u32))
2484                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
2485 
2486                 if (spec->flags & DF_BYTESWAP)
2487                         u32 = htonl(u32);
2488 
2489                 if ((spec->flags & DF_MAX_1) && u32 > 1)
2490                         return nla_parse_error(spec, vm, val, "integer must be 0 or 1");
2491                 else if ((spec->flags & DF_MAX_255) && u32 > 255)
2492                         return nla_parse_error(spec, vm, val, "integer out of range 0-255");
2493                 else if ((spec->flags & DF_MAX_65535) && u32 > 65535)
2494                         return nla_parse_error(spec, vm, val, "integer out of range 0-65535");
2495                 else if ((spec->flags & DF_MAX_16777215) && u32 > 16777215)
2496                         return nla_parse_error(spec, vm, val, "integer out of range 0-16777215");
2497 
2498                 if (spec->attr == 0)
2499                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2500                 else
2501                         nla_put_u32(msg, attr, u32);
2502 
2503                 break;
2504 
2505         case DT_U64:
2506                 assert(spec->attr != 0);
2507 
2508                 if (!uc_nl_parse_u64(val, &u64))
2509                         return nla_parse_error(spec, vm, val, "not an integer or negative");
2510 
2511                 if (spec->flags & DF_BYTESWAP)
2512                         u64 = htobe64(u64);
2513 
2514                 nla_put_u64(msg, attr, u64);
2515                 break;
2516 
2517         case DT_BOOL:
2518                 u32 = (uint32_t)ucv_is_truish(val);
2519 
2520                 if (spec->attr == 0)
2521                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2522                 else
2523                         nla_put_u8(msg, attr, u32);
2524 
2525                 break;
2526 
2527         case DT_FLAG:
2528                 u32 = (uint32_t)ucv_is_truish(val);
2529 
2530                 if (spec->attr == 0)
2531                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
2532                 else if (u32 == 1)
2533                         nla_put_flag(msg, attr);
2534 
2535                 break;
2536 
2537         case DT_STRING:
2538                 assert(spec->attr != 0);
2539 
2540                 s = ucv_to_string(vm, val);
2541 
2542                 if (!s)
2543                         return nla_parse_error(spec, vm, val, "out of memory");
2544 
2545                 nla_put_string(msg, attr, s);
2546                 free(s);
2547 
2548                 break;
2549 
2550         case DT_NETDEV:
2551                 if (ucv_type(val) == UC_INTEGER) {
2552                         if (ucv_int64_get(val) < 0 ||
2553                             ucv_int64_get(val) > UINT32_MAX)
2554                                 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295");
2555 
2556                         u32 = (uint32_t)ucv_int64_get(val);
2557                 }
2558                 else {
2559                         s = ucv_to_string(vm, val);
2560 
2561                         if (!s)
2562                                 return nla_parse_error(spec, vm, val, "out of memory");
2563 
2564                         u32 = if_nametoindex(s);
2565 
2566                         free(s);
2567                 }
2568 
2569                 if (u32 == 0 && !(spec->flags & DF_ALLOW_NONE))
2570                         return nla_parse_error(spec, vm, val, "interface not found");
2571 
2572                 if (spec->attr == 0)
2573                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2574                 else
2575                         nla_put_u32(msg, attr, u32);
2576 
2577                 break;
2578 
2579         case DT_LLADDR:
2580                 assert(spec->attr != 0);
2581 
2582                 s = ucv_to_string(vm, val);
2583 
2584                 if (!s)
2585                         return nla_parse_error(spec, vm, val, "out of memory");
2586 
2587                 ea = ether_aton(s);
2588 
2589                 free(s);
2590 
2591                 if (!ea)
2592                         return nla_parse_error(spec, vm, val, "invalid MAC address");
2593 
2594                 nla_put(msg, attr, sizeof(*ea), ea);
2595 
2596                 break;
2597 
2598         case DT_U64ADDR:
2599                 assert(spec->attr != 0);
2600 
2601                 if (ucv_type(val) == UC_INTEGER) {
2602                         u64 = ucv_uint64_get(val);
2603                 }
2604                 else {
2605                         s = ucv_to_string(vm, val);
2606 
2607                         if (!s)
2608                                 return nla_parse_error(spec, vm, val, "out of memory");
2609 
2610                         u16 = addr64_pton(s, &u64);
2611 
2612                         free(s);
2613 
2614                         if (u16 != 1)
2615                                 return nla_parse_error(spec, vm, val, "invalid address");
2616                 }
2617 
2618                 nla_put_u64(msg, attr, u64);
2619 
2620                 break;
2621 
2622         case DT_INADDR:
2623         case DT_IN6ADDR:
2624         case DT_MPLSADDR:
2625         case DT_ANYADDR:
2626                 assert(spec->attr != 0);
2627 
2628                 rtg = nlmsg_data(nlmsg_hdr(msg));
2629 
2630                 if (!uc_nl_parse_cidr(vm, val, &cidr))
2631                         return nla_parse_error(spec, vm, val, "invalid IP address");
2632 
2633                 if ((spec->type == DT_INADDR && cidr.family != AF_INET) ||
2634                     (spec->type == DT_IN6ADDR && cidr.family != AF_INET6) ||
2635                     (spec->type == DT_MPLSADDR && cidr.family != AF_MPLS))
2636                     return nla_parse_error(spec, vm, val, "wrong address family");
2637 
2638                 if (spec->flags & DF_STORE_MASK)
2639                         uc_nl_put_struct_member_u8(base, spec->auxdata, cidr.mask);
2640                 else if (cidr.mask != cidr.bitlen)
2641                         return nla_parse_error(spec, vm, val, "address range given but single address expected");
2642 
2643                 nla_put(msg, attr, cidr.alen, &cidr.addr.in6);
2644 
2645                 if ((rtg->rtgen_family == AF_UNSPEC) && (spec->flags & DF_FAMILY_HINT))
2646                         rtg->rtgen_family = cidr.family;
2647 
2648                 break;
2649 
2650         case DT_MULTIPATH:
2651                 if (!uc_nl_parse_rta_multipath(spec, msg, base, vm, val))
2652                         return nla_parse_error(spec, vm, val, "invalid nexthop data");
2653 
2654                 break;
2655 
2656         case DT_NUMRANGE:
2657                 if (!uc_nl_parse_rta_numrange(spec, msg, base, vm, val))
2658                         return false;
2659 
2660                 break;
2661 
2662         case DT_FLAGS:
2663                 if (ucv_array_length(val) == 2) {
2664                         if (ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
2665                             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
2666                                 return nla_parse_error(spec, vm, val, "flag or mask value not an integer");
2667 
2668                         if (!uc_nl_parse_u32(ucv_array_get(val, 0), &u32))
2669                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
2670 
2671                         memcpy(&u64, &u32, sizeof(u32));
2672 
2673                         if (!uc_nl_parse_u32(ucv_array_get(val, 1), &u32))
2674                                 return nla_parse_error(spec, vm, val, "mask value not an integer or out of range 0-4294967295");
2675 
2676                         memcpy((char *)&u64 + sizeof(u32), &u32, sizeof(u32));
2677                 }
2678                 else if (ucv_type(val) == UC_INTEGER) {
2679                         if (!uc_nl_parse_u32(val, &u32))
2680                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
2681 
2682                         memcpy(&u64, &u32, sizeof(u32));
2683                         memset((char *)&u64 + sizeof(u32), 0xff, sizeof(u32));
2684                 }
2685                 else {
2686                         return nla_parse_error(spec, vm, val, "value neither an array of flags, mask nor an integer");
2687                 }
2688 
2689                 if (spec->attr == 0)
2690                         uc_nl_put_struct_member(base, spec->auxdata, sizeof(u64), &u64);
2691                 else
2692                         nla_put_u64(msg, attr, u64);
2693 
2694                 break;
2695 
2696         case DT_LINKINFO:
2697                 if (!uc_nl_parse_rta_linkinfo(spec, msg, base, vm, val))
2698                         return false;
2699 
2700                 break;
2701 
2702         case DT_SRH:
2703                 if (!uc_nl_parse_rta_srh(spec, msg, base, vm, val))
2704                         return false;
2705 
2706                 break;
2707 
2708         case DT_ENCAP:
2709                 if (!uc_nl_parse_rta_encap(spec, msg, base, vm, val))
2710                         return false;
2711 
2712                 break;
2713 
2714         case DT_IPOPTS:
2715                 if (!uc_nl_parse_rta_ipopts(spec, msg, base, vm, val))
2716                         return false;
2717 
2718                 break;
2719 
2720         case DT_U32_OR_MEMBER:
2721                 if (!uc_nl_parse_rta_u32_or_member(spec, msg, base, vm, val))
2722                         return false;
2723 
2724                 break;
2725 
2726         case DT_NESTED:
2727                 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val))
2728                         return false;
2729 
2730                 break;
2731 
2732         default:
2733                 assert(0);
2734         }
2735 
2736         return true;
2737 }
2738 
2739 static uc_value_t *
2740 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm)
2741 {
2742         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 };
2743         struct { uint32_t flags; uint32_t mask; } flags;
2744         char buf[sizeof(struct mpls_label) * 16];
2745         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2746         struct rtgenmsg *rtg = nlmsg_data(hdr);
2747         struct ether_addr *ea;
2748         uc_value_t *v;
2749         char *s;
2750 
2751         switch (spec->type) {
2752         case DT_U8:
2753                 if (spec->attr == 0)
2754                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2755                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
2756                         t.u8 = nla_get_u8(tb[spec->attr]);
2757 
2758                 return ucv_uint64_new(t.u8);
2759 
2760         case DT_U16:
2761                 if (spec->attr == 0)
2762                         t.u16 = uc_nl_get_struct_member_u16(base, spec->auxdata);
2763                 else if (nla_check_len(tb[spec->attr], sizeof(t.u16)))
2764                         t.u16 = nla_get_u16(tb[spec->attr]);
2765 
2766                 if (spec->flags & DF_BYTESWAP)
2767                         t.u16 = ntohs(t.u16);
2768 
2769                 return ucv_uint64_new(t.u16);
2770 
2771         case DT_U32:
2772         case DT_S32:
2773                 if (spec->attr == 0)
2774                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2775                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
2776                         t.u32 = nla_get_u32(tb[spec->attr]);
2777 
2778                 if (spec->flags & DF_BYTESWAP)
2779                         t.u32 = ntohl(t.u32);
2780 
2781                 if (spec->type == DT_S32)
2782                         return ucv_int64_new((int32_t)t.u32);
2783 
2784                 return ucv_uint64_new(t.u32);
2785 
2786         case DT_U64:
2787                 if (spec->attr == 0)
2788                         t.u64 = uc_nl_get_struct_member_u64(base, spec->auxdata);
2789                 else if (nla_check_len(tb[spec->attr], sizeof(t.u64)))
2790                         memcpy(&t.u64, nla_data(tb[spec->attr]), sizeof(t.u64));
2791 
2792                 return ucv_uint64_new(t.u64);
2793 
2794         case DT_BOOL:
2795                 if (spec->attr == 0)
2796                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2797                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
2798                         t.u8 = nla_get_u8(tb[spec->attr]);
2799 
2800                 return ucv_boolean_new(t.u8 != 0);
2801 
2802         case DT_FLAG:
2803                 if (spec->attr == 0)
2804                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2805                 else if (tb[spec->attr] != NULL)
2806                         t.u8 = 1;
2807 
2808                 return ucv_boolean_new(t.u8 != 0);
2809 
2810         case DT_STRING:
2811                 assert(spec->attr != 0);
2812 
2813                 if (!nla_check_len(tb[spec->attr], 1))
2814                         return NULL;
2815 
2816                 return ucv_string_new_length(
2817                         nla_data(tb[spec->attr]), nla_len(tb[spec->attr]) - 1);
2818 
2819         case DT_NETDEV:
2820                 if (spec->attr == 0)
2821                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2822                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
2823                         t.u32 = nla_get_u32(tb[spec->attr]);
2824 
2825                 if (if_indextoname(t.u32, buf))
2826                         return ucv_string_new(buf);
2827                 else if (spec->flags & DF_ALLOW_NONE)
2828                         return ucv_int64_new(0);
2829 
2830                 return NULL;
2831 
2832         case DT_LLADDR:
2833                 assert(spec->attr != 0);
2834 
2835                 if (!nla_check_len(tb[spec->attr], sizeof(*ea)))
2836                         return NULL;
2837 
2838                 ea = nla_data(tb[spec->attr]);
2839 
2840                 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
2841                         ea->ether_addr_octet[0], ea->ether_addr_octet[1],
2842                         ea->ether_addr_octet[2], ea->ether_addr_octet[3],
2843                         ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
2844 
2845                 return ucv_string_new(buf);
2846 
2847         case DT_U64ADDR:
2848                 assert(spec->attr != 0);
2849 
2850                 if (!nla_check_len(tb[spec->attr], sizeof(uint64_t)) ||
2851                     !addr64_ntop(nla_data(tb[spec->attr]), buf, sizeof(buf)))
2852                         return NULL;
2853 
2854                 return ucv_string_new(buf);
2855 
2856         case DT_INADDR:
2857         case DT_IN6ADDR:
2858         case DT_MPLSADDR:
2859         case DT_ANYADDR:
2860                 assert(spec->attr != 0);
2861 
2862                 t.sz = (size_t)nla_len(tb[spec->attr]);
2863 
2864                 switch (spec->type) {
2865                 case DT_INADDR:
2866                         if (t.sz < sizeof(struct in_addr) ||
2867                             !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2868                                 return NULL;
2869 
2870                         break;
2871 
2872                 case DT_IN6ADDR:
2873                         if (t.sz < sizeof(struct in6_addr) ||
2874                             !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2875                                 return NULL;
2876 
2877                         break;
2878 
2879                 case DT_MPLSADDR:
2880                         if (t.sz < sizeof(struct mpls_label) ||
2881                             !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
2882                                 return NULL;
2883 
2884                         break;
2885 
2886                 default:
2887                         switch (rtg->rtgen_family) {
2888                         case AF_MPLS:
2889                                 if (t.sz < sizeof(struct mpls_label) ||
2890                                     !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
2891                                         return NULL;
2892 
2893                                 break;
2894 
2895                         case AF_INET6:
2896                                 if (t.sz < sizeof(struct in6_addr) ||
2897                                     !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2898                                         return NULL;
2899 
2900                                 break;
2901 
2902                         case AF_INET:
2903                                 if (t.sz < sizeof(struct in_addr) ||
2904                                     !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
2905                                         return NULL;
2906 
2907                                 break;
2908 
2909                         default:
2910                                 return NULL;
2911                         }
2912 
2913                         break;
2914                 }
2915 
2916                 if (spec->flags & DF_STORE_MASK) {
2917                         s = buf + strlen(buf);
2918                         snprintf(s, buf + sizeof(buf) - s, "/%hhu",
2919                                 uc_nl_get_struct_member_u8(base, spec->auxdata));
2920                 }
2921 
2922                 return ucv_string_new(buf);
2923 
2924         case DT_MULTIPATH:
2925                 return uc_nl_convert_rta_multipath(spec, msg, tb, vm);
2926 
2927         case DT_NUMRANGE:
2928                 return uc_nl_convert_rta_numrange(spec, msg, tb, vm);
2929 
2930         case DT_FLAGS:
2931                 if (spec->attr == 0)
2932                         uc_nl_get_struct_member(base, spec->auxdata, sizeof(flags), &flags);
2933                 else if (nla_check_len(tb[spec->attr], sizeof(flags)))
2934                         memcpy(&flags, nla_data(tb[spec->attr]), sizeof(flags));
2935                 else
2936                         return NULL;
2937 
2938                 if (flags.mask == 0)
2939                         return ucv_uint64_new(flags.flags);
2940 
2941                 v = ucv_array_new(vm);
2942 
2943                 ucv_array_push(v, ucv_uint64_new(flags.flags));
2944                 ucv_array_push(v, ucv_uint64_new(flags.mask));
2945 
2946                 return v;
2947 
2948         case DT_LINKINFO:
2949                 return uc_nl_convert_rta_linkinfo(spec, msg, tb, vm);
2950 
2951         case DT_BRIDGEID:
2952                 return uc_nl_convert_rta_bridgeid(spec, msg, tb, vm);
2953 
2954         case DT_SRH:
2955                 return uc_nl_convert_rta_srh(spec, msg, tb, vm);
2956 
2957         case DT_ENCAP:
2958                 return uc_nl_convert_rta_encap(spec, msg, tb, vm);
2959 
2960         case DT_IPOPTS:
2961                 return uc_nl_convert_rta_ipopts(spec, msg, tb, vm);
2962 
2963         case DT_U32_OR_MEMBER:
2964                 return uc_nl_convert_rta_u32_or_member(spec, msg, base, tb, vm);
2965 
2966         case DT_NESTED:
2967                 return uc_nl_convert_rta_nested(spec, msg, tb, vm);
2968 
2969         default:
2970                 assert(0);
2971         }
2972 
2973         return NULL;
2974 }
2975 
2976 
2977 static struct nl_sock *sock = NULL;
2978 
2979 typedef enum {
2980         STATE_UNREPLIED,
2981         STATE_CONTINUE,
2982         STATE_REPLIED,
2983         STATE_ERROR
2984 } reply_state_t;
2985 
2986 typedef struct {
2987         reply_state_t state;
2988         uc_vm_t *vm;
2989         uc_value_t *res;
2990         int family;
2991         const uc_nl_nested_spec_t *spec;
2992 } request_state_t;
2993 
2994 
2995 static uc_value_t *
2996 uc_nl_error(uc_vm_t *vm, size_t nargs)
2997 {
2998         uc_stringbuf_t *buf;
2999         const char *s;
3000 
3001         if (last_error.code == 0)
3002                 return NULL;
3003 
3004         buf = ucv_stringbuf_new();
3005 
3006         if (last_error.code == NLE_FAILURE && last_error.msg) {
3007                 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
3008         }
3009         else {
3010                 s = nl_geterror(last_error.code);
3011 
3012                 ucv_stringbuf_addstr(buf, s, strlen(s));
3013 
3014                 if (last_error.msg)
3015                         ucv_stringbuf_printf(buf, ": %s", last_error.msg);
3016         }
3017 
3018         set_error(0, NULL);
3019 
3020         return ucv_stringbuf_finish(buf);
3021 }
3022 
3023 /*
3024  * route functions
3025  */
3026 
3027 static int
3028 cb_done(struct nl_msg *msg, void *arg)
3029 {
3030         request_state_t *s = arg;
3031 
3032         s->state = STATE_REPLIED;
3033 
3034         return NL_STOP;
3035 }
3036 
3037 static int
3038 cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
3039 {
3040         request_state_t *s = arg;
3041         int errnum = err->error;
3042 
3043         set_error(NLE_FAILURE, "RTNETLINK answers: %s",
3044                   strerror(errnum < 0 ? -errnum : errnum));
3045 
3046         s->state = STATE_ERROR;
3047 
3048         return NL_STOP;
3049 }
3050 
3051 static int
3052 cb_reply(struct nl_msg *msg, void *arg)
3053 {
3054         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3055         request_state_t *s = arg;
3056         uc_value_t *o;
3057         bool rv;
3058 
3059         if (RTM_FAM(hdr->nlmsg_type) != s->family)
3060                 return NL_SKIP;
3061 
3062         if (s->spec) {
3063                 if (nlmsg_attrlen(hdr, 0) < (ssize_t)s->spec->headsize)
3064                         return NL_SKIP;
3065 
3066                 o = ucv_object_new(s->vm);
3067 
3068                 rv = uc_nl_convert_attrs(msg,
3069                         nlmsg_attrdata(hdr, 0),
3070                         nlmsg_attrlen(hdr, 0),
3071                         s->spec->headsize,
3072                         s->spec->attrs, s->spec->nattrs, s->vm, o);
3073 
3074                 if (rv) {
3075                         if (hdr->nlmsg_flags & NLM_F_MULTI) {
3076                                 if (!s->res)
3077                                         s->res = ucv_array_new(s->vm);
3078 
3079                                 ucv_array_push(s->res, o);
3080                         }
3081                         else {
3082                                 s->res = o;
3083                         }
3084                 }
3085                 else {
3086                         ucv_put(o);
3087                 }
3088         }
3089 
3090         if (hdr->nlmsg_flags & NLM_F_MULTI)
3091                 s->state = STATE_CONTINUE;
3092         else
3093                 s->state = STATE_REPLIED;
3094 
3095         return NL_SKIP;
3096 }
3097 
3098 
3099 static const struct {
3100         int family;
3101         const uc_nl_nested_spec_t *spec;
3102 } rtm_families[] = {
3103         { RTM_FAM(RTM_GETLINK), &link_msg },
3104         { RTM_FAM(RTM_GETROUTE), &route_msg },
3105         { RTM_FAM(RTM_GETNEIGH), &neigh_msg },
3106         { RTM_FAM(RTM_GETADDR), &addr_msg },
3107         { RTM_FAM(RTM_GETRULE), &rule_msg },
3108         { RTM_FAM(RTM_GETADDRLABEL), &addrlabel_msg },
3109         { RTM_FAM(RTM_GETNEIGHTBL), &neightbl_msg },
3110         { RTM_FAM(RTM_GETNETCONF), &netconf_msg },
3111 };
3112 
3113 static uc_value_t *
3114 uc_nl_request(uc_vm_t *vm, size_t nargs)
3115 {
3116         uc_value_t *cmd = uc_fn_arg(0);
3117         uc_value_t *flags = uc_fn_arg(1);
3118         uc_value_t *payload = uc_fn_arg(2);
3119         request_state_t st = { .vm = vm };
3120         uint16_t flagval = 0;
3121         int enable = 1, err;
3122         struct nl_msg *msg;
3123         struct nl_cb *cb;
3124         size_t i;
3125 
3126         if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 ||
3127             (flags != NULL && ucv_type(flags) != UC_INTEGER) ||
3128             (payload != NULL && ucv_type(payload) != UC_OBJECT))
3129                 err_return(NLE_INVAL, NULL);
3130 
3131         if (flags) {
3132                 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff)
3133                         err_return(NLE_INVAL, NULL);
3134                 else
3135                         flagval = (uint16_t)ucv_int64_get(flags);
3136         }
3137 
3138         for (i = 0; i < ARRAY_SIZE(rtm_families); i++) {
3139                 if (rtm_families[i].family == RTM_FAM(ucv_int64_get(cmd))) {
3140                         st.spec = rtm_families[i].spec;
3141                         st.family = rtm_families[i].family;
3142                         break;
3143                 }
3144         }
3145 
3146         if (!sock) {
3147                 sock = nl_socket_alloc();
3148 
3149                 if (!sock)
3150                         err_return(NLE_NOMEM, NULL);
3151 
3152                 err = nl_connect(sock, NETLINK_ROUTE);
3153 
3154                 if (err != 0)
3155                         err_return(err, NULL);
3156 
3157                 if (flagval & NLM_F_STRICT_CHK) {
3158                         if (setsockopt(sock->s_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &enable, sizeof(enable)) < 0)
3159                                 err_return(nl_syserr2nlerr(errno), "Unable to enable NETLINK_GET_STRICT_CHK");
3160 
3161                         flagval &= ~NLM_F_STRICT_CHK;
3162                 }
3163         }
3164 
3165         msg = nlmsg_alloc_simple(ucv_int64_get(cmd), NLM_F_REQUEST | flagval);
3166 
3167         if (!msg)
3168                 err_return(NLE_NOMEM, NULL);
3169 
3170         if (st.spec) {
3171                 nlmsg_reserve(msg, st.spec->headsize, 0);
3172 
3173                 if (!uc_nl_parse_attrs(msg, NLMSG_DATA(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) {
3174                         nlmsg_free(msg);
3175 
3176                         return NULL;
3177                 }
3178         }
3179 
3180         cb = nl_cb_alloc(NL_CB_DEFAULT);
3181 
3182         if (!cb) {
3183                 nlmsg_free(msg);
3184                 err_return(NLE_NOMEM, NULL);
3185         }
3186 
3187         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st);
3188         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
3189         nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
3190 
3191         nl_send_auto_complete(sock, msg);
3192 
3193         do {
3194                 err = nl_recvmsgs(sock, cb);
3195 
3196                 if (err && st.state != STATE_ERROR) {
3197                         set_error(err, NULL);
3198 
3199                         st.state = STATE_ERROR;
3200                 }
3201         }
3202         while (st.state == STATE_CONTINUE);
3203 
3204         nlmsg_free(msg);
3205         nl_cb_put(cb);
3206 
3207         switch (st.state) {
3208         case STATE_REPLIED:
3209                 return st.res;
3210 
3211         case STATE_UNREPLIED:
3212                 return ucv_boolean_new(true);
3213 
3214         case STATE_ERROR:
3215                 return ucv_boolean_new(false);
3216 
3217         default:
3218                 set_error(NLE_FAILURE, "Interrupted reply");
3219 
3220                 return ucv_boolean_new(false);
3221         }
3222 }
3223 
3224 
3225 static void
3226 register_constants(uc_vm_t *vm, uc_value_t *scope)
3227 {
3228         uc_value_t *c = ucv_object_new(vm);
3229 
3230 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x))
3231 
3232         ADD_CONST(NLM_F_ACK);
3233         ADD_CONST(NLM_F_ACK_TLVS);
3234         ADD_CONST(NLM_F_APPEND);
3235         ADD_CONST(NLM_F_ATOMIC);
3236         ADD_CONST(NLM_F_CAPPED);
3237         ADD_CONST(NLM_F_CREATE);
3238         ADD_CONST(NLM_F_DUMP);
3239         ADD_CONST(NLM_F_DUMP_FILTERED);
3240         ADD_CONST(NLM_F_DUMP_INTR);
3241         ADD_CONST(NLM_F_ECHO);
3242         ADD_CONST(NLM_F_EXCL);
3243         ADD_CONST(NLM_F_MATCH);
3244         ADD_CONST(NLM_F_MULTI);
3245         ADD_CONST(NLM_F_NONREC);
3246         ADD_CONST(NLM_F_REPLACE);
3247         ADD_CONST(NLM_F_REQUEST);
3248         ADD_CONST(NLM_F_ROOT);
3249         ADD_CONST(NLM_F_STRICT_CHK); /* custom */
3250 
3251         ADD_CONST(IN6_ADDR_GEN_MODE_EUI64);
3252         ADD_CONST(IN6_ADDR_GEN_MODE_NONE);
3253         ADD_CONST(IN6_ADDR_GEN_MODE_STABLE_PRIVACY);
3254         ADD_CONST(IN6_ADDR_GEN_MODE_RANDOM);
3255 
3256         ADD_CONST(BRIDGE_MODE_UNSPEC);
3257         ADD_CONST(BRIDGE_MODE_HAIRPIN);
3258 
3259         ADD_CONST(MACVLAN_MODE_PRIVATE);
3260         ADD_CONST(MACVLAN_MODE_VEPA);
3261         ADD_CONST(MACVLAN_MODE_BRIDGE);
3262         ADD_CONST(MACVLAN_MODE_PASSTHRU);
3263         ADD_CONST(MACVLAN_MODE_SOURCE);
3264 
3265         ADD_CONST(MACVLAN_MACADDR_ADD);
3266         ADD_CONST(MACVLAN_MACADDR_DEL);
3267         ADD_CONST(MACVLAN_MACADDR_FLUSH);
3268         ADD_CONST(MACVLAN_MACADDR_SET);
3269 
3270         ADD_CONST(MACSEC_VALIDATE_DISABLED);
3271         ADD_CONST(MACSEC_VALIDATE_CHECK);
3272         ADD_CONST(MACSEC_VALIDATE_STRICT);
3273         ADD_CONST(MACSEC_VALIDATE_MAX);
3274 
3275         ADD_CONST(MACSEC_OFFLOAD_OFF);
3276         ADD_CONST(MACSEC_OFFLOAD_PHY);
3277         ADD_CONST(MACSEC_OFFLOAD_MAC);
3278         ADD_CONST(MACSEC_OFFLOAD_MAX);
3279 
3280         ADD_CONST(IPVLAN_MODE_L2);
3281         ADD_CONST(IPVLAN_MODE_L3);
3282         ADD_CONST(IPVLAN_MODE_L3S);
3283 
3284         ADD_CONST(VXLAN_DF_UNSET);
3285         ADD_CONST(VXLAN_DF_SET);
3286         ADD_CONST(VXLAN_DF_INHERIT);
3287         ADD_CONST(VXLAN_DF_MAX);
3288 
3289         ADD_CONST(GENEVE_DF_UNSET);
3290         ADD_CONST(GENEVE_DF_SET);
3291         ADD_CONST(GENEVE_DF_INHERIT);
3292         ADD_CONST(GENEVE_DF_MAX);
3293 
3294         ADD_CONST(GTP_ROLE_GGSN);
3295         ADD_CONST(GTP_ROLE_SGSN);
3296 
3297         ADD_CONST(PORT_REQUEST_PREASSOCIATE);
3298         ADD_CONST(PORT_REQUEST_PREASSOCIATE_RR);
3299         ADD_CONST(PORT_REQUEST_ASSOCIATE);
3300         ADD_CONST(PORT_REQUEST_DISASSOCIATE);
3301 
3302         ADD_CONST(PORT_VDP_RESPONSE_SUCCESS);
3303         ADD_CONST(PORT_VDP_RESPONSE_INVALID_FORMAT);
3304         ADD_CONST(PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES);
3305         ADD_CONST(PORT_VDP_RESPONSE_UNUSED_VTID);
3306         ADD_CONST(PORT_VDP_RESPONSE_VTID_VIOLATION);
3307         ADD_CONST(PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION);
3308         ADD_CONST(PORT_VDP_RESPONSE_OUT_OF_SYNC);
3309         ADD_CONST(PORT_PROFILE_RESPONSE_SUCCESS);
3310         ADD_CONST(PORT_PROFILE_RESPONSE_INPROGRESS);
3311         ADD_CONST(PORT_PROFILE_RESPONSE_INVALID);
3312         ADD_CONST(PORT_PROFILE_RESPONSE_BADSTATE);
3313         ADD_CONST(PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES);
3314         ADD_CONST(PORT_PROFILE_RESPONSE_ERROR);
3315 
3316         ADD_CONST(IPOIB_MODE_DATAGRAM);
3317         ADD_CONST(IPOIB_MODE_CONNECTED);
3318 
3319         ADD_CONST(HSR_PROTOCOL_HSR);
3320         ADD_CONST(HSR_PROTOCOL_PRP);
3321 
3322         ADD_CONST(LINK_XSTATS_TYPE_UNSPEC);
3323         ADD_CONST(LINK_XSTATS_TYPE_BRIDGE);
3324         ADD_CONST(LINK_XSTATS_TYPE_BOND);
3325 
3326         ADD_CONST(XDP_ATTACHED_NONE);
3327         ADD_CONST(XDP_ATTACHED_DRV);
3328         ADD_CONST(XDP_ATTACHED_SKB);
3329         ADD_CONST(XDP_ATTACHED_HW);
3330         ADD_CONST(XDP_ATTACHED_MULTI);
3331 
3332         ADD_CONST(FDB_NOTIFY_BIT);
3333         ADD_CONST(FDB_NOTIFY_INACTIVE_BIT);
3334 
3335         ADD_CONST(RTM_BASE);
3336         ADD_CONST(RTM_NEWLINK);
3337         ADD_CONST(RTM_DELLINK);
3338         ADD_CONST(RTM_GETLINK);
3339         ADD_CONST(RTM_SETLINK);
3340         ADD_CONST(RTM_NEWADDR);
3341         ADD_CONST(RTM_DELADDR);
3342         ADD_CONST(RTM_GETADDR);
3343         ADD_CONST(RTM_NEWROUTE);
3344         ADD_CONST(RTM_DELROUTE);
3345         ADD_CONST(RTM_GETROUTE);
3346         ADD_CONST(RTM_NEWNEIGH);
3347         ADD_CONST(RTM_DELNEIGH);
3348         ADD_CONST(RTM_GETNEIGH);
3349         ADD_CONST(RTM_NEWRULE);
3350         ADD_CONST(RTM_DELRULE);
3351         ADD_CONST(RTM_GETRULE);
3352         ADD_CONST(RTM_NEWQDISC);
3353         ADD_CONST(RTM_DELQDISC);
3354         ADD_CONST(RTM_GETQDISC);
3355         ADD_CONST(RTM_NEWTCLASS);
3356         ADD_CONST(RTM_DELTCLASS);
3357         ADD_CONST(RTM_GETTCLASS);
3358         ADD_CONST(RTM_NEWTFILTER);
3359         ADD_CONST(RTM_DELTFILTER);
3360         ADD_CONST(RTM_GETTFILTER);
3361         ADD_CONST(RTM_NEWACTION);
3362         ADD_CONST(RTM_DELACTION);
3363         ADD_CONST(RTM_GETACTION);
3364         ADD_CONST(RTM_NEWPREFIX);
3365         ADD_CONST(RTM_GETMULTICAST);
3366         ADD_CONST(RTM_GETANYCAST);
3367         ADD_CONST(RTM_NEWNEIGHTBL);
3368         ADD_CONST(RTM_GETNEIGHTBL);
3369         ADD_CONST(RTM_SETNEIGHTBL);
3370         ADD_CONST(RTM_NEWNDUSEROPT);
3371         ADD_CONST(RTM_NEWADDRLABEL);
3372         ADD_CONST(RTM_DELADDRLABEL);
3373         ADD_CONST(RTM_GETADDRLABEL);
3374         ADD_CONST(RTM_GETDCB);
3375         ADD_CONST(RTM_SETDCB);
3376         ADD_CONST(RTM_NEWNETCONF);
3377         ADD_CONST(RTM_DELNETCONF);
3378         ADD_CONST(RTM_GETNETCONF);
3379         ADD_CONST(RTM_NEWMDB);
3380         ADD_CONST(RTM_DELMDB);
3381         ADD_CONST(RTM_GETMDB);
3382         ADD_CONST(RTM_NEWNSID);
3383         ADD_CONST(RTM_DELNSID);
3384         ADD_CONST(RTM_GETNSID);
3385         ADD_CONST(RTM_NEWSTATS);
3386         ADD_CONST(RTM_GETSTATS);
3387         ADD_CONST(RTM_NEWCACHEREPORT);
3388         ADD_CONST(RTM_NEWCHAIN);
3389         ADD_CONST(RTM_DELCHAIN);
3390         ADD_CONST(RTM_GETCHAIN);
3391         ADD_CONST(RTM_NEWNEXTHOP);
3392         ADD_CONST(RTM_DELNEXTHOP);
3393         ADD_CONST(RTM_GETNEXTHOP);
3394         ADD_CONST(RTM_NEWLINKPROP);
3395         ADD_CONST(RTM_DELLINKPROP);
3396         ADD_CONST(RTM_GETLINKPROP);
3397         ADD_CONST(RTM_NEWVLAN);
3398         ADD_CONST(RTM_DELVLAN);
3399         ADD_CONST(RTM_GETVLAN);
3400 
3401         ADD_CONST(RTN_UNSPEC);
3402         ADD_CONST(RTN_UNICAST);
3403         ADD_CONST(RTN_LOCAL);
3404         ADD_CONST(RTN_BROADCAST);
3405         ADD_CONST(RTN_ANYCAST);
3406         ADD_CONST(RTN_MULTICAST);
3407         ADD_CONST(RTN_BLACKHOLE);
3408         ADD_CONST(RTN_UNREACHABLE);
3409         ADD_CONST(RTN_PROHIBIT);
3410         ADD_CONST(RTN_THROW);
3411         ADD_CONST(RTN_NAT);
3412         ADD_CONST(RTN_XRESOLVE);
3413 
3414         ADD_CONST(RT_SCOPE_UNIVERSE);
3415         ADD_CONST(RT_SCOPE_SITE);
3416         ADD_CONST(RT_SCOPE_LINK);
3417         ADD_CONST(RT_SCOPE_HOST);
3418         ADD_CONST(RT_SCOPE_NOWHERE);
3419 
3420         ADD_CONST(RT_TABLE_UNSPEC);
3421         ADD_CONST(RT_TABLE_COMPAT);
3422         ADD_CONST(RT_TABLE_DEFAULT);
3423         ADD_CONST(RT_TABLE_MAIN);
3424         ADD_CONST(RT_TABLE_LOCAL);
3425         ADD_CONST(RT_TABLE_MAX);
3426 
3427         /* required to construct RTAX_LOCK */
3428         ADD_CONST(RTAX_MTU);
3429         ADD_CONST(RTAX_HOPLIMIT);
3430         ADD_CONST(RTAX_ADVMSS);
3431         ADD_CONST(RTAX_REORDERING);
3432         ADD_CONST(RTAX_RTT);
3433         ADD_CONST(RTAX_WINDOW);
3434         ADD_CONST(RTAX_CWND);
3435         ADD_CONST(RTAX_INITCWND);
3436         ADD_CONST(RTAX_INITRWND);
3437         ADD_CONST(RTAX_FEATURES);
3438         ADD_CONST(RTAX_QUICKACK);
3439         ADD_CONST(RTAX_CC_ALGO);
3440         ADD_CONST(RTAX_RTTVAR);
3441         ADD_CONST(RTAX_SSTHRESH);
3442         ADD_CONST(RTAX_FASTOPEN_NO_COOKIE);
3443 
3444         ADD_CONST(PREFIX_UNSPEC);
3445         ADD_CONST(PREFIX_ADDRESS);
3446         ADD_CONST(PREFIX_CACHEINFO);
3447 
3448         ADD_CONST(NDUSEROPT_UNSPEC);
3449         ADD_CONST(NDUSEROPT_SRCADDR);
3450 
3451         ADD_CONST(RTNLGRP_NONE);
3452         ADD_CONST(RTNLGRP_LINK);
3453         ADD_CONST(RTNLGRP_NOTIFY);
3454         ADD_CONST(RTNLGRP_NEIGH);
3455         ADD_CONST(RTNLGRP_TC);
3456         ADD_CONST(RTNLGRP_IPV4_IFADDR);
3457         ADD_CONST(RTNLGRP_IPV4_MROUTE);
3458         ADD_CONST(RTNLGRP_IPV4_ROUTE);
3459         ADD_CONST(RTNLGRP_IPV4_RULE);
3460         ADD_CONST(RTNLGRP_IPV6_IFADDR);
3461         ADD_CONST(RTNLGRP_IPV6_MROUTE);
3462         ADD_CONST(RTNLGRP_IPV6_ROUTE);
3463         ADD_CONST(RTNLGRP_IPV6_IFINFO);
3464         ADD_CONST(RTNLGRP_DECnet_IFADDR);
3465         ADD_CONST(RTNLGRP_NOP2);
3466         ADD_CONST(RTNLGRP_DECnet_ROUTE);
3467         ADD_CONST(RTNLGRP_DECnet_RULE);
3468         ADD_CONST(RTNLGRP_NOP4);
3469         ADD_CONST(RTNLGRP_IPV6_PREFIX);
3470         ADD_CONST(RTNLGRP_IPV6_RULE);
3471         ADD_CONST(RTNLGRP_ND_USEROPT);
3472         ADD_CONST(RTNLGRP_PHONET_IFADDR);
3473         ADD_CONST(RTNLGRP_PHONET_ROUTE);
3474         ADD_CONST(RTNLGRP_DCB);
3475         ADD_CONST(RTNLGRP_IPV4_NETCONF);
3476         ADD_CONST(RTNLGRP_IPV6_NETCONF);
3477         ADD_CONST(RTNLGRP_MDB);
3478         ADD_CONST(RTNLGRP_MPLS_ROUTE);
3479         ADD_CONST(RTNLGRP_NSID);
3480         ADD_CONST(RTNLGRP_MPLS_NETCONF);
3481         ADD_CONST(RTNLGRP_IPV4_MROUTE_R);
3482         ADD_CONST(RTNLGRP_IPV6_MROUTE_R);
3483         ADD_CONST(RTNLGRP_NEXTHOP);
3484         ADD_CONST(RTNLGRP_BRVLAN);
3485 
3486         ADD_CONST(RTM_F_CLONED);
3487         ADD_CONST(RTM_F_EQUALIZE);
3488         ADD_CONST(RTM_F_FIB_MATCH);
3489         ADD_CONST(RTM_F_LOOKUP_TABLE);
3490         ADD_CONST(RTM_F_NOTIFY);
3491         ADD_CONST(RTM_F_PREFIX);
3492 
3493         ADD_CONST(AF_UNSPEC);
3494         ADD_CONST(AF_INET);
3495         ADD_CONST(AF_INET6);
3496         ADD_CONST(AF_MPLS);
3497         ADD_CONST(AF_BRIDGE);
3498 
3499         ADD_CONST(GRE_CSUM);
3500         ADD_CONST(GRE_ROUTING);
3501         ADD_CONST(GRE_KEY);
3502         ADD_CONST(GRE_SEQ);
3503         ADD_CONST(GRE_STRICT);
3504         ADD_CONST(GRE_REC);
3505         ADD_CONST(GRE_ACK);
3506 
3507         ADD_CONST(TUNNEL_ENCAP_NONE);
3508         ADD_CONST(TUNNEL_ENCAP_FOU);
3509         ADD_CONST(TUNNEL_ENCAP_GUE);
3510         ADD_CONST(TUNNEL_ENCAP_MPLS);
3511 
3512         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM);
3513         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM6);
3514         ADD_CONST(TUNNEL_ENCAP_FLAG_REMCSUM);
3515 
3516         ADD_CONST(IP6_TNL_F_ALLOW_LOCAL_REMOTE);
3517         ADD_CONST(IP6_TNL_F_IGN_ENCAP_LIMIT);
3518         ADD_CONST(IP6_TNL_F_MIP6_DEV);
3519         ADD_CONST(IP6_TNL_F_RCV_DSCP_COPY);
3520         ADD_CONST(IP6_TNL_F_USE_ORIG_FLOWLABEL);
3521         ADD_CONST(IP6_TNL_F_USE_ORIG_FWMARK);
3522         ADD_CONST(IP6_TNL_F_USE_ORIG_TCLASS);
3523 
3524         ADD_CONST(NTF_EXT_LEARNED);
3525         ADD_CONST(NTF_MASTER);
3526         ADD_CONST(NTF_OFFLOADED);
3527         ADD_CONST(NTF_PROXY);
3528         ADD_CONST(NTF_ROUTER);
3529         ADD_CONST(NTF_SELF);
3530         ADD_CONST(NTF_STICKY);
3531         ADD_CONST(NTF_USE);
3532 
3533         ADD_CONST(NUD_DELAY);
3534         ADD_CONST(NUD_FAILED);
3535         ADD_CONST(NUD_INCOMPLETE);
3536         ADD_CONST(NUD_NOARP);
3537         ADD_CONST(NUD_NONE);
3538         ADD_CONST(NUD_PERMANENT);
3539         ADD_CONST(NUD_PROBE);
3540         ADD_CONST(NUD_REACHABLE);
3541         ADD_CONST(NUD_STALE);
3542 
3543         ADD_CONST(IFA_F_DADFAILED);
3544         ADD_CONST(IFA_F_DEPRECATED);
3545         ADD_CONST(IFA_F_HOMEADDRESS);
3546         ADD_CONST(IFA_F_MANAGETEMPADDR);
3547         ADD_CONST(IFA_F_MCAUTOJOIN);
3548         ADD_CONST(IFA_F_NODAD);
3549         ADD_CONST(IFA_F_NOPREFIXROUTE);
3550         ADD_CONST(IFA_F_OPTIMISTIC);
3551         ADD_CONST(IFA_F_PERMANENT);
3552         ADD_CONST(IFA_F_SECONDARY);
3553         ADD_CONST(IFA_F_STABLE_PRIVACY);
3554         ADD_CONST(IFA_F_TEMPORARY);
3555         ADD_CONST(IFA_F_TENTATIVE);
3556 
3557         ADD_CONST(FIB_RULE_PERMANENT);
3558         ADD_CONST(FIB_RULE_INVERT);
3559         ADD_CONST(FIB_RULE_UNRESOLVED);
3560         ADD_CONST(FIB_RULE_IIF_DETACHED);
3561         ADD_CONST(FIB_RULE_DEV_DETACHED);
3562         ADD_CONST(FIB_RULE_OIF_DETACHED);
3563 
3564         ADD_CONST(FR_ACT_TO_TBL);
3565         ADD_CONST(FR_ACT_GOTO);
3566         ADD_CONST(FR_ACT_NOP);
3567         ADD_CONST(FR_ACT_BLACKHOLE);
3568         ADD_CONST(FR_ACT_UNREACHABLE);
3569         ADD_CONST(FR_ACT_PROHIBIT);
3570 
3571         ADD_CONST(NETCONFA_IFINDEX_ALL);
3572         ADD_CONST(NETCONFA_IFINDEX_DEFAULT);
3573 
3574         ADD_CONST(BRIDGE_VLAN_INFO_MASTER);
3575         ADD_CONST(BRIDGE_VLAN_INFO_PVID);
3576         ADD_CONST(BRIDGE_VLAN_INFO_UNTAGGED);
3577         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_BEGIN);
3578         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_END);
3579         ADD_CONST(BRIDGE_VLAN_INFO_BRENTRY);
3580 
3581         ucv_object_add(scope, "const", c);
3582 };
3583 
3584 static const uc_function_list_t global_fns[] = {
3585         { "error",              uc_nl_error },
3586         { "request",    uc_nl_request },
3587 };
3588 
3589 
3590 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
3591 {
3592         uc_function_list_register(scope, global_fns);
3593 
3594         register_constants(vm, scope);
3595 }
3596 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt