aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2014-04-08 14:03:16 +0000
committerRich Felker <dalias@aerifal.cx>2014-07-29 20:57:31 -0400
commit08e4052c43692a9306c5c638d70fba7f7ba08c52 (patch)
treec6163802d24ed751bb5942ff862e17217083807b
parentcbb609b3db500e6aebe15762abebc4cb23563b8a (diff)
downloadmusl-08e4052c43692a9306c5c638d70fba7f7ba08c52.zip
musl-08e4052c43692a9306c5c638d70fba7f7ba08c52.tar.gz
musl-08e4052c43692a9306c5c638d70fba7f7ba08c52.tar.bz2
reimplement if_nameindex and getifaddrs using netlink
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.
-rw-r--r--src/network/getifaddrs.c314
-rw-r--r--src/network/if_nameindex.c135
-rw-r--r--src/network/netlink.c52
-rw-r--r--src/network/netlink.h94
4 files changed, 411 insertions, 184 deletions
diff --git a/src/network/getifaddrs.c b/src/network/getifaddrs.c
index 5a94cc7..89a8f72 100644
--- a/src/network/getifaddrs.c
+++ b/src/network/getifaddrs.c
@@ -1,181 +1,203 @@
-/* (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;
+}
diff --git a/src/network/if_nameindex.c b/src/network/if_nameindex.c
index 53b80b2..2deaef7 100644
--- a/src/network/if_nameindex.c
+++ b/src/network/if_nameindex.c
@@ -1,55 +1,114 @@
#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
index 0000000..94dba7f
--- /dev/null
+++ b/src/network/netlink.c
@@ -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
index 0000000..20700ac
--- /dev/null
+++ b/src/network/netlink.h
@@ -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);