about summary refs log tree commit diff
path: root/sysdeps
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2007-11-14 06:58:35 +0000
committerUlrich Drepper <drepper@redhat.com>2007-11-14 06:58:35 +0000
commit773e79b3162dff01e080cb152ff77945244f5a17 (patch)
treec5b1f11c79b36c6c1d02e80b0f6e00274ea6e586 /sysdeps
parente458144c99ddc00769ffa6bd367c21d37e879d83 (diff)
downloadglibc-773e79b3162dff01e080cb152ff77945244f5a17.tar.gz
glibc-773e79b3162dff01e080cb152ff77945244f5a17.tar.xz
glibc-773e79b3162dff01e080cb152ff77945244f5a17.zip
* include/ifaddrs.h: Remove in6ai_temporary.
	(struct in6addrinfo): Add index element.
	Declare __check_native.
	* inet/Makefile (aux): Add check_native.
	* sysdeps/unix/sysv/linux/check_native.c: New file.
	* sysdeps/unix/sysv/linux/check_pf.c: No need to recognize
	IFA_F_TEMPORARY.  Pass back ifa_index.
	* sysdeps/posix/getaddrinfo.c: Remove netlink compatibility code.
	(rfc3484_sort): Add new parameter.  Implement rule 7 correctly:
	call __check_native if necessary.
	(getaddrinfo): Fill in index field.  Use qsort_r instead of qsort
	to sort addresses.  Pass information about the results.
	* posix/tst-rfc3484.c: Adjust for addition of index field and change
	of rfc3484_sort interface.
	* posix/tst-rfc3484-2.c: Likewise.
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/posix/getaddrinfo.c109
-rw-r--r--sysdeps/unix/sysv/linux/check_native.c209
-rw-r--r--sysdeps/unix/sysv/linux/check_pf.c7
3 files changed, 256 insertions, 69 deletions
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 57970b4363..3937394d01 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -61,10 +61,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <nscd/nscd-client.h>
 #include <nscd/nscd_proto.h>
 
-#ifdef HAVE_NETLINK_ROUTE
-# include <kernel-features.h>
-#endif
-
 #ifdef HAVE_LIBIDN
 extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
 extern int __idna_to_unicode_lzlz (const char *input, char **output,
@@ -1007,6 +1003,14 @@ struct sort_result
   bool got_source_addr;
   uint8_t source_addr_flags;
   uint8_t prefixlen;
+  uint32_t index;
+  int32_t native;
+};
+
+struct sort_result_combo
+{
+  struct sort_result *results;
+  int nresults;
 };
 
 
@@ -1233,10 +1237,11 @@ fls (uint32_t a)
 
 
 static int
-rfc3484_sort (const void *p1, const void *p2)
+rfc3484_sort (const void *p1, const void *p2, void *arg)
 {
   const struct sort_result *a1 = (const struct sort_result *) p1;
   const struct sort_result *a2 = (const struct sort_result *) p2;
+  struct sort_result_combo *src = (struct sort_result_combo *) arg;
 
   /* Rule 1: Avoid unusable destinations.
      We have the got_source_addr flag set if the destination is reachable.  */
@@ -1321,14 +1326,34 @@ rfc3484_sort (const void *p1, const void *p2)
   /* Rule 7: Prefer native transport.  */
   if (a1->got_source_addr)
     {
-      if (!(a1->source_addr_flags & in6ai_temporary)
-	  && (a2->source_addr_flags & in6ai_temporary))
-	return -1;
-      if ((a1->source_addr_flags & in6ai_temporary)
-	  && !(a2->source_addr_flags & in6ai_temporary))
-	return 1;
+      /* The same interface index means the same interface which means
+	 there is no difference in transport.  This should catch many
+	 (most?) cases.  */
+      if (a1->index != a2->index)
+	{
+	  if (a1->native == -1 || a2->native == -1)
+	    {
+	      /* If we do not have the information use 'native' as the
+		 default.  */
+	      int a1_native = 0;
+	      int a2_native = 0;
+	      __check_native (a1->index, &a1_native, a2->index, &a2_native);
+
+	      /* Fill in the results in all the records.  */
+	      for (int i = 0; i < src->nresults; ++i)
+		{
+		  if (a1->native == -1 && src->results[i].index == a1->index)
+		    src->results[i].native = a1_native;
+		  if (a2->native == -1 && src->results[i].index == a2->index)
+		    src->results[i].native = a2_native;
+		}
+	    }
 
-      /* XXX Do we need to check anything beside temporary addresses?  */
+	  if (a1->native && !a2->native)
+	    return -1;
+	  if (!a1->native && a2->native)
+	    return 1;
+	}
     }
 
 
@@ -1759,16 +1784,6 @@ gaiconf_reload (void)
 }
 
 
