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