blob: 31aabfce6fb0d4a17f57cb346975611ea900992e [file] [log] [blame]
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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
Yuta HIGUCHIbfc2e922017-06-07 21:46:05 -070028import com.google.common.collect.ImmutableMap;
29
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080030import java.nio.ByteBuffer;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080031import java.util.Map;
32
Jian Li5fc14292015-12-04 11:30:46 -080033import static com.google.common.base.MoreObjects.toStringHelper;
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 ICMPv6 packet format. (RFC 4443)
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080038 */
39public class ICMP6 extends BasePacket {
40 public static final byte HEADER_LENGTH = 4; // bytes
41
Charles M.C. Chan2184de12015-04-26 02:24:53 +080042 // Type
43 /** Destination Unreachable. */
44 public static final byte DEST_UNREACH = (byte) 0x01;
45 /** Packet Too Big. */
46 public static final byte PKT_TOO_BIG = (byte) 0x02;
47 /** Time Exceeded. */
48 public static final byte TIME_EXCEED = (byte) 0x03;
49 /** Parameter Problem. */
50 public static final byte PARAM_ERR = (byte) 0x04;
51 /** Echo Request. */
Charles M.C. Chan197a0122015-04-08 18:15:34 +080052 public static final byte ECHO_REQUEST = (byte) 0x80;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080053 /** Echo Reply. */
Charles M.C. Chan197a0122015-04-08 18:15:34 +080054 public static final byte ECHO_REPLY = (byte) 0x81;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080055 /** Multicast Listener Query. */
56 public static final byte MCAST_QUERY = (byte) 0x82;
57 /** Multicast Listener Report. */
58 public static final byte MCAST_REPORT = (byte) 0x83;
59 /** Multicast Listener Done. */
60 public static final byte MCAST_DONE = (byte) 0x84;
61 /** Router Solicitation. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080062 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080063 /** Router Advertisement. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080064 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080065 /** Neighbor Solicitation. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080066 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080067 /** Neighbor Advertisement. */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080068 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080069 /** Redirect Message. */
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080070 public static final byte REDIRECT = (byte) 0x89;
Charles M.C. Chan2184de12015-04-26 02:24:53 +080071
72 // Code for DEST_UNREACH
73 /** No route to destination. */
74 public static final byte NO_ROUTE = (byte) 0x00;
75 /** Communication with destination administratively prohibited. */
76 public static final byte COMM_PROHIBIT = (byte) 0x01;
77 /** Beyond scope of source address. */
78 public static final byte BEYOND_SCOPE = (byte) 0x02;
79 /** Address unreachable. */
80 public static final byte ADDR_UNREACH = (byte) 0x03;
81 /** Port unreachable. */
82 public static final byte PORT_UNREACH = (byte) 0x04;
83 /** Source address failed ingress/egress policy. */
84 public static final byte FAIL_POLICY = (byte) 0x05;
85 /** Reject route to destination. */
86 public static final byte REJECT_ROUTE = (byte) 0x06;
87 /** Error in Source Routing Header. */
88 public static final byte SRC_ROUTING_HEADER_ERR = (byte) 0x07;
89
90 // Code for TIME_EXCEED
91 /** Hop limit exceeded in transit. */
92 public static final byte HOP_LIMIT_EXCEED = (byte) 0x00;
93 /** Fragment reassembly time exceeded. */
94 public static final byte DEFRAG_TIME_EXCEED = (byte) 0x01;
95
96 // Code for PARAM_ERR
97 /** Erroneous header field encountered. */
98 public static final byte HDR_FIELD_ERR = (byte) 0x00;
99 /** Unrecognized Next Header type encountered. */
100 public static final byte NEXT_HEADER_ERR = (byte) 0x01;
101 /** Unrecognized IPv6 option encountered. */
102 public static final byte IPV6_OPT_ERR = (byte) 0x01;
103
Jonathan Hart2a655752015-04-07 16:46:33 -0700104 public static final Map<Byte, Deserializer<? extends IPacket>> TYPE_DESERIALIZER_MAP =
Yuta HIGUCHIbfc2e922017-06-07 21:46:05 -0700105 ImmutableMap.<Byte, Deserializer<? extends IPacket>>builder()
106 .put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.deserializer())
107 .put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.deserializer())
108 .put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.deserializer())
109 .put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.deserializer())
110 .put(ICMP6.REDIRECT, Redirect.deserializer())
111 .build();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800112
113 protected byte icmpType;
114 protected byte icmpCode;
115 protected short checksum;
116
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800117 private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH];
118
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800119 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800120 * Gets ICMP6 type.
121 *
122 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800123 */
124 public byte getIcmpType() {
125 return this.icmpType;
126 }
127
128 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800129 * Sets ICMP6 type.
130 *
131 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800132 * @return this
133 */
134 public ICMP6 setIcmpType(final byte icmpType) {
135 this.icmpType = icmpType;
136 return this;
137 }
138
139 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800140 * Gets ICMP6 code.
141 *
142 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800143 */
144 public byte getIcmpCode() {
145 return this.icmpCode;
146 }
147
148 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800149 * Sets ICMP6 code.
150 *
151 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800152 * @return this
153 */
154 public ICMP6 setIcmpCode(final byte icmpCode) {
155 this.icmpCode = icmpCode;
156 return this;
157 }
158
159 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800160 * Gets checksum.
161 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800162 * @return the checksum
163 */
164 public short getChecksum() {
165 return this.checksum;
166 }
167
168 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800169 * Sets checksum.
170 *
171 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800172 * @return this
173 */
174 public ICMP6 setChecksum(final short checksum) {
175 this.checksum = checksum;
176 return this;
177 }
178
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800179 @Override
180 public byte[] serialize() {
181 byte[] payloadData = null;
182 if (this.payload != null) {
183 this.payload.setParent(this);
184 payloadData = this.payload.serialize();
185 }
186
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800187 int payloadLength = 0;
188 if (payloadData != null) {
189 payloadLength = payloadData.length;
190 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800191
192 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
Dusan Pajin79f515f2015-02-11 16:40:34 +0100193 final ByteBuffer bbData = ByteBuffer.wrap(data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800194
Dusan Pajin79f515f2015-02-11 16:40:34 +0100195 // Creating ByteBuffer for checksum calculation
196 final byte[] checksumData =
197 new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
198 final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
199
200 //
201 // Creating IPv6 Pseudo Header for checksum calculation according
202 // to RFC 4443 and RFC 2460
203 //
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800204 IPv6 ipv6Parent = null;
205 for (IPacket p = this.parent; p != null; p = p.getParent()) {
206 if (p instanceof IPv6) {
207 ipv6Parent = (IPv6) p;
208 break;
209 }
210 }
211 if (ipv6Parent != null) {
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800212 bbChecksum.put(ipv6Parent.getSourceAddress());
213 bbChecksum.put(ipv6Parent.getDestinationAddress());
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800214 } else {
215 // NOTE: IPv6 source and destination addresses unknown. Use zeroes.
216 bbChecksum.put(ZERO_ADDRESS);
217 bbChecksum.put(ZERO_ADDRESS);
218 }
Dusan Pajin79f515f2015-02-11 16:40:34 +0100219 bbChecksum.putInt(HEADER_LENGTH + payloadLength);
220 bbChecksum.put((byte) 0);
221 bbChecksum.put((byte) 0);
222 bbChecksum.put((byte) 0);
223 bbChecksum.put(IPv6.PROTOCOL_ICMP6);
224 bbChecksum.put(this.icmpType);
225 bbChecksum.put(this.icmpCode);
226 bbChecksum.put((byte) 0);
227 bbChecksum.put((byte) 0);
228
229 bbData.put(this.icmpType);
230 bbData.put(this.icmpCode);
231 bbData.putShort(this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800232 if (payloadData != null) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100233 bbData.put(payloadData);
234 bbChecksum.put(payloadData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800235 }
236
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800237 if (this.parent != null) {
238 if (this.parent instanceof IPv6) {
239 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
240 } else if (this.parent instanceof IExtensionHeader) {
241 ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
242 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800243 }
244
245 // compute checksum if needed
246 if (this.checksum == 0) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100247 bbData.rewind();
248 bbChecksum.rewind();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800249 int accumulation = 0;
250
Dusan Pajin79f515f2015-02-11 16:40:34 +0100251 for (int i = 0; i < checksumData.length / 2; ++i) {
252 accumulation += 0xffff & bbChecksum.getShort();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800253 }
254 // pad to an even number of shorts
Dusan Pajin79f515f2015-02-11 16:40:34 +0100255 if (checksumData.length % 2 > 0) {
256 accumulation += (bbChecksum.get() & 0xff) << 8;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800257 }
258
259 accumulation = (accumulation >> 16 & 0xffff)
260 + (accumulation & 0xffff);
261 this.checksum = (short) (~accumulation & 0xffff);
Dusan Pajin79f515f2015-02-11 16:40:34 +0100262 bbData.putShort(2, this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800263 }
264 return data;
265 }
266
267 /*
268 * (non-Javadoc)
269 *
270 * @see java.lang.Object#hashCode()
271 */
272 @Override
273 public int hashCode() {
274 final int prime = 5807;
275 int result = super.hashCode();
276 result = prime * result + this.icmpType;
277 result = prime * result + this.icmpCode;
278 result = prime * result + this.checksum;
279 return result;
280 }
281
282 /*
283 * (non-Javadoc)
284 *
285 * @see java.lang.Object#equals(java.lang.Object)
286 */
287 @Override
288 public boolean equals(final Object obj) {
289 if (this == obj) {
290 return true;
291 }
292 if (!super.equals(obj)) {
293 return false;
294 }
295 if (!(obj instanceof ICMP6)) {
296 return false;
297 }
298 final ICMP6 other = (ICMP6) obj;
299 if (this.icmpType != other.icmpType) {
300 return false;
301 }
302 if (this.icmpCode != other.icmpCode) {
303 return false;
304 }
305 if (this.checksum != other.checksum) {
306 return false;
307 }
308 return true;
309 }
310
Jonathan Hart2a655752015-04-07 16:46:33 -0700311 /**
312 * Deserializer function for ICMPv6 packets.
313 *
314 * @return deserializer function
315 */
316 public static Deserializer<ICMP6> deserializer() {
317 return (data, offset, length) -> {
318 checkInput(data, offset, length, HEADER_LENGTH);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800319
Jonathan Hart2a655752015-04-07 16:46:33 -0700320 ICMP6 icmp6 = new ICMP6();
321
322 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
323
324 icmp6.icmpType = bb.get();
325 icmp6.icmpCode = bb.get();
326 icmp6.checksum = bb.getShort();
327
328 Deserializer<? extends IPacket> deserializer;
329 if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmp6.icmpType)) {
330 deserializer = TYPE_DESERIALIZER_MAP.get(icmp6.icmpType);
331 } else {
332 deserializer = Data.deserializer();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800333 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700334 icmp6.payload = deserializer.deserialize(data, bb.position(),
335 bb.limit() - bb.position());
336 icmp6.payload.setParent(icmp6);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800337
Jonathan Hart2a655752015-04-07 16:46:33 -0700338 return icmp6;
339 };
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800340 }
Jian Li5fc14292015-12-04 11:30:46 -0800341
342 @Override
343 public String toString() {
344 return toStringHelper(getClass())
345 .add("icmpType", Byte.toString(icmpType))
346 .add("icmpCode", Byte.toString(icmpCode))
347 .add("checksum", Short.toString(checksum))
348 .toString();
349 }
Pier Ventre78e73f62016-12-02 19:59:28 -0800350
351 /**
352 * Builds an ICMPv6 reply using the supplied ICMPv6 request.
353 *
354 * @param ethRequest the Ethernet packet containing the ICMPv6 ECHO request
355 * @return the Ethernet packet containing the ICMPv6 ECHO reply
356 */
357 public static Ethernet buildIcmp6Reply(Ethernet ethRequest) {
358
359 if (ethRequest.getEtherType() != Ethernet.TYPE_IPV6) {
360 return null;
361 }
362
363 IPv6 ipv6Request = (IPv6) ethRequest.getPayload();
364
365 if (ipv6Request.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
366 return null;
367 }
368
369 Ethernet ethReply = new Ethernet();
370
371
372 IPv6 ipv6Reply = new IPv6();
373
374 byte[] destAddress = ipv6Request.getDestinationAddress();
375 ipv6Reply.setDestinationAddress(ipv6Request.getSourceAddress());
376 ipv6Reply.setSourceAddress(destAddress);
377 ipv6Reply.setHopLimit((byte) 64);
Charles Chan83439a32017-10-24 15:21:16 -0700378 ipv6Reply.setTrafficClass(ipv6Request.getTrafficClass());
Pier Ventre78e73f62016-12-02 19:59:28 -0800379 ipv6Reply.setNextHeader(IPv6.PROTOCOL_ICMP6);
380
381 ICMP6 icmpv6Reply = new ICMP6();
382 icmpv6Reply.setPayload(ipv6Request.getPayload().getPayload());
383 icmpv6Reply.setIcmpType(ICMP6.ECHO_REPLY);
384 icmpv6Reply.setIcmpCode((byte) 0);
385 ipv6Reply.setPayload(icmpv6Reply);
386
387 ethReply.setEtherType(Ethernet.TYPE_IPV6);
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700388 ethReply.setQinQVID(ethRequest.getQinQVID());
389 ethReply.setQinQTPID(ethRequest.getQinQTPID());
Pier Ventre78e73f62016-12-02 19:59:28 -0800390 ethReply.setVlanID(ethRequest.getVlanID());
391 ethReply.setDestinationMACAddress(ethRequest.getSourceMACAddress());
392 ethReply.setSourceMACAddress(ethRequest.getDestinationMACAddress());
393 ethReply.setPayload(ipv6Reply);
394
395 return ethReply;
396 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800397}