diff --git a/Makefile b/Makefile
index b42ab57cee863e58534034203d8bf5ef35361871..dece13bdd69ac1baf42f7ecec25cef31d9b48b0a 100644
--- a/Makefile
+++ b/Makefile
@@ -1013,7 +1013,6 @@ LIBGRPC_TEST_UTIL_SRC = \
     test/core/statistics/census_log_tests.c \
     test/core/transport/transport_end2end_tests.c \
     test/core/util/grpc_profiler.c \
-    test/core/util/ipv6_posix.c \
     test/core/util/parse_hexstring.c \
     test/core/util/port_posix.c \
     test/core/util/slice_splitter.c \
diff --git a/build.json b/build.json
index 64a1df266b2e54987e3448cb12bdbe7d786dac9f..07e2318e4b0e9b0626826abd49e4ff0962b4c257 100644
--- a/build.json
+++ b/build.json
@@ -289,7 +289,6 @@
         "test/core/statistics/census_log_tests.c",
         "test/core/transport/transport_end2end_tests.c",
         "test/core/util/grpc_profiler.c",
-        "test/core/util/ipv6_posix.c",
         "test/core/util/parse_hexstring.c",
         "test/core/util/port_posix.c",
         "test/core/util/slice_splitter.c",
diff --git a/src/core/iomgr/socket_utils_common_posix.c b/src/core/iomgr/socket_utils_common_posix.c
index 0767d6f91802c0d0152b6666e5559bc9c7fdffe6..7f2b43f2cad110608db3413bb6a4d42ee0ccf78f 100644
--- a/src/core/iomgr/socket_utils_common_posix.c
+++ b/src/core/iomgr/socket_utils_common_posix.c
@@ -50,6 +50,7 @@
 #include <grpc/support/string.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
 
 /* set a socket to non blocking mode */
 int grpc_set_socket_nonblocking(int fd, int non_blocking) {
@@ -111,6 +112,34 @@ int grpc_set_socket_low_latency(int fd, int low_latency) {
          newval == val;
 }
 
+static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
+static int g_ipv6_loopback_available;
+
+static void probe_ipv6_once() {
+  int fd = socket(AF_INET6, SOCK_STREAM, 0);
+  g_ipv6_loopback_available = 0;
+  if (fd < 0) {
+    gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed.");
+  } else {
+    struct sockaddr_in6 addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
+    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
+      g_ipv6_loopback_available = 1;
+    } else {
+      gpr_log(GPR_INFO,
+              "Disabling AF_INET6 sockets because ::1 is not available.");
+    }
+    close(fd);
+  }
+}
+
+int grpc_ipv6_loopback_available() {
+  gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once);
+  return g_ipv6_loopback_available;
+}
+
 /* This should be 0 in production, but it may be enabled for testing or
    debugging purposes, to simulate an environment where IPv6 sockets can't
    also speak IPv4. */
