blob: 4e7b8566bdcefd288f1a42f3c3d86676bf7f2be7 [file] [log] [blame]
Yotam Harcholf3f11152013-09-05 16:47:16 -07001package org.projectfloodlight.openflow.types;
2
3import java.util.regex.Pattern;
4
5import org.jboss.netty.buffer.ChannelBuffer;
6import org.projectfloodlight.openflow.exceptions.OFParseError;
Andreas Wundsam22ba3af2013-10-04 16:00:30 -07007import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -07008import com.google.common.primitives.Longs;
9
Yotam Harcholf3f11152013-09-05 16:47:16 -070010/**
11 * IPv6 address object. Instance controlled, immutable. Internal representation:
12 * two 64 bit longs (not that you'd have to know).
13 *
14 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
15 */
Yotam Harchol4d634682013-09-26 13:21:06 -070016public class IPv6Address extends IPAddress<IPv6Address> {
Yotam Harcholf3f11152013-09-05 16:47:16 -070017 static final int LENGTH = 16;
18 private final long raw1;
19 private final long raw2;
20
Gregor Maier1acb4502013-12-12 11:25:07 -080021 private static final int NOT_A_CIDR_MASK = -1;
22 private static final int CIDR_MASK_CACHE_UNSET = -2;
Gregor Maier5615b6c2013-12-11 22:29:07 -080023 // Must appear before the static IPv4Address constant assignments
24 private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
25
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070026 private final static long NONE_VAL1 = 0x0L;
27 private final static long NONE_VAL2 = 0x0L;
28 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
29
Gregor Maier5615b6c2013-12-11 22:29:07 -080030
Yotam Harchola289d552013-09-16 10:10:40 -070031 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
32 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070033
Yotam Harchola289d552013-09-16 10:10:40 -070034 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070035 this.raw1 = raw1;
36 this.raw2 = raw2;
37 }
38
Yotam Harchol4d634682013-09-26 13:21:06 -070039 @Override
Yotam Harcholeb023dc2013-09-26 15:45:44 -070040 public IPVersion getIpVersion() {
41 return IPVersion.IPv6;
Yotam Harchol4d634682013-09-26 13:21:06 -070042 }
43
Gregor Maier7f987e62013-12-10 19:34:18 -080044
Gregor Maier5615b6c2013-12-11 22:29:07 -080045 private int computeCidrMask64(long raw) {
46 long mask = raw;
47 if (raw == 0)
48 return 0;
Gregor Maier1acb4502013-12-12 11:25:07 -080049 else if (Long.bitCount((~mask) + 1) == 1) {
Gregor Maier5615b6c2013-12-11 22:29:07 -080050 // represent a true CIDR prefix length
51 return Long.bitCount(mask);
52 }
53 else {
54 // Not a true prefix
55 return NOT_A_CIDR_MASK;
56 }
57 }
58
59 private int asCidrMaskLengthInternal() {
60 if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
Gregor Maier1acb4502013-12-12 11:25:07 -080061 // No synchronization needed. Writing cidrMaskLengthCache only once
62 if (raw1 == 0 && raw2 == 0) {
63 cidrMaskLengthCache = 0;
64 } else if (raw1 == -1L) {
65 // top half is all 1 bits
66 int tmpLength = computeCidrMask64(raw2);
67 if (tmpLength != NOT_A_CIDR_MASK)
68 tmpLength += 64;
69 cidrMaskLengthCache = tmpLength;
70 } else if (raw2 == 0) {
71 cidrMaskLengthCache = computeCidrMask64(raw1);
72 } else {
73 cidrMaskLengthCache = NOT_A_CIDR_MASK;
Gregor Maier5615b6c2013-12-11 22:29:07 -080074 }
75 }
76 return cidrMaskLengthCache;
77 }
78
79 @Override
80 public boolean isCidrMask() {
81 return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
82 }
83
Gregor Maier7f987e62013-12-10 19:34:18 -080084 @Override
85 public int asCidrMaskLength() {
Gregor Maier5615b6c2013-12-11 22:29:07 -080086 if (!isCidrMask()) {
87 throw new IllegalStateException("IP is not a valid CIDR prefix " +
88 "mask " + toString());
Gregor Maier7f987e62013-12-10 19:34:18 -080089 } else {
Gregor Maier5615b6c2013-12-11 22:29:07 -080090 return asCidrMaskLengthInternal();
Gregor Maier7f987e62013-12-10 19:34:18 -080091 }
92 }
93
Yotam Harchola289d552013-09-16 10:10:40 -070094 public static IPv6Address of(final byte[] address) {
Gregor Maier1ff55972013-12-11 02:22:56 -080095 if (address == null) {
96 throw new NullPointerException("Address must not be null");
97 }
Yotam Harcholf3f11152013-09-05 16:47:16 -070098 if (address.length != LENGTH) {
99 throw new IllegalArgumentException(
Andreas Wundsamc85b5c52013-09-24 13:01:43 -0700100 "Invalid byte array length for IPv6 address: " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700101 }
102
103 long raw1 =
104 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
105 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
106 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
107 | (address[6] & 0xFFL) << 8 | (address[7]);
108
109 long raw2 =
110 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
111 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
112 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
113 | (address[14] & 0xFFL) << 8 | (address[15]);
114
Yotam Harchola289d552013-09-16 10:10:40 -0700115 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700116 }
117
118 private static class IPv6Builder {
119 private long raw1, raw2;
120
121 public void setUnsignedShortWord(final int i, final int value) {
122 int shift = 48 - (i % 4) * 16;
123
124 if (value < 0 || value > 0xFFFF)
125 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
126
127 if (i >= 0 && i < 4)
128 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
129 else if (i >= 4 && i < 8)
130 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
131 else
132 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
133 }
134
Yotam Harchola289d552013-09-16 10:10:40 -0700135 public IPv6Address getIPv6() {
136 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700137 }
138 }
139
140 private final static Pattern colonPattern = Pattern.compile(":");
141
Yotam Harchola289d552013-09-16 10:10:40 -0700142 public static IPv6Address of(final String string) {
Gregor Maier1ff55972013-12-11 02:22:56 -0800143 if (string == null) {
144 throw new NullPointerException("String must not be null");
145 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700146 IPv6Builder builder = new IPv6Builder();
147 String[] parts = colonPattern.split(string, -1);
148
149 int leftWord = 0;
150 int leftIndex = 0;
151
152 boolean hitZeroCompression = false;
153
154 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
155 String part = parts[leftIndex];
156 if (part.length() == 0) {
157 // hit empty group of zero compression
158 hitZeroCompression = true;
159 break;
160 }
161 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
162 }
163
164 if (hitZeroCompression) {
165 if (leftIndex == 0) {
166 // if colon is at the start, two columns must be at the start,
167 // move to the second empty group
168 leftIndex = 1;
169 if (parts.length < 2 || parts[1].length() > 0)
170 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
171 }
172
173 int rightWord = 7;
174 int rightIndex;
175 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
176 String part = parts[rightIndex];
177 if (part.length() == 0)
178 break;
179 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
180 }
181 if (rightIndex == parts.length - 1) {
182 // if colon is at the end, two columns must be at the end, move
183 // to the second empty group
184 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
185 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
186 rightIndex--;
187 }
188 if (leftIndex != rightIndex)
189 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
190 } else {
191 if (leftIndex != 8) {
192 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
193 }
194 }
195 return builder.getIPv6();
196 }
197
Yotam Harchola289d552013-09-16 10:10:40 -0700198 public static IPv6Address of(final long raw1, final long raw2) {
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -0700199 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
200 return NONE;
Yotam Harchola289d552013-09-16 10:10:40 -0700201 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700202 }
203
204 volatile byte[] bytesCache = null;
205
206 public byte[] getBytes() {
207 if (bytesCache == null) {
208 synchronized (this) {
209 if (bytesCache == null) {
210 bytesCache =
211 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
212 (byte) ((raw1 >> 48) & 0xFF),
213 (byte) ((raw1 >> 40) & 0xFF),
214 (byte) ((raw1 >> 32) & 0xFF),
215 (byte) ((raw1 >> 24) & 0xFF),
216 (byte) ((raw1 >> 16) & 0xFF),
217 (byte) ((raw1 >> 8) & 0xFF),
218 (byte) ((raw1 >> 0) & 0xFF),
219
220 (byte) ((raw2 >> 56) & 0xFF),
221 (byte) ((raw2 >> 48) & 0xFF),
222 (byte) ((raw2 >> 40) & 0xFF),
223 (byte) ((raw2 >> 32) & 0xFF),
224 (byte) ((raw2 >> 24) & 0xFF),
225 (byte) ((raw2 >> 16) & 0xFF),
226 (byte) ((raw2 >> 8) & 0xFF),
227 (byte) ((raw2 >> 0) & 0xFF) };
228 }
229 }
230 }
231 return bytesCache;
232 }
233
234 @Override
235 public int getLength() {
236 return LENGTH;
237 }
238
239 @Override
240 public String toString() {
241 return toString(true, false);
242 }
243
244 public int getUnsignedShortWord(final int i) {
245 if (i >= 0 && i < 4)
246 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
247 else if (i >= 4 && i < 8)
248 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
249 else
250 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
251 }
252
253 /** get the index of the first word where to apply IPv6 zero compression */
254 public int getZeroCompressStart() {
255 int start = Integer.MAX_VALUE;
256 int maxLength = -1;
257
258 int candidateStart = -1;
259
260 for (int i = 0; i < 8; i++) {
261 if (candidateStart >= 0) {
262 // in a zero octect
263 if (getUnsignedShortWord(i) != 0) {
264 // end of this candidate word
265 int candidateLength = i - candidateStart;
266 if (candidateLength >= maxLength) {
267 start = candidateStart;
268 maxLength = candidateLength;
269 }
270 candidateStart = -1;
271 }
272 } else {
273 // not in a zero octect
274 if (getUnsignedShortWord(i) == 0) {
275 candidateStart = i;
276 }
277 }
278 }
279
280 if (candidateStart >= 0) {
281 int candidateLength = 8 - candidateStart;
282 if (candidateLength >= maxLength) {
283 start = candidateStart;
284 maxLength = candidateLength;
285 }
286 }
287
288 return start;
289 }
290
291 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
292 StringBuilder res = new StringBuilder();
293
294 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
295 boolean inCompression = false;
296 boolean colonNeeded = false;
297
298 for (int i = 0; i < 8; i++) {
299 int word = getUnsignedShortWord(i);
300
301 if (word == 0) {
302 if (inCompression)
303 continue;
304 else if (i == compressionStart) {
305 res.append(':').append(':');
306 inCompression = true;
307 colonNeeded = false;
308 continue;
309 }
310 } else {
311 inCompression = false;
312 }
313
314 if (colonNeeded) {
315 res.append(':');
316 colonNeeded = false;
317 }
318
319 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
320 16));
321 colonNeeded = true;
322 }
323 return res.toString();
324 }
325
326 @Override
327 public int hashCode() {
328 final int prime = 31;
329 int result = 1;
330 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
331 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
332 return result;
333 }
334
335 @Override
336 public boolean equals(final Object obj) {
337 if (this == obj)
338 return true;
339 if (obj == null)
340 return false;
341 if (getClass() != obj.getClass())
342 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700343 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700344 if (raw1 != other.raw1)
345 return false;
346 if (raw2 != other.raw2)
347 return false;
348 return true;
349 }
350
351 public void write16Bytes(ChannelBuffer c) {
352 c.writeLong(this.raw1);
353 c.writeLong(this.raw2);
354 }
355
Yotam Harchola289d552013-09-16 10:10:40 -0700356 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
357 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700358 }
359
360 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700361 public IPv6Address applyMask(IPv6Address mask) {
362 return IPv6Address.of(this.raw1 & mask.raw1, this.raw2 & mask.raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700363 }
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700364
365 @Override
366 public int compareTo(IPv6Address o) {
367 int res = Longs.compare(raw1, o.raw1);
368 if(res != 0)
369 return res;
370 else
371 return Longs.compare(raw2, o.raw2);
372 }
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700373
374 @Override
375 public void putTo(PrimitiveSink sink) {
376 sink.putLong(raw1);
377 sink.putLong(raw2);
378 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700379}