--- libc/include/ifaddrs.h.jj 2006-09-24 18:49:19.000000000 +0200 +++ libc/include/ifaddrs.h 2008-01-10 18:04:39.000000000 +0100 @@ -12,12 +12,19 @@ struct in6addrinfo in6ai_deprecated = 1, in6ai_temporary = 2, in6ai_homeaddress = 4 - } flags; + } 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) + struct in6addrinfo **in6ai, size_t *in6ailen, + bool f9_mode) + 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 */ --- libc/nscd/gai.c.jj 2008-01-08 18:46:29.000000000 +0100 +++ libc/nscd/gai.c 2008-01-10 18:34:45.000000000 +0100 @@ -34,6 +34,7 @@ /* Support code. */ #include <check_pf.c> +#include <check_native.c> #ifdef HAVE_LIBIDN # include <libidn/idn-stub.c> #endif --- libc/inet/Makefile.jj 2008-01-10 13:51:34.000000000 +0100 +++ libc/inet/Makefile 2008-01-10 18:22:56.000000000 +0100 @@ -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 tst-ether_line --- libc/posix/tst-rfc3484-3.c.jj 2008-01-10 19:28:08.000000000 +0100 +++ libc/posix/tst-rfc3484-3.c 2008-01-10 22:43:47.000000000 +0100 @@ -0,0 +1,139 @@ +#include <stdbool.h> +#include <stdio.h> +#include <ifaddrs.h> + +/* Internal definitions used in the libc code. */ +#define __getservbyname_r getservbyname_r +#define __socket socket +#define __getsockname getsockname +#define __inet_aton inet_aton +#define __gethostbyaddr_r gethostbyaddr_r +#define __gethostbyname2_r gethostbyname2_r + +void +attribute_hidden +__check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen, bool f9_mode) +{ + *p1 = *p2 = true; + *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) +{ + return 0; +} +int +__idna_to_unicode_lzlz (const char *input, char **output, int flags) +{ + *output = NULL; + return 0; +} + +#include "../sysdeps/posix/getaddrinfo.c" + +service_user *__nss_hosts_database attribute_hidden; + + +/* This is the beginning of the real test code. The above defines + (among other things) the function rfc3484_sort. */ + + +#if __BYTE_ORDER == __BIG_ENDIAN +# define h(n) n +#else +# define h(n) __bswap_constant_32 (n) +#endif + +struct sockaddr_in addrs[] = +{ + { .sin_family = AF_INET, .sin_addr = { h (0xa0a86d1d) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xa0a85d03) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xa0a82c3d) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xa0a86002) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xa0a802f3) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xa0a80810) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xa0a85e02) } }, + { .sin_family = AF_INET, .sin_addr = { h (0xac162311) } }, + { .sin_family = AF_INET, .sin_addr = { h (0x0a324572) } } +}; +#define naddrs (sizeof (addrs) / sizeof (addrs[0])) +static struct addrinfo ais[naddrs]; +static struct sort_result results[naddrs]; +static struct service_order order[naddrs]; + +static const int expected[naddrs] = + { + 8, 0, 1, 2, 3, 4, 5, 6, 7 + }; + +static const struct scopeentry new_scopes[] = + { + { { { 169, 254, 0, 0 } }, h (0xffff0000), 2 }, + { { { 127, 0, 0, 0 } }, h (0xff000000), 2 }, + { { { 10, 0, 0, 0 } }, h (0xff000000), 5 }, + { { { 192, 168, 0, 0 } }, h(0xffff0000), 5 }, + { { { 0, 0, 0, 0 } }, h (0x00000000), 14 } + }; + + +ssize_t +__getline (char **lineptr, size_t *n, FILE *s) +{ + *lineptr = NULL; + *n = 0; + return 0; +} + + +static int +do_test (void) +{ + labels = default_labels; + precedence = default_precedence; + scopes= new_scopes; + + struct sockaddr_in so; + so.sin_family = AF_INET; + so.sin_addr.s_addr = h (0x0aa85f19); + struct sort_result_combo combo = { .results = results, .nresults = naddrs, .f9_mode = 1 }; + + for (int i = 0; i < naddrs; ++i) + { + ais[i].ai_family = AF_INET; + ais[i].ai_addr = (struct sockaddr *) &addrs[i]; + results[i].dest_addr = &ais[i]; + results[i].got_source_addr = true; + memcpy(&results[i].source_addr, &so, sizeof (so)); + results[i].source_addr_len = sizeof (so); + results[i].source_addr_flags = 0; + results[i].prefixlen = 8; + results[i].index = 0; + order[i].index = i; + order[i].src = &combo; + } + + qsort (order, naddrs, sizeof (order[0]), rfc3484_sort); + + int result = 0; + for (int i = 0; i < naddrs; ++i) + { + struct in_addr addr = ((struct sockaddr_in *) (results[order[i].index].dest_addr->ai_addr))->sin_addr; + + int here = memcmp (&addr, &addrs[expected[i]].sin_addr, + sizeof (struct in_addr)); + printf ("[%d] = %s: %s\n", i, inet_ntoa (addr), here ? "FAIL" : "OK"); + result |= here; + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" --- libc/posix/gai.conf.jj 2006-05-18 18:57:50.000000000 +0200 +++ libc/posix/gai.conf 2008-01-10 23:39:09.000000000 +0100 @@ -26,6 +26,7 @@ #label ::ffff:0:0/96 4 #label fec0::/10 5 #label fc00::/7 6 +#label 2001:0::/32 7 # # This default differs from the tables given in RFC 3484 by handling # (now obsolete) site-local IPv6 addresses and Unique Local Addresses. @@ -35,10 +36,11 @@ # site-local IPv4 and IPv6 addresses a lookup for a global address would # see the IPv6 be preferred. The result is a long delay because the # site-local IPv6 addresses cannot be used while the IPv4 address is -# (at least for the foreseeable future) NATed. +# (at least for the foreseeable future) NATed. We also treat Teredo +# tunnels special. # # precedence <mask> <value> -# Add another rule the to RFC 3484 precedence table. See section 2.1 +# Add another rule to the RFC 3484 precedence table. See section 2.1 # and 10.3 in RFC 3484. The default is: # #precedence ::1/128 50 @@ -50,3 +52,19 @@ # For sites which prefer IPv4 connections change the last line to # #precedence ::ffff:0:0/96 100 + +# +# scopev4 <mask> <value> +# Add another rule to the RFC 3484 scope table for IPv4 addresses. +# By default the scope IDs described in section 3.2 in RFC 3484 are +# used. Changing these defaults should hardly ever be necessary. +# Presence of at least one scopev4 rule enables new getaddrinfo +# sorting rules, if the default scope table is desirable, uncomment +# otherwise unmodified 6 scopev4 rules below. +# The defaults are equivalent to: +# +#scopev4 ::ffff:169.254.0.0/112 2 +#scopev4 ::ffff:127.0.0.0/104 2 +#scopev4 ::ffff:10.0.0.0/104 5 +#scopev4 ::ffff:172.16.0.0/108 5 +#scopev4 ::ffff:192.168.0.0/112 5 --- libc/posix/tst-rfc3484-2.c.jj 2006-05-04 08:36:16.000000000 +0200 +++ libc/posix/tst-rfc3484-2.c 2008-01-10 22:41:35.000000000 +0100 @@ -12,12 +12,18 @@ void attribute_hidden -__check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen) +__check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen, bool f9_mode) { *p1 = *p2 = true; *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) { @@ -26,6 +32,7 @@ __idna_to_ascii_lz (const char *input, c int __idna_to_unicode_lzlz (const char *input, char **output, int flags) { + *output = NULL; return 0; } @@ -59,6 +66,7 @@ do_test (void) { labels = default_labels; precedence = default_precedence; + scopes = default_scopes; struct sockaddr_in so1; so1.sin_family = AF_INET; @@ -92,24 +100,34 @@ do_test (void) struct sort_result results[2]; + struct sort_result_combo combo = { .results = results, .nresults = 2, .f9_mode = 0 }; + struct sort_order order[2]; results[0].dest_addr = &ai1; results[0].got_source_addr = true; results[0].source_addr_len = sizeof (so1); results[0].source_addr_flags = 0; + results[0].prefixlen = 16; + results[0].index = 0; memcpy (&results[0].source_addr, &so1, sizeof (so1)); + order[0].index = 0; + order[0].src = &combo; results[1].dest_addr = &ai2; results[1].got_source_addr = true; results[1].source_addr_len = sizeof (so2); results[1].source_addr_flags = 0; + results[1].prefixlen = 16; + results[1].index = 0; memcpy (&results[1].source_addr, &so2, sizeof (so2)); + order[1].index = 1; + order[1].src = &combo; - qsort (results, 2, sizeof (results[0]), rfc3484_sort); + qsort (order, 2, sizeof (order[0]), rfc3484_sort); int result = 0; - if (results[0].dest_addr->ai_family == AF_INET6) + if (results[order[0].index].dest_addr->ai_family == AF_INET6) { puts ("wrong order in first test"); result |= 1; @@ -121,18 +139,26 @@ do_test (void) results[1].got_source_addr = true; results[1].source_addr_len = sizeof (so1); results[1].source_addr_flags = 0; + results[1].prefixlen = 16; + results[1].index = 0; memcpy (&results[1].source_addr, &so1, sizeof (so1)); + order[1].index = 1; + order[1].src = &combo; results[0].dest_addr = &ai2; results[0].got_source_addr = true; results[0].source_addr_len = sizeof (so2); results[0].source_addr_flags = 0; + results[0].prefixlen = 16; + results[0].index = 0; memcpy (&results[0].source_addr, &so2, sizeof (so2)); + order[0].index = 0; + order[0].src = &combo; - qsort (results, 2, sizeof (results[0]), rfc3484_sort); + qsort (order, 2, sizeof (order[0]), rfc3484_sort); - if (results[0].dest_addr->ai_family == AF_INET6) + if (results[order[0].index].dest_addr->ai_family == AF_INET6) { puts ("wrong order in second test"); result |= 1; --- libc/posix/tst-rfc3484.c.jj 2006-05-04 08:36:16.000000000 +0200 +++ libc/posix/tst-rfc3484.c 2008-01-10 22:46:24.000000000 +0100 @@ -12,12 +12,18 @@ void attribute_hidden -__check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen) +__check_pf (bool *p1, bool *p2, struct in6addrinfo **in6ai, size_t *in6ailen, bool f9_mode) { *p1 = *p2 = true; *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) { @@ -26,6 +32,7 @@ __idna_to_ascii_lz (const char *input, c int __idna_to_unicode_lzlz (const char *input, char **output, int flags) { + *output = NULL; return 0; } @@ -57,6 +64,7 @@ struct sockaddr_in addrs[] = #define naddrs (sizeof (addrs) / sizeof (addrs[0])) static struct addrinfo ais[naddrs]; static struct sort_result results[naddrs]; +static struct sort_order order[naddrs]; static int expected[naddrs] = { @@ -78,10 +86,12 @@ do_test (void) { labels = default_labels; precedence = default_precedence; + scopes= default_scopes; struct sockaddr_in so; so.sin_family = AF_INET; so.sin_addr.s_addr = h (0xc0a85f19); + struct sort_result_combo combo = { .results = results, .nresults = naddrs, .f9_mode = 0 }; for (int i = 0; i < naddrs; ++i) { @@ -92,14 +102,18 @@ do_test (void) memcpy(&results[i].source_addr, &so, sizeof (so)); results[i].source_addr_len = sizeof (so); results[i].source_addr_flags = 0; + results[i].prefixlen = 8; + results[i].index = 0; + order[i].index = i; + order[i].src = &combo; } - qsort (results, naddrs, sizeof (results[0]), rfc3484_sort); + qsort (order, naddrs, sizeof (order[0]), rfc3484_sort); int result = 0; for (int i = 0; i < naddrs; ++i) { - struct in_addr addr = ((struct sockaddr_in *) (results[i].dest_addr->ai_addr))->sin_addr; + struct in_addr addr = ((struct sockaddr_in *) (results[order[i].index].dest_addr->ai_addr))->sin_addr; int here = memcmp (&addr, &addrs[expected[i]].sin_addr, sizeof (struct in_addr)); --- libc/sysdeps/posix/getaddrinfo.c.jj 2008-01-08 18:46:29.000000000 +0100 +++ libc/sysdeps/posix/getaddrinfo.c 2008-01-10 23:45:43.000000000 +0100 @@ -60,6 +60,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBI #include <not-cancel.h> #include <nscd/nscd-client.h> #include <nscd/nscd_proto.h> +#include <atomic.h> #ifdef HAVE_LIBIDN extern int __idna_to_ascii_lz (const char *input, char **output, int flags); @@ -1120,21 +1121,71 @@ static const struct gaih gaih[] = struct sort_result { struct addrinfo *dest_addr; - struct sockaddr_storage source_addr; + /* Using sockaddr_storage is for now overkill. We only support IPv4 + and IPv6 so far. If this changes at some point we can adjust the + type here. */ + struct sockaddr_in6 source_addr; uint8_t source_addr_len; 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; + bool f9_mode; +}; + +struct sort_order +{ + size_t index; + struct sort_result_combo *src; }; +#if __BYTE_ORDER == __BIG_ENDIAN +# define htonl_c(n) n +#else +# define htonl_c(n) __bswap_constant_32 (n) +#endif + +static const struct scopeentry +{ + union + { + char addr[4]; + uint32_t addr32; + }; + uint32_t netmask; + int32_t scope; +} default_scopes[] = + { + /* Link-local addresses: scope 2. */ + { { { 169, 254, 0, 0 } }, htonl_c (0xffff0000), 2 }, + { { { 127, 0, 0, 0 } }, htonl_c (0xff000000), 2 }, + /* Site-local addresses: scope 5. */ + { { { 10, 0, 0, 0 } }, htonl_c (0xff000000), 5 }, + { { { 172, 16, 0, 0 } }, htonl_c (0xfff00000), 5 }, + { { { 192, 168, 0, 0 } }, htonl_c (0xffff0000), 5 }, + /* Default: scope 14. */ + { { { 0, 0, 0, 0 } }, htonl_c (0x00000000), 14 } + }; + +/* The label table. */ +static const struct scopeentry *scopes; +static bool f9_mode = false; + + static int -get_scope (const struct sockaddr_storage *ss) +get_scope (const struct sockaddr_in6 *in6) { int scope; - if (ss->ss_family == PF_INET6) + if (in6->sin6_family == PF_INET6) { - const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss; - if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr)) { if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr)) @@ -1148,20 +1199,20 @@ get_scope (const struct sockaddr_storage else scope = in6->sin6_addr.s6_addr[1] & 0xf; } - else if (ss->ss_family == PF_INET) + else if (in6->sin6_family == PF_INET) { - const struct sockaddr_in *in = (const struct sockaddr_in *) ss; - const uint8_t *addr = (const uint8_t *) &in->sin_addr; + const struct sockaddr_in *in = (const struct sockaddr_in *) in6; - /* RFC 3484 specifies how to map IPv6 addresses to scopes. - 169.254/16 and 127/8 are link-local. */ - if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127) - scope = 2; - else if (addr[0] == 10 || (addr[0] == 172 && (addr[1] & 0xf0) == 16) - || (addr[0] == 192 && addr[1] == 168)) - scope = 5; - else - scope = 14; + size_t cnt = 0; + while (1) + { + if ((in->sin_addr.s_addr & scopes[cnt].netmask) + == scopes[cnt].addr32) + return scopes[cnt].scope; + + ++cnt; + } + /* NOTREACHED */ } else /* XXX What is a good default? */ @@ -1259,18 +1310,15 @@ static const struct prefixentry default_ static int -match_prefix (const struct sockaddr_storage *ss, +match_prefix (const struct sockaddr_in6 *in6, const struct prefixentry *list, int default_val) { int idx; struct sockaddr_in6 in6_mem; - const struct sockaddr_in6 *in6; - if (ss->ss_family == PF_INET6) - in6 = (const struct sockaddr_in6 *) ss; - else if (ss->ss_family == PF_INET) + if (in6->sin6_family == PF_INET) { - const struct sockaddr_in *in = (const struct sockaddr_in *) ss; + const struct sockaddr_in *in = (const struct sockaddr_in *) in6; /* Convert to IPv6 address. */ in6_mem.sin6_family = PF_INET6; @@ -1289,7 +1337,7 @@ match_prefix (const struct sockaddr_stor in6 = &in6_mem; } - else + else if (in6->sin6_family != PF_INET6) return default_val; for (idx = 0; ; ++idx) @@ -1321,18 +1369,18 @@ match_prefix (const struct sockaddr_stor static int -get_label (const struct sockaddr_storage *ss) +get_label (const struct sockaddr_in6 *in6) { /* XXX What is a good default value? */ - return match_prefix (ss, labels, INT_MAX); + return match_prefix (in6, labels, INT_MAX); } static int -get_precedence (const struct sockaddr_storage *ss) +get_precedence (const struct sockaddr_in6 *in6) { /* XXX What is a good default value? */ - return match_prefix (ss, precedence, 0); + return match_prefix (in6, precedence, 0); } @@ -1352,8 +1400,11 @@ fls (uint32_t a) static int rfc3484_sort (const void *p1, const void *p2) { - const struct sort_result *a1 = (const struct sort_result *) p1; - const struct sort_result *a2 = (const struct sort_result *) p2; + const size_t idx1 = ((const struct sort_order *) p1)->index; + const size_t idx2 = ((const struct sort_order *) p2)->index; + struct sort_result_combo *src = ((const struct sort_order *) p1)->src; + struct sort_result *a1 = &src->results[idx1]; + struct sort_result *a2 = &src->results[idx2]; /* Rule 1: Avoid unusable destinations. We have the got_source_addr flag set if the destination is reachable. */ @@ -1366,10 +1417,10 @@ rfc3484_sort (const void *p1, const void /* Rule 2: Prefer matching scope. Only interesting if both destination addresses are IPv6. */ int a1_dst_scope - = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr); + = get_scope ((struct sockaddr_in6 *) a1->dest_addr->ai_addr); int a2_dst_scope - = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr); + = get_scope ((struct sockaddr_in6 *) a2->dest_addr->ai_addr); if (a1->got_source_addr) { @@ -1409,11 +1460,11 @@ rfc3484_sort (const void *p1, const void if (a1->got_source_addr) { int a1_dst_label - = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr); + = get_label ((struct sockaddr_in6 *) a1->dest_addr->ai_addr); int a1_src_label = get_label (&a1->source_addr); int a2_dst_label - = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr); + = get_label ((struct sockaddr_in6 *) a2->dest_addr->ai_addr); int a2_src_label = get_label (&a2->source_addr); if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label) @@ -1425,9 +1476,9 @@ rfc3484_sort (const void *p1, const void /* Rule 6: Prefer higher precedence. */ int a1_prec - = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr); + = get_precedence ((struct sockaddr_in6 *) a1->dest_addr->ai_addr); int a2_prec - = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr); + = get_precedence ((struct sockaddr_in6 *) a2->dest_addr->ai_addr); if (a1_prec > a2_prec) return -1; @@ -1438,14 +1489,72 @@ rfc3484_sort (const void *p1, const void /* 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 (__builtin_expect (src->f9_mode, 0) && a1->index != a2->index) + { + int a1_native = a1->native; + int a2_native = a2->native; + + if (a1_native == -1 || a2_native == -1) + { + uint32_t a1_index; + if (a1_native == -1) + { + /* If we do not have the information use 'native' as + the default. */ + a1_native = 0; + a1_index = a1->index; + } + else + a1_index = 0xffffffffu; - /* XXX Do we need to check anything beside temporary addresses? */ + uint32_t a2_index; + if (a2_native == -1) + { + /* If we do not have the information use 'native' as + the default. */ + a2_native = 0; + a2_index = a2->index; + } + else + a2_index = 0xffffffffu; + + __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 (src->results[i].index == a1_index) + { + assert (src->results[i].native == -1 + || src->results[i].native == a1_native); + src->results[i].native = a1_native; + } + else if (src->results[i].index == a2_index) + { + assert (src->results[i].native == -1 + || src->results[i].native == a2_native); + src->results[i].native = a2_native; + } + } + + if (a1_native && !a2_native) + return -1; + if (!a1_native && a2_native) + return 1; + } + else + { + 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; + + /* XXX Do we need to check anything beside temporary addresses? */ + } } @@ -1463,10 +1572,42 @@ rfc3484_sort (const void *p1, const void int bit1 = 0; int bit2 = 0; - if (a1->dest_addr->ai_family == PF_INET) + if (__builtin_expect (src->f9_mode, 0) + && a1->dest_addr->ai_family == PF_INET) { - assert (a1->source_addr.ss_family == PF_INET); - assert (a2->source_addr.ss_family == PF_INET); + assert (a1->source_addr.sin6_family == PF_INET); + assert (a2->source_addr.sin6_family == PF_INET); + + /* Outside of subnets, as defined by the network masks, + common address prefixes for IPv4 addresses make no sense. + So, define a non-zero value only if source and + destination address are on the same subnet. */ + struct sockaddr_in *in1_dst + = (struct sockaddr_in *) a1->dest_addr->ai_addr; + in_addr_t in1_dst_addr = ntohl (in1_dst->sin_addr.s_addr); + struct sockaddr_in *in1_src + = (struct sockaddr_in *) &a1->source_addr; + in_addr_t in1_src_addr = ntohl (in1_src->sin_addr.s_addr); + in_addr_t netmask1 = 0xffffffffu << (32 - a1->prefixlen); + + if ((in1_src_addr & netmask1) == (in1_dst_addr & netmask1)) + bit1 = fls (in1_dst_addr ^ in1_src_addr); + + struct sockaddr_in *in2_dst + = (struct sockaddr_in *) a2->dest_addr->ai_addr; + in_addr_t in2_dst_addr = ntohl (in2_dst->sin_addr.s_addr); + struct sockaddr_in *in2_src + = (struct sockaddr_in *) &a2->source_addr; + in_addr_t in2_src_addr = ntohl (in2_src->sin_addr.s_addr); + in_addr_t netmask2 = 0xffffffffu << (32 - a2->prefixlen); + + if ((in2_src_addr & netmask2) == (in2_dst_addr & netmask2)) + bit2 = fls (in2_dst_addr ^ in2_src_addr); + } + else if (a1->dest_addr->ai_family == PF_INET) + { + assert (a1->source_addr.sin6_family == PF_INET); + assert (a2->source_addr.sin6_family == PF_INET); struct sockaddr_in *in1_dst; struct sockaddr_in *in1_src; @@ -1485,8 +1626,8 @@ rfc3484_sort (const void *p1, const void } else if (a1->dest_addr->ai_family == PF_INET6) { - assert (a1->source_addr.ss_family == PF_INET6); - assert (a2->source_addr.ss_family == PF_INET6); + assert (a1->source_addr.sin6_family == PF_INET6); + assert (a2->source_addr.sin6_family == PF_INET6); struct sockaddr_in6 *in1_dst; struct sockaddr_in6 *in1_src; @@ -1522,7 +1663,12 @@ rfc3484_sort (const void *p1, const void } - /* Rule 10: Otherwise, leave the order unchanged. */ + /* Rule 10: Otherwise, leave the order unchanged. To ensure this + compare with the value indicating the order in which the entries + have been received from the services. NB: no two entries can have + the same order so the test will never return zero. */ + if (__builtin_expect (src->f9_mode, 0)) + return idx1 < idx2 ? -1 : 1; return 0; } @@ -1545,6 +1691,9 @@ in6aicmp (const void *p1, const void *p2 whenever it changed. */ static int gaiconf_reload_flag; +/* Non-zero if gaiconf_reload_flag was ever set to true. */ +static int gaiconf_reload_flag_ever_set; + /* Last modification time. */ static struct timespec gaiconf_mtime; @@ -1564,6 +1713,13 @@ libc_freeres_fn(fini) precedence = default_precedence; free ((void *) old); } + + if (scopes != default_scopes) + { + const struct scopeentry *old = scopes; + scopes = default_scopes; + free ((void *) old); + } } @@ -1574,6 +1730,13 @@ struct prefixlist }; +struct scopelist +{ + struct scopeentry entry; + struct scopelist *next; +}; + + static void free_prefixlist (struct prefixlist *list) { @@ -1586,6 +1749,18 @@ free_prefixlist (struct prefixlist *list } +static void +free_scopelist (struct scopelist *list) +{ + while (list != NULL) + { + struct scopelist *oldp = list; + list = list->next; + free (oldp); + } +} + + static int prefixcmp (const void *p1, const void *p2) { @@ -1600,6 +1775,20 @@ prefixcmp (const void *p1, const void *p } +static int +scopecmp (const void *p1, const void *p2) +{ + const struct scopeentry *e1 = (const struct scopeentry *) p1; + const struct scopeentry *e2 = (const struct scopeentry *) p2; + + if (e1->netmask > e2->netmask) + return -1; + if (e1->netmask == e2->netmask) + return 0; + return 1; +} + + static void gaiconf_init (void) { @@ -1609,6 +1798,9 @@ gaiconf_init (void) struct prefixlist *precedencelist = NULL; size_t nprecedencelist = 0; bool precedencelist_nullbits = false; + struct scopelist *scopelist = NULL; + size_t nscopelist = 0; + bool scopelist_nullbits = false; FILE *fp = fopen (GAICONF_FNAME, "rc"); if (fp != NULL) @@ -1699,7 +1891,7 @@ gaiconf_init (void) || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX || errno != ERANGE) && *endp == '\0' - && bits <= INT_MAX + && bits <= 128 && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX || errno != ERANGE) && *endp == '\0' @@ -1726,7 +1918,79 @@ gaiconf_init (void) case 6: if (strcmp (cmd, "reload") == 0) - gaiconf_reload_flag = strcmp (val1, "yes") == 0; + { + gaiconf_reload_flag = strcmp (val1, "yes") == 0; + if (gaiconf_reload_flag) + gaiconf_reload_flag_ever_set = 1; + } + break; + + case 7: + if (strcmp (cmd, "scopev4") == 0) + { + struct in6_addr prefix; + unsigned long int bits; + unsigned long int val; + char *endp; + + f9_mode = true; + bits = 32; + __set_errno (0); + cp = strchr (val1, '/'); + if (cp != NULL) + *cp++ = '\0'; + if (inet_pton (AF_INET6, val1, &prefix)) + { + if (IN6_IS_ADDR_V4MAPPED (&prefix) + && (cp == NULL + || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX + || errno != ERANGE) + && *endp == '\0' + && bits >= 96 + && bits <= 128 + && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX + || errno != ERANGE) + && *endp == '\0' + && val <= INT_MAX) + { + struct scopelist *newp; + new_scope: + newp = malloc (sizeof (*newp)); + if (newp == NULL) + { + free (line); + fclose (fp); + goto no_file; + } + + newp->entry.netmask = htonl (bits != 96 + ? (0xffffffff + << (128 - bits)) + : 0); + newp->entry.addr32 = (prefix.s6_addr32[3] + & newp->entry.netmask); + newp->entry.scope = val; + newp->next = scopelist; + scopelist = newp; + ++nscopelist; + scopelist_nullbits |= bits == 96; + } + } + else if (inet_pton (AF_INET, val1, &prefix.s6_addr32[3]) + && (cp == NULL + || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX + || errno != ERANGE) + && *endp == '\0' + && bits <= 32 + && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX + || errno != ERANGE) + && *endp == '\0' + && val <= INT_MAX) + { + bits += 96; + goto new_scope; + } + } break; case 10: @@ -1812,12 +2076,52 @@ gaiconf_init (void) /* Sort the entries so that the most specific ones are at the beginning. */ - qsort (new_precedence, nprecedencelist, sizeof (*new_labels), + qsort (new_precedence, nprecedencelist, sizeof (*new_precedence), prefixcmp); } else new_precedence = (struct prefixentry *) default_precedence; + struct scopeentry *new_scopes; + if (nscopelist > 0) + { + if (!scopelist_nullbits) + ++nscopelist; + new_scopes = malloc (nscopelist * sizeof (*new_scopes)); + if (new_scopes == NULL) + { + if (new_labels != default_labels) + free (new_labels); + if (new_precedence != default_precedence) + free (new_precedence); + goto no_file; + } + + int i = nscopelist; + if (!scopelist_nullbits) + { + --i; + new_scopes[i].addr32 = 0; + new_scopes[i].netmask = 0; + new_scopes[i].scope = 14; + } + + struct scopelist *l = scopelist; + while (i-- > 0) + { + new_scopes[i] = l->entry; + l = l->next; + } + free_scopelist (scopelist); + + /* Sort the entries so that the most specific ones are at + the beginning. */ + qsort (new_scopes, nscopelist, sizeof (*new_scopes), + scopecmp); + } + else + new_scopes = (struct scopeentry *) default_scopes; + /* Now we are ready to replace the values. */ const struct prefixentry *old = labels; labels = new_labels; @@ -1829,6 +2133,11 @@ gaiconf_init (void) if (old != default_precedence) free ((void *) old); + const struct scopeentry *oldscope = scopes; + scopes = new_scopes; + if (oldscope != default_scopes) + free ((void *) oldscope); + gaiconf_mtime = st.st_mtim; } else @@ -1836,6 +2145,7 @@ gaiconf_init (void) no_file: free_prefixlist (labellist); free_prefixlist (precedencelist); + free_scopelist (scopelist); /* If we previously read the file but it is gone now, free the old data and use the builtin one. Leave the reload flag @@ -1864,6 +2174,8 @@ getaddrinfo (const char *name, const cha struct addrinfo *p = NULL; struct gaih_service gaih_service, *pservice; struct addrinfo local_hints; + __libc_once_define (static, once); + __typeof (once) old_once = once; if (name != NULL && name[0] == '*' && name[1] == 0) name = NULL; @@ -1890,22 +2202,22 @@ getaddrinfo (const char *name, const cha return EAI_BADFLAGS; struct in6addrinfo *in6ai = NULL; - size_t in6ailen; + size_t in6ailen = 0; bool seen_ipv4 = false; bool seen_ipv6 = false; - /* We might need information about what kind of interfaces are available. - But even if AI_ADDRCONFIG is not used, if the user requested IPv6 - addresses we have to know whether an address is deprecated or - temporary. */ - if ((hints->ai_flags & AI_ADDRCONFIG) || hints->ai_family == PF_UNSPEC - || hints->ai_family == PF_INET6) - /* 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); - + int f9m = 0; if (hints->ai_flags & AI_ADDRCONFIG) { + /* Read the config file. */ + __libc_once (once, gaiconf_init); + f9m = f9_mode; + f9m = atomic_forced_read (f9m); + /* 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, f9m); + /* Now make a decision on what we return, if anything. */ if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6)) { @@ -2035,27 +2347,38 @@ getaddrinfo (const char *name, const cha if (naddrs > 1) { /* Read the config file. */ - __libc_once_define (static, once); - __typeof (once) old_once = once; - __libc_once (once, gaiconf_init); - if (old_once && gaiconf_reload_flag) - gaiconf_reload (); + if (!(hints->ai_flags & AI_ADDRCONFIG)) + { + __libc_once (once, gaiconf_init); + f9m = f9_mode; + f9m = atomic_forced_read (f9m); + __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen, f9m); + } /* Sort results according to RFC 3484. */ struct sort_result results[nresults]; + struct sort_order order[nresults]; struct addrinfo *q; struct addrinfo *last = NULL; char *canonname = NULL; + struct sort_result_combo src + = { .results = results, .nresults = nresults, .f9_mode = f9m }; + /* If we have information about deprecated and temporary address sort the array now. */ if (in6ai != NULL) qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp); + int fd = -1; + int af = AF_UNSPEC; + for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next) { results[i].dest_addr = q; - results[i].got_source_addr = false; + results[i].native = -1; + order[i].index = i; + order[i].src = &src; /* If we just looked up the address for a different protocol, reuse the result. */ @@ -2067,16 +2390,37 @@ getaddrinfo (const char *name, const cha results[i].source_addr_len = results[i - 1].source_addr_len; 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 cannot determine the source address remember this fact. */ - int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP); + if (__builtin_expect (!src.f9_mode, 1)) + fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP); + else if (fd == -1 || (af == AF_INET && q->ai_family == AF_INET6)) + { + if (fd != -1) + close_retry: + close_not_cancel_no_status (fd); + af = q->ai_family; + fd = __socket (af, SOCK_DGRAM, IPPROTO_IP); + } + else + { + /* Reset the connection. */ + struct sockaddr sa = { .sa_family = AF_UNSPEC }; + __connect (fd, &sa, sizeof (sa)); + } + socklen_t sl = sizeof (results[i].source_addr); if (fd != -1 && __connect (fd, q->ai_addr, q->ai_addrlen) == 0 @@ -2087,7 +2431,8 @@ getaddrinfo (const char *name, const cha results[i].source_addr_len = sl; results[i].got_source_addr = true; - if (q->ai_family == PF_INET6 && in6ai != NULL) + if (__builtin_expect (!src.f9_mode, 1) + && q->ai_family == PF_INET6 && in6ai != NULL) { /* See whether the address is the list of deprecated or temporary addresses. */ @@ -2100,14 +2445,77 @@ getaddrinfo (const char *name, const cha if (found != NULL) results[i].source_addr_flags = found->flags; } + else if (__builtin_expect (src.f9_mode, 0) && in6ai != NULL) + { + /* See whether the source address is on the list of + deprecated or temporary addresses. */ + struct in6addrinfo tmp; + + if (q->ai_family == AF_INET && af == AF_INET) + { + struct sockaddr_in *sinp + = (struct sockaddr_in *) &results[i].source_addr; + tmp.addr[0] = 0; + tmp.addr[1] = 0; + tmp.addr[2] = htonl (0xffff); + tmp.addr[3] = sinp->sin_addr.s_addr; + } + else + { + struct sockaddr_in6 *sin6p + = (struct sockaddr_in6 *) &results[i].source_addr; + memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ); + } + + struct in6addrinfo *found + = bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai), + in6aicmp); + if (found != NULL) + { + results[i].source_addr_flags = found->flags; + results[i].prefixlen = found->prefixlen; + results[i].index = found->index; + } + } + + if (__builtin_expect (src.f9_mode, 0) + && q->ai_family == AF_INET && af == AF_INET6) + { + /* We have to convert the address. The socket is + IPv6 and the request is for IPv4. */ + struct sockaddr_in6 *sin6 + = (struct sockaddr_in6 *) &results[i].source_addr; + struct sockaddr_in *sin + = (struct sockaddr_in *) &results[i].source_addr; + assert (IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr32)); + sin->sin_family = AF_INET; + /* We do not have to initialize sin_port since this + fields has the same position and size in the IPv6 + structure. */ + assert (offsetof (struct sockaddr_in, sin_port) + == offsetof (struct sockaddr_in6, sin6_port)); + assert (sizeof (sin->sin_port) + == sizeof (sin6->sin6_port)); + memcpy (&sin->sin_addr, + &sin6->sin6_addr.s6_addr32[3], INADDRSZ); + results[i].source_addr_len = sizeof (struct sockaddr_in); + } } + else if (__builtin_expect (src.f9_mode, 0) && errno == EAFNOSUPPORT + && af == AF_INET6 && q->ai_family == AF_INET) + /* This could mean IPv6 sockets are IPv6-only. */ + goto close_retry; else /* Just make sure that if we have to process the same address again we do not copy any memory. */ results[i].source_addr_len = 0; - if (fd != -1) - close_not_cancel_no_status (fd); + if (__builtin_expect (!src.f9_mode, 1)) + { + if (fd != -1) + close_not_cancel_no_status (fd); + fd = -1; + } } /* Remember the canonical name. */ @@ -2119,14 +2527,28 @@ getaddrinfo (const char *name, const cha } } + if (fd != -1) + close_not_cancel_no_status (fd); + /* We got all the source addresses we can get, now sort using the information. */ - qsort (results, nresults, sizeof (results[0]), rfc3484_sort); + if (__builtin_expect (gaiconf_reload_flag_ever_set, 0)) + { + __libc_lock_define_initialized (static, lock); + + __libc_lock_lock (lock); + if (old_once && gaiconf_reload_flag) + gaiconf_reload (); + qsort (order, nresults, sizeof (order[0]), rfc3484_sort); + __libc_lock_unlock (lock); + } + else + qsort (order, nresults, sizeof (order[0]), rfc3484_sort); /* Queue the results up as they come out of sorting. */ - q = p = results[0].dest_addr; + q = p = results[order[0].index].dest_addr; for (i = 1; i < nresults; ++i) - q = q->ai_next = results[i].dest_addr; + q = q->ai_next = results[order[i].index].dest_addr; q->ai_next = NULL; /* Fill in the canonical name into the new first entry. */ --- libc/sysdeps/unix/sysv/linux/check_native.c.jj 2008-01-10 18:16:31.000000000 +0100 +++ libc/sysdeps/unix/sysv/linux/check_native.c 2007-11-24 04:12:17.000000000 +0100 @@ -0,0 +1,172 @@ +/* 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 <asm/types.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; + 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) + { + struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlmh); + int native = (ifim->ifi_type != ARPHRD_TUNNEL6 + && ifim->ifi_type != ARPHRD_TUNNEL + && ifim->ifi_type != ARPHRD_SIT); + + if (a1_index == ifim->ifi_index) + { + *a1_native = native; + a1_index = 0xffffffffu; + } + if (a2_index == ifim->ifi_index) + { + *a2_native = native; + a2_index = 0xffffffffu; + } + + if (a1_index == 0xffffffffu + && a2_index == 0xffffffffu) + goto out; + } + 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); + + return; + +out_fail: + if (use_malloc) + free (buf); +} --- libc/sysdeps/unix/sysv/linux/check_pf.c.jj 2008-01-08 21:13:53.000000000 +0100 +++ libc/sysdeps/unix/sysv/linux/check_pf.c 2008-01-10 18:27:03.000000000 +0100 @@ -48,7 +48,7 @@ static int make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6, - struct in6addrinfo **in6ai, size_t *in6ailen) + struct in6addrinfo **in6ai, size_t *in6ailen, bool f9_mode) { struct req { @@ -139,7 +139,76 @@ make_request (int fd, pid_t pid, bool *s || nlmh->nlmsg_seq != req.nlh.nlmsg_seq) continue; - if (nlmh->nlmsg_type == RTM_NEWADDR) + if (__builtin_expect (f9_mode, 0) + && nlmh->nlmsg_type == RTM_NEWADDR) + { + struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh); + struct rtattr *rta = IFA_RTA (ifam); + size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam)); + + if (ifam->ifa_family != AF_INET + && ifam->ifa_family != AF_INET6) + continue; + + const void *local = NULL; + const void *address = NULL; + while (RTA_OK (rta, len)) + { + switch (rta->rta_type) + { + case IFA_LOCAL: + local = RTA_DATA (rta); + break; + + case IFA_ADDRESS: + address = RTA_DATA (rta); + goto out; + } + + rta = RTA_NEXT (rta, len); + } + + if (local != NULL) + { + address = local; + out: + if (ifam->ifa_family == AF_INET) + { + if (*(const in_addr_t *) address + != htonl (INADDR_LOOPBACK)) + *seen_ipv4 = true; + } + else + { + if (!IN6_IS_ADDR_LOOPBACK (address)) + *seen_ipv6 = true; + } + } + + struct in6ailist *newp = alloca (sizeof (*newp)); + newp->info.flags = (((ifam->ifa_flags + & (IFA_F_DEPRECATED + | IFA_F_OPTIMISTIC)) + ? in6ai_deprecated : 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; + newp->info.addr[1] = 0; + newp->info.addr[2] = htonl (0xffff); + newp->info.addr[3] = *(const in_addr_t *) address; + } + else + memcpy (newp->info.addr, address, sizeof (newp->info.addr)); + newp->next = in6ailist; + in6ailist = newp; + ++in6ailistlen; + } + else if (nlmh->nlmsg_type == RTM_NEWADDR) { struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh); @@ -209,6 +278,9 @@ make_request (int fd, pid_t pid, bool *s close_not_cancel_no_status (fd); + if (__builtin_expect (f9_mode, 0) && !*seen_ipv6) + in6ailist = NULL; + if (in6ailist != NULL) { *in6ai = malloc (in6ailistlen * sizeof (**in6ai)); @@ -249,7 +321,7 @@ extern int __no_netlink_support attribut void attribute_hidden __check_pf (bool *seen_ipv4, bool *seen_ipv6, - struct in6addrinfo **in6ai, size_t *in6ailen) + struct in6addrinfo **in6ai, size_t *in6ailen, bool f9_mode) { *in6ai = NULL; *in6ailen = 0; @@ -268,7 +340,7 @@ __check_pf (bool *seen_ipv4, bool *seen_ && __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, - in6ai, in6ailen) == 0) + in6ai, in6ailen, f9_mode) == 0) /* It worked. */ return;