On Tue, Aug 7, 2018 at 5:49 AM, Charles Myers <[email protected]> wrote:
> libc code is from musl library Which version? It's not the version we have in musl/, right? So which version? > with minor change to prevent partial reads if faster than sender. > Is this fixing a bug in musl (in which case you should probably report it to them), or in OSv? > > Signed-off-by: Charles Myers <[email protected]> > --- > Makefile | 1 + > libc/network/getifaddrs.c | 392 ++++++++++++++++++++---------- > -------------- > libc/network/if_nameindex.c | 136 ++++++++++----- > libc/network/netlink.c | 66 ++++++++ > libc/network/netlink.h | 94 +++++++++++ > 5 files changed, 435 insertions(+), 254 deletions(-) > create mode 100644 libc/network/netlink.c > create mode 100644 libc/network/netlink.h > > diff --git a/Makefile b/Makefile > index 49e61b8..d5b6fda 100644 > --- a/Makefile > +++ b/Makefile > @@ -1387,6 +1387,7 @@ musl += network/getservbyname.o > musl += network/getservbyport_r.o > musl += network/getservbyport.o > libc += network/getifaddrs.o > +libc += network/netlink.o > libc += network/if_nameindex.o > musl += network/if_freenameindex.o > libc += network/res_init.o > diff --git a/libc/network/getifaddrs.c b/libc/network/getifaddrs.c > index 4cdf4d6..3b92599 100644 > --- a/libc/network/getifaddrs.c > +++ b/libc/network/getifaddrs.c > @@ -1,252 +1,216 @@ > -/* (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> > +//#define _GNU_SOURCE > #include <errno.h> > -#include <arpa/inet.h> /* inet_pton */ > +#include <string.h> > +#include <stdlib.h> > #include <unistd.h> > -#include <sys/ioctl.h> > -#include <netpacket/packet.h> > -#include <net/if_arp.h> > - > -typedef union { > - struct sockaddr_in6 v6; > +#include <ifaddrs.h> > +#include <syscall.h> > +#include <net/if.h> > +#include <netinet/in.h> > +#include "netlink.h" > + > +#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; > - struct sockaddr_ll hw; > -} 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) > -{ > - 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; > - } > - return curr; > -} > +struct ifaddrs_ctx { > + struct ifaddrs_storage *first; > + struct ifaddrs_storage *last; > + struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE]; > +}; > > void freeifaddrs(struct ifaddrs *ifp) > { > - stor *head = (stor *) ifp; > - while(head) { > - void *p = head; > - head = (stor *) head->next; > - free(p); > + struct ifaddrs *n; > + while (ifp) { > + n = ifp->ifa_next; > + free(ifp); > + ifp = n; > } > } > > -static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa) > +static void copy_addr(struct sockaddr **r, int af, union sockany *sa, > void *addr, size_t addrlen, int ifindex) > { > - 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 *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 dealwithipv6(stor **list, stor** head) > +static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, > int prefixlen) > { > - FILE* f = fopen("/proc/net/if_inet6", "r"); > - /* 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); > + 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); > } > > -int allocate_and_add_ifaddrs(stor **list, stor **head, struct > if_nameindex *ii) > +static void copy_lladdr(struct sockaddr **r, union sockany *sa, void > *addr, size_t addrlen, int ifindex, unsigned short hatype) > { > - 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) { > - return 0; > - } > - } > - return i; > + 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 fill_mac_addrs(int sock, stor *list, struct if_nameindex *ii) > +static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h) > { > - stor *head; > - size_t i; > - > - for(head = list, i = 0; > - head; head = (stor*)(head->next)) { > - struct sockaddr_ll *hw_addr = (struct sockaddr_ll *) > &head->addr; > - head->ifa.ifa_addr = (struct sockaddr*) &head->addr; > - struct ifreq req; > - int ret; > - > - if (!ii[i].if_name) { > - continue; > + struct ifaddrs_ctx *ctx = pctx; > + struct ifaddrs_storage *ifs, *ifs0 = NULL; > + 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; > } > - > - /* zero the ifreq structure */ > - memset(&req, 0, sizeof(req)); > - > - /* fill in the interface name */ > - strlcpy(req.ifr_name, ii[i].if_name, IFNAMSIZ); > - > - /* Get the hardware address */ > - ret = ioctl(sock, SIOCGIFHWADDR, &req); > - if (ret == -1) { > - return ret; > - } > - > - /* Fill address, index, type, length, familly */ > - memcpy(hw_addr->sll_addr, req.ifr_hwaddr.sa_data, > IFHWADDRLEN); > - hw_addr->sll_ifindex = ii[i].if_index; > - hw_addr->sll_hatype = ARPHRD_ETHER; > - hw_addr->sll_halen = IFHWADDRLEN; > - hw_addr->sll_family = AF_PACKET; > - > - /* Get and fill the address flags */ > - ret = ioctl(sock, SIOCGIFFLAGS, &req); > - if (ret == - 1) { > - return ret; > - } > - head->ifa.ifa_flags = req.ifr_flags; > - i++; > + } 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; > } > > - return 0; > -} > + ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len); > + if (ifs == 0) return -1; > > -int getifaddrs(struct ifaddrs **ifap) > -{ > - stor *list = 0, *head = 0; > - struct if_nameindex* ii = if_nameindex(); > - if(!ii) return -1; > - int addr_count; > - size_t i; > - // allocate and add to the list twice the number of > - // interface of ifaddr storage in order to have enough > - // for MAC HW addresses that require their own location. > - for (i = 0; i < 2; i++) { > - addr_count = > - allocate_and_add_ifaddrs(&list, &head, ii); > - if (!addr_count) { > - if_freenameindex(ii); > - goto err2; > - } > - } > - 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); > - int j; > - // loop and fill the INET addrs > - for(head = list, j = 0; head && j < addr_count; head = > (stor*)head->next, j++) { > - 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; > + 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: > + /* If ifa_addr is already set we, received > an IFA_LOCAL before > + * so treat this as destination address */ > + if (ifs->ifa.ifa_addr) > + copy_addr(&ifs->ifa.ifa_dstaddr, > ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), > ifa->ifa_index); > + else > + copy_addr(&ifs->ifa.ifa_addr, > ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), > ifa->ifa_index); > + break; > + case IFA_BROADCAST: > + copy_addr(&ifs->ifa.ifa_broadaddr, > ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), > ifa->ifa_index); > + break; > + case IFA_LOCAL: > + /* If ifa_addr is set and we get > IFA_LOCAL, assume we have > + * a point-to-point network. Move address > to correct field. */ > + if (ifs->ifa.ifa_addr) { > + ifs->ifu = ifs->addr; > + ifs->ifa.ifa_dstaddr = &ifs-> > ifu.sa; > + memset(&ifs->addr, 0, > sizeof(ifs->addr)); > + } > + copy_addr(&ifs->ifa.ifa_addr, > ifa->ifa_family, &ifs->addr, 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 (-1 == fill_mac_addrs(sock, head, ii)) { > - goto err; > + > + 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/libc/network/if_nameindex.c b/libc/network/if_nameindex.c > index b0a4f5c..7c36c85 100644 > --- a/libc/network/if_nameindex.c > +++ b/libc/network/if_nameindex.c > @@ -1,58 +1,114 @@ > -#define _GNU_SOURCE > +//#define _GNU_SOURCE > #include <net/if.h> > -#include <stdlib.h> > -#include <sys/socket.h> > -#include <sys/ioctl.h> > #include <errno.h> > #include <unistd.h> > -#include <sys/syscall.h> > +#include <stdlib.h> > +#include <string.h> > +#include <pthread.h> > +#include "netlink.h" > > -#include <stdio.h> > +#define IFADDRS_HASH_SIZE 64 > > -static void *do_nameindex(int s, size_t n) > -{ > - size_t i, len, k; > - struct ifconf conf; > - struct if_nameindex *idx; > +struct ifnamemap { > + unsigned int hash_next; > + unsigned int index; > + unsigned char namelen; > + char name[IFNAMSIZ]; > +}; > > - idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct > ifreq))); > - if (!idx) return 0; > +struct ifnameindexctx { > + unsigned int num, allocated, str_bytes; > + struct ifnamemap *list; > + unsigned int hash[IFADDRS_HASH_SIZE]; > +}; > > - 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; > +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; > + > + namelen = RTA_DATALEN(rta) - 1; > + if (namelen > IFNAMSIZ) return 0; > > - 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; > + /* 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_INET, 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/libc/network/netlink.c b/libc/network/netlink.c > new file mode 100644 > index 0000000..ab0437a > --- /dev/null > +++ b/libc/network/netlink.c > @@ -0,0 +1,66 @@ > +#include <errno.h> > +#include <string.h> > +#include <syscall.h> > +#include <sys/socket.h> > +#include <unistd.h> > +#include "netlink.h" > +#include <stdio.h> > +#include <poll.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) { > + /* Wait if recv() timed out because response might > not be queued in time */ > + if (r < 0 && (errno == EAGAIN || errno == > EWOULDBLOCK)) { > + struct pollfd pfds[1]; > + pfds[0].fd = fd; > + pfds[0].events = POLLIN; > + pfds[0].revents = 0; > + if (poll(pfds, 1, 1000) != 0) > + continue; > + } > + 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/libc/network/netlink.h b/libc/network/netlink.h > new file mode 100644 > index 0000000..20700ac > --- /dev/null > +++ b/libc/network/netlink.h > @@ -0,0 +1,94 @@ > +#include <stdint.h> > + > +/* linux/netlink.h */ > In the long run, maybe this stuff belongs in include/api/linux/netlink.h; Don't you also need the same definitions in the *implementation* of the netlink protocol, not just its user in libc? But if this fits your organization and doesn't cause duplication of the definitions it can stay here for now. + > +#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); > -- > 2.7.4 > > -- > You received this message because you are subscribed to the Google Groups > "OSv Development" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
