about summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2003-06-10 08:58:05 +0000
committerUlrich Drepper <drepper@redhat.com>2003-06-10 08:58:05 +0000
commit1dc869d13c77297661bf7ba4bde4c879818149da (patch)
tree286fc8327a3a4fdc742d90b8ca8bd278f3616b8d
parent06120d793a3ae31f8f510f220c8a0a8e23b6a177 (diff)
downloadglibc-1dc869d13c77297661bf7ba4bde4c879818149da.tar.gz
glibc-1dc869d13c77297661bf7ba4bde4c879818149da.tar.xz
glibc-1dc869d13c77297661bf7ba4bde4c879818149da.zip
Update.
	* inet/Makefile (aux): Add check_fd.
	* include/ifaddrs.h: Add prototype for __check_fd.
	* sysdeps/generic/check_fd.c: New file.
	* sysdeps/unix/sysv/linux/check_fd.c: New file.
	* sysdeps/unix/sysv/linux/ifaddrs.h (__no_netlink_support): Renamed
	from no_netlink_support.  Export.
	* sysdeps/posix/getaddrinfo.c (getaddrinfo): Don't call getifaddrs,
	call __check_pf.

	* sysdeps/generic/ifaddrs.h: Add libc_hidden_def.
-rw-r--r--ChangeLog11
-rw-r--r--include/ifaddrs.h3
-rw-r--r--inet/Makefile4
-rw-r--r--sysdeps/generic/check_pf.c50
-rw-r--r--sysdeps/generic/ifaddrs.c4
-rw-r--r--sysdeps/posix/getaddrinfo.c30
-rw-r--r--sysdeps/unix/sysv/linux/check_pf.c193
-rw-r--r--sysdeps/unix/sysv/linux/ifaddrs.c10
8 files changed, 272 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index 844f522831..d0d9055855 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2003-06-10  Ulrich Drepper  <drepper@redhat.com>
 
+	* inet/Makefile (aux): Add check_fd.
+	* include/ifaddrs.h: Add prototype for __check_fd.
+	* sysdeps/generic/check_fd.c: New file.
+	* sysdeps/unix/sysv/linux/check_fd.c: New file.
+	* sysdeps/unix/sysv/linux/ifaddrs.h (__no_netlink_support): Renamed
+	from no_netlink_support.  Export.
+	* sysdeps/posix/getaddrinfo.c (getaddrinfo): Don't call getifaddrs,
+	call __check_pf.
+
+	* sysdeps/generic/ifaddrs.h: Add libc_hidden_def.
+
 	* sysdeps/posix/getaddrinfo.c (getaddrinfo): Don't leak memory
 	from getifaddr calls.
 
diff --git a/include/ifaddrs.h b/include/ifaddrs.h
index 294cd0d932..aa20c35101 100644
--- a/include/ifaddrs.h
+++ b/include/ifaddrs.h
@@ -1,7 +1,10 @@
 #ifndef _IFADDRS_H
 #include <inet/ifaddrs.h>
+#include <stdbool.h>
 
 libc_hidden_proto (getifaddrs)
 libc_hidden_proto (freeifaddrs)
 
+extern void __check_pf (bool *seen_ipv4, bool *seen_ipv6) attribute_hidden;
+
 #endif	/* ifaddrs.h */
diff --git a/inet/Makefile b/inet/Makefile
index 60a1a2e8fe..b276877099 100644
--- a/inet/Makefile
+++ b/inet/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1991-1999,2000,01,02 Free Software Foundation, Inc.
+# Copyright (C) 1991-2002, 2003 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
@@ -47,6 +47,8 @@ routines := htonl htons		\
 	    getaliasent_r getaliasent getaliasname getaliasname_r \
 	    in6_addr getnameinfo if_index ifaddrs
 
+aux := check_pf
+
 tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \
 	 tst-gethnm test-ifaddrs
 
