From 2302736429850e2c965f750a24ceb9fcc6c0f056 Mon Sep 17 00:00:00 2001 From: Sergey Doroshenko Date: Mon, 5 Jan 2015 11:00:41 -0800 Subject: [PATCH] folly: AsyncServerSocket::getAddress: prefer IPv6 Summary: Can't connect from ipv6-only cluster to ipv4/ipv6 service which uses different ports. Facebook: A lot of tests start a service, get its port, and try to connect to 127.0.0.1:port, which breaks after this diff since the port corresponds to IPv6 address, not IPv4. Fixing this by changing 127.0.0.1 to ::1 in places found by sandcastle. ``` fbconfig servicerouter/aggregator/tests servicerouter/client/cpp/test common/fb303/cpp/test thrift/lib/cpp2/test unicorn/hotswap/test common/client_mgmt thrift/test servicerouter/client/swig/tests unicorn/async/test servicerouter/selection/tests ``` Test Plan: ``` $ ./fastcopy_server --dir . & $ netstat -atnlp | grep LISTEN | grep fastcopy 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 Subscribers: trunkagent, vkatich, fbcode-common-diffs@, chaoyc, search-fbcode-diffs@, davejwatson, andrewcox, mcduff, hitesh, unicorn-diffs@, alandau, mshneer, folly-diffs@, bmatheny, ps, soren FB internal diff: D1760372 Tasks: 5868818, 5886688 Signature: t1:1760372:1419992695:e7d254b2b8f730baefc169effa236b8daa9d846d --- folly/io/async/AsyncServerSocket.cpp | 33 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/folly/io/async/AsyncServerSocket.cpp b/folly/io/async/AsyncServerSocket.cpp index 7539ac67..ef6b8902 100644 --- a/folly/io/async/AsyncServerSocket.cpp +++ b/folly/io/async/AsyncServerSocket.cpp @@ -364,13 +364,13 @@ void AsyncServerSocket::bind(uint16_t port) { freeaddrinfo(res0); }); - 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); @@ -397,7 +397,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"); @@ -418,10 +437,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_); } -- 2.34.1