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