IpPrefixFunctions.java
/*
* 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.facebook.presto.operator.scalar;
import com.facebook.presto.common.block.Block;
import com.facebook.presto.common.block.BlockBuilder;
import com.facebook.presto.common.type.StandardTypes;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.function.Description;
import com.facebook.presto.spi.function.LiteralParameters;
import com.facebook.presto.spi.function.ScalarFunction;
import com.facebook.presto.spi.function.SqlType;
import com.google.common.net.InetAddresses;
import io.airlift.slice.Slice;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static com.facebook.presto.operator.scalar.ArraySortFunction.sort;
import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR;
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
import static com.facebook.presto.type.IpAddressOperators.between;
import static com.facebook.presto.type.IpAddressOperators.castFromVarcharToIpAddress;
import static com.facebook.presto.type.IpAddressType.IPADDRESS;
import static com.facebook.presto.type.IpPrefixOperators.castFromIpPrefixToIpAddress;
import static com.facebook.presto.type.IpPrefixOperators.castFromVarcharToIpPrefix;
import static com.facebook.presto.type.IpPrefixType.IPPREFIX;
import static com.facebook.presto.util.Failures.checkCondition;
import static io.airlift.slice.Slices.utf8Slice;
import static io.airlift.slice.Slices.wrappedBuffer;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.System.arraycopy;
public final class IpPrefixFunctions
{
private static final BigInteger TWO = BigInteger.valueOf(2);
private static final Block EMPTY_BLOCK = IPPREFIX.createBlockBuilder(null, 0).build();
/**
* Our definitions for what IANA considers not "globally reachable" are taken from the docs at
* https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml and
* https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml.
* Java's InetAddress.isSiteLocalAddress only covers three of these: 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16,
* so we operate over the complete list below.
*/
private static final String[] privatePrefixes = new String[] {
// IPv4 private ranges
"0.0.0.0/8", // RFC1122: "This host on this network"
"10.0.0.0/8", // RFC1918: Private-Use
"100.64.0.0/10", // RFC6598: Shared Address Space
"127.0.0.0/8", // RFC1122: Loopback
"169.254.0.0/16", // RFC3927: Link Local
"172.16.0.0/12", // RFC1918: Private-Use
"192.0.0.0/24", // RFC6890: IETF Protocol Assignments
"192.0.2.0/24", // RFC5737: Documentation (TEST-NET-1)
"192.88.99.0/24", // RFC3068: 6to4 Relay anycast
"192.168.0.0/16", // RFC1918: Private-Use
"198.18.0.0/15", // RFC2544: Benchmarking
"198.51.100.0/24", // RFC5737: Documentation (TEST-NET-2)
"203.0.113.0/24", // RFC5737: Documentation (TEST-NET-3)
"240.0.0.0/4", // RFC1112: Reserved
// IPv6 private ranges
"::/127", // RFC4291: Unspecified address and Loopback address
"64:ff9b:1::/48", // RFC8215: IPv4-IPv6 Translation
"100::/64", // RFC6666: Discard-Only Address Block
"2001:2::/48", // RFC5180, RFC Errata 1752: Benchmarking
"2001:db8::/32", // RFC3849: Documentation
"2001::/23", // RFC2928: IETF Protocol Assignments
"5f00::/16", // RFC-ietf-6man-sids-06: Segment Routing (SRv6)
"fe80::/10", // RFC4291: Link-Local Unicast
"fc00::/7", // RFC4193, RFC8190: Unique Local
};
private static final List<BigInteger[]> privateIPv4AddressRanges;
private static final List<BigInteger[]> privateIPv6AddressRanges;
static {
privateIPv4AddressRanges = new ArrayList<>();
privateIPv6AddressRanges = new ArrayList<>();
// convert the private prefixes into the first and last BigInteger ranges
for (String privatePrefix : privatePrefixes) {
Slice ipPrefixSlice = castFromVarcharToIpPrefix(utf8Slice(privatePrefix));
Slice startingIpAddress = ipSubnetMin(ipPrefixSlice);
Slice endingIpAddress = ipSubnetMax(ipPrefixSlice);
BigInteger startingIpAsBigInt = toBigInteger(startingIpAddress);
BigInteger endingIpAsBigInt = toBigInteger(endingIpAddress);
BigInteger[] privateRange = new BigInteger[]{startingIpAsBigInt, endingIpAsBigInt};
if (isIpv4(ipPrefixSlice)) {
privateIPv4AddressRanges.add(privateRange);
}
else {
privateIPv6AddressRanges.add(privateRange);
}
}
privateIPv4AddressRanges.sort(Comparator.comparing(e -> e[0]));
privateIPv6AddressRanges.sort(Comparator.comparing(e -> e[0]));
}
private IpPrefixFunctions() {}
@Description("IP prefix for a given IP address and subnet size")
@ScalarFunction("ip_prefix")
@SqlType(StandardTypes.IPPREFIX)
public static Slice ipPrefix(@SqlType(StandardTypes.IPADDRESS) Slice value, @SqlType(StandardTypes.BIGINT) long subnetSize)
{
InetAddress address = toInetAddress(value);
int addressLength = address.getAddress().length;
if (addressLength == 4) {
checkCondition(0 <= subnetSize && subnetSize <= 32, INVALID_FUNCTION_ARGUMENT, "IPv4 subnet size must be in range [0, 32]");
}
else if (addressLength == 16) {
checkCondition(0 <= subnetSize && subnetSize <= 128, INVALID_FUNCTION_ARGUMENT, "IPv6 subnet size must be in range [0, 128]");
}
else {
throw new PrestoException(GENERIC_INTERNAL_ERROR, "Invalid InetAddress length: " + addressLength);
}
return castFromVarcharToIpPrefix(utf8Slice(InetAddresses.toAddrString(address) + "/" + subnetSize));
}
@Description("IP prefix for a given IP address and subnet size")
@ScalarFunction("ip_prefix")
@LiteralParameters("x")
@SqlType(StandardTypes.IPPREFIX)
public static Slice stringIpPrefix(@SqlType("varchar(x)") Slice slice, @SqlType(StandardTypes.BIGINT) long subnetSize)
{
return ipPrefix(castFromVarcharToIpAddress(slice), subnetSize);
}
@Description("Smallest IP address for a given IP prefix")
@ScalarFunction("ip_subnet_min")
@SqlType(StandardTypes.IPADDRESS)
public static Slice ipSubnetMin(@SqlType(StandardTypes.IPPREFIX) Slice value)
{
return castFromIpPrefixToIpAddress(value);
}
@Description("Largest IP address for a given IP prefix")
@ScalarFunction("ip_subnet_max")
@SqlType(StandardTypes.IPADDRESS)
public static Slice ipSubnetMax(@SqlType(StandardTypes.IPPREFIX) Slice value)
{
byte[] address = toInetAddress(value.slice(0, IPADDRESS.getFixedSize())).getAddress();
int subnetSize = value.getByte(IPPREFIX.getFixedSize() - 1) & 0xFF;
if (address.length == 4) {
for (int i = 0; i < 4; i++) {
address[3 - i] |= (byte) ~(~0 << min(max((32 - subnetSize) - 8 * i, 0), 8));
}
byte[] bytes = new byte[16];
bytes[10] = (byte) 0xff;
bytes[11] = (byte) 0xff;
arraycopy(address, 0, bytes, 12, 4);
address = bytes;
}
else if (address.length == 16) {
for (int i = 0; i < 16; i++) {
address[15 - i] |= (byte) ~(~0 << min(max((128 - subnetSize) - 8 * i, 0), 8));
}
}
return wrappedBuffer(address);
}
@Description("Array of smallest and largest IP address in the subnet of the given IP prefix")
@ScalarFunction("ip_subnet_range")
@SqlType("array(IPADDRESS)")
public static Block ipSubnetRange(@SqlType(StandardTypes.IPPREFIX) Slice value)
{
BlockBuilder blockBuilder = IPADDRESS.createBlockBuilder(null, 2);
IPADDRESS.writeSlice(blockBuilder, ipSubnetMin(value));
IPADDRESS.writeSlice(blockBuilder, ipSubnetMax(value));
return blockBuilder.build();
}
@Description("Is the IP address in the subnet of IP prefix")
@ScalarFunction("is_subnet_of")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isSubnetOf(@SqlType(StandardTypes.IPPREFIX) Slice ipPrefix, @SqlType(StandardTypes.IPADDRESS) Slice ipAddress)
{
toInetAddress(ipAddress);
return between(ipAddress, ipSubnetMin(ipPrefix), ipSubnetMax(ipPrefix));
}
@Description("Is the second IP prefix in the subnet of the first IP prefix")
@ScalarFunction("is_subnet_of")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isPrefixSubnetOf(@SqlType(StandardTypes.IPPREFIX) Slice first, @SqlType(StandardTypes.IPPREFIX) Slice second)
{
return between(ipSubnetMin(second), ipSubnetMin(first), ipSubnetMax(first)) && between(ipSubnetMax(second), ipSubnetMin(first), ipSubnetMax(first));
}
@Description("Combines the input set of IP prefixes into the fewest contiguous CIDR ranges possible.")
@ScalarFunction("ip_prefix_collapse")
@SqlType("array(IPPREFIX)")
public static Block collapseIpPrefixes(@SqlType("array(IPPREFIX)") Block unsortedIpPrefixArray)
{
int inputPrefixCount = unsortedIpPrefixArray.getPositionCount();
// If we get an empty array or an array non-null single element, just return the original array.
if (inputPrefixCount == 0 || (inputPrefixCount == 1 && !unsortedIpPrefixArray.isNull(0))) {
return unsortedIpPrefixArray;
}
// Sort prefixes. lessThanFunction is never used. NULLs are placed at the end.
// Prefixes are ordered by first IP and then prefix length.
// Example:
// Input: 10.0.0.0/8, 9.255.255.0/24, 10.0.0.0/7, 10.1.0.0/24, 10.10.0.0/16
// Output: 9.255.255.0/24, 10.0.0.0/7, 10.0.0.0/8, 10.1.0.0/24, 10.10.0.0/16
Block ipPrefixArray = sort(null, IPPREFIX, unsortedIpPrefixArray);
// throw if anything is null
if (ipPrefixArray.isNull(0) || ipPrefixArray.isNull(inputPrefixCount - 1)) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "ip_prefix_collapse does not support null elements");
}
// check the first and last prefixes in the array to make sure their IP versions match.
Slice firstIpPrefix = IPPREFIX.getSlice(ipPrefixArray, 0);
boolean v4 = isIpv4(firstIpPrefix);
Slice lastIpPrefix = IPPREFIX.getSlice(ipPrefixArray, inputPrefixCount - 1);
if (isIpv4(lastIpPrefix) != v4) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "All IPPREFIX elements must be the same IP version.");
}
List<List<Slice>> outputIpPrefixes = new ArrayList<>();
int outputPrefixCount = 0;
int ipMaxBitLength = v4 ? 32 : 128;
List<BigInteger[]> mergedIpRanges = mergeIpRanges(ipPrefixArray);
for (BigInteger[] ipRange : mergedIpRanges) {
List<Slice> ipPrefixes = generateMinIpPrefixes(ipRange[0], ipRange[1], ipMaxBitLength);
outputIpPrefixes.add(ipPrefixes);
outputPrefixCount += ipPrefixes.size();
}
BlockBuilder blockBuilder = IPPREFIX.createBlockBuilder(null, outputPrefixCount);
for (List<Slice> ipPrefixSlices : outputIpPrefixes) {
for (Slice ipPrefix : ipPrefixSlices) {
IPPREFIX.writeSlice(blockBuilder, ipPrefix);
}
}
return blockBuilder.build();
}
@Description("Returns whether ipAddress is a private or reserved IP address that is not globally reachable.")
@ScalarFunction("is_private_ip")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isPrivateIpAddress(@SqlType(StandardTypes.IPADDRESS) Slice ipAddress)
{
BigInteger ipAsBigInt = toBigInteger(ipAddress);
boolean isIPv4 = isIpv4(ipAddress);
List<BigInteger[]> rangesToCheck = isIPv4 ? privateIPv4AddressRanges : privateIPv6AddressRanges;
// rangesToCheck is sorted
for (BigInteger[] privateAddressRange : rangesToCheck) {
BigInteger startIp = privateAddressRange[0];
BigInteger endIp = privateAddressRange[1];
if (ipAsBigInt.compareTo(startIp) < 0) {
return false; // current and subsequent ranges are all higher values, so we can fail fast here.
}
// ipAddress at least >= to startIp
if (ipAsBigInt.compareTo(endIp) <= 0) {
return true; // if ipAsBigInt is in between startIp and endIp of private range then return true
}
}
return false;
}
@Description("Split the input prefix into subnets the size of the new prefix length.")
@ScalarFunction("ip_prefix_subnets")
@SqlType("array(IPPREFIX)")
public static Block ipPrefixSubnets(@SqlType(StandardTypes.IPPREFIX) Slice prefix, @SqlType(StandardTypes.BIGINT) long newPrefixLength)
{
boolean inputIsIpV4 = isIpv4(prefix);
if (newPrefixLength < 0 || (inputIsIpV4 && newPrefixLength > 32) || (!inputIsIpV4 && newPrefixLength > 128)) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Invalid prefix length for IPv" + (inputIsIpV4 ? "4" : "6") + ": " + newPrefixLength);
}
int inputPrefixLength = getPrefixLength(prefix);
// An IP prefix is a 'network', or group of contiguous IP addresses. The common format for describing IP prefixes is
// uses 2 parts separated by a '/': (1) the IP address part and the (2) prefix length part (also called subnet size or CIDR).
// For example, in 9.255.255.0/24, 9.255.255.0 is the IP address part and 24 is the prefix length.
// The prefix length describes how many IP addresses the prefix contains in terms of the leading number of bits required. A higher number of bits
// means smaller number of IP addresses. Subnets inherently mean smaller groups of IP addresses.
// We can only disaggregate a prefix if the prefix length is the same length or longer (more-specific) than the length of the input prefix.
// E.g., if the input prefix is 9.255.255.0/24, the prefix length can be /24, /25, /26, etc... but not 23 or larger value than 24.
int newPrefixCount = 0; // if inputPrefixLength > newPrefixLength, there are no new prefixes and we will return an empty array.
if (inputPrefixLength <= newPrefixLength) {
// Next, count how many new prefixes we will generate. In general, every difference in prefix length doubles the number new prefixes.
// For example if we start with 9.255.255.0/24, and want to split into /25s, we would have 2 new prefixes. If we wanted to split into /26s,
// we would have 4 new prefixes, and /27 would have 8 prefixes etc....
newPrefixCount = 1 << (newPrefixLength - inputPrefixLength); // 2^N
}
if (newPrefixCount == 0) {
return EMPTY_BLOCK;
}
BlockBuilder blockBuilder = IPPREFIX.createBlockBuilder(null, newPrefixCount);
if (newPrefixCount == 1) {
IPPREFIX.writeSlice(blockBuilder, prefix); // just return the original prefix in an array
return blockBuilder.build(); // returns empty or single entry
}
int ipVersionMaxBits = inputIsIpV4 ? 32 : 128;
BigInteger newPrefixIpCount = TWO.pow(ipVersionMaxBits - (int) newPrefixLength);
Slice startingIpAddressAsSlice = ipSubnetMin(prefix);
BigInteger currentIpAddress = toBigInteger(startingIpAddressAsSlice);
try {
for (int i = 0; i < newPrefixCount; i++) {
InetAddress asInetAddress = bigIntegerToIpAddress(currentIpAddress);
Slice ipPrefixAsSlice = castFromVarcharToIpPrefix(utf8Slice(InetAddresses.toAddrString(asInetAddress) + "/" + newPrefixLength));
IPPREFIX.writeSlice(blockBuilder, ipPrefixAsSlice);
currentIpAddress = currentIpAddress.add(newPrefixIpCount); // increment to start of next new prefix
}
}
catch (UnknownHostException ex) {
throw new PrestoException(GENERIC_INTERNAL_ERROR, "Unable to convert " + currentIpAddress + " to IP prefix", ex);
}
return blockBuilder.build();
}
private static int getPrefixLength(Slice ipPrefix)
{
return ipPrefix.getByte(IPPREFIX.getFixedSize() - 1) & 0xFF;
}
private static List<Slice> generateMinIpPrefixes(BigInteger firstIpAddress, BigInteger lastIpAddress, int ipVersionMaxBits)
{
List<Slice> ipPrefixSlices = new ArrayList<>();
// i.e., while firstIpAddress <= lastIpAddress
while (firstIpAddress.compareTo(lastIpAddress) <= 0) {
long rangeBits = findRangeBits(firstIpAddress, lastIpAddress); // find the number of bits for the next prefix in the range
int prefixLength = (int) (ipVersionMaxBits - rangeBits);
try {
InetAddress asInetAddress = bigIntegerToIpAddress(firstIpAddress); // convert firstIpAddress from BigInt to Slice
Slice ipPrefixAsSlice = castFromVarcharToIpPrefix(utf8Slice(InetAddresses.toAddrString(asInetAddress) + "/" + prefixLength));
ipPrefixSlices.add(ipPrefixAsSlice);
}
catch (UnknownHostException ex) {
throw new PrestoException(GENERIC_INTERNAL_ERROR, "Unable to convert " + firstIpAddress + " to IP prefix", ex);
}
BigInteger ipCount = TWO.pow(ipVersionMaxBits - prefixLength);
firstIpAddress = firstIpAddress.add(ipCount); // move to the next prefix in the range
}
return ipPrefixSlices;
}
private static long findRangeBits(BigInteger firstIpAddress, BigInteger lastIpAddress)
{
// The number of IP addresses in the range
BigInteger ipCount = lastIpAddress.subtract(firstIpAddress).add(BigInteger.ONE);
// We have two possibilities for determining the right prefix boundary
// Case 1. Find the largest possible prefix that firstIpAddress can be.
// Say we have an input range of 192.168.0.0 to 192.184.0.0.
// The number of IP addresses in the range is 1048576 = 2^20, so we would need a /12 (32-20).
// to cover that many IP addresses but the largest valid prefix that can start from 192.168.0.0 is /13.
int firstAddressMaxBits = firstIpAddress.getLowestSetBit();
// Case 2. Find the largest prefix length to cover N IP addresses.
// The number of IP addresses within a valid prefix must be a power of 2 but the IP count
// in our IP ranges may not be a power of 2. If it isn't exactly a power of 2, we find the
// highest power of 2 that the doesn't overrun the ipCount.
// If ipCount's bitLength is greater than the number of IP addresses (i.e., not a power of 2), then use 1 bit less.
int ipRangeMaxBits = (TWO.pow(ipCount.bitLength()).compareTo(ipCount) > 0) ? ipCount.bitLength() - 1 : ipCount.bitLength();
return min(firstAddressMaxBits, ipRangeMaxBits);
}
private static List<BigInteger[]> mergeIpRanges(Block ipPrefixArray)
{
List<BigInteger[]> mergedRanges = new ArrayList<>();
Slice startingIpPrefix = IPPREFIX.getSlice(ipPrefixArray, 0);
BigInteger firstIpAddress = toBigInteger(ipSubnetMin(startingIpPrefix));
BigInteger lastIpAddress = toBigInteger(ipSubnetMax(startingIpPrefix));
/*
There are four cases to cover for two IP ranges where range1.startIp <= range2.startIp
1. Could be equal/duplicates.
[-------]
[-------]
In this case, we just ignore the second one.
2. Second could be subnet/contained within first.
[-------] OR [-------] OR [-------]
[---] [----] [----]
In this case we ignore the second one.
3. Second could be adjacent/contiguous with the first.
[-------]
[-------]
In this case we extend the range to include the last IP address of the second one.
4. Second can be disjoint from the first.
[-------]
[-------]
In this case the first range is finalized, and the second range becomes the current one.
*/
for (int i = 1; i < ipPrefixArray.getPositionCount(); i++) {
Slice ipPrefix = IPPREFIX.getSlice(ipPrefixArray, i);
BigInteger nextFirstIpAddress = toBigInteger(ipSubnetMin(ipPrefix));
BigInteger nextLastIpAddress = toBigInteger(ipSubnetMax(ipPrefix));
// If nextFirstIpAddress <= lastIpAddress then there is overlap.
// However, based on the properties of the input sorted array, this will
// always mean that the next* range is a subnet of [firstIpAddress, lastIpAddress].
// We just ignore these prefixes since they are already covered (case 1 and case 2).
if (lastIpAddress.compareTo(nextFirstIpAddress) < 0) { // i.e. nextFirstIpAddress > lastIpAddress -- the next range does not overlap the first
// If they are not contiguous (case 4), finalize the range.
// Otherwise, extend the current range (case 3).
if (lastIpAddress.add(BigInteger.ONE).compareTo(nextFirstIpAddress) != 0) {
BigInteger[] finalizedRange = {firstIpAddress, lastIpAddress};
mergedRanges.add(finalizedRange);
firstIpAddress = nextFirstIpAddress;
}
lastIpAddress = nextLastIpAddress;
}
}
// Add the last range
BigInteger[] finalizedRange = {firstIpAddress, lastIpAddress};
mergedRanges.add(finalizedRange);
return mergedRanges;
}
private static byte[] bigIntegerToIPAddressBytes(BigInteger ipAddress)
{
byte[] ipAddressBytes = ipAddress.toByteArray();
// Covers IPv4 (4 bytes) and IPv6 (16 bytes) plus an additional 0-value byte for sign
if ((ipAddressBytes.length == 5 || ipAddressBytes.length == 17) && ipAddressBytes[0] == 0) {
ipAddressBytes = Arrays.copyOfRange(ipAddressBytes, 1, ipAddressBytes.length); // remove leading 0
}
// Covers IPv4 and IPv6 cases when BigInteger needs less than 4 or 16 bytes to represent
// the integer value. E.g., 0.0.0.1 will be 1 byte and 15.1.99.212 will be 3 bytes
else if (ipAddressBytes.length <= 3 || (ipAddressBytes.length != 4 && ipAddressBytes.length <= 15)) {
// start with zero'd out byte array and fill in starting at position j
byte[] emptyRange = new byte[ipAddressBytes.length <= 3 ? 4 : 16];
int j = emptyRange.length - ipAddressBytes.length;
for (int i = 0; i < ipAddressBytes.length; i++, j++) {
emptyRange[j] = ipAddressBytes[i];
}
ipAddressBytes = emptyRange;
}
// else length is already 4 or 16
return ipAddressBytes;
}
private static InetAddress bigIntegerToIpAddress(BigInteger ipAddress) throws UnknownHostException
{
byte[] ipAddressBytes = bigIntegerToIPAddressBytes(ipAddress);
return InetAddress.getByAddress(ipAddressBytes);
}
private static BigInteger toBigInteger(Slice ipAddress)
{
// first param sets values to always be positive
return new BigInteger(1, ipAddress.getBytes());
}
private static boolean isIpv4(Slice ipPrefix)
{
// IPADDRESS types are 16 bytes for IPv4 and IPv6. IPv4 is stored as IPv4-mapped IPv6 addresses specified in RFC 4291.
// The IPv4 address is encoded into the low-order 32 bits of the IPv6 address, and the high-order 96 bits
// hold the fixed prefix 0:0:0:0:0:FFFF.
// To check if this is an IPv4 address, we check if the first 10 bytes are 0 and that bytes 11 and 12 are 0xFF.
byte[] ipPartBytes = ipPrefix.getBytes(0, 2 * Long.BYTES);
for (int i = 0; i <= 9; i++) {
if (ipPartBytes[i] != (byte) 0) {
return false;
}
}
return ipPartBytes[10] == (byte) 0xff && ipPartBytes[11] == (byte) 0xff;
}
private static InetAddress toInetAddress(Slice ipAddress)
{
try {
return InetAddress.getByAddress(ipAddress.getBytes());
}
catch (UnknownHostException e) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Invalid IP address binary: " + ipAddress.toStringUtf8(), e);
}
}
}