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