On Tue, Aug 7, 2018 at 5:49 AM, Charles Myers <charles.my...@spirent.com>
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 <charles.my...@spirent.com>
> ---
>  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 osv-dev+unsubscr...@googlegroups.com.
> 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 osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to