diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java index 700d30a5e4..da07c51177 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java @@ -150,7 +150,8 @@ public class PeerDiscoveryAgent implements DisconnectCallback { LOG.info("Starting peer discovery agent on host={}, port={}", host, port); vertx - .createDatagramSocket(new DatagramSocketOptions()) + .createDatagramSocket( + new DatagramSocketOptions().setIpV6(NetworkUtility.isIPv6Available())) .listen( port, host, diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java index 1081cf4414..a2a49168b2 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java @@ -25,6 +25,7 @@ import tech.pegasys.pantheon.ethereum.p2p.config.NetworkingConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryServiceException; import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.util.NetworkUtility; import java.io.IOException; @@ -56,7 +57,9 @@ public class NetworkingServiceLifecycleTest { service.run(); final int port = service.getDiscoverySocketAddress().getPort(); - assertEquals("/0.0.0.0:" + port, service.getDiscoverySocketAddress().toString()); + assertEquals( + (NetworkUtility.isIPv6Available() ? "/0:0:0:0:0:0:0:0:" : "/0.0.0.0:") + port, + service.getDiscoverySocketAddress().toString()); assertThat(service.getDiscoveryPeers()).hasSize(0); } } diff --git a/util/src/main/java/tech/pegasys/pantheon/util/NetworkUtility.java b/util/src/main/java/tech/pegasys/pantheon/util/NetworkUtility.java index 1e61bcec42..b4a57b8e2f 100644 --- a/util/src/main/java/tech/pegasys/pantheon/util/NetworkUtility.java +++ b/util/src/main/java/tech/pegasys/pantheon/util/NetworkUtility.java @@ -12,13 +12,56 @@ */ package tech.pegasys.pantheon.util; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; public class NetworkUtility { private NetworkUtility() {} + private static final Supplier ipv6Available = + Suppliers.memoize(NetworkUtility::checkIpv6Availability); + + /** + * Is IPv6 available? + * + * @return Returns true if the machine reports having any IPv6 addresses. + */ + public static boolean isIPv6Available() { + return ipv6Available.get(); + } + + /** + * The standard for IPv6 availability is if the machine has any IPv6 addresses. + * + * @return Returns true if any IPv6 addresses are iterable via {@link NetworkInterface}. + */ + private static Boolean checkIpv6Availability() { + try { + final Enumeration networkInterfaces = + NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + final Enumeration addresses = + networkInterfaces.nextElement().getInetAddresses(); + while (addresses.hasMoreElements()) { + if (addresses.nextElement() instanceof Inet6Address) { + // Found an IPv6 address, hence the IPv6 stack is available. + return true; + } + } + } + } catch (final Exception ignore) { + // Any exception means we treat it as not available. + } + return false; + } + /** * Checks the port is not null and is in the valid range port (1-65536). *