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 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.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 }