1 /*
   2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.test.lib;
  25 
  26 import java.io.IOException;
  27 import java.io.PrintStream;
  28 import java.io.UncheckedIOException;
  29 import java.net.Inet4Address;
  30 import java.net.Inet6Address;
  31 import java.net.InetAddress;
  32 import java.net.NetworkInterface;
  33 import java.util.Arrays;
  34 import java.util.HashMap;
  35 import java.util.LinkedList;
  36 import java.util.List;
  37 import java.util.Map;
  38 import java.util.stream.Collectors;
  39 import java.util.stream.Stream;
  40 import java.security.AccessController;
  41 import java.security.PrivilegedAction;
  42 
  43 import static java.net.NetworkInterface.getNetworkInterfaces;
  44 import static java.util.Collections.list;
  45 
  46 /**
  47  * Helper class for retrieving network interfaces and local addresses
  48  * suitable for testing.
  49  */
  50 public class NetworkConfiguration {
  51 
  52     private Map<NetworkInterface,List<Inet4Address>> ip4Interfaces;
  53     private Map<NetworkInterface,List<Inet6Address>> ip6Interfaces;
  54     private final boolean isIPv6Available;
  55     private boolean has_testableipv6address = false;
  56     private boolean has_sitelocaladdress = false;
  57     private boolean has_linklocaladdress = false;
  58     private boolean has_globaladdress = false;
  59 
  60     private NetworkConfiguration(
  61             Map<NetworkInterface,List<Inet4Address>> ip4Interfaces,
  62             Map<NetworkInterface,List<Inet6Address>> ip6Interfaces) {
  63         this.ip4Interfaces = ip4Interfaces;
  64         this.ip6Interfaces = ip6Interfaces;
  65 
  66         // initialize properties that can be queried
  67         isIPv6Available = !ip6Interfaces().collect(Collectors.toList()).isEmpty();
  68         ip6Interfaces().forEach(nif -> {
  69             ip6Addresses(nif)
  70                 // On Solaris or AIX, a configuration with only local or loopback
  71                 // addresses does not fully enable IPv6 operations.
  72                 // E.g. IPv6 multicasting does not work.
  73                 // So, don't set has_testableipv6address if we only find these.
  74                 .filter(addr -> Platform.isSolaris() || Platform.isAix() ?
  75                     !(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) : true)
  76                 .forEach(ia -> {
  77                     has_testableipv6address = true;
  78                     if (ia.isLinkLocalAddress()) has_linklocaladdress = true;
  79                     if (ia.isSiteLocalAddress()) has_sitelocaladdress = true;
  80 
  81                     if (!ia.isLinkLocalAddress() &&
  82                         !ia.isSiteLocalAddress() &&
  83                         !ia.isLoopbackAddress()) {
  84                         has_globaladdress = true;
  85                     }
  86                 });
  87         });
  88     }
  89 
  90     private static boolean isNotExcludedInterface(NetworkInterface nif) {
  91         if (Platform.isOSX() && nif.getName().contains("awdl")) {
  92             return false;
  93         }
  94         if (Platform.isWindows()) {
  95             String dName = nif.getDisplayName();
  96             if (dName != null && dName.contains("Teredo")) {
  97                 return false;
  98             }
  99         }
 100         return true;
 101     }
 102 
 103     private static boolean isNotLoopback(NetworkInterface nif) {
 104         try {
 105             return !nif.isLoopback();
 106         } catch (IOException e) {
 107             throw new UncheckedIOException(e);
 108         }
 109     }
 110 
 111     private boolean hasIp4Addresses(NetworkInterface nif) {
 112         return ip4Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
 113     }
 114 
 115     private boolean hasIp6Addresses(NetworkInterface nif) {
 116         return ip6Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
 117     }
 118 
 119     private boolean supportsIp4Multicast(NetworkInterface nif) {
 120         try {
 121             if (!nif.supportsMulticast()) {
 122                 return false;
 123             }
 124 
 125             // On AIX there is a bug:
 126             // When IPv6 is enabled on the system, the JDK opens sockets as AF_INET6.
 127             // If there's an interface configured with IPv4 addresses only, it should
 128             // be able to become the network interface for a multicast socket (that
 129             // could be in both, IPv4 or IPv6 space). But both possible setsockopt
 130             // calls for either IPV6_MULTICAST_IF or IP_MULTICAST_IF return
 131             // EADDRNOTAVAIL. So we must skip such interfaces here.
 132             if (Platform.isAix() && isIPv6Available() && !hasIp6Addresses(nif)) {
 133                 return false;
 134             }
 135 
 136             return hasIp4Addresses(nif);
 137         } catch (IOException e) {
 138             throw new UncheckedIOException(e);
 139         }
 140     }
 141 
 142     private boolean supportsIp6Multicast(NetworkInterface nif) {
 143         try {
 144             if (!nif.supportsMulticast()) {
 145                 return false;
 146             }
 147 
 148             return hasIp6Addresses(nif);
 149         } catch (IOException e) {
 150             throw new UncheckedIOException(e);
 151         }
 152     }
 153 
 154     /**
 155      * Returns whether IPv6 is available at all.
 156      * This should resemble the result of native ipv6_available() in net_util.c
 157      */
 158     public boolean isIPv6Available() {
 159         return isIPv6Available;
 160     }
 161 
 162     /**
 163      * Does any (usable) IPv6 address exist in the network configuration?
 164      */
 165     public boolean hasTestableIPv6Address() {
 166         return has_testableipv6address;
 167     }
 168 
 169     /**
 170      * Does any site local address exist?
 171      */
 172     public boolean hasSiteLocalAddress() {
 173         return has_sitelocaladdress;
 174     }
 175 
 176     /**
 177      * Does any link local address exist?
 178      */
 179     public boolean hasLinkLocalAddress() {
 180         return has_linklocaladdress;
 181     }
 182 
 183     /**
 184      * Does any global IPv6 address exist?
 185      */
 186     public boolean has_globaladdress() {
 187         return has_globaladdress;
 188     }
 189 
 190     /**
 191      * Returns a stream of interfaces suitable for functional tests.
 192      */
 193     public Stream<NetworkInterface> interfaces() {
 194         return Stream.concat(ip4Interfaces(), ip6Interfaces())
 195                      .distinct();
 196     }
 197 
 198     /**
 199      * Returns a stream of interfaces suitable for IPv4 functional tests.
 200      */
 201     public Stream<NetworkInterface> ip4Interfaces() {
 202         return ip4Interfaces.keySet()
 203                             .stream()
 204                             .filter(NetworkConfiguration::isNotExcludedInterface)
 205                             .filter(this::hasIp4Addresses);
 206     }
 207 
 208     /**
 209      * Returns a stream of interfaces suitable for IPv6 functional tests.
 210      */
 211     public Stream<NetworkInterface> ip6Interfaces() {
 212         return ip6Interfaces.keySet()
 213                             .stream()
 214                             .filter(NetworkConfiguration::isNotExcludedInterface)
 215                             .filter(this::hasIp6Addresses);
 216     }
 217 
 218     /**
 219      * Returns a stream of interfaces suitable for functional tests.
 220      */
 221     public Stream<NetworkInterface> multicastInterfaces(boolean includeLoopback) {
 222         return Stream
 223             .concat(ip4MulticastInterfaces(includeLoopback),
 224                     ip6MulticastInterfaces(includeLoopback))
 225             .distinct();
 226     }
 227 
 228     /**
 229      * Returns a stream of interfaces suitable for IPv4 multicast tests.
 230      *
 231      * The loopback interface will not be included.
 232      */
 233     public Stream<NetworkInterface> ip4MulticastInterfaces() {
 234         return ip4MulticastInterfaces(false);
 235     }
 236 
 237     /**
 238      * Returns a stream of interfaces suitable for IPv4 multicast tests.
 239      */
 240     public Stream<NetworkInterface> ip4MulticastInterfaces(boolean includeLoopback) {
 241         return (includeLoopback) ?
 242             ip4Interfaces().filter(this::supportsIp4Multicast) :
 243             ip4Interfaces().filter(this::supportsIp4Multicast)
 244                 .filter(NetworkConfiguration::isNotLoopback);
 245     }
 246 
 247     /**
 248      * Returns a stream of interfaces suitable for IPv6 multicast tests.
 249      *
 250      * The loopback interface will not be included.
 251      */
 252     public Stream<NetworkInterface> ip6MulticastInterfaces() {
 253         return ip6MulticastInterfaces(false);
 254     }
 255 
 256     /**
 257      * Returns a stream of interfaces suitable for IPv6 multicast tests.
 258      */
 259     public Stream<NetworkInterface> ip6MulticastInterfaces(boolean includeLoopback) {
 260         return (includeLoopback) ?
 261             ip6Interfaces().filter(this::supportsIp6Multicast) :
 262             ip6Interfaces().filter(this::supportsIp6Multicast)
 263                 .filter(NetworkConfiguration::isNotLoopback);
 264     }
 265 
 266     /**
 267      * Returns all addresses on all "functional" interfaces.
 268      */
 269     public Stream<InetAddress> addresses(NetworkInterface nif) {
 270         return Stream.concat(ip4Interfaces.get(nif).stream(),
 271                              ip6Interfaces.get(nif).stream());
 272     }
 273 
 274     /**
 275      * Returns all IPv4 addresses on all "functional" interfaces.
 276      */
 277     public Stream<Inet4Address> ip4Addresses() {
 278         return ip4Interfaces().flatMap(this::ip4Addresses);
 279     }
 280 
 281     /**
 282      * Returns all IPv6 addresses on all "functional" interfaces.
 283      */
 284     public Stream<Inet6Address> ip6Addresses() {
 285         return ip6Interfaces().flatMap(this::ip6Addresses);
 286     }
 287 
 288     /**
 289      * Returns all IPv4 addresses the given interface.
 290      */
 291     public Stream<Inet4Address> ip4Addresses(NetworkInterface nif) {
 292         return ip4Interfaces.get(nif).stream();
 293     }
 294 
 295     /**
 296      * Returns all IPv6 addresses for the given interface.
 297      */
 298     public Stream<Inet6Address> ip6Addresses(NetworkInterface nif) {
 299         return ip6Interfaces.get(nif).stream();
 300     }
 301 
 302     @Override
 303     public String toString() {
 304         return interfaces().map(NetworkConfiguration::interfaceInformation)
 305                            .collect(Collectors.joining());
 306     }
 307 
 308     /**
 309      * Return a NetworkConfiguration instance.
 310      */
 311     public static NetworkConfiguration probe() throws IOException {
 312         Map<NetworkInterface, List<Inet4Address>> ip4Interfaces = new HashMap<>();
 313         Map<NetworkInterface, List<Inet6Address>> ip6Interfaces = new HashMap<>();
 314 
 315         List<NetworkInterface> nifs = list(getNetworkInterfaces());
 316         for (NetworkInterface nif : nifs) {
 317             // ignore interfaces that are down
 318             if (!nif.isUp() || nif.isPointToPoint()) {
 319                 continue;
 320             }
 321 
 322             List<Inet4Address> ip4Addresses = new LinkedList<>();
 323             List<Inet6Address> ip6Addresses = new LinkedList<>();
 324             ip4Interfaces.put(nif, ip4Addresses);
 325             ip6Interfaces.put(nif, ip6Addresses);
 326             for (InetAddress addr : list(nif.getInetAddresses())) {
 327                 if (addr instanceof Inet4Address) {
 328                     ip4Addresses.add((Inet4Address) addr);
 329                 } else if (addr instanceof Inet6Address) {
 330                     ip6Addresses.add((Inet6Address) addr);
 331                 }
 332             }
 333         }
 334         return new NetworkConfiguration(ip4Interfaces, ip6Interfaces);
 335     }
 336 
 337     /** Returns detailed information for the given interface. */
 338     public static String interfaceInformation(NetworkInterface nif) {
 339         StringBuilder sb = new StringBuilder();
 340         try {
 341             sb.append("Display name: ")
 342               .append(nif.getDisplayName())
 343               .append("\n");
 344             sb.append("Name: ")
 345               .append(nif.getName())
 346               .append("\n");
 347             for (InetAddress inetAddress : list(nif.getInetAddresses())) {
 348                 sb.append("InetAddress: ")
 349                   .append(inetAddress)
 350                   .append("\n");
 351             }
 352             sb.append("Up? ")
 353               .append(nif.isUp())
 354               .append("\n");
 355             sb.append("Loopback? ")
 356               .append(nif.isLoopback())
 357               .append("\n");
 358             sb.append("PointToPoint? ")
 359               .append(nif.isPointToPoint())
 360               .append("\n");
 361             sb.append("Supports multicast? ")
 362               .append(nif.supportsMulticast())
 363               .append("\n");
 364             sb.append("Virtual? ")
 365               .append(nif.isVirtual())
 366               .append("\n");
 367             sb.append("Hardware address: ")
 368               .append(Arrays.toString(nif.getHardwareAddress()))
 369               .append("\n");
 370             sb.append("MTU: ")
 371               .append(nif.getMTU())
 372               .append("\n");
 373             sb.append("Index: ")
 374               .append(nif.getIndex())
 375               .append("\n");
 376             sb.append("\n");
 377             return sb.toString();
 378         } catch (IOException e) {
 379             throw new UncheckedIOException(e);
 380         }
 381     }
 382 
 383     /** Prints all the system interface information to the give stream. */
 384     public static void printSystemConfiguration(PrintStream out) {
 385         PrivilegedAction<Void> pa = () -> {
 386         try {
 387             out.println("*** all system network interface configuration ***");
 388             for (NetworkInterface nif : list(getNetworkInterfaces())) {
 389                 out.print(interfaceInformation(nif));
 390             }
 391             out.println("*** end ***");
 392             return null;
 393         } catch (IOException e) {
 394             throw new UncheckedIOException(e);
 395         }};
 396         AccessController.doPrivileged(pa);
 397     }
 398 }