blob: bf0140658eb9f1be22ef05caf02d39941e69389b [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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;
Yuta HIGUCHIc012dda2016-08-17 00:43:46 -070019import java.net.UnknownHostException;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -070020import java.net.Inet4Address;
21import java.net.Inet6Address;
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 -070025import com.google.common.net.InetAddresses;
26import com.google.common.primitives.UnsignedBytes;
27
Jonathan Hartdec62d42014-09-22 15:59:04 -070028
29/**
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -070030 * A class representing an IP address.
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080031 * This class is immutable.
Jonathan Hartdec62d42014-09-22 15:59:04 -070032 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -080033public class IpAddress implements Comparable<IpAddress> {
Aaron Kruglikovafdf4de2015-06-24 09:28:22 -070034 private static final int BIT_MASK = 0x000000ff;
35
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 /**
Yuta HIGUCHIc012dda2016-08-17 00:43:46 -0700146 * Returns the IP address as InetAddress.
147 *
148 * @return InetAddress
149 */
150 public InetAddress toInetAddress() {
151 try {
152 return InetAddress.getByAddress(octets);
153 } catch (UnknownHostException e) {
154 // Should never reach here
155 return null;
156 }
157 }
158
159 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700160 * Computes the IP address byte length for a given IP version.
161 *
162 * @param version the IP version
163 * @return the IP address byte length for the IP version
164 * @throws IllegalArgumentException if the IP version is invalid
165 */
166 public static int byteLength(Version version) {
167 switch (version) {
168 case INET:
169 return INET_BYTE_LENGTH;
170 case INET6:
171 return INET6_BYTE_LENGTH;
172 default:
173 String msg = "Invalid IP version " + version;
174 throw new IllegalArgumentException(msg);
175 }
176 }
177
178 /**
179 * Converts an integer into an IPv4 address.
180 *
181 * @param value an integer representing an IPv4 address value
182 * @return an IP address
183 */
184 public static IpAddress valueOf(int value) {
185 byte[] bytes =
186 ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
187 return new IpAddress(Version.INET, bytes);
188 }
189
190 /**
191 * Converts a byte array into an IP address.
192 *
193 * @param version the IP address version
194 * @param value the IP address value stored in network byte order
195 * (i.e., the most significant byte first)
196 * @return an IP address
197 * @throws IllegalArgumentException if the arguments are invalid
198 */
199 public static IpAddress valueOf(Version version, byte[] value) {
200 return new IpAddress(version, value);
201 }
202
203 /**
204 * Converts a byte array and a given offset from the beginning of the
205 * array into an IP address.
206 * <p>
207 * The IP address is stored in network byte order (i.e., the most
208 * significant byte first).
209 * </p>
210 * @param version the IP address version
211 * @param value the value to use
212 * @param offset the offset in bytes from the beginning of the byte array
213 * @return an IP address
214 * @throws IllegalArgumentException if the arguments are invalid
215 */
216 public static IpAddress valueOf(Version version, byte[] value,
217 int offset) {
218 checkArguments(version, value, offset);
219 byte[] bc = Arrays.copyOfRange(value, offset, value.length);
220 return IpAddress.valueOf(version, bc);
221 }
222
223 /**
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700224 * Converts an InetAddress into an IP address.
225 *
226 * @param inetAddress the InetAddress value to use
227 * @return an IP address
228 * @throws IllegalArgumentException if the argument is invalid
229 */
230 public static IpAddress valueOf(InetAddress inetAddress) {
231 byte[] bytes = inetAddress.getAddress();
232 if (inetAddress instanceof Inet4Address) {
233 return new IpAddress(Version.INET, bytes);
234 }
235 if (inetAddress instanceof Inet6Address) {
236 return new IpAddress(Version.INET6, bytes);
237 }
238 // Use the number of bytes as a hint
239 if (bytes.length == INET_BYTE_LENGTH) {
240 return new IpAddress(Version.INET, bytes);
241 }
242 if (bytes.length == INET6_BYTE_LENGTH) {
243 return new IpAddress(Version.INET6, bytes);
244 }
245 final String msg = "Unrecognized IP version address string: " +
246 inetAddress.toString();
247 throw new IllegalArgumentException(msg);
248 }
249
250 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700251 * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
252 * "1111:2222::8888") into an IP address.
253 *
254 * @param value an IP address value in string form
255 * @return an IP address
256 * @throws IllegalArgumentException if the argument is invalid
257 */
258 public static IpAddress valueOf(String value) {
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700259 InetAddress inetAddress = null;
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700260 try {
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700261 inetAddress = InetAddresses.forString(value);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700262 } catch (IllegalArgumentException e) {
263 final String msg = "Invalid IP address string: " + value;
264 throw new IllegalArgumentException(msg);
265 }
Pavlin Radoslavovaf5ff792014-10-31 20:51:47 -0700266 return valueOf(inetAddress);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700267 }
268
269 /**
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700270 * Creates an IP network mask prefix.
271 *
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700272 * @param version the IP address version
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700273 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700274 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700275 * @return a new IP address that contains a mask prefix of the
276 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700277 * @throws IllegalArgumentException if the arguments are invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700278 */
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700279 public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800280 byte[] mask = makeMaskPrefixArray(version, prefixLength);
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700281 return new IpAddress(version, mask);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700282 }
283
284 /**
285 * Creates an IP address by masking it with a network mask of given
286 * mask length.
287 *
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800288 * @param address the address to mask
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700289 * @param prefixLength the length of the mask prefix. Must be in the
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700290 * interval [0, 32] for IPv4, or [0, 128] for IPv6
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700291 * @return a new IP address that is masked with a mask prefix of the
292 * specified length
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700293 * @throws IllegalArgumentException if the prefix length is invalid
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700294 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800295 public static IpAddress makeMaskedAddress(final IpAddress address,
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700296 int prefixLength) {
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800297 if (address instanceof Ip4Address) {
298 Ip4Address ip4a = (Ip4Address) address;
299 return Ip4Address.makeMaskedAddress(ip4a, prefixLength);
300 } else if (address instanceof Ip6Address) {
301 Ip6Address ip6a = (Ip6Address) address;
302 return Ip6Address.makeMaskedAddress(ip6a, prefixLength);
303 } else {
304 byte[] net = makeMaskedAddressArray(address, prefixLength);
305 return IpAddress.valueOf(address.version(), net);
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700306 }
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700307 }
308
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800309 /**
310 * Check if this IP address is zero.
311 *
Thomas Vachuskae7966102015-09-09 17:33:33 -0700312 * @return true if this address is zero
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800313 */
314 public boolean isZero() {
315 for (byte b : octets) {
316 if (b != 0) {
317 return false;
318 }
319 }
320 return true;
321 }
322
Thomas Vachuskae7966102015-09-09 17:33:33 -0700323 /**
324 * Check if this IP address is self-assigned.
325 *
326 * @return true if this address is self-assigned
327 */
328 public boolean isSelfAssigned() {
329 return isIp4() && octets[0] == (byte) 169;
330 }
331
Charles Chan4ca8e602016-02-25 18:05:59 -0800332 /**
333 * Check if this IP address is a multicast address.
334 *
335 * @return true if this address a multicast address
336 */
337 public boolean isMulticast() {
338 return isIp4() ?
Charles Chanaedabfd2016-02-26 09:31:48 -0800339 Ip4Prefix.IPV4_MULTICAST_PREFIX.contains(this.getIp4Address()) :
340 Ip6Prefix.IPV6_MULTICAST_PREFIX.contains(this.getIp6Address());
Charles Chan4ca8e602016-02-25 18:05:59 -0800341 }
342
Jonathan Hartdec62d42014-09-22 15:59:04 -0700343 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700344 public int compareTo(IpAddress o) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700345 // Compare first the version
346 if (this.version != o.version) {
347 return this.version.compareTo(o.version);
348 }
349
350 // Compare the bytes, one-by-one
351 for (int i = 0; i < this.octets.length; i++) {
352 if (this.octets[i] != o.octets[i]) {
353 return UnsignedBytes.compare(this.octets[i], o.octets[i]);
354 }
355 }
356 return 0; // Equal
Jonathan Hartab63aac2014-10-16 08:52:55 -0700357 }
358
359 @Override
Jonathan Hartdec62d42014-09-22 15:59:04 -0700360 public int hashCode() {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700361 return Objects.hash(version, Arrays.hashCode(octets));
Jonathan Hartdec62d42014-09-22 15:59:04 -0700362 }
363
364 @Override
365 public boolean equals(Object obj) {
Pavlin Radoslavov855ea2d2014-10-30 15:32:39 -0700366 if (this == obj) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700367 return true;
368 }
Pavlin Radoslavov50b70672014-11-05 11:22:25 -0800369 if ((obj == null) || (!(obj instanceof IpAddress))) {
Jonathan Hartdec62d42014-09-22 15:59:04 -0700370 return false;
371 }
372 IpAddress other = (IpAddress) obj;
Pavlin Radoslavov49e159a2014-10-29 16:22:13 -0700373 return (version == other.version) &&
374 Arrays.equals(octets, other.octets);
Jonathan Hartdec62d42014-09-22 15:59:04 -0700375 }
376
377 @Override
378 /*
379 * (non-Javadoc)
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700380 * The string representation of the IP address: "x.x.x.x" for IPv4
381 * addresses, or ':' separated string for IPv6 addresses.
Jonathan Hartdec62d42014-09-22 15:59:04 -0700382 *
383 * @see java.lang.Object#toString()
384 */
385 public String toString() {
Brian O'Connor041515f2015-02-19 15:36:17 -0800386 // FIXME InetAddress is super slow
387 switch (version) {
388 case INET:
389 return String.format("%d.%d.%d.%d", octets[0] & 0xff,
Aaron Kruglikovafdf4de2015-06-24 09:28:22 -0700390 octets[1] & 0xff,
391 octets[2] & 0xff,
392 octets[3] & 0xff);
Brian O'Connor041515f2015-02-19 15:36:17 -0800393 case INET6:
394 default:
Aaron Kruglikovafdf4de2015-06-24 09:28:22 -0700395 return ipv6ToStringHelper();
Jonathan Hartdec62d42014-09-22 15:59:04 -0700396 }
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700397 }
398
399 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700400 * Generates an IP prefix.
401 *
402 * @return the IP prefix of the IP address
403 */
404 public IpPrefix toIpPrefix() {
405
406 if (isIp4()) {
407 return IpPrefix.valueOf(new IpAddress(Version.INET, octets),
408 Ip4Address.BIT_LENGTH);
409 } else {
410 return IpPrefix.valueOf(new IpAddress(Version.INET6, octets),
411 Ip6Address.BIT_LENGTH);
412 }
413 }
414
415 /**
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700416 * Gets the IP address name for the IP address version.
417 *
418 * @param version the IP address version
419 * @return the IP address name for the IP address version
420 */
421 private static String addressName(Version version) {
422 switch (version) {
423 case INET:
424 return "IPv4";
425 case INET6:
426 return "IPv6";
427 default:
428 break;
429 }
430 return "UnknownIP(" + version + ")";
431 }
432
433 /**
434 * Checks whether the arguments are valid.
435 *
436 * @param version the IP address version
437 * @param value the IP address value stored in a byte array
438 * @param offset the offset in bytes from the beginning of the byte
439 * array with the address
440 * @throws IllegalArgumentException if any of the arguments is invalid
441 */
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800442 static void checkArguments(Version version, byte[] value, int offset) {
Pavlin Radoslavovd0e32d72014-10-31 18:11:43 -0700443 // Check the offset and byte array length
444 int addrByteLength = byteLength(version);
445 if ((offset < 0) || (offset + addrByteLength > value.length)) {
446 String msg;
447 if (value.length < addrByteLength) {
448 msg = "Invalid " + addressName(version) +
449 " address array: array length: " + value.length +
450 ". Must be at least " + addrByteLength;
451 } else {
452 msg = "Invalid " + addressName(version) +
453 " address array: array offset: " + offset +
454 ". Must be in the interval [0, " +
455 (value.length - addrByteLength) + "]";
456 }
457 throw new IllegalArgumentException(msg);
458 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700459 }
Pavlin Radoslavovf182f012014-11-04 15:03:18 -0800460
461 /**
462 * Creates a byte array for IP network mask prefix.
463 *
464 * @param version the IP address version
465 * @param prefixLength the length of the mask prefix. Must be in the
466 * interval [0, 32] for IPv4, or [0, 128] for IPv6
467 * @return a byte array that contains a mask prefix of the
468 * specified length
469 * @throws IllegalArgumentException if the arguments are invalid
470 */
471 static byte[] makeMaskPrefixArray(Version version, int prefixLength) {
472 int addrByteLength = byteLength(version);
473 int addrBitLength = addrByteLength * Byte.SIZE;
474
475 // Verify the prefix length
476 if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
477 final String msg = "Invalid IP prefix length: " + prefixLength +
478 ". Must be in the interval [0, " + addrBitLength + "].";
479 throw new IllegalArgumentException(msg);
480 }
481
482 // Number of bytes and extra bits that should be all 1s
483 int maskBytes = prefixLength / Byte.SIZE;
484 int maskBits = prefixLength % Byte.SIZE;
485 byte[] mask = new byte[addrByteLength];
486
487 // Set the bytes and extra bits to 1s
488 for (int i = 0; i < maskBytes; i++) {
489 mask[i] = (byte) 0xff; // Set mask bytes to 1s
490 }
491 for (int i = maskBytes; i < addrByteLength; i++) {
492 mask[i] = 0; // Set remaining bytes to 0s
493 }
494 if (maskBits > 0) {
495 mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
496 }
497 return mask;
498 }
499
500 /**
501 * Creates a byte array that represents an IP address masked with
502 * a network mask of given mask length.
503 *
504 * @param addr the address to mask
505 * @param prefixLength the length of the mask prefix. Must be in the
506 * interval [0, 32] for IPv4, or [0, 128] for IPv6
507 * @return a byte array that represents the IP address masked with
508 * a mask prefix of the specified length
509 * @throws IllegalArgumentException if the prefix length is invalid
510 */
511 static byte[] makeMaskedAddressArray(final IpAddress addr,
512 int prefixLength) {
513 byte[] mask = IpAddress.makeMaskPrefixArray(addr.version(),
514 prefixLength);
515 byte[] net = new byte[mask.length];
516
517 // Mask each byte
518 for (int i = 0; i < net.length; i++) {
519 net[i] = (byte) (addr.octets[i] & mask[i]);
520 }
521 return net;
522 }
Aaron Kruglikovafdf4de2015-06-24 09:28:22 -0700523
524 /**
525 * Creates a string based on the IPv6 recommendations for canonical representations found here:
526 * https://tools.ietf.org/html/rfc5952#section-1.
527 * @return A properly formatted IPv6 canonical representation.
528 */
529 private String ipv6ToStringHelper() {
530 //Populate a buffer with the string of the full address with leading zeros stripped
531 StringBuffer buff = new StringBuffer();
532 buff.append(String.format("%x:%x:%x:%x:%x:%x:%x:%x",
533 (((octets[0] & BIT_MASK) << 8) | (octets[1] & BIT_MASK)),
534 (((octets[2] & BIT_MASK) << 8) | (octets[3] & BIT_MASK)),
535 (((octets[4] & BIT_MASK) << 8) | (octets[5] & BIT_MASK)),
536 (((octets[6] & BIT_MASK) << 8) | (octets[7] & BIT_MASK)),
537 (((octets[8] & BIT_MASK) << 8) | (octets[9] & BIT_MASK)),
538 (((octets[10] & BIT_MASK) << 8) | (octets[11] & BIT_MASK)),
539 (((octets[12] & BIT_MASK) << 8) | (octets[13] & BIT_MASK)),
540 (((octets[14] & BIT_MASK) << 8) | (octets[15] & BIT_MASK))));
541 //Initialize variables for tracking longest zero subsequence, tiebreaking by first occurence
542 int longestSeqStart, longestSeqLen, currSeqStart, currSeqLen;
543 longestSeqStart = 0;
544 longestSeqLen = 0;
545 currSeqStart = 0;
546 currSeqLen = 0;
547
548 for (int index = 0; index < buff.length(); index++) {
549 if (buff.charAt(index) == ':') {
550 if (currSeqLen != 0 && buff.charAt(index + 1) == '0') {
551 currSeqLen += 1;
552 }
553 } else if (buff.charAt(index) == '0' && ((index == 0) || (buff.charAt(index - 1) == ':'))) {
554 if (currSeqLen == 0) {
555 currSeqStart = index;
556 }
557 currSeqLen += 1;
558 } else {
559 if (currSeqLen > longestSeqLen) {
560 longestSeqStart = currSeqStart;
561 longestSeqLen = currSeqLen;
562 }
563 currSeqLen = 0;
564 }
565 }
566
567 if (currSeqLen > longestSeqLen) {
568 longestSeqLen = currSeqLen;
569 longestSeqStart = currSeqStart;
570 }
571 if (longestSeqLen > 1) {
572 if (buff.length() == (longestSeqStart + longestSeqLen)) {
573 buff.append(':');
574 }
575
576 buff.delete(longestSeqStart, longestSeqStart + longestSeqLen);
577
578 if (longestSeqStart == 0) {
579 buff.insert(0, ':');
580 }
581 }
582
583 return buff.toString();
584 }
Jonathan Hartdec62d42014-09-22 15:59:04 -0700585}