aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 844f522..d0d9055 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 294cd0d..aa20c35 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 60a1a2e..b276877 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 0000000..5d98c98
--- /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 7b29aa1..330aae3 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 23f7122..0d6ac84 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 0000000..0bc57da
--- /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 bc1ca85..837bfca 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