blob: 03d3102cc45ce5c013fb8bd2b709a7b9a34c6d7a [file] [log] [blame]
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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
37 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
38 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
39 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
40 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080041 public static final byte REDIRECT = (byte) 0x89;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080042 public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
43 new HashMap<>();
44
45 static {
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080046 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.class);
47 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.class);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080048 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080049 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.class);
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080050 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.REDIRECT, Redirect.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080051 }
52
53 protected byte icmpType;
54 protected byte icmpCode;
55 protected short checksum;
56
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -080057 private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH];
58
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080059 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080060 * Gets ICMP6 type.
61 *
62 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080063 */
64 public byte getIcmpType() {
65 return this.icmpType;
66 }
67
68 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080069 * Sets ICMP6 type.
70 *
71 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080072 * @return this
73 */
74 public ICMP6 setIcmpType(final byte icmpType) {
75 this.icmpType = icmpType;
76 return this;
77 }
78
79 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080080 * Gets ICMP6 code.
81 *
82 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080083 */
84 public byte getIcmpCode() {
85 return this.icmpCode;
86 }
87
88 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080089 * Sets ICMP6 code.
90 *
91 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080092 * @return this
93 */
94 public ICMP6 setIcmpCode(final byte icmpCode) {
95 this.icmpCode = icmpCode;
96 return this;
97 }
98
99 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800100 * Gets checksum.
101 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800102 * @return the checksum
103 */
104 public short getChecksum() {
105 return this.checksum;
106 }
107
108 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800109 * Sets checksum.
110 *
111 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800112 * @return this
113 */
114 public ICMP6 setChecksum(final short checksum) {
115 this.checksum = checksum;
116 return this;
117 }
118
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800119 @Override
120 public byte[] serialize() {
121 byte[] payloadData = null;
122 if (this.payload != null) {
123 this.payload.setParent(this);
124 payloadData = this.payload.serialize();
125 }
126
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800127 int payloadLength = 0;
128 if (payloadData != null) {
129 payloadLength = payloadData.length;
130 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800131
132 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
Dusan Pajin79f515f2015-02-11 16:40:34 +0100133 final ByteBuffer bbData = ByteBuffer.wrap(data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800134
Dusan Pajin79f515f2015-02-11 16:40:34 +0100135 // Creating ByteBuffer for checksum calculation
136 final byte[] checksumData =
137 new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
138 final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
139
140 //
141 // Creating IPv6 Pseudo Header for checksum calculation according
142 // to RFC 4443 and RFC 2460
143 //
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800144 IPv6 ipv6Parent = null;
145 for (IPacket p = this.parent; p != null; p = p.getParent()) {
146 if (p instanceof IPv6) {
147 ipv6Parent = (IPv6) p;
148 break;
149 }
150 }
151 if (ipv6Parent != null) {
152 bbChecksum.put(((IPv6) ipv6Parent).getSourceAddress());
153 bbChecksum.put(((IPv6) ipv6Parent).getDestinationAddress());
154 } else {
155 // NOTE: IPv6 source and destination addresses unknown. Use zeroes.
156 bbChecksum.put(ZERO_ADDRESS);
157 bbChecksum.put(ZERO_ADDRESS);
158 }
Dusan Pajin79f515f2015-02-11 16:40:34 +0100159 bbChecksum.putInt(HEADER_LENGTH + payloadLength);
160 bbChecksum.put((byte) 0);
161 bbChecksum.put((byte) 0);
162 bbChecksum.put((byte) 0);
163 bbChecksum.put(IPv6.PROTOCOL_ICMP6);
164 bbChecksum.put(this.icmpType);
165 bbChecksum.put(this.icmpCode);
166 bbChecksum.put((byte) 0);
167 bbChecksum.put((byte) 0);
168
169 bbData.put(this.icmpType);
170 bbData.put(this.icmpCode);
171 bbData.putShort(this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800172 if (payloadData != null) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100173 bbData.put(payloadData);
174 bbChecksum.put(payloadData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800175 }
176
Pavlin Radoslavovc6aef922015-02-27 10:23:04 -0800177 if (this.parent != null) {
178 if (this.parent instanceof IPv6) {
179 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
180 } else if (this.parent instanceof IExtensionHeader) {
181 ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
182 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800183 }
184
185 // compute checksum if needed
186 if (this.checksum == 0) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100187 bbData.rewind();
188 bbChecksum.rewind();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800189 int accumulation = 0;
190
Dusan Pajin79f515f2015-02-11 16:40:34 +0100191 for (int i = 0; i < checksumData.length / 2; ++i) {
192 accumulation += 0xffff & bbChecksum.getShort();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800193 }
194 // pad to an even number of shorts
Dusan Pajin79f515f2015-02-11 16:40:34 +0100195 if (checksumData.length % 2 > 0) {
196 accumulation += (bbChecksum.get() & 0xff) << 8;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800197 }
198
199 accumulation = (accumulation >> 16 & 0xffff)
200 + (accumulation & 0xffff);
201 this.checksum = (short) (~accumulation & 0xffff);
Dusan Pajin79f515f2015-02-11 16:40:34 +0100202 bbData.putShort(2, this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800203 }
204 return data;
205 }
206
207 /*
208 * (non-Javadoc)
209 *
210 * @see java.lang.Object#hashCode()
211 */
212 @Override
213 public int hashCode() {
214 final int prime = 5807;
215 int result = super.hashCode();
216 result = prime * result + this.icmpType;
217 result = prime * result + this.icmpCode;
218 result = prime * result + this.checksum;
219 return result;
220 }
221
222 /*
223 * (non-Javadoc)
224 *
225 * @see java.lang.Object#equals(java.lang.Object)
226 */
227 @Override
228 public boolean equals(final Object obj) {
229 if (this == obj) {
230 return true;
231 }
232 if (!super.equals(obj)) {
233 return false;
234 }
235 if (!(obj instanceof ICMP6)) {
236 return false;
237 }
238 final ICMP6 other = (ICMP6) obj;
239 if (this.icmpType != other.icmpType) {
240 return false;
241 }
242 if (this.icmpCode != other.icmpCode) {
243 return false;
244 }
245 if (this.checksum != other.checksum) {
246 return false;
247 }
248 return true;
249 }
250
251 @Override
252 public IPacket deserialize(final byte[] data, final int offset,
253 final int length) {
254 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
255 this.icmpType = bb.get();
256 this.icmpCode = bb.get();
257 this.checksum = bb.getShort();
258
259 IPacket payload;
260 if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
261 final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
262 .get(this.icmpType);
263 try {
264 payload = clazz.newInstance();
265 } catch (final Exception e) {
266 throw new RuntimeException(
267 "Error parsing payload for ICMP6 packet", e);
268 }
269 } else {
270 payload = new Data();
271 }
272 this.payload = payload.deserialize(data, bb.position(),
273 bb.limit() - bb.position());
274 this.payload.setParent(this);
275
276 return this;
277 }
278}