blob: 4ad8f19d2e99433d4d23151824850de438b2d0b7 [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
21import java.nio.ByteBuffer;
22import java.util.HashMap;
23import java.util.Map;
24
25/**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080026 * Implements ICMPv6 packet format. (RFC 4443)
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080027 */
28public class ICMP6 extends BasePacket {
29 public static final byte HEADER_LENGTH = 4; // bytes
30
31 public static final byte ROUTER_SOLICITATION = (byte) 0x85;
32 public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86;
33 public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87;
34 public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88;
35 public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
36 new HashMap<>();
37
38 static {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080039 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.class);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080040 ICMP6.PROTOCOL_CLASS_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.class);
41 }
42
43 protected byte icmpType;
44 protected byte icmpCode;
45 protected short checksum;
46
47 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080048 * Gets ICMP6 type.
49 *
50 * @return the ICMP6 type
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080051 */
52 public byte getIcmpType() {
53 return this.icmpType;
54 }
55
56 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080057 * Sets ICMP6 type.
58 *
59 * @param icmpType the ICMP type to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080060 * @return this
61 */
62 public ICMP6 setIcmpType(final byte icmpType) {
63 this.icmpType = icmpType;
64 return this;
65 }
66
67 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080068 * Gets ICMP6 code.
69 *
70 * @return the ICMP6 code
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080071 */
72 public byte getIcmpCode() {
73 return this.icmpCode;
74 }
75
76 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080077 * Sets ICMP6 code.
78 *
79 * @param icmpCode the ICMP6 code to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080080 * @return this
81 */
82 public ICMP6 setIcmpCode(final byte icmpCode) {
83 this.icmpCode = icmpCode;
84 return this;
85 }
86
87 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080088 * Gets checksum.
89 *
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080090 * @return the checksum
91 */
92 public short getChecksum() {
93 return this.checksum;
94 }
95
96 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080097 * Sets checksum.
98 *
99 * @param checksum the checksum to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800100 * @return this
101 */
102 public ICMP6 setChecksum(final short checksum) {
103 this.checksum = checksum;
104 return this;
105 }
106
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800107 @Override
108 public byte[] serialize() {
109 byte[] payloadData = null;
110 if (this.payload != null) {
111 this.payload.setParent(this);
112 payloadData = this.payload.serialize();
113 }
114
115 int payloadLength = payloadData == null ? 0 : (short) payloadData.length;
116
117 final byte[] data = new byte[HEADER_LENGTH + payloadLength];
118 final ByteBuffer bb = ByteBuffer.wrap(data);
119
120 bb.put(this.icmpType);
121 bb.put(this.icmpCode);
122 bb.putShort(this.checksum);
123 if (payloadData != null) {
124 bb.put(payloadData);
125 }
126
127 if (this.parent != null && this.parent instanceof IPv6) {
128 ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6);
129 }
130
131 // compute checksum if needed
132 if (this.checksum == 0) {
133 bb.rewind();
134 int accumulation = 0;
135
136 for (int i = 0; i < data.length / 2; ++i) {
137 accumulation += 0xffff & bb.getShort();
138 }
139 // pad to an even number of shorts
140 if (data.length % 2 > 0) {
141 accumulation += (bb.get() & 0xff) << 8;
142 }
143
144 accumulation = (accumulation >> 16 & 0xffff)
145 + (accumulation & 0xffff);
146 this.checksum = (short) (~accumulation & 0xffff);
147 bb.putShort(2, this.checksum);
148 }
149 return data;
150 }
151
152 /*
153 * (non-Javadoc)
154 *
155 * @see java.lang.Object#hashCode()
156 */
157 @Override
158 public int hashCode() {
159 final int prime = 5807;
160 int result = super.hashCode();
161 result = prime * result + this.icmpType;
162 result = prime * result + this.icmpCode;
163 result = prime * result + this.checksum;
164 return result;
165 }
166
167 /*
168 * (non-Javadoc)
169 *
170 * @see java.lang.Object#equals(java.lang.Object)
171 */
172 @Override
173 public boolean equals(final Object obj) {
174 if (this == obj) {
175 return true;
176 }
177 if (!super.equals(obj)) {
178 return false;
179 }
180 if (!(obj instanceof ICMP6)) {
181 return false;
182 }
183 final ICMP6 other = (ICMP6) obj;
184 if (this.icmpType != other.icmpType) {
185 return false;
186 }
187 if (this.icmpCode != other.icmpCode) {
188 return false;
189 }
190 if (this.checksum != other.checksum) {
191 return false;
192 }
193 return true;
194 }
195
196 @Override
197 public IPacket deserialize(final byte[] data, final int offset,
198 final int length) {
199 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
200 this.icmpType = bb.get();
201 this.icmpCode = bb.get();
202 this.checksum = bb.getShort();
203
204 IPacket payload;
205 if (ICMP6.PROTOCOL_CLASS_MAP.containsKey(this.icmpType)) {
206 final Class<? extends IPacket> clazz = ICMP6.PROTOCOL_CLASS_MAP
207 .get(this.icmpType);
208 try {
209 payload = clazz.newInstance();
210 } catch (final Exception e) {
211 throw new RuntimeException(
212 "Error parsing payload for ICMP6 packet", e);
213 }
214 } else {
215 payload = new Data();
216 }
217 this.payload = payload.deserialize(data, bb.position(),
218 bb.limit() - bb.position());
219 this.payload.setParent(this);
220
221 return this;
222 }
223}