blob: 45b7a6233e7d3468bb9a82679da6e9366f4ad4fa [file] [log] [blame]
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08001/*
Charles M.C. Chan197a0122015-04-08 18:15:34 +08002 * Copyright 2014-2015 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
17
18
19package org.onlab.packet;
20
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -080021import org.onlab.packet.ipv6.IExtensionHeader;
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080022import org.onlab.packet.ndp.NeighborAdvertisement;
23import org.onlab.packet.ndp.NeighborSolicitation;
24import org.onlab.packet.ndp.Redirect;
25import org.onlab.packet.ndp.RouterAdvertisement;
26import org.onlab.packet.ndp.RouterSolicitation;
Jonathan Hart2a655752015-04-07 16:46:33 -070027
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080028import java.nio.ByteBuffer;
29import java.util.HashMap;
30import java.util.Map;
31
Jian Li5fc14292015-12-04 11:30:46 -080032import static com.google.common.base.MoreObjects.toStringHelper;
Jonathan Hart2a655752015-04-07 16:46:33 -070033import static org.onlab.packet.PacketUtils.checkInput;
34
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080035/**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080036 * Implements ICMPv6 packet format. (RFC 4443)
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080037 */
38public class ICMP6 extends BasePacket {
39 public static final byte HEADER_LENGTH = 4; // bytes
40
Charles M.C. Chan2184de12015-04-26 02:24:53 +080041 // Type
42 /** Destination Unreachable. */
43 public static final byte DEST_UNREACH = (byte) 0x01;
44 /** Packet Too Big. */
45 public static final byte PKT_TOO_BIG = (byte) 0x02;
46 /** Time Exceeded. */
47 public static final byte TIME_EXCEED = (byte) 0x03;
48 /** Parameter Problem. */
49 public static final byte PARAM_ERR = (byte) 0x04;
50 /** Echo Request. */
Charles M.C. Chan197a0122015-04-08 18:15:34 +080051 public static final byte ECHO_REQUEST = (byte) 0x80;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080052 /** Echo Reply. */
Charles M.C. Chan197a0122015-04-08 18:15:34 +080053 public static final byte ECHO_REPLY = (byte) 0x81;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080054 /** Multicast Listener Query. */
55 public static final byte MCAST_QUERY = (byte) 0x82;
56 /** Multicast Listener Report. */
57 public static final byte MCAST_REPORT = (byte) 0x83;
58 /** Multicast Listener Done. */
59 public static final byte MCAST_DONE = (byte) 0x84;
60 /** Router Solicitation. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080061 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080062 /** Router Advertisement. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080063 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080064 /** Neighbor Solicitation. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080065 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080066 /** Neighbor Advertisement. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080067 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080068 /** Redirect Message. */
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080069 public static final byte REDIRECT = (byte) 0x89;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080070
71 // Code for DEST_UNREACH
72 /** No route to destination. */
73 public static final byte NO_ROUTE = (byte) 0x00;
74 /** Communication with destination administratively prohibited. */
75 public static final byte COMM_PROHIBIT = (byte) 0x01;
76 /** Beyond scope of source address. */
77 public static final byte BEYOND_SCOPE = (byte) 0x02;
78 /** Address unreachable. */
79 public static final byte ADDR_UNREACH = (byte) 0x03;
80 /** Port unreachable. */
81 public static final byte PORT_UNREACH = (byte) 0x04;
82 /** Source address failed ingress/egress policy. */
83 public static final byte FAIL_POLICY = (byte) 0x05;
84 /** Reject route to destination. */
85 public static final byte REJECT_ROUTE = (byte) 0x06;
86 /** Error in Source Routing Header. */
87 public static final byte SRC_ROUTING_HEADER_ERR = (byte) 0x07;
88
89 // Code for TIME_EXCEED
90 /** Hop limit exceeded in transit. */
91 public static final byte HOP_LIMIT_EXCEED = (byte) 0x00;
92 /** Fragment reassembly time exceeded. */
93 public static final byte DEFRAG_TIME_EXCEED = (byte) 0x01;
94
95 // Code for PARAM_ERR
96 /** Erroneous header field encountered. */
97 public static final byte HDR_FIELD_ERR = (byte) 0x00;
98 /** Unrecognized Next Header type encountered. */
99 public static final byte NEXT_HEADER_ERR = (byte) 0x01;
100 /** Unrecognized IPv6 option encountered. */
101 public static final byte IPV6_OPT_ERR = (byte) 0x01;
102
Jonathan Hart2a655752015-04-07 16:46:33 -0700103 public static final Map<Byte, Deserializer<? extends IPacket>> TYPE_DESERIALIZER_MAP =
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800104 new HashMap<>();
105
106 static {
Jonathan Hart2a655752015-04-07 16:46:33 -0700107 ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.deserializer());
108 ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.deserializer());
109 ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.deserializer());
110 ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.deserializer());
111 ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.REDIRECT, Redirect.deserializer());
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800112 }
113
114 protected byte icmpType;
115 protected byte icmpCode;
116 protected short checksum;
117
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800118 private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH];
119
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800120 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800121 * Gets ICMP6 type.
122 *
123 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800124 */
125 public byte getIcmpType() {
126 return this.icmpType;
127 }
128
129 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800130 * Sets ICMP6 type.
131 *
132 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800133 * @return this
134 */
135 public ICMP6 setIcmpType(final byte icmpType) {
136 this.icmpType = icmpType;
137 return this;
138 }
139
140 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800141 * Gets ICMP6 code.
142 *
143 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800144 */
145 public byte getIcmpCode() {
146 return this.icmpCode;
147 }
148
149 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800150 * Sets ICMP6 code.
151 *
152 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800153 * @return this
154 */
155 public ICMP6 setIcmpCode(final byte icmpCode) {
156 this.icmpCode = icmpCode;
157 return this;
158 }
159
160 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800161 * Gets checksum.
162 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800163 * @return the checksum
164 */
165 public short getChecksum() {
166 return this.checksum;
167 }
168
169 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800170 * Sets checksum.
171 *
172 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800173 * @return this
174 */
175 public ICMP6 setChecksum(final short checksum) {
176 this.checksum = checksum;
177 return this;
178 }
179
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800180 @Override
181 public byte[] serialize() {
182 byte[] payloadData = null;
183 if (this.payload != null) {
184 this.payload.setParent(this);
185 payloadData = this.payload.serialize();
186 }
187
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800188 int payloadLength = 0;
189 if (payloadData != null) {
190 payloadLength = payloadData.length;
191 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800192
193 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
Dusan Pajin79f515f2015-02-11 16:40:34 +0100194 final ByteBuffer bbData = ByteBuffer.wrap(data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800195
Dusan Pajin79f515f2015-02-11 16:40:34 +0100196 // Creating ByteBuffer for checksum calculation
197 final byte[] checksumData =
198 new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
199 final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
200
201 //
202 // Creating IPv6 Pseudo Header for checksum calculation according
203 // to RFC 4443 and RFC 2460
204 //
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800205 IPv6 ipv6Parent = null;
206 for (IPacket p = this.parent; p != null; p = p.getParent()) {
207 if (p instanceof IPv6) {
208 ipv6Parent = (IPv6) p;
209 break;
210 }
211 }
212 if (ipv6Parent != null) {
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800213 bbChecksum.put(ipv6Parent.getSourceAddress());
214 bbChecksum.put(ipv6Parent.getDestinationAddress());
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800215 } else {
216 // NOTE: IPv6 source and destination addresses unknown. Use zeroes.
217 bbChecksum.put(ZERO_ADDRESS);
218 bbChecksum.put(ZERO_ADDRESS);
219 }
Dusan Pajin79f515f2015-02-11 16:40:34 +0100220 bbChecksum.putInt(HEADER_LENGTH + payloadLength);
221 bbChecksum.put((byte) 0);
222 bbChecksum.put((byte) 0);
223 bbChecksum.put((byte) 0);
224 bbChecksum.put(IPv6.PROTOCOL_ICMP6);
225 bbChecksum.put(this.icmpType);
226 bbChecksum.put(this.icmpCode);
227 bbChecksum.put((byte) 0);
228 bbChecksum.put((byte) 0);
229
230 bbData.put(this.icmpType);
231 bbData.put(this.icmpCode);
232 bbData.putShort(this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800233 if (payloadData != null) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100234 bbData.put(payloadData);
235 bbChecksum.put(payloadData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800236 }
237
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800238 if (this.parent != null) {
239 if (this.parent instanceof IPv6) {
240 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
241 } else if (this.parent instanceof IExtensionHeader) {
242 ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
243 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800244 }
245
246 // compute checksum if needed
247 if (this.checksum == 0) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100248 bbData.rewind();
249 bbChecksum.rewind();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800250 int accumulation = 0;
251
Dusan Pajin79f515f2015-02-11 16:40:34 +0100252 for (int i = 0; i < checksumData.length / 2; ++i) {
253 accumulation += 0xffff & bbChecksum.getShort();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800254 }
255 // pad to an even number of shorts
Dusan Pajin79f515f2015-02-11 16:40:34 +0100256 if (checksumData.length % 2 > 0) {
257 accumulation += (bbChecksum.get() & 0xff) << 8;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800258 }
259
260 accumulation = (accumulation >> 16 & 0xffff)
261 + (accumulation & 0xffff);
262 this.checksum = (short) (~accumulation & 0xffff);
Dusan Pajin79f515f2015-02-11 16:40:34 +0100263 bbData.putShort(2, this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800264 }
265 return data;
266 }
267
Jonathan Hart2a655752015-04-07 16:46:33 -0700268 @Override
269 public IPacket deserialize(final byte[] data, final int offset,
270 final int length) {
271 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
272 this.icmpType = bb.get();
273 this.icmpCode = bb.get();
274 this.checksum = bb.getShort();
275
276 Deserializer<? extends IPacket> deserializer;
277 if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmpType)) {
278 deserializer = TYPE_DESERIALIZER_MAP.get(icmpType);
279 } else {
280 deserializer = Data.deserializer();
281 }
282 try {
283 this.payload = deserializer.deserialize(data, bb.position(),
284 bb.limit() - bb.position());
285 this.payload.setParent(this);
286 } catch (DeserializationException e) {
287 return this;
288 }
289
290 return this;
291 }
292
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800293 /*
294 * (non-Javadoc)
295 *
296 * @see java.lang.Object#hashCode()
297 */
298 @Override
299 public int hashCode() {
300 final int prime = 5807;
301 int result = super.hashCode();
302 result = prime * result + this.icmpType;
303 result = prime * result + this.icmpCode;
304 result = prime * result + this.checksum;
305 return result;
306 }
307
308 /*
309 * (non-Javadoc)
310 *
311 * @see java.lang.Object#equals(java.lang.Object)
312 */
313 @Override
314 public boolean equals(final Object obj) {
315 if (this == obj) {
316 return true;
317 }
318 if (!super.equals(obj)) {
319 return false;
320 }
321 if (!(obj instanceof ICMP6)) {
322 return false;
323 }
324 final ICMP6 other = (ICMP6) obj;
325 if (this.icmpType != other.icmpType) {
326 return false;
327 }
328 if (this.icmpCode != other.icmpCode) {
329 return false;
330 }
331 if (this.checksum != other.checksum) {
332 return false;
333 }
334 return true;
335 }
336
Jonathan Hart2a655752015-04-07 16:46:33 -0700337 /**
338 * Deserializer function for ICMPv6 packets.
339 *
340 * @return deserializer function
341 */
342 public static Deserializer<ICMP6> deserializer() {
343 return (data, offset, length) -> {
344 checkInput(data, offset, length, HEADER_LENGTH);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800345
Jonathan Hart2a655752015-04-07 16:46:33 -0700346 ICMP6 icmp6 = new ICMP6();
347
348 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
349
350 icmp6.icmpType = bb.get();
351 icmp6.icmpCode = bb.get();
352 icmp6.checksum = bb.getShort();
353
354 Deserializer<? extends IPacket> deserializer;
355 if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmp6.icmpType)) {
356 deserializer = TYPE_DESERIALIZER_MAP.get(icmp6.icmpType);
357 } else {
358 deserializer = Data.deserializer();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800359 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700360 icmp6.payload = deserializer.deserialize(data, bb.position(),
361 bb.limit() - bb.position());
362 icmp6.payload.setParent(icmp6);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800363
Jonathan Hart2a655752015-04-07 16:46:33 -0700364 return icmp6;
365 };
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800366 }
Jian Li5fc14292015-12-04 11:30:46 -0800367
368 @Override
369 public String toString() {
370 return toStringHelper(getClass())
371 .add("icmpType", Byte.toString(icmpType))
372 .add("icmpCode", Byte.toString(icmpCode))
373 .add("checksum", Short.toString(checksum))
374 .toString();
375 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800376}