Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20140409170222.786b7bee@vostro>
Date: Wed, 9 Apr 2014 17:02:22 +0300
From: Timo Teras <timo.teras@....fi>
To: Timo Teras <timo.teras@....fi>
Cc: musl@...ts.openwall.com, dalias@...ifal.cx, justin@...cialbusservice.com
Subject: Re: if_nameindex/getifaddrs and dhcpcd issue

On Tue, 8 Apr 2014 19:08:07 +0300
Timo Teras <timo.teras@....fi> wrote:

> > > I'm willing to write an alternative getifaddrs() and
> > > if_nameindex() implementations using netlink. Perhaps let's see
> > > how they end up?  
> > 
> > It wouldn't hurt; if they're not upstreamed they could still be used
> > as a separate library for the one application which needs this
> > functionality (dhcpcd).
> 
> Yeah. I'll play with this and see what I come up with. I'll also
> delete the bad kernel #define's and try to do them a bit better - not
> sure how well I succeed that at, though.

Posting here the current version of my patch as requested.

While it has some corner cases still that need cleaning, it's a good
start. It implements the APIs, and is usable in read world.

From 4a82a84fb4fbfdae48f14bf86df0fd92086b7556 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@....fi>
Date: Tue, 8 Apr 2014 14:03:16 +0000
Subject: [PATCH] reimplement if_nameindex and getifaddrs using netlink

---
 src/network/__netlink.c    |  68 ++++++++++
 src/network/__netlink.h    | 143 ++++++++++++++++++++
 src/network/getifaddrs.c   | 322 ++++++++++++++++++++++++---------------------
 src/network/if_nameindex.c | 105 +++++++++------
 4 files changed, 451 insertions(+), 187 deletions(-)
 create mode 100644 src/network/__netlink.c
 create mode 100644 src/network/__netlink.h

