blob: 5c2a6e665032c085a48ebb2dafa96f6b5468c1d6 [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. Chan197a0122015-04-08 18:15:34 +080037 public static final byte ECHO_REQUEST = (byte) 0x80;
38 public static final byte ECHO_REPLY = (byte) 0x81;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080039 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
40 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
41 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
42 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080043 public static final byte REDIRECT = (byte) 0x89;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080044 public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
45 new HashMap<>();
46
47 static {
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080048 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.class);
49 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.class);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080050 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080051 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.class);
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080052 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.REDIRECT, Redirect.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080053 }
54
55 protected byte icmpType;
56 protected byte icmpCode;
57 protected short checksum;
58
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -080059 private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH];
60
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080061 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080062 * Gets ICMP6 type.
63 *
64 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080065 */
66 public byte getIcmpType() {
67 return this.icmpType;
68 }
69
70 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080071 * Sets ICMP6 type.
72 *
73 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080074 * @return this
75 */
76 public ICMP6 setIcmpType(final byte icmpType) {
77 this.icmpType = icmpType;
78 return this;
79 }
80
81 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080082 * Gets ICMP6 code.
83 *
84 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080085 */
86 public byte getIcmpCode() {
87 return this.icmpCode;
88 }
89
90 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080091 * Sets ICMP6 code.
92 *
93 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080094 * @return this
95 */
96 public ICMP6 setIcmpCode(final byte icmpCode) {
97 this.icmpCode = icmpCode;
98 return this;
99 }
100
101 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800102 * Gets checksum.
103 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800104 * @return the checksum
105 */
106 public short getChecksum() {
107 return this.checksum;
108 }
109
110 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800111 * Sets checksum.
112 *
113 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800114 * @return this
115 */
116 public ICMP6 setChecksum(final short checksum) {
117 this.checksum = checksum;
118 return this;
119 }
120
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800121 @Override
122 public byte[] serialize() {
123 byte[] payloadData = null;
124 if (this.payload != null) {
125 this.payload.setParent(this);
126 payloadData = this.payload.serialize();
127 }
128
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800129 int payloadLength = 0;
130 if (payloadData != null) {
131 payloadLength = payloadData.length;
132 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800133
134 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
Dusan Pajin79f515f2015-02-11 16:40:34 +0100135 final ByteBuffer bbData = ByteBuffer.wrap(data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800136
Dusan Pajin79f515f2015-02-11 16:40:34 +0100137 // Creating ByteBuffer for checksum calculation
138 final byte[] checksumData =
139 new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
140 final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
141
142 //
143 // Creating IPv6 Pseudo Header for checksum calculation according
144 // to RFC 4443 and RFC 2460
145 //
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800146 IPv6 ipv6Parent = null;
147 for (IPacket p = this.parent; p != null; p = p.getParent()) {
148 if (p instanceof IPv6) {
149 ipv6Parent = (IPv6) p;
150 break;
151 }
152 }
153 if (ipv6Parent != null) {
Charles M.C. Chan197a0122015-04-08 18:15:34 +0800154 bbChecksum.put(ipv6Parent.getSourceAddress());
155 bbChecksum.put(ipv6Parent.getDestinationAddress());
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800156 } else {
157 // NOTE: IPv6 source and destination addresses unknown. Use zeroes.
158 bbChecksum.put(ZERO_ADDRESS);
159 bbChecksum.put(ZERO_ADDRESS);
160 }
Dusan Pajin79f515f2015-02-11 16:40:34 +0100161 bbChecksum.putInt(HEADER_LENGTH + payloadLength);
162 bbChecksum.put((byte) 0);
163 bbChecksum.put((byte) 0);
164 bbChecksum.put((byte) 0);
165 bbChecksum.put(IPv6.PROTOCOL_ICMP6);
166 bbChecksum.put(this.icmpType);
167 bbChecksum.put(this.icmpCode);
168 bbChecksum.put((byte) 0);
169 bbChecksum.put((byte) 0);
170
171 bbData.put(this.icmpType);
172 bbData.put(this.icmpCode);
173 bbData.putShort(this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800174 if (payloadData != null) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100175 bbData.put(payloadData);
176 bbChecksum.put(payloadData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800177 }
178
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800179 if (this.parent != null) {
180 if (this.parent instanceof IPv6) {
181 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
182 } else if (this.parent instanceof IExtensionHeader) {
183 ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
184 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800185 }
186
187 // compute checksum if needed
188 if (this.checksum == 0) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100189 bbData.rewind();
190 bbChecksum.rewind();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800191 int accumulation = 0;
192
Dusan Pajin79f515f2015-02-11 16:40:34 +0100193 for (int i = 0; i < checksumData.length / 2; ++i) {
194 accumulation += 0xffff & bbChecksum.getShort();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800195 }
196 // pad to an even number of shorts
Dusan Pajin79f515f2015-02-11 16:40:34 +0100197 if (checksumData.length % 2 > 0) {
198 accumulation += (bbChecksum.get() & 0xff) << 8;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800199 }
200
201 accumulation = (accumulation >> 16 & 0xffff)
202 + (accumulation & 0xffff);
203 this.checksum = (short) (~accumulation & 0xffff);
Dusan Pajin79f515f2015-02-11 16:40:34 +0100204 bbData.putShort(2, this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800205 }
206 return data;
207 }
208
209 /*
210 * (non-Javadoc)
211 *
212 * @see java.lang.Object#hashCode()
213 */
214 @Override
215 public int hashCode() {
216 final int prime = 5807;
217 int result = super.hashCode();
218 result = prime * result + this.icmpType;
219 result = prime * result + this.icmpCode;
220 result = prime * result + this.checksum;
221 return result;
222 }
223
224 /*
225 * (non-Javadoc)
226 *
227 * @see java.lang.Object#equals(java.lang.Object)
228 */
229 @Override
230 public boolean equals(final Object obj) {
231 if (this == obj) {
232 return true;
233 }
234 if (!super.equals(obj)) {
235 return false;
236 }
237 if (!(obj instanceof ICMP6)) {
238 return false;
239 }
240 final ICMP6 other = (ICMP6) obj;
241 if (this.icmpType != other.icmpType) {
242 return false;
243 }
244 if (this.icmpCode != other.icmpCode) {
245 return false;
246 }
247 if (this.checksum != other.checksum) {
248 return false;
249 }
250 return true;
251 }
252
253 @Override
254 public IPacket deserialize(final byte[] data, final int offset,
255 final int length) {
256 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
257 this.icmpType = bb.get();
258 this.icmpCode = bb.get();
259 this.checksum = bb.getShort();
260
261 IPacket payload;
262 if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
263 final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
264 .get(this.icmpType);
265 try {
266 payload = clazz.newInstance();
267 } catch (final Exception e) {
268 throw new RuntimeException(
269 "Error parsing payload for ICMP6 packet", e);
270 }
271 } else {
272 payload = new Data();
273 }
274 this.payload = payload.deserialize(data, bb.position(),
275 bb.limit() - bb.position());
276 this.payload.setParent(this);
277
278 return this;
279 }
280}