reimplement if_nameindex and getifaddrs using netlink
authorTimo Teräs <timo.teras@iki.fi>
Tue, 8 Apr 2014 14:03:16 +0000 (14:03 +0000)
committerRich Felker <dalias@aerifal.cx>
Wed, 30 Jul 2014 00:57:31 +0000 (20:57 -0400)
the previous implementations had several deficiencies, the most severe
of which was the inability to report unconfigured interfaces or
interfaces without ipv4 addresses. among the options discussed for
fixing this, using netlink turned out to be the one with the least
cost and most additional advantages. other improvements include:

if_nameindex now avoids duplicates in the list it produces, but still
includes legacy-style interface aliases if any are in use.

getifaddrs now reports hardware addresses and includes the scope_id
for link-local ipv6 addresses in the resulting address.

src/network/getifaddrs.c
src/network/if_nameindex.c
src/network/netlink.c [new file with mode: 0644]
src/network/netlink.h [new file with mode: 0644]

index 5a94cc7c4496fe4edda2938dc672c9a3301d0552..89a8f729d9ba5e5df71ca7d9bf092e9de0302c4b 100644 (file)
-/* (C) 2013 John Spencer. released under musl's standard MIT license. */
-#undef _GNU_SOURCE
 #define _GNU_SOURCE
-#include <ifaddrs.h>
-#include <stdlib.h>
-#include <net/if.h> /* IFNAMSIZ, ifreq, ifconf */
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
 #include <errno.h>
-#include <arpa/inet.h> /* inet_pton */
+#include <string.h>
+#include <stdlib.h>
 #include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <syscall.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include "netlink.h"
 
-typedef union {
-       struct sockaddr_in6 v6;
+#define IFADDRS_HASH_SIZE 64
+
+/* getifaddrs() reports hardware addresses with PF_PACKET that implies
+ * struct sockaddr_ll.  But e.g. Infiniband socket address length is
+ * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
+ * to extend ssl_addr - callers should be able to still use it. */
+struct sockaddr_ll_hack {
+       unsigned short sll_family, sll_protocol;
+       int sll_ifindex;
+       unsigned short sll_hatype;
+       unsigned char sll_pkttype, sll_halen;
+       unsigned char sll_addr[24];
+};
+
+union sockany {
+       struct sockaddr sa;
+       struct sockaddr_ll_hack ll;
        struct sockaddr_in v4;
-} soa;
+       struct sockaddr_in6 v6;
+};
 
-typedef struct ifaddrs_storage {
+struct ifaddrs_storage {
        struct ifaddrs ifa;
-       soa addr;
-       soa netmask;
-       soa dst;
+       struct ifaddrs_storage *hash_next;
+       union sockany addr, netmask, ifu;
+       unsigned int index;
        char name[IFNAMSIZ+1];
-} stor;
-#define next ifa.ifa_next
+};
 
-static stor* list_add(stor** list, stor** head, char* ifname)
+struct ifaddrs_ctx {
+       struct ifaddrs_storage *first;
+       struct ifaddrs_storage *last;
+       struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
+};
+
+void freeifaddrs(struct ifaddrs *ifp)
 {
-       stor* curr = calloc(1, sizeof(stor));
-       if(curr) {
-               strcpy(curr->name, ifname);
-               curr->ifa.ifa_name = curr->name;
-               if(*head) (*head)->next = (struct ifaddrs*) curr;
-               *head = curr;
-               if(!*list) *list = curr;
+       struct ifaddrs *n;
+       while (ifp) {
+               n = ifp->ifa_next;
+               free(ifp);
+               ifp = n;
        }
-       return curr;
 }
 
-void freeifaddrs(struct ifaddrs *ifp)
+static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex)
 {
-       stor *head = (stor *) ifp;
-       while(head) {
-               void *p = head;
-               head = (stor *) head->next;
-               free(p);
+       uint8_t *dst;
+       int len;
+
+       switch (af) {
+       case AF_INET:
+               dst = (uint8_t*) &sa->v4.sin_addr;
+               len = 4;
+               break;
+       case AF_INET6:
+               dst = (uint8_t*) &sa->v6.sin6_addr;
+               len = 16;
+               if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
+                       sa->v6.sin6_scope_id = ifindex;
+               break;
+       default:
+               return;
        }
+       if (addrlen < len) return;
+       sa->sa.sa_family = af;
+       memcpy(dst, addr, len);
+       *r = &sa->sa;
 }
 
-static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
+static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen)
 {
-       unsigned char* hb = sa->sin6_addr.s6_addr;
-       unsigned onebytes = prefix_length / 8;
-       unsigned bits = prefix_length % 8;
-       unsigned nullbytes = 16 - onebytes;
-       memset(hb, -1, onebytes);
-       memset(hb+onebytes, 0, nullbytes);
-       if(bits) {
-               unsigned char x = -1;
-               x <<= 8 - bits;
-               hb[onebytes] = x;
-       }
+       uint8_t addr[16] = {0};
+       int i;
+
+       if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr);
+       i = prefixlen / 8;
+       memset(addr, 0xff, i);
+       if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8));
+       copy_addr(r, af, sa, addr, sizeof(addr), 0);
 }
 
