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