From a798b9f0e171643b0bc7bbf79f14cb018689f715 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 20 Dec 2010 11:15:33 +0100 Subject: [PATCH 1/4] Backport (simple cherry-pick) d1624add2b73ce8ff7826ce27b1d6d6e35bb83a6 to gingerbread. Don't treat private IPv4 addresses as being in a non-global scope. The effect of this change is essentially to prefer NATed IPv4 over 6to4. --- libc/netbsd/net/getaddrinfo.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index e7564c427..aa3b81abb 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -1299,11 +1299,13 @@ _get_scope(const struct sockaddr *addr) if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */ (na & 0xffff0000) == 0xa9fe0000) { /* 169.254.0.0/16 */ return IPV6_ADDR_SCOPE_LINKLOCAL; - } else if ((na & 0xff000000) == 0x0a000000 || /* 10.0.0.0/8 */ - (na & 0xfff00000) == 0xac100000 || /* 172.16.0.0/12 */ - (na & 0xffff0000) == 0xc0a80000) { /* 192.168.0.0/16 */ - return IPV6_ADDR_SCOPE_SITELOCAL; } else { + /* + * According to draft-ietf-6man-rfc3484-revise-01 section 2.3, + * it is best not to treat the private IPv4 ranges + * (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16) as being + * in a special scope, so we don't. + */ return IPV6_ADDR_SCOPE_GLOBAL; } } else { From f4dca7be3b7bc181f1534be187428c5a52cb8c6a Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 20 Dec 2010 11:33:31 +0100 Subject: [PATCH 2/4] Backport (simple cherry-pick) 64b6c43379dba176659bc1313b6bb488ac94cfd6 to gingerbread. Add a new #define IN6_IS_ADDR_ULA, for testing for Universal Local IPv6 Unicast addresses (ULAs). These replace the old site-local IPv6 addresses. --- libc/include/netinet/in6.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libc/include/netinet/in6.h b/libc/include/netinet/in6.h index 2f5fee116..efd5c0a41 100644 --- a/libc/include/netinet/in6.h +++ b/libc/include/netinet/in6.h @@ -60,6 +60,10 @@ #define IN6_IS_ADDR_SITELOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) +/* RFC 4193. */ +#define IN6_IS_ADDR_ULA(a) \ + (((a)->s6_addr[0] & 0xfe) == 0xfc) + #define IN6_IS_ADDR_MULTICAST(a) \ (((__const uint8_t *) (a))[0] == 0xff) From 2e23e29245aa42d0f9419187c94e72dba3888eef Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 20 Dec 2010 11:48:07 +0100 Subject: [PATCH 3/4] Backport (simple cherry-pick) d33019030c1f0cddca557f9659e3c471bde0e6a9 to gingerbread. Implement RFC3484 policy table changes from draft-ietf-6man-rfc3484-revise-01. The changes in a nutshell: - Handle v4-mapped as different from v4-compat (this was probably an existing bug in our code). - Add policy entries for ULA, above most everything else. - Put v4-compat, old-style IPv6 site-local and 6bone addresses way down in the preference table. The rest is just shuffling numbers around (no actual changes to priority). --- libc/netbsd/net/getaddrinfo.c | 44 ++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index aa3b81abb..7ae1439ac 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -1327,9 +1327,13 @@ _get_scope(const struct sockaddr *addr) #define IN6_IS_ADDR_6TO4(a) \ (((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02)) +/* 6bone testing address area (3ffe::/16), deprecated in RFC 3701. */ +#define IN6_IS_ADDR_6BONE(a) \ + (((a)->s6_addr[0] == 0x3f) && ((a)->s6_addr[1] == 0xfe)) + /* * Get the label for a given IPv4/IPv6 address. - * RFC 3484, section 2.1, plus Teredo added in with label 5. + * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01. */ /*ARGSUSED*/ @@ -1337,19 +1341,27 @@ static int _get_label(const struct sockaddr *addr) { if (addr->sa_family == AF_INET) { - return 4; + return 3; } else if (addr->sa_family == AF_INET6) { const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { return 0; - } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { + } else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) { + return 1; + } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) { return 3; + } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { + return 4; } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { return 5; - } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { - return 2; + } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { + return 10; + } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) { + return 11; + } else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) { + return 12; } else { - return 1; + return 2; } } else { /* @@ -1362,7 +1374,7 @@ _get_label(const struct sockaddr *addr) /* * Get the precedence for a given IPv4/IPv6 address. - * RFC 3484, section 2.1, plus Teredo added in with precedence 25. + * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01. */ /*ARGSUSED*/ @@ -1370,22 +1382,28 @@ static int _get_precedence(const struct sockaddr *addr) { if (addr->sa_family == AF_INET) { - return 10; + return 30; } else if (addr->sa_family == AF_INET6) { const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { + return 60; + } else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) { return 50; - } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { + } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) { + return 30; + } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { return 20; } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { - return 25; - } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { - return 30; + return 10; + } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr) || + IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) || + IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) { + return 1; } else { return 40; } } else { - return 5; + return 1; } } From ba96e30fa08212e48ec1ff9c1d545b2d05e787b0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 14 Jan 2011 12:26:05 -0800 Subject: [PATCH 4/4] Only look up A records if the system has IPv4. getaddrinfo only asks DNS for IPv6 addresses if the system has IPv6 connectivity, but always asks for IPv4 addresses. Don't ask for IPv4 addresses if there is no IPv4 connectivity. Change-Id: Iefe9fcb006fabe60b4b11dd4653a7c4a406506f4 --- libc/netbsd/net/getaddrinfo.c | 88 +++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index e7564c427..895662ed0 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -340,35 +340,55 @@ str2number(const char *p) return -1; } -/* Determine whether IPv6 connectivity is available. */ +/* + * Connect a UDP socket to a given unicast address. This will cause no network + * traffic, but will fail fast if the system has no or limited reachability to + * the destination (e.g., no IPv4 address, no IPv6 default route, ...). + */ static int -_have_ipv6() { - /* - * Connect a UDP socket to an global unicast IPv6 address. This will - * cause no network traffic, but will fail fast if the system has no or - * limited IPv6 connectivity (e.g., only a link-local address). - */ - static const struct sockaddr_in6 sin6_test = { - /* family, port, flow label */ - AF_INET6, 0, 0, - /* 2000:: */ - {{{ 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}}, - /* scope ID */ - 0}; - sockaddr_union addr_test; - addr_test.in6 = sin6_test; - int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); +_test_connect(int pf, struct sockaddr *addr, size_t addrlen) { + int s = socket(pf, SOCK_DGRAM, IPPROTO_UDP); if (s < 0) return 0; int ret; do { - ret = connect(s, &addr_test.generic, sizeof(addr_test.in6)); + ret = connect(s, addr, addrlen); } while (ret < 0 && errno == EINTR); - int have_ipv6 = (ret == 0); + int success = (ret == 0); do { ret = close(s); } while (ret < 0 && errno == EINTR); - return have_ipv6; + return success; +} + +/* + * The following functions determine whether IPv4 or IPv6 connectivity is + * available in order to implement AI_ADDRCONFIG. + * + * Strictly speaking, AI_ADDRCONFIG should not look at whether connectivity is + * available, but whether addresses of the specified family are "configured + * on the local system". However, bionic doesn't currently support getifaddrs, + * so checking for connectivity is the next best thing. + */ +static int +_have_ipv6() { + static const struct sockaddr_in6 sin6_test = { + .sin6_family = AF_INET6, + .sin6_addr.s6_addr = { // 2000:: + 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + sockaddr_union addr = { .in6 = sin6_test }; + return _test_connect(PF_INET6, &addr.generic, sizeof(addr.in6)); +} + +static int +_have_ipv4() { + static const struct sockaddr_in sin_test = { + .sin_family = AF_INET, + .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8 + }; + sockaddr_union addr = { .in = sin_test }; + return _test_connect(PF_INET, &addr.generic, sizeof(addr.in)); } int @@ -1667,17 +1687,27 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) q.qclass = C_IN; q.answer = buf->buf; q.anslen = sizeof(buf->buf); - /* If AI_ADDRCONFIG, lookup IPv6 only if we have connectivity */ - if (!(pai->ai_flags & AI_ADDRCONFIG) || _have_ipv6()) { + int query_ipv6 = 1, query_ipv4 = 1; + if (pai->ai_flags & AI_ADDRCONFIG) { + query_ipv6 = _have_ipv6(); + query_ipv4 = _have_ipv4(); + } + if (query_ipv6) { q.qtype = T_AAAA; - q.next = &q2; - q2.name = name; - q2.qclass = C_IN; - q2.qtype = T_A; - q2.answer = buf2->buf; - q2.anslen = sizeof(buf2->buf); - } else { + if (query_ipv4) { + q.next = &q2; + q2.name = name; + q2.qclass = C_IN; + q2.qtype = T_A; + q2.answer = buf2->buf; + q2.anslen = sizeof(buf2->buf); + } + } else if (query_ipv4) { q.qtype = T_A; + } else { + free(buf); + free(buf2); + return NS_NOTFOUND; } break; case AF_INET: