blob: f685e79a4abd679e1c4feb26a61b5a17fe192c4c [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.
Jonathan Hartdec62d42014-09-22 15:59:04 -070033 */
Jonathan Hartab63aac2014-10-16 08:52:55 -070034public final class IpAddress implements Comparable<IpAddress> {
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070035 // IP Versions
Jonathan Hartdec62d42014-09-22 15:59:04 -070036 public enum Version { INET, INET6 };
37
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070038 // lengths of address, in bytes
39 public static final int INET_BYTE_LENGTH = 4;
40 public static final int INET_BIT_LENGTH = INET_BYTE_LENGTH * Byte.SIZE;
41 public static final int INET6_BYTE_LENGTH = 16;
42 public static final int INET6_BIT_LENGTH = INET6_BYTE_LENGTH * Byte.SIZE;
Jonathan Hartdec62d42014-09-22 15:59:04 -070043
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070044 private final Version version;
45 private final byte[] octets;
Jonathan Hartdec62d42014-09-22 15:59:04 -070046
47 /**
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070048 * Constructor for given IP address version and address octets.
49 *
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070050 * @param version the IP address version
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070051 * @param value the IP address value stored in network byte order
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070052 * (i.e., the most significant byte first)
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070053 * @throws IllegalArgumentException if the arguments are invalid
Jonathan Hartdec62d42014-09-22 15:59:04 -070054 */
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070055 private IpAddress(Version version, byte[] value) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070056 checkArguments(version, value, 0);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070057 this.version = version;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070058 switch (version) {
59 case INET:
60 this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
61 break;
62 case INET6:
63 this.octets = Arrays.copyOf(value, INET6_BYTE_LENGTH);
64 break;
65 default:
66 // Should not be reached
67 this.octets = null;
68 break;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -070069 }
Jonathan Hartdec62d42014-09-22 15:59:04 -070070 }
71
72 /**
73 * Returns the IP version of this address.
74 *
75 * @return the version
76 */
77 public Version version() {
78 return this.version;
79 }
80
81 /**
82 * Returns the IP address as a byte array.
83 *
84 * @return a byte array
85 */
86 public byte[] toOctets() {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070087 return Arrays.copyOf(octets, octets.length);
Jonathan Hartdec62d42014-09-22 15:59:04 -070088 }
89
90 /**
91 * Returns the integral value of this IP address.
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070092 * TODO: This method should be moved to Ip4Address.
Jonathan Hartdec62d42014-09-22 15:59:04 -070093 *
94 * @return the IP address's value as an integer
95 */
96 public int toInt() {
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070097 ByteBuffer bb = ByteBuffer.wrap(octets);
98 return bb.getInt();
Jonathan Hartdec62d42014-09-22 15:59:04 -070099 }
100
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700101 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700102 * Computes the IP address byte length for a given IP version.
103 *
104 * @param version the IP version
105 * @return the IP address byte length for the IP version
106 * @throws IllegalArgumentException if the IP version is invalid
107 */
108 public static int byteLength(Version version) {
109 switch (version) {
110 case INET:
111 return INET_BYTE_LENGTH;
112 case INET6:
113 return INET6_BYTE_LENGTH;
114 default:
115 String msg = "Invalid IP version " + version;
116 throw new IllegalArgumentException(msg);
117 }
118 }
119
120 /**
121 * Converts an integer into an IPv4 address.
122 *
123 * @param value an integer representing an IPv4 address value
124 * @return an IP address
125 */
126 public static IpAddress valueOf(int value) {
127 byte[] bytes =
128 ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
129 return new IpAddress(Version.INET, bytes);
130 }
131
132 /**
133 * Converts a byte array into an IP address.
134 *
135 * @param version the IP address version
136 * @param value the IP address value stored in network byte order
137 * (i.e., the most significant byte first)
138 * @return an IP address
139 * @throws IllegalArgumentException if the arguments are invalid
140 */
141 public static IpAddress valueOf(Version version, byte[] value) {
142 return new IpAddress(version, value);
143 }
144
145 /**
146 * Converts a byte array and a given offset from the beginning of the
147 * array into an IP address.
148 * <p>
149 * The IP address is stored in network byte order (i.e., the most
150 * significant byte first).
151 * </p>
152 * @param version the IP address version
153 * @param value the value to use
154 * @param offset the offset in bytes from the beginning of the byte array
155 * @return an IP address
156 * @throws IllegalArgumentException if the arguments are invalid
157 */
158 public static IpAddress valueOf(Version version, byte[] value,
159 int offset) {
160 checkArguments(version, value, offset);
161 byte[] bc = Arrays.copyOfRange(value, offset, value.length);
162 return IpAddress.valueOf(version, bc);
163 }
164
165 /**
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700166 * Converts an InetAddress into an IP address.
167 *
168 * @param inetAddress the InetAddress value to use
169 * @return an IP address
170 * @throws IllegalArgumentException if the argument is invalid
171 */
172 public static IpAddress valueOf(InetAddress inetAddress) {
173 byte[] bytes = inetAddress.getAddress();
174 if (inetAddress instanceof Inet4Address) {
175 return new IpAddress(Version.INET, bytes);
176 }
177 if (inetAddress instanceof Inet6Address) {
178 return new IpAddress(Version.INET6, bytes);
179 }
180 // Use the number of bytes as a hint
181 if (bytes.length == INET_BYTE_LENGTH) {
182 return new IpAddress(Version.INET, bytes);
183 }
184 if (bytes.length == INET6_BYTE_LENGTH) {
185 return new IpAddress(Version.INET6, bytes);
186 }
187 final String msg = "Unrecognized IP version address string: " +
188 inetAddress.toString();
189 throw new IllegalArgumentException(msg);
190 }
191
192 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700193 * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
194 * "1111:2222::8888") into an IP address.
195 *
196 * @param value an IP address value in string form
197 * @return an IP address
198 * @throws IllegalArgumentException if the argument is invalid
199 */
200 public static IpAddress valueOf(String value) {
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700201 InetAddress inetAddress = null;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700202 try {
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700203 inetAddress = InetAddresses.forString(value);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700204 } catch (IllegalArgumentException e) {
205 final String msg = "Invalid IP address string: " + value;
206 throw new IllegalArgumentException(msg);
207 }
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700208 return valueOf(inetAddress);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700209 }
210
211 /**
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700212 * Creates an IP network mask prefix.
213 *
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700214 * @param version the IP address version
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700215 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700216 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700217 * @return a new IP address that contains a mask prefix of the
218 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700219 * @throws IllegalArgumentException if the arguments are invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700220 */
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700221 public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
222 int addrByteLength = byteLength(version);
223 int addrBitLength = addrByteLength * Byte.SIZE;
224
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700225 // Verify the prefix length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700226 if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
227 final String msg = "Invalid IP prefix length: " + prefixLength +
228 ". Must be in the interval [0, " + addrBitLength + "].";
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700229 throw new IllegalArgumentException(msg);
230 }
231
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700232 // Number of bytes and extra bits that should be all 1s
233 int maskBytes = prefixLength / Byte.SIZE;
234 int maskBits = prefixLength % Byte.SIZE;
235 byte[] mask = new byte[addrByteLength];
236
237 // Set the bytes and extra bits to 1s
238 for (int i = 0; i < maskBytes; i++) {
239 mask[i] = (byte) 0xff; // Set mask bytes to 1s
240 }
241 for (int i = maskBytes; i < addrByteLength; i++) {
242 mask[i] = 0; // Set remaining bytes to 0s
243 }
244 if (maskBits > 0) {
245 mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
246 }
247 return new IpAddress(version, mask);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700248 }
249
250 /**
251 * Creates an IP address by masking it with a network mask of given
252 * mask length.
253 *
254 * @param addr the address to mask
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700255 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700256 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700257 * @return a new IP address that is masked with a mask prefix of the
258 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700259 * @throws IllegalArgumentException if the prefix length is invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700260 */
261 public static IpAddress makeMaskedAddress(final IpAddress addr,
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700262 int prefixLength) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700263 IpAddress mask = IpAddress.makeMaskPrefix(addr.version(),
264 prefixLength);
265 byte[] net = new byte[mask.octets.length];
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700266
267 // Mask each byte
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700268 for (int i = 0; i < net.length; i++) {
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700269 net[i] = (byte) (addr.octets[i] & mask.octets[i]);
270 }
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700271 return IpAddress.valueOf(addr.version(), net);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700272 }
273
Jonathan Hartdec62d42014-09-22 15:59:04 -0700274 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700275 public int compareTo(IpAddress o) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700276 // Compare first the version
277 if (this.version != o.version) {
278 return this.version.compareTo(o.version);
279 }
280
281 // Compare the bytes, one-by-one
282 for (int i = 0; i < this.octets.length; i++) {
283 if (this.octets[i] != o.octets[i]) {
284 return UnsignedBytes.compare(this.octets[i], o.octets[i]);
285 }
286 }
287 return 0; // Equal
Jonathan Hartab63aac2014-10-16 08:52:55 -0700288 }
289
290 @Override
Jonathan Hartdec62d42014-09-22 15:59:04 -0700291 public int hashCode() {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700292 return Objects.hash(version, Arrays.hashCode(octets));
Jonathan Hartdec62d42014-09-22 15:59:04 -0700293 }
294
295 @Override
296 public boolean equals(Object obj) {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700297 if (this == obj) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700298 return true;
299 }
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700300 if ((obj == null) || (getClass() != obj.getClass())) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700301 return false;
302 }
303 IpAddress other = (IpAddress) obj;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700304 return (version == other.version) &&
305 Arrays.equals(octets, other.octets);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700306 }
307
308 @Override
309 /*
310 * (non-Javadoc)
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700311 * The string representation of the IP address: "x.x.x.x" for IPv4
312 * addresses, or ':' separated string for IPv6 addresses.
Jonathan Hartdec62d42014-09-22 15:59:04 -0700313 *
314 * @see java.lang.Object#toString()
315 */
316 public String toString() {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700317 InetAddress inetAddr = null;
318 try {
319 inetAddr = InetAddress.getByAddress(octets);
320 } catch (UnknownHostException e) {
321 // Should never happen
322 checkState(false, "Internal error: Ip6Address.toString()");
323 return "[Invalid IP Address]";
Jonathan Hartdec62d42014-09-22 15:59:04 -0700324 }
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700325 return InetAddresses.toAddrString(inetAddr);
326 }
327
328 /**
329 * Gets the IP address name for the IP address version.
330 *
331 * @param version the IP address version
332 * @return the IP address name for the IP address version
333 */
334 private static String addressName(Version version) {
335 switch (version) {
336 case INET:
337 return "IPv4";
338 case INET6:
339 return "IPv6";
340 default:
341 break;
342 }
343 return "UnknownIP(" + version + ")";
344 }
345
346 /**
347 * Checks whether the arguments are valid.
348 *
349 * @param version the IP address version
350 * @param value the IP address value stored in a byte array
351 * @param offset the offset in bytes from the beginning of the byte
352 * array with the address
353 * @throws IllegalArgumentException if any of the arguments is invalid
354 */
355 private static void checkArguments(Version version, byte[] value,
356 int offset) {
357 // Check the offset and byte array length
358 int addrByteLength = byteLength(version);
359 if ((offset < 0) || (offset + addrByteLength > value.length)) {
360 String msg;
361 if (value.length < addrByteLength) {
362 msg = "Invalid " + addressName(version) +
363 " address array: array length: " + value.length +
364 ". Must be at least " + addrByteLength;
365 } else {
366 msg = "Invalid " + addressName(version) +
367 " address array: array offset: " + offset +
368 ". Must be in the interval [0, " +
369 (value.length - addrByteLength) + "]";
370 }
371 throw new IllegalArgumentException(msg);
372 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700373 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700374}