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