blob: 489beda4e29deb29d5089c4bf559c5931145f1ad [file] [log] [blame]
Jonathan Hart8f6dc092014-04-18 15:56:43 -07001package net.onrc.onos.apps.sdnip;
pingping-lina2cbfad2013-03-07 08:39:21 +08002
Jonathan Hart850dddc2014-08-28 09:54:10 -07003import java.math.BigInteger;
pingping-lina2cbfad2013-03-07 08:39:21 +08004import java.net.InetAddress;
5import java.net.UnknownHostException;
Jonathan Hart850dddc2014-08-28 09:54:10 -07006import java.nio.ByteBuffer;
Jonathan Hart32e18222013-08-07 22:05:42 +12007import java.util.Arrays;
pingping-lina2cbfad2013-03-07 08:39:21 +08008
Jonathan Hartabf10222013-08-13 10:19:34 +12009import com.google.common.net.InetAddresses;
10
Jonathan Hart31e15f12014-04-10 10:33:00 -070011/**
12 * Represents an IP prefix.
13 * <p/>
14 * It is made up of an IP address and a number of significant bits in the
15 * prefix (i.e. the size of the network part of the address).
16 * E.g. {@code 192.168.0.0/16}.
17 * <p/>
18 * Currently only IPv4 is supported, so a prefix length can be up to 32 bits.
19 */
pingping-lina2cbfad2013-03-07 08:39:21 +080020public class Prefix {
Jonathan Hart31e15f12014-04-10 10:33:00 -070021 /**
22 * The length of addresses this class can represent prefixes of, in bytes.
23 */
Jonathan Hartf6978ce2014-06-23 11:20:04 -070024 public static final int ADDRESS_LENGTH_BYTES = 4;
25
26 /**
27 * The length of addresses this class can represent prefixes of, in bits.
28 */
29 public static final int MAX_PREFIX_LENGTH = Byte.SIZE * ADDRESS_LENGTH_BYTES;
30
31 public static final int MIN_PREFIX_LENGTH = 0;
pingping-lina2cbfad2013-03-07 08:39:21 +080032
Jonathan Hart850dddc2014-08-28 09:54:10 -070033 private static final int BINARY_RADIX = 2;
34
Ray Milkey269ffb92014-04-03 14:43:30 -070035 private final int prefixLength;
36 private final byte[] address;
Jonathan Hartf6978ce2014-06-23 11:20:04 -070037 private final String binaryString;
Jonathan Hart61ba9372013-05-19 20:10:29 -070038
Jonathan Hart31e15f12014-04-10 10:33:00 -070039 // For verifying the arguments and pretty printing
Ray Milkey269ffb92014-04-03 14:43:30 -070040 private final InetAddress inetAddress;
Jonathan Hart32e18222013-08-07 22:05:42 +120041
Jonathan Hart31e15f12014-04-10 10:33:00 -070042 /**
43 * Class constructor, taking an byte array representing and IP address and
44 * a prefix length.
45 * <p/>
46 * The valid values for addr and prefixLength are bounded by
Jonathan Hartf6978ce2014-06-23 11:20:04 -070047 * {@link #ADDRESS_LENGTH_BYTES}.
Jonathan Hart31e15f12014-04-10 10:33:00 -070048 * <p/>
49 * A valid addr array satisfies
Jonathan Hartf6978ce2014-06-23 11:20:04 -070050 * {@code addr.length == }{@value #ADDRESS_LENGTH_BYTES}.
Jonathan Hart31e15f12014-04-10 10:33:00 -070051 * <p/>
52 * A valid prefixLength satisfies
Jonathan Hartf6978ce2014-06-23 11:20:04 -070053 * {@code (prefixLength >= 0 && prefixLength <=} {@link Byte#SIZE}
54 * {@code * }{@value #ADDRESS_LENGTH_BYTES}{@code )}.
Jonathan Hart31e15f12014-04-10 10:33:00 -070055 *
56 * @param addr a byte array representing the address
57 * @param prefixLength length of the prefix of the specified address
58 */
Ray Milkey269ffb92014-04-03 14:43:30 -070059 public Prefix(byte[] addr, int prefixLength) {
Jonathan Hartf6978ce2014-06-23 11:20:04 -070060 if (addr == null || addr.length != ADDRESS_LENGTH_BYTES ||
61 prefixLength < MIN_PREFIX_LENGTH ||
62 prefixLength > MAX_PREFIX_LENGTH) {
Ray Milkey269ffb92014-04-03 14:43:30 -070063 throw new IllegalArgumentException();
64 }
Jonathan Hart61ba9372013-05-19 20:10:29 -070065
Ray Milkey269ffb92014-04-03 14:43:30 -070066 address = canonicalizeAddress(addr, prefixLength);
67 this.prefixLength = prefixLength;
Jonathan Hartf6978ce2014-06-23 11:20:04 -070068 binaryString = createBinaryString();
Ray Milkey269ffb92014-04-03 14:43:30 -070069
70 try {
71 inetAddress = InetAddress.getByAddress(address);
72 } catch (UnknownHostException e) {
Jonathan Hart738980f2014-04-04 10:11:15 -070073 throw new IllegalArgumentException("Couldn't parse IP address", e);
Ray Milkey269ffb92014-04-03 14:43:30 -070074 }
75 }
76
Jonathan Hart31e15f12014-04-10 10:33:00 -070077 /**
78 * Class constructor, taking an address in String format and a prefix
79 * length. The address must be in dot-notation form (e.g. {@code 0.0.0.0}).
80 *
81 * @param strAddress a String representing the address
82 * @param prefixLength length of the prefix of the specified address
83 */
Ray Milkey269ffb92014-04-03 14:43:30 -070084 public Prefix(String strAddress, int prefixLength) {
85 byte[] addr = null;
86 addr = InetAddresses.forString(strAddress).getAddress();
87
Jonathan Hartf6978ce2014-06-23 11:20:04 -070088 if (addr == null || addr.length != ADDRESS_LENGTH_BYTES ||
89 prefixLength < MIN_PREFIX_LENGTH ||
90 prefixLength > MAX_PREFIX_LENGTH) {
Ray Milkey269ffb92014-04-03 14:43:30 -070091 throw new IllegalArgumentException();
92 }
93
94 address = canonicalizeAddress(addr, prefixLength);
95 this.prefixLength = prefixLength;
Jonathan Hartf6978ce2014-06-23 11:20:04 -070096 binaryString = createBinaryString();
Ray Milkey269ffb92014-04-03 14:43:30 -070097
98 try {
99 inetAddress = InetAddress.getByAddress(address);
100 } catch (UnknownHostException e) {
Jonathan Hart738980f2014-04-04 10:11:15 -0700101 throw new IllegalArgumentException("Couldn't parse IP address", e);
Ray Milkey269ffb92014-04-03 14:43:30 -0700102 }
103 }
104
Jonathan Hartf6978ce2014-06-23 11:20:04 -0700105 /**
106 * This method takes a byte array passed in by the user and ensures it
107 * conforms to the format we want. The byte array can contain anything,
108 * but only some of the bits are significant. The bits after
109 * prefixLengthValue are not significant, and this method will zero them
110 * out in order to ensure there is a canonical representation for each
111 * prefix. This simplifies the equals and hashcode methods, because once we
112 * have a canonical byte array representation we can simply compare byte
113 * arrays to test equality.
114 *
115 * @param addressValue the byte array to canonicalize
116 * @param prefixLengthValue The length of the prefix. Bits up to and including
117 * prefixLength are significant, bits following prefixLength are not
118 * significant and will be zeroed out.
119 * @return the canonicalized representation of the prefix
120 */
Ray Milkey5df613b2014-04-15 10:50:56 -0700121 private byte[] canonicalizeAddress(byte[] addressValue,
122 int prefixLengthValue) {
123 byte[] result = new byte[addressValue.length];
Ray Milkey269ffb92014-04-03 14:43:30 -0700124
Ray Milkey5df613b2014-04-15 10:50:56 -0700125 if (prefixLengthValue == 0) {
Jonathan Hartf6978ce2014-06-23 11:20:04 -0700126 for (int i = 0; i < ADDRESS_LENGTH_BYTES; i++) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700127 result[i] = 0;
128 }
129
130 return result;
131 }
132
Ray Milkey5df613b2014-04-15 10:50:56 -0700133 result = Arrays.copyOf(addressValue, addressValue.length);
Ray Milkey269ffb92014-04-03 14:43:30 -0700134
135 //Set all bytes after the end of the prefix to 0
Ray Milkey5df613b2014-04-15 10:50:56 -0700136 int lastByteIndex = (prefixLengthValue - 1) / Byte.SIZE;
Jonathan Hartf6978ce2014-06-23 11:20:04 -0700137 for (int i = lastByteIndex; i < ADDRESS_LENGTH_BYTES; i++) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700138 result[i] = 0;
139 }
140
Ray Milkey5df613b2014-04-15 10:50:56 -0700141 byte lastByte = addressValue[lastByteIndex];
Ray Milkey269ffb92014-04-03 14:43:30 -0700142 byte mask = 0;
143 byte msb = (byte) 0x80;
Ray Milkey5df613b2014-04-15 10:50:56 -0700144 int lastBit = (prefixLengthValue - 1) % Byte.SIZE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700145 for (int i = 0; i < Byte.SIZE; i++) {
146 if (i <= lastBit) {
147 mask |= (msb >> i);
148 }
149 }
150
151 result[lastByteIndex] = (byte) (lastByte & mask);
152
153 return result;
154 }
155
Jonathan Hart31e15f12014-04-10 10:33:00 -0700156 /**
Jonathan Hartf6978ce2014-06-23 11:20:04 -0700157 * Creates the binary string representation of the prefix.
158 * The strings length is equal to prefixLength.
159 *
160 * @return the binary string representation
161 */
162 private String createBinaryString() {
163 if (prefixLength == 0) {
164 return "";
165 }
166
167 StringBuilder result = new StringBuilder(prefixLength);
168 for (int i = 0; i < address.length; i++) {
169 byte b = address[i];
170 for (int j = 0; j < Byte.SIZE; j++) {
171 byte mask = (byte) (0x80 >>> j);
172 result.append(((b & mask) == 0) ? "0" : "1");
173 if (i * Byte.SIZE + j == prefixLength - 1) {
174 return result.toString();
175 }
176 }
177 }
178 return result.toString();
179 }
180
181 /**
Jonathan Hart31e15f12014-04-10 10:33:00 -0700182 * Gets the length of the prefix of the address.
183 *
184 * @return the prefix length
185 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700186 public int getPrefixLength() {
187 return prefixLength;
188 }
189
Jonathan Hart31e15f12014-04-10 10:33:00 -0700190 /**
191 * Gets the address.
192 *
193 * @return the address as a byte array
194 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700195 public byte[] getAddress() {
Jonathan Hartec9ee2e2014-04-08 22:45:44 -0700196 return Arrays.copyOf(address, address.length);
Ray Milkey269ffb92014-04-03 14:43:30 -0700197 }
198
pingping-lin1ada7ce2014-08-14 13:45:22 -0700199 /**
200 * Gets the InetAddress.
201 *
202 * @return the inetAddress
203 */
204 public InetAddress getInetAddress() {
205 return inetAddress;
206 }
207
208
Ray Milkey269ffb92014-04-03 14:43:30 -0700209 @Override
210 public boolean equals(Object other) {
Jonathan Hart738980f2014-04-04 10:11:15 -0700211 if (!(other instanceof Prefix)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700212 return false;
213 }
214
215 Prefix otherPrefix = (Prefix) other;
216
217 return (Arrays.equals(address, otherPrefix.address)) &&
218 (prefixLength == otherPrefix.prefixLength);
219 }
220
221 @Override
222 public int hashCode() {
223 int hash = 17;
224 hash = 31 * hash + prefixLength;
225 hash = 31 * hash + Arrays.hashCode(address);
226 return hash;
227 }
228
229 @Override
230 public String toString() {
231 return inetAddress.getHostAddress() + "/" + prefixLength;
232 }
233
Jonathan Hart31e15f12014-04-10 10:33:00 -0700234 /**
Jonathan Hartf6978ce2014-06-23 11:20:04 -0700235 * Gets a binary string representation of the prefix.
236 *
237 * @return a binary string representation
238 */
239 public String toBinaryString() {
240 return binaryString;
241 }
242
243 /**
Jonathan Hart850dddc2014-08-28 09:54:10 -0700244 * Creates a Prefix object from a binary string.
245 * <p/>
246 * This is the reverse operation of {@link Prefix#toBinaryString()}. It
247 * takes a binary string (a String containing only '0' and '1'
248 * characters) and parses it to create a corresponding Prefix object.
Jonathan Hart31e15f12014-04-10 10:33:00 -0700249 *
Jonathan Hart850dddc2014-08-28 09:54:10 -0700250 * @param binaryString the binary string to convert to Prefix
251 * @return a Prefix object representing the same prefix as the input string
Jonathan Hart31e15f12014-04-10 10:33:00 -0700252 */
Jonathan Hart850dddc2014-08-28 09:54:10 -0700253 public static Prefix fromBinaryString(String binaryString) {
254 if (binaryString.length() > MAX_PREFIX_LENGTH) {
255 throw new IllegalArgumentException(
256 "Binary string length must not be greater than "
257 + MAX_PREFIX_LENGTH);
Ray Milkey269ffb92014-04-03 14:43:30 -0700258 }
Jonathan Hart850dddc2014-08-28 09:54:10 -0700259
260 for (int i = 0; i < binaryString.length(); i++) {
261 char character = binaryString.charAt(i);
262 if (character != '0' && character != '1') {
263 throw new IllegalArgumentException(
264 "Binary string may only contain the characters "
265 + "\'0\' or \'1\'");
266 }
267 }
268
269 // Pad the binary string out to be MAX_PREFIX_LENGTH bits long
270 StringBuilder paddedBinaryString = new StringBuilder(MAX_PREFIX_LENGTH);
271 paddedBinaryString.append(binaryString);
272 for (int i = binaryString.length(); i < MAX_PREFIX_LENGTH; i++) {
273 paddedBinaryString.append('0');
274 }
275
276 // BigInteger will parse the binary string to an integer using radix 2
277 BigInteger bigInteger =
278 new BigInteger(paddedBinaryString.toString(), BINARY_RADIX);
279
280 // Convert the integer to a byte array
281 ByteBuffer bb = ByteBuffer.allocate(ADDRESS_LENGTH_BYTES);
282 bb.putInt(bigInteger.intValue());
283
284 return new Prefix(bb.array(), binaryString.length());
Ray Milkey269ffb92014-04-03 14:43:30 -0700285 }
pingping-lina2cbfad2013-03-07 08:39:21 +0800286}