* 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.
This commit is contained in:
Ulrich Drepper 2007-11-14 06:58:35 +00:00
parent e458144c99
commit 773e79b316
8 changed files with 300 additions and 75 deletions

View File

@ -1,5 +1,21 @@
2007-11-13 Ulrich Drepper <drepper@redhat.com>
* 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.

View File

@ -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 */

View File

@ -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

View File

@ -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)
{

View File

@ -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)

View File

@ -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);
/* XXX Do we need to check anything beside temporary addresses? */
/* 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;
}
}
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;

View File

@ -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);
}

View File

@ -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
@ -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;