-static void dealwithipv6(stor **list, stor** head)
+static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype)
 {
-       FILE* f = fopen("/proc/net/if_inet6", "rbe");
-       /* 00000000000000000000000000000001 01 80 10 80 lo
-          A                                B  C  D  E  F
-          all numbers in hex
-          A = addr B=netlink device#, C=prefix length,
-          D = scope value (ipv6.h) E = interface flags (rnetlink.h, addrconf.c)
-          F = if name */
-       char v6conv[32 + 7 + 1], *v6;
-       char *line, linebuf[512];
-       if(!f) return;
-       while((line = fgets(linebuf, sizeof linebuf, f))) {
-               v6 = v6conv;
-               size_t i = 0;
-               for(; i < 8; i++) {
-                       memcpy(v6, line, 4);
-                       v6+=4;
-                       *v6++=':';
-                       line+=4;
-               }
-               --v6; *v6 = 0;
-               line++;
-               unsigned b, c, d, e;
-               char name[IFNAMSIZ+1];
-               if(5 == sscanf(line, "%x %x %x %x %s", &b, &c, &d, &e, name)) {
-                       struct sockaddr_in6 sa = {0};
-                       if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) {
-                               sa.sin6_family = AF_INET6;
-                               stor* curr = list_add(list, head, name);
-                               if(!curr) goto out;
-                               curr->addr.v6 = sa;
-                               curr->ifa.ifa_addr = (struct sockaddr*) &curr->addr;
-                               ipv6netmask(c, &sa);
-                               curr->netmask.v6 = sa;
-                               curr->ifa.ifa_netmask = (struct sockaddr*) &curr->netmask;
-                               /* find ipv4 struct with the same interface name to copy flags */
-                               stor* scan = *list;
-                               for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next);
-                               if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags;
-                               else curr->ifa.ifa_flags = 0;
-                       } else errno = 0;
-               }
-       }
-       out:
-       fclose(f);
+       if (addrlen > sizeof(sa->ll.sll_addr)) return;
+       sa->ll.sll_family = AF_PACKET;
+       sa->ll.sll_ifindex = ifindex;
+       sa->ll.sll_hatype = hatype;
+       sa->ll.sll_halen = addrlen;
+       memcpy(sa->ll.sll_addr, addr, addrlen);
+       *r = &sa->sa;
 }
 
