blob: d6b1df0c2998c35c27ed7b91032b307280569b52 [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
Ray Milkeyf0c47612017-09-28 11:29:38 -070018import com.google.common.base.MoreObjects;
19import com.google.common.base.Objects;
Jonathan Hart2a655752015-04-07 16:46:33 -070020import org.onlab.packet.BasePacket;
21import org.onlab.packet.DeserializationException;
22import org.onlab.packet.Deserializer;
Jonathan Hart2a655752015-04-07 16:46:33 -070023
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080024import java.nio.ByteBuffer;
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
28
Jian Li5fc14292015-12-04 11:30:46 -080029import static com.google.common.base.MoreObjects.toStringHelper;
Jonathan Hart2a655752015-04-07 16:46:33 -070030import static org.onlab.packet.PacketUtils.checkInput;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080031
32/**
33 * Neighbor Discovery Protocol packet options.
34 */
35public class NeighborDiscoveryOptions extends BasePacket {
36 public static final byte TYPE_SOURCE_LL_ADDRESS = 1;
37 public static final byte TYPE_TARGET_LL_ADDRESS = 2;
38 public static final byte TYPE_PREFIX_INFORMATION = 3;
39 public static final byte TYPE_REDIRECTED_HEADER = 4;
40 public static final byte TYPE_MTU = 5;
41
Jonathan Hart2a655752015-04-07 16:46:33 -070042 public static final byte INITIAL_HEADER_REQUIRED = 2;
43
44 private static final String BUFFER_UNDERFLOW_ERROR =
45 "Not enough bytes in buffer to read option";
46
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080047 private final List<Option> options = new ArrayList<>();
48
49 /**
50 * Packet option.
51 */
52 public final class Option {
53 private final byte type;
54 private final byte[] data;
55
56 /**
57 * Constructor.
58 *
59 * @param type the option type
60 * @param data the option data
61 */
62 private Option(byte type, byte[] data) {
63 this.type = type;
64 this.data = Arrays.copyOfRange(data, 0, data.length);
65 }
66
67 /**
68 * Gets the option type.
69 *
70 * @return the option type
71 */
72 public byte type() {
73 return this.type;
74 }
75
76 /**
77 * Gets the option data.
78 *
79 * @return the option data
80 */
81 public byte[] data() {
82 return this.data;
83 }
84
85 /**
86 * Gets the option data length (in number of octets).
87 *
88 * @return the option data length (in number of octets)
89 */
90 public int dataLength() {
91 return data.length;
92 }
93
94 /**
95 * Gets the option length (in number of octets), including the type and
96 * length fields (one octet each).
97 *
98 * @return the option length (in number of octets), including the type
99 * and length fields
100 */
101 private int optionLength() {
102 return 2 + dataLength();
103 }
104
105 /**
106 * Gets the option length field value (in units of 8 octets).
107 *
108 * @return the option length field value (in units of 8 octets)
109 */
110 private byte optionLengthField() {
111 return (byte) ((optionLength() + 7) / 8);
112 }
113
114 /**
115 * Gets the option length on the wire (in number of octets).
116 *
117 * @return the option length on the wire (in number of octets)
118 */
119 private int optionWireLength() {
120 return 8 * optionLengthField();
121 }
Jian Li5fc14292015-12-04 11:30:46 -0800122
123 @Override
124 public String toString() {
rpatodiya45fa9ab2017-01-23 15:04:19 +0530125 return MoreObjects.toStringHelper(this).add("type", type).
126 add("data", data).toString();
Jian Li5fc14292015-12-04 11:30:46 -0800127 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800128 }
129
130 /**
131 * Adds a Neighbor Discovery Protocol packet option.
132 *
133 * @param type the option type
134 * @param data the option data
135 * @return this
136 */
137 public NeighborDiscoveryOptions addOption(byte type, byte[] data) {
138 options.add(new Option(type, data));
139 return this;
140 }
141
142 /**
143 * Gets the Neighbor Discovery Protocol packet options.
144 *
145 * @return the Neighbor Discovery Protocol packet options
146 */
147 public List<NeighborDiscoveryOptions.Option> options() {
148 return this.options;
149 }
150
151 /**
152 * Checks whether any options are included.
153 *
154 * @return true if options are included, otherwise false
155 */
156 public boolean hasOptions() {
157 return !this.options.isEmpty();
158 }
159
160 @Override
161 public byte[] serialize() {
162 // Compute first the total length on the wire for all options
163
164 int wireLength = 0;
165
166 for (Option option : this.options) {
167 wireLength += option.optionWireLength();
168 }
169
170 final byte[] data = new byte[wireLength];
171 final ByteBuffer bb = ByteBuffer.wrap(data);
172
173 //
174 // Serialize all options
175 //
176 for (Option option : this.options) {
177 bb.put(option.type());
178 bb.put(option.optionLengthField());
179 bb.put(option.data());
180 // Add the padding
181 int paddingLength =
182 option.optionWireLength() - option.optionLength();
183 for (int i = 0; i < paddingLength; i++) {
184 bb.put((byte) 0);
185 }
186 }
187
188 return data;
189 }
190
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800191
192 @Override
193 public int hashCode() {
rpatodiya45fa9ab2017-01-23 15:04:19 +0530194 return Objects.hashCode(this.options.toArray());
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800195 }
196
197 @Override
198 public boolean equals(final Object obj) {
199 if (this == obj) {
200 return true;
201 }
202 if (obj instanceof NeighborDiscoveryOptions) {
203 NeighborDiscoveryOptions other = (NeighborDiscoveryOptions) obj;
rpatodiya45fa9ab2017-01-23 15:04:19 +0530204 return Objects.equal(this.options, other.options);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800205 }
206 return false;
207 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700208
209 public static Deserializer<NeighborDiscoveryOptions> deserializer() {
210 return (data, offset, length) -> {
211 checkInput(data, offset, length, INITIAL_HEADER_REQUIRED);
212
213 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
214
215 NeighborDiscoveryOptions ndo = new NeighborDiscoveryOptions();
216
217 ndo.options.clear();
218
219 //
220 // Deserialize all options
221 //
222 while (bb.hasRemaining()) {
223 byte type = bb.get();
224 if (!bb.hasRemaining()) {
225 throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
226 }
227 byte lengthField = bb.get();
228 int dataLength = lengthField * 8; // The data length field is in
229 // unit of 8 octets
230
231 // Exclude the type and length fields
232 if (dataLength < 2) {
233 break;
234 }
235 dataLength -= 2;
236
237 if (bb.remaining() < dataLength) {
238 throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
239 }
240 byte[] optionData = new byte[dataLength];
241 bb.get(optionData, 0, optionData.length);
242 ndo.addOption(type, optionData);
243 }
244
245 return ndo;
246 };
247 }
Jian Li5fc14292015-12-04 11:30:46 -0800248
249 @Override
250 public String toString() {
251 return toStringHelper(getClass())
252 .toString();
253 // TODO: need to handle options
254 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800255}