[PATCH] make getaddrinfo hint AI_ADDRCONFIG work

Ricard Wanderlof ricard.wanderlof at axis.com
Thu Feb 21 14:30:19 UTC 2008


We experienced a problem with a product when IPv6 is compiled into the 
product (and uClibc) but selectively disabled by a user. In this case, 
IPv6 traffic was still be generated, which in this particular case was 
not acceptable.

The problem turned out to be in getaddrinfo(). When hints.ai_flags has the 
AI_ADDRCONFIG bit set in a call to getaddrinfo, IPv4 and IPv6 addresses 
should only be returned if the system as at least one address of the 
appropriate type configured. However, this turned out not to be the case, 
and both types of addresses were returned in any case, causing IPv6 DNS 
traffic even if no interface had an IPv6 address configured.

The following patch fixes the problem, while still touching the existing 
code as little as possible. It has been tested and works and I would like 
to commit it, but comments would be valued.

I've also included the patch as an attachement. It was made against svn 
21085, head of the development tree att the time of writing.

Some comments on the changes:

First of all, two new functions ipv4_available() and ipv6_available() 
check if ipv4 or ipb6 is enabled. For ipv6, /proc/net/if_inet6 is checked. 
For ipv4 there is no corresponding file, instead /proc/net/route is 
checked to determine there is a route defined (i.e. the file has one 
more line with data besides the header line), as this indicates ipv4 is 
available.

The addrconfig() and gaih_inet() functions have then been modified to use 
these two functions when AF_INET or AF_INET6 is specified, respectively. 
For addrconfig() it has been noted that just being able to create a 
SOCK_DGRAM socket of the corresponding address family type is not enough 
to determine the existence of correspondingly configured interfaces.

The final block in the patch fixes a bug which caused an infinite loop 
when hints->ai_flags had the AI_ADDRCONFIG bit set. (Perhaps this was a 
failed attempt att fixing the problem at some time, as the coresponding 
glibc version does not seem to have the corresponding 
if-followed-by-continue clause. It has been in the svn repo since the 
first entry in 2002 (svn 4414) ).

/Ricard


Index: libc/inet/getaddrinfo.c
===================================================================
--- libc/inet/getaddrinfo.c	(revision 21085)
+++ libc/inet/getaddrinfo.c	(working copy)
@@ -165,20 +165,72 @@
  { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
  #endif

+static int ipv4_available(void) {
+    int fd;
+    size_t bytes = 1;
+    void *pos;
+    char buf[10];
+    int firstline = 1;
+    int routefound = 0;

+    fd = open("/proc/net/route", O_RDONLY);
+    // If proc is not mounted assume IPv4 is available
+    if (fd < 0) 
+	return 1;
+
+    while (routefound == 0 && bytes > 0) 
+    {
+	bytes = read(fd, buf, sizeof(buf));
+	pos = memchr(buf, '\n', bytes);
+	if (pos != NULL) 
+	    if (!firstline) 
+		routefound = 1;
+	    else
+		firstline = 0;
+    }
+    close(fd);
+    return routefound;
+}
+
+#if __UCLIBC_HAS_IPV6__
+static int ipv6_available(void) {
+    int fd;
+    size_t bytes = 0;
+    char buf[1];
+ 
+    fd = open("/proc/net/if_inet6", O_RDONLY);
+    // If proc is not mounted assume IPv4 is available
+    if (fd < 0) 
+	return 1;
+
+    bytes = read(fd, buf, sizeof(buf));
+    close(fd);
+    return (bytes > 0) ? 1 : 0;
+}
+#endif
+
  static int addrconfig (sa_family_t af)
  {
      int s;
      int ret;
      int saved_errno = errno;
-    s = socket(af, SOCK_DGRAM, 0);
-    if (s < 0)
-	ret = (errno == EMFILE) ? 1 : 0;
-    else
-    {
-	close(s);
-	ret = 1;
+    if (af == AF_INET)
+	ret = ipv4_available();
+#if __UCLIBC_HAS_IPV6__
+    else if (af == AF_INET6)
+	ret = ipv6_available();
+#endif
+    else {
+	s = socket(af, SOCK_DGRAM, 0);
+	if (s < 0)
+	    ret = (errno == EMFILE) ? 1 : 0;
+	else
+	{
+	    close(s);
+	    ret = 1;
+	}
      }
+
      __set_errno (saved_errno);
      return ret;
  }
@@ -383,6 +435,11 @@
      int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
  	(req->ai_flags & AI_V4MAPPED);

+#if __UCLIBC_HAS_IPV6__
+    int ipv6 = ipv6_available();
+#endif
+    int ipv4 = ipv4_available();
+
      if (req->ai_protocol || req->ai_socktype)
      {
  	++tp;
@@ -569,14 +626,16 @@

  #if __UCLIBC_HAS_IPV6__
  	    if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-		gethosts (AF_INET6, struct in6_addr);
+		if (!(req->ai_flags & AI_ADDRCONFIG) || ipv6) 
+		    gethosts (AF_INET6, struct in6_addr);
  #endif
  	    no_inet6_data = no_data;

  	    if (req->ai_family == AF_INET ||
  		(!v4mapped && req->ai_family == AF_UNSPEC) ||
  		(v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
-		gethosts (AF_INET, struct in_addr);
+		if (!(req->ai_flags & AI_ADDRCONFIG) || ipv4) 
+		    gethosts (AF_INET, struct in_addr);

  	    if (no_data != 0 && no_inet6_data != 0)
  	    {
@@ -704,6 +763,13 @@

  	    for (st2 = st; st2 != NULL; st2 = st2->next)
  	    {
+		if (req->ai_flags & AI_ADDRCONFIG)
+		    if (family == AF_INET && !ipv4)
+			break;
+#if __UCLIBC_HAS_IPV6__
+		    else if (family == AF_INET6 && !ipv6)
+			break;
+#endif
  		*pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
  		if (*pai == NULL)
  		    return -EAI_MEMORY;
@@ -862,7 +928,10 @@
  	if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
  	{
  	    if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
+	    {
+		++g;
  		continue;
+	    }
  	    j++;
  	    if (pg == NULL || pg->gaih != g->gaih)
  	    {



--
Ricard Wolf Wanderlöf                           ricardw(at)axis.com
Axis Communications AB, Lund, Sweden            www.axis.com
Phone +46 46 272 2016                           Fax +46 46 13 61 30
-------------- next part --------------
Index: libc/inet/getaddrinfo.c
===================================================================
--- libc/inet/getaddrinfo.c	(revision 21085)
+++ libc/inet/getaddrinfo.c	(working copy)
@@ -165,20 +165,72 @@
 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
 #endif
 
+static int ipv4_available(void) {
+    int fd;
+    size_t bytes = 1;
+    void *pos;
+    char buf[10];
+    int firstline = 1;
+    int routefound = 0;
 
+    fd = open("/proc/net/route", O_RDONLY);
+    // If proc is not mounted assume IPv4 is available
+    if (fd < 0) 
+	return 1;
+
+    while (routefound == 0 && bytes > 0) 
+    {
+	bytes = read(fd, buf, sizeof(buf));
+	pos = memchr(buf, '\n', bytes);
+	if (pos != NULL) 
+	    if (!firstline) 
+		routefound = 1;
+	    else
+		firstline = 0;
+    }
+    close(fd);
+    return routefound;
+}
+
+#if __UCLIBC_HAS_IPV6__
+static int ipv6_available(void) {
+    int fd;
+    size_t bytes = 0;
+    char buf[1];
+  
+    fd = open("/proc/net/if_inet6", O_RDONLY);
+    // If proc is not mounted assume IPv4 is available
+    if (fd < 0) 
+	return 1;
+
+    bytes = read(fd, buf, sizeof(buf));
+    close(fd);
+    return (bytes > 0) ? 1 : 0;
+}
+#endif
+
 static int addrconfig (sa_family_t af)
 {
     int s;
     int ret;
     int saved_errno = errno;
-    s = socket(af, SOCK_DGRAM, 0);
-    if (s < 0)
-	ret = (errno == EMFILE) ? 1 : 0;
-    else
-    {
-	close(s);
-	ret = 1;
+    if (af == AF_INET)
+	ret = ipv4_available();
+#if __UCLIBC_HAS_IPV6__
+    else if (af == AF_INET6)
+	ret = ipv6_available();
+#endif
+    else {
+	s = socket(af, SOCK_DGRAM, 0);
+	if (s < 0)
+	    ret = (errno == EMFILE) ? 1 : 0;
+	else
+	{
+	    close(s);
+	    ret = 1;
+	}
     }
+    
     __set_errno (saved_errno);
     return ret;
 }
@@ -383,6 +435,11 @@
     int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
 	(req->ai_flags & AI_V4MAPPED);
 
+#if __UCLIBC_HAS_IPV6__
+    int ipv6 = ipv6_available();
+#endif
+    int ipv4 = ipv4_available();
+    
     if (req->ai_protocol || req->ai_socktype)
     {
 	++tp;
@@ -569,14 +626,16 @@
 
 #if __UCLIBC_HAS_IPV6__
 	    if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-		gethosts (AF_INET6, struct in6_addr);
+		if (!(req->ai_flags & AI_ADDRCONFIG) || ipv6) 
+		    gethosts (AF_INET6, struct in6_addr);
 #endif
 	    no_inet6_data = no_data;
 
 	    if (req->ai_family == AF_INET ||
 		(!v4mapped && req->ai_family == AF_UNSPEC) ||
 		(v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
-		gethosts (AF_INET, struct in_addr);
+		if (!(req->ai_flags & AI_ADDRCONFIG) || ipv4) 
+		    gethosts (AF_INET, struct in_addr);
 
 	    if (no_data != 0 && no_inet6_data != 0)
 	    {
@@ -704,6 +763,13 @@
 
 	    for (st2 = st; st2 != NULL; st2 = st2->next)
 	    {
+		if (req->ai_flags & AI_ADDRCONFIG)
+		    if (family == AF_INET && !ipv4)
+			break;
+#if __UCLIBC_HAS_IPV6__
+		    else if (family == AF_INET6 && !ipv6)
+			break;
+#endif
 		*pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
 		if (*pai == NULL)
 		    return -EAI_MEMORY;
@@ -862,7 +928,10 @@
 	if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
 	{
 	    if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
+	    {
+		++g;
 		continue;
+	    }
 	    j++;
 	    if (pg == NULL || pg->gaih != g->gaih)
 	    {


More information about the uClibc mailing list