diff --git a/src/network/__netlink.c b/src/network/__netlink.c
new file mode 100644
index 0000000..d0c9fab
--- /dev/null
+++ b/src/network/__netlink.c
@@ -0,0 +1,68 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include "__netlink.h"
+
+struct __netlink_handle {
+	int fd;
+	unsigned int seq;
+	size_t bufsize;
+};
+
+struct __netlink_handle *__netlink_open(int type)
+{
+	struct __netlink_handle *nh;
+	int bufsize = getpagesize();
+	/* required buffer size is MIN(8192,pagesize)-sizeof(struct skb_shared_info)
+	 * the estimate for skb_shared_info size is conservative, but gives enough
+	 * space to fit struct __netlink_handle including malloc overhead in one page . */
+	if (bufsize > 8192) bufsize = 8192;
+	bufsize -= 128;
+	nh = malloc(sizeof(struct __netlink_handle) + bufsize);
+	if (!nh) return 0;
+	nh->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, type);
+	if (nh->fd < 0) { free(nh); return 0; }
+	nh->seq = 1;
+	nh->bufsize = bufsize;
+	return nh;
+}
+
+void __netlink_close(struct __netlink_handle *nh)
+{
+	close(nh->fd);
+	free(nh);
+}
+
+int __netlink_enumerate(struct __netlink_handle *nh, int type, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx)
+{
+	struct nlmsghdr *h;
+	void *buf = (void*)(nh+1);
+	struct {
+		struct nlmsghdr nlh;
+		struct rtgenmsg g;
+	} *req = buf;
+	int r, ret = 0;
+
+	memset(req, 0, NETLINK_ALIGN(sizeof(*req)));
+	req->nlh.nlmsg_len = sizeof(*req);
+	req->nlh.nlmsg_type = type;
+	req->nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+	req->nlh.nlmsg_seq = nh->seq++;
+	req->g.rtgen_family = AF_UNSPEC;
+	r = send(nh->fd, req, sizeof(*req), 0);
+	if (r < 0) return r;
+
+	while (1) {
+		r = recv(nh->fd, buf, nh->bufsize, MSG_DONTWAIT);
+		if (r <= 0) return -1;
+		for (h = (struct nlmsghdr*) buf; NLMSG_OK(h, (void*)((uint8_t*)buf+r)); h = NLMSG_NEXT(h)) {
+			if (h->nlmsg_type == NLMSG_DONE) return ret;
+			if (h->nlmsg_type == NLMSG_ERROR) return -1;
+			if (!ret) ret = cb(ctx, h);
+		}
+	}
+}
diff --git a/src/network/__netlink.h b/src/network/__netlink.h
new file mode 100644
index 0000000..94728f3
--- /dev/null
+++ b/src/network/__netlink.h
@@ -0,0 +1,143 @@
+#include <stdint.h>
+
+/* linux/netlink.h */
+
+#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_ECHO	8
+#define NLM_F_DUMP_INTR	16
+
+#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_GETLINK	18
+#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 */
+
+enum {
+	IFLA_UNSPEC,
+	IFLA_ADDRESS,
+	IFLA_BROADCAST,
+	IFLA_IFNAME,
+	IFLA_MTU,
+	IFLA_LINK,
+	IFLA_QDISC,
+	IFLA_STATS,
+	IFLA_COST,
+	IFLA_PRIORITY,
+	IFLA_MASTER,
+	IFLA_WIRELESS,
+	IFLA_PROTINFO,
+	IFLA_TXQLEN,
+	IFLA_MAP,
+	IFLA_WEIGHT,
+	IFLA_OPERSTATE,
+	IFLA_LINKMODE,
+	IFLA_LINKINFO,
+	IFLA_NET_NS_PID,
+	IFLA_IFALIAS,
+	IFLA_NUM_VF,
+	IFLA_VFINFO_LIST,
+	IFLA_STATS64,
+	IFLA_VF_PORTS,
+	IFLA_PORT_SELF,
+	IFLA_AF_SPEC,
+	IFLA_GROUP,
+	IFLA_NET_NS_FD,
+	IFLA_EXT_MASK,
+	IFLA_PROMISCUITY,
+	IFLA_NUM_TX_QUEUES,
+	IFLA_NUM_RX_QUEUES,
+	IFLA_CARRIER,
+	IFLA_PHYS_PORT_ID,
+	__IFLA_MAX
+};
+
+/* 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;
+};
+
+enum {
+	IFA_UNSPEC,
+	IFA_ADDRESS,
+	IFA_LOCAL,
+	IFA_LABEL,
+	IFA_BROADCAST,
+	IFA_ANYCAST,
+	IFA_CACHEINFO,
+	IFA_MULTICAST,
+	__IFA_MAX
+};
+
+/* musl */
+
+#define NETLINK_ALIGN(len)	(((len)+3) & ~3)
+#define NLMSG_DATA(nlh)		((void*)((char*)(nlh)+NETLINK_ALIGN(sizeof(struct nlmsghdr))))
+#define NLMSG_DATALEN(nlh)	((nlh)->nlmsg_len-NETLINK_ALIGN(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)	(NLMSG_DATA(nlh) <= (end) && \
+				 (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+				 (void*)NLMSG_NEXT(nlh) <= (end))
+
+#define RTA_DATA(rta)		((void*)((char*)(rta)+NETLINK_ALIGN(sizeof(struct rtattr))))
+#define RTA_DATALEN(rta)	((rta)->rta_len-NETLINK_ALIGN(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(rta,end)		(RTA_DATA(rta) <= (void*)(end) && \
+				 (rta)->rta_len >= sizeof(struct rtattr) && \
+				 (void*)RTA_NEXT(rta) <= (void*)(end))
+
+#define NLMSG_RTA(nlh,len)	((void*)((char*)(nlh)+NETLINK_ALIGN(sizeof(struct nlmsghdr))+NETLINK_ALIGN(len)))
+#define NLMSG_RTAOK(rta,nlh)	RTA_OK(rta,NLMSG_DATAEND(nlh))
+
+struct __netlink_handle;
+
+struct __netlink_handle *__netlink_open(int type);
+void __netlink_close(struct __netlink_handle *h);
+int __netlink_enumerate(struct __netlink_handle *h, int type, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx);
diff --git a/src/network/getifaddrs.c b/src/network/getifaddrs.c
index 5a94cc7..5b1ebe7 100644
--- a/src/network/getifaddrs.c
+++ b/src/network/getifaddrs.c
@@ -1,181 +1,209 @@
-/* (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>
 #include <errno.h>
-#include <arpa/inet.h> /* inet_pton */
+#include <string.h>
+#include <stdlib.h>
 #include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include "__netlink.h"
 
