1 /*
   2  * Copyright (c) 2017, 2020, 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.LinkedHashMap;
  35 import java.util.LinkedList;
  36 import java.util.List;
  37 import java.util.Map;
  38 import java.util.function.Predicate;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.Stream;
  41 import java.security.AccessController;
  42 import java.security.PrivilegedAction;
  43 
  44 import static java.net.NetworkInterface.getNetworkInterfaces;
  45 import static java.util.Collections.list;
  46 
  47 /**
  48  * Helper class for retrieving network interfaces and local addresses
  49  * suitable for testing.
  50  */
  51 public class NetworkConfiguration {
  52 
  53     private Map<NetworkInterface,List<Inet4Address>> ip4Interfaces;
  54     private Map<NetworkInterface,List<Inet6Address>> ip6Interfaces;
  55     private final boolean isIPv6Available;
  56     private boolean has_testableipv6address = false;
  57     private boolean has_sitelocaladdress = false;
  58     private boolean has_linklocaladdress = false;
  59     private boolean has_globaladdress = false;
  60 
  61     private NetworkConfiguration(
  62             Map<NetworkInterface,List<Inet4Address>> ip4Interfaces,
  63             Map<NetworkInterface,List<Inet6Address>> ip6Interfaces) {
  64         this.ip4Interfaces = ip4Interfaces;
  65         this.ip6Interfaces = ip6Interfaces;
  66 
  67         // initialize properties that can be queried
  68         isIPv6Available = !ip6Interfaces().collect(Collectors.toList()).isEmpty();
  69         ip6Interfaces().forEach(nif -> {
  70             ip6Addresses(nif)
  71                 // On Solaris or AIX, a configuration with only local or loopback
  72                 // addresses does not fully enable IPv6 operations.
  73                 // E.g. IPv6 multicasting does not work.
  74                 // So, don't set has_testableipv6address if we only find these.
  75                 .filter(addr -> Platform.isSolaris() || Platform.isAix() ?
  76                     !(addr.isAnyLocalAddress() || addr.isLoopbackAddress()) : true)
  77                 .forEach(ia -> {
  78                     has_testableipv6address = true;
  79                     if (ia.isLinkLocalAddress()) has_linklocaladdress = true;
  80                     if (ia.isSiteLocalAddress()) has_sitelocaladdress = true;
  81 
  82                     if (!ia.isLinkLocalAddress() &&
  83                         !ia.isSiteLocalAddress() &&
  84                         !ia.isLoopbackAddress()) {
  85                         has_globaladdress = true;
  86                     }
  87                 });
  88         });
  89     }
  90 
  91     private static boolean isIPv6LinkLocal(InetAddress a) {
  92         return Inet6Address.class.isInstance(a) && a.isLinkLocalAddress();
  93     }
  94 
  95     public static boolean isTestable(NetworkInterface nif) {
  96         if (Platform.isOSX()) {
  97             if (nif.getName().contains("awdl")) {
  98                 return false; // exclude awdl
  99             }
 100             // filter out interfaces that only have link-local IPv6 addresses
 101             // on macOS interfaces like 'en6' fall in this category and
 102             // need to be skipped
 103             return nif.inetAddresses()
 104                     .filter(Predicate.not(NetworkConfiguration::isIPv6LinkLocal))
 105                     .findAny()
 106                     .isPresent();
 107         }
 108 
 109         if (Platform.isWindows()) {
 110             String dName = nif.getDisplayName();
 111             if (dName != null && dName.contains("Teredo")) {
 112                 return false;
 113             }
 114         }
 115         return true;
 116     }
 117 
 118     private static boolean isNotLoopback(NetworkInterface nif) {
 119         try {
 120             return !nif.isLoopback();
 121         } catch (IOException e) {
 122             throw new UncheckedIOException(e);
 123         }
 124     }
 125 
 126     private boolean hasIp4Addresses(NetworkInterface nif) {
 127         return ip4Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
 128     }
 129 
 130     private boolean hasIp6Addresses(NetworkInterface nif) {
 131         return ip6Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
 132     }
 133 
 134     public static boolean hasNonLinkLocalAddress(NetworkInterface nif) {
 135         return nif.inetAddresses()
 136                 .filter(Predicate.not(InetAddress::isLinkLocalAddress))
 137                 .findAny().isPresent();
 138     }
 139 
 140     private boolean supportsIp4Multicast(NetworkInterface nif) {
 141         try {
 142             if (!nif.supportsMulticast()) {
 143                 return false;
 144             }
 145 
 146             // On AIX there is a bug:
 147             // When IPv6 is enabled on the system, the JDK opens sockets as AF_INET6.
 148             // If there's an interface configured with IPv4 addresses only, it should
 149             // be able to become the network interface for a multicast socket (that
 150             // could be in both, IPv4 or IPv6 space). But both possible setsockopt
 151             // calls for either IPV6_MULTICAST_IF or IP_MULTICAST_IF return
 152             // EADDRNOTAVAIL. So we must skip such interfaces here.
 153             if (Platform.isAix() && isIPv6Available() && !hasIp6Addresses(nif)) {
 154                 return false;
 155             }
 156 
 157             if (Platform.isOSX()) {
 158                 // multicasting may not work on interfaces that only
 159                 // have link local addresses
 160                 if (!hasNonLinkLocalAddress(nif)) {
 161                     return false;
 162                 }
 163             }
 164 
 165             return hasIp4Addresses(nif);
 166         } catch (IOException e) {
 167             throw new UncheckedIOException(e);
 168         }
 169     }
 170 
 171     private boolean supportsIp6Multicast(NetworkInterface nif) {
 172         try {
 173             if (!nif.supportsMulticast()) {
 174                 return false;
 175             }
 176 
 177             if (Platform.isOSX()) {
 178                 // multicasting may not work on interfaces that only
 179                 // have link local addresses
 180                 if (!hasNonLinkLocalAddress(nif)) {
 181                     return false;
 182                 }
 183             }
 184 
 185             return hasIp6Addresses(nif);
 186         } catch (IOException e) {
 187             throw new UncheckedIOException(e);
 188         }
 189     }
 190 
 191     /**
 192      * Returns whether IPv6 is available at all.
 193      * This should resemble the result of native ipv6_available() in net_util.c
 194      */
 195     public boolean isIPv6Available() {
 196         return isIPv6Available;
 197     }
 198 
 199     /**
 200      * Does any (usable) IPv6 address exist in the network configuration?
 201      */
 202     public boolean hasTestableIPv6Address() {
 203         return has_testableipv6address;
 204     }
 205 
 206     /**
 207      * Does any site local address exist?
 208      */
 209     public boolean hasSiteLocalAddress() {
 210         return has_sitelocaladdress;
 211     }
 212 
 213     /**
 214      * Does any link local address exist?
 215      */
 216     public boolean hasLinkLocalAddress() {
 217         return has_linklocaladdress;
 218     }
 219 
 220     /**
 221      * Does any global IPv6 address exist?
 222      */
 223     public boolean has_globaladdress() {
 224         return has_globaladdress;
 225     }
 226 
 227     /**
 228      * Returns a stream of interfaces suitable for functional tests.
 229      */
 230     public Stream<NetworkInterface> interfaces() {
 231         return Stream.concat(ip4Interfaces(), ip6Interfaces())
 232                      .distinct();
 233     }
 234 
 235     /**
 236      * Returns a stream of interfaces suitable for IPv4 functional tests.
 237      */
 238     public Stream<NetworkInterface> ip4Interfaces() {
 239         return ip4Interfaces.keySet()
 240                             .stream()
 241                             .filter(NetworkConfiguration::isTestable)
 242                             .filter(this::hasIp4Addresses);
 243     }
 244 
 245     /**
 246      * Returns a stream of interfaces suitable for IPv6 functional tests.
 247      */
 248     public Stream<NetworkInterface> ip6Interfaces() {
 249         return ip6Interfaces.keySet()
 250                             .stream()
 251                             .filter(NetworkConfiguration::isTestable)
 252                             .filter(this::hasIp6Addresses);
 253     }
 254 
 255     /**
 256      * Returns a stream of interfaces suitable for functional tests.
 257      */
 258     public Stream<NetworkInterface> multicastInterfaces(boolean includeLoopback) {
 259         return Stream
 260             .concat(ip4MulticastInterfaces(includeLoopback),
 261                     ip6MulticastInterfaces(includeLoopback))
 262             .distinct();
 263     }
 264 
 265     /**
 266      * Returns a stream of interfaces suitable for IPv4 multicast tests.
 267      *
 268      * The loopback interface will not be included.
 269      */
 270     public Stream<NetworkInterface> ip4MulticastInterfaces() {
 271         return ip4MulticastInterfaces(false);
 272     }
 273 
 274     /**
 275      * Returns a stream of interfaces suitable for IPv4 multicast tests.
 276      */
 277     public Stream<NetworkInterface> ip4MulticastInterfaces(boolean includeLoopback) {
 278         return (includeLoopback) ?
 279             ip4Interfaces().filter(this::supportsIp4Multicast) :
 280             ip4Interfaces().filter(this::supportsIp4Multicast)
 281                 .filter(NetworkConfiguration::isNotLoopback);
 282     }
 283 
 284     /**
 285      * Returns a stream of interfaces suitable for IPv6 multicast tests.
 286      *
 287      * The loopback interface will not be included.
 288      */
 289     public Stream<NetworkInterface> ip6MulticastInterfaces() {
 290         return ip6MulticastInterfaces(false);
 291     }
 292 
 293     /**
 294      * Returns a stream of interfaces suitable for IPv6 multicast tests.
 295      */
 296     public Stream<NetworkInterface> ip6MulticastInterfaces(boolean includeLoopback) {
 297         return (includeLoopback) ?
 298             ip6Interfaces().filter(this::supportsIp6Multicast) :
 299             ip6Interfaces().filter(this::supportsIp6Multicast)
 300                 .filter(NetworkConfiguration::isNotLoopback);
 301     }
 302 
 303     /**
 304      * Returns all addresses on all "functional" interfaces.
 305      */
 306     public Stream<InetAddress> addresses(NetworkInterface nif) {
 307         return Stream.concat(ip4Interfaces.get(nif).stream(),
 308                              ip6Interfaces.get(nif).stream());
 309     }
 310 
 311     /**
 312      * Returns all IPv4 addresses on all "functional" interfaces.
 313      */
 314     public Stream<Inet4Address> ip4Addresses() {
 315         return ip4Interfaces().flatMap(this::ip4Addresses);
 316     }
 317 
 318     /**
 319      * Returns all IPv6 addresses on all "functional" interfaces.
 320      */
 321     public Stream<Inet6Address> ip6Addresses() {
 322         return ip6Interfaces().flatMap(this::ip6Addresses);
 323     }
 324 
 325     /**
 326      * Returns all IPv4 addresses the given interface.
 327      */
 328     public Stream<Inet4Address> ip4Addresses(NetworkInterface nif) {
 329         return ip4Interfaces.get(nif).stream();
 330     }
 331 
 332     /**
 333      * Returns all IPv6 addresses for the given interface.
 334      */
 335     public Stream<Inet6Address> ip6Addresses(NetworkInterface nif) {
 336         return ip6Interfaces.get(nif).stream();
 337     }
 338 
 339     @Override
 340     public String toString() {
 341         return interfaces().map(NetworkConfiguration::interfaceInformation)
 342                            .collect(Collectors.joining());
 343     }
 344 
 345     /**
 346      * Return a NetworkConfiguration instance.
 347      */
 348     public static NetworkConfiguration probe() throws IOException {
 349         Map<NetworkInterface, List<Inet4Address>> ip4Interfaces = new LinkedHashMap<>();
 350         Map<NetworkInterface, List<Inet6Address>> ip6Interfaces = new LinkedHashMap<>();
 351 
 352         List<NetworkInterface> nifs = list(getNetworkInterfaces());
 353         for (NetworkInterface nif : nifs) {
 354             // ignore interfaces that are down
 355             if (!nif.isUp() || nif.isPointToPoint()) {
 356                 continue;
 357             }
 358 
 359             List<Inet4Address> ip4Addresses = new LinkedList<>();
 360             List<Inet6Address> ip6Addresses = new LinkedList<>();
 361             ip4Interfaces.put(nif, ip4Addresses);
 362             ip6Interfaces.put(nif, ip6Addresses);
 363             for (InetAddress addr : list(nif.getInetAddresses())) {
 364                 if (addr instanceof Inet4Address) {
 365                     ip4Addresses.add((Inet4Address) addr);
 366                 } else if (addr instanceof Inet6Address) {
 367                     ip6Addresses.add((Inet6Address) addr);
 368                 }
 369             }
 370         }
 371         return new NetworkConfiguration(ip4Interfaces, ip6Interfaces);
 372     }
 373 
 374     /** Returns detailed information for the given interface. */
 375     public static String interfaceInformation(NetworkInterface nif) {
 376         StringBuilder sb = new StringBuilder();
 377         try {
 378             sb.append("Display name: ")
 379               .append(nif.getDisplayName())
 380               .append("\n");
 381             sb.append("Name: ")
 382               .append(nif.getName())
 383               .append("\n");
 384             for (InetAddress inetAddress : list(nif.getInetAddresses())) {
 385                 sb.append("InetAddress: ")
 386                   .append(inetAddress)
 387                   .append("\n");
 388             }
 389             sb.append("Up? ")
 390               .append(nif.isUp())
 391               .append("\n");
 392             sb.append("Loopback? ")
 393               .append(nif.isLoopback())
 394               .append("\n");
 395             sb.append("PointToPoint? ")
 396               .append(nif.isPointToPoint())
 397               .append("\n");
 398             sb.append("Supports multicast? ")
 399               .append(nif.supportsMulticast())
 400               .append("\n");
 401             sb.append("Virtual? ")
 402               .append(nif.isVirtual())
 403               .append("\n");
 404             sb.append("Hardware address: ")
 405               .append(Arrays.toString(nif.getHardwareAddress()))
 406               .append("\n");
 407             sb.append("MTU: ")
 408               .append(nif.getMTU())
 409               .append("\n");
 410             sb.append("Index: ")
 411               .append(nif.getIndex())
 412               .append("\n");
 413             sb.append("\n");
 414             return sb.toString();
 415         } catch (IOException e) {
 416             throw new UncheckedIOException(e);
 417         }
 418     }
 419 
 420     /** Prints all the system interface information to the give stream. */
 421     public static void printSystemConfiguration(PrintStream out) {
 422         PrivilegedAction<Void> pa = () -> {
 423         try {
 424             out.println("*** all system network interface configuration ***");
 425             for (NetworkInterface nif : list(getNetworkInterfaces())) {
 426                 out.print(interfaceInformation(nif));
 427             }
 428             out.println("*** end ***");
 429             return null;
 430         } catch (IOException e) {
 431             throw new UncheckedIOException(e);
 432         }};
 433         AccessController.doPrivileged(pa);
 434     }
 435 }