github.com/afumu/libc@v0.0.6/musl/src/network/getifaddrs.c (about)

     1  #define _GNU_SOURCE
     2  #include <errno.h>
     3  #include <string.h>
     4  #include <stdlib.h>
     5  #include <unistd.h>
     6  #include <ifaddrs.h>
     7  #include <syscall.h>
     8  #include <net/if.h>
     9  #include <netinet/in.h>
    10  #include "netlink.h"
    11  
    12  #define IFADDRS_HASH_SIZE 64
    13  
    14  /* getifaddrs() reports hardware addresses with PF_PACKET that implies
    15   * struct sockaddr_ll.  But e.g. Infiniband socket address length is
    16   * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
    17   * to extend ssl_addr - callers should be able to still use it. */
    18  struct sockaddr_ll_hack {
    19  	unsigned short sll_family, sll_protocol;
    20  	int sll_ifindex;
    21  	unsigned short sll_hatype;
    22  	unsigned char sll_pkttype, sll_halen;
    23  	unsigned char sll_addr[24];
    24  };
    25  
    26  union sockany {
    27  	struct sockaddr sa;
    28  	struct sockaddr_ll_hack ll;
    29  	struct sockaddr_in v4;
    30  	struct sockaddr_in6 v6;
    31  };
    32  
    33  struct ifaddrs_storage {
    34  	struct ifaddrs ifa;
    35  	struct ifaddrs_storage *hash_next;
    36  	union sockany addr, netmask, ifu;
    37  	unsigned int index;
    38  	char name[IFNAMSIZ+1];
    39  };
    40  
    41  struct ifaddrs_ctx {
    42  	struct ifaddrs_storage *first;
    43  	struct ifaddrs_storage *last;
    44  	struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
    45  };
    46  
    47  void freeifaddrs(struct ifaddrs *ifp)
    48  {
    49  	struct ifaddrs *n;
    50  	while (ifp) {
    51  		n = ifp->ifa_next;
    52  		free(ifp);
    53  		ifp = n;
    54  	}
    55  }
    56  
    57  static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex)
    58  {
    59  	uint8_t *dst;
    60  	int len;
    61  
    62  	switch (af) {
    63  	case AF_INET:
    64  		dst = (uint8_t*) &sa->v4.sin_addr;
    65  		len = 4;
    66  		break;
    67  	case AF_INET6:
    68  		dst = (uint8_t*) &sa->v6.sin6_addr;
    69  		len = 16;
    70  		if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
    71  			sa->v6.sin6_scope_id = ifindex;
    72  		break;
    73  	default:
    74  		return;
    75  	}
    76  	if (addrlen < len) return;
    77  	sa->sa.sa_family = af;
    78  	memcpy(dst, addr, len);
    79  	*r = &sa->sa;
    80  }
    81  
    82  static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen)
    83  {
    84  	uint8_t addr[16] = {0};
    85  	int i;
    86  
    87  	if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr);
    88  	i = prefixlen / 8;
    89  	memset(addr, 0xff, i);
    90  	if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8));
    91  	copy_addr(r, af, sa, addr, sizeof(addr), 0);
    92  }
    93  
    94  static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype)
    95  {
    96  	if (addrlen > sizeof(sa->ll.sll_addr)) return;
    97  	sa->ll.sll_family = AF_PACKET;
    98  	sa->ll.sll_ifindex = ifindex;
    99  	sa->ll.sll_hatype = hatype;
   100  	sa->ll.sll_halen = addrlen;
   101  	memcpy(sa->ll.sll_addr, addr, addrlen);
   102  	*r = &sa->sa;
   103  }
   104  
   105  static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
   106  {
   107  	struct ifaddrs_ctx *ctx = pctx;
   108  	struct ifaddrs_storage *ifs, *ifs0;
   109  	struct ifinfomsg *ifi = NLMSG_DATA(h);
   110  	struct ifaddrmsg *ifa = NLMSG_DATA(h);
   111  	struct rtattr *rta;
   112  	int stats_len = 0;
   113  
   114  	if (h->nlmsg_type == RTM_NEWLINK) {
   115  		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
   116  			if (rta->rta_type != IFLA_STATS) continue;
   117  			stats_len = RTA_DATALEN(rta);
   118  			break;
   119  		}
   120  	} else {
   121  		for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next)
   122  			if (ifs0->index == ifa->ifa_index)
   123  				break;
   124  		if (!ifs0) return 0;
   125  	}
   126  
   127  	ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
   128  	if (ifs == 0) return -1;
   129  
   130  	if (h->nlmsg_type == RTM_NEWLINK) {
   131  		ifs->index = ifi->ifi_index;
   132  		ifs->ifa.ifa_flags = ifi->ifi_flags;
   133  
   134  		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
   135  			switch (rta->rta_type) {
   136  			case IFLA_IFNAME:
   137  				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
   138  					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
   139  					ifs->ifa.ifa_name = ifs->name;
   140  				}
   141  				break;
   142  			case IFLA_ADDRESS:
   143  				copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
   144  				break;
   145  			case IFLA_BROADCAST:
   146  				copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
   147  				break;
   148  			case IFLA_STATS:
   149  				ifs->ifa.ifa_data = (void*)(ifs+1);
   150  				memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta));
   151  				break;
   152  			}
   153  		}
   154  		if (ifs->ifa.ifa_name) {
   155  			unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
   156  			ifs->hash_next = ctx->hash[bucket];
   157  			ctx->hash[bucket] = ifs;
   158  		}
   159  	} else {
   160  		ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
   161  		ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
   162  		for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
   163  			switch (rta->rta_type) {
   164  			case IFA_ADDRESS:
   165  				/* If ifa_addr is already set we, received an IFA_LOCAL before
   166  				 * so treat this as destination address */
   167  				if (ifs->ifa.ifa_addr)
   168  					copy_addr(&ifs->ifa.ifa_dstaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
   169  				else
   170  					copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
   171  				break;
   172  			case IFA_BROADCAST:
   173  				copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
   174  				break;
   175  			case IFA_LOCAL:
   176  				/* If ifa_addr is set and we get IFA_LOCAL, assume we have
   177  				 * a point-to-point network. Move address to correct field. */
   178  				if (ifs->ifa.ifa_addr) {
   179  					ifs->ifu = ifs->addr;
   180  					ifs->ifa.ifa_dstaddr = &ifs->ifu.sa;
   181  					memset(&ifs->addr, 0, sizeof(ifs->addr));
   182  				}
   183  				copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
   184  				break;
   185  			case IFA_LABEL:
   186  				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
   187  					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
   188  					ifs->ifa.ifa_name = ifs->name;
   189  				}
   190  				break;
   191  			}
   192  		}
   193  		if (ifs->ifa.ifa_addr)
   194  			gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
   195  	}
   196  
   197  	if (ifs->ifa.ifa_name) {
   198  		if (!ctx->first) ctx->first = ifs;
   199  		if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa;
   200  		ctx->last = ifs;
   201  	} else {
   202  		free(ifs);
   203  	}
   204  	return 0;
   205  }
   206  
   207  int getifaddrs(struct ifaddrs **ifap)
   208  {
   209  	struct ifaddrs_ctx _ctx, *ctx = &_ctx;
   210  	int r;
   211  	memset(ctx, 0, sizeof *ctx);
   212  	r = __rtnetlink_enumerate(AF_UNSPEC, AF_UNSPEC, netlink_msg_to_ifaddr, ctx);
   213  	if (r == 0) *ifap = &ctx->first->ifa;
   214  	else freeifaddrs(&ctx->first->ifa);
   215  	return r;
   216  }