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