blob: ea71c65cca6c27329a919258336fd3a1d7178d97 [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
8/**
9 * IPv6 address object. Instance controlled, immutable. Internal representation:
10 * two 64 bit longs (not that you'd have to know).
11 *
12 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
13 */
Yotam Harchola289d552013-09-16 10:10:40 -070014public class IPv6Address implements OFValueType<IPv6Address> {
Yotam Harcholf3f11152013-09-05 16:47:16 -070015 static final int LENGTH = 16;
16 private final long raw1;
17 private final long raw2;
18
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070019 private final static long NONE_VAL1 = 0x0L;
20 private final static long NONE_VAL2 = 0x0L;
21 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
22
Yotam Harchola289d552013-09-16 10:10:40 -070023 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
24 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070025
Yotam Harchola289d552013-09-16 10:10:40 -070026 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070027 this.raw1 = raw1;
28 this.raw2 = raw2;
29 }
30
Yotam Harchola289d552013-09-16 10:10:40 -070031 public static IPv6Address of(final byte[] address) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070032 if (address.length != LENGTH) {
33 throw new IllegalArgumentException(
34 "Invalid byte array length for IPv6 address: " + address);
35 }
36
37 long raw1 =
38 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
39 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
40 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
41 | (address[6] & 0xFFL) << 8 | (address[7]);
42
43 long raw2 =
44 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
45 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
46 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
47 | (address[14] & 0xFFL) << 8 | (address[15]);
48
Yotam Harchola289d552013-09-16 10:10:40 -070049 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -070050 }
51
52 private static class IPv6Builder {
53 private long raw1, raw2;
54
55 public void setUnsignedShortWord(final int i, final int value) {
56 int shift = 48 - (i % 4) * 16;
57
58 if (value < 0 || value > 0xFFFF)
59 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
60
61 if (i >= 0 && i < 4)
62 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
63 else if (i >= 4 && i < 8)
64 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
65 else
66 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
67 }
68
Yotam Harchola289d552013-09-16 10:10:40 -070069 public IPv6Address getIPv6() {
70 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -070071 }
72 }
73
74 private final static Pattern colonPattern = Pattern.compile(":");
75
Yotam Harchola289d552013-09-16 10:10:40 -070076 public static IPv6Address of(final String string) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070077 IPv6Builder builder = new IPv6Builder();
78 String[] parts = colonPattern.split(string, -1);
79
80 int leftWord = 0;
81 int leftIndex = 0;
82
83 boolean hitZeroCompression = false;
84
85 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
86 String part = parts[leftIndex];
87 if (part.length() == 0) {
88 // hit empty group of zero compression
89 hitZeroCompression = true;
90 break;
91 }
92 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
93 }
94
95 if (hitZeroCompression) {
96 if (leftIndex == 0) {
97 // if colon is at the start, two columns must be at the start,
98 // move to the second empty group
99 leftIndex = 1;
100 if (parts.length < 2 || parts[1].length() > 0)
101 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
102 }
103
104 int rightWord = 7;
105 int rightIndex;
106 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
107 String part = parts[rightIndex];
108 if (part.length() == 0)
109 break;
110 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
111 }
112 if (rightIndex == parts.length - 1) {
113 // if colon is at the end, two columns must be at the end, move
114 // to the second empty group
115 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
116 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
117 rightIndex--;
118 }
119 if (leftIndex != rightIndex)
120 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
121 } else {
122 if (leftIndex != 8) {
123 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
124 }
125 }
126 return builder.getIPv6();
127 }
128
Yotam Harchola289d552013-09-16 10:10:40 -0700129 public static IPv6Address of(final long raw1, final long raw2) {
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -0700130 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
131 return NONE;
Yotam Harchola289d552013-09-16 10:10:40 -0700132 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700133 }
134
135 volatile byte[] bytesCache = null;
136
137 public byte[] getBytes() {
138 if (bytesCache == null) {
139 synchronized (this) {
140 if (bytesCache == null) {
141 bytesCache =
142 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
143 (byte) ((raw1 >> 48) & 0xFF),
144 (byte) ((raw1 >> 40) & 0xFF),
145 (byte) ((raw1 >> 32) & 0xFF),
146 (byte) ((raw1 >> 24) & 0xFF),
147 (byte) ((raw1 >> 16) & 0xFF),
148 (byte) ((raw1 >> 8) & 0xFF),
149 (byte) ((raw1 >> 0) & 0xFF),
150
151 (byte) ((raw2 >> 56) & 0xFF),
152 (byte) ((raw2 >> 48) & 0xFF),
153 (byte) ((raw2 >> 40) & 0xFF),
154 (byte) ((raw2 >> 32) & 0xFF),
155 (byte) ((raw2 >> 24) & 0xFF),
156 (byte) ((raw2 >> 16) & 0xFF),
157 (byte) ((raw2 >> 8) & 0xFF),
158 (byte) ((raw2 >> 0) & 0xFF) };
159 }
160 }
161 }
162 return bytesCache;
163 }
164
165 @Override
166 public int getLength() {
167 return LENGTH;
168 }
169
170 @Override
171 public String toString() {
172 return toString(true, false);
173 }
174
175 public int getUnsignedShortWord(final int i) {
176 if (i >= 0 && i < 4)
177 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
178 else if (i >= 4 && i < 8)
179 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
180 else
181 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
182 }
183
184 /** get the index of the first word where to apply IPv6 zero compression */
185 public int getZeroCompressStart() {
186 int start = Integer.MAX_VALUE;
187 int maxLength = -1;
188
189 int candidateStart = -1;
190
191 for (int i = 0; i < 8; i++) {
192 if (candidateStart >= 0) {
193 // in a zero octect
194 if (getUnsignedShortWord(i) != 0) {
195 // end of this candidate word
196 int candidateLength = i - candidateStart;
197 if (candidateLength >= maxLength) {
198 start = candidateStart;
199 maxLength = candidateLength;
200 }
201 candidateStart = -1;
202 }
203 } else {
204 // not in a zero octect
205 if (getUnsignedShortWord(i) == 0) {
206 candidateStart = i;
207 }
208 }
209 }
210
211 if (candidateStart >= 0) {
212 int candidateLength = 8 - candidateStart;
213 if (candidateLength >= maxLength) {
214 start = candidateStart;
215 maxLength = candidateLength;
216 }
217 }
218
219 return start;
220 }
221
222 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
223 StringBuilder res = new StringBuilder();
224
225 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
226 boolean inCompression = false;
227 boolean colonNeeded = false;
228
229 for (int i = 0; i < 8; i++) {
230 int word = getUnsignedShortWord(i);
231
232 if (word == 0) {
233 if (inCompression)
234 continue;
235 else if (i == compressionStart) {
236 res.append(':').append(':');
237 inCompression = true;
238 colonNeeded = false;
239 continue;
240 }
241 } else {
242 inCompression = false;
243 }
244
245 if (colonNeeded) {
246 res.append(':');
247 colonNeeded = false;
248 }
249
250 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
251 16));
252 colonNeeded = true;
253 }
254 return res.toString();
255 }
256
257 @Override
258 public int hashCode() {
259 final int prime = 31;
260 int result = 1;
261 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
262 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
263 return result;
264 }
265
266 @Override
267 public boolean equals(final Object obj) {
268 if (this == obj)
269 return true;
270 if (obj == null)
271 return false;
272 if (getClass() != obj.getClass())
273 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700274 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700275 if (raw1 != other.raw1)
276 return false;
277 if (raw2 != other.raw2)
278 return false;
279 return true;
280 }
281
282 public void write16Bytes(ChannelBuffer c) {
283 c.writeLong(this.raw1);
284 c.writeLong(this.raw2);
285 }
286
Yotam Harchola289d552013-09-16 10:10:40 -0700287 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
288 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700289 }
290
291 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700292 public IPv6Address applyMask(IPv6Address mask) {
293 return IPv6Address.of(this.raw1 & mask.raw1, this.raw2 & mask.raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700294 }
295}