• 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                 s = ucv_to_string(vm, val);
2778 
2779                 if (!s)
2780                         return nla_parse_error(spec, vm, val, "out of memory");
2781 
2782                 nla_put_string(msg, attr, s);
2783                 free(s);
2784 
2785                 break;
2786 
2787         case DT_NETDEV:
2788                 if (ucv_type(val) == UC_INTEGER) {
2789                         if (ucv_int64_get(val) < 0 ||
2790                             ucv_int64_get(val) > UINT32_MAX)
2791                                 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295");
2792 
2793                         u32 = (uint32_t)ucv_int64_get(val);
2794                 }
2795                 else {
2796                         s = ucv_to_string(vm, val);
2797 
2798                         if (!s)
2799                                 return nla_parse_error(spec, vm, val, "out of memory");
2800 
2801                         u32 = if_nametoindex(s);
2802 
2803                         free(s);
2804                 }
2805 
2806                 if (u32 == 0 && !(spec->flags & DF_ALLOW_NONE))
2807                         return nla_parse_error(spec, vm, val, "interface not found");
2808 
2809                 if (spec->attr == 0)
2810                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
2811                 else
2812                         nla_put_u32(msg, attr, u32);
2813 
2814                 break;
2815 
2816         case DT_LLADDR:
2817                 assert(spec->attr != 0);
2818 
2819                 s = ucv_to_string(vm, val);
2820 
2821                 if (!s)
2822                         return nla_parse_error(spec, vm, val, "out of memory");
2823 
2824                 ea = ether_aton(s);
2825 
2826                 free(s);
2827 
2828                 if (!ea)
2829                         return nla_parse_error(spec, vm, val, "invalid MAC address");
2830 
2831                 nla_put(msg, attr, sizeof(*ea), ea);
2832 
2833                 break;
2834 
2835         case DT_U64ADDR:
2836                 assert(spec->attr != 0);
2837 
2838                 if (ucv_type(val) == UC_INTEGER) {
2839                         u64 = ucv_uint64_get(val);
2840                 }
2841                 else {
2842                         s = ucv_to_string(vm, val);
2843 
2844                         if (!s)
2845                                 return nla_parse_error(spec, vm, val, "out of memory");
2846 
2847                         u16 = addr64_pton(s, &u64);
2848 
2849                         free(s);
2850 
2851                         if (u16 != 1)
2852                                 return nla_parse_error(spec, vm, val, "invalid address");
2853                 }
2854 
2855                 nla_put_u64(msg, attr, u64);
2856 
2857                 break;
2858 
2859         case DT_INADDR:
2860         case DT_IN6ADDR:
2861         case DT_MPLSADDR:
2862         case DT_ANYADDR:
2863                 assert(spec->attr != 0);
2864 
2865                 rtg = nlmsg_data(nlmsg_hdr(msg));
2866 
2867                 if (!uc_nl_parse_cidr(vm, val, &cidr))
2868                         return nla_parse_error(spec, vm, val, "invalid IP address");
2869 
2870                 if ((spec->type == DT_INADDR && cidr.family != AF_INET) ||
2871                     (spec->type == DT_IN6ADDR && cidr.family != AF_INET6) ||
2872                     (spec->type == DT_MPLSADDR && cidr.family != AF_MPLS))
2873                     return nla_parse_error(spec, vm, val, "wrong address family");
2874 
2875                 if (spec->flags & DF_STORE_MASK)
2876                         uc_nl_put_struct_member_u8(base, spec->auxdata, cidr.mask);
2877                 else if (cidr.mask != cidr.bitlen)
2878                         return nla_parse_error(spec, vm, val, "address range given but single address expected");
2879 
2880                 nla_put(msg, attr, cidr.alen, &cidr.addr.in6);
2881 
2882                 if ((rtg->rtgen_family == AF_UNSPEC) && (spec->flags & DF_FAMILY_HINT))
2883                         rtg->rtgen_family = cidr.family;
2884 
2885                 break;
2886 
2887         case DT_MULTIPATH:
2888                 if (!uc_nl_parse_rta_multipath(spec, msg, base, vm, val))
2889                         return nla_parse_error(spec, vm, val, "invalid nexthop data");
2890 
2891                 break;
2892 
2893         case DT_NUMRANGE:
2894                 if (!uc_nl_parse_rta_numrange(spec, msg, base, vm, val))
2895                         return false;
2896 
2897                 break;
2898 
2899         case DT_FLAGS:
2900                 if (ucv_array_length(val) == 2) {
2901                         if (ucv_type(ucv_array_get(val, 0)) != UC_INTEGER ||
2902                             ucv_type(ucv_array_get(val, 1)) != UC_INTEGER)
2903                                 return nla_parse_error(spec, vm, val, "flag or mask value not an integer");
2904 
2905                         if (!uc_nl_parse_u32(ucv_array_get(val, 0), &u32))
2906                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
2907 
2908                         memcpy(&u64, &u32, sizeof(u32));
2909 
2910                         if (!uc_nl_parse_u32(ucv_array_get(val, 1), &u32))
2911                                 return nla_parse_error(spec, vm, val, "mask value not an integer or out of range 0-4294967295");
2912 
2913                         memcpy((char *)&u64 + sizeof(u32), &u32, sizeof(u32));
2914                 }
2915                 else if (ucv_type(val) == UC_INTEGER) {
2916                         if (!uc_nl_parse_u32(val, &u32))
2917                                 return nla_parse_error(spec, vm, val, "flag value not an integer or out of range 0-4294967295");
2918 
2919                         memcpy(&u64, &u32, sizeof(u32));
2920                         memset((char *)&u64 + sizeof(u32), 0xff, sizeof(u32));
2921                 }
2922                 else {
2923                         return nla_parse_error(spec, vm, val, "value neither an array of flags, mask nor an integer");
2924                 }
2925 
2926                 if (spec->attr == 0)
2927                         uc_nl_put_struct_member(base, spec->auxdata, sizeof(u64), &u64);
2928                 else
2929                         nla_put_u64(msg, attr, u64);
2930 
2931                 break;
2932 
2933         case DT_LINKINFO:
2934                 if (!uc_nl_parse_rta_linkinfo(spec, msg, base, vm, val))
2935                         return false;
2936 
2937                 break;
2938 
2939         case DT_SRH:
2940                 if (!uc_nl_parse_rta_srh(spec, msg, base, vm, val))
2941                         return false;
2942 
2943                 break;
2944 
2945         case DT_ENCAP:
2946                 if (!uc_nl_parse_rta_encap(spec, msg, base, vm, val))
2947                         return false;
2948 
2949                 break;
2950 
2951         case DT_IPOPTS:
2952                 if (!uc_nl_parse_rta_ipopts(spec, msg, base, vm, val))
2953                         return false;
2954 
2955                 break;
2956 
2957         case DT_AFSPEC:
2958                 if (!uc_nl_parse_rta_afspec(spec, msg, base, vm, val))
2959                         return false;
2960 
2961                 break;
2962 
2963         case DT_U32_OR_MEMBER:
2964                 if (!uc_nl_parse_rta_u32_or_member(spec, msg, base, vm, val))
2965                         return false;
2966 
2967                 break;
2968 
2969         case DT_NESTED:
2970                 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val))
2971                         return false;
2972 
2973                 break;
2974 
2975         default:
2976                 assert(0);
2977         }
2978 
2979         return true;
2980 }
2981 
2982 static uc_value_t *
2983 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm)
2984 {
2985         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 };
2986         struct { uint32_t flags; uint32_t mask; } flags;
2987         char buf[sizeof(struct mpls_label) * 16];
2988         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2989         struct rtgenmsg *rtg = nlmsg_data(hdr);
2990         struct ether_addr *ea;
2991         uc_value_t *v;
2992         char *s;
2993 
2994         switch (spec->type) {
2995         case DT_U8:
2996                 if (spec->attr == 0)
2997                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
2998                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
2999                         t.u8 = nla_get_u8(tb[spec->attr]);
3000 
3001                 return ucv_uint64_new(t.u8);
3002 
3003         case DT_U16:
3004                 if (spec->attr == 0)
3005                         t.u16 = uc_nl_get_struct_member_u16(base, spec->auxdata);
3006                 else if (nla_check_len(tb[spec->attr], sizeof(t.u16)))
3007                         t.u16 = nla_get_u16(tb[spec->attr]);
3008 
3009                 if (spec->flags & DF_BYTESWAP)
3010                         t.u16 = ntohs(t.u16);
3011 
3012                 return ucv_uint64_new(t.u16);
3013 
3014         case DT_U32:
3015         case DT_S32:
3016                 if (spec->attr == 0)
3017                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
3018                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
3019                         t.u32 = nla_get_u32(tb[spec->attr]);
3020 
3021                 if (spec->flags & DF_BYTESWAP)
3022                         t.u32 = ntohl(t.u32);
3023 
3024                 if (spec->type == DT_S32)
3025                         return ucv_int64_new((int32_t)t.u32);
3026 
3027                 return ucv_uint64_new(t.u32);
3028 
3029         case DT_U64:
3030                 if (spec->attr == 0)
3031                         t.u64 = uc_nl_get_struct_member_u64(base, spec->auxdata);
3032                 else if (nla_check_len(tb[spec->attr], sizeof(t.u64)))
3033                         memcpy(&t.u64, nla_data(tb[spec->attr]), sizeof(t.u64));
3034 
3035                 return ucv_uint64_new(t.u64);
3036 
3037         case DT_BOOL:
3038                 if (spec->attr == 0)
3039                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
3040                 else if (nla_check_len(tb[spec->attr], sizeof(t.u8)))
3041                         t.u8 = nla_get_u8(tb[spec->attr]);
3042 
3043                 return ucv_boolean_new(t.u8 != 0);
3044 
3045         case DT_FLAG:
3046                 if (spec->attr == 0)
3047                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
3048                 else if (tb[spec->attr] != NULL)
3049                         t.u8 = 1;
3050 
3051                 return ucv_boolean_new(t.u8 != 0);
3052 
3053         case DT_STRING:
3054                 assert(spec->attr != 0);
3055 
3056                 if (!nla_check_len(tb[spec->attr], 1))
3057                         return NULL;
3058 
3059                 return ucv_string_new_length(
3060                         nla_data(tb[spec->attr]), nla_len(tb[spec->attr]) - 1);
3061 
3062         case DT_NETDEV:
3063                 if (spec->attr == 0)
3064                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
3065                 else if (nla_check_len(tb[spec->attr], sizeof(t.u32)))
3066                         t.u32 = nla_get_u32(tb[spec->attr]);
3067 
3068                 if (if_indextoname(t.u32, buf))
3069                         return ucv_string_new(buf);
3070                 else if (spec->flags & DF_ALLOW_NONE)
3071                         return ucv_int64_new(0);
3072 
3073                 return NULL;
3074 
3075         case DT_LLADDR:
3076                 assert(spec->attr != 0);
3077 
3078                 if (!nla_check_len(tb[spec->attr], sizeof(*ea)))
3079                         return NULL;
3080 
3081                 ea = nla_data(tb[spec->attr]);
3082 
3083                 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
3084                         ea->ether_addr_octet[0], ea->ether_addr_octet[1],
3085                         ea->ether_addr_octet[2], ea->ether_addr_octet[3],
3086                         ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
3087 
3088                 return ucv_string_new(buf);
3089 
3090         case DT_U64ADDR:
3091                 assert(spec->attr != 0);
3092 
3093                 if (!nla_check_len(tb[spec->attr], sizeof(uint64_t)) ||
3094                     !addr64_ntop(nla_data(tb[spec->attr]), buf, sizeof(buf)))
3095                         return NULL;
3096 
3097                 return ucv_string_new(buf);
3098 
3099         case DT_INADDR:
3100         case DT_IN6ADDR:
3101         case DT_MPLSADDR:
3102         case DT_ANYADDR:
3103                 assert(spec->attr != 0);
3104 
3105                 t.sz = (size_t)nla_len(tb[spec->attr]);
3106 
3107                 switch (spec->type) {
3108                 case DT_INADDR:
3109                         if (t.sz < sizeof(struct in_addr) ||
3110                             !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3111                                 return NULL;
3112 
3113                         break;
3114 
3115                 case DT_IN6ADDR:
3116                         if (t.sz < sizeof(struct in6_addr) ||
3117                             !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3118                                 return NULL;
3119 
3120                         break;
3121 
3122                 case DT_MPLSADDR:
3123                         if (t.sz < sizeof(struct mpls_label) ||
3124                             !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
3125                                 return NULL;
3126 
3127                         break;
3128 
3129                 default:
3130                         switch (rtg->rtgen_family) {
3131                         case AF_MPLS:
3132                                 if (t.sz < sizeof(struct mpls_label) ||
3133                                     !mpls_ntop(nla_data(tb[spec->attr]), t.sz, buf, sizeof(buf)))
3134                                         return NULL;
3135 
3136                                 break;
3137 
3138                         case AF_INET6:
3139                                 if (t.sz < sizeof(struct in6_addr) ||
3140                                     !inet_ntop(AF_INET6, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3141                                         return NULL;
3142 
3143                                 break;
3144 
3145                         case AF_INET:
3146                                 if (t.sz < sizeof(struct in_addr) ||
3147                                     !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf)))
3148                                         return NULL;
3149 
3150                                 break;
3151 
3152                         default:
3153                                 return NULL;
3154                         }
3155 
3156                         break;
3157                 }
3158 
3159                 if (spec->flags & DF_STORE_MASK) {
3160                         s = buf + strlen(buf);
3161                         snprintf(s, buf + sizeof(buf) - s, "/%hhu",
3162                                 uc_nl_get_struct_member_u8(base, spec->auxdata));
3163                 }
3164 
3165                 return ucv_string_new(buf);
3166 
3167         case DT_MULTIPATH:
3168                 return uc_nl_convert_rta_multipath(spec, msg, tb, vm);
3169 
3170         case DT_NUMRANGE:
3171                 return uc_nl_convert_rta_numrange(spec, msg, tb, vm);
3172 
3173         case DT_FLAGS:
3174                 if (spec->attr == 0)
3175                         uc_nl_get_struct_member(base, spec->auxdata, sizeof(flags), &flags);
3176                 else if (nla_check_len(tb[spec->attr], sizeof(flags)))
3177                         memcpy(&flags, nla_data(tb[spec->attr]), sizeof(flags));
3178                 else
3179                         return NULL;
3180 
3181                 if (flags.mask == 0)
3182                         return ucv_uint64_new(flags.flags);
3183 
3184                 v = ucv_array_new(vm);
3185 
3186                 ucv_array_push(v, ucv_uint64_new(flags.flags));
3187                 ucv_array_push(v, ucv_uint64_new(flags.mask));
3188 
3189                 return v;
3190 
3191         case DT_LINKINFO:
3192                 return uc_nl_convert_rta_linkinfo(spec, msg, tb, vm);
3193 
3194         case DT_BRIDGEID:
3195                 return uc_nl_convert_rta_bridgeid(spec, msg, tb, vm);
3196 
3197         case DT_SRH:
3198                 return uc_nl_convert_rta_srh(spec, msg, tb, vm);
3199 
3200         case DT_ENCAP:
3201                 return uc_nl_convert_rta_encap(spec, msg, tb, vm);
3202 
3203         case DT_IPOPTS:
3204                 return uc_nl_convert_rta_ipopts(spec, msg, tb, vm);
3205 
3206         case DT_AFSPEC:
3207                 return uc_nl_convert_rta_afspec(spec, msg, tb, vm);
3208 
3209         case DT_U32_OR_MEMBER:
3210                 return uc_nl_convert_rta_u32_or_member(spec, msg, base, tb, vm);
3211 
3212         case DT_NESTED:
3213                 return uc_nl_convert_rta_nested(spec, msg, tb, vm);
3214 
3215         default:
3216                 assert(0);
3217         }
3218 
3219         return NULL;
3220 }
3221 
3222 
3223 static struct nl_sock *sock = NULL;
3224 static struct {
3225         struct nl_sock *evsock;
3226         struct uloop_fd evsock_fd;
3227         uint32_t groups[RTNL_GRPS_BITMAP_SIZE];
3228 } nl_conn;
3229 
3230 typedef enum {
3231         STATE_UNREPLIED,
3232         STATE_CONTINUE,
3233         STATE_REPLIED,
3234         STATE_ERROR
3235 } reply_state_t;
3236 
3237 typedef struct {
3238         reply_state_t state;
3239         uc_vm_t *vm;
3240         uc_value_t *res;
3241         int family;
3242         const uc_nl_nested_spec_t *spec;
3243 } request_state_t;
3244 
3245 
3246 static uc_value_t *
3247 uc_nl_error(uc_vm_t *vm, size_t nargs)
3248 {
3249         uc_stringbuf_t *buf;
3250         const char *s;
3251 
3252         if (last_error.code == 0)
3253                 return NULL;
3254 
3255         buf = ucv_stringbuf_new();
3256 
3257         if (last_error.code == NLE_FAILURE && last_error.msg) {
3258                 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
3259         }
3260         else {
3261                 s = nl_geterror(last_error.code);
3262 
3263                 ucv_stringbuf_addstr(buf, s, strlen(s));
3264 
3265                 if (last_error.msg)
3266                         ucv_stringbuf_printf(buf, ": %s", last_error.msg);
3267         }
3268 
3269         set_error(0, NULL);
3270 
3271         return ucv_stringbuf_finish(buf);
3272 }
3273 
3274 /*
3275  * route functions
3276  */
3277 
3278 static int
3279 cb_done(struct nl_msg *msg, void *arg)
3280 {
3281         request_state_t *s = arg;
3282 
3283         s->state = STATE_REPLIED;
3284 
3285         return NL_STOP;
3286 }
3287 
3288 static int
3289 cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
3290 {
3291         request_state_t *s = arg;
3292         int errnum = err->error;
3293 
3294         set_error(NLE_FAILURE, "RTNETLINK answers: %s",
3295                   strerror(errnum < 0 ? -errnum : errnum));
3296 
3297         s->state = STATE_ERROR;
3298 
3299         return NL_STOP;
3300 }
3301 
3302 static int
3303 cb_reply(struct nl_msg *msg, void *arg)
3304 {
3305         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3306         request_state_t *s = arg;
3307         uc_value_t *o;
3308         bool rv;
3309 
3310         if (RTM_FAM(hdr->nlmsg_type) != s->family)
3311                 return NL_SKIP;
3312 
3313         if (s->spec) {
3314                 if (nlmsg_attrlen(hdr, 0) < (ssize_t)s->spec->headsize)
3315                         return NL_SKIP;
3316 
3317                 o = ucv_object_new(s->vm);
3318 
3319                 rv = uc_nl_convert_attrs(msg,
3320                         nlmsg_attrdata(hdr, 0),
3321                         nlmsg_attrlen(hdr, 0),
3322                         s->spec->headsize,
3323                         s->spec->attrs, s->spec->nattrs, s->vm, o);
3324 
3325                 if (rv) {
3326                         if (hdr->nlmsg_flags & NLM_F_MULTI) {
3327                                 if (!s->res)
3328                                         s->res = ucv_array_new(s->vm);
3329 
3330                                 ucv_array_push(s->res, o);
3331                         }
3332                         else {
3333                                 s->res = o;
3334                         }
3335                 }
3336                 else {
3337                         ucv_put(o);
3338                 }
3339         }
3340 
3341         s->state = STATE_CONTINUE;
3342 
3343         return NL_SKIP;
3344 }
3345 
3346 
3347 static const struct {
3348         int family;
3349         const uc_nl_nested_spec_t *spec;
3350 } rtm_families[] = {
3351         { RTM_FAM(RTM_GETLINK), &link_msg },
3352         { RTM_FAM(RTM_GETROUTE), &route_msg },
3353         { RTM_FAM(RTM_GETNEIGH), &neigh_msg },
3354         { RTM_FAM(RTM_GETADDR), &addr_msg },
3355         { RTM_FAM(RTM_GETRULE), &rule_msg },
3356         { RTM_FAM(RTM_GETADDRLABEL), &addrlabel_msg },
3357         { RTM_FAM(RTM_GETNEIGHTBL), &neightbl_msg },
3358         { RTM_FAM(RTM_GETNETCONF), &netconf_msg },
3359 };
3360 
3361 static uc_value_t *
3362 uc_nl_request(uc_vm_t *vm, size_t nargs)
3363 {
3364         uc_value_t *cmd = uc_fn_arg(0);
3365         uc_value_t *flags = uc_fn_arg(1);
3366         uc_value_t *payload = uc_fn_arg(2);
3367         request_state_t st = { .vm = vm };
3368         uint16_t flagval = 0;
3369         struct nl_msg *msg;
3370         struct nl_cb *cb;
3371         socklen_t optlen;
3372         int enable, err;
3373         void *buf;
3374         size_t i;
3375 
3376         if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 ||
3377             (flags != NULL && ucv_type(flags) != UC_INTEGER) ||
3378             (payload != NULL && ucv_type(payload) != UC_OBJECT))
3379                 err_return(NLE_INVAL, NULL);
3380 
3381         if (flags) {
3382                 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff)
3383                         err_return(NLE_INVAL, NULL);
3384                 else
3385                         flagval = (uint16_t)ucv_int64_get(flags);
3386         }
3387 
3388         for (i = 0; i < ARRAY_SIZE(rtm_families); i++) {
3389                 if (rtm_families[i].family == RTM_FAM(ucv_int64_get(cmd))) {
3390                         st.spec = rtm_families[i].spec;
3391                         st.family = rtm_families[i].family;
3392                         break;
3393                 }
3394         }
3395 
3396         if (!sock) {
3397                 sock = nl_socket_alloc();
3398 
3399                 if (!sock)
3400                         err_return(NLE_NOMEM, NULL);
3401 
3402                 err = nl_connect(sock, NETLINK_ROUTE);
3403 
3404                 if (err != 0)
3405                         err_return(err, NULL);
3406         }
3407 
3408         optlen = sizeof(enable);
3409 
3410         if (getsockopt(sock->s_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &enable, &optlen) < 0)
3411                 enable = 0;
3412 
3413         if (!!(flagval & NLM_F_STRICT_CHK) != enable) {
3414                 enable = !!(flagval & NLM_F_STRICT_CHK);
3415 
3416                 if (setsockopt(sock->s_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &enable, sizeof(enable)) < 0)
3417                         err_return(nl_syserr2nlerr(errno), "Unable to toggle NETLINK_GET_STRICT_CHK");
3418         }
3419 
3420         msg = nlmsg_alloc_simple(ucv_int64_get(cmd), NLM_F_REQUEST | (flagval & ~NLM_F_STRICT_CHK));
3421 
3422         if (!msg)
3423                 err_return(NLE_NOMEM, NULL);
3424 
3425         if (st.spec) {
3426                 if (st.spec->headsize) {
3427                         buf = nlmsg_reserve(msg, st.spec->headsize, 0);
3428 
3429                         if (!buf) {
3430                                 nlmsg_free(msg);
3431 
3432                                 return NULL;
3433                         }
3434 
3435                         memset(buf, 0, st.spec->headsize);
3436                 }
3437 
3438                 if (!uc_nl_parse_attrs(msg, NLMSG_DATA(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) {
3439                         nlmsg_free(msg);
3440 
3441                         return NULL;
3442                 }
3443         }
3444 
3445         cb = nl_cb_alloc(NL_CB_DEFAULT);
3446 
3447         if (!cb) {
3448                 nlmsg_free(msg);
3449                 err_return(NLE_NOMEM, NULL);
3450         }
3451 
3452         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st);
3453         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
3454         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_done, &st);
3455         nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
3456 
3457         nl_send_auto_complete(sock, msg);
3458 
3459         do {
3460                 err = nl_recvmsgs(sock, cb);
3461 
3462                 if (err && st.state != STATE_ERROR) {
3463                         set_error(err, NULL);
3464 
3465                         st.state = STATE_ERROR;
3466                 }
3467         }
3468         while (st.state < STATE_REPLIED);
3469 
3470         nlmsg_free(msg);
3471         nl_cb_put(cb);
3472 
3473         switch (st.state) {
3474         case STATE_REPLIED:
3475                 return st.res;
3476 
3477         case STATE_UNREPLIED:
3478                 return ucv_boolean_new(true);
3479 
3480         case STATE_ERROR:
3481                 return ucv_boolean_new(false);
3482 
3483         default:
3484                 set_error(NLE_FAILURE, "Interrupted reply");
3485 
3486                 return ucv_boolean_new(false);
3487         }
3488 }
3489 
3490 static const uc_nl_nested_spec_t *
3491 uc_nl_msg_spec(int type)
3492 {
3493         switch (type) {
3494         case RTM_NEWLINK:
3495         case RTM_DELLINK:
3496                 return &link_msg;
3497         case RTM_NEWROUTE:
3498         case RTM_DELROUTE:
3499                 return &route_msg;
3500         case RTM_NEWNEIGH:
3501         case RTM_DELNEIGH:
3502                 return &neigh_msg;
3503         case RTM_NEWADDR:
3504         case RTM_DELADDR:
3505                 return &addr_msg;
3506         case RTM_NEWRULE:
3507         case RTM_DELRULE:
3508                 return &rule_msg;
3509         case RTM_NEWADDRLABEL:
3510         case RTM_DELADDRLABEL:
3511                 return &addrlabel_msg;
3512         case RTM_NEWNEIGHTBL:
3513                 return &neightbl_msg;
3514         case RTM_NEWNETCONF:
3515         case RTM_DELNETCONF:
3516                 return &netconf_msg;
3517         default:
3518                 return NULL;
3519         }
3520 }
3521 
3522 static void
3523 uc_nl_prepare_event(uc_vm_t *vm, uc_value_t *dest, struct nl_msg *msg)
3524 {
3525         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3526         const uc_nl_nested_spec_t *spec;
3527         const uc_nl_attr_spec_t *attrs = NULL;
3528         size_t nattrs = 0, headsize = 0;
3529         uc_value_t *o;
3530 
3531         spec = uc_nl_msg_spec(hdr->nlmsg_type);
3532         if (spec) {
3533                 attrs = spec->attrs;
3534                 nattrs = spec->nattrs;
3535                 headsize = spec->headsize;
3536         }
3537 
3538         o = ucv_object_new(vm);
3539         if (!uc_nl_convert_attrs(msg, nlmsg_attrdata(hdr, 0),
3540                 nlmsg_attrlen(hdr, 0), headsize, attrs, nattrs, vm, o)) {
3541                 ucv_put(o);
3542                 return;
3543         }
3544 
3545         ucv_object_add(dest, "msg", o);
3546         if (headsize)
3547                 ucv_object_add(dest, "head", ucv_string_new_length(NLMSG_DATA(hdr), headsize));
3548 }
3549 
3550 static bool
3551 uc_nl_fill_cmds(uint32_t *cmd_bits, uc_value_t *cmds)
3552 {
3553         if (ucv_type(cmds) == UC_ARRAY) {
3554                 for (size_t i = 0; i < ucv_array_length(cmds); i++) {
3555                         int64_t n = ucv_int64_get(ucv_array_get(cmds, i));
3556 
3557                         if (errno || n < 0 || n >= __RTM_MAX)
3558                                 return false;
3559 
3560                         cmd_bits[n / 32] |= (1 << (n % 32));
3561                 }
3562         }
3563         else if (ucv_type(cmds) == UC_INTEGER) {
3564                 int64_t n = ucv_int64_get(cmds);
3565 
3566                 if (errno || n < 0 || n > 255)
3567                         return false;
3568 
3569                 cmd_bits[n / 32] |= (1 << (n % 32));
3570         }
3571         else if (!cmds)
3572                 memset(cmd_bits, 0xff, RTNL_CMDS_BITMAP_SIZE * sizeof(*cmd_bits));
3573         else
3574                 return false;
3575 
3576         return true;
3577 }
3578 
3579 static int
3580 cb_listener_event(struct nl_msg *msg, void *arg)
3581 {
3582         struct nlmsghdr *hdr = nlmsg_hdr(msg);
3583         uc_vm_t *vm = listener_vm;
3584         int cmd = hdr->nlmsg_type;
3585 
3586         if (!nl_conn.evsock_fd.registered || !vm)
3587                 return NL_SKIP;
3588 
3589         for (size_t i = 0; i < ucv_array_length(listener_registry); i += 2) {
3590                 uc_value_t *this = ucv_array_get(listener_registry, i);
3591                 uc_value_t *func = ucv_array_get(listener_registry, i + 1);
3592                 uc_nl_listener_t *l;
3593                 uc_value_t *o;
3594 
3595                 l = ucv_resource_data(this, "rtnl.listener");
3596                 if (!l)
3597                         continue;
3598 
3599                 if (cmd > __RTM_MAX || !(l->cmds[cmd / 32] & (1 << (cmd % 32))))
3600                         continue;
3601 
3602                 if (!ucv_is_callable(func))
3603                         continue;
3604 
3605                 o = ucv_object_new(vm);
3606                 uc_nl_prepare_event(vm, o, msg);
3607                 ucv_object_add(o, "cmd", ucv_int64_new(cmd));
3608 
3609                 uc_vm_stack_push(vm, ucv_get(this));
3610                 uc_vm_stack_push(vm, ucv_get(func));
3611                 uc_vm_stack_push(vm, o);
3612 
3613                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
3614                         uloop_end();
3615                         return NL_STOP;
3616                 }
3617 
3618                 ucv_put(uc_vm_stack_pop(vm));
3619         }
3620 
3621         errno = 0;
3622 
3623         return NL_SKIP;
3624 }
3625 
3626 static void
3627 uc_nl_listener_cb(struct uloop_fd *fd, unsigned int events)
3628 {
3629         while (true) {
3630                 errno = 0;
3631 
3632                 nl_recvmsgs_default(nl_conn.evsock);
3633 
3634                 if (errno != 0) {
3635                         if (errno != EAGAIN && errno != EWOULDBLOCK)
3636                                 set_error(errno, NULL);
3637 
3638                         break;
3639                 }
3640         }
3641 }
3642 
3643 static void
3644 uc_nl_add_group(unsigned int idx)
3645 {
3646         if (idx >= __RTNLGRP_MAX)
3647                 return;
3648 
3649         if (nl_conn.groups[idx / 32] & (1 << (idx % 32)))
3650                 return;
3651 
3652         nl_conn.groups[idx / 32] |= (1 << (idx % 32));
3653         nl_socket_add_membership(nl_conn.evsock, idx);
3654 }
3655 
3656 static bool
3657 uc_nl_evsock_init(void)
3658 {
3659         struct uloop_fd *fd = &nl_conn.evsock_fd;
3660         struct nl_sock *sock;
3661 
3662         if (nl_conn.evsock)
3663                 return true;
3664 
3665         sock = nl_socket_alloc();
3666 
3667         if (nl_connect(sock, NETLINK_ROUTE))
3668                 goto free;
3669 
3670         fd->fd = nl_socket_get_fd(sock);
3671         fd->cb = uc_nl_listener_cb;
3672         uloop_fd_add(fd, ULOOP_READ);
3673 
3674         nl_socket_set_buffer_size(sock, 1024 * 1024, 0);
3675         nl_socket_disable_seq_check(sock);
3676         nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, cb_listener_event, NULL);
3677 
3678         nl_conn.evsock = sock;
3679 
3680         return true;
3681 
3682 free:
3683         nl_socket_free(sock);
3684         return false;
3685 }
3686 
3687 static uc_value_t *
3688 uc_nl_listener(uc_vm_t *vm, size_t nargs)
3689 {
3690         uc_nl_listener_t *l;
3691         uc_value_t *cb_func = uc_fn_arg(0);
3692         uc_value_t *cmds = uc_fn_arg(1);
3693         uc_value_t *groups = uc_fn_arg(2);
3694         uc_value_t *rv;
3695         size_t i;
3696 
3697         if (!ucv_is_callable(cb_func)) {
3698                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid callback");
3699                 return NULL;
3700         }
3701 
3702         if (!uc_nl_evsock_init())
3703                 return NULL;
3704 
3705         if (ucv_type(groups) == UC_ARRAY) {
3706                 for (i = 0; i < ucv_array_length(groups); i++) {
3707                         int64_t n = ucv_int64_get(ucv_array_get(groups, i));
3708 
3709                         if (errno || n < 0 || n >= __RTNLGRP_MAX)
3710                                 err_return(NLE_INVAL, NULL);
3711 
3712                         uc_nl_add_group(n);
3713                 }
3714         } else {
3715                 uc_nl_add_group(RTNLGRP_LINK);
3716         }
3717 
3718         for (i = 0; i < ucv_array_length(listener_registry); i += 2) {
3719                 if (!ucv_array_get(listener_registry, i))
3720                         break;
3721         }
3722 
3723         l = xalloc(sizeof(*l));
3724         l->index = i;
3725 
3726         if (!uc_nl_fill_cmds(l->cmds, cmds)) {
3727                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
3728                 free(l);
3729                 return NULL;
3730         }
3731 
3732         rv = uc_resource_new(listener_type, l);
3733 
3734         ucv_array_set(listener_registry, i, ucv_get(rv));
3735         ucv_array_set(listener_registry, i + 1, ucv_get(cb_func));
3736 
3737         listener_vm = vm;
3738 
3739         return rv;
3740 }
3741 
3742 static void
3743 uc_nl_listener_free(void *arg)
3744 {
3745         uc_nl_listener_t *l = arg;
3746 
3747         ucv_array_set(listener_registry, l->index, NULL);
3748         ucv_array_set(listener_registry, l->index + 1, NULL);
3749         free(l);
3750 }
3751 
3752 static uc_value_t *
3753 uc_nl_listener_set_commands(uc_vm_t *vm, size_t nargs)
3754 {
3755         uc_nl_listener_t *l = uc_fn_thisval("rtnl.listener");
3756         uc_value_t *cmds = uc_fn_arg(0);
3757 
3758         if (!l)
3759                 return NULL;
3760 
3761         memset(l->cmds, 0, sizeof(l->cmds));
3762         if (!uc_nl_fill_cmds(l->cmds, cmds))
3763                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
3764 
3765         return NULL;
3766 }
3767 
3768 static uc_value_t *
3769 uc_nl_listener_close(uc_vm_t *vm, size_t nargs)
3770 {
3771         uc_nl_listener_t **lptr = uc_fn_this("rtnl.listener");
3772         uc_nl_listener_t *l;
3773 
3774         if (!lptr)
3775                 return NULL;
3776 
3777         l = *lptr;
3778         if (!l)
3779                 return NULL;
3780 
3781         *lptr = NULL;
3782         uc_nl_listener_free(l);
3783 
3784         return NULL;
3785 }
3786 
3787 
3788 static void
3789 register_constants(uc_vm_t *vm, uc_value_t *scope)
3790 {
3791         uc_value_t *c = ucv_object_new(vm);
3792 
3793 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x))
3794 
3795         ADD_CONST(NLM_F_ACK);
3796         ADD_CONST(NLM_F_ACK_TLVS);
3797         ADD_CONST(NLM_F_APPEND);
3798         ADD_CONST(NLM_F_ATOMIC);
3799         ADD_CONST(NLM_F_CAPPED);
3800         ADD_CONST(NLM_F_CREATE);
3801         ADD_CONST(NLM_F_DUMP);
3802         ADD_CONST(NLM_F_DUMP_FILTERED);
3803         ADD_CONST(NLM_F_DUMP_INTR);
3804         ADD_CONST(NLM_F_ECHO);
3805         ADD_CONST(NLM_F_EXCL);
3806         ADD_CONST(NLM_F_MATCH);
3807         ADD_CONST(NLM_F_MULTI);
3808         ADD_CONST(NLM_F_NONREC);
3809         ADD_CONST(NLM_F_REPLACE);
3810         ADD_CONST(NLM_F_REQUEST);
3811         ADD_CONST(NLM_F_ROOT);
3812         ADD_CONST(NLM_F_STRICT_CHK); /* custom */
3813 
3814         ADD_CONST(IN6_ADDR_GEN_MODE_EUI64);
3815         ADD_CONST(IN6_ADDR_GEN_MODE_NONE);
3816         ADD_CONST(IN6_ADDR_GEN_MODE_STABLE_PRIVACY);
3817         ADD_CONST(IN6_ADDR_GEN_MODE_RANDOM);
3818 
3819         ADD_CONST(BRIDGE_MODE_UNSPEC);
3820         ADD_CONST(BRIDGE_MODE_HAIRPIN);
3821 
3822         ADD_CONST(MACVLAN_MODE_PRIVATE);
3823         ADD_CONST(MACVLAN_MODE_VEPA);
3824         ADD_CONST(MACVLAN_MODE_BRIDGE);
3825         ADD_CONST(MACVLAN_MODE_PASSTHRU);
3826         ADD_CONST(MACVLAN_MODE_SOURCE);
3827 
3828         ADD_CONST(MACVLAN_MACADDR_ADD);
3829         ADD_CONST(MACVLAN_MACADDR_DEL);
3830         ADD_CONST(MACVLAN_MACADDR_FLUSH);
3831         ADD_CONST(MACVLAN_MACADDR_SET);
3832 
3833         ADD_CONST(MACSEC_VALIDATE_DISABLED);
3834         ADD_CONST(MACSEC_VALIDATE_CHECK);
3835         ADD_CONST(MACSEC_VALIDATE_STRICT);
3836         ADD_CONST(MACSEC_VALIDATE_MAX);
3837 
3838         ADD_CONST(MACSEC_OFFLOAD_OFF);
3839         ADD_CONST(MACSEC_OFFLOAD_PHY);
3840         ADD_CONST(MACSEC_OFFLOAD_MAC);
3841         ADD_CONST(MACSEC_OFFLOAD_MAX);
3842 
3843         ADD_CONST(IPVLAN_MODE_L2);
3844         ADD_CONST(IPVLAN_MODE_L3);
3845         ADD_CONST(IPVLAN_MODE_L3S);
3846 
3847         ADD_CONST(VXLAN_DF_UNSET);
3848         ADD_CONST(VXLAN_DF_SET);
3849         ADD_CONST(VXLAN_DF_INHERIT);
3850         ADD_CONST(VXLAN_DF_MAX);
3851 
3852         ADD_CONST(GENEVE_DF_UNSET);
3853         ADD_CONST(GENEVE_DF_SET);
3854         ADD_CONST(GENEVE_DF_INHERIT);
3855         ADD_CONST(GENEVE_DF_MAX);
3856 
3857         ADD_CONST(GTP_ROLE_GGSN);
3858         ADD_CONST(GTP_ROLE_SGSN);
3859 
3860         ADD_CONST(PORT_REQUEST_PREASSOCIATE);
3861         ADD_CONST(PORT_REQUEST_PREASSOCIATE_RR);
3862         ADD_CONST(PORT_REQUEST_ASSOCIATE);
3863         ADD_CONST(PORT_REQUEST_DISASSOCIATE);
3864 
3865         ADD_CONST(PORT_VDP_RESPONSE_SUCCESS);
3866         ADD_CONST(PORT_VDP_RESPONSE_INVALID_FORMAT);
3867         ADD_CONST(PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES);
3868         ADD_CONST(PORT_VDP_RESPONSE_UNUSED_VTID);
3869         ADD_CONST(PORT_VDP_RESPONSE_VTID_VIOLATION);
3870         ADD_CONST(PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION);
3871         ADD_CONST(PORT_VDP_RESPONSE_OUT_OF_SYNC);
3872         ADD_CONST(PORT_PROFILE_RESPONSE_SUCCESS);
3873         ADD_CONST(PORT_PROFILE_RESPONSE_INPROGRESS);
3874         ADD_CONST(PORT_PROFILE_RESPONSE_INVALID);
3875         ADD_CONST(PORT_PROFILE_RESPONSE_BADSTATE);
3876         ADD_CONST(PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES);
3877         ADD_CONST(PORT_PROFILE_RESPONSE_ERROR);
3878 
3879         ADD_CONST(IPOIB_MODE_DATAGRAM);
3880         ADD_CONST(IPOIB_MODE_CONNECTED);
3881 
3882         ADD_CONST(HSR_PROTOCOL_HSR);
3883         ADD_CONST(HSR_PROTOCOL_PRP);
3884 
3885         ADD_CONST(LINK_XSTATS_TYPE_UNSPEC);
3886         ADD_CONST(LINK_XSTATS_TYPE_BRIDGE);
3887         ADD_CONST(LINK_XSTATS_TYPE_BOND);
3888 
3889         ADD_CONST(XDP_ATTACHED_NONE);
3890         ADD_CONST(XDP_ATTACHED_DRV);
3891         ADD_CONST(XDP_ATTACHED_SKB);
3892         ADD_CONST(XDP_ATTACHED_HW);
3893         ADD_CONST(XDP_ATTACHED_MULTI);
3894 
3895         ADD_CONST(FDB_NOTIFY_BIT);
3896         ADD_CONST(FDB_NOTIFY_INACTIVE_BIT);
3897 
3898         ADD_CONST(RTM_BASE);
3899         ADD_CONST(RTM_NEWLINK);
3900         ADD_CONST(RTM_DELLINK);
3901         ADD_CONST(RTM_GETLINK);
3902         ADD_CONST(RTM_SETLINK);
3903         ADD_CONST(RTM_NEWADDR);
3904         ADD_CONST(RTM_DELADDR);
3905         ADD_CONST(RTM_GETADDR);
3906         ADD_CONST(RTM_NEWROUTE);
3907         ADD_CONST(RTM_DELROUTE);
3908         ADD_CONST(RTM_GETROUTE);
3909         ADD_CONST(RTM_NEWNEIGH);
3910         ADD_CONST(RTM_DELNEIGH);
3911         ADD_CONST(RTM_GETNEIGH);
3912         ADD_CONST(RTM_NEWRULE);
3913         ADD_CONST(RTM_DELRULE);
3914         ADD_CONST(RTM_GETRULE);
3915         ADD_CONST(RTM_NEWQDISC);
3916         ADD_CONST(RTM_DELQDISC);
3917         ADD_CONST(RTM_GETQDISC);
3918         ADD_CONST(RTM_NEWTCLASS);
3919         ADD_CONST(RTM_DELTCLASS);
3920         ADD_CONST(RTM_GETTCLASS);
3921         ADD_CONST(RTM_NEWTFILTER);
3922         ADD_CONST(RTM_DELTFILTER);
3923         ADD_CONST(RTM_GETTFILTER);
3924         ADD_CONST(RTM_NEWACTION);
3925         ADD_CONST(RTM_DELACTION);
3926         ADD_CONST(RTM_GETACTION);
3927         ADD_CONST(RTM_NEWPREFIX);
3928         ADD_CONST(RTM_GETMULTICAST);
3929         ADD_CONST(RTM_GETANYCAST);
3930         ADD_CONST(RTM_NEWNEIGHTBL);
3931         ADD_CONST(RTM_GETNEIGHTBL);
3932         ADD_CONST(RTM_SETNEIGHTBL);
3933         ADD_CONST(RTM_NEWNDUSEROPT);
3934         ADD_CONST(RTM_NEWADDRLABEL);
3935         ADD_CONST(RTM_DELADDRLABEL);
3936         ADD_CONST(RTM_GETADDRLABEL);
3937         ADD_CONST(RTM_GETDCB);
3938         ADD_CONST(RTM_SETDCB);
3939         ADD_CONST(RTM_NEWNETCONF);
3940         ADD_CONST(RTM_DELNETCONF);
3941         ADD_CONST(RTM_GETNETCONF);
3942         ADD_CONST(RTM_NEWMDB);
3943         ADD_CONST(RTM_DELMDB);
3944         ADD_CONST(RTM_GETMDB);
3945         ADD_CONST(RTM_NEWNSID);
3946         ADD_CONST(RTM_DELNSID);
3947         ADD_CONST(RTM_GETNSID);
3948         ADD_CONST(RTM_NEWSTATS);
3949         ADD_CONST(RTM_GETSTATS);
3950         ADD_CONST(RTM_NEWCACHEREPORT);
3951         ADD_CONST(RTM_NEWCHAIN);
3952         ADD_CONST(RTM_DELCHAIN);
3953         ADD_CONST(RTM_GETCHAIN);
3954         ADD_CONST(RTM_NEWNEXTHOP);
3955         ADD_CONST(RTM_DELNEXTHOP);
3956         ADD_CONST(RTM_GETNEXTHOP);
3957         ADD_CONST(RTM_NEWLINKPROP);
3958         ADD_CONST(RTM_DELLINKPROP);
3959         ADD_CONST(RTM_GETLINKPROP);
3960         ADD_CONST(RTM_NEWVLAN);
3961         ADD_CONST(RTM_DELVLAN);
3962         ADD_CONST(RTM_GETVLAN);
3963 
3964         ADD_CONST(RTN_UNSPEC);
3965         ADD_CONST(RTN_UNICAST);
3966         ADD_CONST(RTN_LOCAL);
3967         ADD_CONST(RTN_BROADCAST);
3968         ADD_CONST(RTN_ANYCAST);
3969         ADD_CONST(RTN_MULTICAST);
3970         ADD_CONST(RTN_BLACKHOLE);
3971         ADD_CONST(RTN_UNREACHABLE);
3972         ADD_CONST(RTN_PROHIBIT);
3973         ADD_CONST(RTN_THROW);
3974         ADD_CONST(RTN_NAT);
3975         ADD_CONST(RTN_XRESOLVE);
3976 
3977         ADD_CONST(RT_SCOPE_UNIVERSE);
3978         ADD_CONST(RT_SCOPE_SITE);
3979         ADD_CONST(RT_SCOPE_LINK);
3980         ADD_CONST(RT_SCOPE_HOST);
3981         ADD_CONST(RT_SCOPE_NOWHERE);
3982 
3983         ADD_CONST(RT_TABLE_UNSPEC);
3984         ADD_CONST(RT_TABLE_COMPAT);
3985         ADD_CONST(RT_TABLE_DEFAULT);
3986         ADD_CONST(RT_TABLE_MAIN);
3987         ADD_CONST(RT_TABLE_LOCAL);
3988         ADD_CONST(RT_TABLE_MAX);
3989 
3990         /* required to construct RTAX_LOCK */
3991         ADD_CONST(RTAX_MTU);
3992         ADD_CONST(RTAX_HOPLIMIT);
3993         ADD_CONST(RTAX_ADVMSS);
3994         ADD_CONST(RTAX_REORDERING);
3995         ADD_CONST(RTAX_RTT);
3996         ADD_CONST(RTAX_WINDOW);
3997         ADD_CONST(RTAX_CWND);
3998         ADD_CONST(RTAX_INITCWND);
3999         ADD_CONST(RTAX_INITRWND);
4000         ADD_CONST(RTAX_FEATURES);
4001         ADD_CONST(RTAX_QUICKACK);
4002         ADD_CONST(RTAX_CC_ALGO);
4003         ADD_CONST(RTAX_RTTVAR);
4004         ADD_CONST(RTAX_SSTHRESH);
4005         ADD_CONST(RTAX_FASTOPEN_NO_COOKIE);
4006 
4007         ADD_CONST(PREFIX_UNSPEC);
4008         ADD_CONST(PREFIX_ADDRESS);
4009         ADD_CONST(PREFIX_CACHEINFO);
4010 
4011         ADD_CONST(NDUSEROPT_UNSPEC);
4012         ADD_CONST(NDUSEROPT_SRCADDR);
4013 
4014         ADD_CONST(RTNLGRP_NONE);
4015         ADD_CONST(RTNLGRP_LINK);
4016         ADD_CONST(RTNLGRP_NOTIFY);
4017         ADD_CONST(RTNLGRP_NEIGH);
4018         ADD_CONST(RTNLGRP_TC);
4019         ADD_CONST(RTNLGRP_IPV4_IFADDR);
4020         ADD_CONST(RTNLGRP_IPV4_MROUTE);
4021         ADD_CONST(RTNLGRP_IPV4_ROUTE);
4022         ADD_CONST(RTNLGRP_IPV4_RULE);
4023         ADD_CONST(RTNLGRP_IPV6_IFADDR);
4024         ADD_CONST(RTNLGRP_IPV6_MROUTE);
4025         ADD_CONST(RTNLGRP_IPV6_ROUTE);
4026         ADD_CONST(RTNLGRP_IPV6_IFINFO);
4027         ADD_CONST(RTNLGRP_DECnet_IFADDR);
4028         ADD_CONST(RTNLGRP_NOP2);
4029         ADD_CONST(RTNLGRP_DECnet_ROUTE);
4030         ADD_CONST(RTNLGRP_DECnet_RULE);
4031         ADD_CONST(RTNLGRP_NOP4);
4032         ADD_CONST(RTNLGRP_IPV6_PREFIX);
4033         ADD_CONST(RTNLGRP_IPV6_RULE);
4034         ADD_CONST(RTNLGRP_ND_USEROPT);
4035         ADD_CONST(RTNLGRP_PHONET_IFADDR);
4036         ADD_CONST(RTNLGRP_PHONET_ROUTE);
4037         ADD_CONST(RTNLGRP_DCB);
4038         ADD_CONST(RTNLGRP_IPV4_NETCONF);
4039         ADD_CONST(RTNLGRP_IPV6_NETCONF);
4040         ADD_CONST(RTNLGRP_MDB);
4041         ADD_CONST(RTNLGRP_MPLS_ROUTE);
4042         ADD_CONST(RTNLGRP_NSID);
4043         ADD_CONST(RTNLGRP_MPLS_NETCONF);
4044         ADD_CONST(RTNLGRP_IPV4_MROUTE_R);
4045         ADD_CONST(RTNLGRP_IPV6_MROUTE_R);
4046         ADD_CONST(RTNLGRP_NEXTHOP);
4047         ADD_CONST(RTNLGRP_BRVLAN);
4048 
4049         ADD_CONST(RTM_F_CLONED);
4050         ADD_CONST(RTM_F_EQUALIZE);
4051         ADD_CONST(RTM_F_FIB_MATCH);
4052         ADD_CONST(RTM_F_LOOKUP_TABLE);
4053         ADD_CONST(RTM_F_NOTIFY);
4054         ADD_CONST(RTM_F_PREFIX);
4055 
4056         ADD_CONST(AF_UNSPEC);
4057         ADD_CONST(AF_INET);
4058         ADD_CONST(AF_INET6);
4059         ADD_CONST(AF_MPLS);
4060         ADD_CONST(AF_BRIDGE);
4061 
4062         ADD_CONST(GRE_CSUM);
4063         ADD_CONST(GRE_ROUTING);
4064         ADD_CONST(GRE_KEY);
4065         ADD_CONST(GRE_SEQ);
4066         ADD_CONST(GRE_STRICT);
4067         ADD_CONST(GRE_REC);
4068         ADD_CONST(GRE_ACK);
4069 
4070         ADD_CONST(TUNNEL_ENCAP_NONE);
4071         ADD_CONST(TUNNEL_ENCAP_FOU);
4072         ADD_CONST(TUNNEL_ENCAP_GUE);
4073         ADD_CONST(TUNNEL_ENCAP_MPLS);
4074 
4075         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM);
4076         ADD_CONST(TUNNEL_ENCAP_FLAG_CSUM6);
4077         ADD_CONST(TUNNEL_ENCAP_FLAG_REMCSUM);
4078 
4079         ADD_CONST(IP6_TNL_F_ALLOW_LOCAL_REMOTE);
4080         ADD_CONST(IP6_TNL_F_IGN_ENCAP_LIMIT);
4081         ADD_CONST(IP6_TNL_F_MIP6_DEV);
4082         ADD_CONST(IP6_TNL_F_RCV_DSCP_COPY);
4083         ADD_CONST(IP6_TNL_F_USE_ORIG_FLOWLABEL);
4084         ADD_CONST(IP6_TNL_F_USE_ORIG_FWMARK);
4085         ADD_CONST(IP6_TNL_F_USE_ORIG_TCLASS);
4086 
4087         ADD_CONST(NTF_EXT_LEARNED);
4088         ADD_CONST(NTF_MASTER);
4089         ADD_CONST(NTF_OFFLOADED);
4090         ADD_CONST(NTF_PROXY);
4091         ADD_CONST(NTF_ROUTER);
4092         ADD_CONST(NTF_SELF);
4093         ADD_CONST(NTF_STICKY);
4094         ADD_CONST(NTF_USE);
4095 
4096         ADD_CONST(NUD_DELAY);
4097         ADD_CONST(NUD_FAILED);
4098         ADD_CONST(NUD_INCOMPLETE);
4099         ADD_CONST(NUD_NOARP);
4100         ADD_CONST(NUD_NONE);
4101         ADD_CONST(NUD_PERMANENT);
4102         ADD_CONST(NUD_PROBE);
4103         ADD_CONST(NUD_REACHABLE);
4104         ADD_CONST(NUD_STALE);
4105 
4106         ADD_CONST(IFA_F_DADFAILED);
4107         ADD_CONST(IFA_F_DEPRECATED);
4108         ADD_CONST(IFA_F_HOMEADDRESS);
4109         ADD_CONST(IFA_F_MANAGETEMPADDR);
4110         ADD_CONST(IFA_F_MCAUTOJOIN);
4111         ADD_CONST(IFA_F_NODAD);
4112         ADD_CONST(IFA_F_NOPREFIXROUTE);
4113         ADD_CONST(IFA_F_OPTIMISTIC);
4114         ADD_CONST(IFA_F_PERMANENT);
4115         ADD_CONST(IFA_F_SECONDARY);
4116         ADD_CONST(IFA_F_STABLE_PRIVACY);
4117         ADD_CONST(IFA_F_TEMPORARY);
4118         ADD_CONST(IFA_F_TENTATIVE);
4119 
4120         ADD_CONST(FIB_RULE_PERMANENT);
4121         ADD_CONST(FIB_RULE_INVERT);
4122         ADD_CONST(FIB_RULE_UNRESOLVED);
4123         ADD_CONST(FIB_RULE_IIF_DETACHED);
4124         ADD_CONST(FIB_RULE_DEV_DETACHED);
4125         ADD_CONST(FIB_RULE_OIF_DETACHED);
4126 
4127         ADD_CONST(FR_ACT_TO_TBL);
4128         ADD_CONST(FR_ACT_GOTO);
4129         ADD_CONST(FR_ACT_NOP);
4130         ADD_CONST(FR_ACT_BLACKHOLE);
4131         ADD_CONST(FR_ACT_UNREACHABLE);
4132         ADD_CONST(FR_ACT_PROHIBIT);
4133 
4134         ADD_CONST(NETCONFA_IFINDEX_ALL);
4135         ADD_CONST(NETCONFA_IFINDEX_DEFAULT);
4136 
4137         ADD_CONST(BRIDGE_FLAGS_MASTER);
4138         ADD_CONST(BRIDGE_FLAGS_SELF);
4139 
4140         ADD_CONST(BRIDGE_MODE_VEB);
4141         ADD_CONST(BRIDGE_MODE_VEPA);
4142         ADD_CONST(BRIDGE_MODE_UNDEF);
4143 
4144         ADD_CONST(BRIDGE_VLAN_INFO_MASTER);
4145         ADD_CONST(BRIDGE_VLAN_INFO_PVID);
4146         ADD_CONST(BRIDGE_VLAN_INFO_UNTAGGED);
4147         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_BEGIN);
4148         ADD_CONST(BRIDGE_VLAN_INFO_RANGE_END);
4149         ADD_CONST(BRIDGE_VLAN_INFO_BRENTRY);
4150 
4151         ucv_object_add(scope, "const", c);
4152 };
4153 
4154 static const uc_function_list_t global_fns[] = {
4155         { "error",              uc_nl_error },
4156         { "request",    uc_nl_request },
4157         { "listener",   uc_nl_listener },
4158 };
4159 
4160 static const uc_function_list_t listener_fns[] = {
4161         { "set_commands",       uc_nl_listener_set_commands },
4162         { "close",                      uc_nl_listener_close },
4163 };
4164 
4165 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
4166 {
4167         uc_function_list_register(scope, global_fns);
4168 
4169         listener_type = uc_type_declare(vm, "rtnl.listener", listener_fns, uc_nl_listener_free);
4170         listener_registry = ucv_array_new(vm);
4171 
4172         uc_vm_registry_set(vm, "rtnl.registry", listener_registry);
4173 
4174         register_constants(vm, scope);
4175 }
4176 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt