diff options
author | Timo Teräs <timo.teras@iki.fi> | 2014-04-08 14:03:16 +0000 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2014-07-29 20:57:31 -0400 |
commit | 08e4052c43692a9306c5c638d70fba7f7ba08c52 (patch) | |
tree | c6163802d24ed751bb5942ff862e17217083807b /src/network/if_nameindex.c | |
parent | cbb609b3db500e6aebe15762abebc4cb23563b8a (diff) | |
download | musl-08e4052c43692a9306c5c638d70fba7f7ba08c52.tar.gz musl-08e4052c43692a9306c5c638d70fba7f7ba08c52.tar.bz2 musl-08e4052c43692a9306c5c638d70fba7f7ba08c52.tar.xz musl-08e4052c43692a9306c5c638d70fba7f7ba08c52.zip |
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.
Diffstat (limited to 'src/network/if_nameindex.c')
-rw-r--r-- | src/network/if_nameindex.c | 135 |
1 files changed, 97 insertions, 38 deletions
diff --git a/src/network/if_nameindex.c b/src/network/if_nameindex.c index 53b80b21..2deaef76 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; } |