mailing list of musl libc
 help / color / mirror / code / Atom feed
* Progress towards 1.1.4
@ 2014-07-21  5:59 Rich Felker
  2014-07-21 13:31 ` Timo Teras
  0 siblings, 1 reply; 9+ messages in thread
From: Rich Felker @ 2014-07-21  5:59 UTC (permalink / raw)
  To: musl

A lot of off-the-roadmap stuff has come up lately that's prevented
making progress towards the roadmap goals for 1.1.4: all the problems
I've been fixing with atomics and stat bits on the less-used archs,
patch submissions, dealing with gcc 4.9.1 still being broken, and
fixing various bugs I found in the process working on all of the
above.

As for the roadmap, getting the or1k port merged was a big step. I've
been working with Stefan Kristiansson on support for porting for quite
a while, and now that it's merged and my role is back to just applying
fixes as they come up, I should be able to get back to the other tasks
that are remaining.

The big ones at this point are:

- Adding locale file loading. There's still some "policy" stuff to be
  discussed with regards to what env vars get used, etc. but I think
  the discussions so far have produced enough ideas that the big
  remaining task is actual implementation.

- Merging and hardening the mo-file lookup code I wrote (as discussed
  in the earlier mailing list threads, it should be hardened in case
  untrusted files ever get used) and enabling LC_TIME and LC_MESSAGES
  translations.

- Implementing punycode encode/decode (and possibly stringprep? only
  if needed to avoid introducing related security weaknesses) for IDN
  support.

- Solving the long-standing if_nameindex and getifaddrs lack of
  support for unconfigured and v6-only interfaces.

Given other non-musl things I need to get done, I'm not sure if all of
the above will happen by the end of the month. We could push the
release back a bit, or possibly omit one or both of the last two
items, although I know they've been getting pushed back for quite a
while now. I don't want to omit the locale stuff since making a
release with the framework but no functionality looks really bad (the
cost is there, but none of the benefits).

Rich


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Progress towards 1.1.4
  2014-07-21  5:59 Progress towards 1.1.4 Rich Felker
@ 2014-07-21 13:31 ` Timo Teras
  2014-07-21 14:59   ` if_nameindex, etc. [Re: Progress towards 1.1.4] Rich Felker
  0 siblings, 1 reply; 9+ messages in thread
From: Timo Teras @ 2014-07-21 13:31 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

On Mon, 21 Jul 2014 01:59:07 -0400
Rich Felker <dalias@libc.org> wrote:

> - Solving the long-standing if_nameindex and getifaddrs lack of
>   support for unconfigured and v6-only interfaces.

I understand that the big part of this is making the informed decision
on whether or not to use netlink. My take is to use netlink, as the
only argument against was that emulation layers do not support netlink.
But IIRC, it was mentioned that emulation does not support any of the
other APIs that would be needed to implement this.

If considering my netlink patch, the biggest job is likely auditing the
code. But I will be happy to do any changes requested.

Thanks,
Timo


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-21 13:31 ` Timo Teras
@ 2014-07-21 14:59   ` Rich Felker
  2014-07-21 16:09     ` Timo Teras
  0 siblings, 1 reply; 9+ messages in thread
From: Rich Felker @ 2014-07-21 14:59 UTC (permalink / raw)
  To: Timo Teras; +Cc: musl

On Mon, Jul 21, 2014 at 04:31:09PM +0300, Timo Teras wrote:
> On Mon, 21 Jul 2014 01:59:07 -0400
> Rich Felker <dalias@libc.org> wrote:
> 
> > - Solving the long-standing if_nameindex and getifaddrs lack of
> >   support for unconfigured and v6-only interfaces.
> 
> I understand that the big part of this is making the informed decision
> on whether or not to use netlink. My take is to use netlink, as the
> only argument against was that emulation layers do not support netlink.
> But IIRC, it was mentioned that emulation does not support any of the
> other APIs that would be needed to implement this.
> 
> If considering my netlink patch, the biggest job is likely auditing the
> code. But I will be happy to do any changes requested.

