blob: 1ee9e896c70f8252fbf4e00dded177a15f0da5e5 [file] [log] [blame]
Yotam Harcholf3f11152013-09-05 16:47:16 -07001package org.projectfloodlight.openflow.types;
2
Andreas Wundsam5f71b412014-02-18 12:56:35 -08003import java.util.Arrays;
Yotam Harcholf3f11152013-09-05 16:47:16 -07004import java.util.regex.Pattern;
5
6import org.jboss.netty.buffer.ChannelBuffer;
7import org.projectfloodlight.openflow.exceptions.OFParseError;
Andreas Wundsam5f71b412014-02-18 12:56:35 -08008
Andreas Wundsam22ba3af2013-10-04 16:00:30 -07009import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -070010import com.google.common.primitives.Longs;
11
Yotam Harcholf3f11152013-09-05 16:47:16 -070012/**
13 * IPv6 address object. Instance controlled, immutable. Internal representation:
14 * two 64 bit longs (not that you'd have to know).
15 *
16 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
17 */
Yotam Harchol4d634682013-09-26 13:21:06 -070018public class IPv6Address extends IPAddress<IPv6Address> {
Yotam Harcholf3f11152013-09-05 16:47:16 -070019 static final int LENGTH = 16;
20 private final long raw1;
21 private final long raw2;
22
Gregor Maier1acb4502013-12-12 11:25:07 -080023 private static final int NOT_A_CIDR_MASK = -1;
24 private static final int CIDR_MASK_CACHE_UNSET = -2;
Gregor Maier5615b6c2013-12-11 22:29:07 -080025 // Must appear before the static IPv4Address constant assignments
26 private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
27
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070028 private final static long NONE_VAL1 = 0x0L;
29 private final static long NONE_VAL2 = 0x0L;
30 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
31
Gregor Maier5615b6c2013-12-11 22:29:07 -080032
Yotam Harchola289d552013-09-16 10:10:40 -070033 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
34 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070035
Yotam Harchola289d552013-09-16 10:10:40 -070036 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070037 this.raw1 = raw1;
38 this.raw2 = raw2;
39 }
40
Yotam Harchol4d634682013-09-26 13:21:06 -070041 @Override
Yotam Harcholeb023dc2013-09-26 15:45:44 -070042 public IPVersion getIpVersion() {
43 return IPVersion.IPv6;
Yotam Harchol4d634682013-09-26 13:21:06 -070044 }
45
Gregor Maier7f987e62013-12-10 19:34:18 -080046
Gregor Maier5615b6c2013-12-11 22:29:07 -080047 private int computeCidrMask64(long raw) {
48 long mask = raw;
49 if (raw == 0)
50 return 0;
Gregor Maier1acb4502013-12-12 11:25:07 -080051 else if (Long.bitCount((~mask) + 1) == 1) {
Gregor Maier5615b6c2013-12-11 22:29:07 -080052 // represent a true CIDR prefix length
53 return Long.bitCount(mask);
54 }
55 else {
56 // Not a true prefix
57 return NOT_A_CIDR_MASK;
58 }
59 }
60
61 private int asCidrMaskLengthInternal() {
62 if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
Gregor Maier1acb4502013-12-12 11:25:07 -080063 // No synchronization needed. Writing cidrMaskLengthCache only once
64 if (raw1 == 0 && raw2 == 0) {
65 cidrMaskLengthCache = 0;
66 } else if (raw1 == -1L) {
67 // top half is all 1 bits
68 int tmpLength = computeCidrMask64(raw2);
69 if (tmpLength != NOT_A_CIDR_MASK)
70 tmpLength += 64;
71 cidrMaskLengthCache = tmpLength;
72 } else if (raw2 == 0) {
73 cidrMaskLengthCache = computeCidrMask64(raw1);
74 } else {
75 cidrMaskLengthCache = NOT_A_CIDR_MASK;
Gregor Maier5615b6c2013-12-11 22:29:07 -080076 }
77 }
78 return cidrMaskLengthCache;
79 }
80
81 @Override
82 public boolean isCidrMask() {
83 return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
84 }
85
Gregor Maier7f987e62013-12-10 19:34:18 -080086 @Override
87 public int asCidrMaskLength() {
Gregor Maier5615b6c2013-12-11 22:29:07 -080088 if (!isCidrMask()) {
89 throw new IllegalStateException("IP is not a valid CIDR prefix " +
90 "mask " + toString());
Gregor Maier7f987e62013-12-10 19:34:18 -080091 } else {
Gregor Maier5615b6c2013-12-11 22:29:07 -080092 return asCidrMaskLengthInternal();
Gregor Maier7f987e62013-12-10 19:34:18 -080093 }
94 }
95
Aditya Vaja56b8b182014-03-11 13:13:58 -070096 @Override
97 public boolean isBroadcast() {
98 return this.equals(NO_MASK);
99 }
100
101 @Override
Aditya Vaja98c96e72014-03-11 15:19:01 -0700102 public IPv6Address and(IPv6Address other) {
Aditya Vaja56b8b182014-03-11 13:13:58 -0700103 if (other == null) {
104 throw new NullPointerException("Other IP Address must not be null");
105 }
106 IPv6Address otherIp = (IPv6Address) other;
107 return IPv6Address.of((raw1 & otherIp.raw1), (raw2 & otherIp.raw2));
108 }
109
110 @Override
Aditya Vaja98c96e72014-03-11 15:19:01 -0700111 public IPv6Address or(IPv6Address other) {
Aditya Vaja56b8b182014-03-11 13:13:58 -0700112 if (other == null) {
113 throw new NullPointerException("Other IP Address must not be null");
114 }
115 IPv6Address otherIp = (IPv6Address) other;
116 return IPv6Address.of((raw1 | otherIp.raw1), (raw2 | otherIp.raw2));
117 }
118
119 @Override
120 public IPv6Address not() {
121 return IPv6Address.of(~raw1, ~raw2);
122 }
123
Yotam Harchola289d552013-09-16 10:10:40 -0700124 public static IPv6Address of(final byte[] address) {
Gregor Maier1ff55972013-12-11 02:22:56 -0800125 if (address == null) {
126 throw new NullPointerException("Address must not be null");
127 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700128 if (address.length != LENGTH) {
129 throw new IllegalArgumentException(
Andreas Wundsamc85b5c52013-09-24 13:01:43 -0700130 "Invalid byte array length for IPv6 address: " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700131 }
132
133 long raw1 =
134 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
135 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
136 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
137 | (address[6] & 0xFFL) << 8 | (address[7]);
138
139 long raw2 =
140 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
141 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
142 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
143 | (address[14] & 0xFFL) << 8 | (address[15]);
144
Yotam Harchola289d552013-09-16 10:10:40 -0700145 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700146 }
147
148 private static class IPv6Builder {
149 private long raw1, raw2;
150
151 public void setUnsignedShortWord(final int i, final int value) {
152 int shift = 48 - (i % 4) * 16;
153
154 if (value < 0 || value > 0xFFFF)
155 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
156
157 if (i >= 0 && i < 4)
158 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
159 else if (i >= 4 && i < 8)
160 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
161 else
162 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
163 }
164
Yotam Harchola289d552013-09-16 10:10:40 -0700165 public IPv6Address getIPv6() {
166 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700167 }
168 }
169
170 private final static Pattern colonPattern = Pattern.compile(":");
171
Yotam Harchola289d552013-09-16 10:10:40 -0700172 public static IPv6Address of(final String string) {
Gregor Maier1ff55972013-12-11 02:22:56 -0800173 if (string == null) {
174 throw new NullPointerException("String must not be null");
175 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700176 IPv6Builder builder = new IPv6Builder();
177 String[] parts = colonPattern.split(string, -1);
178
179 int leftWord = 0;
180 int leftIndex = 0;
181
182 boolean hitZeroCompression = false;
183
184 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
185 String part = parts[leftIndex];
186 if (part.length() == 0) {
187 // hit empty group of zero compression
188 hitZeroCompression = true;
189 break;
190 }
191 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
192 }
193
194 if (hitZeroCompression) {
195 if (leftIndex == 0) {
196 // if colon is at the start, two columns must be at the start,
197 // move to the second empty group
198 leftIndex = 1;
199 if (parts.length < 2 || parts[1].length() > 0)
200 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
201 }
202
203 int rightWord = 7;
204 int rightIndex;
205 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
206 String part = parts[rightIndex];
207 if (part.length() == 0)
208 break;
209 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
210 }
211 if (rightIndex == parts.length - 1) {
212 // if colon is at the end, two columns must be at the end, move
213 // to the second empty group
214 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
215 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
216 rightIndex--;
217 }
218 if (leftIndex != rightIndex)
219 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
220 } else {
221 if (leftIndex != 8) {
222 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
223 }
224 }
225 return builder.getIPv6();
226 }
227
Yotam Harchola289d552013-09-16 10:10:40 -0700228 public static IPv6Address of(final long raw1, final long raw2) {
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -0700229 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
230 return NONE;
Yotam Harchola289d552013-09-16 10:10:40 -0700231 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700232 }
233
Andreas Wundsam4e2469e2014-02-17 15:32:43 -0800234 private volatile byte[] bytesCache = null;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700235
236 public byte[] getBytes() {
237 if (bytesCache == null) {
238 synchronized (this) {
239 if (bytesCache == null) {
240 bytesCache =
241 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
242 (byte) ((raw1 >> 48) & 0xFF),
243 (byte) ((raw1 >> 40) & 0xFF),
244 (byte) ((raw1 >> 32) & 0xFF),
245 (byte) ((raw1 >> 24) & 0xFF),
246 (byte) ((raw1 >> 16) & 0xFF),
247 (byte) ((raw1 >> 8) & 0xFF),
248 (byte) ((raw1 >> 0) & 0xFF),
249
250 (byte) ((raw2 >> 56) & 0xFF),
251 (byte) ((raw2 >> 48) & 0xFF),
252 (byte) ((raw2 >> 40) & 0xFF),
253 (byte) ((raw2 >> 32) & 0xFF),
254 (byte) ((raw2 >> 24) & 0xFF),
255 (byte) ((raw2 >> 16) & 0xFF),
256 (byte) ((raw2 >> 8) & 0xFF),
257 (byte) ((raw2 >> 0) & 0xFF) };
258 }
259 }
260 }
Andreas Wundsam5f71b412014-02-18 12:56:35 -0800261 return Arrays.copyOf(bytesCache, bytesCache.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700262 }
263
264 @Override
265 public int getLength() {
266 return LENGTH;
267 }
268
269 @Override
270 public String toString() {
271 return toString(true, false);
272 }
273
274 public int getUnsignedShortWord(final int i) {
275 if (i >= 0 && i < 4)
276 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
277 else if (i >= 4 && i < 8)
278 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
279 else
280 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
281 }
282
283 /** get the index of the first word where to apply IPv6 zero compression */
284 public int getZeroCompressStart() {
285 int start = Integer.MAX_VALUE;
286 int maxLength = -1;
287
288 int candidateStart = -1;
289
290 for (int i = 0; i < 8; i++) {
291 if (candidateStart >= 0) {
292 // in a zero octect
293 if (getUnsignedShortWord(i) != 0) {
294 // end of this candidate word
295 int candidateLength = i - candidateStart;
296 if (candidateLength >= maxLength) {
297 start = candidateStart;
298 maxLength = candidateLength;
299 }
300 candidateStart = -1;
301 }
302 } else {
303 // not in a zero octect
304 if (getUnsignedShortWord(i) == 0) {
305 candidateStart = i;
306 }
307 }
308 }
309
310 if (candidateStart >= 0) {
311 int candidateLength = 8 - candidateStart;
312 if (candidateLength >= maxLength) {
313 start = candidateStart;
314 maxLength = candidateLength;
315 }
316 }
317
318 return start;
319 }
320
321 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
322 StringBuilder res = new StringBuilder();
323
324 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
325 boolean inCompression = false;
326 boolean colonNeeded = false;
327
328 for (int i = 0; i < 8; i++) {
329 int word = getUnsignedShortWord(i);
330
331 if (word == 0) {
332 if (inCompression)
333 continue;
334 else if (i == compressionStart) {
335 res.append(':').append(':');
336 inCompression = true;
337 colonNeeded = false;
338 continue;
339 }
340 } else {
341 inCompression = false;
342 }
343
344 if (colonNeeded) {
345 res.append(':');
346 colonNeeded = false;
347 }
348
349 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
350 16));
351 colonNeeded = true;
352 }
353 return res.toString();
354 }
355
356 @Override
357 public int hashCode() {
358 final int prime = 31;
359 int result = 1;
360 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
361 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
362 return result;
363 }
364
365 @Override
366 public boolean equals(final Object obj) {
367 if (this == obj)
368 return true;
369 if (obj == null)
370 return false;
371 if (getClass() != obj.getClass())
372 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700373 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700374 if (raw1 != other.raw1)
375 return false;
376 if (raw2 != other.raw2)
377 return false;
378 return true;
379 }
380
381 public void write16Bytes(ChannelBuffer c) {
382 c.writeLong(this.raw1);
383 c.writeLong(this.raw2);
384 }
385
Yotam Harchola289d552013-09-16 10:10:40 -0700386 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
387 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700388 }
389
390 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700391 public IPv6Address applyMask(IPv6Address mask) {
Aditya Vaja98c96e72014-03-11 15:19:01 -0700392 return and(mask);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700393 }
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700394
395 @Override
396 public int compareTo(IPv6Address o) {
397 int res = Longs.compare(raw1, o.raw1);
398 if(res != 0)
399 return res;
400 else
401 return Longs.compare(raw2, o.raw2);
402 }
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700403
404 @Override
405 public void putTo(PrimitiveSink sink) {
406 sink.putLong(raw1);
407 sink.putLong(raw2);
408 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700409}