-int getifaddrs(struct ifaddrs **ifap)
+static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
 {
-       stor *list = 0, *head = 0;
-       struct if_nameindex* ii = if_nameindex();
-       if(!ii) return -1;
-       size_t i;
-       for(i = 0; ii[i].if_index || ii[i].if_name; i++) {
-               stor* curr = list_add(&list, &head, ii[i].if_name);
-               if(!curr) {
-                       if_freenameindex(ii);
-                       goto err2;
+       struct ifaddrs_ctx *ctx = pctx;
+       struct ifaddrs_storage *ifs, *ifs0;
+       struct ifinfomsg *ifi = NLMSG_DATA(h);
+       struct ifaddrmsg *ifa = NLMSG_DATA(h);
+       struct rtattr *rta;
+       int stats_len = 0;
+
+       if (h->nlmsg_type == RTM_NEWLINK) {
+               for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+                       if (rta->rta_type != IFLA_STATS) continue;
+                       stats_len = RTA_DATALEN(rta);
+                       break;
                }
+       } else {
+               for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next)
+                       if (ifs0->index == ifa->ifa_index)
+                               break;
+               if (!ifs0) return 0;
        }
-       if_freenameindex(ii);
-
-       int sock = socket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP);
-       if(sock == -1) goto err2;
-       struct ifreq reqs[32]; /* arbitrary chosen boundary */
-       struct ifconf conf = {.ifc_len = sizeof reqs, .ifc_req = reqs};
-       if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
-       size_t reqitems = conf.ifc_len / sizeof(struct ifreq);
-       for(head = list; head; head = (stor*)head->next) {
-               for(i = 0; i < reqitems; i++) {
-                       // get SIOCGIFADDR of active interfaces.
-                       if(!strcmp(reqs[i].ifr_name, head->name)) {
-                               head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr;
-                               head->ifa.ifa_addr = (struct sockaddr*) &head->addr;
+
+       ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
+       if (ifs == 0) return -1;
+
+       if (h->nlmsg_type == RTM_NEWLINK) {
+               ifs->index = ifi->ifi_index;
+               ifs->ifa.ifa_flags = ifi->ifi_flags;
+
+               for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+                       switch (rta->rta_type) {
+                       case IFLA_IFNAME:
+                               if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
+                                       memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
+                                       ifs->ifa.ifa_name = ifs->name;
+                               }
+                               break;
+                       case IFLA_ADDRESS:
+                               copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
+                               break;
+                       case IFLA_BROADCAST:
+                               copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
+                               break;
+                       case IFLA_STATS:
+                               ifs->ifa.ifa_data = (void*)(ifs+1);
+                               memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta));
                                break;
                        }
                }
-               struct ifreq req;
-               snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->name);
-               if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
-
-               head->ifa.ifa_flags = req.ifr_flags;
-               if(head->ifa.ifa_addr) {
-                       /* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
-                       head->ifa.ifa_flags |= IFF_LOWER_UP; 
-                       if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err;
-                       head->netmask.v4 = *(struct sockaddr_in*)&req.ifr_netmask;
-                       head->ifa.ifa_netmask = (struct sockaddr*) &head->netmask;
-       
-                       if(head->ifa.ifa_flags & IFF_POINTOPOINT) {
-                               if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err;
-                               head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr;
-                       } else {
-                               if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
-                               head->dst.v4 = *(struct sockaddr_in*)&req.ifr_broadaddr;
+               if (ifs->ifa.ifa_name) {
+                       unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
+                       ifs->hash_next = ctx->hash[bucket];
+                       ctx->hash[bucket] = ifs;
+               }
+       } else {
+               ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
+               ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
+               for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+                       switch (rta->rta_type) {
+                       case IFA_ADDRESS:
+                               copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
+                               break;
+                       case IFA_BROADCAST:
+                               /* For point-to-point links this is peer, but ifa_broadaddr
+                                * and ifa_dstaddr are union, so this works for both.  */
+                               copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
+                               break;
+                       case IFA_LABEL:
+                               if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
+                                       memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
+                                       ifs->ifa.ifa_name = ifs->name;
+                               }
+                               break;
                        }
-                       head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst;
                }
+               if (ifs->ifa.ifa_addr)
+                       gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
+       }
+
+       if (ifs->ifa.ifa_name) {
+               if (!ctx->first) ctx->first = ifs;
+               if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa;
+               ctx->last = ifs;
+       } else {
+               free(ifs);
        }
-       close(sock);
-       void* last = 0;
-       for(head = list; head; head=(stor*)head->next) last=head;
-       head = last;
-       dealwithipv6(&list, &head);
-       *ifap = (struct ifaddrs*) list;
        return 0;
