blob: 94d55853f73f9c09a089b8ed4f6f03aea54b4c46 [file] [log] [blame]
/*
* Copyright (c) 2017, 2019, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.test.lib;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static java.net.NetworkInterface.getNetworkInterfaces;
import static java.util.Collections.list;
/**
* Helper class for retrieving network interfaces and local addresses
* suitable for testing.
*/
public class NetworkConfiguration {
private Map<NetworkInterface,List<Inet4Address>> ip4Interfaces;
private Map<NetworkInterface,List<Inet6Address>> ip6Interfaces;
private final boolean isIPv6Available;
private boolean has_testableipv6address = false;
private boolean has_sitelocaladdress = false;
private boolean has_linklocaladdress = false;
private boolean has_globaladdress = false;
private NetworkConfiguration(
Map<NetworkInterface,List<Inet4Address>> ip4Interfaces,
Map<NetworkInterface,List<Inet6Address>> ip6Interfaces) {
this.ip4Interfaces = ip4Interfaces;
this.ip6Interfaces = ip6Interfaces;
// initialize properties that can be queried
isIPv6Available = !ip6Interfaces().collect(Collectors.toList()).isEmpty();
ip6Interfaces().forEach(nif -> {
ip6Addresses(nif)
// On Solaris or AIX, a configuration with only local or loopback
// addresses does not fully enable IPv6 operations.
// E.g. IPv6 multicasting does not work.
// So, don't set has_testableipv6address if we only find these.
.filter(addr -> Platform.isSolaris() || Platform.isAix() ?
!(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) : true)
.forEach(ia -> {
has_testableipv6address = true;
if (ia.isLinkLocalAddress()) has_linklocaladdress = true;
if (ia.isSiteLocalAddress()) has_sitelocaladdress = true;
if (!ia.isLinkLocalAddress() &&
!ia.isSiteLocalAddress() &&
!ia.isLoopbackAddress()) {
has_globaladdress = true;
}
});
});
}
private static boolean isNotExcludedInterface(NetworkInterface nif) {
if (Platform.isOSX() && nif.getName().contains("awdl")) {
return false;
}
if (Platform.isWindows()) {
String dName = nif.getDisplayName();
if (dName != null && dName.contains("Teredo")) {
return false;
}
}
return true;
}
private static boolean isNotLoopback(NetworkInterface nif) {
try {
return !nif.isLoopback();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private boolean hasIp4Addresses(NetworkInterface nif) {
return ip4Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
}
private boolean hasIp6Addresses(NetworkInterface nif) {
return ip6Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
}
private boolean supportsIp4Multicast(NetworkInterface nif) {
try {
if (!nif.supportsMulticast()) {
return false;
}
// On AIX there is a bug:
// When IPv6 is enabled on the system, the JDK opens sockets as AF_INET6.
// If there's an interface configured with IPv4 addresses only, it should
// be able to become the network interface for a multicast socket (that
// could be in both, IPv4 or IPv6 space). But both possible setsockopt
// calls for either IPV6_MULTICAST_IF or IP_MULTICAST_IF return
// EADDRNOTAVAIL. So we must skip such interfaces here.
if (Platform.isAix() && isIPv6Available() && !hasIp6Addresses(nif)) {
return false;
}
return hasIp4Addresses(nif);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private boolean supportsIp6Multicast(NetworkInterface nif) {
try {
if (!nif.supportsMulticast()) {
return false;
}
return hasIp6Addresses(nif);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Returns whether IPv6 is available at all.
* This should resemble the result of native ipv6_available() in net_util.c
*/
public boolean isIPv6Available() {
return isIPv6Available;
}
/**
* Does any (usable) IPv6 address exist in the network configuration?
*/
public boolean hasTestableIPv6Address() {
return has_testableipv6address;
}
/**
* Does any site local address exist?
*/
public boolean hasSiteLocalAddress() {
return has_sitelocaladdress;
}
/**
* Does any link local address exist?
*/
public boolean hasLinkLocalAddress() {
return has_linklocaladdress;
}
/**
* Does any global IPv6 address exist?
*/
public boolean has_globaladdress() {
return has_globaladdress;
}
/**
* Returns a stream of interfaces suitable for functional tests.
*/
public Stream<NetworkInterface> interfaces() {
return Stream.concat(ip4Interfaces(), ip6Interfaces())
.distinct();
}
/**
* Returns a stream of interfaces suitable for IPv4 functional tests.
*/
public Stream<NetworkInterface> ip4Interfaces() {
return ip4Interfaces.keySet()
.stream()
.filter(NetworkConfiguration::isNotExcludedInterface)
.filter(this::hasIp4Addresses);
}
/**
* Returns a stream of interfaces suitable for IPv6 functional tests.
*/
public Stream<NetworkInterface> ip6Interfaces() {
return ip6Interfaces.keySet()
.stream()
.filter(NetworkConfiguration::isNotExcludedInterface)
.filter(this::hasIp6Addresses);
}
/**
* Returns a stream of interfaces suitable for functional tests.
*/
public Stream<NetworkInterface> multicastInterfaces(boolean includeLoopback) {
return Stream
.concat(ip4MulticastInterfaces(includeLoopback),
ip6MulticastInterfaces(includeLoopback))
.distinct();
}
/**
* Returns a stream of interfaces suitable for IPv4 multicast tests.
*
* The loopback interface will not be included.
*/
public Stream<NetworkInterface> ip4MulticastInterfaces() {
return ip4MulticastInterfaces(false);
}
/**
* Returns a stream of interfaces suitable for IPv4 multicast tests.
*/
public Stream<NetworkInterface> ip4MulticastInterfaces(boolean includeLoopback) {
return (includeLoopback) ?
ip4Interfaces().filter(this::supportsIp4Multicast) :
ip4Interfaces().filter(this::supportsIp4Multicast)
.filter(NetworkConfiguration::isNotLoopback);
}
/**
* Returns a stream of interfaces suitable for IPv6 multicast tests.
*
* The loopback interface will not be included.
*/
public Stream<NetworkInterface> ip6MulticastInterfaces() {
return ip6MulticastInterfaces(false);
}
/**
* Returns a stream of interfaces suitable for IPv6 multicast tests.
*/
public Stream<NetworkInterface> ip6MulticastInterfaces(boolean includeLoopback) {
return (includeLoopback) ?
ip6Interfaces().filter(this::supportsIp6Multicast) :
ip6Interfaces().filter(this::supportsIp6Multicast)
.filter(NetworkConfiguration::isNotLoopback);
}
/**
* Returns all addresses on all "functional" interfaces.
*/
public Stream<InetAddress> addresses(NetworkInterface nif) {
return Stream.concat(ip4Interfaces.get(nif).stream(),
ip6Interfaces.get(nif).stream());
}
/**
* Returns all IPv4 addresses on all "functional" interfaces.
*/
public Stream<Inet4Address> ip4Addresses() {
return ip4Interfaces().flatMap(this::ip4Addresses);
}
/**
* Returns all IPv6 addresses on all "functional" interfaces.
*/
public Stream<Inet6Address> ip6Addresses() {
return ip6Interfaces().flatMap(this::ip6Addresses);
}
/**
* Returns all IPv4 addresses the given interface.
*/
public Stream<Inet4Address> ip4Addresses(NetworkInterface nif) {
return ip4Interfaces.get(nif).stream();
}
/**
* Returns all IPv6 addresses for the given interface.
*/
public Stream<Inet6Address> ip6Addresses(NetworkInterface nif) {
return ip6Interfaces.get(nif).stream();
}
@Override
public String toString() {
return interfaces().map(NetworkConfiguration::interfaceInformation)
.collect(Collectors.joining());
}
/**
* Return a NetworkConfiguration instance.
*/
public static NetworkConfiguration probe() throws IOException {
Map<NetworkInterface, List<Inet4Address>> ip4Interfaces = new HashMap<>();
Map<NetworkInterface, List<Inet6Address>> ip6Interfaces = new HashMap<>();
List<NetworkInterface> nifs = list(getNetworkInterfaces());
for (NetworkInterface nif : nifs) {
// ignore interfaces that are down
if (!nif.isUp() || nif.isPointToPoint()) {
continue;
}
List<Inet4Address> ip4Addresses = new LinkedList<>();
List<Inet6Address> ip6Addresses = new LinkedList<>();
ip4Interfaces.put(nif, ip4Addresses);
ip6Interfaces.put(nif, ip6Addresses);
for (InetAddress addr : list(nif.getInetAddresses())) {
if (addr instanceof Inet4Address) {
ip4Addresses.add((Inet4Address) addr);
} else if (addr instanceof Inet6Address) {
ip6Addresses.add((Inet6Address) addr);
}
}
}
return new NetworkConfiguration(ip4Interfaces, ip6Interfaces);
}
/** Returns detailed information for the given interface. */
public static String interfaceInformation(NetworkInterface nif) {
StringBuilder sb = new StringBuilder();
try {
sb.append("Display name: ")
.append(nif.getDisplayName())
.append("\n");
sb.append("Name: ")
.append(nif.getName())
.append("\n");
for (InetAddress inetAddress : list(nif.getInetAddresses())) {
sb.append("InetAddress: ")
.append(inetAddress)
.append("\n");
}
sb.append("Up? ")
.append(nif.isUp())
.append("\n");
sb.append("Loopback? ")
.append(nif.isLoopback())
.append("\n");
sb.append("PointToPoint? ")
.append(nif.isPointToPoint())
.append("\n");
sb.append("Supports multicast? ")
.append(nif.supportsMulticast())
.append("\n");
sb.append("Virtual? ")
.append(nif.isVirtual())
.append("\n");
sb.append("Hardware address: ")
.append(Arrays.toString(nif.getHardwareAddress()))
.append("\n");
sb.append("MTU: ")
.append(nif.getMTU())
.append("\n");
sb.append("Index: ")
.append(nif.getIndex())
.append("\n");
sb.append("\n");
return sb.toString();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/** Prints all the system interface information to the give stream. */
public static void printSystemConfiguration(PrintStream out) {
PrivilegedAction<Void> pa = () -> {
try {
out.println("*** all system network interface configuration ***");
for (NetworkInterface nif : list(getNetworkInterfaces())) {
out.print(interfaceInformation(nif));
}
out.println("*** end ***");
return null;
} catch (IOException e) {
throw new UncheckedIOException(e);
}};
AccessController.doPrivileged(pa);
}
}