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}
115