-       err:
-       close(sock);
-       err2:
-       freeifaddrs((struct ifaddrs*) list);
-       return -1;
 }
 
+int getifaddrs(struct ifaddrs **ifap)
+{
+       struct ifaddrs_ctx _ctx, *ctx = &_ctx;
+       int r;
+       memset(ctx, 0, sizeof *ctx);
+       r = __rtnetlink_enumerate(AF_UNSPEC, AF_UNSPEC, netlink_msg_to_ifaddr, ctx);
+       if (r == 0) *ifap = &ctx->first->ifa;
+       else freeifaddrs(&ctx->first->ifa);
+       return r;
+}
index 53b80b21dc5570d84ea6bc44b9f94f6010e2a847..2deaef769600e5bb6b67d613eb3b3e1f65597383 100644 (file)
 #define _GNU_SOURCE
 #include <net/if.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <errno.h>
-#include "syscall.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include "netlink.h"
 
-static void *do_nameindex(int s, size_t n)
-{
-       size_t i, len, k;
-       struct ifconf conf;
-       struct if_nameindex *idx;
+#define IFADDRS_HASH_SIZE 64
 
-       idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct ifreq)));
-       if (!idx) return 0;
+struct ifnamemap {
+       unsigned int hash_next;
+       unsigned int index;
+       unsigned char namelen;
+       char name[IFNAMSIZ];
+};
 