I admit it's been low priority on my list of things to look at, so I
haven't looked at it in enough detail, but my first impression was
that it looked larger and more complex than needed (but of course much
smalled and less ugly than other netlink code I've seen, so maybe
that's just netlink...).

Some useful questions to get answers to:

- How does size(1) vary before/after the patch?

- Would it be easy to make if_nameindex support pulling interface
  names from 3 sources (ioctl, /proc/net/dev, and netlink), using the
  index numbers obtained during that process where available (netlink
  only?), and using ioctl to get index numbers for the rest? I think
  that would provide maximum compatibility but it may also be a lot
  more bloat than we want...

- Would netlink code get any smaller/simpler if it were just flattened
  in the two places it's used rather than having callbacks and general
  framework?

I'm not expecting you to have answers to all of these but maybe it's
something others could look into and/or experiment with too.

Rich


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-21 14:59   ` if_nameindex, etc. [Re: Progress towards 1.1.4] Rich Felker
@ 2014-07-21 16:09     ` Timo Teras
  2014-07-21 16:13       ` Rich Felker
  0 siblings, 1 reply; 9+ messages in thread
From: Timo Teras @ 2014-07-21 16:09 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

On Mon, 21 Jul 2014 10:59:35 -0400
Rich Felker <dalias@libc.org> wrote:

> On Mon, Jul 21, 2014 at 04:31:09PM +0300, Timo Teras wrote:
> > If considering my netlink patch, the biggest job is likely auditing
> > the code. But I will be happy to do any changes requested.
> 
> I admit it's been low priority on my list of things to look at, so I
> haven't looked at it in enough detail, but my first impression was
> that it looked larger and more complex than needed (but of course much
> smalled and less ugly than other netlink code I've seen, so maybe
> that's just netlink...).

Much of the code is just #define's copied from kernel. Basically the
whole header file is that. The header can be simplified a bit by
deleting the unused flags, etc.

> Some useful questions to get answers to:
> 
> - How does size(1) vary before/after the patch?

Example numbers from my x86-64 system (git snapshot with few additional
patches applied).

No netlink patch:

text     data    bss     dec    hex  filename
544538   1920  11576  558034  883d2  lib/libc.so

With netlink patch:

text     data    bss     dec    hex  filename
544846   1920  11576  558342  88506  lib/libc.so

So +300 bytes in text, but in return there is added functionality such
as returning the link-level info in PF_PACKET dump (including
statistics). I also expect the code to be a lot faster:

- less syscalls; info from multiple interfaces are returned in single
  recv() call, instead of one syscall per interface

- no text parsing, it is all binary structures that are fast to
  interpret

And for static builds, there is less dependencies: sscanf machinery
is not used.

The only thing I would like to fix in if_nameindex() is the current
realloc() per interface, it should be made exponential or similar.
Possibly fixing the list generation to happen properly right away thus
avoiding the final conversion that is done now.

Also, auditing for cancellation correctness is pending.

> - Would it be easy to make if_nameindex support pulling interface
>   names from 3 sources (ioctl, /proc/net/dev, and netlink), using the
>   index numbers obtained during that process where available (netlink
>   only?), and using ioctl to get index numbers for the rest? I think
>   that would provide maximum compatibility but it may also be a lot
>   more bloat than we want...

It would be doable. But I'm not sure if it feasible, sounds like just
adding bloat.

> - Would netlink code get any smaller/simpler if it were just flattened
>   in the two places it's used rather than having callbacks and general
>   framework?

I don't think so. The code would be pretty much duplicated in both
places.

> I'm not expecting you to have answers to all of these but maybe it's
> something others could look into and/or experiment with too.

- Timo



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-21 16:09     ` Timo Teras
@ 2014-07-21 16:13       ` Rich Felker
  2014-07-21 16:35         ` Timo Teras
  2014-07-22  8:58         ` Natanael Copa
  0 siblings, 2 replies; 9+ messages in thread
From: Rich Felker @ 2014-07-21 16:13 UTC (permalink / raw)
  To: musl

On Mon, Jul 21, 2014 at 07:09:56PM +0300, Timo Teras wrote:
> So +300 bytes in text, but in return there is added functionality such
> as returning the link-level info in PF_PACKET dump (including
> statistics). I also expect the code to be a lot faster:

One thing I forgot to ask: is there any regression in returning
old-style interface "alias" names, i.e. do things like "eth0:1" still
appear in the output of if_nameindex?

Rich


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-21 16:13       ` Rich Felker
@ 2014-07-21 16:35         ` Timo Teras
  2014-07-22  8:58         ` Natanael Copa
  1 sibling, 0 replies; 9+ messages in thread
From: Timo Teras @ 2014-07-21 16:35 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

On Mon, 21 Jul 2014 12:13:42 -0400
Rich Felker <dalias@libc.org> wrote:

> On Mon, Jul 21, 2014 at 07:09:56PM +0300, Timo Teras wrote:
> > So +300 bytes in text, but in return there is added functionality
> > such as returning the link-level info in PF_PACKET dump (including
> > statistics). I also expect the code to be a lot faster:
> 
> One thing I forgot to ask: is there any regression in returning
> old-style interface "alias" names, i.e. do things like "eth0:1" still
> appear in the output of if_nameindex?

Hum. Seems there are two ways to have multiple IP-address: using the
alias hack, and by just adding address via "ip addr add".

If using "ip addr", there is no alias interface. The old code seems to
list the basename for each address, so the old code is "buggy" by
possibly listing the same interface multiple times in this scenario.

But the netlink code I wrote, does not currently enumerate the alias
interface names if addresses are added with the alias hack "ifconfig
eth0:1 ...". Netlink seems to report also these alias names, so doing
this properly is easily fixable. I can fix it tomorrow. I can also look
at fixing the if_nameindex() memory alloc issues while at it.

- Timo


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-21 16:13       ` Rich Felker
  2014-07-21 16:35         ` Timo Teras
@ 2014-07-22  8:58         ` Natanael Copa
  2014-07-22  9:04           ` Timo Teras
  2014-07-22 13:45           ` Rich Felker
  1 sibling, 2 replies; 9+ messages in thread
From: Natanael Copa @ 2014-07-22  8:58 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

On Mon, 21 Jul 2014 12:13:42 -0400
Rich Felker <dalias@libc.org> wrote:

> On Mon, Jul 21, 2014 at 07:09:56PM +0300, Timo Teras wrote:
> > So +300 bytes in text, but in return there is added functionality such
> > as returning the link-level info in PF_PACKET dump (including
> > statistics). I also expect the code to be a lot faster:
> 
> One thing I forgot to ask: is there any regression in returning
> old-style interface "alias" names, i.e. do things like "eth0:1" still
> appear in the output of if_nameindex?

Maybe this is an opportunity to get rid of the interface "alias"? it
was a hack to make it possible to assign multiple addresses on a single
NIC.

Do we need support that kind of legacy?

-nc


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-22  8:58         ` Natanael Copa
@ 2014-07-22  9:04           ` Timo Teras
  2014-07-22 13:45           ` Rich Felker
  1 sibling, 0 replies; 9+ messages in thread
From: Timo Teras @ 2014-07-22  9:04 UTC (permalink / raw)
  To: Natanael Copa; +Cc: Rich Felker, musl

On Tue, 22 Jul 2014 10:58:43 +0200
Natanael Copa <ncopa@alpinelinux.org> wrote:

> On Mon, 21 Jul 2014 12:13:42 -0400
> Rich Felker <dalias@libc.org> wrote:
> 
> > On Mon, Jul 21, 2014 at 07:09:56PM +0300, Timo Teras wrote:
> > > So +300 bytes in text, but in return there is added functionality
> > > such as returning the link-level info in PF_PACKET dump (including
> > > statistics). I also expect the code to be a lot faster:
> > 
> > One thing I forgot to ask: is there any regression in returning
> > old-style interface "alias" names, i.e. do things like "eth0:1"
> > still appear in the output of if_nameindex?
> 
> Maybe this is an opportunity to get rid of the interface "alias"? it
> was a hack to make it possible to assign multiple addresses on a
> single NIC.
> 
> Do we need support that kind of legacy?

Well, I think it might be desirable. For the record, glibc is funny in
this regard: if_nameindex() reports only real names (no aliases), but
getifaddrs() reports the alias name.

I have now updated the netlink patch and fixed:
 - getifaddrs() to report alias names where approriate
 - if_nameindex() fixed to report alias names (it also reports
   duplicates like the current code)
 - if_nameindex() calls realloc() less, but has now strdup() per
   interface (i wonder if this should be made in to realloc() bucket,
   and then merged in the end to return single allocated area again)
 - optimized some bytes away
 - few error case handling corrections

The size difference is now:
   text	   data	    bss	    dec	    hex	filename
 544840	   1920	  11576	 558336	  88500	lib/libc.nonetlink.so
 545022	   1920	  11576	 558518	  885b6	lib/libc.so

So now it's only +182 bytes with the added functionality (the earlier
patch was +320 or so).

