/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.brooklyn.util.net; import java.io.Serializable; import java.net.InetAddress; import java.util.Arrays; import java.util.List; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.math.BitList; import org.apache.brooklyn.util.math.BitUtils; import org.apache.brooklyn.util.text.Strings; import com.google.common.collect.ImmutableList; /** represents a CIDR (classless inter-domain routing) token, i.e. 10.0.0.0/8 or 192.168.4.0/24 */ public class Cidr implements Serializable { private static final long serialVersionUID = -4605909101590811958L; /** 0.0.0.0/0 -- matches all addresses */ public static final Cidr UNIVERSAL = new Cidr(); public static final Cidr _10 = new Cidr(10); public static final Cidr _172_16 = new Cidr("172.16.0.0/12"); public static final Cidr _192_168 = new Cidr(192, 168); public static final Cidr CLASS_A = _10; public static final Cidr CLASS_B = _172_16; public static final Cidr CLASS_C = _192_168; public static final List<Cidr> PRIVATE_NETWORKS_RFC_1918 = ImmutableList.<Cidr>of(_192_168, _172_16, _10); public static final Cidr _169_254 = new Cidr("169.254.0.0/16"); public static final Cidr LINK_LOCAL = _169_254; public static final Cidr _127 = new Cidr("127.0.0.0/8"); public static final Cidr LOOPBACK = _127; public static final List<Cidr> NON_PUBLIC_CIDRS = ImmutableList.<Cidr>builder().addAll(PRIVATE_NETWORKS_RFC_1918).add(LINK_LOCAL).add(LOOPBACK).build(); final int[] subnetBytes = new int[] { 0, 0, 0, 0 }; final int length; public Cidr(String cidr) { if (Strings.isBlank(cidr)) // useful e.g. if user leaves it blank in gui cidr = "0.0.0.0/0"; int slash = cidr.indexOf('/'); if (slash==-1) throw new IllegalArgumentException("CIDR should be of form 192.168.0.0/16 (missing slash); input="+cidr); String subnet = cidr.substring(0, slash); String lengthS = cidr.substring(slash+1); this.length = Integer.parseInt(lengthS); String[] bytes = subnet.split("\\."); int i=0; for (; i<this.length/8; i++) subnetBytes[i] = Integer.parseInt(bytes[i]); for (; i<(this.length+7)/8; i++) // for fractional parts: reverse significance, trim, reverse back subnetBytes[i] = BitUtils.unsigned( BitUtils.reverseBitSignificanceInByte( BitList.newInstanceFromBytes(BitUtils.reverseBitSignificanceInByte(Integer.parseInt(bytes[i]))). resized(this.length % 8).intValue() )); } /** returns true iff this CIDR is well-formed and canonical, * i.e. 4 dot-separated bytes followed by a slash and a length, * where length is <= 32, and the preceding 4 bytes don't include any 1 bits beyond the indicated length; * e.g. 10.0.0.0/8 -- but not 10.0.0.1/8 or 10.../8 * (although the latter ones are accepted by the constructor and converted to the canonical CIDR) */ public static boolean isCanonical(String cidr) { try { return cidr.equals(new Cidr(cidr).toString()); } catch (Throwable e) { Exceptions.propagateIfFatal(e); return false; } } /** allows creation as Cidr(192, 168) for 192.168.0.0/16; * zero bits or ints included are significant, i.e. Cidr(10, 0) gives 10.0.0.0/16 */ public Cidr(int ...unsignedBytes) { length = unsignedBytes.length*8; System.arraycopy(unsignedBytes, 0, subnetBytes, 0, unsignedBytes.length); } public Cidr(int[] subnetBytes, int length) { this.length = length; if (subnetBytes.length>4) throw new IllegalArgumentException("Cannot create CIDR beyond 4 bytes: "+Arrays.toString(subnetBytes)); if (length>32) throw new IllegalArgumentException("Cannot create CIDR beyond 4 bytes: length "+length); // reverse the bits to remove zeroed bits, then reverse back byte[] significantSubnetBytes = BitList.newInstance(BitUtils.reverseBitSignificanceInBytes(subnetBytes)).resized(length).asBytes(); for (int i=0; i<significantSubnetBytes.length; i++) this.subnetBytes[i] = BitUtils.unsigned(BitUtils.reverseBitSignificance(significantSubnetBytes[i])); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + length; result = prime * result + Arrays.hashCode(subnetBytes); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Cidr other = (Cidr) obj; if (length != other.length) return false; if (!Arrays.equals(subnetBytes, other.subnetBytes)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); int i=0; while (i<(length+7)/8) { if (sb.length()>0) sb.append("."); sb.append(""+subnetBytes[i]); i++; } while (i<4) { if (sb.length()>0) sb.append("."); sb.append("0"); i++; } sb.append("/"); sb.append(""+length); return sb.toString(); } public int[] getBytes() { return Arrays.copyOf(subnetBytes, 4); } public int getLength() { return length; } public Cidr subnet(int ...extraUnsignedBytes) { if ((length%8)!=0) throw new IllegalStateException("subnet can only be used for byte boundary subnetted CIDR's; not "+this); int[] newBytes = getBytes(); int newLen = this.length + extraUnsignedBytes.length*8; if (newLen>32) throw new IllegalStateException("further subnet for "+Arrays.toString(extraUnsignedBytes)+" not possible on CIDR "+this); for (int i=0; i<extraUnsignedBytes.length; i++) { newBytes[this.length/8 + i] = extraUnsignedBytes[i]; } return new Cidr(newBytes, newLen); } /** returns the netmask for this cidr; e.g. for a /24 cidr returns 255.255.255.0 */ public InetAddress netmask() { final byte[] netmaskBytes = new byte[] { 0, 0, 0, 0 }; int lengthLeft = length; int i=0; while (lengthLeft>0) { if (lengthLeft>=8) { netmaskBytes[i] = (byte)255; } else { netmaskBytes[i] = (byte) ( (1 << lengthLeft) - 1 ); } lengthLeft -= 8; i++; } return Networking.getInetAddressWithFixedName(netmaskBytes); } /** taking the addresses in the CIDR in order, returns the one in the offset^th position * (starting with the CIDR itself, even if final bits are 0) */ public InetAddress addressAtOffset(int offset) { int[] ints = getBytes(); ints[3] += offset; { int i=3; while (ints[i]>=256) { ints[i-1] += (ints[i] / 256); ints[i] %= 256; i--; } } byte[] bytes = new byte[] { 0, 0, 0, 0 }; for (int i=0; i<4; i++) bytes[i] = (byte) ints[i]; return Networking.getInetAddressWithFixedName(bytes); } /** returns length of the prefix in common between the two cidrs */ public int commonPrefixLength(Cidr other) { return asBitList().commonPrefixLength(other.asBitList()); } public Cidr commonPrefix(Cidr other) { return new Cidr(other.getBytes(), commonPrefixLength(other)); } /** returns list of bits for the significant (length) bits of this CIDR */ public BitList asBitList() { return BitList.newInstance(BitUtils.reverseBitSignificanceInBytes(getBytes())).resized(getLength()); } public boolean contains(Cidr target) { return commonPrefixLength(target) == getLength(); } // FIXME remove from here, promote NetworkUtils /** @deprecated use {@link Networking#getInetAddressWithFixedName(byte[])} */ @Deprecated public static InetAddress getInetAddressWithFixedName(byte[] ip) { return Networking.getInetAddressWithFixedName(ip); } }