-       conf.ifc_buf = (void *)&idx[n];
-       conf.ifc_len = len = n * sizeof(struct ifreq);
-       if (ioctl(s, SIOCGIFCONF, &conf) < 0) {
-               free(idx);
-               return 0;
-       }
-       if (conf.ifc_len == len) {
-               free(idx);
-               return (void *)-1;
+struct ifnameindexctx {
+       unsigned int num, allocated, str_bytes;
+       struct ifnamemap *list;
+       unsigned int hash[IFADDRS_HASH_SIZE];
+};
+
+static int netlink_msg_to_nameindex(void *pctx, struct nlmsghdr *h)
+{
+       struct ifnameindexctx *ctx = pctx;
+       struct ifnamemap *map;
+       struct rtattr *rta;
+       unsigned int i;
+       int index, type, namelen, bucket;
+
+       if (h->nlmsg_type == RTM_NEWLINK) {
+               struct ifinfomsg *ifi = NLMSG_DATA(h);
+               index = ifi->ifi_index;
+               type = IFLA_IFNAME;
+               rta = NLMSG_RTA(h, sizeof(*ifi));
+       } else {
+               struct ifaddrmsg *ifa = NLMSG_DATA(h);
+               index = ifa->ifa_index;
+               type = IFA_LABEL;
+               rta = NLMSG_RTA(h, sizeof(*ifa));
        }
+       for (; NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+               if (rta->rta_type != type) continue;
 
-       n = conf.ifc_len / sizeof(struct ifreq);
-       for (i=k=0; i<n; i++) {
-               if (ioctl(s, SIOCGIFINDEX, &conf.ifc_req[i]) < 0) {
-                       k++;
-                       continue;
+               namelen = RTA_DATALEN(rta) - 1;
+               if (namelen > IFNAMSIZ) return 0;
+
+               /* suppress duplicates */
+               bucket = index % IFADDRS_HASH_SIZE;
+               i = ctx->hash[bucket];
+               while (i) {
+                       map = &ctx->list[i-1];
+                       if (map->index == index &&
+                           map->namelen == namelen &&
+                           memcmp(map->name, RTA_DATA(rta), namelen) == 0)
+                               return 0;
+                       i = map->hash_next;
                }
-               idx[i-k].if_index = conf.ifc_req[i].ifr_ifindex;
-               idx[i-k].if_name = conf.ifc_req[i].ifr_name;
-       }
-       idx[i-k].if_name = 0;
-       idx[i-k].if_index = 0;
 
-       return idx;
+               if (ctx->num >= ctx->allocated) {
+                       size_t a = ctx->allocated ? ctx->allocated * 2 + 1 : 8;
+                       if (a > SIZE_MAX/sizeof *map) return -1;
+                       map = realloc(ctx->list, a * sizeof *map);
+                       if (!map) return -1;
+                       ctx->list = map;
+                       ctx->allocated = a;
+               }
+               map = &ctx->list[ctx->num];
+               map->index = index;
+               map->namelen = namelen;
+               memcpy(map->name, RTA_DATA(rta), namelen);
+               ctx->str_bytes += namelen + 1;
+               ctx->num++;
+               map->hash_next = ctx->hash[bucket];
+               ctx->hash[bucket] = ctx->num;
+               return 0;
+       }
+       return 0;
 }
 
 struct if_nameindex *if_nameindex()
 {
-       size_t n;
-       void *p = 0;
-       int s = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-       if (s>=0) {
-               for (n=0; (p=do_nameindex(s, n)) == (void *)-1; n++);
-               __syscall(SYS_close, s);
+       struct ifnameindexctx _ctx, *ctx = &_ctx;
+       struct if_nameindex *ifs = 0, *d;
+       struct ifnamemap *s;
+       char *p;
+       int i;
+       int cs;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+       memset(ctx, 0, sizeof(*ctx));
+       if (__rtnetlink_enumerate(AF_UNSPEC, AF_INET, netlink_msg_to_nameindex, ctx) < 0) goto err;
+
+       ifs = malloc(sizeof(struct if_nameindex[ctx->num+1]) + ctx->str_bytes);
+       if (!ifs) goto err;
+
+       p = (char*)(ifs + ctx->num + 1);
+       for (i = ctx->num, d = ifs, s = ctx->list; i; i--, s++, d++) {
+               d->if_index = s->index;
+               d->if_name = p;
+               memcpy(p, s->name, s->namelen);
+               p += s->namelen;
+               *p++ = 0;
        }
+       d->if_index = 0;
+       d->if_name = 0;
+err:
+       pthread_setcancelstate(cs, 0);
+       free(ctx->list);
        errno = ENOBUFS;
-       return p;
+       return ifs;
 }
diff --git a/src/network/netlink.c b/src/network/netlink.c
new file mode 100644 (file)
index 0000000..94dba7f
--- /dev/null
@@ -0,0 +1,52 @@
+#include <errno.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/socket.h>
+#include "netlink.h"
+
+static int __netlink_enumerate(int fd, unsigned int seq, int type, int af,
+       int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx)
+{
+       struct nlmsghdr *h;
+       union {
+               uint8_t buf[8192];
+               struct {
+                       struct nlmsghdr nlh;
+                       struct rtgenmsg g;
+               } req;
+               struct nlmsghdr reply;
+       } u;
+       int r, ret;
+
+       memset(&u.req, 0, sizeof(u.req));
+       u.req.nlh.nlmsg_len = sizeof(u.req);
+       u.req.nlh.nlmsg_type = type;
+       u.req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+       u.req.nlh.nlmsg_seq = seq;
+       u.req.g.rtgen_family = af;
+       r = send(fd, &u.req, sizeof(u.req), 0);
+       if (r < 0) return r;
+
+       while (1) {
+               r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT);
+               if (r <= 0) return -1;
+               for (h = &u.reply; NLMSG_OK(h, (void*)&u.buf[r]); h = NLMSG_NEXT(h)) {
+                       if (h->nlmsg_type == NLMSG_DONE) return 0;
+                       if (h->nlmsg_type == NLMSG_ERROR) return -1;
+                       ret = cb(ctx, h);
+                       if (ret) return ret;
+               }
+       }
+}
+
+int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx)
+{
+       int fd, r;
+
+       fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
+       if (fd < 0) return -1;
+       r = __netlink_enumerate(fd, 1, RTM_GETLINK, link_af, cb, ctx);
+       if (!r) r = __netlink_enumerate(fd, 2, RTM_GETADDR, addr_af, cb, ctx);
+       __syscall(SYS_close,fd);
+       return r;
+}
diff --git a/src/network/netlink.h b/src/network/netlink.h
new file mode 100644 (file)
index 0000000..20700ac
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdint.h>
+
+/* linux/netlink.h */
+
+#define NETLINK_ROUTE 0
+
+struct nlmsghdr {
+       uint32_t        nlmsg_len;
+       uint16_t        nlmsg_type;
+       uint16_t        nlmsg_flags;
+       uint32_t        nlmsg_seq;
+       uint32_t        nlmsg_pid;
+};
+
+#define NLM_F_REQUEST  1
+#define NLM_F_MULTI    2
+#define NLM_F_ACK      4
+
+#define NLM_F_ROOT     0x100
+#define NLM_F_MATCH    0x200
+#define NLM_F_ATOMIC   0x400
+#define NLM_F_DUMP     (NLM_F_ROOT|NLM_F_MATCH)
+
+#define NLMSG_NOOP     0x1
+#define NLMSG_ERROR    0x2
+#define NLMSG_DONE     0x3
+#define NLMSG_OVERRUN  0x4
+
+/* linux/rtnetlink.h */
+
+#define RTM_NEWLINK    16
+#define RTM_GETLINK    18
+#define RTM_NEWADDR    20
+#define RTM_GETADDR    22
+
+struct rtattr {
+       unsigned short  rta_len;
+       unsigned short  rta_type;
+};
+
+struct rtgenmsg {
+       unsigned char   rtgen_family;
+};
+
+struct ifinfomsg {
+       unsigned char   ifi_family;
+       unsigned char   __ifi_pad;
+       unsigned short  ifi_type;
+       int             ifi_index;
+       unsigned        ifi_flags;
+       unsigned        ifi_change;
+};
+
+/* linux/if_link.h */
+
+#define IFLA_ADDRESS   1
+#define IFLA_BROADCAST 2
+#define IFLA_IFNAME    3
+#define IFLA_STATS     7
+
+/* linux/if_addr.h */
+
+struct ifaddrmsg {
+       uint8_t         ifa_family;
+       uint8_t         ifa_prefixlen;
+       uint8_t         ifa_flags;
+       uint8_t         ifa_scope;
+       uint32_t        ifa_index;
+};
+
+#define IFA_ADDRESS    1
+#define IFA_LOCAL      2
+#define IFA_LABEL      3
+#define IFA_BROADCAST  4
+
+/* musl */
+
+#define NETLINK_ALIGN(len)     (((len)+3) & ~3)
+#define NLMSG_DATA(nlh)                ((void*)((char*)(nlh)+sizeof(struct nlmsghdr)))
+#define NLMSG_DATALEN(nlh)     ((nlh)->nlmsg_len-sizeof(struct nlmsghdr))
+#define NLMSG_DATAEND(nlh)     ((char*)(nlh)+(nlh)->nlmsg_len)
+#define NLMSG_NEXT(nlh)                (struct nlmsghdr*)((char*)(nlh)+NETLINK_ALIGN((nlh)->nlmsg_len))
+#define NLMSG_OK(nlh,end)      ((char*)(end)-(char*)(nlh) >= sizeof(struct nlmsghdr))
+
+#define RTA_DATA(rta)          ((void*)((char*)(rta)+sizeof(struct rtattr)))
+#define RTA_DATALEN(rta)       ((rta)->rta_len-sizeof(struct rtattr))
+#define RTA_DATAEND(rta)       ((char*)(rta)+(rta)->rta_len)
+#define RTA_NEXT(rta)          (struct rtattr*)((char*)(rta)+NETLINK_ALIGN((rta)->rta_len))
+#define RTA_OK(nlh,end)                ((char*)(end)-(char*)(rta) >= sizeof(struct rtattr))
+
+#define NLMSG_RTA(nlh,len)     ((void*)((char*)(nlh)+sizeof(struct nlmsghdr)+NETLINK_ALIGN(len)))
+#define NLMSG_RTAOK(rta,nlh)   RTA_OK(rta,NLMSG_DATAEND(nlh))
+
+int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx);