/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.common.network; import java.util.Arrays; import java.util.Locale; import java.util.Objects; public final class Cidrs { private Cidrs() { } /** * Parses an IPv4 address block in CIDR notation into a pair of * longs representing the bottom and top of the address block * * @param cidr an address block in CIDR notation a.b.c.d/n * @return array representing the address block * @throws IllegalArgumentException if the cidr can not be parsed */ public static long[] cidrMaskToMinMax(String cidr) { Objects.requireNonNull(cidr, "cidr"); String[] fields = cidr.split("/"); if (fields.length != 2) { throw new IllegalArgumentException( String.format(Locale.ROOT, "invalid IPv4/CIDR; expected [a.b.c.d, e] but was [%s] after splitting on \"/\" in [%s]", Arrays.toString(fields), cidr) ); } // do not try to parse IPv4-mapped IPv6 address if (fields[0].contains(":")) { throw new IllegalArgumentException( String.format(Locale.ROOT, "invalid IPv4/CIDR; expected [a.b.c.d, e] where a, b, c, d are decimal octets but was [%s] after splitting on \"/\" in [%s]", Arrays.toString(fields), cidr) ); } byte[] addressBytes; try { addressBytes = InetAddresses.forString(fields[0]).getAddress(); } catch (Exception e) { throw new IllegalArgumentException( String.format(Locale.ROOT, "invalid IPv4/CIDR; unable to parse [%s] as an IP address literal", fields[0]), e ); } long accumulator = ((addressBytes[0] & 0xFFL) << 24) + ((addressBytes[1] & 0xFFL) << 16) + ((addressBytes[2] & 0xFFL) << 8) + ((addressBytes[3] & 0xFFL)); int networkMask; try { networkMask = Integer.parseInt(fields[1]); } catch (NumberFormatException e) { throw new IllegalArgumentException( String.format(Locale.ROOT, "invalid IPv4/CIDR; invalid network mask [%s] in [%s]", fields[1], cidr), e ); } if (networkMask < 0 || networkMask > 32) { throw new IllegalArgumentException( String.format(Locale.ROOT, "invalid IPv4/CIDR; invalid network mask [%s], out of range in [%s]", fields[1], cidr) ); } long blockSize = 1L << (32 - networkMask); // validation if ((accumulator & (blockSize - 1)) != 0) { throw new IllegalArgumentException( String.format( Locale.ROOT, "invalid IPv4/CIDR; invalid address/network mask combination in [%s]; perhaps [%s] was intended?", cidr, octetsToCIDR(longToOctets(accumulator - (accumulator & (blockSize - 1))), networkMask) ) ); } return new long[] { accumulator, accumulator + blockSize }; } static int[] longToOctets(long value) { assert value >= 0 && value <= (1L << 32) : value; int[] octets = new int[4]; octets[0] = (int)((value >> 24) & 0xFF); octets[1] = (int)((value >> 16) & 0xFF); octets[2] = (int)((value >> 8) & 0xFF); octets[3] = (int)(value & 0xFF); return octets; } static String octetsToString(int[] octets) { assert octets != null; assert octets.length == 4; return String.format(Locale.ROOT, "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); } static String octetsToCIDR(int[] octets, int networkMask) { assert octets != null; assert octets.length == 4; return octetsToString(octets) + "/" + networkMask; } public static String createCIDR(long ipAddress, int networkMask) { return octetsToCIDR(longToOctets(ipAddress), networkMask); } }