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