-typedef union {
-	struct sockaddr_in6 v6;
+/* getifaddrs() uses PF_PACKET to relay hardware addresses.
+ * But Infiniband socket address length is longer, so use this hack
+ * (like glibc) to return it anyway. */
+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;
-} 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)
+#define IFADDRS_HASH_SIZE 64
+struct ifaddrs_ctx {
+	struct ifaddrs_storage *first;
+	struct ifaddrs_storage *last;
+	struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
+};
+
+void freeifaddrs(struct ifaddrs *ifp)
 {
-	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;
+	struct ifaddrs *n;
+	while (ifp) {
+		n = ifp->ifa_next;
+		free(ifp);
+		ifp = n;
 	}
-	return curr;
 }
 
-void freeifaddrs(struct ifaddrs *ifp)
+static void addifaddrs(struct ifaddrs_ctx *ctx, struct ifaddrs_storage *add)
 {
-	stor *head = (stor *) ifp;
-	while(head) {
-		void *p = head;
-		head = (stor *) head->next;
-		free(p);
+	if (!add->ifa.ifa_name) {
+		free(add);
+		return;
 	}
+	if (!ctx->first) ctx->first = add;
+	if (ctx->last) ctx->last->ifa.ifa_next = &add->ifa;
+	ctx->last = add;
 }
 
-static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
+static struct sockaddr* copy_lladdr(union sockany *sa, struct rtattr *rta, struct ifinfomsg *ifi)
 {
-	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;
+	if (RTA_DATALEN(rta) > sizeof(sa->ll.sll_addr)) return 0;
+	sa->ll.sll_family = AF_PACKET;
+	sa->ll.sll_ifindex = ifi->ifi_index;
+	sa->ll.sll_hatype = ifi->ifi_type;
+	sa->ll.sll_halen = RTA_DATALEN(rta);
+	memcpy(sa->ll.sll_addr, RTA_DATA(rta), RTA_DATALEN(rta));
+	return &sa->sa;
+}
+
+static uint8_t *sockany_addr(int af, union sockany *sa, int *len)
+{
+	switch (af) {
+	case AF_INET: *len = 4; return (uint8_t*) &sa->v4.sin_addr;
+	case AF_INET6: *len = 16; return (uint8_t*) &sa->v6.sin6_addr;
 	}
+	return 0;
 }
 
-static void dealwithipv6(stor **list, stor** head)
+static struct sockaddr* copy_addr(int af, union sockany *sa, struct rtattr *rta)
 {
-	FILE* f = fopen("/proc/net/if_inet6", "rbe");
-	/* 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;
-		}
+	int len;
+	uint8_t *dst = sockany_addr(af, sa, &len);
+	if (!dst || RTA_DATALEN(rta) != len) return 0;
+	sa->sa.sa_family = af;
+	memcpy(dst, RTA_DATA(rta), len);
+	return &sa->sa;
+}
+
+static struct sockaddr *gen_netmask(int af, union sockany *sa, int prefixlen)
+{
+	int maxlen, i;
+	uint8_t *dst = sockany_addr(af, sa, &maxlen);
+	if (!dst) return 0;
+	sa->sa.sa_family = af;
+	if (prefixlen > 8*maxlen) prefixlen = 8*maxlen;
+	i = prefixlen / 8;
+	memset(dst, 0xff, i);
+	if (i<maxlen) {
+		dst[i++] = 0xff << (8 - (prefixlen % 8));
+		if (i<maxlen) memset(&dst[i+1], 0x00, maxlen-i);
 	}
-	out:
-	fclose(f);
+	return &sa->sa;
 }
 
-int getifaddrs(struct ifaddrs **ifap)
+static int __handle_link(void *pctx, struct nlmsghdr *h)
 {
-	stor *list = 0, *head = 0;
-	struct if_nameindex* ii = if_nameindex();
-	if(!ii) return -1;
-	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) {
-			if_freenameindex(ii);
-			goto err2;
-		}
+	struct ifaddrs_ctx *ctx = pctx;
+	struct ifaddrs_storage *ifs;
+	struct ifinfomsg *ifi = NLMSG_DATA(h);
+	struct rtattr *rta;
+	int stats_len = 0;
+
+	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;
 	}
-	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);
-	for(head = list; head; head = (stor*)head->next) {
-		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;
-				break;
+
+	ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
+	if (ifs == 0) return -1;
+
+	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) <= IFNAMSIZ) {
+				strncpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
+				ifs->ifa.ifa_name = ifs->name;
 			}
+			break;
+		case IFLA_ADDRESS:
+			ifs->ifa.ifa_addr = copy_lladdr(&ifs->addr, rta, ifi);
+			break;
+		case IFLA_BROADCAST:
+			ifs->ifa.ifa_broadaddr = copy_lladdr(&ifs->ifu, rta, ifi);
+			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;
-			}
-			head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst;
+	}
+	if (ifs->ifa.ifa_name) {
+		ifs->hash_next = ctx->hash[ifs->index%IFADDRS_HASH_SIZE];
+		ctx->hash[ifs->index%IFADDRS_HASH_SIZE] = ifs;
+	}
+	addifaddrs(ctx, ifs);
+	return 0;
+}
+
+static int __handle_addr(void *pctx, struct nlmsghdr *h)
+{
+	struct ifaddrs_ctx *ctx = pctx;
+	struct ifaddrs_storage *ifs, *ifs0;
+	struct ifaddrmsg *ifa = NLMSG_DATA(h);
+	struct rtattr *rta;
+
+	ifs = calloc(1, sizeof(struct ifaddrs_storage));
+	if (ifs == 0) return -1;
+
+	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;
+
+	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:
+			ifs->ifa.ifa_addr = copy_addr(ifa->ifa_family, &ifs->addr, rta);
+			if (ifs->ifa.ifa_addr)
+				ifs->ifa.ifa_netmask = gen_netmask(ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
+			break;
+		case IFA_BROADCAST:
+			/* For point-to-point links this is peer, but ifa_broadaddr
+			 * and ifa_dstaddr are union, so this works for both.  */
+			ifs->ifa.ifa_broadaddr = copy_addr(ifa->ifa_family, &ifs->ifu, rta);
+			break;
 		}
 	}