Any comments?

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

---
 src/network/__netlink.c        |  52 +++++++
 src/network/__netlink.h        | 100 ++++++++++++++
 src/network/getifaddrs.c       | 307 ++++++++++++++++++++++-------------------
 src/network/if_freenameindex.c |   4 +
 src/network/if_nameindex.c     |  89 ++++++------
 5 files changed, 368 insertions(+), 184 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..78ab205
--- /dev/null
+++ b/src/network/__netlink.c
@@ -0,0 +1,52 @@
+#include <errno.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/socket.h>
+#include "__netlink.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 = 0;
+
+	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_ROOT | NLM_F_MATCH | 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 (ret == 0) {
+		r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT);
+		if (r <= 0) return -1;
+		for (h = &u.reply; NLMSG_OK(h, (void*)&u.buf[r]); h = NLMSG_NEXT(h)) {
+			if (h->nlmsg_type == NLMSG_DONE) return ret;
+			if (h->nlmsg_type == NLMSG_ERROR) return -1;
+			ret = cb(ctx, h);
+		}
+	}
+	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 == 0) r = __netlink_enumerate(fd, 2, RTM_GETADDR, addr_af, cb, ctx);
+	__syscall(SYS_close,fd);
+	return r;
+}
diff --git a/src/network/__netlink.h b/src/network/__netlink.h
new file mode 100644
index 0000000..04abfac
--- /dev/null
+++ b/src/network/__netlink.h
@@ -0,0 +1,100 @@
+#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_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)	(NLMSG_DATA(nlh) <= (end) && \
+				 (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+				 (void*)NLMSG_NEXT(nlh) <= (end))
+
+#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(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)+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);
diff --git a/src/network/getifaddrs.c b/src/network/getifaddrs.c
index 5a94cc7..ac2275f 100644
--- a/src/network/getifaddrs.c
+++ b/src/network/getifaddrs.c
@@ -1,181 +1,198 @@
-/* (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 <syscall.h>
+#include <net/if.h>
+#include "__netlink.h"
 
-typedef union {
-	struct sockaddr_in6 v6;
+/* 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;
-} 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
+};
+
+#define IFADDRS_HASH_SIZE 64
+struct ifaddrs_ctx {
+	struct ifaddrs_storage *first;
+	struct ifaddrs_storage *last;
+	struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
+};
 
-static stor* list_add(stor** list, stor** head, char* ifname)
+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 struct sockaddr* copy_lladdr(union sockany *sa, struct rtattr *rta, struct ifinfomsg *ifi)
 {
-	stor *head = (stor *) ifp;
-	while(head) {
-		void *p = head;
-		head = (stor *) head->next;
-		free(p);
-	}
+	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 void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
+static uint8_t *sockany_addr(int af, union sockany *sa, int *len)
 {
-	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;
+	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(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, *ifs0;
+	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;
 		}
+	} 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;
 	}
-	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;
+
+	ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
+	if (ifs == 0) return -1;
+
+	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:
+				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;
+		if (ifs->ifa.ifa_name) {
+			ifs->hash_next = ctx->hash[ifs->index%IFADDRS_HASH_SIZE];
+			ctx->hash[ifs->index%IFADDRS_HASH_SIZE] = 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:
+				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;
+			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;
 		}
 	}
-	close(sock);
-	void* last = 0;
-	for(head = list; head; head=(stor*)head->next) last=head;
-	head = last;
-	dealwithipv6(&list, &head);
-	*ifap = (struct ifaddrs*) list;
+
+	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);
+	}
 	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, __handle, ctx);
+	if (r == 0) *ifap = &ctx->first->ifa;
+	else freeifaddrs(&ctx->first->ifa);
+	return r;
+}
diff --git a/src/network/if_freenameindex.c b/src/network/if_freenameindex.c
index 89bafcc..bee80a7 100644
--- a/src/network/if_freenameindex.c
+++ b/src/network/if_freenameindex.c
@@ -3,5 +3,9 @@
 
 void if_freenameindex(struct if_nameindex *idx)
 {
+	struct if_nameindex *e;
+	if (!idx) return;
+	for (e = idx; e->if_name; e++)
+		free(e->if_name);
 	free(idx);
 }
diff --git a/src/network/if_nameindex.c b/src/network/if_nameindex.c
index 53b80b2..53d5269 100644
--- a/src/network/if_nameindex.c
+++ b/src/network/if_nameindex.c
@@ -1,55 +1,66 @@
 #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"
 
-static void *do_nameindex(int s, size_t n)
-{
-	size_t i, len, k;
-	struct ifconf conf;
-	struct if_nameindex *idx;
+struct ifnameindexctx {
+	unsigned int num, allocated;
+	struct if_nameindex *list;
+};
 
-	idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct ifreq)));
-	if (!idx) return 0;
+static int __handle(void *pctx, struct nlmsghdr *h)
+{
+	struct ifnameindexctx *ctx = pctx;
+	struct if_nameindex *e;
+	struct rtattr *rta;
+	int index, type;
 
-	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;
+	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;
 
-	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;
+		/* Make sure the end-of-list record is always allocated */
+		if (ctx->num+1 >= ctx->allocated) {
+			size_t a = ctx->allocated ? ctx->allocated * 2 + 1 : 8;
+			if (a > SIZE_MAX/sizeof *e) return -1;
+			e = realloc(ctx->list, a * sizeof *e);
+			if (!e) return -1;
+			ctx->list = e;
+			ctx->allocated = a;
 		}