@@ -132,7 +161,13 @@ int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
                                  int protocol, grpc_dualstack_mode *dsmode) {
   int family = addr->sa_family;
   if (family == AF_INET6) {
-    int fd = socket(family, type, protocol);
+    int fd;
+    if (grpc_ipv6_loopback_available()) {
+      fd = socket(family, type, protocol);
+    } else {
+      fd = -1;
+      errno = EAFNOSUPPORT;
+    }
     /* Check if we've got a valid dualstack socket. */
     if (fd >= 0 && set_socket_dualstack(fd)) {
       *dsmode = GRPC_DSMODE_DUALSTACK;
diff --git a/src/core/iomgr/socket_utils_posix.h b/src/core/iomgr/socket_utils_posix.h
index 5c31e5e6d882156c30454f4517c7046d5ec51a58..9c5d93c2b4bce303a9c6a95c35732231f759b5ec 100644
--- a/src/core/iomgr/socket_utils_posix.h
+++ b/src/core/iomgr/socket_utils_posix.h
@@ -53,6 +53,16 @@ int grpc_set_socket_reuse_addr(int fd, int reuse);
 /* disable nagle */
 int grpc_set_socket_low_latency(int fd, int low_latency);
 
+/* Returns true if this system can create AF_INET6 sockets bound to ::1.
+   The value is probed once, and cached for the life of the process.
+
+   This is more restrictive than checking for socket(AF_INET6) to succeed,
+   because Linux with "net.ipv6.conf.all.disable_ipv6 = 1" is able to create
+   and bind IPv6 sockets, but cannot connect to a getsockname() of [::]:port
+   without a valid loopback interface.  Rather than expose this half-broken
+   state to library users, we turn off IPv6 sockets. */
+int grpc_ipv6_loopback_available();
+
 /* An enum to keep track of IPv4/IPv6 socket modes.
 
    Currently, this information is only used when a socket is first created, but
diff --git a/test/core/echo/echo_test.c b/test/core/echo/echo_test.c
index cc265cadbf0295d3c536eed2d1d0c4be8389dd87..748e8bc0ef09b65d80a53b266917c5e5ce4bc13e 100644
--- a/test/core/echo/echo_test.c
+++ b/test/core/echo/echo_test.c
@@ -40,11 +40,11 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include "src/core/iomgr/socket_utils_posix.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string.h>
-#include "test/core/util/ipv6.h"
 #include "test/core/util/port.h"
 
 int test_client(const char *root, const char *host, int port) {
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 808fcbd65d2a7059c040a93574fb0bdfbe3e0dc3..b443caa2a67249702d9c42b7c111f0333969716b 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -37,7 +37,6 @@
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include "test/core/end2end/cq_verifier.h"
-#include "test/core/util/ipv6.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/core/util/ipv6.h b/test/core/util/ipv6.h
deleted file mode 100644
index bb536645b8775467270fe22f1c0bffa856082a25..0000000000000000000000000000000000000000
--- a/test/core/util/ipv6.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef __GRPC_TEST_UTIL_IPV6_H__
-#define __GRPC_TEST_UTIL_IPV6_H__
-
-/* Returns true if we're able to create an AF_INET6 socket bound to ::1 on an
-   arbitrary port. */
-int grpc_ipv6_loopback_available();
-
-#endif /* __GRPC_TEST_UTIL_IPV6_H__ */
diff --git a/test/core/util/ipv6_posix.c b/test/core/util/ipv6_posix.c
deleted file mode 100644
index 25758f59fea8e328b99e28277b85d8a198b62f91..0000000000000000000000000000000000000000
--- a/test/core/util/ipv6_posix.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <grpc/support/port_platform.h>
-#ifdef GPR_POSIX_SOCKET
-
-#include "test/core/util/ipv6.h"
-
-#include <netinet/in.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-int grpc_ipv6_loopback_available() {
-  int ok = 0;
-  int fd = socket(AF_INET6, SOCK_STREAM, 0);
-  if (fd >= 0) {
-    struct sockaddr_in6 addr;
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
-    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
-      ok = 1;
-    }
-    close(fd);
-  }
-  return ok;
-}
-
-#endif  /* GPR_POSIX_SOCKET */
diff --git a/vsprojects/vs2013/grpc_test_util.vcxproj b/vsprojects/vs2013/grpc_test_util.vcxproj
index 9a118cf590ccb24effa0502854780bed79928a3d..a86d415cc6ed62cbe9dab14a3fe388e334adf0cf 100644
--- a/vsprojects/vs2013/grpc_test_util.vcxproj
+++ b/vsprojects/vs2013/grpc_test_util.vcxproj
@@ -89,8 +89,6 @@
     </ClCompile>
     <ClCompile Include="..\..\test\core\util\grpc_profiler.c">
     </ClCompile>
-    <ClCompile Include="..\..\test\core\util\ipv6_posix.c">
-    </ClCompile>
     <ClCompile Include="..\..\test\core\util\parse_hexstring.c">
     </ClCompile>
     <ClCompile Include="..\..\test\core\util\port_posix.c">