Skip to content
Permalink
Browse files

8241786: Improve heuristic to determine default network interface on …

…macOS

DefaultInetrface.getDefault is updated to prefer interfaces that have non link-local addresses. NetworkConfiguration is updated to skip interface that have only link-local addresses, whether IPv4 or IPv6, for multicasting.

Reviewed-by: chegar, alanb
  • Loading branch information
dfuch committed Apr 3, 2020
1 parent 553ea1e commit f541970b3148877e9297a1033e554fd90f3882f9
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,11 +54,30 @@ static NetworkInterface getDefault() {
/**
* Choose a default interface. This method returns the first interface that
* is both "up" and supports multicast. This method chooses an interface in
* order of preference:
* 1. neither loopback nor point to point
* ( prefer interfaces with dual IP support )
* 2. point to point
* 3. loopback
* order of preference, using the following algorithm:
*
* <pre>
* Interfaces that are down, or don't support multicasting, are skipped.
* In steps 1-4 below, PPP and loopback interfaces are skipped.
*
* 1. The first interface that has at least an IPv4 address, and an IPv6 address,
* and a non link-local IP address, is picked.
*
* 2. If none is found, then the first interface that has at least an
* IPv4 address, and an IPv6 address is picked.
*
* 3. If none is found, then the first interface that has at least a
* non link local IP address is picked.
*
* 4. If none is found, then the first non loopback and non PPP interface
* is picked.
*
* 5. If none is found then first PPP interface is picked.
*
* 6. If none is found, then the first loopback interface is picked.
*
* 7. If none is found, then null is returned.
* </pre>
*
* @return the chosen interface or {@code null} if there isn't a suitable
* default
@@ -74,6 +93,8 @@ private static NetworkInterface chooseDefaultInterface() {
}

NetworkInterface preferred = null;
NetworkInterface dual = null;
NetworkInterface nonLinkLocal = null;
NetworkInterface ppp = null;
NetworkInterface loopback = null;

@@ -83,7 +104,7 @@ private static NetworkInterface chooseDefaultInterface() {
if (!ni.isUp() || !ni.supportsMulticast())
continue;

boolean ip4 = false, ip6 = false;
boolean ip4 = false, ip6 = false, isNonLinkLocal = false;
PrivilegedAction<Enumeration<InetAddress>> pa = ni::getInetAddresses;
Enumeration<InetAddress> addrs = AccessController.doPrivileged(pa);
while (addrs.hasMoreElements()) {
@@ -94,6 +115,9 @@ private static NetworkInterface chooseDefaultInterface() {
} else if (addr instanceof Inet6Address) {
ip6 = true;
}
if (!addr.isLinkLocalAddress()) {
isNonLinkLocal = true;
}
}
}

@@ -104,8 +128,13 @@ private static NetworkInterface chooseDefaultInterface() {
// point-to-point interface
if (preferred == null) {
preferred = ni;
} else if (ip4 && ip6){
return ni;
}
if (ip4 && ip6) {
if (isNonLinkLocal) return ni;
if (dual == null) dual = ni;
}
if (nonLinkLocal == null) {
if (isNonLinkLocal) nonLinkLocal = ni;
}
}
if (ppp == null && isPPP)
@@ -116,7 +145,11 @@ private static NetworkInterface chooseDefaultInterface() {
} catch (IOException skip) { }
}

if (preferred != null) {
if (dual != null) {
return dual;
} else if (nonLinkLocal != null) {
return nonLinkLocal;
} else if (preferred != null) {
return preferred;
} else {
return (ppp != null) ? ppp : loopback;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,14 @@
* @bug 4686717
* @summary Test MulticastSocket.setLoopbackMode
* @library /test/lib
* @modules java.base/java.net:+open
* @build jdk.test.lib.NetworkConfiguration
* jdk.test.lib.Platform
* @run main/othervm SetLoopbackMode
*/

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.*;
import java.io.IOException;
import jdk.test.lib.NetworkConfiguration;
@@ -99,38 +102,75 @@ public static void main (String args[]) throws Exception {
int failures = 0;
NetworkConfiguration nc = NetworkConfiguration.probe();

MulticastSocket mc = new MulticastSocket();
InetAddress grp = InetAddress.getByName("224.80.80.80");
try (MulticastSocket mc = new MulticastSocket()) {
InetAddress grp = InetAddress.getByName("224.80.80.80");


/*
* If IPv6 is available then use IPv6 multicast group - needed
* to workaround Linux IPv6 bug whereby !IPV6_MULTICAST_LOOP
* doesn't prevent loopback of IPv4 multicast packets.
*/
/*
* If IPv6 is available then use IPv6 multicast group - needed
* to workaround Linux IPv6 bug whereby !IPV6_MULTICAST_LOOP
* doesn't prevent loopback of IPv4 multicast packets.
*/

if (canUseIPv6(nc)) {
grp = InetAddress.getByName("ff01::1");
}
if (canUseIPv6(nc)) {
System.out.println("IPv6 can be used");
grp = InetAddress.getByName("ff01::1");
} else {
System.out.println("IPv6 cannot be used: using IPv4");
}
System.out.println("Default network interface: " + DefaultInterface.getDefaultName());
System.out.println("\nTest will use multicast group: " + grp);
try {
System.out.println("NetworkInterface.getByInetAddress(grp): "
+ getName(NetworkInterface.getByInetAddress(grp)));
} catch (Exception x) {
// OK
}
mc.joinGroup(grp);

//mc.setNetworkInterface(NetworkInterface.getByInetAddress(lb));
System.out.println("\nTest will use multicast group: " + grp);
mc.joinGroup(grp);
System.out.println("\n******************\n");

System.out.println("\n******************\n");
mc.setLoopbackMode(true);
if (test(mc, grp) == FAILED) failures++;

mc.setLoopbackMode(true);
if (test(mc, grp) == FAILED) failures++;
System.out.println("\n******************\n");

System.out.println("\n******************\n");
mc.setLoopbackMode(false);
if (test(mc, grp) == FAILED) failures++;

mc.setLoopbackMode(false);
if (test(mc, grp) == FAILED) failures++;
System.out.println("\n******************\n");

if (failures > 0) {
throw new RuntimeException("Test failed");
}
}
}

System.out.println("\n******************\n");
static String getName(NetworkInterface nif) {
return nif == null ? null : nif.getName();
}

if (failures > 0) {
throw new RuntimeException("Test failed");
static class DefaultInterface {
static final Method GET_DEFAULT;
static {
try {
GET_DEFAULT = Class.forName("java.net.DefaultInterface")
.getDeclaredMethod("getDefault");
GET_DEFAULT.setAccessible(true);
} catch (Exception x) {
throw new ExceptionInInitializerError(x);
}
}
static NetworkInterface getDefault() {
try {
return (NetworkInterface) GET_DEFAULT
.invoke(null);
} catch (Exception x) {
throw new UndeclaredThrowableException(x);
}
}
static String getDefaultName() {
return getName(getDefault());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,9 +23,10 @@

/*
* @test
* @bug 4686717
* @bug 4686717 8241786
* @summary Test MulticastSocket.setLoopbackMode with IPv4 addresses
* @library /test/lib
* @modules java.base/java.net:+open
* @build jdk.test.lib.NetworkConfiguration
* jdk.test.lib.Platform
* SetLoopbackMode

0 comments on commit f541970

Please sign in to comment.