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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt