1 /*
   2  * Copyright (c) 2017, 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.PrintStream;
  27 import java.io.UncheckedIOException;
  28 import java.io.IOException;
  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.function.Predicate;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.Stream;
  41 import static java.net.NetworkInterface.getNetworkInterfaces;
  42 import static java.util.Collections.list;
  43 
  44 /**
  45  * Helper class for retrieving network interfaces and local addresses
  46  * suitable for testing.
  47  */
  48 public class NetworkConfiguration {
  49 
  50     private Map<NetworkInterface,List<Inet4Address>> ip4Interfaces;
  51     private Map<NetworkInterface,List<Inet6Address>> ip6Interfaces;
  52 
  53     private NetworkConfiguration(
  54             Map<NetworkInterface,List<Inet4Address>> ip4Interfaces,
  55             Map<NetworkInterface,List<Inet6Address>> ip6Interfaces) {
  56         this.ip4Interfaces = ip4Interfaces;
  57         this.ip6Interfaces = ip6Interfaces;
  58     }
  59 
  60     /**
  61      * Returns a stream of interfaces suitable for functional tests.
  62      */
  63     public Stream<NetworkInterface> interfaces() {
  64         return Stream.concat(ip4Interfaces(), ip6Interfaces())
  65                      .distinct();
  66     }
  67 
  68     /**
  69      * Returns a stream of interfaces suitable for IPv4 functional tests.
  70      */
  71     public Stream<NetworkInterface> ip4Interfaces() {
  72         return ip4Interfaces.keySet()
  73                             .stream()
  74                             .filter(NetworkConfiguration::isNotExcludedInterface)
  75                             .filter(hasIp4Addresses);
  76     }
  77 
  78     /**
  79      * Returns a stream of interfaces suitable for IPv6 functional tests.
  80      */
  81     public Stream<NetworkInterface> ip6Interfaces() {
  82         return ip6Interfaces.keySet()
  83                             .stream()
  84                             .filter(NetworkConfiguration::isNotExcludedInterface)
  85                             .filter(hasIp6Addresses);
  86     }
  87 
  88     private static boolean isNotExcludedInterface(NetworkInterface nif) {
  89         if (Platform.isOSX() && nif.getName().contains("awdl")) {
  90             return false;
  91         }
  92         String dName = nif.getDisplayName();
  93         if (Platform.isWindows() && dName != null && dName.contains("Teredo")) {
  94             return false;
  95         }
  96         return true;
  97     }
  98 
  99     private final Predicate<NetworkInterface> hasIp4Addresses = nif ->
 100             ip4Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
 101 
 102     private final Predicate<NetworkInterface> hasIp6Addresses = nif ->
 103             ip6Interfaces.get(nif).stream().anyMatch(a -> !a.isAnyLocalAddress());
 104 
 105 
 106     /**
 107      * Returns a stream of interfaces suitable for IPv4 multicast tests.
 108      */
 109     public Stream<NetworkInterface> ip4MulticastInterfaces() {
 110         return ip4Interfaces().filter(supportsIp4Multicast);
 111     }
 112 
 113     /**
 114      * Returns a stream of interfaces suitable for IPv6 multicast tests.
 115      */
 116     public Stream<NetworkInterface> ip6MulticastInterfaces() {
 117         return ip6Interfaces().filter(supportsIp6Multicast);
 118     }
 119 
 120     private final Predicate<NetworkInterface> supportsIp4Multicast = nif -> {
 121         try {
 122             if (!nif.supportsMulticast() || nif.isLoopback()) {
 123                 return false;
 124             }
 125             return hasIp4Addresses.test(nif);
 126         } catch (IOException e) {
 127             throw new UncheckedIOException(e);
 128         }
 129     };
 130 
 131     private final Predicate<NetworkInterface> supportsIp6Multicast = nif -> {
 132         try {
 133             if (!nif.supportsMulticast() || nif.isLoopback()) {
 134                 return false;
 135             }
 136 
 137             return hasIp6Addresses.test(nif);
 138         } catch (IOException e) {
 139             throw new UncheckedIOException(e);
 140         }
 141     };
 142 
 143     /**
 144      * Returns all addresses on all "functional" interfaces.
 145      */
 146     public Stream<InetAddress> addresses(NetworkInterface nif) {
 147         return Stream.concat(ip4Interfaces.get(nif).stream(),
 148                              ip6Interfaces.get(nif).stream());
 149     }
 150 
 151     /**
 152      * Returns all IPv4 addresses on all "functional" interfaces.
 153      */
 154     public Stream<Inet4Address> ip4Addresses() {
 155         return ip4Interfaces().flatMap(this::ip4Addresses);
 156     }
 157 
 158     /**
 159      * Returns all IPv6 addresses on all "functional" interfaces.
 160      */
 161     public Stream<Inet6Address> ip6Addresses() {
 162         return ip6Interfaces().flatMap(this::ip6Addresses);
 163     }
 164 
 165     /**
 166      * Returns all IPv4 addresses the given interface.
 167      */
 168     public Stream<Inet4Address> ip4Addresses(NetworkInterface nif) {
 169         return ip4Interfaces.get(nif).stream();
 170     }
 171 
 172     /**
 173      * Returns all IPv6 addresses for the given interface.
 174      */
 175     public Stream<Inet6Address> ip6Addresses(NetworkInterface nif) {
 176         return ip6Interfaces.get(nif).stream();
 177     }
 178 
 179     /**
 180      * Return a NetworkConfiguration instance.
 181      */
 182     public static NetworkConfiguration probe() throws IOException {
 183         Map<NetworkInterface, List<Inet4Address>> ip4Interfaces = new HashMap<>();
 184         Map<NetworkInterface, List<Inet6Address>> ip6Interfaces = new HashMap<>();
 185 
 186         List<NetworkInterface> nifs = list(getNetworkInterfaces());
 187         for (NetworkInterface nif : nifs) {
 188             // ignore interfaces that are down
 189             if (!nif.isUp() || nif.isPointToPoint()) {
 190                 continue;
 191             }
 192 
 193             List<Inet4Address> ip4Addresses = new LinkedList<>();
 194             List<Inet6Address> ip6Addresses = new LinkedList<>();
 195             ip4Interfaces.put(nif, ip4Addresses);
 196             ip6Interfaces.put(nif, ip6Addresses);
 197             for (InetAddress addr : list(nif.getInetAddresses())) {
 198                 if (addr instanceof Inet4Address) {
 199                     ip4Addresses.add((Inet4Address) addr);
 200                 } else if (addr instanceof Inet6Address) {
 201                     ip6Addresses.add((Inet6Address) addr);
 202                 }
 203             }
 204         }
 205         return new NetworkConfiguration(ip4Interfaces, ip6Interfaces);
 206     }
 207 
 208     @Override
 209     public String toString() {
 210         return interfaces().map(NetworkConfiguration::interfaceInformation)
 211                            .collect(Collectors.joining());
 212     }
 213 
 214     /** Returns detailed information for the given interface. */
 215     public static String interfaceInformation(NetworkInterface nif) {
 216         StringBuilder sb = new StringBuilder();
 217         try {
 218             sb.append("Display name: ")
 219               .append(nif.getDisplayName())
 220               .append("\n");
 221             sb.append("Name: ")
 222               .append(nif.getName())
 223               .append("\n");
 224             for (InetAddress inetAddress : list(nif.getInetAddresses())) {
 225                 sb.append("InetAddress: ")
 226                   .append(inetAddress)
 227                   .append("\n");
 228             }
 229             sb.append("Up? ")
 230               .append(nif.isUp())
 231               .append("\n");
 232             sb.append("Loopback? ")
 233               .append(nif.isLoopback())
 234               .append("\n");
 235             sb.append("PointToPoint? ")
 236               .append(nif.isPointToPoint())
 237               .append("\n");
 238             sb.append("Supports multicast? ")
 239               .append(nif.supportsMulticast())
 240               .append("\n");
 241             sb.append("Virtual? ")
 242               .append(nif.isVirtual())
 243               .append("\n");
 244             sb.append("Hardware address: ")
 245               .append(Arrays.toString(nif.getHardwareAddress()))
 246               .append("\n");
 247             sb.append("MTU: ")
 248               .append(nif.getMTU())
 249               .append("\n");
 250             sb.append("Index: ")
 251               .append(nif.getIndex())
 252               .append("\n");
 253             sb.append("\n");
 254             return sb.toString();
 255         } catch (IOException e) {
 256             throw new UncheckedIOException(e);
 257         }
 258     }
 259 
 260     /** Prints all the system interface information to the give stream. */
 261     public static void printSystemConfiguration(PrintStream out) {
 262         try {
 263             out.println("*** all system network interface configuration ***");
 264             for (NetworkInterface nif : list(getNetworkInterfaces())) {
 265                 out.print(interfaceInformation(nif));
 266             }
 267             out.println("*** end ***");
 268         } catch (IOException e) {
 269             throw new UncheckedIOException(e);
 270         }
 271     }
 272 }