blob: 001f18574d99b5395e7d3f2f6fbf4d7c829c863e [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
Yotam Harchola289d552013-09-16 10:10:40 -070019 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
20 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070021
Yotam Harchola289d552013-09-16 10:10:40 -070022 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070023 this.raw1 = raw1;
24 this.raw2 = raw2;
25 }
26
Yotam Harchola289d552013-09-16 10:10:40 -070027 public static IPv6Address of(final byte[] address) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070028 if (address.length != LENGTH) {
29 throw new IllegalArgumentException(
30 "Invalid byte array length for IPv6 address: " + address);
31 }
32
33 long raw1 =
34 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
35 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
36 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
37 | (address[6] & 0xFFL) << 8 | (address[7]);
38
39 long raw2 =
40 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
41 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
42 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
43 | (address[14] & 0xFFL) << 8 | (address[15]);
44
Yotam Harchola289d552013-09-16 10:10:40 -070045 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -070046 }
47
48 private static class IPv6Builder {
49 private long raw1, raw2;
50
51 public void setUnsignedShortWord(final int i, final int value) {
52 int shift = 48 - (i % 4) * 16;
53
54 if (value < 0 || value > 0xFFFF)
55 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
56
57 if (i >= 0 && i < 4)
58 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
59 else if (i >= 4 && i < 8)
60 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
61 else
62 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
63 }
64
Yotam Harchola289d552013-09-16 10:10:40 -070065 public IPv6Address getIPv6() {
66 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -070067 }
68 }
69
70 private final static Pattern colonPattern = Pattern.compile(":");
71
Yotam Harchola289d552013-09-16 10:10:40 -070072 public static IPv6Address of(final String string) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070073 IPv6Builder builder = new IPv6Builder();
74 String[] parts = colonPattern.split(string, -1);
75
76 int leftWord = 0;
77 int leftIndex = 0;
78
79 boolean hitZeroCompression = false;
80
81 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
82 String part = parts[leftIndex];
83 if (part.length() == 0) {
84 // hit empty group of zero compression
85 hitZeroCompression = true;
86 break;
87 }
88 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
89 }
90
91 if (hitZeroCompression) {
92 if (leftIndex == 0) {
93 // if colon is at the start, two columns must be at the start,
94 // move to the second empty group
95 leftIndex = 1;
96 if (parts.length < 2 || parts[1].length() > 0)
97 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
98 }
99
100 int rightWord = 7;
101 int rightIndex;
102 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
103 String part = parts[rightIndex];
104 if (part.length() == 0)
105 break;
106 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
107 }
108 if (rightIndex == parts.length - 1) {
109 // if colon is at the end, two columns must be at the end, move
110 // to the second empty group
111 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
112 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
113 rightIndex--;
114 }
115 if (leftIndex != rightIndex)
116 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
117 } else {
118 if (leftIndex != 8) {
119 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
120 }
121 }
122 return builder.getIPv6();
123 }
124
Yotam Harchola289d552013-09-16 10:10:40 -0700125 public static IPv6Address of(final long raw1, final long raw2) {
126 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700127 }
128
129 volatile byte[] bytesCache = null;
130
131 public byte[] getBytes() {
132 if (bytesCache == null) {
133 synchronized (this) {
134 if (bytesCache == null) {
135 bytesCache =
136 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
137 (byte) ((raw1 >> 48) & 0xFF),
138 (byte) ((raw1 >> 40) & 0xFF),
139 (byte) ((raw1 >> 32) & 0xFF),
140 (byte) ((raw1 >> 24) & 0xFF),
141 (byte) ((raw1 >> 16) & 0xFF),
142 (byte) ((raw1 >> 8) & 0xFF),
143 (byte) ((raw1 >> 0) & 0xFF),
144
145 (byte) ((raw2 >> 56) & 0xFF),
146 (byte) ((raw2 >> 48) & 0xFF),
147 (byte) ((raw2 >> 40) & 0xFF),
148 (byte) ((raw2 >> 32) & 0xFF),
149 (byte) ((raw2 >> 24) & 0xFF),
150 (byte) ((raw2 >> 16) & 0xFF),
151 (byte) ((raw2 >> 8) & 0xFF),
152 (byte) ((raw2 >> 0) & 0xFF) };
153 }
154 }
155 }
156 return bytesCache;
157 }
158
159 @Override
160 public int getLength() {
161 return LENGTH;
162 }
163
164 @Override
165 public String toString() {
166 return toString(true, false);
167 }
168
169 public int getUnsignedShortWord(final int i) {
170 if (i >= 0 && i < 4)
171 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
172 else if (i >= 4 && i < 8)
173 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
174 else
175 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
176 }
177
178 /** get the index of the first word where to apply IPv6 zero compression */
179 public int getZeroCompressStart() {
180 int start = Integer.MAX_VALUE;
181 int maxLength = -1;
182
183 int candidateStart = -1;
184
185 for (int i = 0; i < 8; i++) {
186 if (candidateStart >= 0) {
187 // in a zero octect
188 if (getUnsignedShortWord(i) != 0) {
189 // end of this candidate word
190 int candidateLength = i - candidateStart;
191 if (candidateLength >= maxLength) {
192 start = candidateStart;
193 maxLength = candidateLength;
194 }
195 candidateStart = -1;
196 }
197 } else {
198 // not in a zero octect
199 if (getUnsignedShortWord(i) == 0) {
200 candidateStart = i;
201 }
202 }
203 }
204
205 if (candidateStart >= 0) {
206 int candidateLength = 8 - candidateStart;
207 if (candidateLength >= maxLength) {
208 start = candidateStart;
209 maxLength = candidateLength;
210 }
211 }
212
213 return start;
214 }
215
216 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
217 StringBuilder res = new StringBuilder();
218
219 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
220 boolean inCompression = false;
221 boolean colonNeeded = false;
222
223 for (int i = 0; i < 8; i++) {
224 int word = getUnsignedShortWord(i);
225
226 if (word == 0) {
227 if (inCompression)
228 continue;
229 else if (i == compressionStart) {
230 res.append(':').append(':');
231 inCompression = true;
232 colonNeeded = false;
233 continue;
234 }
235 } else {
236 inCompression = false;
237 }
238
239 if (colonNeeded) {
240 res.append(':');
241 colonNeeded = false;
242 }
243
244 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
245 16));
246 colonNeeded = true;
247 }
248 return res.toString();
249 }
250
251 @Override
252 public int hashCode() {
253 final int prime = 31;
254 int result = 1;
255 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
256 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
257 return result;
258 }
259
260 @Override
261 public boolean equals(final Object obj) {
262 if (this == obj)
263 return true;
264 if (obj == null)
265 return false;
266 if (getClass() != obj.getClass())
267 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700268 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700269 if (raw1 != other.raw1)
270 return false;
271 if (raw2 != other.raw2)
272 return false;
273 return true;
274 }
275
276 public void write16Bytes(ChannelBuffer c) {
277 c.writeLong(this.raw1);
278 c.writeLong(this.raw2);
279 }
280
Yotam Harchola289d552013-09-16 10:10:40 -0700281 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
282 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700283 }
284
285 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700286 public IPv6Address applyMask(IPv6Address mask) {
287 return IPv6Address.of(this.raw1 & mask.raw1, this.raw2 & mask.raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700288 }
289}