diff --git a/sysdeps/generic/check_pf.c b/sysdeps/generic/check_pf.c
new file mode 100644
index 0000000000..5d98c98aff
--- /dev/null
+++ b/sysdeps/generic/check_pf.c
@@ -0,0 +1,50 @@
+/* Determine protocol families for which interfaces exist.  Generic version.
+   Copyright (C) 2003 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 <ifaddrs.h>
+#include <netdb.h>
+
+
+void
+attribute_hidden
+__check_pf (bool *seen_ipv4, bool *seen_ipv6)
+{
+  /* Get the interface list via getifaddrs.  */
+  struct ifaddrs *ifa = NULL;
+  if (getifaddrs (&ifa) != 0)
+    {
+      /* We cannot determine what interfaces are available.  Be
+	 pessimistic.  */
+      *seen_ipv4 = true;
+      *seen_ipv6 = true;
+      return;
+    }
+
+  *seen_ipv4 = false;
+  *seen_ipv6 = false;
+
+  struct ifaddrs *runp;
+  for (runp = ifa; runp != NULL; runp = runp->ifa_next)
+    if (runp->ifa_addr->sa_family == PF_INET)
+      *seen_ipv4 = true;
+    else if (runp->ifa_addr->sa_family == PF_INET6)
+      *seen_ipv6 = true;
+
+  (void) freeifaddrs (ifa);
+}
diff --git a/sysdeps/generic/ifaddrs.c b/sysdeps/generic/ifaddrs.c
index 7b29aa111c..330aae3b39 100644
--- a/sysdeps/generic/ifaddrs.c
+++ b/sysdeps/generic/ifaddrs.c
@@ -1,5 +1,5 @@
 /* getifaddrs -- get names and addresses of all network interfaces
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003 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
@@ -30,6 +30,7 @@ getifaddrs (struct ifaddrs **ifap)
   __set_errno (ENOSYS);
   return -1;
 }
+libc_hidden_def (getifaddrs)
 stub_warning (getifaddrs)
 
 void
@@ -41,4 +42,5 @@ freeifaddrs (struct ifaddrs *ifa)
   /* Can't be called properly if getifaddrs never succeeded.  */
   abort ();
 }
