|
Message-ID: <20140409170222.786b7bee@vostro> Date: Wed, 9 Apr 2014 17:02:22 +0300 From: Timo Teras <timo.teras@....fi> To: Timo Teras <timo.teras@....fi> Cc: musl@...ts.openwall.com, dalias@...ifal.cx, justin@...cialbusservice.com Subject: Re: if_nameindex/getifaddrs and dhcpcd issue On Tue, 8 Apr 2014 19:08:07 +0300 Timo Teras <timo.teras@....fi> wrote: > > > I'm willing to write an alternative getifaddrs() and > > > if_nameindex() implementations using netlink. Perhaps let's see > > > how they end up? > > > > It wouldn't hurt; if they're not upstreamed they could still be used > > as a separate library for the one application which needs this > > functionality (dhcpcd). > > Yeah. I'll play with this and see what I come up with. I'll also > delete the bad kernel #define's and try to do them a bit better - not > sure how well I succeed that at, though. Posting here the current version of my patch as requested. While it has some corner cases still that need cleaning, it's a good start. It implements the APIs, and is usable in read world. From 4a82a84fb4fbfdae48f14bf86df0fd92086b7556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@....fi> Date: Tue, 8 Apr 2014 14:03:16 +0000 Subject: [PATCH] reimplement if_nameindex and getifaddrs using netlink --- src/network/__netlink.c | 68 ++++++++++ src/network/__netlink.h | 143 ++++++++++++++++++++ src/network/getifaddrs.c | 322 ++++++++++++++++++++++++--------------------- src/network/if_nameindex.c | 105 +++++++++------ 4 files changed, 451 insertions(+), 187 deletions(-) create mode 100644 src/network/__netlink.c create mode 100644 src/network/__netlink.h diff --git a/src/network/__netlink.c b/src/network/__netlink.c new file mode 100644 index 0000000..d0c9fab --- /dev/null +++ b/src/network/__netlink.c @@ -0,0 +1,68 @@ +#define _GNU_SOURCE +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/param.h> +#include "__netlink.h" + +struct __netlink_handle { + int fd; + unsigned int seq; + size_t bufsize; +}; + +struct __netlink_handle *__netlink_open(int type) +{ + struct __netlink_handle *nh; + int bufsize = getpagesize(); + /* required buffer size is MIN(8192,pagesize)-sizeof(struct skb_shared_info) + * the estimate for skb_shared_info size is conservative, but gives enough + * space to fit struct __netlink_handle including malloc overhead in one page . */ + if (bufsize > 8192) bufsize = 8192; + bufsize -= 128; + nh = malloc(sizeof(struct __netlink_handle) + bufsize); + if (!nh) return 0; + nh->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, type); + if (nh->fd < 0) { free(nh); return 0; } + nh->seq = 1; + nh->bufsize = bufsize; + return nh; +} + +void __netlink_close(struct __netlink_handle *nh) +{ + close(nh->fd); + free(nh); +} + +int __netlink_enumerate(struct __netlink_handle *nh, int type, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx) +{ + struct nlmsghdr *h; + void *buf = (void*)(nh+1); + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } *req = buf; + int r, ret = 0; + + memset(req, 0, NETLINK_ALIGN(sizeof(*req))); + req->nlh.nlmsg_len = sizeof(*req); + req->nlh.nlmsg_type = type; + req->nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req->nlh.nlmsg_seq = nh->seq++; + req->g.rtgen_family = AF_UNSPEC; + r = send(nh->fd, req, sizeof(*req), 0); + if (r < 0) return r; + + while (1) { + r = recv(nh->fd, buf, nh->bufsize, MSG_DONTWAIT); + if (r <= 0) return -1; + for (h = (struct nlmsghdr*) buf; NLMSG_OK(h, (void*)((uint8_t*)buf+r)); h = NLMSG_NEXT(h)) { + if (h->nlmsg_type == NLMSG_DONE) return ret; + if (h->nlmsg_type == NLMSG_ERROR) return -1; + if (!ret) ret = cb(ctx, h); + } + } +} diff --git a/src/network/__netlink.h b/src/network/__netlink.h new file mode 100644 index 0000000..94728f3 --- /dev/null +++ b/src/network/__netlink.h @@ -0,0 +1,143 @@ +#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_ECHO 8 +#define NLM_F_DUMP_INTR 16 + +#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_GETLINK 18 +#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 */ + +enum { + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, + IFLA_PRIORITY, + IFLA_MASTER, + IFLA_WIRELESS, + IFLA_PROTINFO, + IFLA_TXQLEN, + IFLA_MAP, + IFLA_WEIGHT, + IFLA_OPERSTATE, + IFLA_LINKMODE, + IFLA_LINKINFO, + IFLA_NET_NS_PID, + IFLA_IFALIAS, + IFLA_NUM_VF, + IFLA_VFINFO_LIST, + IFLA_STATS64, + IFLA_VF_PORTS, + IFLA_PORT_SELF, + IFLA_AF_SPEC, + IFLA_GROUP, + IFLA_NET_NS_FD, + IFLA_EXT_MASK, + IFLA_PROMISCUITY, + IFLA_NUM_TX_QUEUES, + IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, + IFLA_PHYS_PORT_ID, + __IFLA_MAX +}; + +/* 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; +}; + +enum { + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX +}; + +/* musl */ + +#define NETLINK_ALIGN(len) (((len)+3) & ~3) +#define NLMSG_DATA(nlh) ((void*)((char*)(nlh)+NETLINK_ALIGN(sizeof(struct nlmsghdr)))) +#define NLMSG_DATALEN(nlh) ((nlh)->nlmsg_len-NETLINK_ALIGN(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) (NLMSG_DATA(nlh) <= (end) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (void*)NLMSG_NEXT(nlh) <= (end)) + +#define RTA_DATA(rta) ((void*)((char*)(rta)+NETLINK_ALIGN(sizeof(struct rtattr)))) +#define RTA_DATALEN(rta) ((rta)->rta_len-NETLINK_ALIGN(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(rta,end) (RTA_DATA(rta) <= (void*)(end) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (void*)RTA_NEXT(rta) <= (void*)(end)) + +#define NLMSG_RTA(nlh,len) ((void*)((char*)(nlh)+NETLINK_ALIGN(sizeof(struct nlmsghdr))+NETLINK_ALIGN(len))) +#define NLMSG_RTAOK(rta,nlh) RTA_OK(rta,NLMSG_DATAEND(nlh)) + +struct __netlink_handle; + +struct __netlink_handle *__netlink_open(int type); +void __netlink_close(struct __netlink_handle *h); +int __netlink_enumerate(struct __netlink_handle *h, int type, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx); diff --git a/src/network/getifaddrs.c b/src/network/getifaddrs.c index 5a94cc7..5b1ebe7 100644 --- a/src/network/getifaddrs.c +++ b/src/network/getifaddrs.c @@ -1,181 +1,209 @@ -/* (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 <net/if.h> +#include "__netlink.h" -typedef union { - struct sockaddr_in6 v6; +/* getifaddrs() uses PF_PACKET to relay hardware addresses. + * But Infiniband socket address length is longer, so use this hack + * (like glibc) to return it anyway. */ +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) +#define IFADDRS_HASH_SIZE 64 +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 addifaddrs(struct ifaddrs_ctx *ctx, struct ifaddrs_storage *add) { - stor *head = (stor *) ifp; - while(head) { - void *p = head; - head = (stor *) head->next; - free(p); + if (!add->ifa.ifa_name) { + free(add); + return; } + if (!ctx->first) ctx->first = add; + if (ctx->last) ctx->last->ifa.ifa_next = &add->ifa; + ctx->last = add; } -static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa) +static struct sockaddr* copy_lladdr(union sockany *sa, struct rtattr *rta, struct ifinfomsg *ifi) { - 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; + if (RTA_DATALEN(rta) > sizeof(sa->ll.sll_addr)) return 0; + sa->ll.sll_family = AF_PACKET; + sa->ll.sll_ifindex = ifi->ifi_index; + sa->ll.sll_hatype = ifi->ifi_type; + sa->ll.sll_halen = RTA_DATALEN(rta); + memcpy(sa->ll.sll_addr, RTA_DATA(rta), RTA_DATALEN(rta)); + return &sa->sa; +} + +static uint8_t *sockany_addr(int af, union sockany *sa, int *len) +{ + switch (af) { + case AF_INET: *len = 4; return (uint8_t*) &sa->v4.sin_addr; + case AF_INET6: *len = 16; return (uint8_t*) &sa->v6.sin6_addr; } + return 0; } -static void dealwithipv6(stor **list, stor** head) +static struct sockaddr* copy_addr(int af, union sockany *sa, struct rtattr *rta) { - 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; - } + int len; + uint8_t *dst = sockany_addr(af, sa, &len); + if (!dst || RTA_DATALEN(rta) != len) return 0; + sa->sa.sa_family = af; + memcpy(dst, RTA_DATA(rta), len); + return &sa->sa; +} + +static struct sockaddr *gen_netmask(int af, union sockany *sa, int prefixlen) +{ + int maxlen, i; + uint8_t *dst = sockany_addr(af, sa, &maxlen); + if (!dst) return 0; + sa->sa.sa_family = af; + if (prefixlen > 8*maxlen) prefixlen = 8*maxlen; + i = prefixlen / 8; + memset(dst, 0xff, i); + if (i<maxlen) { + dst[i++] = 0xff << (8 - (prefixlen % 8)); + if (i<maxlen) memset(&dst[i+1], 0x00, maxlen-i); } - out: - fclose(f); + return &sa->sa; } -int getifaddrs(struct ifaddrs **ifap) +static int __handle_link(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; + struct ifinfomsg *ifi = NLMSG_DATA(h); + struct rtattr *rta; + int stats_len = 0; + + 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; } - 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; - break; + + ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len); + if (ifs == 0) return -1; + + 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) <= IFNAMSIZ) { + strncpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta)); + ifs->ifa.ifa_name = ifs->name; } + break; + case IFLA_ADDRESS: + ifs->ifa.ifa_addr = copy_lladdr(&ifs->addr, rta, ifi); + break; + case IFLA_BROADCAST: + ifs->ifa.ifa_broadaddr = copy_lladdr(&ifs->ifu, rta, ifi); + 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; - } - head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst; + } + if (ifs->ifa.ifa_name) { + ifs->hash_next = ctx->hash[ifs->index%IFADDRS_HASH_SIZE]; + ctx->hash[ifs->index%IFADDRS_HASH_SIZE] = ifs; + } + addifaddrs(ctx, ifs); + return 0; +} + +static int __handle_addr(void *pctx, struct nlmsghdr *h) +{ + struct ifaddrs_ctx *ctx = pctx; + struct ifaddrs_storage *ifs, *ifs0; + struct ifaddrmsg *ifa = NLMSG_DATA(h); + struct rtattr *rta; + + ifs = calloc(1, sizeof(struct ifaddrs_storage)); + if (ifs == 0) return -1; + + 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; + + 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: + ifs->ifa.ifa_addr = copy_addr(ifa->ifa_family, &ifs->addr, rta); + if (ifs->ifa.ifa_addr) + ifs->ifa.ifa_netmask = gen_netmask(ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen); + 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. */ + ifs->ifa.ifa_broadaddr = copy_addr(ifa->ifa_family, &ifs->ifu, rta); + break; } } - close(sock); - void* last = 0; - for(head = list; head; head=(stor*)head->next) last=head; - head = last; - dealwithipv6(&list, &head); - *ifap = (struct ifaddrs*) list; + + addifaddrs(ctx, ifs); return 0; - err: - close(sock); - err2: - freeifaddrs((struct ifaddrs*) list); - return -1; } +int getifaddrs(struct ifaddrs **ifap) +{ + struct ifaddrs_ctx _ctx, *ctx = &_ctx; + struct __netlink_handle *nh; + int r = 0; + + nh = __netlink_open(NETLINK_ROUTE); + if (!nh) return -1; + memset(ctx, 0, sizeof(*ctx)); + if (__netlink_enumerate(nh, RTM_GETLINK, __handle_link, ctx)) r = -1; + if (__netlink_enumerate(nh, RTM_GETADDR, __handle_addr, ctx)) r = -1; + __netlink_close(nh); + 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..d4e8b2d 100644 --- a/src/network/if_nameindex.c +++ b/src/network/if_nameindex.c @@ -1,55 +1,80 @@ #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 "__netlink.h" + +struct ifnamemap { + unsigned int index; + unsigned char namelen; + char name[IFNAMSIZ]; +}; -static void *do_nameindex(int s, size_t n) +struct ifnameindexctx { + unsigned int num; + unsigned int str_bytes; + struct ifnamemap *list; +}; + +static int __handle_link(void *pctx, struct nlmsghdr *h) { - size_t i, len, k; - struct ifconf conf; - struct if_nameindex *idx; - - idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct ifreq))); - if (!idx) return 0; - - 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 *ctx = pctx; + struct ifinfomsg *ifim = NLMSG_DATA(h); + struct rtattr *rta; + struct ifnamemap *e; - 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; - } - idx[i-k].if_index = conf.ifc_req[i].ifr_ifindex; - idx[i-k].if_name = conf.ifc_req[i].ifr_name; + for (rta = NLMSG_RTA(h, sizeof(*ifim)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) { + if (rta->rta_type != IFLA_IFNAME) continue; + if (RTA_DATALEN(rta) > IFNAMSIZ) return -ENOBUFS; + + ctx->num++; + ctx->str_bytes += RTA_DATALEN(rta) + 1; + e = realloc(ctx->list, sizeof(struct ifnamemap[ctx->num])); + if (e == 0) return -ENOMEM; + ctx->list = e; + + e = &ctx->list[ctx->num-1]; + e->index = ifim->ifi_index; + e->namelen = RTA_DATALEN(rta); + memcpy(e->name, RTA_DATA(rta), IFNAMSIZ); } - idx[i-k].if_name = 0; - idx[i-k].if_index = 0; - return idx; + 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 = NULL; + struct __netlink_handle *nh; + int r, i; + char *p; + + nh = __netlink_open(NETLINK_ROUTE); + if (!nh) goto err; + memset(ctx, 0, sizeof(*ctx)); + r = __netlink_enumerate(nh, RTM_GETLINK, __handle_link, ctx); + __netlink_close(nh); + if (r < 0) goto err; + + ifs = malloc(sizeof(struct if_nameindex[ctx->num+1]) + ctx->str_bytes); + if (ifs == 0) goto err; + + p = (char*)ifs + sizeof(struct if_nameindex[ctx->num+1]); + for (i = 0; i < ctx->num; i++) { + ifs[i].if_index = ctx->list[i].index; + ifs[i].if_name = p; + memcpy(p, ctx->list[i].name, ctx->list[i].namelen); + p += ctx->list[i].namelen; + *p++ = 0; } - errno = ENOBUFS; - return p; + ifs[i].if_index = 0; + ifs[i].if_name = 0; +err: + free(ctx->list); + if (ifs == NULL) errno = ENOBUFS; + return ifs; } -- 1.9.1
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.