-		idx[i-k].if_index = conf.ifc_req[i].ifr_ifindex;
-		idx[i-k].if_name = conf.ifc_req[i].ifr_name;
+		e = &ctx->list[ctx->num++];
+		e->if_index = index;
+		e->if_name = strdup(RTA_DATA(rta));
+		if (!e->if_name) return -1;
+		return 0;
 	}
-	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;
+	int r;
+
+	memset(ctx, 0, sizeof(*ctx));
+	r = __rtnetlink_enumerate(AF_UNSPEC, AF_INET, __handle, ctx);
+	if (ctx->list) memset(&ctx->list[ctx->num], 0, sizeof(struct if_nameindex));
+	if (r < 0) {
+		if_freenameindex(ctx->list);
+		return 0;
 	}
-	errno = ENOBUFS;
-	return p;
+	return realloc(ctx->list, sizeof(struct if_nameindex[ctx->num+1]));
 }
-- 
2.0.2



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Re: if_nameindex, etc. [Re: Progress towards 1.1.4]
  2014-07-22  8:58         ` Natanael Copa
  2014-07-22  9:04           ` Timo Teras
@ 2014-07-22 13:45           ` Rich Felker
  1 sibling, 0 replies; 9+ messages in thread
From: Rich Felker @ 2014-07-22 13:45 UTC (permalink / raw)
  To: musl

On Tue, Jul 22, 2014 at 10:58:43AM +0200, Natanael Copa wrote:
> On Mon, 21 Jul 2014 12:13:42 -0400
> Rich Felker <dalias@libc.org> wrote:
> 
> > On Mon, Jul 21, 2014 at 07:09:56PM +0300, Timo Teras wrote:
> > > So +300 bytes in text, but in return there is added functionality such
> > > as returning the link-level info in PF_PACKET dump (including
> > > statistics). I also expect the code to be a lot faster:
> > 
> > One thing I forgot to ask: is there any regression in returning
> > old-style interface "alias" names, i.e. do things like "eth0:1" still
> > appear in the output of if_nameindex?
> 
> Maybe this is an opportunity to get rid of the interface "alias"? it
> was a hack to make it possible to assign multiple addresses on a single
> NIC.
> 
> Do we need support that kind of legacy?

This is actually one of the few legacy features I somewhat want -- for
being able to do most simple configurations with just ifconfig.

Regardless of whether I want it or not though, it's more a matter of
presenting complete and consistent results. If we suppressed aliases
we'd either need to fake the address as being on the main interface
name (which might confuse tools working with both) or omit them
completely (which would obviously be bad).

I think if you do want to deprecate these aliases, the right place to
do it is in the kernel, so that the state can't exist to begin with,
rather than having libc try to hide it/pretend it doesn't exist.

Rich


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2014-07-22 13:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-21  5:59 Progress towards 1.1.4 Rich Felker
2014-07-21 13:31 ` Timo Teras
2014-07-21 14:59   ` if_nameindex, etc. [Re: Progress towards 1.1.4] Rich Felker
2014-07-21 16:09     ` Timo Teras
2014-07-21 16:13       ` Rich Felker
2014-07-21 16:35         ` Timo Teras
2014-07-22  8:58         ` Natanael Copa
2014-07-22  9:04           ` Timo Teras
2014-07-22 13:45           ` Rich Felker

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).