blob: b6aa691bc2939c944b27e0c356d064cfa02a98af [file] [log] [blame]
Yi Tseng60bf35a2017-06-02 17:05:48 -07001/*
2 * Copyright 2017-present 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 */
16
17package org.onlab.packet;
18
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Lists;
21
22import java.nio.ByteBuffer;
23import java.util.List;
24import java.util.Set;
25
26import static com.google.common.base.Preconditions.checkNotNull;
27
28/**
29 * Representation of an DHCPv6 Packet.
30 * Base on RFC-3315.
31 */
32public class DHCP6 extends BasePacket {
33 // size of different field of option
34 private static final int OPT_CODE_SIZE = 2;
35 private static final int OPT_LEN_SIZE = 2;
36
37 // default size of DHCPv6 payload (without options)
38 private static final int DHCP6_DEFAULT_SIZE = 4;
39
40 // default size of DHCPv6 relay message payload (without options)
41 private static final int DHCP6_RELAY_MSG_SIZE = 34;
42 private static final int IPV6_ADDR_LEN = 16;
43
44 // masks & offsets for default DHCPv6 header
45 private static final int MSG_TYPE_OFFSET = 24;
46 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
47
48 // Relay message types
49 private static final Set<Byte> RELAY_MSG_TYPES =
50 ImmutableSet.of(MsgType.RELAY_FORW.value,
51 MsgType.RELAY_REPL.value);
52
53 /**
54 * DHCPv6 message type.
55 */
56 public enum MsgType {
57 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
58 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
59 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
60 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
61 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13);
62
63 protected byte value;
64 MsgType(final byte value) {
65 this.value = value;
66 }
67 public byte value() {
68 return this.value;
69 }
70 }
71
72 /**
73 * DHCPv6 option code.
74 */
75 public enum OptionCode {
76 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
77 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
78 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
79 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
80 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
81 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20);
82
83 protected short value;
84 OptionCode(final short value) {
85 this.value = value;
86 }
87 public short value() {
88 return this.value;
89 }
90 }
91
92 // general field
93 private byte msgType; // 1 byte
94 private List<DHCP6Option> options;
95
96 // non-relay field
97 private int transactionId; // 3 bytes
98
99 // relay field
100 private byte hopCount; // 1 byte
101 private byte[] linkAddress; // 16 bytes
102 private byte[] peerAddress; // 16 bytes
103
104 /**
105 * Creates new DHCPv6 object.
106 */
107 public DHCP6() {
108 options = Lists.newArrayList();
109 }
110
111 @Override
112 public byte[] serialize() {
113 int payloadLength = options.stream()
114 .mapToInt(DHCP6Option::getLength)
115 .sum();
116
117 // 2 bytes code and 2 bytes length
118 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
119
120 if (RELAY_MSG_TYPES.contains(msgType)) {
121 payloadLength += DHCP6_RELAY_MSG_SIZE;
122 } else {
123 payloadLength += DHCP6_DEFAULT_SIZE;
124 }
125
126 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
127
128 if (RELAY_MSG_TYPES.contains(msgType)) {
129 bb.put(msgType);
130 bb.put(hopCount);
131 bb.put(linkAddress);
132 bb.put(peerAddress);
133 } else {
134 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
135 bb.putInt(defaultHeader);
136 }
137
138 // serialize options
139 options.forEach(option -> {
140 bb.putShort(option.getCode());
141 bb.putShort(option.getLength());
142 bb.put(option.getData());
143 });
144
145 return bb.array();
146 }
147
148 /**
149 * Returns a deserializer for DHCPv6.
150 *
151 * @return the deserializer for DHCPv6
152 */
153 public static Deserializer<DHCP6> deserializer() {
154 return (data, offset, length) -> {
155 DHCP6 dhcp6 = new DHCP6();
156
157 checkNotNull(data);
158
159 if (offset < 0 || length < 0 ||
160 length > data.length || offset >= data.length ||
161 offset + length > data.length) {
162 throw new DeserializationException("Illegal offset or length");
163 }
164
165 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
166 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
167 throw new DeserializationException(
168 "Buffer underflow while reading DHCPv6 option");
169 }
170
171 // peek message type
172 dhcp6.msgType = bb.array()[0];
173 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
174 bb.get(); // drop message type
175 dhcp6.hopCount = bb.get();
176 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
177 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
178
179 bb.get(dhcp6.linkAddress);
180 bb.get(dhcp6.peerAddress);
181 } else {
182 // get msg type + transaction id (1 + 3 bytes)
183 int defaultHeader = bb.getInt();
184 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
185 }
186
187 dhcp6.options = Lists.newArrayList();
188 while (bb.remaining() >= OPT_CODE_SIZE) {
189 DHCP6Option option = new DHCP6Option();
190 short code = bb.getShort();
191 if (bb.remaining() < OPT_LEN_SIZE) {
192 throw new DeserializationException(
193 "Buffer underflow while reading DHCPv6 option");
194 }
195
196 short optionLen = bb.getShort();
197 if (bb.remaining() < optionLen) {
198 throw new DeserializationException(
199 "Buffer underflow while reading DHCPv6 option");
200 }
201
202 byte[] optionData = new byte[optionLen];
203 bb.get(optionData);
204
205 option.setCode(code);
206 option.setLength(optionLen);
207 option.setData(optionData);
208 dhcp6.options.add(option);
209 }
210
211 return dhcp6;
212 };
213 }
214
215 @Override
216 public IPacket deserialize(byte[] data, int offset, int length) {
217 try {
218 return deserializer().deserialize(data, offset, length);
219 } catch (DeserializationException e) {
220 return null;
221 }
222 }
223
224 /**
225 * Gets the message type of this DHCPv6 packet.
226 *
227 * @return the message type
228 */
229 public byte getMsgType() {
230 return msgType;
231 }
232
233 /**
234 * Gets options from this DHCPv6 packet.
235 *
236 * @return DHCPv6 options
237 */
238 public List<DHCP6Option> getOptions() {
239 return options;
240 }
241
242 /**
243 * Gets the transaction ID of this DHCPv6 packet.
244 *
245 * @return the transaction ID
246 */
247 public int getTransactionId() {
248 return transactionId;
249 }
250
251 /**
252 * Gets the hop count of this DHCPv6 relay message.
253 *
254 * @return the hop count
255 */
256 public byte getHopCount() {
257 return hopCount;
258 }
259
260 /**
261 * Gets the link address of this DHCPv6 relay message.
262 *
263 * @return the link address
264 */
265 public byte[] getLinkAddress() {
266 return linkAddress;
267 }
268
269 /**
270 * Gets the peer address of this DHCPv6 relay message.
271 *
272 * @return the link address
273 */
274 public byte[] getPeerAddress() {
275 return peerAddress;
276 }
277
278 /**
279 * Sets message type.
280 *
281 * @param msgType the message type
282 */
283 public void setMsgType(byte msgType) {
284 this.msgType = msgType;
285 }
286
287 /**
288 * Sets options.
289 *
290 * @param options the options
291 */
292 public void setOptions(List<DHCP6Option> options) {
293 this.options = options;
294 }
295
296 /**
297 * Sets transaction id.
298 *
299 * @param transactionId the transaction id
300 */
301 public void setTransactionId(int transactionId) {
302 this.transactionId = transactionId;
303 }
304
305 /**
306 * Sets hop count.
307 *
308 * @param hopCount the hop count
309 */
310 public void setHopCount(byte hopCount) {
311 this.hopCount = hopCount;
312 }
313
314 /**
315 * Sets link address.
316 *
317 * @param linkAddress the link address
318 */
319 public void setLinkAddress(byte[] linkAddress) {
320 this.linkAddress = linkAddress;
321 }
322
323 /**
324 * Sets peer address.
325 *
326 * @param peerAddress the peer address
327 */
328 public void setPeerAddress(byte[] peerAddress) {
329 this.peerAddress = peerAddress;
330 }
331}