-#if HAVE_NETLINK_ROUTE
-# if __ASSUME_NETLINK_SUPPORT == 0
-/* Defined in ifaddrs.c.  */
-extern int __no_netlink_support attribute_hidden;
-# else
-#  define __no_netlink_support 0
-# endif
-#endif
-
-
 int
 getaddrinfo (const char *name, const char *service,
 	     const struct addrinfo *hints, struct addrinfo **pai)
@@ -1807,50 +1822,12 @@ getaddrinfo (const char *name, const char *service,
   size_t in6ailen = 0;
   bool seen_ipv4 = false;
   bool seen_ipv6 = false;
-#ifdef HAVE_NETLINK_ROUTE
-  int sockfd = -1;
-  pid_t nl_pid;
-#endif
   /* We might need information about what interfaces are available.
      Also determine whether we have IPv4 or IPv6 interfaces or both.  We
      cannot cache the results since new interfaces could be added at
      any time.  */
   __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
-#ifdef HAVE_NETLINK_ROUTE
-  if (! __no_netlink_support)
-    {
-      sockfd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-
-      struct sockaddr_nl nladdr;
-      memset (&nladdr, '\0', sizeof (nladdr));
-      nladdr.nl_family = AF_NETLINK;
-
-      socklen_t addr_len = sizeof (nladdr);
-
-      if (sockfd >= 0
-	  && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
-	  && __getsockname (sockfd, (struct sockaddr *) &nladdr,
-			    &addr_len) == 0
-	  && make_request (sockfd, nladdr.nl_pid, &seen_ipv4, &seen_ipv6,
-			   in6ai, in6ailen) == 0)
-	{
-	  /* It worked.  */
-	  nl_pid = nladdr.nl_pid;
-	  goto got_netlink_socket;
-	}
 
-      if (sockfd >= 0)
-	close_not_cancel_no_status (sockfd);
-
-      /* Remember that there is no netlink support.  */
-      if (errno != EMFILE && errno != ENFILE)
-	__no_netlink_support = 1;
-    }
-#endif
-
-#ifdef HAVE_NETLINK_ROUTE
- got_netlink_socket:
-#endif
   if (hints->ai_flags & AI_ADDRCONFIG)
     {
       /* Now make a decision on what we return, if anything.  */
@@ -1938,7 +1915,7 @@ getaddrinfo (const char *name, const char *service,
       struct addrinfo *last = NULL;
       char *canonname = NULL;
 
-      /* If we have information about deprecated and temporary address
+      /* If we have information about deprecated and temporary addresses
 	 sort the array now.  */
       if (in6ai != NULL)
 	qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp);
@@ -1950,6 +1927,7 @@ getaddrinfo (const char *name, const char *service,
 	{
 	  results[i].dest_addr = q;
 	  results[i].service_order = i;
+	  results[i].native = -1;
 
 	  /* If we just looked up the address for a different
 	     protocol, reuse the result.  */
@@ -1962,12 +1940,14 @@ getaddrinfo (const char *name, const char *service,
 	      results[i].got_source_addr = results[i - 1].got_source_addr;
 	      results[i].source_addr_flags = results[i - 1].source_addr_flags;
 	      results[i].prefixlen = results[i - 1].prefixlen;
+	      results[i].index = results[i - 1].index;
 	    }
 	  else
 	    {
 	      results[i].got_source_addr = false;
 	      results[i].source_addr_flags = 0;
 	      results[i].prefixlen = 0;
+	      results[i].index = 0xffffffffu;
 
 	      /* We overwrite the type with SOCK_DGRAM since we do not
 		 want connect() to connect to the other side.  If we
@@ -2027,6 +2007,7 @@ getaddrinfo (const char *name, const char *service,
 			{
 			  results[i].source_addr_flags = found->flags;
 			  results[i].prefixlen = found->prefixlen;
+			  results[i].index = found->index;
 			}
 		    }
 
@@ -2076,6 +2057,8 @@ getaddrinfo (const char *name, const char *service,
 
       /* We got all the source addresses we can get, now sort using
 	 the information.  */
+      struct sort_result_combo src
+	= { .results = results, .nresults = nresults };
       if (__builtin_expect (gaiconf_reload_flag_ever_set, 0))
 	{
 	  __libc_lock_define_initialized (static, lock);
@@ -2083,11 +2066,11 @@ getaddrinfo (const char *name, const char *service,
 	  __libc_lock_lock (lock);
 	  if (old_once && gaiconf_reload_flag)
 	    gaiconf_reload ();
-	  qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
+	  qsort_r (results, nresults, sizeof (results[0]), rfc3484_sort, &src);
 	  __libc_lock_unlock (lock);
 	}
       else
-	qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
+	qsort_r (results, nresults, sizeof (results[0]), rfc3484_sort, &src);
 
       /* Queue the results up as they come out of sorting.  */
       q = p = results[0].dest_addr;
diff --git a/sysdeps/unix/sysv/linux/check_native.c b/sysdeps/unix/sysv/linux/check_native.c
new file mode 100644
index 0000000000..9b1bb61941
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/check_native.c
@@ -0,0 +1,209 @@
+/* Determine whether interfaces use native transport.  Linux version.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <sys/ioctl.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <not-cancel.h>
+
+
+void
+__check_native (uint32_t a1_index, int *a1_native,
+		uint32_t a2_index, int *a2_native)
+{
+  int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+
+  struct sockaddr_nl nladdr;
+  memset (&nladdr, '\0', sizeof (nladdr));
+  nladdr.nl_family = AF_NETLINK;
+
+  socklen_t addr_len = sizeof (nladdr);
+
+  if (fd < 0
+      || __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) != 0
+      || __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) != 0)
+    return;
+
+  pid_t pid = nladdr.nl_pid;
+  struct req
+  {
+    struct nlmsghdr nlh;
+    struct rtgenmsg g;
+    /* struct rtgenmsg consists of a single byte.  This means there
+       are three bytes of padding included in the REQ definition.
+       We make them explicit here.  */
+    char pad[3];
+  } req;
+
+  req.nlh.nlmsg_len = sizeof (req);
+  req.nlh.nlmsg_type = RTM_GETLINK;
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+  req.nlh.nlmsg_pid = 0;
+  req.nlh.nlmsg_seq = time (NULL);
+  req.g.rtgen_family = AF_UNSPEC;
+
+  assert (sizeof (req) - offsetof (struct req, pad) == 3);
+  memset (req.pad, '\0', sizeof (req.pad));
+
+  memset (&nladdr, '\0', sizeof (nladdr));
+  nladdr.nl_family = AF_NETLINK;
+
+#ifdef PAGE_SIZE
+  /* Help the compiler optimize out the malloc call if PAGE_SIZE
+     is constant and smaller or equal to PTHREAD_STACK_MIN/4.  */
+  const size_t buf_size = PAGE_SIZE;
+#else
+  const size_t buf_size = __getpagesize ();
+#endif
+  bool use_malloc = false;
+  char *buf;
+
+  if (__libc_use_alloca (buf_size))
+    buf = alloca (buf_size);
+  else
+    {
+      buf = malloc (buf_size);
+      if (buf != NULL)
+	use_malloc = true;
+      else
+	goto out_fail;
+    }
+
+  struct iovec iov = { buf, buf_size };
+
+  if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
+				    (struct sockaddr *) &nladdr,
+				    sizeof (nladdr))) < 0)
+    goto out_fail;
+
+  bool done = false;
+  int v4fd = -1;
+  do
+    {
+      struct msghdr msg =
+	{
+	  (void *) &nladdr, sizeof (nladdr),
+	  &iov, 1,
+	  NULL, 0,
+	  0
+	};
+
+      ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0));
+      if (read_len < 0)
+	goto out_fail;
+
+      if (msg.msg_flags & MSG_TRUNC)
+	goto out_fail;
+
+      struct nlmsghdr *nlmh;
+      for (nlmh = (struct nlmsghdr *) buf;
+	   NLMSG_OK (nlmh, (size_t) read_len);
+	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len))
+	{
+	  if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid
+	      || nlmh->nlmsg_seq != req.nlh.nlmsg_seq)
+	    continue;
+
+	  if (nlmh->nlmsg_type == RTM_NEWLINK)
+	    {
+	      /* A RTM_NEWLINK message can have IFLA_STATS data. We need to
+		 know the size before creating the list to allocate enough
+		 memory.  */
+	      struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlmh);
+	      struct rtattr *rta = IFLA_RTA (ifim);
+	      size_t rtasize = IFLA_PAYLOAD (nlmh);
+	      int index = ifim->ifi_index;
+
+	      if (a1_index == index || a2_index == index)
+		while (RTA_OK (rta, rtasize))
+		  {
+		    char *rta_data = RTA_DATA (rta);
+		    size_t rta_payload = RTA_PAYLOAD (rta);
+
+		    if (rta->rta_type == IFLA_IFNAME)
+		      {
+			struct ifreq ifr;
+			*((char *) mempcpy (ifr.ifr_name, rta_data,
+					    rta_payload))= '\0';
+
+			if (v4fd == -1)
+			  {
+			    v4fd = __socket (AF_INET, SOCK_DGRAM, 0);
+			    if (v4fd == -1)
+			      return;
+			  }
+
+			if (__ioctl (v4fd, SIOCGIFHWADDR, &ifr) >= 0)
+			  {
+			    int native
+			      = (ifr.ifr_hwaddr.sa_family != ARPHRD_TUNNEL6
+				 && ifr.ifr_hwaddr.sa_family != ARPHRD_TUNNEL
+				 && ifr.ifr_hwaddr.sa_family != ARPHRD_SIT);
+
+			    if (a1_index == index)
+			      {
+				*a1_native = native;
+				a1_index = 0xffffffffu;
+			      }
+			    if (a2_index == index)
+			      {
+				*a2_native = native;
+				a2_index = 0xffffffffu;
+			      }
+
+			    if (a1_index == 0xffffffffu
+				&& a2_index == 0xffffffffu)
+			      goto out;
+			  }
+			break;
+		      }
+
+		    rta = RTA_NEXT (rta, rtasize);
+		  }
+	    }
+	  else if (nlmh->nlmsg_type == NLMSG_DONE)
+	    /* We found the end, leave the loop.  */
+	    done = true;
+	}
+    }
+  while (! done);
+
+ out:
+  close_not_cancel_no_status (fd);
+  if (v4fd != -1)
+    close_not_cancel_no_status (v4fd);
+
+  return;
+
+out_fail:
+  if (use_malloc)
+    free (buf);
+}
diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c
index 532e1d923d..b7556aebc5 100644
--- a/sysdeps/unix/sysv/linux/check_pf.c
+++ b/sysdeps/unix/sysv/linux/check_pf.c
@@ -35,9 +35,6 @@
 #include <kernel-features.h>
 
 
-#ifndef IFA_F_TEMPORARY
-# define IFA_F_TEMPORARY IFA_F_SECONDARY
-#endif
 #ifndef IFA_F_HOMEADDRESS
 # define IFA_F_HOMEADDRESS 0
 #endif
@@ -190,12 +187,10 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
 				       | IFA_F_OPTIMISTIC))
 				   ? in6ai_deprecated : 0)
 				  | ((ifam->ifa_flags
-				      & IFA_F_TEMPORARY)
-				     ? in6ai_temporary : 0)
-				  | ((ifam->ifa_flags
 				      & IFA_F_HOMEADDRESS)
 				     ? in6ai_homeaddress : 0));
 	      newp->info.prefixlen = ifam->ifa_prefixlen;
+	      newp->info.index = ifam->ifa_index;
 	      if (ifam->ifa_family == AF_INET)
 		{
 		  newp->info.addr[0] = 0;