master
  1#define _GNU_SOURCE
  2#include <net/if.h>
  3#include <errno.h>
  4#include <unistd.h>
  5#include <stdlib.h>
  6#include <string.h>
  7#include <pthread.h>
  8#include "netlink.h"
  9
 10#define IFADDRS_HASH_SIZE 64
 11
 12struct ifnamemap {
 13	unsigned int hash_next;
 14	unsigned int index;
 15	unsigned char namelen;
 16	char name[IFNAMSIZ];
 17};
 18
 19struct ifnameindexctx {
 20	unsigned int num, allocated, str_bytes;
 21	struct ifnamemap *list;
 22	unsigned int hash[IFADDRS_HASH_SIZE];
 23};
 24
 25static int netlink_msg_to_nameindex(void *pctx, struct nlmsghdr *h)
 26{
 27	struct ifnameindexctx *ctx = pctx;
 28	struct ifnamemap *map;
 29	struct rtattr *rta;
 30	unsigned int i;
 31	int index, type, namelen, bucket;
 32
 33	if (h->nlmsg_type == RTM_NEWLINK) {
 34		struct ifinfomsg *ifi = NLMSG_DATA(h);
 35		index = ifi->ifi_index;
 36		type = IFLA_IFNAME;
 37		rta = NLMSG_RTA(h, sizeof(*ifi));
 38	} else {
 39		struct ifaddrmsg *ifa = NLMSG_DATA(h);
 40		index = ifa->ifa_index;
 41		type = IFA_LABEL;
 42		rta = NLMSG_RTA(h, sizeof(*ifa));
 43	}
 44	for (; NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
 45		if (rta->rta_type != type) continue;
 46
 47		namelen = RTA_DATALEN(rta) - 1;
 48		if (namelen > IFNAMSIZ) return 0;
 49
 50		/* suppress duplicates */
 51		bucket = index % IFADDRS_HASH_SIZE;
 52		i = ctx->hash[bucket];
 53		while (i) {
 54			map = &ctx->list[i-1];
 55			if (map->index == index &&
 56			    map->namelen == namelen &&
 57			    memcmp(map->name, RTA_DATA(rta), namelen) == 0)
 58				return 0;
 59			i = map->hash_next;
 60		}
 61
 62		if (ctx->num >= ctx->allocated) {
 63			size_t a = ctx->allocated ? ctx->allocated * 2 + 1 : 8;
 64			if (a > SIZE_MAX/sizeof *map) return -1;
 65			map = realloc(ctx->list, a * sizeof *map);
 66			if (!map) return -1;
 67			ctx->list = map;
 68			ctx->allocated = a;
 69		}
 70		map = &ctx->list[ctx->num];
 71		map->index = index;
 72		map->namelen = namelen;
 73		memcpy(map->name, RTA_DATA(rta), namelen);
 74		ctx->str_bytes += namelen + 1;
 75		ctx->num++;
 76		map->hash_next = ctx->hash[bucket];
 77		ctx->hash[bucket] = ctx->num;
 78		return 0;
 79	}
 80	return 0;
 81}
 82
 83struct if_nameindex *if_nameindex()
 84{
 85	struct ifnameindexctx _ctx, *ctx = &_ctx;
 86	struct if_nameindex *ifs = 0, *d;
 87	struct ifnamemap *s;
 88	char *p;
 89	int i;
 90	int cs;
 91
 92	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 93	memset(ctx, 0, sizeof(*ctx));
 94	if (__rtnetlink_enumerate(AF_UNSPEC, AF_INET, netlink_msg_to_nameindex, ctx) < 0) goto err;
 95
 96	ifs = malloc(sizeof(struct if_nameindex[ctx->num+1]) + ctx->str_bytes);
 97	if (!ifs) goto err;
 98
 99	p = (char*)(ifs + ctx->num + 1);
100	for (i = ctx->num, d = ifs, s = ctx->list; i; i--, s++, d++) {
101		d->if_index = s->index;
102		d->if_name = p;
103		memcpy(p, s->name, s->namelen);
104		p += s->namelen;
105		*p++ = 0;
106	}
107	d->if_index = 0;
108	d->if_name = 0;
109err:
110	pthread_setcancelstate(cs, 0);
111	free(ctx->list);
112	errno = ENOBUFS;
113	return ifs;
114}