-	close(sock);
-	void* last = 0;
-	for(head = list; head; head=(stor*)head->next) last=head;
-	head = last;
-	dealwithipv6(&list, &head);
-	*ifap = (struct ifaddrs*) list;
+
+	addifaddrs(ctx, ifs);
 	return 0;
-	err:
-	close(sock);
-	err2:
-	freeifaddrs((struct ifaddrs*) list);
-	return -1;
 }
 
+int getifaddrs(struct ifaddrs **ifap)
+{
+	struct ifaddrs_ctx _ctx, *ctx = &_ctx;
+	struct __netlink_handle *nh;
+	int r = 0;
+
+	nh = __netlink_open(NETLINK_ROUTE);
+	if (!nh) return -1;
+	memset(ctx, 0, sizeof(*ctx));
+	if (__netlink_enumerate(nh, RTM_GETLINK, __handle_link, ctx)) r = -1;
+	if (__netlink_enumerate(nh, RTM_GETADDR, __handle_addr, ctx)) r = -1;
+	__netlink_close(nh);
+	if (r == 0) *ifap = &ctx->first->ifa;
+	else freeifaddrs(&ctx->first->ifa);
+	return r;
+}
diff --git a/src/network/if_nameindex.c b/src/network/if_nameindex.c
index 53b80b2..d4e8b2d 100644
--- a/src/network/if_nameindex.c
+++ b/src/network/if_nameindex.c
@@ -1,55 +1,80 @@
 #define _GNU_SOURCE
 #include <net/if.h>
