All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.aeron.driver.media.NetworkUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2025 Real Logic Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.aeron.driver.media;

import org.agrona.BufferUtil;

import java.net.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Objects;

import static java.lang.Boolean.compare;
import static java.lang.Integer.compare;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.util.Collections.sort;

/**
 * Collection of network specific utility functions.
 */
public final class NetworkUtil
{
    /**
     * Search for a list of network interfaces that match the specified address and subnet prefix.
     * The results will be ordered by the length of the subnet prefix
     * ({@link InterfaceAddress#getNetworkPrefixLength()}). If no results match, then the collection
     * will be empty.
     *
     * @param address      to search for on the {@link NetworkInterface}s.
     * @param subnetPrefix to limit the search.
     * @return {@link NetworkInterface}s that match the supplied criteria, ordered by the length
     * of the subnet prefix. Empty if none match.
     * @throws SocketException if an error occurs
     */
    public static NetworkInterface[] filterBySubnet(final InetAddress address, final int subnetPrefix)
        throws SocketException
    {
        return filterBySubnet(NetworkInterfaceShim.DEFAULT, address, subnetPrefix);
    }

    /**
     * Allocate a direct {@link ByteBuffer} that is padded at the end with at least alignment bytes.
     *
     * @param capacity  for the buffer.
     * @param alignment for the buffer.
     * @return the direct {@link ByteBuffer}.
     */
    public static ByteBuffer allocateDirectAlignedAndPadded(final int capacity, final int alignment)
    {
        final ByteBuffer buffer = BufferUtil.allocateDirectAligned(capacity + alignment, alignment);

        buffer.limit(buffer.limit() - alignment);

        return buffer.slice();
    }

    /**
     * Format an address and port pair, so they can be used in a URI endpoint.
     *
     * @param address part of the endpoint.
     * @param port    part of the endpoint.
     * @return The formatted string for an address, IPv4 or IPv6, and port separated by a ':'.
     */
    public static String formatAddressAndPort(final InetAddress address, final int port)
    {
        if (address instanceof Inet6Address)
        {
            return "[" + address.getHostAddress() + "]:" + port;
        }
        else
        {
            return address.getHostAddress() + ":" + port;
        }
    }

    /**
     * Get the {@link ProtocolFamily} to which the address belongs.
     *
     * @param address to get the {@link ProtocolFamily} for.
     * @return the {@link ProtocolFamily} to which the address belongs.
     */
    public static ProtocolFamily getProtocolFamily(final InetAddress address)
    {
        if (address instanceof Inet4Address)
        {
            return StandardProtocolFamily.INET;
        }
        else if (address instanceof Inet6Address)
        {
            return StandardProtocolFamily.INET6;
        }
        else
        {
            throw new IllegalStateException("Unknown ProtocolFamily");
        }
    }

    /**
     * Scans through to the local interfaces and returns the first bound InetAddress that matches the locally
     * defined address and network mask. Useful for determining a return path network address for machines with
     * a simple network topology. May not give the ideal result when a machine has multiple interfaces that match
     * an outgoing network address.
     *
     * @param address used to scan for a matching local address
     * @return first matching address bound to a local interface or null if none match.
     * @throws SocketException if an underlying SocketException is thrown, e.g. when getting the network interfaces.
     */
    public static InetAddress findFirstMatchingLocalAddress(final InetAddress address) throws SocketException
    {
        final Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements())
        {
            final NetworkInterface networkInterface = networkInterfaces.nextElement();
            for (final InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses())
            {
                if (NetworkUtil.isMatchWithPrefix(
                    address.getAddress(),
                    interfaceAddress.getAddress().getAddress(),
                    interfaceAddress.getNetworkPrefixLength()))
                {
                    return interfaceAddress.getAddress();
                }
            }
        }

