blob: 7c9b4b989ca3e5f9b45d3f50886b4da31ecbc983 [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;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080027import java.nio.ByteBuffer;
28import java.util.HashMap;
29import java.util.Map;
30
31/**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080032 * Implements ICMPv6 packet format. (RFC 4443)
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080033 */
34public class ICMP6 extends BasePacket {
35 public static final byte HEADER_LENGTH = 4; // bytes
36
Charles M.C. Chan2184de12015-04-26 02:24:53 +080037 // Type
38 /** Destination Unreachable. */
39 public static final byte DEST_UNREACH = (byte) 0x01;
40 /** Packet Too Big. */
41 public static final byte PKT_TOO_BIG = (byte) 0x02;
42 /** Time Exceeded. */
43 public static final byte TIME_EXCEED = (byte) 0x03;
44 /** Parameter Problem. */
45 public static final byte PARAM_ERR = (byte) 0x04;
46 /** Echo Request. */
Charles M.C. Chan197a0122015-04-08 18:15:34 +080047 public static final byte ECHO_REQUEST = (byte) 0x80;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080048 /** Echo Reply. */
Charles M.C. Chan197a0122015-04-08 18:15:34 +080049 public static final byte ECHO_REPLY = (byte) 0x81;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080050 /** Multicast Listener Query. */
51 public static final byte MCAST_QUERY = (byte) 0x82;
52 /** Multicast Listener Report. */
53 public static final byte MCAST_REPORT = (byte) 0x83;
54 /** Multicast Listener Done. */
55 public static final byte MCAST_DONE = (byte) 0x84;
56 /** Router Solicitation. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080057 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080058 /** Router Advertisement. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080059 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080060 /** Neighbor Solicitation. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080061 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080062 /** Neighbor Advertisement. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080063 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080064 /** Redirect Message. */
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080065 public static final byte REDIRECT = (byte) 0x89;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080066
67 // Code for DEST_UNREACH
68 /** No route to destination. */
69 public static final byte NO_ROUTE = (byte) 0x00;
70 /** Communication with destination administratively prohibited. */
71 public static final byte COMM_PROHIBIT = (byte) 0x01;
72 /** Beyond scope of source address. */
73 public static final byte BEYOND_SCOPE = (byte) 0x02;
74 /** Address unreachable. */
75 public static final byte ADDR_UNREACH = (byte) 0x03;
76 /** Port unreachable. */
77 public static final byte PORT_UNREACH = (byte) 0x04;
78 /** Source address failed ingress/egress policy. */
79 public static final byte FAIL_POLICY = (byte) 0x05;
80 /** Reject route to destination. */
81 public static final byte REJECT_ROUTE = (byte) 0x06;
82 /** Error in Source Routing Header. */
83 public static final byte SRC_ROUTING_HEADER_ERR = (byte) 0x07;
84
85 // Code for TIME_EXCEED
86 /** Hop limit exceeded in transit. */
87 public static final byte HOP_LIMIT_EXCEED = (byte) 0x00;
88 /** Fragment reassembly time exceeded. */
89 public static final byte DEFRAG_TIME_EXCEED = (byte) 0x01;
90
91 // Code for PARAM_ERR
92 /** Erroneous header field encountered. */
93 public static final byte HDR_FIELD_ERR = (byte) 0x00;
94 /** Unrecognized Next Header type encountered. */
95 public static final byte NEXT_HEADER_ERR = (byte) 0x01;
96 /** Unrecognized IPv6 option encountered. */
97 public static final byte IPV6_OPT_ERR = (byte) 0x01;
98
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080099 public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
100 new HashMap<>();
101
102 static {
Charles M.C. Chanea5aa472015-01-03 13:40:39 +0800103 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.class);
104 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.class);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800105 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800106 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.class);
Charles M.C. Chanea5aa472015-01-03 13:40:39 +0800107 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.REDIRECT, Redirect.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800108 }
109
110 protected byte icmpType;
111 protected byte icmpCode;
112 protected short checksum;
113
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800114 private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH];
115
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800116 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800117 * Gets ICMP6 type.
118 *
119 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800120 */
121 public byte getIcmpType() {
122 return this.icmpType;
123 }
124
125 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800126 * Sets ICMP6 type.
127 *
128 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800129 * @return this
130 */
131 public ICMP6 setIcmpType(final byte icmpType) {
132 this.icmpType = icmpType;
133 return this;
134 }
135
136 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800137 * Gets ICMP6 code.
138 *
139 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800140 */
141 public byte getIcmpCode() {
142 return this.icmpCode;
143 }
144
145 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800146 * Sets ICMP6 code.
147 *
148 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800149 * @return this
150 */
151 public ICMP6 setIcmpCode(final byte icmpCode) {
152 this.icmpCode = icmpCode;
153 return this;
154 }
155
156 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800157 * Gets checksum.
158 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800159 * @return the checksum
160 */
161 public short getChecksum() {
162 return this.checksum;
163 }
164
165 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800166 * Sets checksum.
167 *
168 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800169 * @return this
170 */
171 public ICMP6 setChecksum(final short checksum) {
172 this.checksum = checksum;
173 return this;
174 }
175
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800176 @Override
177 public byte[] serialize() {
178 byte[] payloadData = null;
179 if (this.payload != null) {
180 this.payload.setParent(this);
181 payloadData = this.payload.serialize();
182 }
183
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800184 int payloadLength = 0;
185 if (payloadData != null) {
186 payloadLength = payloadData.length;
187 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800188
189 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
Dusan Pajin79f515f2015-02-11 16:40:34 +0100190 final ByteBuffer bbData = ByteBuffer.wrap(data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800191
Dusan Pajin79f515f2015-02-11 16:40:34 +0100192 // Creating ByteBuffer for checksum calculation
193 final byte[] checksumData =
194 new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
195 final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
196
197 //
198 // Creating IPv6 Pseudo Header for checksum calculation according
199 // to RFC 4443 and RFC 2460
200 //
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800201 IPv6 ipv6Parent = null;
202 for (IPacket p = this.parent; p != null; p = p.getParent()) {
203 if (p instanceof IPv6) {
204 ipv6Parent = (IPv6) p;
205 break;
206 }
207 }
208 if (ipv6Parent != null) {
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800209 bbChecksum.put(ipv6Parent.getSourceAddress());
210 bbChecksum.put(ipv6Parent.getDestinationAddress());
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800211 } else {
212 // NOTE: IPv6 source and destination addresses unknown. Use zeroes.
213 bbChecksum.put(ZERO_ADDRESS);
214 bbChecksum.put(ZERO_ADDRESS);
215 }
Dusan Pajin79f515f2015-02-11 16:40:34 +0100216 bbChecksum.putInt(HEADER_LENGTH + payloadLength);
217 bbChecksum.put((byte) 0);
218 bbChecksum.put((byte) 0);
219 bbChecksum.put((byte) 0);
220 bbChecksum.put(IPv6.PROTOCOL_ICMP6);
221 bbChecksum.put(this.icmpType);
222 bbChecksum.put(this.icmpCode);
223 bbChecksum.put((byte) 0);
224 bbChecksum.put((byte) 0);
225
226 bbData.put(this.icmpType);
227 bbData.put(this.icmpCode);
228 bbData.putShort(this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800229 if (payloadData != null) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100230 bbData.put(payloadData);
231 bbChecksum.put(payloadData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800232 }
233
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800234 if (this.parent != null) {
235 if (this.parent instanceof IPv6) {
236 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
237 } else if (this.parent instanceof IExtensionHeader) {
238 ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
239 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800240 }
241
242 // compute checksum if needed
243 if (this.checksum == 0) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100244 bbData.rewind();
245 bbChecksum.rewind();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800246 int accumulation = 0;
247
Dusan Pajin79f515f2015-02-11 16:40:34 +0100248 for (int i = 0; i < checksumData.length / 2; ++i) {
249 accumulation += 0xffff & bbChecksum.getShort();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800250 }
251 // pad to an even number of shorts
Dusan Pajin79f515f2015-02-11 16:40:34 +0100252 if (checksumData.length % 2 > 0) {
253 accumulation += (bbChecksum.get() & 0xff) << 8;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800254 }
255
256 accumulation = (accumulation >> 16 & 0xffff)
257 + (accumulation & 0xffff);
258 this.checksum = (short) (~accumulation & 0xffff);
Dusan Pajin79f515f2015-02-11 16:40:34 +0100259 bbData.putShort(2, this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800260 }
261 return data;
262 }
263
264 /*
265 * (non-Javadoc)
266 *
267 * @see java.lang.Object#hashCode()
268 */
269 @Override
270 public int hashCode() {
271 final int prime = 5807;
272 int result = super.hashCode();
273 result = prime * result + this.icmpType;
274 result = prime * result + this.icmpCode;
275 result = prime * result + this.checksum;
276 return result;
277 }
278
279 /*
280 * (non-Javadoc)
281 *
282 * @see java.lang.Object#equals(java.lang.Object)
283 */
284 @Override
285 public boolean equals(final Object obj) {
286 if (this == obj) {
287 return true;
288 }
289 if (!super.equals(obj)) {
290 return false;
291 }
292 if (!(obj instanceof ICMP6)) {
293 return false;
294 }
295 final ICMP6 other = (ICMP6) obj;
296 if (this.icmpType != other.icmpType) {
297 return false;
298 }
299 if (this.icmpCode != other.icmpCode) {
300 return false;
301 }
302 if (this.checksum != other.checksum) {
303 return false;
304 }
305 return true;
306 }
307
308 @Override
309 public IPacket deserialize(final byte[] data, final int offset,
310 final int length) {
311 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
312 this.icmpType = bb.get();
313 this.icmpCode = bb.get();
314 this.checksum = bb.getShort();
315
316 IPacket payload;
317 if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
318 final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
319 .get(this.icmpType);
320 try {
321 payload = clazz.newInstance();
322 } catch (final Exception e) {
323 throw new RuntimeException(
324 "Error parsing payload for ICMP6 packet", e);
325 }
326 } else {
327 payload = new Data();
328 }
329 this.payload = payload.deserialize(data, bb.position(),
330 bb.limit() - bb.position());
331 this.payload.setParent(this);
332
333 return this;
334 }
335}