blob: 1aad85b99f81dae54a2f64ad16285d5d3f45d5a3 [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;
7
Andreas Wundsam22ba3af2013-10-04 16:00:30 -07008import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -07009import com.google.common.primitives.Longs;
10
Yotam Harcholf3f11152013-09-05 16:47:16 -070011/**
12 * IPv6 address object. Instance controlled, immutable. Internal representation:
13 * two 64 bit longs (not that you'd have to know).
14 *
15 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
16 */
Yotam Harchol4d634682013-09-26 13:21:06 -070017public class IPv6Address extends IPAddress<IPv6Address> {
Yotam Harcholf3f11152013-09-05 16:47:16 -070018 static final int LENGTH = 16;
19 private final long raw1;
20 private final long raw2;
21
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070022 private final static long NONE_VAL1 = 0x0L;
23 private final static long NONE_VAL2 = 0x0L;
24 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
25
Yotam Harchola289d552013-09-16 10:10:40 -070026 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
27 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070028
Yotam Harchola289d552013-09-16 10:10:40 -070029 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070030 this.raw1 = raw1;
31 this.raw2 = raw2;
32 }
33
Yotam Harchol4d634682013-09-26 13:21:06 -070034 @Override
Yotam Harcholeb023dc2013-09-26 15:45:44 -070035 public IPVersion getIpVersion() {
36 return IPVersion.IPv6;
Yotam Harchol4d634682013-09-26 13:21:06 -070037 }
38
Yotam Harchola289d552013-09-16 10:10:40 -070039 public static IPv6Address of(final byte[] address) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070040 if (address.length != LENGTH) {
41 throw new IllegalArgumentException(
Andreas Wundsamc85b5c52013-09-24 13:01:43 -070042 "Invalid byte array length for IPv6 address: " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -070043 }
44
45 long raw1 =
46 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
47 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
48 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
49 | (address[6] & 0xFFL) << 8 | (address[7]);
50
51 long raw2 =
52 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
53 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
54 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
55 | (address[14] & 0xFFL) << 8 | (address[15]);
56
Yotam Harchola289d552013-09-16 10:10:40 -070057 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -070058 }
59
60 private static class IPv6Builder {
61 private long raw1, raw2;
62
63 public void setUnsignedShortWord(final int i, final int value) {
64 int shift = 48 - (i % 4) * 16;
65
66 if (value < 0 || value > 0xFFFF)
67 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
68
69 if (i >= 0 && i < 4)
70 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
71 else if (i >= 4 && i < 8)
72 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
73 else
74 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
75 }
76
Yotam Harchola289d552013-09-16 10:10:40 -070077 public IPv6Address getIPv6() {
78 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -070079 }
80 }
81
82 private final static Pattern colonPattern = Pattern.compile(":");
83
Yotam Harchola289d552013-09-16 10:10:40 -070084 public static IPv6Address of(final String string) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070085 IPv6Builder builder = new IPv6Builder();
86 String[] parts = colonPattern.split(string, -1);
87
88 int leftWord = 0;
89 int leftIndex = 0;
90
91 boolean hitZeroCompression = false;
92
93 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
94 String part = parts[leftIndex];
95 if (part.length() == 0) {
96 // hit empty group of zero compression
97 hitZeroCompression = true;
98 break;
99 }
100 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
101 }
102
103 if (hitZeroCompression) {
104 if (leftIndex == 0) {
105 // if colon is at the start, two columns must be at the start,
106 // move to the second empty group
107 leftIndex = 1;
108 if (parts.length < 2 || parts[1].length() > 0)
109 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
110 }
111
112 int rightWord = 7;
113 int rightIndex;
114 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
115 String part = parts[rightIndex];
116 if (part.length() == 0)
117 break;
118 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
119 }
120 if (rightIndex == parts.length - 1) {
121 // if colon is at the end, two columns must be at the end, move
122 // to the second empty group
123 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
124 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
125 rightIndex--;
126 }
127 if (leftIndex != rightIndex)
128 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
129 } else {
130 if (leftIndex != 8) {
131 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
132 }
133 }
134 return builder.getIPv6();
135 }
136
Yotam Harchola289d552013-09-16 10:10:40 -0700137 public static IPv6Address of(final long raw1, final long raw2) {
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -0700138 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
139 return NONE;
Yotam Harchola289d552013-09-16 10:10:40 -0700140 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700141 }
142
143 volatile byte[] bytesCache = null;
144
145 public byte[] getBytes() {
146 if (bytesCache == null) {
147 synchronized (this) {
148 if (bytesCache == null) {
149 bytesCache =
150 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
151 (byte) ((raw1 >> 48) & 0xFF),
152 (byte) ((raw1 >> 40) & 0xFF),
153 (byte) ((raw1 >> 32) & 0xFF),
154 (byte) ((raw1 >> 24) & 0xFF),
155 (byte) ((raw1 >> 16) & 0xFF),
156 (byte) ((raw1 >> 8) & 0xFF),
157 (byte) ((raw1 >> 0) & 0xFF),
158
159 (byte) ((raw2 >> 56) & 0xFF),
160 (byte) ((raw2 >> 48) & 0xFF),
161 (byte) ((raw2 >> 40) & 0xFF),
162 (byte) ((raw2 >> 32) & 0xFF),
163 (byte) ((raw2 >> 24) & 0xFF),
164 (byte) ((raw2 >> 16) & 0xFF),
165 (byte) ((raw2 >> 8) & 0xFF),
166 (byte) ((raw2 >> 0) & 0xFF) };
167 }
168 }
169 }
170 return bytesCache;
171 }
172
173 @Override
174 public int getLength() {
175 return LENGTH;
176 }
177
178 @Override
179 public String toString() {
180 return toString(true, false);
181 }
182
183 public int getUnsignedShortWord(final int i) {
184 if (i >= 0 && i < 4)
185 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
186 else if (i >= 4 && i < 8)
187 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
188 else
189 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
190 }
191
192 /** get the index of the first word where to apply IPv6 zero compression */
193 public int getZeroCompressStart() {
194 int start = Integer.MAX_VALUE;
195 int maxLength = -1;
196
197 int candidateStart = -1;
198
199 for (int i = 0; i < 8; i++) {
200 if (candidateStart >= 0) {
201 // in a zero octect
202 if (getUnsignedShortWord(i) != 0) {
203 // end of this candidate word
204 int candidateLength = i - candidateStart;
205 if (candidateLength >= maxLength) {
206 start = candidateStart;
207 maxLength = candidateLength;
208 }
209 candidateStart = -1;
210 }
211 } else {
212 // not in a zero octect
213 if (getUnsignedShortWord(i) == 0) {
214 candidateStart = i;
215 }
216 }
217 }
218
219 if (candidateStart >= 0) {
220 int candidateLength = 8 - candidateStart;
221 if (candidateLength >= maxLength) {
222 start = candidateStart;
223 maxLength = candidateLength;
224 }
225 }
226
227 return start;
228 }
229
230 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
231 StringBuilder res = new StringBuilder();
232
233 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
234 boolean inCompression = false;
235 boolean colonNeeded = false;
236
237 for (int i = 0; i < 8; i++) {
238 int word = getUnsignedShortWord(i);
239
240 if (word == 0) {
241 if (inCompression)
242 continue;
243 else if (i == compressionStart) {
244 res.append(':').append(':');
245 inCompression = true;
246 colonNeeded = false;
247 continue;
248 }
249 } else {
250 inCompression = false;
251 }
252
253 if (colonNeeded) {
254 res.append(':');
255 colonNeeded = false;
256 }
257
258 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
259 16));
260 colonNeeded = true;
261 }
262 return res.toString();
263 }
264
265 @Override
266 public int hashCode() {
267 final int prime = 31;
268 int result = 1;
269 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
270 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
271 return result;
272 }
273
274 @Override
275 public boolean equals(final Object obj) {
276 if (this == obj)
277 return true;
278 if (obj == null)
279 return false;
280 if (getClass() != obj.getClass())
281 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700282 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700283 if (raw1 != other.raw1)
284 return false;
285 if (raw2 != other.raw2)
286 return false;
287 return true;
288 }
289
290 public void write16Bytes(ChannelBuffer c) {
291 c.writeLong(this.raw1);
292 c.writeLong(this.raw2);
293 }
294
Yotam Harchola289d552013-09-16 10:10:40 -0700295 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
296 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700297 }
298
299 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700300 public IPv6Address applyMask(IPv6Address mask) {
301 return IPv6Address.of(this.raw1 & mask.raw1, this.raw2 & mask.raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700302 }
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700303
304 @Override
305 public int compareTo(IPv6Address o) {
306 int res = Longs.compare(raw1, o.raw1);
307 if(res != 0)
308 return res;
309 else
310 return Longs.compare(raw2, o.raw2);
311 }
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700312
313 @Override
314 public void putTo(PrimitiveSink sink) {
315 sink.putLong(raw1);
316 sink.putLong(raw2);
317 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700318}