blob: fbbc9efefe50163f610fc77648ca1199efe95da7 [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
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080021import org.onlab.packet.ndp.NeighborAdvertisement;
22import org.onlab.packet.ndp.NeighborSolicitation;
23import org.onlab.packet.ndp.Redirect;
24import org.onlab.packet.ndp.RouterAdvertisement;
25import org.onlab.packet.ndp.RouterSolicitation;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080026import java.nio.ByteBuffer;
27import java.util.HashMap;
28import java.util.Map;
29
30/**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080031 * Implements ICMPv6 packet format. (RFC 4443)
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080032 */
33public class ICMP6 extends BasePacket {
34 public static final byte HEADER_LENGTH = 4; // bytes
35
36 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
37 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
38 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
39 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080040 public static final byte REDIRECT = (byte) 0x89;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080041 public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
42 new HashMap<>();
43
44 static {
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080045 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.class);
46 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.class);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080047 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080048 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.class);
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080049 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.REDIRECT, Redirect.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080050 }
51
52 protected byte icmpType;
53 protected byte icmpCode;
54 protected short checksum;
55
56 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080057 * Gets ICMP6 type.
58 *
59 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080060 */
61 public byte getIcmpType() {
62 return this.icmpType;
63 }
64
65 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080066 * Sets ICMP6 type.
67 *
68 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080069 * @return this
70 */
71 public ICMP6 setIcmpType(final byte icmpType) {
72 this.icmpType = icmpType;
73 return this;
74 }
75
76 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080077 * Gets ICMP6 code.
78 *
79 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080080 */
81 public byte getIcmpCode() {
82 return this.icmpCode;
83 }
84
85 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080086 * Sets ICMP6 code.
87 *
88 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080089 * @return this
90 */
91 public ICMP6 setIcmpCode(final byte icmpCode) {
92 this.icmpCode = icmpCode;
93 return this;
94 }
95
96 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080097 * Gets checksum.
98 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080099 * @return the checksum
100 */
101 public short getChecksum() {
102 return this.checksum;
103 }
104
105 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800106 * Sets checksum.
107 *
108 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800109 * @return this
110 */
111 public ICMP6 setChecksum(final short checksum) {
112 this.checksum = checksum;
113 return this;
114 }
115
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800116 @Override
117 public byte[] serialize() {
118 byte[] payloadData = null;
119 if (this.payload != null) {
120 this.payload.setParent(this);
121 payloadData = this.payload.serialize();
122 }
123
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800124 int payloadLength = 0;
125 if (payloadData != null) {
126 payloadLength = payloadData.length;
127 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800128
129 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
Dusan Pajin79f515f2015-02-11 16:40:34 +0100130 final ByteBuffer bbData = ByteBuffer.wrap(data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800131
Dusan Pajin79f515f2015-02-11 16:40:34 +0100132 // Creating ByteBuffer for checksum calculation
133 final byte[] checksumData =
134 new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength];
135 final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData);
136
137 //
138 // Creating IPv6 Pseudo Header for checksum calculation according
139 // to RFC 4443 and RFC 2460
140 //
141 bbChecksum.put(((IPv6) this.parent).getSourceAddress());
142 bbChecksum.put(((IPv6) this.parent).getDestinationAddress());
143 bbChecksum.putInt(HEADER_LENGTH + payloadLength);
144 bbChecksum.put((byte) 0);
145 bbChecksum.put((byte) 0);
146 bbChecksum.put((byte) 0);
147 bbChecksum.put(IPv6.PROTOCOL_ICMP6);
148 bbChecksum.put(this.icmpType);
149 bbChecksum.put(this.icmpCode);
150 bbChecksum.put((byte) 0);
151 bbChecksum.put((byte) 0);
152
153 bbData.put(this.icmpType);
154 bbData.put(this.icmpCode);
155 bbData.putShort(this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800156 if (payloadData != null) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100157 bbData.put(payloadData);
158 bbChecksum.put(payloadData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800159 }
160
161 if (this.parent != null && this.parent instanceof IPv6) {
162 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
163 }
164
165 // compute checksum if needed
166 if (this.checksum == 0) {
Dusan Pajin79f515f2015-02-11 16:40:34 +0100167 bbData.rewind();
168 bbChecksum.rewind();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800169 int accumulation = 0;
170
Dusan Pajin79f515f2015-02-11 16:40:34 +0100171 for (int i = 0; i < checksumData.length / 2; ++i) {
172 accumulation += 0xffff & bbChecksum.getShort();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800173 }
174 // pad to an even number of shorts
Dusan Pajin79f515f2015-02-11 16:40:34 +0100175 if (checksumData.length % 2 > 0) {
176 accumulation += (bbChecksum.get() & 0xff) << 8;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800177 }
178
179 accumulation = (accumulation >> 16 & 0xffff)
180 + (accumulation & 0xffff);
181 this.checksum = (short) (~accumulation & 0xffff);
Dusan Pajin79f515f2015-02-11 16:40:34 +0100182 bbData.putShort(2, this.checksum);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800183 }
184 return data;
185 }
186
187 /*
188 * (non-Javadoc)
189 *
190 * @see java.lang.Object#hashCode()
191 */
192 @Override
193 public int hashCode() {
194 final int prime = 5807;
195 int result = super.hashCode();
196 result = prime * result + this.icmpType;
197 result = prime * result + this.icmpCode;
198 result = prime * result + this.checksum;
199 return result;
200 }
201
202 /*
203 * (non-Javadoc)
204 *
205 * @see java.lang.Object#equals(java.lang.Object)
206 */
207 @Override
208 public boolean equals(final Object obj) {
209 if (this == obj) {
210 return true;
211 }
212 if (!super.equals(obj)) {
213 return false;
214 }
215 if (!(obj instanceof ICMP6)) {
216 return false;
217 }
218 final ICMP6 other = (ICMP6) obj;
219 if (this.icmpType != other.icmpType) {
220 return false;
221 }
222 if (this.icmpCode != other.icmpCode) {
223 return false;
224 }
225 if (this.checksum != other.checksum) {
226 return false;
227 }
228 return true;
229 }
230
231 @Override
232 public IPacket deserialize(final byte[] data, final int offset,
233 final int length) {
234 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
235 this.icmpType = bb.get();
236 this.icmpCode = bb.get();
237 this.checksum = bb.getShort();
238
239 IPacket payload;
240 if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
241 final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
242 .get(this.icmpType);
243 try {
244 payload = clazz.newInstance();
245 } catch (final Exception e) {
246 throw new RuntimeException(
247 "Error parsing payload for ICMP6 packet", e);
248 }
249 } else {
250 payload = new Data();
251 }
252 this.payload = payload.deserialize(data, bb.position(),
253 bb.limit() - bb.position());
254 this.payload.setParent(this);
255
256 return this;
257 }
258}