From 773e79b3162dff01e080cb152ff77945244f5a17 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 14 Nov 2007 06:58:35 +0000 Subject: * 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. --- ChangeLog | 16 +++ include/ifaddrs.h | 7 +- inet/Makefile | 2 +- posix/tst-rfc3484-2.c | 15 ++- posix/tst-rfc3484.c | 10 +- sysdeps/posix/getaddrinfo.c | 109 ++++++++--------- sysdeps/unix/sysv/linux/check_native.c | 209 +++++++++++++++++++++++++++++++++ sysdeps/unix/sysv/linux/check_pf.c | 7 +- 8 files changed, 300 insertions(+), 75 deletions(-) create mode 100644 sysdeps/unix/sysv/linux/check_native.c diff --git a/ChangeLog b/ChangeLog index fa869d81a5..6b32a063a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2007-11-13 Ulrich Drepper + * 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. + * stdlib/stdlib.h: Define __compar_d_fn_t. Declare qsort_r. * include/stdlib.h: Add hidden_proto for qsort_t and adjust protoype for _quicksort. diff --git a/include/ifaddrs.h b/include/ifaddrs.h index 7d52dc6dc8..50e4c48e82 100644 --- a/include/ifaddrs.h +++ b/include/ifaddrs.h @@ -10,16 +10,19 @@ struct in6addrinfo { enum { in6ai_deprecated = 1, - in6ai_temporary = 2, - in6ai_homeaddress = 4 + in6ai_homeaddress = 2 } flags:8; uint8_t prefixlen; uint16_t :16; + uint32_t index; uint32_t addr[4]; }; extern void __check_pf (bool *seen_ipv4, bool *seen_ipv6, struct in6addrinfo **in6ai, size_t *in6ailen) attribute_hidden; +extern void __check_native (uint32_t a1_index, int *a1_native, + uint32_t a2_index, int *a2_native) + attribute_hidden; #endif /* ifaddrs.h */ diff --git a/inet/Makefile b/inet/Makefile index 5823b69e9c..2250cc6e8c 100644 --- a/inet/Makefile +++ b/inet/Makefile @@ -49,7 +49,7 @@ routines := htonl htons \ getipv4sourcefilter setipv4sourcefilter \ getsourcefilter setsourcefilter inet6_opt inet6_rth -aux := check_pf ifreq +aux := check_pf check_native ifreq tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \ tst-gethnm test-ifaddrs bug-if1 test-inet6_opt diff --git a/posix/tst-rfc3484-2.c b/posix/tst-rfc3484-2.c index f9fffd5d05..6d60ccd3bc 100644 --- a/posix/tst-rfc3484-2.c +++ b/posix/tst-rfc3484-2.c @@ -18,6 +18,12 @@ __check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen) *in6ai = NULL; *in6ailen = 0; } +void +attribute_hidden +__check_native (uint32_t a1_index, int *a1_native, + uint32_t a2_index, int *a2_native) +{ +} int __idna_to_ascii_lz (const char *input, char **output, int flags) { @@ -100,6 +106,7 @@ do_test (void) results[0].source_addr_flags = 0; results[0].service_order = 0; results[0].prefixlen = 16; + results[0].index = 0; memcpy (&results[0].source_addr, &so1, sizeof (so1)); results[1].dest_addr = &ai2; @@ -108,10 +115,12 @@ do_test (void) results[1].source_addr_flags = 0; results[1].service_order = 1; results[1].prefixlen = 16; + results[1].index = 0; memcpy (&results[1].source_addr, &so2, sizeof (so2)); - qsort (results, 2, sizeof (results[0]), rfc3484_sort); + struct sort_result_combo combo = { .results = results, .nresults = 2 }; + qsort_r (results, 2, sizeof (results[0]), rfc3484_sort, &combo); int result = 0; if (results[0].dest_addr->ai_family == AF_INET6) @@ -128,6 +137,7 @@ do_test (void) results[1].source_addr_flags = 0; results[1].service_order = 1; results[1].prefixlen = 16; + results[1].index = 0; memcpy (&results[1].source_addr, &so1, sizeof (so1)); results[0].dest_addr = &ai2; @@ -136,10 +146,11 @@ do_test (void) results[0].source_addr_flags = 0; results[0].service_order = 0; results[0].prefixlen = 16; + results[0].index = 0; memcpy (&results[0].source_addr, &so2, sizeof (so2)); - qsort (results, 2, sizeof (results[0]), rfc3484_sort); + qsort_r (results, 2, sizeof (results[0]), rfc3484_sort, &combo); if (results[0].dest_addr->ai_family == AF_INET6) { diff --git a/posix/tst-rfc3484.c b/posix/tst-rfc3484.c index 477e2b220d..c4dd04e023 100644 --- a/posix/tst-rfc3484.c +++ b/posix/tst-rfc3484.c @@ -18,6 +18,12 @@ __check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen) *in6ai = NULL; *in6ailen = 0; } +void +attribute_hidden +__check_native (uint32_t a1_index, int *a1_native, + uint32_t a2_index, int *a2_native) +{ +} int __idna_to_ascii_lz (const char *input, char **output, int flags) { @@ -95,9 +101,11 @@ do_test (void) results[i].source_addr_flags = 0; results[i].service_order = i; results[i].prefixlen = 8; + results[i].index = 0; } - qsort (results, naddrs, sizeof (results[0]), rfc3484_sort); + struct sort_result_combo combo = { .results = results, .nresults = naddrs }; + qsort_r (results, naddrs, sizeof (results[0]), rfc3484_sort, &combo); int result = 0; for (int i = 0; i < naddrs; ++i) 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 #include -#ifdef HAVE_NETLINK_ROUTE -# include -#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +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 -#ifndef IFA_F_TEMPORARY -# define IFA_F_TEMPORARY IFA_F_SECONDARY -#endif #ifndef IFA_F_HOMEADDRESS # define IFA_F_HOMEADDRESS 0 #endif @@ -189,13 +186,11 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6, & (IFA_F_DEPRECATED | 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; -- cgit 1.4.1