+libc_hidden_def (freeifaddrs)
 stub_warning (freeifaddrs)
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 23f7122ae1..0d6ac84ade 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -925,32 +925,10 @@ getaddrinfo (const char *name, const char *service,
     {
       /* Determine whether we have IPv4 or IPv6 interfaces or both.
 	 We cannot cache the results since new interfaces could be
-	 added at any time.
-
-	 XXX We are using getifaddrs here which is more costly than
-	 it is really necessary.  Once things are stable we will have
-	 a special function which performs the task with less overhead.  */
-      struct ifaddrs *ifa = NULL;
-
-      if (getifaddrs (&ifa) != 0)
-	/* Cannot get the interface list, very bad.  */
-	return EAI_SYSTEM;
-
-      bool seen_ipv4 = false;
-      bool seen_ipv6 = false;
-
-      struct ifaddrs *runp = ifa;
-      while (runp != NULL)
-	{
-	  if (runp->ifa_addr->sa_family == PF_INET)
-	    seen_ipv4 = true;
-	  else if (runp->ifa_addr->sa_family == PF_INET6)
-	    seen_ipv6 = true;
-
-	  runp = runp->ifa_next;
-	}
-
-      (void) freeifaddrs (ifa);
+	 added at any time.  */
+      bool seen_ipv4;
+      bool seen_ipv6;
+      __check_pf (&seen_ipv4, &seen_ipv6);
 
       /* Now make a decision on what we return, if anything.  */
       if (hints->ai_family == PF_UNSPEC)
diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c
new file mode 100644
index 0000000000..0bc57daa51
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/check_pf.c
@@ -0,0 +1,193 @@
+/* Determine protocol families for which interfaces exist.  Linux version.
+   Copyright (C) 2003 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 <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include "kernel-features.h"
+
+
+static int
+make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6)
+{
+  struct
+  {
+    struct nlmsghdr nlh;
+    struct rtgenmsg g;
+  } req;
+  struct sockaddr_nl nladdr;
+
+  req.nlh.nlmsg_len = sizeof (req);
+  req.nlh.nlmsg_type = RTM_GETADDR;
+  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;
+
+  memset (&nladdr, '\0', sizeof (nladdr));
+  nladdr.nl_family = AF_NETLINK;
+
+  if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
+				    (struct sockaddr *) &nladdr,
+				    sizeof (nladdr))) < 0)
+    return -1;
+
+  *seen_ipv4 = false;
+  *seen_ipv6 = false;
+
+  bool done = false;
+  char buf[4096];
+  struct iovec iov = { buf, sizeof (buf) };
+
+  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)
+	return -1;
+
+      if (msg.msg_flags & MSG_TRUNC)
+	return -1;
+
+      struct nlmsghdr *nlmh;
+      for (nlmh = (struct nlmsghdr *) buf;
+	   NLMSG_OK (nlmh, (size_t) read_len);
+	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len))
+	{
+	  if ((pid_t) nlmh->nlmsg_pid != pid
+	      || nlmh->nlmsg_seq != req.nlh.nlmsg_seq)
+	    continue;
+
+	  if (nlmh->nlmsg_type == RTM_NEWADDR)
+	    {
+	      struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh);
+
+	      switch (ifam->ifa_family)
+		{
+		case AF_INET:
+		  *seen_ipv4 = true;
+		  break;
+		case AF_INET6:
+		  *seen_ipv6 = true;
+		  break;
+		default:
+		  /* Ignore.  */
+		  break;
+		}
+	    }
+	  else if (nlmh->nlmsg_type == NLMSG_DONE)
+	    /* We found the end, leave the loop.  */
+	    done = true;
+	  else ;
+	}
+    }
+  while (! done);
+
+  close (fd);
+
+  return 0;
+}
+
+
+/* We don't know if we have NETLINK support compiled in in our
+   Kernel.  */
+#if __ASSUME_NETLINK_SUPPORT == 0
+/* Define in ifaddrs.h.  */
+extern int __no_netlink_support attribute_hidden;
+#else
+# define __no_netlink_support 0
+#endif
+
+
+void
+attribute_hidden
+__check_pf (bool *seen_ipv4, bool *seen_ipv6)
+{
+  if (! __no_netlink_support)
+    {
+      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
+	  && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6) == 0)
+	/* It worked.  */
+	return;
+
+      if (fd >= 0)
+	close (fd);
+
+#if __ASSUME_NETLINK_SUPPORT == 0
+      /* Remember that there is no netlink support.  */
+      __no_netlink_support = 1;
+#else
+      /* We cannot determine what interfaces are available.  Be
+	 pessimistic.  */
+      *seen_ipv4 = true;
+      *seen_ipv6 = true;
+#endif
+    }
+
+#if __ASSUME_NETLINK_SUPPORT == 0
+  /* No netlink.  Get the interface list via getifaddrs.  */
+  struct ifaddrs *ifa = NULL;
+  if (getifaddrs (&ifa) != 0)
+    {
+      /* We cannot determine what interfaces are available.  Be
+	 pessimistic.  */
+      *seen_ipv4 = true;
+      *seen_ipv6 = true;
+      return;
+    }
+
+  *seen_ipv4 = false;
+  *seen_ipv6 = false;
+
+  struct ifaddrs *runp;
+  for (runp = ifa; runp != NULL; runp = runp->ifa_next)
+    if (runp->ifa_addr->sa_family == PF_INET)
+      *seen_ipv4 = true;
+    else if (runp->ifa_addr->sa_family == PF_INET6)
+      *seen_ipv6 = true;
+
+  (void) freeifaddrs (ifa);
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/ifaddrs.c b/sysdeps/unix/sysv/linux/ifaddrs.c
index bc1ca8565f..837bfca664 100644
--- a/sysdeps/unix/sysv/linux/ifaddrs.c
+++ b/sysdeps/unix/sysv/linux/ifaddrs.c
@@ -41,7 +41,7 @@
 /* We don't know if we have NETLINK support compiled in in our
    Kernel, so include the old implementation as fallback.  */
 #if __ASSUME_NETLINK_SUPPORT == 0
-static int no_netlink_support;
+int __no_netlink_support attribute_hidden;
 
 # define getifaddrs fallback_getifaddrs
 # include "sysdeps/gnu/ifaddrs.c"
@@ -49,7 +49,7 @@ static int no_netlink_support;
 
 #else
 
-# define no_netlink_support 0
+# define __no_netlink_support 0
 
 #endif
 
@@ -297,17 +297,17 @@ getifaddrs (struct ifaddrs **ifap)
   if (ifap)
     *ifap = NULL;
 
-  if (! no_netlink_support && netlink_open (&nh) < 0)
+  if (! __no_netlink_support && netlink_open (&nh) < 0)
     {
 #if __ASSUME_NETLINK_SUPPORT == 0
-       no_netlink_support = 1;
+       __no_netlink_support = 1;
 #else
        return -1;
 #endif
     }
 
 #if __ASSUME_NETLINK_SUPPORT == 0
-  if (no_netlink_support)
+  if (__no_netlink_support)
     return fallback_getifaddrs (ifap);
 #endif