blob: 3f5931fe7b01cd7867d05e4f17c1eac4e7844c18 [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.
33 * TODO: Add support for IPv6 as well.
Jonathan Hartdec62d42014-09-22 15:59:04 -070034 */
Jonathan Hartab63aac2014-10-16 08:52:55 -070035public final 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 Radoslavov49e159a2014-10-29 16:22:13 -070056 private 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 /**
83 * Returns the IP address as a byte array.
84 *
85 * @return a byte array
86 */
87 public byte[] toOctets() {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070088 return Arrays.copyOf(octets, octets.length);
Jonathan Hartdec62d42014-09-22 15:59:04 -070089 }
90
91 /**
92 * Returns the integral value of this IP address.
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070093 * TODO: This method should be moved to Ip4Address.
Jonathan Hartdec62d42014-09-22 15:59:04 -070094 *
95 * @return the IP address's value as an integer
96 */
97 public int toInt() {
Pavlin Radoslavov52307e62014-10-29 15:07:37 -070098 ByteBuffer bb = ByteBuffer.wrap(octets);
99 return bb.getInt();
Jonathan Hartdec62d42014-09-22 15:59:04 -0700100 }
101
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700102 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700103 * Computes the IP address byte length for a given IP version.
104 *
105 * @param version the IP version
106 * @return the IP address byte length for the IP version
107 * @throws IllegalArgumentException if the IP version is invalid
108 */
109 public static int byteLength(Version version) {
110 switch (version) {
111 case INET:
112 return INET_BYTE_LENGTH;
113 case INET6:
114 return INET6_BYTE_LENGTH;
115 default:
116 String msg = "Invalid IP version " + version;
117 throw new IllegalArgumentException(msg);
118 }
119 }
120
121 /**
122 * Converts an integer into an IPv4 address.
123 *
124 * @param value an integer representing an IPv4 address value
125 * @return an IP address
126 */
127 public static IpAddress valueOf(int value) {
128 byte[] bytes =
129 ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
130 return new IpAddress(Version.INET, bytes);
131 }
132
133 /**
134 * Converts a byte array into an IP address.
135 *
136 * @param version the IP address version
137 * @param value the IP address value stored in network byte order
138 * (i.e., the most significant byte first)
139 * @return an IP address
140 * @throws IllegalArgumentException if the arguments are invalid
141 */
142 public static IpAddress valueOf(Version version, byte[] value) {
143 return new IpAddress(version, value);
144 }
145
146 /**
147 * Converts a byte array and a given offset from the beginning of the
148 * array into an IP address.
149 * <p>
150 * The IP address is stored in network byte order (i.e., the most
151 * significant byte first).
152 * </p>
153 * @param version the IP address version
154 * @param value the value to use
155 * @param offset the offset in bytes from the beginning of the byte array
156 * @return an IP address
157 * @throws IllegalArgumentException if the arguments are invalid
158 */
159 public static IpAddress valueOf(Version version, byte[] value,
160 int offset) {
161 checkArguments(version, value, offset);
162 byte[] bc = Arrays.copyOfRange(value, offset, value.length);
163 return IpAddress.valueOf(version, bc);
164 }
165
166 /**
167 * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
168 * "1111:2222::8888") into an IP address.
169 *
170 * @param value an IP address value in string form
171 * @return an IP address
172 * @throws IllegalArgumentException if the argument is invalid
173 */
174 public static IpAddress valueOf(String value) {
175 InetAddress addr = null;
176 try {
177 addr = InetAddresses.forString(value);
178 } catch (IllegalArgumentException e) {
179 final String msg = "Invalid IP address string: " + value;
180 throw new IllegalArgumentException(msg);
181 }
182 byte[] bytes = addr.getAddress();
183 if (addr instanceof Inet4Address) {
184 return new IpAddress(Version.INET, bytes);
185 }
186 if (addr instanceof Inet6Address) {
187 return new IpAddress(Version.INET6, bytes);
188 }
189 final String msg = "Unrecognized IP version address string: " + value;
190 throw new IllegalArgumentException(msg);
191 }
192
193 /**
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700194 * Creates an IP network mask prefix.
195 *
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700196 * @param version the IP address version
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700197 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700198 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700199 * @return a new IP address that contains a mask prefix of the
200 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700201 * @throws IllegalArgumentException if the arguments are invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700202 */
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700203 public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
204 int addrByteLength = byteLength(version);
205 int addrBitLength = addrByteLength * Byte.SIZE;
206
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700207 // Verify the prefix length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700208 if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
209 final String msg = "Invalid IP prefix length: " + prefixLength +
210 ". Must be in the interval [0, " + addrBitLength + "].";
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700211 throw new IllegalArgumentException(msg);
212 }
213
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700214 // Number of bytes and extra bits that should be all 1s
215 int maskBytes = prefixLength / Byte.SIZE;
216 int maskBits = prefixLength % Byte.SIZE;
217 byte[] mask = new byte[addrByteLength];
218
219 // Set the bytes and extra bits to 1s
220 for (int i = 0; i < maskBytes; i++) {
221 mask[i] = (byte) 0xff; // Set mask bytes to 1s
222 }
223 for (int i = maskBytes; i < addrByteLength; i++) {
224 mask[i] = 0; // Set remaining bytes to 0s
225 }
226 if (maskBits > 0) {
227 mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
228 }
229 return new IpAddress(version, mask);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700230 }
231
232 /**
233 * Creates an IP address by masking it with a network mask of given
234 * mask length.
235 *
236 * @param addr the address to mask
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700237 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700238 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700239 * @return a new IP address that is masked with a mask prefix of the
240 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700241 * @throws IllegalArgumentException if the prefix length is invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700242 */
243 public static IpAddress makeMaskedAddress(final IpAddress addr,
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700244 int prefixLength) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700245 IpAddress mask = IpAddress.makeMaskPrefix(addr.version(),
246 prefixLength);
247 byte[] net = new byte[mask.octets.length];
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700248
249 // Mask each byte
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700250 for (int i = 0; i < net.length; i++) {
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700251 net[i] = (byte) (addr.octets[i] & mask.octets[i]);
252 }
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700253 return IpAddress.valueOf(addr.version(), net);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700254 }
255
Jonathan Hartdec62d42014-09-22 15:59:04 -0700256 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700257 public int compareTo(IpAddress o) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700258 // Compare first the version
259 if (this.version != o.version) {
260 return this.version.compareTo(o.version);
261 }
262
263 // Compare the bytes, one-by-one
264 for (int i = 0; i < this.octets.length; i++) {
265 if (this.octets[i] != o.octets[i]) {
266 return UnsignedBytes.compare(this.octets[i], o.octets[i]);
267 }
268 }
269 return 0; // Equal
Jonathan Hartab63aac2014-10-16 08:52:55 -0700270 }
271
272 @Override
Jonathan Hartdec62d42014-09-22 15:59:04 -0700273 public int hashCode() {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700274 return Objects.hash(version, Arrays.hashCode(octets));
Jonathan Hartdec62d42014-09-22 15:59:04 -0700275 }
276
277 @Override
278 public boolean equals(Object obj) {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700279 if (this == obj) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700280 return true;
281 }
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700282 if ((obj == null) || (getClass() != obj.getClass())) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700283 return false;
284 }
285 IpAddress other = (IpAddress) obj;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700286 return (version == other.version) &&
287 Arrays.equals(octets, other.octets);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700288 }
289
290 @Override
291 /*
292 * (non-Javadoc)
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700293 * The string representation of the IP address: "x.x.x.x" for IPv4
294 * addresses, or ':' separated string for IPv6 addresses.
Jonathan Hartdec62d42014-09-22 15:59:04 -0700295 *
296 * @see java.lang.Object#toString()
297 */
298 public String toString() {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700299 InetAddress inetAddr = null;
300 try {
301 inetAddr = InetAddress.getByAddress(octets);
302 } catch (UnknownHostException e) {
303 // Should never happen
304 checkState(false, "Internal error: Ip6Address.toString()");
305 return "[Invalid IP Address]";
Jonathan Hartdec62d42014-09-22 15:59:04 -0700306 }
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700307 return InetAddresses.toAddrString(inetAddr);
308 }
309
310 /**
311 * Gets the IP address name for the IP address version.
312 *
313 * @param version the IP address version
314 * @return the IP address name for the IP address version
315 */
316 private static String addressName(Version version) {
317 switch (version) {
318 case INET:
319 return "IPv4";
320 case INET6:
321 return "IPv6";
322 default:
323 break;
324 }
325 return "UnknownIP(" + version + ")";
326 }
327
328 /**
329 * Checks whether the arguments are valid.
330 *
331 * @param version the IP address version
332 * @param value the IP address value stored in a byte array
333 * @param offset the offset in bytes from the beginning of the byte
334 * array with the address
335 * @throws IllegalArgumentException if any of the arguments is invalid
336 */
337 private static void checkArguments(Version version, byte[] value,
338 int offset) {
339 // Check the offset and byte array length
340 int addrByteLength = byteLength(version);
341 if ((offset < 0) || (offset + addrByteLength > value.length)) {
342 String msg;
343 if (value.length < addrByteLength) {
344 msg = "Invalid " + addressName(version) +
345 " address array: array length: " + value.length +
346 ". Must be at least " + addrByteLength;
347 } else {
348 msg = "Invalid " + addressName(version) +
349 " address array: array offset: " + offset +
350 ". Must be in the interval [0, " +
351 (value.length - addrByteLength) + "]";
352 }
353 throw new IllegalArgumentException(msg);
354 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700355 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700356}