blob: 18c0143debedbbee50599bc4b580c44d129afbce [file] [log] [blame]
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -08001/*
2 * Copyright 2015 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 */
16package org.onlab.packet.ndp;
17
Jonathan Hart2a655752015-04-07 16:46:33 -070018import org.onlab.packet.BasePacket;
19import org.onlab.packet.DeserializationException;
20import org.onlab.packet.Deserializer;
21import org.onlab.packet.IPacket;
22
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080023import java.nio.ByteBuffer;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.List;
27
Jian Li5fc14292015-12-04 11:30:46 -080028import static com.google.common.base.MoreObjects.toStringHelper;
Jonathan Hart2a655752015-04-07 16:46:33 -070029import static org.onlab.packet.PacketUtils.checkInput;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080030
31/**
32 * Neighbor Discovery Protocol packet options.
33 */
34public class NeighborDiscoveryOptions extends BasePacket {
35 public static final byte TYPE_SOURCE_LL_ADDRESS = 1;
36 public static final byte TYPE_TARGET_LL_ADDRESS = 2;
37 public static final byte TYPE_PREFIX_INFORMATION = 3;
38 public static final byte TYPE_REDIRECTED_HEADER = 4;
39 public static final byte TYPE_MTU = 5;
40
Jonathan Hart2a655752015-04-07 16:46:33 -070041 public static final byte INITIAL_HEADER_REQUIRED = 2;
42
43 private static final String BUFFER_UNDERFLOW_ERROR =
44 "Not enough bytes in buffer to read option";
45
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080046 private final List<Option> options = new ArrayList<>();
47
48 /**
49 * Packet option.
50 */
51 public final class Option {
52 private final byte type;
53 private final byte[] data;
54
55 /**
56 * Constructor.
57 *
58 * @param type the option type
59 * @param data the option data
60 */
61 private Option(byte type, byte[] data) {
62 this.type = type;
63 this.data = Arrays.copyOfRange(data, 0, data.length);
64 }
65
66 /**
67 * Gets the option type.
68 *
69 * @return the option type
70 */
71 public byte type() {
72 return this.type;
73 }
74
75 /**
76 * Gets the option data.
77 *
78 * @return the option data
79 */
80 public byte[] data() {
81 return this.data;
82 }
83
84 /**
85 * Gets the option data length (in number of octets).
86 *
87 * @return the option data length (in number of octets)
88 */
89 public int dataLength() {
90 return data.length;
91 }
92
93 /**
94 * Gets the option length (in number of octets), including the type and
95 * length fields (one octet each).
96 *
97 * @return the option length (in number of octets), including the type
98 * and length fields
99 */
100 private int optionLength() {
101 return 2 + dataLength();
102 }
103
104 /**
105 * Gets the option length field value (in units of 8 octets).
106 *
107 * @return the option length field value (in units of 8 octets)
108 */
109 private byte optionLengthField() {
110 return (byte) ((optionLength() + 7) / 8);
111 }
112
113 /**
114 * Gets the option length on the wire (in number of octets).
115 *
116 * @return the option length on the wire (in number of octets)
117 */
118 private int optionWireLength() {
119 return 8 * optionLengthField();
120 }
Jian Li5fc14292015-12-04 11:30:46 -0800121
122 @Override
123 public String toString() {
124 StringBuilder sb = new StringBuilder();
125 sb.append("[");
126 sb.append("type= ");
127 sb.append(type);
128 sb.append("data= ");
129 sb.append(data);
130 sb.append("]");
131 return sb.toString();
132 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800133 }
134
135 /**
136 * Adds a Neighbor Discovery Protocol packet option.
137 *
138 * @param type the option type
139 * @param data the option data
140 * @return this
141 */
142 public NeighborDiscoveryOptions addOption(byte type, byte[] data) {
143 options.add(new Option(type, data));
144 return this;
145 }
146
147 /**
148 * Gets the Neighbor Discovery Protocol packet options.
149 *
150 * @return the Neighbor Discovery Protocol packet options
151 */
152 public List<NeighborDiscoveryOptions.Option> options() {
153 return this.options;
154 }
155
156 /**
157 * Checks whether any options are included.
158 *
159 * @return true if options are included, otherwise false
160 */
161 public boolean hasOptions() {
162 return !this.options.isEmpty();
163 }
164
165 @Override
166 public byte[] serialize() {
167 // Compute first the total length on the wire for all options
168
169 int wireLength = 0;
170
171 for (Option option : this.options) {
172 wireLength += option.optionWireLength();
173 }
174
175 final byte[] data = new byte[wireLength];
176 final ByteBuffer bb = ByteBuffer.wrap(data);
177
178 //
179 // Serialize all options
180 //
181 for (Option option : this.options) {
182 bb.put(option.type());
183 bb.put(option.optionLengthField());
184 bb.put(option.data());
185 // Add the padding
186 int paddingLength =
187 option.optionWireLength() - option.optionLength();
188 for (int i = 0; i < paddingLength; i++) {
189 bb.put((byte) 0);
190 }
191 }
192
193 return data;
194 }
195
196 @Override
197 public IPacket deserialize(byte[] data, int offset, int length) {
198 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
199
200 options.clear();
201
202 //
203 // Deserialize all options
204 //
205 while (bb.hasRemaining()) {
206 byte type = bb.get();
207 if (!bb.hasRemaining()) {
208 break;
209 }
210 byte lengthField = bb.get();
211 int dataLength = lengthField * 8; // The data length field is in
Jonathan Hart2a655752015-04-07 16:46:33 -0700212 // unit of 8 octets
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800213
214 // Exclude the type and length fields
215 if (dataLength < 2) {
216 break;
217 }
218 dataLength -= 2;
219
220 if (bb.remaining() < dataLength) {
221 break;
222 }
223 byte[] optionData = new byte[dataLength];
224 bb.get(optionData, 0, optionData.length);
225 addOption(type, optionData);
226 }
227
228 return this;
229 }
230
231 @Override
232 public int hashCode() {
233 final int prime = 31;
234 int result = 1;
235
236 for (Option option : this.options) {
237 result = prime * result + option.type();
238 result = prime * result + Arrays.hashCode(option.data());
239 }
240 return result;
241 }
242
243 @Override
244 public boolean equals(final Object obj) {
245 if (this == obj) {
246 return true;
247 }
248 if (obj instanceof NeighborDiscoveryOptions) {
249 NeighborDiscoveryOptions other = (NeighborDiscoveryOptions) obj;
250 return this.options.equals(other.options);
251 }
252 return false;
253 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700254
255 public static Deserializer<NeighborDiscoveryOptions> deserializer() {
256 return (data, offset, length) -> {
257 checkInput(data, offset, length, INITIAL_HEADER_REQUIRED);
258
259 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
260
261 NeighborDiscoveryOptions ndo = new NeighborDiscoveryOptions();
262
263 ndo.options.clear();
264
265 //
266 // Deserialize all options
267 //
268 while (bb.hasRemaining()) {
269 byte type = bb.get();
270 if (!bb.hasRemaining()) {
271 throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
272 }
273 byte lengthField = bb.get();
274 int dataLength = lengthField * 8; // The data length field is in
275 // unit of 8 octets
276
277 // Exclude the type and length fields
278 if (dataLength < 2) {
279 break;
280 }
281 dataLength -= 2;
282
283 if (bb.remaining() < dataLength) {
284 throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
285 }
286 byte[] optionData = new byte[dataLength];
287 bb.get(optionData, 0, optionData.length);
288 ndo.addOption(type, optionData);
289 }
290
291 return ndo;
292 };
293 }
Jian Li5fc14292015-12-04 11:30:46 -0800294
295 @Override
296 public String toString() {
297 return toStringHelper(getClass())
298 .toString();
299 // TODO: need to handle options
300 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800301}