blob: a783fa0218b34c1ded12f8b7d7e549b1ffe4b15b [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
Jonathan Hartdec62d42014-09-22 15:59:04 -070016package org.onlab.packet;
17
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070018import java.net.InetAddress;
19import java.net.Inet4Address;
20import java.net.Inet6Address;
21import java.net.UnknownHostException;
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070022import java.nio.ByteBuffer;
Jonathan Hartdec62d42014-09-22 15:59:04 -070023import java.util.Arrays;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070024import java.util.Objects;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070025
26import com.google.common.net.InetAddresses;
27import com.google.common.primitives.UnsignedBytes;
28
29import static com.google.common.base.Preconditions.checkState;
Jonathan Hartdec62d42014-09-22 15:59:04 -070030
31/**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070032 * A class representing an IP address.
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080033 * This class is immutable.
Jonathan Hartdec62d42014-09-22 15:59:04 -070034 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080035public class IpAddress implements Comparable<IpAddress> {
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070036 // IP Versions
Jonathan Hartdec62d42014-09-22 15:59:04 -070037 public enum Version { INET, INET6 };
38
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070039 // lengths of address, in bytes
40 public static final int INET_BYTE_LENGTH = 4;
41 public static final int INET_BIT_LENGTH = INET_BYTE_LENGTH * Byte.SIZE;
42 public static final int INET6_BYTE_LENGTH = 16;
43 public static final int INET6_BIT_LENGTH = INET6_BYTE_LENGTH * Byte.SIZE;
Jonathan Hartdec62d42014-09-22 15:59:04 -070044
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070045 private final Version version;
46 private final byte[] octets;
Jonathan Hartdec62d42014-09-22 15:59:04 -070047
48 /**
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070049 * Constructor for given IP address version and address octets.
50 *
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070051 * @param version the IP address version
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070052 * @param value the IP address value stored in network byte order
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070053 * (i.e., the most significant byte first)
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070054 * @throws IllegalArgumentException if the arguments are invalid
Jonathan Hartdec62d42014-09-22 15:59:04 -070055 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080056 protected IpAddress(Version version, byte[] value) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070057 checkArguments(version, value, 0);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070058 this.version = version;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070059 switch (version) {
60 case INET:
61 this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
62 break;
63 case INET6:
64 this.octets = Arrays.copyOf(value, INET6_BYTE_LENGTH);
65 break;
66 default:
67 // Should not be reached
68 this.octets = null;
69 break;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070070 }
Jonathan Hartdec62d42014-09-22 15:59:04 -070071 }
72
73 /**
74 * Returns the IP version of this address.
75 *
76 * @return the version
77 */
78 public Version version() {
79 return this.version;
80 }
81
82 /**
Pavlin Radoslavov34ffe722015-03-10 12:48:55 -070083 * Tests whether the IP version of this address is IPv4.
84 *
85 * @return true if the IP version of this address is IPv4, otherwise false.
86 */
87 public boolean isIp4() {
88 return (version() == Ip4Address.VERSION);
89 }
90
91 /**
92 * Tests whether the IP version of this address is IPv6.
93 *
94 * @return true if the IP version of this address is IPv6, otherwise false.
95 */
96 public boolean isIp6() {
97 return (version() == Ip6Address.VERSION);
98 }
99
100 /**
Pavlin Radoslavov34c81642014-11-04 16:21:38 -0800101 * Gets the {@link Ip4Address} view of the IP address.
102 *
103 * @return the {@link Ip4Address} view of the IP address if it is IPv4,
104 * otherwise null
105 */
106 public Ip4Address getIp4Address() {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700107 if (!isIp4()) {
Pavlin Radoslavov34c81642014-11-04 16:21:38 -0800108 return null;
109 }
110
111 // Return this object itself if it is already instance of Ip4Address
112 if (this instanceof Ip4Address) {
113 return (Ip4Address) this;
114 }
115 return Ip4Address.valueOf(octets);
116 }
117
118 /**
119 * Gets the {@link Ip6Address} view of the IP address.
120 *
121 * @return the {@link Ip6Address} view of the IP address if it is IPv6,
122 * otherwise null
123 */
124 public Ip6Address getIp6Address() {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700125 if (!isIp6()) {
Pavlin Radoslavov34c81642014-11-04 16:21:38 -0800126 return null;
127 }
128
129 // Return this object itself if it is already instance of Ip6Address
130 if (this instanceof Ip6Address) {
131 return (Ip6Address) this;
132 }
133 return Ip6Address.valueOf(octets);
134 }
135
136 /**
Jonathan Hartdec62d42014-09-22 15:59:04 -0700137 * Returns the IP address as a byte array.
138 *
139 * @return a byte array
140 */
141 public byte[] toOctets() {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700142 return Arrays.copyOf(octets, octets.length);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700143 }
144
145 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700146 * Computes the IP address byte length for a given IP version.
147 *
148 * @param version the IP version
149 * @return the IP address byte length for the IP version
150 * @throws IllegalArgumentException if the IP version is invalid
151 */
152 public static int byteLength(Version version) {
153 switch (version) {
154 case INET:
155 return INET_BYTE_LENGTH;
156 case INET6:
157 return INET6_BYTE_LENGTH;
158 default:
159 String msg = "Invalid IP version " + version;
160 throw new IllegalArgumentException(msg);
161 }
162 }
163
164 /**
165 * Converts an integer into an IPv4 address.
166 *
167 * @param value an integer representing an IPv4 address value
168 * @return an IP address
169 */
170 public static IpAddress valueOf(int value) {
171 byte[] bytes =
172 ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
173 return new IpAddress(Version.INET, bytes);
174 }
175
176 /**
177 * Converts a byte array into an IP address.
178 *
179 * @param version the IP address version
180 * @param value the IP address value stored in network byte order
181 * (i.e., the most significant byte first)
182 * @return an IP address
183 * @throws IllegalArgumentException if the arguments are invalid
184 */
185 public static IpAddress valueOf(Version version, byte[] value) {
186 return new IpAddress(version, value);
187 }
188
189 /**
190 * Converts a byte array and a given offset from the beginning of the
191 * array into an IP address.
192 * <p>
193 * The IP address is stored in network byte order (i.e., the most
194 * significant byte first).
195 * </p>
196 * @param version the IP address version
197 * @param value the value to use
198 * @param offset the offset in bytes from the beginning of the byte array
199 * @return an IP address
200 * @throws IllegalArgumentException if the arguments are invalid
201 */
202 public static IpAddress valueOf(Version version, byte[] value,
203 int offset) {
204 checkArguments(version, value, offset);
205 byte[] bc = Arrays.copyOfRange(value, offset, value.length);
206 return IpAddress.valueOf(version, bc);
207 }
208
209 /**
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700210 * Converts an InetAddress into an IP address.
211 *
212 * @param inetAddress the InetAddress value to use
213 * @return an IP address
214 * @throws IllegalArgumentException if the argument is invalid
215 */
216 public static IpAddress valueOf(InetAddress inetAddress) {
217 byte[] bytes = inetAddress.getAddress();
218 if (inetAddress instanceof Inet4Address) {
219 return new IpAddress(Version.INET, bytes);
220 }
221 if (inetAddress instanceof Inet6Address) {
222 return new IpAddress(Version.INET6, bytes);
223 }
224 // Use the number of bytes as a hint
225 if (bytes.length == INET_BYTE_LENGTH) {
226 return new IpAddress(Version.INET, bytes);
227 }
228 if (bytes.length == INET6_BYTE_LENGTH) {
229 return new IpAddress(Version.INET6, bytes);
230 }
231 final String msg = "Unrecognized IP version address string: " +
232 inetAddress.toString();
233 throw new IllegalArgumentException(msg);
234 }
235
236 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700237 * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
238 * "1111:2222::8888") into an IP address.
239 *
240 * @param value an IP address value in string form
241 * @return an IP address
242 * @throws IllegalArgumentException if the argument is invalid
243 */
244 public static IpAddress valueOf(String value) {
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700245 InetAddress inetAddress = null;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700246 try {
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700247 inetAddress = InetAddresses.forString(value);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700248 } catch (IllegalArgumentException e) {
249 final String msg = "Invalid IP address string: " + value;
250 throw new IllegalArgumentException(msg);
251 }
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700252 return valueOf(inetAddress);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700253 }
254
255 /**
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700256 * Creates an IP network mask prefix.
257 *
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700258 * @param version the IP address version
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700259 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700260 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700261 * @return a new IP address that contains a mask prefix of the
262 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700263 * @throws IllegalArgumentException if the arguments are invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700264 */
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700265 public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800266 byte[] mask = makeMaskPrefixArray(version, prefixLength);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700267 return new IpAddress(version, mask);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700268 }
269
270 /**
271 * Creates an IP address by masking it with a network mask of given
272 * mask length.
273 *
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800274 * @param address the address to mask
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700275 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700276 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700277 * @return a new IP address that is masked with a mask prefix of the
278 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700279 * @throws IllegalArgumentException if the prefix length is invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700280 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800281 public static IpAddress makeMaskedAddress(final IpAddress address,
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700282 int prefixLength) {
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800283 if (address instanceof Ip4Address) {
284 Ip4Address ip4a = (Ip4Address) address;
285 return Ip4Address.makeMaskedAddress(ip4a, prefixLength);
286 } else if (address instanceof Ip6Address) {
287 Ip6Address ip6a = (Ip6Address) address;
288 return Ip6Address.makeMaskedAddress(ip6a, prefixLength);
289 } else {
290 byte[] net = makeMaskedAddressArray(address, prefixLength);
291 return IpAddress.valueOf(address.version(), net);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700292 }
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700293 }
294
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800295 /**
296 * Check if this IP address is zero.
297 *
298 * @return true if this address is zero.
299 */
300 public boolean isZero() {
301 for (byte b : octets) {
302 if (b != 0) {
303 return false;
304 }
305 }
306 return true;
307 }
308
Jonathan Hartdec62d42014-09-22 15:59:04 -0700309 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700310 public int compareTo(IpAddress o) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700311 // Compare first the version
312 if (this.version != o.version) {
313 return this.version.compareTo(o.version);
314 }
315
316 // Compare the bytes, one-by-one
317 for (int i = 0; i < this.octets.length; i++) {
318 if (this.octets[i] != o.octets[i]) {
319 return UnsignedBytes.compare(this.octets[i], o.octets[i]);
320 }
321 }
322 return 0; // Equal
Jonathan Hartab63aac2014-10-16 08:52:55 -0700323 }
324
325 @Override
Jonathan Hartdec62d42014-09-22 15:59:04 -0700326 public int hashCode() {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700327 return Objects.hash(version, Arrays.hashCode(octets));
Jonathan Hartdec62d42014-09-22 15:59:04 -0700328 }
329
330 @Override
331 public boolean equals(Object obj) {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700332 if (this == obj) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700333 return true;
334 }
Pavlin Radoslavov50b70672014-11-05 11:22:25 -0800335 if ((obj == null) || (!(obj instanceof IpAddress))) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700336 return false;
337 }
338 IpAddress other = (IpAddress) obj;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700339 return (version == other.version) &&
340 Arrays.equals(octets, other.octets);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700341 }
342
343 @Override
344 /*
345 * (non-Javadoc)
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700346 * The string representation of the IP address: "x.x.x.x" for IPv4
347 * addresses, or ':' separated string for IPv6 addresses.
Jonathan Hartdec62d42014-09-22 15:59:04 -0700348 *
349 * @see java.lang.Object#toString()
350 */
351 public String toString() {
Brian O'Connor041515f2015-02-19 15:36:17 -0800352 // FIXME InetAddress is super slow
353 switch (version) {
354 case INET:
355 return String.format("%d.%d.%d.%d", octets[0] & 0xff,
356 octets[1] & 0xff,
357 octets[2] & 0xff,
358 octets[3] & 0xff);
359 case INET6:
360 default:
361 InetAddress inetAddr = null;
362 try {
363 inetAddr = InetAddress.getByAddress(octets);
364 } catch (UnknownHostException e) {
365 // Should never happen
366 checkState(false, "Internal error: Ip6Address.toString()");
367 return "[Invalid IP Address]";
368 }
369 return InetAddresses.toAddrString(inetAddr);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700370 }
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700371 }
372
373 /**
374 * Gets the IP address name for the IP address version.
375 *
376 * @param version the IP address version
377 * @return the IP address name for the IP address version
378 */
379 private static String addressName(Version version) {
380 switch (version) {
381 case INET:
382 return "IPv4";
383 case INET6:
384 return "IPv6";
385 default:
386 break;
387 }
388 return "UnknownIP(" + version + ")";
389 }
390
391 /**
392 * Checks whether the arguments are valid.
393 *
394 * @param version the IP address version
395 * @param value the IP address value stored in a byte array
396 * @param offset the offset in bytes from the beginning of the byte
397 * array with the address
398 * @throws IllegalArgumentException if any of the arguments is invalid
399 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800400 static void checkArguments(Version version, byte[] value, int offset) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700401 // Check the offset and byte array length
402 int addrByteLength = byteLength(version);
403 if ((offset < 0) || (offset + addrByteLength > value.length)) {
404 String msg;
405 if (value.length < addrByteLength) {
406 msg = "Invalid " + addressName(version) +
407 " address array: array length: " + value.length +
408 ". Must be at least " + addrByteLength;
409 } else {
410 msg = "Invalid " + addressName(version) +
411 " address array: array offset: " + offset +
412 ". Must be in the interval [0, " +
413 (value.length - addrByteLength) + "]";
414 }
415 throw new IllegalArgumentException(msg);
416 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700417 }
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800418
419 /**
420 * Creates a byte array for IP network mask prefix.
421 *
422 * @param version the IP address version
423 * @param prefixLength the length of the mask prefix. Must be in the
424 * interval [0, 32] for IPv4, or [0, 128] for IPv6
425 * @return a byte array that contains a mask prefix of the
426 * specified length
427 * @throws IllegalArgumentException if the arguments are invalid
428 */
429 static byte[] makeMaskPrefixArray(Version version, int prefixLength) {
430 int addrByteLength = byteLength(version);
431 int addrBitLength = addrByteLength * Byte.SIZE;
432
433 // Verify the prefix length
434 if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
435 final String msg = "Invalid IP prefix length: " + prefixLength +
436 ". Must be in the interval [0, " + addrBitLength + "].";
437 throw new IllegalArgumentException(msg);
438 }
439
440 // Number of bytes and extra bits that should be all 1s
441 int maskBytes = prefixLength / Byte.SIZE;
442 int maskBits = prefixLength % Byte.SIZE;
443 byte[] mask = new byte[addrByteLength];
444
445 // Set the bytes and extra bits to 1s
446 for (int i = 0; i < maskBytes; i++) {
447 mask[i] = (byte) 0xff; // Set mask bytes to 1s
448 }
449 for (int i = maskBytes; i < addrByteLength; i++) {
450 mask[i] = 0; // Set remaining bytes to 0s
451 }
452 if (maskBits > 0) {
453 mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
454 }
455 return mask;
456 }
457
458 /**
459 * Creates a byte array that represents an IP address masked with
460 * a network mask of given mask length.
461 *
462 * @param addr the address to mask
463 * @param prefixLength the length of the mask prefix. Must be in the
464 * interval [0, 32] for IPv4, or [0, 128] for IPv6
465 * @return a byte array that represents the IP address masked with
466 * a mask prefix of the specified length
467 * @throws IllegalArgumentException if the prefix length is invalid
468 */
469 static byte[] makeMaskedAddressArray(final IpAddress addr,
470 int prefixLength) {
471 byte[] mask = IpAddress.makeMaskPrefixArray(addr.version(),
472 prefixLength);
473 byte[] net = new byte[mask.length];
474
475 // Mask each byte
476 for (int i = 0; i < net.length; i++) {
477 net[i] = (byte) (addr.octets[i] & mask[i]);
478 }
479 return net;
480 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700481}