blob: 13deab2093ebe12a9def7a6eb4349bbd0535c028 [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
124 int payloadLength = payloadData == null ? 0 : (short) payloadData.length;
125
126 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
127 final ByteBuffer bb = ByteBuffer.wrap(data);
128
129 bb.put(this.icmpType);
130 bb.put(this.icmpCode);
131 bb.putShort(this.checksum);
132 if (payloadData != null) {
133 bb.put(payloadData);
134 }
135
136 if (this.parent != null && this.parent instanceof IPv6) {
137 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
138 }
139
140 // compute checksum if needed
141 if (this.checksum == 0) {
142 bb.rewind();
143 int accumulation = 0;
144
145 for (int i = 0; i < data.length / 2; ++i) {
146 accumulation += 0xffff & bb.getShort();
147 }
148 // pad to an even number of shorts
149 if (data.length % 2 > 0) {
150 accumulation += (bb.get() & 0xff) << 8;
151 }
152
153 accumulation = (accumulation >> 16 & 0xffff)
154 + (accumulation & 0xffff);
155 this.checksum = (short) (~accumulation & 0xffff);
156 bb.putShort(2, this.checksum);
157 }
158 return data;
159 }
160
161 /*
162 * (non-Javadoc)
163 *
164 * @see java.lang.Object#hashCode()
165 */
166 @Override
167 public int hashCode() {
168 final int prime = 5807;
169 int result = super.hashCode();
170 result = prime * result + this.icmpType;
171 result = prime * result + this.icmpCode;
172 result = prime * result + this.checksum;
173 return result;
174 }
175
176 /*
177 * (non-Javadoc)
178 *
179 * @see java.lang.Object#equals(java.lang.Object)
180 */
181 @Override
182 public boolean equals(final Object obj) {
183 if (this == obj) {
184 return true;
185 }
186 if (!super.equals(obj)) {
187 return false;
188 }
189 if (!(obj instanceof ICMP6)) {
190 return false;
191 }
192 final ICMP6 other = (ICMP6) obj;
193 if (this.icmpType != other.icmpType) {
194 return false;
195 }
196 if (this.icmpCode != other.icmpCode) {
197 return false;
198 }
199 if (this.checksum != other.checksum) {
200 return false;
201 }
202 return true;
203 }
204
205 @Override
206 public IPacket deserialize(final byte[] data, final int offset,
207 final int length) {
208 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
209 this.icmpType = bb.get();
210 this.icmpCode = bb.get();
211 this.checksum = bb.getShort();
212
213 IPacket payload;
214 if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
215 final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
216 .get(this.icmpType);
217 try {
218 payload = clazz.newInstance();
219 } catch (final Exception e) {
220 throw new RuntimeException(
221 "Error parsing payload for ICMP6 packet", e);
222 }
223 } else {
224 payload = new Data();
225 }
226 this.payload = payload.deserialize(data, bb.position(),
227 bb.limit() - bb.position());
228 this.payload.setParent(this);
229
230 return this;
231 }
232}