blob: 471d0fbdb4116dea9f34ebc90d09e5f159cccc69 [file] [log] [blame]
tom0eb04ca2014-08-25 14:34:51 -07001package org.projectfloodlight.openflow.types;
2
3import java.net.Inet6Address;
4import java.net.InetAddress;
5import java.util.Arrays;
6import java.util.regex.Pattern;
7
8import javax.annotation.Nonnull;
9
10import org.jboss.netty.buffer.ChannelBuffer;
11import org.projectfloodlight.openflow.exceptions.OFParseError;
12
13import com.google.common.base.Preconditions;
14import com.google.common.hash.PrimitiveSink;
15import com.google.common.primitives.Longs;
16
17/**
18 * IPv6 address object. Instance controlled, immutable. Internal representation:
19 * two 64 bit longs (not that you'd have to know).
20 *
21 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
22 */
23public class IPv6Address extends IPAddress<IPv6Address> {
24 static final int LENGTH = 16;
25 private final long raw1;
26 private final long raw2;
27
28 private static final int NOT_A_CIDR_MASK = -1;
29 private static final int CIDR_MASK_CACHE_UNSET = -2;
30 // Must appear before the static IPv4Address constant assignments
31 private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
32
33 private final static long NONE_VAL1 = 0x0L;
34 private final static long NONE_VAL2 = 0x0L;
35 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
36
37
38 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
39 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
40
41 private IPv6Address(final long raw1, final long raw2) {
42 this.raw1 = raw1;
43 this.raw2 = raw2;
44 }
45
46 @Override
47 public IPVersion getIpVersion() {
48 return IPVersion.IPv6;
49 }
50
51
52 private int computeCidrMask64(long raw) {
53 long mask = raw;
54 if (raw == 0)
55 return 0;
56 else if (Long.bitCount((~mask) + 1) == 1) {
57 // represent a true CIDR prefix length
58 return Long.bitCount(mask);
59 }
60 else {
61 // Not a true prefix
62 return NOT_A_CIDR_MASK;
63 }
64 }
65
66 private int asCidrMaskLengthInternal() {
67 if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
68 // No synchronization needed. Writing cidrMaskLengthCache only once
69 if (raw1 == 0 && raw2 == 0) {
70 cidrMaskLengthCache = 0;
71 } else if (raw1 == -1L) {
72 // top half is all 1 bits
73 int tmpLength = computeCidrMask64(raw2);
74 if (tmpLength != NOT_A_CIDR_MASK)
75 tmpLength += 64;
76 cidrMaskLengthCache = tmpLength;
77 } else if (raw2 == 0) {
78 cidrMaskLengthCache = computeCidrMask64(raw1);
79 } else {
80 cidrMaskLengthCache = NOT_A_CIDR_MASK;
81 }
82 }
83 return cidrMaskLengthCache;
84 }
85
86 @Override
87 public boolean isCidrMask() {
88 return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
89 }
90
91 @Override
92 public int asCidrMaskLength() {
93 if (!isCidrMask()) {
94 throw new IllegalStateException("IP is not a valid CIDR prefix " +
95 "mask " + toString());
96 } else {
97 return asCidrMaskLengthInternal();
98 }
99 }
100
101 @Override
102 public boolean isBroadcast() {
103 return this.equals(NO_MASK);
104 }
105
106 @Override
107 public IPv6Address and(IPv6Address other) {
108 Preconditions.checkNotNull(other, "other must not be null");
109
110 IPv6Address otherIp = other;
111 return IPv6Address.of((raw1 & otherIp.raw1), (raw2 & otherIp.raw2));
112 }
113
114 @Override
115 public IPv6Address or(IPv6Address other) {
116 Preconditions.checkNotNull(other, "other must not be null");
117
118 IPv6Address otherIp = other;
119 return IPv6Address.of((raw1 | otherIp.raw1), (raw2 | otherIp.raw2));
120 }
121
122 @Override
123 public IPv6Address not() {
124 return IPv6Address.of(~raw1, ~raw2);
125 }
126
127 /**
128 * Returns an {@code IPv6Address} object that represents the given
129 * IP address. The argument is in network byte order: the highest
130 * order byte of the address is in {@code address[0]}.
131 * <p>
132 * The address byte array must be 16 bytes long (128 bits long).
133 * <p>
134 * Similar to {@link InetAddress#getByAddress(byte[])}.
135 *
136 * @param address the raw IP address in network byte order
137 * @return an {@code IPv6Address} object that represents the given
138 * raw IP address
139 * @throws NullPointerException if the given address was {@code null}
140 * @throws IllegalArgumentException if the given address was of an invalid
141 * byte array length
142 * @see InetAddress#getByAddress(byte[])
143 */
144 @Nonnull
145 public static IPv6Address of(@Nonnull final byte[] address) {
146 Preconditions.checkNotNull(address, "address must not be null");
147
148 if (address.length != LENGTH) {
149 throw new IllegalArgumentException(
150 "Invalid byte array length for IPv6 address: " + address.length);
151 }
152
153 long raw1 =
154 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
155 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
156 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
157 | (address[6] & 0xFFL) << 8 | (address[7]);
158
159 long raw2 =
160 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
161 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
162 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
163 | (address[14] & 0xFFL) << 8 | (address[15]);
164
165 return IPv6Address.of(raw1, raw2);
166 }
167
168 private static class IPv6Builder {
169 private long raw1, raw2;
170
171 public void setUnsignedShortWord(final int i, final int value) {
172 int shift = 48 - (i % 4) * 16;
173
174 if (value < 0 || value > 0xFFFF)
175 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
176
177 if (i >= 0 && i < 4)
178 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
179 else if (i >= 4 && i < 8)
180 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
181 else
182 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
183 }
184
185 public IPv6Address getIPv6() {
186 return IPv6Address.of(raw1, raw2);
187 }
188 }
189
190 private final static Pattern colonPattern = Pattern.compile(":");
191
192 /**
193 * Returns an {@code IPv6Address} object that represents the given
194 * IP address. The argument is in the conventional string representation
195 * of IPv6 addresses.
196 * <p>
197 * Expects up to 8 groups of 16-bit hex words seperated by colons
198 * (e.g., 2001:db8:85a3:8d3:1319:8a2e:370:7348).
199 * <p>
200 * Supports zero compression (e.g., 2001:db8::7348).
201 * Does <b>not</b> currently support embedding a dotted-quad IPv4 address
202 * into the IPv6 address (e.g., 2001:db8::192.168.0.1).
203 *
204 * @param string the IP address in the conventional string representation
205 * of IPv6 addresses
206 * @return an {@code IPv6Address} object that represents the given
207 * IP address
208 * @throws NullPointerException if the given string was {@code null}
209 * @throws IllegalArgumentException if the given string was not a valid
210 * IPv6 address
211 */
212 @Nonnull
213 public static IPv6Address of(@Nonnull final String string) throws IllegalArgumentException {
214 Preconditions.checkNotNull(string, "string must not be null");
215
216 IPv6Builder builder = new IPv6Builder();
217 String[] parts = colonPattern.split(string, -1);
218
219 int leftWord = 0;
220 int leftIndex = 0;
221
222 boolean hitZeroCompression = false;
223
224 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
225 String part = parts[leftIndex];
226 if (part.length() == 0) {
227 // hit empty group of zero compression
228 hitZeroCompression = true;
229 break;
230 }
231 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
232 }
233
234 if (hitZeroCompression) {
235 if (leftIndex == 0) {
236 // if colon is at the start, two columns must be at the start,
237 // move to the second empty group
238 leftIndex = 1;
239 if (parts.length < 2 || parts[1].length() > 0)
240 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
241 }
242
243 int rightWord = 7;
244 int rightIndex;
245 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
246 String part = parts[rightIndex];
247 if (part.length() == 0)
248 break;
249 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
250 }
251 if (rightIndex == parts.length - 1) {
252 // if colon is at the end, two columns must be at the end, move
253 // to the second empty group
254 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
255 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
256 rightIndex--;
257 }
258 if (leftIndex != rightIndex)
259 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
260 } else {
261 if (leftIndex != 8) {
262 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
263 }
264 }
265 return builder.getIPv6();
266 }
267
268 /**
269 * Returns an {@code IPv6Address} object that represents the given
270 * IP address. The arguments are the two 64-bit integers representing
271 * the first (higher-order) and second (lower-order) 64-bit blocks
272 * of the IP address.
273 *
274 * @param raw1 the first (higher-order) 64-bit block of the IP address
275 * @param raw2 the second (lower-order) 64-bit block of the IP address
276 * @return an {@code IPv6Address} object that represents the given
277 * raw IP address
278 */
279 @Nonnull
280 public static IPv6Address of(final long raw1, final long raw2) {
281 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
282 return NONE;
283 return new IPv6Address(raw1, raw2);
284 }
285
286 /**
287 * Returns an {@code IPv6Address} object that represents the given
288 * IP address. The argument is given as an {@code Inet6Address} object.
289 *
290 * @param address the IP address as an {@code Inet6Address} object
291 * @return an {@code IPv6Address} object that represents the
292 * given IP address
293 * @throws NullPointerException if the given {@code Inet6Address} was
294 * {@code null}
295 */
296 @Nonnull
297 public static IPv6Address of(@Nonnull final Inet6Address address) {
298 Preconditions.checkNotNull(address, "address must not be null");
299 return IPv6Address.of(address.getAddress());
300 }
301
302 /**
303 * Returns an {@code IPv6Address} object that represents the
304 * CIDR subnet mask of the given prefix length.
305 *
306 * @param cidrMaskLength the prefix length of the CIDR subnet mask
307 * (i.e. the number of leading one-bits),
308 * where {@code 0 <= cidrMaskLength <= 128}
309 * @return an {@code IPv6Address} object that represents the
310 * CIDR subnet mask of the given prefix length
311 * @throws IllegalArgumentException if the given prefix length was invalid
312 */
313 @Nonnull
314 public static IPv6Address ofCidrMaskLength(final int cidrMaskLength) {
315 Preconditions.checkArgument(
316 cidrMaskLength >= 0 && cidrMaskLength <= 128,
317 "Invalid IPv6 CIDR mask length: %s", cidrMaskLength);
318
319 if (cidrMaskLength == 128) {
320 return IPv6Address.NO_MASK;
321 } else if (cidrMaskLength == 0) {
322 return IPv6Address.FULL_MASK;
323 } else {
324 int shift1 = Math.min(cidrMaskLength, 64);
325 long raw1 = shift1 == 0 ? 0 : -1L << (64 - shift1);
326 int shift2 = Math.max(cidrMaskLength - 64, 0);
327 long raw2 = shift2 == 0 ? 0 : -1L << (64 - shift2);
328 return IPv6Address.of(raw1, raw2);
329 }
330 }
331
332 /**
333 * Returns an {@code IPv6AddressWithMask} object that represents this
334 * IP address masked by the given IP address mask.
335 *
336 * @param mask the {@code IPv6Address} object that represents the mask
337 * @return an {@code IPv6AddressWithMask} object that represents this
338 * IP address masked by the given mask
339 * @throws NullPointerException if the given mask was {@code null}
340 */
341 @Nonnull
342 @Override
343 public IPv6AddressWithMask withMask(@Nonnull final IPv6Address mask) {
344 return IPv6AddressWithMask.of(this, mask);
345 }
346
347 /**
348 * Returns an {@code IPv6AddressWithMask} object that represents this
349 * IP address masked by the CIDR subnet mask of the given prefix length.
350 *
351 * @param cidrMaskLength the prefix length of the CIDR subnet mask
352 * (i.e. the number of leading one-bits),
353 * where {@code 0 <= cidrMaskLength <= 128}
354 * @return an {@code IPv6AddressWithMask} object that
355 * represents this IP address masked by the CIDR
356 * subnet mask of the given prefix length
357 * @throws IllegalArgumentException if the given prefix length was invalid
358 * @see #ofCidrMaskLength(int)
359 */
360 @Nonnull
361 @Override
362 public IPv6AddressWithMask withMaskOfLength(final int cidrMaskLength) {
363 return this.withMask(IPv6Address.ofCidrMaskLength(cidrMaskLength));
364 }
365
366 private volatile byte[] bytesCache = null;
367
368 public byte[] getBytes() {
369 if (bytesCache == null) {
370 synchronized (this) {
371 if (bytesCache == null) {
372 bytesCache =
373 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
374 (byte) ((raw1 >> 48) & 0xFF),
375 (byte) ((raw1 >> 40) & 0xFF),
376 (byte) ((raw1 >> 32) & 0xFF),
377 (byte) ((raw1 >> 24) & 0xFF),
378 (byte) ((raw1 >> 16) & 0xFF),
379 (byte) ((raw1 >> 8) & 0xFF),
380 (byte) ((raw1 >> 0) & 0xFF),
381
382 (byte) ((raw2 >> 56) & 0xFF),
383 (byte) ((raw2 >> 48) & 0xFF),
384 (byte) ((raw2 >> 40) & 0xFF),
385 (byte) ((raw2 >> 32) & 0xFF),
386 (byte) ((raw2 >> 24) & 0xFF),
387 (byte) ((raw2 >> 16) & 0xFF),
388 (byte) ((raw2 >> 8) & 0xFF),
389 (byte) ((raw2 >> 0) & 0xFF) };
390 }
391 }
392 }
393 return Arrays.copyOf(bytesCache, bytesCache.length);
394 }
395
396 @Override
397 public int getLength() {
398 return LENGTH;
399 }
400
401 @Override
402 public String toString() {
403 return toString(true, false);
404 }
405
406 public int getUnsignedShortWord(final int i) {
407 if (i >= 0 && i < 4)
408 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
409 else if (i >= 4 && i < 8)
410 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
411 else
412 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
413 }
414
415 /** get the index of the first word where to apply IPv6 zero compression */
416 public int getZeroCompressStart() {
417 int start = Integer.MAX_VALUE;
418 int maxLength = -1;
419
420 int candidateStart = -1;
421
422 for (int i = 0; i < 8; i++) {
423 if (candidateStart >= 0) {
424 // in a zero octect
425 if (getUnsignedShortWord(i) != 0) {
426 // end of this candidate word
427 int candidateLength = i - candidateStart;
428 if (candidateLength >= maxLength) {
429 start = candidateStart;
430 maxLength = candidateLength;
431 }
432 candidateStart = -1;
433 }
434 } else {
435 // not in a zero octect
436 if (getUnsignedShortWord(i) == 0) {
437 candidateStart = i;
438 }
439 }
440 }
441
442 if (candidateStart >= 0) {
443 int candidateLength = 8 - candidateStart;
444 if (candidateLength >= maxLength) {
445 start = candidateStart;
446 maxLength = candidateLength;
447 }
448 }
449
450 return start;
451 }
452
453 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
454 StringBuilder res = new StringBuilder();
455
456 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
457 boolean inCompression = false;
458 boolean colonNeeded = false;
459
460 for (int i = 0; i < 8; i++) {
461 int word = getUnsignedShortWord(i);
462
463 if (word == 0) {
464 if (inCompression)
465 continue;
466 else if (i == compressionStart) {
467 res.append(':').append(':');
468 inCompression = true;
469 colonNeeded = false;
470 continue;
471 }
472 } else {
473 inCompression = false;
474 }
475
476 if (colonNeeded) {
477 res.append(':');
478 colonNeeded = false;
479 }
480
481 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
482 16));
483 colonNeeded = true;
484 }
485 return res.toString();
486 }
487
488 @Override
489 public int hashCode() {
490 final int prime = 31;
491 int result = 1;
492 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
493 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
494 return result;
495 }
496
497 @Override
498 public boolean equals(final Object obj) {
499 if (this == obj)
500 return true;
501 if (obj == null)
502 return false;
503 if (getClass() != obj.getClass())
504 return false;
505 IPv6Address other = (IPv6Address) obj;
506 if (raw1 != other.raw1)
507 return false;
508 if (raw2 != other.raw2)
509 return false;
510 return true;
511 }
512
513 public void write16Bytes(ChannelBuffer c) {
514 c.writeLong(this.raw1);
515 c.writeLong(this.raw2);
516 }
517
518 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
519 return IPv6Address.of(c.readLong(), c.readLong());
520 }
521
522 @Override
523 public IPv6Address applyMask(IPv6Address mask) {
524 return and(mask);
525 }
526
527 @Override
528 public int compareTo(IPv6Address o) {
529 int res = Longs.compare(raw1, o.raw1);
530 if(res != 0)
531 return res;
532 else
533 return Longs.compare(raw2, o.raw2);
534 }
535
536 @Override
537 public void putTo(PrimitiveSink sink) {
538 sink.putLong(raw1);
539 sink.putLong(raw2);
540 }
541}