blob: 8a3a60a927ec2bfe282c4915748492d1dc1d3f50 [file] [log] [blame]
Yotam Harcholf3f11152013-09-05 16:47:16 -07001package org.projectfloodlight.openflow.types;
2
Ronald Lifea17892014-07-03 18:36:06 -07003import java.math.BigInteger;
Andreas Wundsam5f71b412014-02-18 12:56:35 -08004import java.util.Arrays;
Yotam Harcholf3f11152013-09-05 16:47:16 -07005import java.util.regex.Pattern;
6
Andreas Wundsam3700d162014-03-11 04:43:38 -07007import javax.annotation.Nonnull;
8
Yotam Harcholf3f11152013-09-05 16:47:16 -07009import org.jboss.netty.buffer.ChannelBuffer;
10import org.projectfloodlight.openflow.exceptions.OFParseError;
Andreas Wundsam5f71b412014-02-18 12:56:35 -080011
Sovietaced9dfc1ef2014-06-27 11:13:57 -070012import com.google.common.base.Preconditions;
Andreas Wundsam22ba3af2013-10-04 16:00:30 -070013import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -070014import com.google.common.primitives.Longs;
15
Yotam Harcholf3f11152013-09-05 16:47:16 -070016/**
17 * IPv6 address object. Instance controlled, immutable. Internal representation:
18 * two 64 bit longs (not that you'd have to know).
19 *
20 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
21 */
Yotam Harchol4d634682013-09-26 13:21:06 -070022public class IPv6Address extends IPAddress<IPv6Address> {
Yotam Harcholf3f11152013-09-05 16:47:16 -070023 static final int LENGTH = 16;
24 private final long raw1;
25 private final long raw2;
26
Gregor Maier1acb4502013-12-12 11:25:07 -080027 private static final int NOT_A_CIDR_MASK = -1;
28 private static final int CIDR_MASK_CACHE_UNSET = -2;
Gregor Maier5615b6c2013-12-11 22:29:07 -080029 // Must appear before the static IPv4Address constant assignments
30 private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
31
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070032 private final static long NONE_VAL1 = 0x0L;
33 private final static long NONE_VAL2 = 0x0L;
34 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
35
Gregor Maier5615b6c2013-12-11 22:29:07 -080036
Yotam Harchola289d552013-09-16 10:10:40 -070037 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
38 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070039
Yotam Harchola289d552013-09-16 10:10:40 -070040 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070041 this.raw1 = raw1;
42 this.raw2 = raw2;
43 }
44
Yotam Harchol4d634682013-09-26 13:21:06 -070045 @Override
Yotam Harcholeb023dc2013-09-26 15:45:44 -070046 public IPVersion getIpVersion() {
47 return IPVersion.IPv6;
Yotam Harchol4d634682013-09-26 13:21:06 -070048 }
49
Gregor Maier7f987e62013-12-10 19:34:18 -080050
Gregor Maier5615b6c2013-12-11 22:29:07 -080051 private int computeCidrMask64(long raw) {
52 long mask = raw;
53 if (raw == 0)
54 return 0;
Gregor Maier1acb4502013-12-12 11:25:07 -080055 else if (Long.bitCount((~mask) + 1) == 1) {
Gregor Maier5615b6c2013-12-11 22:29:07 -080056 // represent a true CIDR prefix length
57 return Long.bitCount(mask);
58 }
59 else {
60 // Not a true prefix
61 return NOT_A_CIDR_MASK;
62 }
63 }
64
65 private int asCidrMaskLengthInternal() {
66 if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
Gregor Maier1acb4502013-12-12 11:25:07 -080067 // No synchronization needed. Writing cidrMaskLengthCache only once
68 if (raw1 == 0 && raw2 == 0) {
69 cidrMaskLengthCache = 0;
70 } else if (raw1 == -1L) {
71 // top half is all 1 bits
72 int tmpLength = computeCidrMask64(raw2);
73 if (tmpLength != NOT_A_CIDR_MASK)
74 tmpLength += 64;
75 cidrMaskLengthCache = tmpLength;
76 } else if (raw2 == 0) {
77 cidrMaskLengthCache = computeCidrMask64(raw1);
78 } else {
79 cidrMaskLengthCache = NOT_A_CIDR_MASK;
Gregor Maier5615b6c2013-12-11 22:29:07 -080080 }
81 }
82 return cidrMaskLengthCache;
83 }
84
85 @Override
86 public boolean isCidrMask() {
87 return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
88 }
89
Gregor Maier7f987e62013-12-10 19:34:18 -080090 @Override
91 public int asCidrMaskLength() {
Gregor Maier5615b6c2013-12-11 22:29:07 -080092 if (!isCidrMask()) {
93 throw new IllegalStateException("IP is not a valid CIDR prefix " +
94 "mask " + toString());
Gregor Maier7f987e62013-12-10 19:34:18 -080095 } else {
Gregor Maier5615b6c2013-12-11 22:29:07 -080096 return asCidrMaskLengthInternal();
Gregor Maier7f987e62013-12-10 19:34:18 -080097 }
98 }
99
Aditya Vaja56b8b182014-03-11 13:13:58 -0700100 @Override
101 public boolean isBroadcast() {
102 return this.equals(NO_MASK);
103 }
104
105 @Override
Aditya Vaja98c96e72014-03-11 15:19:01 -0700106 public IPv6Address and(IPv6Address other) {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700107 Preconditions.checkNotNull(other, "other must not be null");
108
109 IPv6Address otherIp = other;
Aditya Vaja56b8b182014-03-11 13:13:58 -0700110 return IPv6Address.of((raw1 & otherIp.raw1), (raw2 & otherIp.raw2));
111 }
112
113 @Override
Aditya Vaja98c96e72014-03-11 15:19:01 -0700114 public IPv6Address or(IPv6Address other) {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700115 Preconditions.checkNotNull(other, "other must not be null");
116
117 IPv6Address otherIp = other;
Aditya Vaja56b8b182014-03-11 13:13:58 -0700118 return IPv6Address.of((raw1 | otherIp.raw1), (raw2 | otherIp.raw2));
119 }
120
121 @Override
122 public IPv6Address not() {
123 return IPv6Address.of(~raw1, ~raw2);
124 }
125
Yotam Harchola289d552013-09-16 10:10:40 -0700126 public static IPv6Address of(final byte[] address) {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700127 Preconditions.checkNotNull(address, "address must not be null");
128
Yotam Harcholf3f11152013-09-05 16:47:16 -0700129 if (address.length != LENGTH) {
130 throw new IllegalArgumentException(
Andreas Wundsamc85b5c52013-09-24 13:01:43 -0700131 "Invalid byte array length for IPv6 address: " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700132 }
133
134 long raw1 =
135 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
136 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
137 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
138 | (address[6] & 0xFFL) << 8 | (address[7]);
139
140 long raw2 =
141 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
142 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
143 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
144 | (address[14] & 0xFFL) << 8 | (address[15]);
145
Yotam Harchola289d552013-09-16 10:10:40 -0700146 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700147 }
148
149 private static class IPv6Builder {
150 private long raw1, raw2;
151
152 public void setUnsignedShortWord(final int i, final int value) {
153 int shift = 48 - (i % 4) * 16;
154
155 if (value < 0 || value > 0xFFFF)
156 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
157
158 if (i >= 0 && i < 4)
159 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
160 else if (i >= 4 && i < 8)
161 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
162 else
163 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
164 }
165
Yotam Harchola289d552013-09-16 10:10:40 -0700166 public IPv6Address getIPv6() {
167 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700168 }
169 }
170
171 private final static Pattern colonPattern = Pattern.compile(":");
172
Andreas Wundsam3700d162014-03-11 04:43:38 -0700173 /** parse an IPv6Address from its conventional string representation.
174 * <p>
175 * Expects up to 8 groups of 16-bit hex words seperated by colons
176 * (e.g., 2001:db8:85a3:8d3:1319:8a2e:370:7348).
177 * <p>
178 * Supports zero compression (e.g., 2001:db8::7348).
179 * Does <b>not</b> currently support embedding a dotted-quad IPv4 address
180 * into the IPv6 address (e.g., 2001:db8::192.168.0.1).
181 *
182 * @param string a string representation of an IPv6 address
183 * @return the parsed IPv6 address
184 * @throws NullPointerException if string is null
185 * @throws IllegalArgumentException if string is not a valid IPv6Address
186 */
187 @Nonnull
188 public static IPv6Address of(@Nonnull final String string) throws IllegalArgumentException {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700189 Preconditions.checkNotNull(string, "string must not be null");
190
Yotam Harcholf3f11152013-09-05 16:47:16 -0700191 IPv6Builder builder = new IPv6Builder();
192 String[] parts = colonPattern.split(string, -1);
193
194 int leftWord = 0;
195 int leftIndex = 0;
196
197 boolean hitZeroCompression = false;
198
199 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
200 String part = parts[leftIndex];
201 if (part.length() == 0) {
202 // hit empty group of zero compression
203 hitZeroCompression = true;
204 break;
205 }
206 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
207 }
208
209 if (hitZeroCompression) {
210 if (leftIndex == 0) {
211 // if colon is at the start, two columns must be at the start,
212 // move to the second empty group
213 leftIndex = 1;
214 if (parts.length < 2 || parts[1].length() > 0)
215 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
216 }
217
218 int rightWord = 7;
219 int rightIndex;
220 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
221 String part = parts[rightIndex];
222 if (part.length() == 0)
223 break;
224 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
225 }
226 if (rightIndex == parts.length - 1) {
227 // if colon is at the end, two columns must be at the end, move
228 // to the second empty group
229 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
230 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
231 rightIndex--;
232 }
233 if (leftIndex != rightIndex)
234 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
235 } else {
236 if (leftIndex != 8) {
237 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
238 }
239 }
240 return builder.getIPv6();
241 }
242
Andreas Wundsam3700d162014-03-11 04:43:38 -0700243 /** construct an IPv6 adress from two 64 bit integers representing the first and
244 * second 8-byte blocks of the address.
245 *
246 * @param raw1 - the first 8 byte block of the address
247 * @param raw2 - the second 8 byte block of the address
248 * @return the constructed IPv6Address
249 */
Yotam Harchola289d552013-09-16 10:10:40 -0700250 public static IPv6Address of(final long raw1, final long raw2) {
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -0700251 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
252 return NONE;
Yotam Harchola289d552013-09-16 10:10:40 -0700253 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700254 }
255
Ronald Lifea17892014-07-03 18:36:06 -0700256 public static IPv6Address ofCidrMaskLength(int cidrMaskLength) {
257 Preconditions.checkArgument(
258 cidrMaskLength >= 0 && cidrMaskLength <= 128,
259 "Invalid IPv6 CIDR mask length: %s", cidrMaskLength);
260
261 if (cidrMaskLength == 128) {
262 return IPv6Address.NO_MASK;
263 } else if (cidrMaskLength == 0) {
264 return IPv6Address.FULL_MASK;
265 } else {
266 BigInteger mask
267 = BigInteger.ONE.negate().shiftLeft(128 - cidrMaskLength);
268 byte[] maskBytesTemp = mask.toByteArray();
269 byte[] maskBytes;
270 if (maskBytesTemp.length < 16) {
271 maskBytes = new byte[16];
272 System.arraycopy(maskBytesTemp, 0,
273 maskBytes, 16 - maskBytesTemp.length,
274 maskBytesTemp.length);
275 Arrays.fill(maskBytes, 0, 16 - maskBytesTemp.length, (byte)(0xFF));
276 } else if (maskBytesTemp.length > 16) {
277 maskBytes = new byte[16];
278 System.arraycopy(maskBytesTemp, 0, maskBytes, 0, maskBytes.length);
279 } else {
280 maskBytes = maskBytesTemp;
281 }
282 return IPv6Address.of(maskBytes);
283 }
284 }
285
Andreas Wundsam4e2469e2014-02-17 15:32:43 -0800286 private volatile byte[] bytesCache = null;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700287
288 public byte[] getBytes() {
289 if (bytesCache == null) {
290 synchronized (this) {
291 if (bytesCache == null) {
292 bytesCache =
293 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
294 (byte) ((raw1 >> 48) & 0xFF),
295 (byte) ((raw1 >> 40) & 0xFF),
296 (byte) ((raw1 >> 32) & 0xFF),
297 (byte) ((raw1 >> 24) & 0xFF),
298 (byte) ((raw1 >> 16) & 0xFF),
299 (byte) ((raw1 >> 8) & 0xFF),
300 (byte) ((raw1 >> 0) & 0xFF),
301
302 (byte) ((raw2 >> 56) & 0xFF),
303 (byte) ((raw2 >> 48) & 0xFF),
304 (byte) ((raw2 >> 40) & 0xFF),
305 (byte) ((raw2 >> 32) & 0xFF),
306 (byte) ((raw2 >> 24) & 0xFF),
307 (byte) ((raw2 >> 16) & 0xFF),
308 (byte) ((raw2 >> 8) & 0xFF),
309 (byte) ((raw2 >> 0) & 0xFF) };
310 }
311 }
312 }
Andreas Wundsam5f71b412014-02-18 12:56:35 -0800313 return Arrays.copyOf(bytesCache, bytesCache.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700314 }
315
316 @Override
317 public int getLength() {
318 return LENGTH;
319 }
320
321 @Override
322 public String toString() {
323 return toString(true, false);
324 }
325
326 public int getUnsignedShortWord(final int i) {
327 if (i >= 0 && i < 4)
328 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
329 else if (i >= 4 && i < 8)
330 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
331 else
332 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
333 }
334
335 /** get the index of the first word where to apply IPv6 zero compression */
336 public int getZeroCompressStart() {
337 int start = Integer.MAX_VALUE;
338 int maxLength = -1;
339
340 int candidateStart = -1;
341
342 for (int i = 0; i < 8; i++) {
343 if (candidateStart >= 0) {
344 // in a zero octect
345 if (getUnsignedShortWord(i) != 0) {
346 // end of this candidate word
347 int candidateLength = i - candidateStart;
348 if (candidateLength >= maxLength) {
349 start = candidateStart;
350 maxLength = candidateLength;
351 }
352 candidateStart = -1;
353 }
354 } else {
355 // not in a zero octect
356 if (getUnsignedShortWord(i) == 0) {
357 candidateStart = i;
358 }
359 }
360 }
361
362 if (candidateStart >= 0) {
363 int candidateLength = 8 - candidateStart;
364 if (candidateLength >= maxLength) {
365 start = candidateStart;
366 maxLength = candidateLength;
367 }
368 }
369
370 return start;
371 }
372
373 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
374 StringBuilder res = new StringBuilder();
375
376 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
377 boolean inCompression = false;
378 boolean colonNeeded = false;
379
380 for (int i = 0; i < 8; i++) {
381 int word = getUnsignedShortWord(i);
382
383 if (word == 0) {
384 if (inCompression)
385 continue;
386 else if (i == compressionStart) {
387 res.append(':').append(':');
388 inCompression = true;
389 colonNeeded = false;
390 continue;
391 }
392 } else {
393 inCompression = false;
394 }
395
396 if (colonNeeded) {
397 res.append(':');
398 colonNeeded = false;
399 }
400
401 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
402 16));
403 colonNeeded = true;
404 }
405 return res.toString();
406 }
407
408 @Override
409 public int hashCode() {
410 final int prime = 31;
411 int result = 1;
412 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
413 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
414 return result;
415 }
416
417 @Override
418 public boolean equals(final Object obj) {
419 if (this == obj)
420 return true;
421 if (obj == null)
422 return false;
423 if (getClass() != obj.getClass())
424 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700425 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700426 if (raw1 != other.raw1)
427 return false;
428 if (raw2 != other.raw2)
429 return false;
430 return true;
431 }
432
433 public void write16Bytes(ChannelBuffer c) {
434 c.writeLong(this.raw1);
435 c.writeLong(this.raw2);
436 }
437
Yotam Harchola289d552013-09-16 10:10:40 -0700438 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
439 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700440 }
441
442 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700443 public IPv6Address applyMask(IPv6Address mask) {
Aditya Vaja98c96e72014-03-11 15:19:01 -0700444 return and(mask);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700445 }
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700446
447 @Override
448 public int compareTo(IPv6Address o) {
449 int res = Longs.compare(raw1, o.raw1);
450 if(res != 0)
451 return res;
452 else
453 return Longs.compare(raw2, o.raw2);
454 }
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700455
456 @Override
457 public void putTo(PrimitiveSink sink) {
458 sink.putLong(raw1);
459 sink.putLong(raw2);
460 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700461}