/* * Copyright (C) 2020 The Android Open Source Project * * 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 * * http://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 com.android.net.module.util; import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.NonNull; import android.net.IpPrefix; import com.android.internal.annotations.VisibleForTesting; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Queue; /** * This class represents an IP range, i.e., a contiguous block of IP addresses defined by a starting * and ending IP address. These addresses may not be power-of-two aligned. * *
Conversion to prefixes are deterministic, and will always return the same set of {@link * IpPrefix}(es). Ordering of IpPrefix instances is not guaranteed. * * @hide */ public final class IpRange { private static final int SIGNUM_POSITIVE = 1; private final byte[] mStartAddr; private final byte[] mEndAddr; public IpRange(@NonNull InetAddress startAddr, @NonNull InetAddress endAddr) { Objects.requireNonNull(startAddr, "startAddr must not be null"); Objects.requireNonNull(endAddr, "endAddr must not be null"); if (!startAddr.getClass().equals(endAddr.getClass())) { throw new IllegalArgumentException("Invalid range: Address family mismatch"); } if (addrToBigInteger(startAddr.getAddress()).compareTo( addrToBigInteger(endAddr.getAddress())) >= 0) { throw new IllegalArgumentException( "Invalid range; start address must be before end address"); } mStartAddr = startAddr.getAddress(); mEndAddr = endAddr.getAddress(); } @VisibleForTesting(visibility = Visibility.PRIVATE) public IpRange(@NonNull IpPrefix prefix) { Objects.requireNonNull(prefix, "prefix must not be null"); // Use masked address from IpPrefix to zero out lower order bits. mStartAddr = prefix.getRawAddress(); // Set all non-prefix bits to max. mEndAddr = prefix.getRawAddress(); for (int bitIndex = prefix.getPrefixLength(); bitIndex < 8 * mEndAddr.length; ++bitIndex) { mEndAddr[bitIndex / 8] |= (byte) (0x80 >> (bitIndex % 8)); } } private static InetAddress getAsInetAddress(byte[] address) { try { return InetAddress.getByAddress(address); } catch (UnknownHostException e) { // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte // array is the wrong length, but are always generated from InetAddress(es). throw new IllegalArgumentException("Address is invalid"); } } @VisibleForTesting(visibility = Visibility.PRIVATE) public InetAddress getStartAddr() { return getAsInetAddress(mStartAddr); } @VisibleForTesting(visibility = Visibility.PRIVATE) public InetAddress getEndAddr() { return getAsInetAddress(mEndAddr); } /** * Converts this IP range to a list of IpPrefix instances. * *
This method outputs the IpPrefix instances for use in the routing architecture. * *
For example, the range 192.0.2.4 - 192.0.3.1 converts to the following prefixes: * *
For example, for the prefix 192.0.2.0/24, this will return the two prefixes that combined * make up the current prefix: * *
Checks based on byte values. For other to be contained within this IP range, other's * starting address must be greater or equal to the current IpRange's starting address, and the * other's ending address must be less than or equal to the current IP range's ending address. */ @VisibleForTesting(visibility = Visibility.PRIVATE) public boolean containsRange(IpRange other) { return addrToBigInteger(mStartAddr).compareTo(addrToBigInteger(other.mStartAddr)) <= 0 && addrToBigInteger(mEndAddr).compareTo(addrToBigInteger(other.mEndAddr)) >= 0; } /** * Checks if the other IP range overlaps with this one * *
Checks based on byte values. For there to be overlap, this IpRange's starting address must * be less than the other's ending address, and vice versa. */ @VisibleForTesting(visibility = Visibility.PRIVATE) public boolean overlapsRange(IpRange other) { return addrToBigInteger(mStartAddr).compareTo(addrToBigInteger(other.mEndAddr)) <= 0 && addrToBigInteger(other.mStartAddr).compareTo(addrToBigInteger(mEndAddr)) <= 0; } @Override public int hashCode() { return Objects.hash(mStartAddr, mEndAddr); } @Override public boolean equals(Object obj) { if (!(obj instanceof IpRange)) { return false; } final IpRange other = (IpRange) obj; return Arrays.equals(mStartAddr, other.mStartAddr) && Arrays.equals(mEndAddr, other.mEndAddr); } /** Gets the InetAddress in BigInteger form */ private static BigInteger addrToBigInteger(byte[] addr) { // Since addr.getAddress() returns network byte order (big-endian), it is compatible with // the BigInteger constructor (which assumes big-endian). return new BigInteger(SIGNUM_POSITIVE, addr); } }