blob: e88998d78b6e8ac7fa6a1d910c4e2a79abef5ac7 [file] [log] [blame]
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080017package org.onlab.packet;
18
Charles M.C. Chan94f37372015-01-10 17:53:42 +080019import org.onlab.packet.ipv6.Authentication;
20import org.onlab.packet.ipv6.DestinationOptions;
21import org.onlab.packet.ipv6.EncapSecurityPayload;
22import org.onlab.packet.ipv6.Fragment;
Charles M.C. Chan94f37372015-01-10 17:53:42 +080023import org.onlab.packet.ipv6.HopByHopOptions;
Jonathan Hart2a655752015-04-07 16:46:33 -070024import org.onlab.packet.ipv6.IExtensionHeader;
Charles M.C. Chan94f37372015-01-10 17:53:42 +080025import org.onlab.packet.ipv6.Routing;
Jonathan Hart2a655752015-04-07 16:46:33 -070026
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080027import java.nio.ByteBuffer;
28import java.util.Arrays;
29import java.util.HashMap;
30import java.util.Map;
31
Jian Li5fc14292015-12-04 11:30:46 -080032import static com.google.common.base.MoreObjects.toStringHelper;
Pier Luigi37b687b2017-01-16 22:46:33 -080033import static com.google.common.base.Preconditions.checkArgument;
Jonathan Hart2a655752015-04-07 16:46:33 -070034import static org.onlab.packet.PacketUtils.checkInput;
35
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080036/**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080037 * Implements IPv6 packet format. (RFC 2460)
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080038 */
Jian Li1270aea2016-09-15 13:32:24 +090039public class IPv6 extends IP implements IExtensionHeader {
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080040 public static final byte FIXED_HEADER_LENGTH = 40; // bytes
41
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080042 public static final byte PROTOCOL_TCP = 0x6;
43 public static final byte PROTOCOL_UDP = 0x11;
44 public static final byte PROTOCOL_ICMP6 = 0x3A;
Charles M.C. Chan94f37372015-01-10 17:53:42 +080045 public static final byte PROTOCOL_HOPOPT = 0x00;
46 public static final byte PROTOCOL_ROUTING = 0x2B;
47 public static final byte PROTOCOL_FRAG = 0x2C;
48 public static final byte PROTOCOL_ESP = 0x32;
49 public static final byte PROTOCOL_AH = 0x33;
50 public static final byte PROTOCOL_DSTOPT = 0x3C;
51
Pier Luigi37b687b2017-01-16 22:46:33 -080052 public static final byte LINK_LOCAL_0 = (byte) 0xfe;
53 public static final byte LINK_LOCAL_1 = (byte) 0x80;
Charles M.C. Chan94f37372015-01-10 17:53:42 +080054
Jonathan Hart2a655752015-04-07 16:46:33 -070055 public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080056 new HashMap<>();
57
58 static {
Jonathan Hart2a655752015-04-07 16:46:33 -070059 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ICMP6, ICMP6.deserializer());
60 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_TCP, TCP.deserializer());
61 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_UDP, UDP.deserializer());
62 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_HOPOPT, HopByHopOptions.deserializer());
63 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ROUTING, Routing.deserializer());
64 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_FRAG, Fragment.deserializer());
65 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ESP, EncapSecurityPayload.deserializer());
66 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_AH, Authentication.deserializer());
67 IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_DSTOPT, DestinationOptions.deserializer());
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080068 }
69
70 protected byte version;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080071 protected byte trafficClass;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080072 protected int flowLabel;
73 protected short payloadLength;
74 protected byte nextHeader;
75 protected byte hopLimit;
76 protected byte[] sourceAddress = new byte[Ip6Address.BYTE_LENGTH];
77 protected byte[] destinationAddress = new byte[Ip6Address.BYTE_LENGTH];
78
79 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080080 * Default constructor that sets the version to 6.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080081 */
82 public IPv6() {
83 super();
84 this.version = 6;
85 }
86
Jian Li1270aea2016-09-15 13:32:24 +090087 @Override
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080088 public byte getVersion() {
89 return this.version;
90 }
91
Jian Li1270aea2016-09-15 13:32:24 +090092 @Override
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080093 public IPv6 setVersion(final byte version) {
94 this.version = version;
95 return this;
96 }
97
98 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080099 * Gets traffic class.
100 *
101 * @return the traffic class
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800102 */
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800103 public byte getTrafficClass() {
104 return this.trafficClass;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800105 }
106
107 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800108 * Sets traffic class.
109 *
110 * @param trafficClass the traffic class to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800111 * @return this
112 */
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800113 public IPv6 setTrafficClass(final byte trafficClass) {
114 this.trafficClass = trafficClass;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800115 return this;
116 }
117
118 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800119 * Gets flow label.
120 *
121 * @return the flow label
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800122 */
123 public int getFlowLabel() {
124 return this.flowLabel;
125 }
126
127 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800128 * Sets flow label.
129 *
130 * @param flowLabel the flow label to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800131 * @return this
132 */
133 public IPv6 setFlowLabel(final int flowLabel) {
134 this.flowLabel = flowLabel;
135 return this;
136 }
137
Charles M.C. Chan94f37372015-01-10 17:53:42 +0800138 @Override
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800139 public byte getNextHeader() {
140 return this.nextHeader;
141 }
142
Charles M.C. Chan94f37372015-01-10 17:53:42 +0800143 @Override
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800144 public IPv6 setNextHeader(final byte nextHeader) {
145 this.nextHeader = nextHeader;
146 return this;
147 }
148
149 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800150 * Gets hop limit.
151 *
152 * @return the hop limit
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800153 */
154 public byte getHopLimit() {
155 return this.hopLimit;
156 }
157
158 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800159 * Sets hop limit.
160 *
161 * @param hopLimit the hop limit to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800162 * @return this
163 */
164 public IPv6 setHopLimit(final byte hopLimit) {
165 this.hopLimit = hopLimit;
166 return this;
167 }
168
169 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800170 * Gets source address.
171 *
172 * @return the IPv6 source address
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800173 */
174 public byte[] getSourceAddress() {
175 return this.sourceAddress;
176 }
177
178 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800179 * Sets source address.
180 *
181 * @param sourceAddress the IPv6 source address to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800182 * @return this
183 */
184 public IPv6 setSourceAddress(final byte[] sourceAddress) {
185 this.sourceAddress = Arrays.copyOfRange(sourceAddress, 0, Ip6Address.BYTE_LENGTH);
186 return this;
187 }
188
189 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800190 * Gets destination address.
191 *
192 * @return the IPv6 destination address
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800193 */
194 public byte[] getDestinationAddress() {
195 return this.destinationAddress;
196 }
197
198 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800199 * Sets destination address.
200 *
201 * @param destinationAddress the IPv6 destination address to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800202 * @return this
203 */
204 public IPv6 setDestinationAddress(final byte[] destinationAddress) {
205 this.destinationAddress = Arrays.copyOfRange(destinationAddress, 0, Ip6Address.BYTE_LENGTH);
206 return this;
207 }
208
209 @Override
210 public byte[] serialize() {
211 byte[] payloadData = null;
212 if (this.payload != null) {
213 this.payload.setParent(this);
214 payloadData = this.payload.serialize();
215 }
216
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800217 this.payloadLength = 0;
218 if (payloadData != null) {
219 this.payloadLength = (short) payloadData.length;
220 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800221
222 final byte[] data = new byte[FIXED_HEADER_LENGTH + payloadLength];
223 final ByteBuffer bb = ByteBuffer.wrap(data);
224
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800225 bb.putInt((this.version & 0xf) << 28 | (this.trafficClass & 0xff) << 20 | this.flowLabel & 0xfffff);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800226 bb.putShort(this.payloadLength);
227 bb.put(this.nextHeader);
228 bb.put(this.hopLimit);
229 bb.put(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
230 bb.put(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
231
232 if (payloadData != null) {
233 bb.put(payloadData);
234 }
235
236 return data;
237 }
238
239 @Override
240 public IPacket deserialize(final byte[] data, final int offset,
241 final int length) {
242 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
243 int iscratch;
244
245 iscratch = bb.getInt();
246 this.version = (byte) (iscratch >> 28 & 0xf);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800247 this.trafficClass = (byte) (iscratch >> 20 & 0xff);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800248 this.flowLabel = iscratch & 0xfffff;
249 this.payloadLength = bb.getShort();
250 this.nextHeader = bb.get();
251 this.hopLimit = bb.get();
252 bb.get(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
253 bb.get(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
254
Jonathan Hart2a655752015-04-07 16:46:33 -0700255 Deserializer<? extends IPacket> deserializer;
256 if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
257 deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800258 } else {
Jonathan Hart2a655752015-04-07 16:46:33 -0700259 deserializer = Data.deserializer();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800260 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700261 try {
262 this.payload = deserializer.deserialize(data, bb.position(),
263 bb.limit() - bb.position());
264 this.payload.setParent(this);
265 } catch (DeserializationException e) {
266 return this;
267 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800268
269 return this;
270 }
271
272 /*
273 * (non-Javadoc)
274 *
275 * @see java.lang.Object#hashCode()
276 */
277 @Override
278 public int hashCode() {
279 final int prime = 2521;
280 int result = super.hashCode();
281 ByteBuffer bb;
282 bb = ByteBuffer.wrap(this.destinationAddress);
283 for (int i = 0; i < 4; i++) {
284 result = prime * result + bb.getInt();
285 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800286 result = prime * result + this.trafficClass;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800287 result = prime * result + this.flowLabel;
288 result = prime * result + this.hopLimit;
289 result = prime * result + this.nextHeader;
290 result = prime * result + this.payloadLength;
291 bb = ByteBuffer.wrap(this.sourceAddress);
292 for (int i = 0; i < 4; i++) {
293 result = prime * result + bb.getInt();
294 }
295 result = prime * result + this.version;
296 return result;
297 }
298
299 /*
300 * (non-Javadoc)
301 *
302 * @see java.lang.Object#equals(java.lang.Object)
303 */
304 @Override
305 public boolean equals(final Object obj) {
306 if (this == obj) {
307 return true;
308 }
309 if (!super.equals(obj)) {
310 return false;
311 }
312 if (!(obj instanceof IPv6)) {
313 return false;
314 }
315 final IPv6 other = (IPv6) obj;
316 if (!Arrays.equals(this.destinationAddress, other.destinationAddress)) {
317 return false;
318 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800319 if (this.trafficClass != other.trafficClass) {
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800320 return false;
321 }
322 if (this.flowLabel != other.flowLabel) {
323 return false;
324 }
325 if (this.hopLimit != other.hopLimit) {
326 return false;
327 }
328 if (this.nextHeader != other.nextHeader) {
329 return false;
330 }
331 if (this.payloadLength != other.payloadLength) {
332 return false;
333 }
334 if (!Arrays.equals(this.sourceAddress, other.sourceAddress)) {
335 return false;
336 }
337 return true;
338 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700339
340 /**
341 * Deserializer function for IPv6 packets.
342 *
343 * @return deserializer function
344 */
345 public static Deserializer<IPv6> deserializer() {
346 return (data, offset, length) -> {
347 checkInput(data, offset, length, FIXED_HEADER_LENGTH);
348
349 IPv6 ipv6 = new IPv6();
350
351 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
352
353 int iscratch = bb.getInt();
354
355 ipv6.version = (byte) (iscratch >> 28 & 0xf);
356 ipv6.trafficClass = (byte) (iscratch >> 20 & 0xff);
357 ipv6.flowLabel = iscratch & 0xfffff;
358 ipv6.payloadLength = bb.getShort();
359 ipv6.nextHeader = bb.get();
360 ipv6.hopLimit = bb.get();
361 bb.get(ipv6.sourceAddress, 0, Ip6Address.BYTE_LENGTH);
362 bb.get(ipv6.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
363
364 Deserializer<? extends IPacket> deserializer;
365 if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv6.nextHeader)) {
366 deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(ipv6.nextHeader);
367 } else {
368 deserializer = Data.deserializer();
369 }
370 ipv6.payload = deserializer.deserialize(data, bb.position(),
371 bb.limit() - bb.position());
372 ipv6.payload.setParent(ipv6);
373
374 return ipv6;
375 };
376 }
Jian Li5fc14292015-12-04 11:30:46 -0800377
378 @Override
379 public String toString() {
380 return toStringHelper(getClass())
381 .add("version", Byte.toString(version))
382 .add("trafficClass", Byte.toString(trafficClass))
383 .add("flowLabel", Integer.toString(flowLabel))
384 .add("payloadLength", Short.toString(payloadLength))
385 .add("nextHeader", Byte.toString(nextHeader))
386 .add("hopLimit", Byte.toString(hopLimit))
387 .add("sourceAddress", Arrays.toString(sourceAddress))
388 .add("destinationAddress", Arrays.toString(destinationAddress))
389 .toString();
390 }
Pier Ventre78e73f62016-12-02 19:59:28 -0800391
392 /**
393 * According to the RFC 4291, the solicitation node addresses are
394 * formed by taking the low-order 24 bits of an address (unicast or anycast)
395 * and appending those bits to the prefix FF02:0:0:0:0:1:FF00::/104.
396 *
397 * Solicited-Node Address: FF02:0:0:0:0:1:FFXX:XXXX
398 *
399 * @param targetIp the unicast or anycast address
400 * @return the computed solicitation node address
401 */
Pier Luigi37b687b2017-01-16 22:46:33 -0800402 public static byte[] getSolicitNodeAddress(byte[] targetIp) {
403 checkArgument(targetIp.length == Ip6Address.BYTE_LENGTH);
404 return new byte[] {
Pier Ventre78e73f62016-12-02 19:59:28 -0800405 (byte) 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
406 0x00, 0x00, 0x00, 0x01, (byte) 0xff,
407 targetIp[targetIp.length - 3],
408 targetIp[targetIp.length - 2],
409 targetIp[targetIp.length - 1]
410 };
411 }
412
413 /**
414 * According to the RFC 2464, an IPv6 packet with a multicast
415 * destination address DST, consisting of the sixteen octets DST[1]
416 * through DST[16], is transmitted to the Ethernet multicast address
417 * whose first two octets are the value 3333 hexadecimal and whose last
418 * four octets are the last four octets of DST.
419 *
420 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421 * |0 0 1 1 0 0 1 1|0 0 1 1 0 0 1 1|
422 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
423 * | DST[13] | DST[14] |
424 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
425 * | DST[15] | DST[16] |
426 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427 *
428 * @param targetIp the multicast address.
429 * @return the multicast mac address
430 */
Pier Luigi37b687b2017-01-16 22:46:33 -0800431 public static byte[] getMCastMacAddress(byte[] targetIp) {
432 checkArgument(targetIp.length == Ip6Address.BYTE_LENGTH);
433 return new byte[] {
Pier Ventre78e73f62016-12-02 19:59:28 -0800434 0x33, 0x33,
435 targetIp[targetIp.length - 4],
436 targetIp[targetIp.length - 3],
437 targetIp[targetIp.length - 2],
438 targetIp[targetIp.length - 1],
439 };
440 }
Pier Luigi37b687b2017-01-16 22:46:33 -0800441
442 /**
443 * According to the RFC 4291, an IPv6 link local address is an IPv6
444 * unicast address that can be automatically configured on any interface
445 * using the link-local prefix FE80::/10 (1111 1110 10) and the interface
446 * identifier in the modified EUI-64 format.
447 *
448 * +----------------------------------------------------------------+
449 * | 10 bits | 54 bits | 64 bits |
450 * +----------- +-------------------------+-------------------------+
451 * | 1111111010 | 0 | interface ID |
452 * +----------- +-------------------------+-------------------------+
453 *
454 * @param targetIp the ip address to verify
455 * @return true if the ipv6 address is link local,
456 * false otherwise
457 */
458 public static boolean isLinkLocalAddress(byte[] targetIp) {
459 checkArgument(targetIp.length == Ip6Address.BYTE_LENGTH);
460 return (targetIp[0] & 0xff) == 0xfe && (targetIp[1] & 0xc0) == 0x80;
461 }
462
463 /**
464 * Returns the auto-generated link local address using the
465 * mac address as parameter.
466 *
467 * @param macAddress the mac address to use
468 * @return the ipv6 link local address
469 */
470 public static byte[] getLinkLocalAddress(byte[] macAddress) {
471 checkArgument(macAddress.length == MacAddress.MAC_ADDRESS_LENGTH);
472 return new byte[] {
473 LINK_LOCAL_0,
474 LINK_LOCAL_1,
475 0, 0, 0, 0, 0, 0,
476 (byte) (macAddress[0] ^ (1 << 1)),
477 macAddress[1],
478 macAddress[2],
479 (byte) 0xff,
480 (byte) 0xfe,
481 macAddress[3],
482 macAddress[4],
483 macAddress[5],
484 };
485 }
486
487 /**
488 * Returns the mac address from the auto-generated
489 * link local address.
490 *
491 * @param linkLocalAddress the ipv6 to use
492 * @return the mac address
493 */
494 public static byte[] getMacAddress(byte[] linkLocalAddress) {
495 return !isLinkLocalAddress(linkLocalAddress) ? null : new byte[] {
496 (byte) (linkLocalAddress[8] ^ (1 << 1)),
497 linkLocalAddress[9],
498 linkLocalAddress[10],
499 linkLocalAddress[13],
500 linkLocalAddress[14],
501 linkLocalAddress[15],
502 };
503 }
504
505
506
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800507}