2 * Copyright (c) 2015 CableLabs and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.packetcable.provider;
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static com.google.common.base.Preconditions.checkNotNull;
14 import java.math.BigInteger;
15 import java.net.Inet4Address;
16 import java.net.InetAddress;
17 import java.net.UnknownHostException;
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
20 import org.apache.commons.lang3.builder.HashCodeBuilder;
23 * @author c3oe.de, based on snippets from Scott Plante, John Kugelmann
27 /** Minimum length of a v4 or v6 subnet mask */
28 private static final int MIN_MASK_BITS = 0;
30 /** Maximum length of a v4 subnet mask */
31 private static final int MAX_MASK_BITS_V4 = 32;
33 /** Maximum length of a v6 subnet mask */
34 private static final int MAX_MASK_BITS_V6 = 128;
36 /** The length of the subnet prefix */
37 private final int prefixLen;
39 /** The subnet mask */
40 private final BigInteger mask;
42 /** The actual routing prefix leading to this subnet */
43 private final BigInteger routingPrefix;
45 /** The number of bytes in an address. Will be 4 or 16 corresponding to ipv4 and ipv6 respectively */
46 private final int addressByteCount;
49 * Generates a Subnet from CIDR style notation. <br>
50 * Eg. "192.168.0.0/24" or "2001:db8:85a3:880:0:0:0:0/57"
51 * @param subnetAddress An address in this subnet or the routing prefix leading to this subnet.
52 * @param prefixLength The number of prefix bits that are set, must be between [0..32] for ipv4 and [0..128] for ipv6
53 * @throws NullPointerException if the subnetAddress is null
54 * @throws IllegalArgumentException if the bits argument is not in the allowable range.
55 * @see <a href="http://wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation">http://wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation</a>
57 public Subnet(@Nonnull final InetAddress subnetAddress, final int prefixLength )
59 checkNotNull(subnetAddress, "subnetAddress can not be null");
60 final int maxMaskBits = (subnetAddress instanceof Inet4Address ? MAX_MASK_BITS_V4 : MAX_MASK_BITS_V6);
61 checkArgument(prefixLength >= MIN_MASK_BITS && prefixLength <= maxMaskBits, "The prefixLength must be in range [%s..%s] but was %s", MIN_MASK_BITS, maxMaskBits, prefixLength);
63 this.prefixLen = prefixLength;
64 this.addressByteCount = subnetAddress.getAddress().length; // 4 or 16
65 this.mask = BigInteger.valueOf( -1 ).shiftLeft(this.addressByteCount * 8 - prefixLength);
67 // ensure subnetAddress is properly masked and is not an address in the subnet
68 this.routingPrefix = new BigInteger( subnetAddress.getAddress() ).and( this.mask);
72 * Generates a subnet from an address and subnet mask. The old ipv4 way.
73 * Eg: "192.168.0.0/255.255.255.0" or single address with no subnet
74 * @param subnetAddress An address in the subnet or the routing prefix leading to this subnet.
75 * @param subnetMask The subnet mask or null. If null the subnet will be a host identifier.
77 public Subnet(@Nonnull final InetAddress subnetAddress, @Nullable final InetAddress subnetMask )
79 this(subnetAddress, maskToPrefixLen(subnetAddress, subnetMask));
83 * Helper method that computes the prefix length an address and subnet mask pair.
84 * @param subnetAddress An address in the subnet
85 * @param subnetMask The mask or null. Expected to be CIDR compliant (mask is a continuous prefix).
86 * If null this will return the length of a host identifier.
87 * @return The prefix length
88 * @throws NullPointerException if subnetAddress is null
89 * @throws IllegalArgumentException if subnetMask is not null and
90 * either (subnetAddress and subnetMask are not the same type)
91 * or (if subnetMask is not CIDR compliant).
93 private static int maskToPrefixLen(@Nonnull final InetAddress subnetAddress, @Nullable final InetAddress subnetMask)
95 checkNotNull(subnetAddress, "subnetAddress can not be null");
96 if (subnetMask == null) {
97 return (subnetAddress instanceof Inet4Address ? MAX_MASK_BITS_V4 : MAX_MASK_BITS_V6);
100 // address and mask must both be ipv4 or ipv6
101 checkArgument(subnetAddress.getClass().equals(subnetMask.getClass()));
103 // validate subnet mask. All leading bits should be set
104 final BigInteger m = new BigInteger(subnetMask.getAddress());
105 final BigInteger allOnes = BigInteger.valueOf(-1);
106 final int rightMostBit = m.getLowestSetBit();
107 final BigInteger validSubnet = allOnes.shiftLeft(rightMostBit);
109 checkArgument(validSubnet.equals(m), "Subnet should have contiguous prefix bits, mask: %s(%s)", subnetMask);
111 return (subnetAddress instanceof Inet4Address
112 ? MAX_MASK_BITS_V4 - rightMostBit
113 : MAX_MASK_BITS_V6 - rightMostBit);
118 * Subnet factory method.
119 * @param addressAndSubnetStr format: "192.168.0.0/24" or "192.168.0.0/255.255.255.0"
120 * or single address or "2001:db8:85a3:880:0:0:0:0/57"
121 * @return a new instance
122 * @throws UnknownHostException thrown if unsupported subnet mask.
124 public static Subnet createInstance(@Nonnull final String addressAndSubnetStr )
125 throws UnknownHostException
127 final String[] stringArr = addressAndSubnetStr.split("/");
128 if ( 2 > stringArr.length ) {
129 return new Subnet(InetAddress.getByName(stringArr[0]), null);
131 else if ( stringArr[ 1 ].contains(".") || stringArr[ 1 ].contains(":") ) {
132 return new Subnet(InetAddress.getByName(stringArr[0]), InetAddress.getByName(stringArr[1]));
135 return new Subnet(InetAddress.getByName(stringArr[0]), Integer.parseInt(stringArr[1]));
140 * Returns the length of the routing prefix length.
141 * @return the routing prefix length.
143 public int getPrefixLen() {
148 * Determins if the passed in address is contained in this subnet.
149 * @param address The address to test.
150 * @return true if the address is in this subnet.
152 public boolean isInNet(@Nonnull final InetAddress address )
154 checkNotNull(address, "address must not be null");
156 final byte[] bytesAddress = address.getAddress();
157 if ( this.addressByteCount != bytesAddress.length ) {
160 final BigInteger bigAddress = new BigInteger( bytesAddress );
161 return bigAddress.and(this.mask).equals(this.routingPrefix);
165 public final boolean equals( Object obj )
167 if (null == obj) return false;
168 if (this == obj) return true;
169 if (!(obj instanceof Subnet)) return false;
171 final Subnet other = (Subnet)obj;
172 return this.prefixLen == other.prefixLen &&
173 this.routingPrefix.equals(other.routingPrefix) &&
174 this.mask.equals(other.mask) &&
175 this.addressByteCount == other.addressByteCount;
179 public final int hashCode()
181 return new HashCodeBuilder(997, 311)
184 .append(routingPrefix)
185 .append(addressByteCount)
190 public String toString()
192 final StringBuilder buf = new StringBuilder();
193 bigInteger2IpString( buf, this.routingPrefix, this.addressByteCount);
195 bigInteger2IpString( buf, this.mask, this.addressByteCount);
196 return buf.toString();
199 private static void bigInteger2IpString( final StringBuilder buf, final BigInteger bigInteger, final int displayBytes )
201 final boolean isIPv4 = 4 == displayBytes;
202 byte[] bytes = bigInteger.toByteArray();
203 int diffLen = displayBytes - bytes.length;
204 final byte fillByte = 0 > (int)bytes[ 0 ] ? (byte)0xFF : (byte)0x00;
207 for ( int i = 0; i < displayBytes; i++ )
209 if ( 0 < i && ! isIPv4 && i % 2 == 0 ) {
212 else if ( 0 < i && isIPv4 ) {
215 integer = 0xFF & (i < diffLen ? fillByte : bytes[ i - diffLen ]);
216 if ( ! isIPv4 && 0x10 > integer ) {
219 buf.append( isIPv4 ? integer : Integer.toHexString( integer ) );