        return null;
    }


    static NetworkInterface[] filterBySubnet(
        final NetworkInterfaceShim shim, final InetAddress address, final int subnetPrefix)
        throws SocketException
    {
        final ArrayList filterResults = new ArrayList<>();
        final byte[] queryAddress = address.getAddress();

        final Enumeration interfaces = shim.getNetworkInterfaces();
        while (interfaces.hasMoreElements())
        {
            final NetworkInterface networkInterface = interfaces.nextElement();
            final InterfaceAddress interfaceAddress = findAddressOnInterface(
                shim, networkInterface, queryAddress, subnetPrefix);

            if (null != interfaceAddress)
            {
                filterResults.add(new FilterResult(
                    interfaceAddress, networkInterface, shim.isLoopback(networkInterface)));
            }
        }

        sort(filterResults);

        final int size = filterResults.size();
        final NetworkInterface[] results = new NetworkInterface[size];
        for (int i = 0; i < size; i++)
        {
            results[i] = filterResults.get(i).networkInterface;
        }

        return results;
    }

    static InetAddress findAddressOnInterface(
        final NetworkInterface networkInterface, final InetAddress address, final int subnetPrefix)
    {
        final InterfaceAddress interfaceAddress = findAddressOnInterface(
            NetworkInterfaceShim.DEFAULT, networkInterface, address.getAddress(), subnetPrefix);

        return null == interfaceAddress ? null : interfaceAddress.getAddress();
    }

    static InterfaceAddress findAddressOnInterface(
        final NetworkInterfaceShim shim,
        final NetworkInterface networkInterface,
        final byte[] queryAddress,
        final int prefixLength)
    {
        for (final InterfaceAddress interfaceAddress : shim.getInterfaceAddresses(networkInterface))
        {
            if (null != interfaceAddress)
            {
                final InetAddress address = interfaceAddress.getAddress();
                if (null != address)
                {
                    if (isMatchWithPrefix(address.getAddress(), queryAddress, prefixLength))
                    {
                        return interfaceAddress;
                    }
                }
            }
        }

        return null;
    }

    /**
     * Matches to network address with the specified prefix length.  Only works with 4 and 16 byte addresses.
     *
     * @param candidate address to be matched
     * @param expected address to match against (could be a network address - with 0s at the end)
     * @param prefixLength the number of bit required to match.
     * @return true if the leading prefixLength number of bits of the two addresses match.
     */
    public static boolean isMatchWithPrefix(final byte[] candidate, final byte[] expected, final int prefixLength)
    {
        if (candidate.length != expected.length)
        {
            return false;
        }

        if (candidate.length == 4)
        {
            final int mask = prefixLengthToIpV4Mask(prefixLength);

            return (toInt(candidate) & mask) == (toInt(expected) & mask);
        }
        else if (candidate.length == 16)
        {
            final long upperMask = prefixLengthToIpV6Mask(min(prefixLength, 64));
            final long lowerMask = prefixLengthToIpV6Mask(max(prefixLength - 64, 0));

            return
                (upperMask & toLong(candidate, 0)) == (upperMask & toLong(expected, 0)) &&
                (lowerMask & toLong(candidate, 8)) == (lowerMask & toLong(expected, 8));
        }

        throw new IllegalArgumentException("how many bytes does an IP address have again?");
    }

    static int prefixLengthToIpV4Mask(final int subnetPrefix)
    {
        return 0 == subnetPrefix ? 0 : -(1 << 32 - subnetPrefix);
    }

    private static long prefixLengthToIpV6Mask(final int subnetPrefix)
    {
        return 0 == subnetPrefix ? 0 : -(1L << 64 - subnetPrefix);
    }

    private static int toInt(final byte[] b)
    {
        return ((b[3] & 0xFF)) + ((b[2] & 0xFF) << 8) + ((b[1] & 0xFF) << 16) + ((b[0]) << 24);
    }

    static long toLong(final byte[] b, final int offset)
    {
        return ((b[offset + 7] & 0xFFL)) +
            ((b[offset + 6] & 0xFFL) << 8) +
            ((b[offset + 5] & 0xFFL) << 16) +
            ((b[offset + 4] & 0xFFL) << 24) +
            ((b[offset + 3] & 0xFFL) << 32) +
            ((b[offset + 2] & 0xFFL) << 40) +
            ((b[offset + 1] & 0xFFL) << 48) +
            (((long)b[offset]) << 56);
    }

    static class FilterResult implements Comparable
    {
        private final InterfaceAddress interfaceAddress;
        private final NetworkInterface networkInterface;
        private final boolean isLoopback;

        FilterResult(
            final InterfaceAddress interfaceAddress,
            final NetworkInterface networkInterface,
            final boolean isLoopback)
        {
            this.interfaceAddress = interfaceAddress;
            this.networkInterface = networkInterface;
            this.isLoopback = isLoopback;
        }

        public int compareTo(final FilterResult other)
        {
            if (isLoopback == other.isLoopback)
            {
                return -compare(
                    interfaceAddress.getNetworkPrefixLength(),
                    other.interfaceAddress.getNetworkPrefixLength());
            }
            else
            {
                return compare(isLoopback, other.isLoopback);
            }
        }

        public boolean equals(final Object o)
        {
            return o instanceof FilterResult && compareTo((FilterResult)o) == 0;
        }

        public int hashCode()
        {
            return Objects.hash(interfaceAddress, networkInterface, isLoopback);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy