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 }