From: Lucian Grijincu Date: Tue, 23 Dec 2014 10:19:34 +0000 (-0800) Subject: folly: AsyncServerSocket::getAddress: prefer IPv6 X-Git-Tag: v0.22.0~75 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=cc42e3ad3a3ef875f5353c06b9b4a99202ba445f;p=folly.git folly: AsyncServerSocket::getAddress: prefer IPv6 Summary: Can't connect from ipv6-only cluster to ipv4/ipv6 service which uses different ports. Test Plan: ``` ./fastcopy_server --dir . & # netstat -atnlp | grep LISTEN | grep fastc tcp 0 0 0.0.0.0:65478 0.0.0.0:* LISTEN 9348/./fastcopy_ser tcp 0 0 :::52793 :::* LISTEN 9348/./fastcopy_ser ``` Reviewed By: philipp@fb.com, sdoroshenko@fb.com Subscribers: ps, bmatheny, folly-diffs@ FB internal diff: D1752846 Tasks: 5868818 Signature: t1:1752846:1419043494:7cc0646882249f17258ade5ce7ae5619b13148a0 --- diff --git a/folly/io/async/AsyncServerSocket.cpp b/folly/io/async/AsyncServerSocket.cpp index ea2a46d9..ba0b925d 100644 --- a/folly/io/async/AsyncServerSocket.cpp +++ b/folly/io/async/AsyncServerSocket.cpp @@ -365,13 +365,13 @@ void AsyncServerSocket::bind(uint16_t port) { }); DCHECK(&guard); - for (res = res0; res; res = res->ai_next) { + auto setupAddress = [&] (struct addrinfo* res) { int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); // IPv6/IPv4 may not be supported by the kernel if (s < 0 && errno == EAFNOSUPPORT) { - continue; + return; } - CHECK(s); + CHECK_GE(s, 0); try { setupSocket(s); @@ -398,7 +398,26 @@ void AsyncServerSocket::bind(uint16_t port) { errno, "failed to bind to async server socket for port"); } + }; + + // Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo + // should return IPv6 first and then IPv4 addresses, but glibc's + // getaddrinfo(nullptr) with AI_PASSIVE returns: + // - 0.0.0.0 (IPv4-only) + // - :: (IPv6+IPv4) in this order + // See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981 + for (res = res0; res; res = res->ai_next) { + if (res->ai_family == AF_INET6) { + setupAddress(res); + } } + + for (res = res0; res; res = res->ai_next) { + if (res->ai_family != AF_INET6) { + setupAddress(res); + } + } + if (sockets_.size() == 0) { throw std::runtime_error( "did not bind any async server socket for port"); @@ -419,10 +438,10 @@ void AsyncServerSocket::listen(int backlog) { void AsyncServerSocket::getAddress(SocketAddress* addressReturn) const { CHECK(sockets_.size() >= 1); - if (sockets_.size() > 1) { - VLOG(2) << "Warning: getAddress can return multiple addresses, " << - "but getAddress was called, so only returning the first"; - } + VLOG_IF(2, sockets_.size() > 1) + << "Warning: getAddress() called and multiple addresses available (" + << sockets_.size() << "). Returning only the first one."; + addressReturn->setFromLocalAddress(sockets_[0].socket_); }