|
@@ -19,303 +19,205 @@
|
|
|
|
|
|
package org.elasticsearch.common.network;
|
|
|
|
|
|
-import com.google.common.collect.Lists;
|
|
|
import org.apache.lucene.util.BytesRef;
|
|
|
-import org.apache.lucene.util.CollectionUtil;
|
|
|
import org.apache.lucene.util.Constants;
|
|
|
import org.elasticsearch.common.logging.ESLogger;
|
|
|
import org.elasticsearch.common.logging.Loggers;
|
|
|
|
|
|
-import java.net.*;
|
|
|
-import java.util.*;
|
|
|
+import java.net.Inet4Address;
|
|
|
+import java.net.Inet6Address;
|
|
|
+import java.net.InetAddress;
|
|
|
+import java.net.NetworkInterface;
|
|
|
+import java.net.SocketException;
|
|
|
+import java.net.UnknownHostException;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.List;
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ * Utilities for network interfaces / addresses
|
|
|
*/
|
|
|
public abstract class NetworkUtils {
|
|
|
|
|
|
- private final static ESLogger logger = Loggers.getLogger(NetworkUtils.class);
|
|
|
-
|
|
|
- public static enum StackType {
|
|
|
- IPv4, IPv6, Unknown
|
|
|
- }
|
|
|
-
|
|
|
- public static final String IPv4_SETTING = "java.net.preferIPv4Stack";
|
|
|
- public static final String IPv6_SETTING = "java.net.preferIPv6Addresses";
|
|
|
-
|
|
|
- public static final String NON_LOOPBACK_ADDRESS = "non_loopback_address";
|
|
|
-
|
|
|
- private final static InetAddress localAddress;
|
|
|
-
|
|
|
- static {
|
|
|
- InetAddress localAddressX;
|
|
|
- try {
|
|
|
- localAddressX = InetAddress.getLocalHost();
|
|
|
- } catch (Throwable e) {
|
|
|
- logger.warn("failed to resolve local host, fallback to loopback", e);
|
|
|
- localAddressX = InetAddress.getLoopbackAddress();
|
|
|
+ /** no instantation */
|
|
|
+ private NetworkUtils() {}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * By default we bind to any addresses on an interface/name, unless restricted by :ipv4 etc.
|
|
|
+ * This property is unrelated to that, this is about what we *publish*. Today the code pretty much
|
|
|
+ * expects one address so this is used for the sort order.
|
|
|
+ * @deprecated transition mechanism only
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
+ static final boolean PREFER_V4 = Boolean.parseBoolean(System.getProperty("java.net.preferIPv4Stack", "true"));
|
|
|
+
|
|
|
+ /** Sorts an address by preference. This way code like publishing can just pick the first one */
|
|
|
+ static int sortKey(InetAddress address, boolean prefer_v4) {
|
|
|
+ int key = address.getAddress().length;
|
|
|
+ if (prefer_v4 == false) {
|
|
|
+ key = -key;
|
|
|
}
|
|
|
- localAddress = localAddressX;
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean defaultReuseAddress() {
|
|
|
- return Constants.WINDOWS ? false : true;
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean isIPv4() {
|
|
|
- return System.getProperty("java.net.preferIPv4Stack") != null && System.getProperty("java.net.preferIPv4Stack").equals("true");
|
|
|
- }
|
|
|
-
|
|
|
- public static InetAddress getIPv4Localhost() throws UnknownHostException {
|
|
|
- return getLocalhost(StackType.IPv4);
|
|
|
- }
|
|
|
-
|
|
|
- public static InetAddress getIPv6Localhost() throws UnknownHostException {
|
|
|
- return getLocalhost(StackType.IPv6);
|
|
|
- }
|
|
|
-
|
|
|
- public static InetAddress getLocalAddress() {
|
|
|
- return localAddress;
|
|
|
- }
|
|
|
-
|
|
|
- public static String getLocalHostName(String defaultHostName) {
|
|
|
- if (localAddress == null) {
|
|
|
- return defaultHostName;
|
|
|
+
|
|
|
+ if (address.isAnyLocalAddress()) {
|
|
|
+ key += 5;
|
|
|
}
|
|
|
- String hostName = localAddress.getHostName();
|
|
|
- if (hostName == null) {
|
|
|
- return defaultHostName;
|
|
|
+ if (address.isMulticastAddress()) {
|
|
|
+ key += 4;
|
|
|
}
|
|
|
- return hostName;
|
|
|
- }
|
|
|
-
|
|
|
- public static String getLocalHostAddress(String defaultHostAddress) {
|
|
|
- if (localAddress == null) {
|
|
|
- return defaultHostAddress;
|
|
|
+ if (address.isLoopbackAddress()) {
|
|
|
+ key += 3;
|
|
|
}
|
|
|
- String hostAddress = localAddress.getHostAddress();
|
|
|
- if (hostAddress == null) {
|
|
|
- return defaultHostAddress;
|
|
|
+ if (address.isLinkLocalAddress()) {
|
|
|
+ key += 2;
|
|
|
+ }
|
|
|
+ if (address.isSiteLocalAddress()) {
|
|
|
+ key += 1;
|
|
|
}
|
|
|
- return hostAddress;
|
|
|
- }
|
|
|
|
|
|
- public static InetAddress getLocalhost(StackType ip_version) throws UnknownHostException {
|
|
|
- if (ip_version == StackType.IPv4)
|
|
|
- return InetAddress.getByName("127.0.0.1");
|
|
|
- else
|
|
|
- return InetAddress.getByName("::1");
|
|
|
+ return key;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Returns the first non-loopback address on any interface on the current host.
|
|
|
- *
|
|
|
- * @param ip_version Constraint on IP version of address to be returned, 4 or 6
|
|
|
+ /**
|
|
|
+ * Sorts addresses by order of preference. This is used to pick the first one for publishing
|
|
|
+ * @deprecated remove this when multihoming is really correct
|
|
|
*/
|
|
|
- public static InetAddress getFirstNonLoopbackAddress(StackType ip_version) throws SocketException {
|
|
|
- InetAddress address;
|
|
|
- for (NetworkInterface intf : getInterfaces()) {
|
|
|
- try {
|
|
|
- if (!intf.isUp() || intf.isLoopback())
|
|
|
- continue;
|
|
|
- } catch (Exception e) {
|
|
|
- // might happen when calling on a network interface that does not exists
|
|
|
- continue;
|
|
|
- }
|
|
|
- address = getFirstNonLoopbackAddress(intf, ip_version);
|
|
|
- if (address != null) {
|
|
|
- return address;
|
|
|
+ @Deprecated
|
|
|
+ private static void sortAddresses(List<InetAddress> list) {
|
|
|
+ Collections.sort(list, new Comparator<InetAddress>() {
|
|
|
+ @Override
|
|
|
+ public int compare(InetAddress left, InetAddress right) {
|
|
|
+ int cmp = Integer.compare(sortKey(left, PREFER_V4), sortKey(right, PREFER_V4));
|
|
|
+ if (cmp == 0) {
|
|
|
+ cmp = new BytesRef(left.getAddress()).compareTo(new BytesRef(right.getAddress()));
|
|
|
+ }
|
|
|
+ return cmp;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- private static List<NetworkInterface> getInterfaces() throws SocketException {
|
|
|
- Enumeration intfs = NetworkInterface.getNetworkInterfaces();
|
|
|
-
|
|
|
- List<NetworkInterface> intfsList = Lists.newArrayList();
|
|
|
- while (intfs.hasMoreElements()) {
|
|
|
- intfsList.add((NetworkInterface) intfs.nextElement());
|
|
|
- }
|
|
|
-
|
|
|
- sortInterfaces(intfsList);
|
|
|
- return intfsList;
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+ private final static ESLogger logger = Loggers.getLogger(NetworkUtils.class);
|
|
|
|
|
|
- private static void sortInterfaces(List<NetworkInterface> intfsList) {
|
|
|
- // order by index, assuming first ones are more interesting
|
|
|
- CollectionUtil.timSort(intfsList, new Comparator<NetworkInterface>() {
|
|
|
+ /** Return all interfaces (and subinterfaces) on the system */
|
|
|
+ static List<NetworkInterface> getInterfaces() throws SocketException {
|
|
|
+ List<NetworkInterface> all = new ArrayList<>();
|
|
|
+ addAllInterfaces(all, Collections.list(NetworkInterface.getNetworkInterfaces()));
|
|
|
+ Collections.sort(all, new Comparator<NetworkInterface>() {
|
|
|
@Override
|
|
|
- public int compare(NetworkInterface o1, NetworkInterface o2) {
|
|
|
- return Integer.compare (o1.getIndex(), o2.getIndex());
|
|
|
+ public int compare(NetworkInterface left, NetworkInterface right) {
|
|
|
+ return Integer.compare(left.getIndex(), right.getIndex());
|
|
|
}
|
|
|
});
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the first non-loopback address on the given interface on the current host.
|
|
|
- *
|
|
|
- * @param intf the interface to be checked
|
|
|
- * @param ipVersion Constraint on IP version of address to be returned, 4 or 6
|
|
|
- */
|
|
|
- public static InetAddress getFirstNonLoopbackAddress(NetworkInterface intf, StackType ipVersion) throws SocketException {
|
|
|
- if (intf == null)
|
|
|
- throw new IllegalArgumentException("Network interface pointer is null");
|
|
|
-
|
|
|
- for (Enumeration addresses = intf.getInetAddresses(); addresses.hasMoreElements(); ) {
|
|
|
- InetAddress address = (InetAddress) addresses.nextElement();
|
|
|
- if (!address.isLoopbackAddress()) {
|
|
|
- if ((address instanceof Inet4Address && ipVersion == StackType.IPv4) ||
|
|
|
- (address instanceof Inet6Address && ipVersion == StackType.IPv6))
|
|
|
- return address;
|
|
|
+ return all;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Helper for getInterfaces, recursively adds subinterfaces to {@code target} */
|
|
|
+ private static void addAllInterfaces(List<NetworkInterface> target, List<NetworkInterface> level) {
|
|
|
+ if (!level.isEmpty()) {
|
|
|
+ target.addAll(level);
|
|
|
+ for (NetworkInterface intf : level) {
|
|
|
+ addAllInterfaces(target, Collections.list(intf.getSubInterfaces()));
|
|
|
}
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the first address with the proper ipVersion on the given interface on the current host.
|
|
|
- *
|
|
|
- * @param intf the interface to be checked
|
|
|
- * @param ipVersion Constraint on IP version of address to be returned, 4 or 6
|
|
|
- */
|
|
|
- public static InetAddress getFirstAddress(NetworkInterface intf, StackType ipVersion) throws SocketException {
|
|
|
- if (intf == null)
|
|
|
- throw new IllegalArgumentException("Network interface pointer is null");
|
|
|
-
|
|
|
- for (Enumeration addresses = intf.getInetAddresses(); addresses.hasMoreElements(); ) {
|
|
|
- InetAddress address = (InetAddress) addresses.nextElement();
|
|
|
- if ((address instanceof Inet4Address && ipVersion == StackType.IPv4) ||
|
|
|
- (address instanceof Inet6Address && ipVersion == StackType.IPv6))
|
|
|
- return address;
|
|
|
+
|
|
|
+ /** Returns system default for SO_REUSEADDR */
|
|
|
+ public static boolean defaultReuseAddress() {
|
|
|
+ return Constants.WINDOWS ? false : true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Returns localhost, or if its misconfigured, falls back to loopback. Use with caution!!!! */
|
|
|
+ // TODO: can we remove this?
|
|
|
+ public static InetAddress getLocalHost() {
|
|
|
+ try {
|
|
|
+ return InetAddress.getLocalHost();
|
|
|
+ } catch (UnknownHostException e) {
|
|
|
+ logger.warn("failed to resolve local host, fallback to loopback", e);
|
|
|
+ return InetAddress.getLoopbackAddress();
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * A function to check if an interface supports an IP version (i.e has addresses
|
|
|
- * defined for that IP version).
|
|
|
- *
|
|
|
- * @param intf
|
|
|
- * @return
|
|
|
- */
|
|
|
- public static boolean interfaceHasIPAddresses(NetworkInterface intf, StackType ipVersion) throws SocketException, UnknownHostException {
|
|
|
- boolean supportsVersion = false;
|
|
|
- if (intf != null) {
|
|
|
- // get all the InetAddresses defined on the interface
|
|
|
- Enumeration addresses = intf.getInetAddresses();
|
|
|
- while (addresses != null && addresses.hasMoreElements()) {
|
|
|
- // get the next InetAddress for the current interface
|
|
|
- InetAddress address = (InetAddress) addresses.nextElement();
|
|
|
-
|
|
|
- // check if we find an address of correct version
|
|
|
- if ((address instanceof Inet4Address && (ipVersion == StackType.IPv4)) ||
|
|
|
- (address instanceof Inet6Address && (ipVersion == StackType.IPv6))) {
|
|
|
- supportsVersion = true;
|
|
|
- break;
|
|
|
- }
|
|
|
+
|
|
|
+ /** Returns addresses for all loopback interfaces that are up. */
|
|
|
+ public static InetAddress[] getLoopbackAddresses() throws SocketException {
|
|
|
+ List<InetAddress> list = new ArrayList<>();
|
|
|
+ for (NetworkInterface intf : getInterfaces()) {
|
|
|
+ if (intf.isLoopback() && intf.isUp()) {
|
|
|
+ list.addAll(Collections.list(intf.getInetAddresses()));
|
|
|
}
|
|
|
- } else {
|
|
|
- throw new UnknownHostException("network interface not found");
|
|
|
}
|
|
|
- return supportsVersion;
|
|
|
+ if (list.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("No up-and-running loopback interfaces found, got " + getInterfaces());
|
|
|
+ }
|
|
|
+ sortAddresses(list);
|
|
|
+ return list.toArray(new InetAddress[list.size()]);
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * Tries to determine the type of IP stack from the available interfaces and their addresses and from the
|
|
|
- * system properties (java.net.preferIPv4Stack and java.net.preferIPv6Addresses)
|
|
|
- *
|
|
|
- * @return StackType.IPv4 for an IPv4 only stack, StackYTypeIPv6 for an IPv6 only stack, and StackType.Unknown
|
|
|
- * if the type cannot be detected
|
|
|
- */
|
|
|
- public static StackType getIpStackType() {
|
|
|
- boolean isIPv4StackAvailable = isStackAvailable(true);
|
|
|
- boolean isIPv6StackAvailable = isStackAvailable(false);
|
|
|
-
|
|
|
- // if only IPv4 stack available
|
|
|
- if (isIPv4StackAvailable && !isIPv6StackAvailable) {
|
|
|
- return StackType.IPv4;
|
|
|
+
|
|
|
+ /** Returns addresses for the first non-loopback interface that is up. */
|
|
|
+ public static InetAddress[] getFirstNonLoopbackAddresses() throws SocketException {
|
|
|
+ List<InetAddress> list = new ArrayList<>();
|
|
|
+ for (NetworkInterface intf : getInterfaces()) {
|
|
|
+ if (intf.isLoopback() == false && intf.isUp()) {
|
|
|
+ list.addAll(Collections.list(intf.getInetAddresses()));
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- // if only IPv6 stack available
|
|
|
- else if (isIPv6StackAvailable && !isIPv4StackAvailable) {
|
|
|
- return StackType.IPv6;
|
|
|
+ if (list.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("No up-and-running non-loopback interfaces found, got " + getInterfaces());
|
|
|
}
|
|
|
- // if dual stack
|
|
|
- else if (isIPv4StackAvailable && isIPv6StackAvailable) {
|
|
|
- // get the System property which records user preference for a stack on a dual stack machine
|
|
|
- if (Boolean.getBoolean(IPv4_SETTING)) // has preference over java.net.preferIPv6Addresses
|
|
|
- return StackType.IPv4;
|
|
|
- if (Boolean.getBoolean(IPv6_SETTING))
|
|
|
- return StackType.IPv6;
|
|
|
- return StackType.IPv6;
|
|
|
+ sortAddresses(list);
|
|
|
+ return list.toArray(new InetAddress[list.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Returns addresses for the given interface (it must be marked up) */
|
|
|
+ public static InetAddress[] getAddressesForInterface(String name) throws SocketException {
|
|
|
+ NetworkInterface intf = NetworkInterface.getByName(name);
|
|
|
+ if (intf == null) {
|
|
|
+ throw new IllegalArgumentException("No interface named '" + name + "' found, got " + getInterfaces());
|
|
|
}
|
|
|
- return StackType.Unknown;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public static boolean isStackAvailable(boolean ipv4) {
|
|
|
- Collection<InetAddress> allAddrs = getAllAvailableAddresses();
|
|
|
- for (InetAddress addr : allAddrs)
|
|
|
- if (ipv4 && addr instanceof Inet4Address || (!ipv4 && addr instanceof Inet6Address))
|
|
|
- return true;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns all the available interfaces, including first level sub interfaces.
|
|
|
- */
|
|
|
- public static List<NetworkInterface> getAllAvailableInterfaces() throws SocketException {
|
|
|
- List<NetworkInterface> allInterfaces = new ArrayList<>();
|
|
|
- for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements(); ) {
|
|
|
- NetworkInterface intf = interfaces.nextElement();
|
|
|
- allInterfaces.add(intf);
|
|
|
-
|
|
|
- Enumeration<NetworkInterface> subInterfaces = intf.getSubInterfaces();
|
|
|
- if (subInterfaces != null && subInterfaces.hasMoreElements()) {
|
|
|
- while (subInterfaces.hasMoreElements()) {
|
|
|
- allInterfaces.add(subInterfaces.nextElement());
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!intf.isUp()) {
|
|
|
+ throw new IllegalArgumentException("Interface '" + name + "' is not up and running");
|
|
|
}
|
|
|
- sortInterfaces(allInterfaces);
|
|
|
- return allInterfaces;
|
|
|
- }
|
|
|
-
|
|
|
- public static Collection<InetAddress> getAllAvailableAddresses() {
|
|
|
- // we want consistent order here.
|
|
|
- final Set<InetAddress> retval = new TreeSet<>(new Comparator<InetAddress>() {
|
|
|
- BytesRef left = new BytesRef();
|
|
|
- BytesRef right = new BytesRef();
|
|
|
- @Override
|
|
|
- public int compare(InetAddress o1, InetAddress o2) {
|
|
|
- return set(left, o1).compareTo(set(right, o1));
|
|
|
- }
|
|
|
-
|
|
|
- private BytesRef set(BytesRef ref, InetAddress addr) {
|
|
|
- ref.bytes = addr.getAddress();
|
|
|
- ref.offset = 0;
|
|
|
- ref.length = ref.bytes.length;
|
|
|
- return ref;
|
|
|
+ List<InetAddress> list = Collections.list(intf.getInetAddresses());
|
|
|
+ if (list.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("Interface '" + name + "' has no internet addresses");
|
|
|
+ }
|
|
|
+ sortAddresses(list);
|
|
|
+ return list.toArray(new InetAddress[list.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Returns addresses for the given host, sorted by order of preference */
|
|
|
+ public static InetAddress[] getAllByName(String host) throws UnknownHostException {
|
|
|
+ InetAddress addresses[] = InetAddress.getAllByName(host);
|
|
|
+ sortAddresses(Arrays.asList(addresses));
|
|
|
+ return addresses;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Returns only the IPV4 addresses in {@code addresses} */
|
|
|
+ public static InetAddress[] filterIPV4(InetAddress addresses[]) {
|
|
|
+ List<InetAddress> list = new ArrayList<>();
|
|
|
+ for (InetAddress address : addresses) {
|
|
|
+ if (address instanceof Inet4Address) {
|
|
|
+ list.add(address);
|
|
|
}
|
|
|
- });
|
|
|
- try {
|
|
|
- for (NetworkInterface intf : getInterfaces()) {
|
|
|
- Enumeration<InetAddress> addrs = intf.getInetAddresses();
|
|
|
- while (addrs.hasMoreElements())
|
|
|
- retval.add(addrs.nextElement());
|
|
|
+ }
|
|
|
+ if (list.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("No ipv4 addresses found in " + Arrays.toString(addresses));
|
|
|
+ }
|
|
|
+ return list.toArray(new InetAddress[list.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Returns only the IPV6 addresses in {@code addresses} */
|
|
|
+ public static InetAddress[] filterIPV6(InetAddress addresses[]) {
|
|
|
+ List<InetAddress> list = new ArrayList<>();
|
|
|
+ for (InetAddress address : addresses) {
|
|
|
+ if (address instanceof Inet6Address) {
|
|
|
+ list.add(address);
|
|
|
}
|
|
|
- } catch (SocketException e) {
|
|
|
- logger.warn("Failed to derive all available interfaces", e);
|
|
|
}
|
|
|
-
|
|
|
- return retval;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private NetworkUtils() {
|
|
|
-
|
|
|
+ if (list.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("No ipv6 addresses found in " + Arrays.toString(addresses));
|
|
|
+ }
|
|
|
+ return list.toArray(new InetAddress[list.size()]);
|
|
|
}
|
|
|
}
|