-#include <stdlib.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <errno.h>
-#include "syscall.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "__netlink.h"
+
+struct ifnamemap {
+	unsigned int index;
+	unsigned char namelen;
+	char name[IFNAMSIZ];
+};
 
-static void *do_nameindex(int s, size_t n)
+struct ifnameindexctx {
+	unsigned int num;
+	unsigned int str_bytes;
+	struct ifnamemap *list;
+};
+
+static int __handle_link(void *pctx, struct nlmsghdr *h)
 {
-	size_t i, len, k;
-	struct ifconf conf;
-	struct if_nameindex *idx;
-
-	idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct ifreq)));
-	if (!idx) return 0;
-
-	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;
-	}
+	struct ifnameindexctx *ctx = pctx;
+	struct ifinfomsg *ifim = NLMSG_DATA(h);
+	struct rtattr *rta;
+	struct ifnamemap *e;
 
-	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;
-		}
-		idx[i-k].if_index = conf.ifc_req[i].ifr_ifindex;
-		idx[i-k].if_name = conf.ifc_req[i].ifr_name;
+	for (rta = NLMSG_RTA(h, sizeof(*ifim)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+		if (rta->rta_type != IFLA_IFNAME) continue;
+		if (RTA_DATALEN(rta) > IFNAMSIZ) return -ENOBUFS;
+
+		ctx->num++;
+		ctx->str_bytes += RTA_DATALEN(rta) + 1;
+		e = realloc(ctx->list, sizeof(struct ifnamemap[ctx->num]));
+		if (e == 0) return -ENOMEM;
+		ctx->list = e;
+
+		e = &ctx->list[ctx->num-1];
+		e->index = ifim->ifi_index;
+		e->namelen = RTA_DATALEN(rta);
+		memcpy(e->name,  RTA_DATA(rta), IFNAMSIZ);
 	}
-	idx[i-k].if_name = 0;
-	idx[i-k].if_index = 0;
 
-	return idx;
+	return 0;
 }
 
 struct if_nameindex *if_nameindex()
 {
-	size_t n;
-	void *p = 0;
-	int s = socket(AF_UNIX, 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 = NULL;
+	struct __netlink_handle *nh;
+	int r, i;
+	char *p;
+
+	nh = __netlink_open(NETLINK_ROUTE);
+	if (!nh) goto err;
+	memset(ctx, 0, sizeof(*ctx));
+	r = __netlink_enumerate(nh, RTM_GETLINK, __handle_link, ctx);
+	__netlink_close(nh);
+	if (r < 0) goto err;
+
+	ifs = malloc(sizeof(struct if_nameindex[ctx->num+1]) + ctx->str_bytes);
+	if (ifs == 0) goto err;
+
+	p = (char*)ifs + sizeof(struct if_nameindex[ctx->num+1]);
+	for (i = 0; i < ctx->num; i++) {
+		ifs[i].if_index = ctx->list[i].index;
+		ifs[i].if_name = p;
+		memcpy(p, ctx->list[i].name, ctx->list[i].namelen);
+		p += ctx->list[i].namelen;
+		*p++ = 0;
 	}
-	errno = ENOBUFS;
-	return p;
+	ifs[i].if_index = 0;
+	ifs[i].if_name = 0;
+err:
+	free(ctx->list);
+	if (ifs == NULL) errno = ENOBUFS;
+	return ifs;
 }
-- 
1.9.1


Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.