blob: b8561b1c3ee9d2500c29ee3bae1a60f845a933eb [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
18import java.nio.ByteBuffer;
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.List;
22
23import org.onlab.packet.BasePacket;
24import org.onlab.packet.IPacket;
25
26/**
27 * Neighbor Discovery Protocol packet options.
28 */
29public class NeighborDiscoveryOptions extends BasePacket {
30 public static final byte TYPE_SOURCE_LL_ADDRESS = 1;
31 public static final byte TYPE_TARGET_LL_ADDRESS = 2;
32 public static final byte TYPE_PREFIX_INFORMATION = 3;
33 public static final byte TYPE_REDIRECTED_HEADER = 4;
34 public static final byte TYPE_MTU = 5;
35
36 private final List<Option> options = new ArrayList<>();
37
38 /**
39 * Packet option.
40 */
41 public final class Option {
42 private final byte type;
43 private final byte[] data;
44
45 /**
46 * Constructor.
47 *
48 * @param type the option type
49 * @param data the option data
50 */
51 private Option(byte type, byte[] data) {
52 this.type = type;
53 this.data = Arrays.copyOfRange(data, 0, data.length);
54 }
55
56 /**
57 * Gets the option type.
58 *
59 * @return the option type
60 */
61 public byte type() {
62 return this.type;
63 }
64
65 /**
66 * Gets the option data.
67 *
68 * @return the option data
69 */
70 public byte[] data() {
71 return this.data;
72 }
73
74 /**
75 * Gets the option data length (in number of octets).
76 *
77 * @return the option data length (in number of octets)
78 */
79 public int dataLength() {
80 return data.length;
81 }
82
83 /**
84 * Gets the option length (in number of octets), including the type and
85 * length fields (one octet each).
86 *
87 * @return the option length (in number of octets), including the type
88 * and length fields
89 */
90 private int optionLength() {
91 return 2 + dataLength();
92 }
93
94 /**
95 * Gets the option length field value (in units of 8 octets).
96 *
97 * @return the option length field value (in units of 8 octets)
98 */
99 private byte optionLengthField() {
100 return (byte) ((optionLength() + 7) / 8);
101 }
102
103 /**
104 * Gets the option length on the wire (in number of octets).
105 *
106 * @return the option length on the wire (in number of octets)
107 */
108 private int optionWireLength() {
109 return 8 * optionLengthField();
110 }
111 }
112
113 /**
114 * Adds a Neighbor Discovery Protocol packet option.
115 *
116 * @param type the option type
117 * @param data the option data
118 * @return this
119 */
120 public NeighborDiscoveryOptions addOption(byte type, byte[] data) {
121 options.add(new Option(type, data));
122 return this;
123 }
124
125 /**
126 * Gets the Neighbor Discovery Protocol packet options.
127 *
128 * @return the Neighbor Discovery Protocol packet options
129 */
130 public List<NeighborDiscoveryOptions.Option> options() {
131 return this.options;
132 }
133
134 /**
135 * Checks whether any options are included.
136 *
137 * @return true if options are included, otherwise false
138 */
139 public boolean hasOptions() {
140 return !this.options.isEmpty();
141 }
142
143 @Override
144 public byte[] serialize() {
145 // Compute first the total length on the wire for all options
146
147 int wireLength = 0;
148
149 for (Option option : this.options) {
150 wireLength += option.optionWireLength();
151 }
152
153 final byte[] data = new byte[wireLength];
154 final ByteBuffer bb = ByteBuffer.wrap(data);
155
156 //
157 // Serialize all options
158 //
159 for (Option option : this.options) {
160 bb.put(option.type());
161 bb.put(option.optionLengthField());
162 bb.put(option.data());
163 // Add the padding
164 int paddingLength =
165 option.optionWireLength() - option.optionLength();
166 for (int i = 0; i < paddingLength; i++) {
167 bb.put((byte) 0);
168 }
169 }
170
171 return data;
172 }
173
174 @Override
175 public IPacket deserialize(byte[] data, int offset, int length) {
176 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
177
178 options.clear();
179
180 //
181 // Deserialize all options
182 //
183 while (bb.hasRemaining()) {
184 byte type = bb.get();
185 if (!bb.hasRemaining()) {
186 break;
187 }
188 byte lengthField = bb.get();
189 int dataLength = lengthField * 8; // The data length field is in
190 // unit of 8 octets
191
192 // Exclude the type and length fields
193 if (dataLength < 2) {
194 break;
195 }
196 dataLength -= 2;
197
198 if (bb.remaining() < dataLength) {
199 break;
200 }
201 byte[] optionData = new byte[dataLength];
202 bb.get(optionData, 0, optionData.length);
203 addOption(type, optionData);
204 }
205
206 return this;
207 }
208
209 @Override
210 public int hashCode() {
211 final int prime = 31;
212 int result = 1;
213
214 for (Option option : this.options) {
215 result = prime * result + option.type();
216 result = prime * result + Arrays.hashCode(option.data());
217 }
218 return result;
219 }
220
221 @Override
222 public boolean equals(final Object obj) {
223 if (this == obj) {
224 return true;
225 }
226 if (obj instanceof NeighborDiscoveryOptions) {
227 NeighborDiscoveryOptions other = (NeighborDiscoveryOptions) obj;
228 return this.options.equals(other.options);
229 }
230 return false;
231 }
232}