Skip to content

Commit

Permalink
Resolve to a local IPv6 address when --p2p-interface is ::0 (#8293)
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov authored May 8, 2024
1 parent 8293a3b commit 768da84
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.InetAddresses.isInetAddress;

import java.io.UncheckedIOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
Expand Down Expand Up @@ -130,40 +133,51 @@ private String resolveAnyLocalAddress(final String ipAddress) {
try {
final InetAddress advertisedAddress = InetAddress.getByName(ipAddress);
if (advertisedAddress.isAnyLocalAddress()) {
return getSiteLocalAddress();
final boolean useIPV6 = advertisedAddress instanceof Inet6Address;
return getSiteLocalOrUniqueLocalAddress(useIPV6);
} else {
return ipAddress;
}
} catch (UnknownHostException err) {
LOG.error(
"Unable to start LibP2PNetwork due to failed attempt at obtaining host address", err);
return ipAddress;
} catch (final UnknownHostException ex) {
throw new UncheckedIOException(ex);
}
}

private String getSiteLocalAddress() throws UnknownHostException {
private String getSiteLocalOrUniqueLocalAddress(final boolean useIPV6)
throws UnknownHostException {
try {
final InetAddress address = InetAddress.getLocalHost();
if (address.isAnyLocalAddress()) {
return address.getHostAddress();
}
final Enumeration<NetworkInterface> networkInterfaces =
NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface n = networkInterfaces.nextElement();
final Enumeration<InetAddress> inetAddresses = n.getInetAddresses();
final NetworkInterface networkInterface = networkInterfaces.nextElement();
final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress i = inetAddresses.nextElement();
if (i.isSiteLocalAddress()) {
return i.getHostAddress();
final InetAddress inetAddress = inetAddresses.nextElement();
// IPv4 (include only site local addresses)
if (!useIPV6 && inetAddress instanceof Inet4Address && inetAddress.isSiteLocalAddress()) {
return inetAddress.getHostAddress();
}
// IPv6 (include only site local addresses or ULAs)
if (useIPV6
&& inetAddress instanceof Inet6Address
&& (inetAddress.isSiteLocalAddress() || isUniqueLocalAddress(inetAddress))) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
LOG.error("Failed to find site local address", e);
throw new UnknownHostException(e.getMessage());
} catch (final SocketException ex) {
LOG.error("Failed to find site local or unique local address", ex);
throw new UnknownHostException(ex.getMessage());
}
throw new UnknownHostException("Unable to determine local IP Address");
throw new UnknownHostException(
String.format("Unable to determine local %s Address", useIPV6 ? "IPv6" : "IPv4"));
}

private boolean isUniqueLocalAddress(final InetAddress inetAddress) {
// Check the first byte to determine if it's in the fc00::/7 range
// Unique local IPv6 addresses start with 0xfc or 0xfd
final int firstByte = inetAddress.getAddress()[0] & 0xff; // Convert to unsigned
return (firstByte == 0xfc || firstByte == 0xfd);
}

public static class Builder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import static org.assertj.core.api.Assertions.assertThat;

import io.libp2p.core.multiformats.Multiaddr;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -55,11 +58,22 @@ void getAdvertisedIp_shouldResolveLocalhostIpWhenInterfaceIpIsAnyLocal() {
}

@Test
void getAdvertisedIp_shouldResolveLocalhostIpWhenInterfaceIpIsAnyLocalIpv6() {
void getAdvertisedIp_shouldResolveLocalhostIpWhenInterfaceIpIsAnyLocalIpv6()
throws UnknownHostException {
listenIp = "::0";
final String result = createConfig().getAdvertisedIp();
final String result;
try {
result = createConfig().getAdvertisedIp();
} catch (Exception ex) {
// local IPv6 not supported
assertThat(ex.getCause()).isInstanceOf(UnknownHostException.class);
assertThat(ex.getMessage()).contains("Unable to determine local IPv6 Address");
return;
}
assertThat(result).isNotEqualTo("::0");
assertThat(result).isNotEqualTo("0.0.0.0");
// check the advertised IP is IPv6
assertThat(InetAddress.getByName(result)).isInstanceOf(Inet6Address.class);
}

@Test
Expand Down

0 comments on commit 768da84

Please sign in to comment.