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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt