blob: 8dbd1dca0ddbe5015eb50c45056bb993f2186e78 [file] [log] [blame]
Yi Tseng60bf35a2017-06-02 17:05:48 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Yi Tseng60bf35a2017-06-02 17:05:48 -07003 *
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
Yi Tsengca34e1d2017-07-18 16:16:25 -070019import com.google.common.collect.ImmutableMap;
Yi Tseng60bf35a2017-06-02 17:05:48 -070020import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Lists;
Yi Tsengca34e1d2017-07-18 16:16:25 -070022import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
23import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
24import org.onlab.packet.dhcp.Dhcp6IaNaOption;
25import org.onlab.packet.dhcp.Dhcp6IaTaOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000026import org.onlab.packet.dhcp.Dhcp6IaPdOption;
Yi Tsengc7403c22017-06-19 16:23:22 -070027import org.onlab.packet.dhcp.Dhcp6Option;
Yi Tsengca34e1d2017-07-18 16:16:25 -070028import org.onlab.packet.dhcp.Dhcp6RelayOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000029import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Yi Tseng60bf35a2017-06-02 17:05:48 -070030
31import java.nio.ByteBuffer;
32import java.util.List;
Yi Tsengca34e1d2017-07-18 16:16:25 -070033import java.util.Map;
Yi Tseng60bf35a2017-06-02 17:05:48 -070034import java.util.Set;
35
Yi Tsengca34e1d2017-07-18 16:16:25 -070036import static com.google.common.base.MoreObjects.toStringHelper;
Yi Tseng60bf35a2017-06-02 17:05:48 -070037import static com.google.common.base.Preconditions.checkNotNull;
38
39/**
40 * Representation of an DHCPv6 Packet.
41 * Base on RFC-3315.
42 */
43public class DHCP6 extends BasePacket {
Yi Tsengb4fdb042017-08-07 13:32:32 -070044 private static final int UNSIGNED_SHORT_MASK = 0xffff;
Yi Tseng60bf35a2017-06-02 17:05:48 -070045 // size of different field of option
46 private static final int OPT_CODE_SIZE = 2;
47 private static final int OPT_LEN_SIZE = 2;
48
49 // default size of DHCPv6 payload (without options)
50 private static final int DHCP6_DEFAULT_SIZE = 4;
51
52 // default size of DHCPv6 relay message payload (without options)
53 private static final int DHCP6_RELAY_MSG_SIZE = 34;
54 private static final int IPV6_ADDR_LEN = 16;
55
56 // masks & offsets for default DHCPv6 header
57 private static final int MSG_TYPE_OFFSET = 24;
58 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
59
60 // Relay message types
Yi Tsengca34e1d2017-07-18 16:16:25 -070061 public static final Set<Byte> RELAY_MSG_TYPES =
Yi Tseng60bf35a2017-06-02 17:05:48 -070062 ImmutableSet.of(MsgType.RELAY_FORW.value,
63 MsgType.RELAY_REPL.value);
64
65 /**
66 * DHCPv6 message type.
67 */
68 public enum MsgType {
69 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
70 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
71 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
72 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
73 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13);
74
75 protected byte value;
76 MsgType(final byte value) {
77 this.value = value;
78 }
79 public byte value() {
80 return this.value;
81 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +000082 public static MsgType getType(final int value) {
83 switch (value) {
84 case 1:
85 return SOLICIT;
86 case 2:
87 return ADVERTISE;
88 case 3:
89 return REQUEST;
90 case 4:
91 return CONFIRM;
92 case 5:
93 return RENEW;
94 case 6:
95 return REBIND;
96 case 7:
97 return REPLY;
98 case 8:
99 return RELEASE;
100 case 9:
101 return DECLINE;
102 case 10:
103 return RECONFIGURE;
104 case 11:
105 return INFORMATION_REQUEST;
106 case 12:
107 return RELAY_FORW;
108 case 13:
109 return RELAY_REPL;
110 default:
111 return null;
112 }
113 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000114 public static String getMsgTypeStr(final MsgType msgType) {
115 switch (msgType) {
116 case SOLICIT:
117 return "SOLICIT";
118 case ADVERTISE:
119 return "ADVERTISE";
120 case REQUEST:
121 return "REQUEST";
122 case CONFIRM:
123 return "CONFIRM";
124 case RENEW:
125 return "RENEW";
126 case REBIND:
127 return "REBIND";
128 case REPLY:
129 return "REPLY";
130 case RELEASE:
131 return "RELEASE";
132 case DECLINE:
133 return "DECLINE";
134 case RECONFIGURE:
135 return "RECONFIGURE";
136 case INFORMATION_REQUEST:
137 return "INFORMATION_REQUEST";
138 case RELAY_FORW:
139 return "RELAY_FORW";
140 case RELAY_REPL:
141 return "RELAY_REPL";
142 default:
143 return "NULL";
144 }
145 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700146 }
147
148 /**
149 * DHCPv6 option code.
150 */
151 public enum OptionCode {
152 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
153 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
154 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
155 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
156 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
Kalhee Kim45fede42017-09-05 19:05:06 +0000157 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20), IA_PD((short) 25), IAPREFIX((short) 26),
158 SUBSCRIBER_ID((short) 38);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700159
160 protected short value;
161 OptionCode(final short value) {
162 this.value = value;
163 }
164 public short value() {
165 return this.value;
166 }
167 }
168
Yi Tsengca34e1d2017-07-18 16:16:25 -0700169 private static final Map<Short, Deserializer<Dhcp6Option>> OPT_DESERIALIZERS =
Kalhee Kim45fede42017-09-05 19:05:06 +0000170 ImmutableMap.<Short, Deserializer<Dhcp6Option>>builder()
171 .put(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer())
172 .put(OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer())
173 .put(OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer())
174 .put(OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer())
175 .put(OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer())
176 .put(OptionCode.IA_PD.value, Dhcp6IaPdOption.deserializer())
177 .put(OptionCode.INTERFACE_ID.value, Dhcp6InterfaceIdOption.deserializer())
178 .build();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700179
Yi Tseng60bf35a2017-06-02 17:05:48 -0700180 // general field
181 private byte msgType; // 1 byte
Yi Tsengc7403c22017-06-19 16:23:22 -0700182 private List<Dhcp6Option> options;
Yi Tseng60bf35a2017-06-02 17:05:48 -0700183
184 // non-relay field
185 private int transactionId; // 3 bytes
186
187 // relay field
188 private byte hopCount; // 1 byte
189 private byte[] linkAddress; // 16 bytes
190 private byte[] peerAddress; // 16 bytes
191
192 /**
193 * Creates new DHCPv6 object.
194 */
195 public DHCP6() {
196 options = Lists.newArrayList();
197 }
198
199 @Override
200 public byte[] serialize() {
201 int payloadLength = options.stream()
Yi Tsengc7403c22017-06-19 16:23:22 -0700202 .mapToInt(Dhcp6Option::getLength)
Yi Tseng60bf35a2017-06-02 17:05:48 -0700203 .sum();
204
205 // 2 bytes code and 2 bytes length
206 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
207
208 if (RELAY_MSG_TYPES.contains(msgType)) {
209 payloadLength += DHCP6_RELAY_MSG_SIZE;
210 } else {
211 payloadLength += DHCP6_DEFAULT_SIZE;
212 }
213
214 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
215
216 if (RELAY_MSG_TYPES.contains(msgType)) {
217 bb.put(msgType);
218 bb.put(hopCount);
219 bb.put(linkAddress);
220 bb.put(peerAddress);
221 } else {
222 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
223 bb.putInt(defaultHeader);
224 }
225
226 // serialize options
227 options.forEach(option -> {
Yi Tsengca34e1d2017-07-18 16:16:25 -0700228 bb.put(option.serialize());
Yi Tseng60bf35a2017-06-02 17:05:48 -0700229 });
230
231 return bb.array();
232 }
233
234 /**
235 * Returns a deserializer for DHCPv6.
236 *
237 * @return the deserializer for DHCPv6
238 */
239 public static Deserializer<DHCP6> deserializer() {
240 return (data, offset, length) -> {
241 DHCP6 dhcp6 = new DHCP6();
242
243 checkNotNull(data);
244
245 if (offset < 0 || length < 0 ||
246 length > data.length || offset >= data.length ||
247 offset + length > data.length) {
248 throw new DeserializationException("Illegal offset or length");
249 }
250
251 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
252 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
253 throw new DeserializationException(
254 "Buffer underflow while reading DHCPv6 option");
255 }
256
257 // peek message type
Yi Tsengb4fdb042017-08-07 13:32:32 -0700258 dhcp6.msgType = bb.array()[offset];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700259 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
260 bb.get(); // drop message type
261 dhcp6.hopCount = bb.get();
262 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
263 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
264
265 bb.get(dhcp6.linkAddress);
266 bb.get(dhcp6.peerAddress);
267 } else {
268 // get msg type + transaction id (1 + 3 bytes)
269 int defaultHeader = bb.getInt();
270 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
271 }
272
273 dhcp6.options = Lists.newArrayList();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700274 while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
275 // create temporary byte buffer for reading code and length
276 ByteBuffer optByteBuffer =
Yi Tsengb4fdb042017-08-07 13:32:32 -0700277 ByteBuffer.wrap(data, bb.position(), bb.limit() - bb.position());
Yi Tsengca34e1d2017-07-18 16:16:25 -0700278 short code = optByteBuffer.getShort();
Yi Tsengb4fdb042017-08-07 13:32:32 -0700279 int optionLen = UNSIGNED_SHORT_MASK & optByteBuffer.getShort();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700280 if (optByteBuffer.remaining() < optionLen) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700281 throw new DeserializationException(
282 "Buffer underflow while reading DHCPv6 option");
283 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700284 Dhcp6Option option;
285 byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700286 bb.get(optionData);
Yi Tsengca34e1d2017-07-18 16:16:25 -0700287 if (OPT_DESERIALIZERS.containsKey(code)) {
288 option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
289 } else {
290 option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
291 }
292 option.setParent(dhcp6);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700293 dhcp6.options.add(option);
294 }
295
296 return dhcp6;
297 };
298 }
299
Yi Tseng60bf35a2017-06-02 17:05:48 -0700300
301 /**
302 * Gets the message type of this DHCPv6 packet.
303 *
304 * @return the message type
305 */
306 public byte getMsgType() {
307 return msgType;
308 }
309
310 /**
311 * Gets options from this DHCPv6 packet.
312 *
313 * @return DHCPv6 options
314 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700315 public List<Dhcp6Option> getOptions() {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700316 return options;
317 }
318
319 /**
320 * Gets the transaction ID of this DHCPv6 packet.
321 *
322 * @return the transaction ID
323 */
324 public int getTransactionId() {
325 return transactionId;
326 }
327
328 /**
329 * Gets the hop count of this DHCPv6 relay message.
330 *
331 * @return the hop count
332 */
333 public byte getHopCount() {
334 return hopCount;
335 }
336
337 /**
338 * Gets the link address of this DHCPv6 relay message.
339 *
340 * @return the link address
341 */
342 public byte[] getLinkAddress() {
343 return linkAddress;
344 }
345
346 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700347 * Gets IPv6 link address.
348 *
349 * @return the IPv6 link address
350 */
351 public Ip6Address getIp6LinkAddress() {
352 return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
353 }
354
355 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700356 * Gets the peer address of this DHCPv6 relay message.
357 *
358 * @return the link address
359 */
360 public byte[] getPeerAddress() {
361 return peerAddress;
362 }
363
364 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700365 * Gets IPv6 peer address.
366 *
367 * @return the IPv6 peer address
368 */
369 public Ip6Address getIp6PeerAddress() {
370 return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
371 }
372
373 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700374 * Sets message type.
375 *
376 * @param msgType the message type
377 */
378 public void setMsgType(byte msgType) {
379 this.msgType = msgType;
380 }
381
382 /**
383 * Sets options.
384 *
385 * @param options the options
386 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700387 public void setOptions(List<Dhcp6Option> options) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700388 this.options = options;
389 }
390
391 /**
392 * Sets transaction id.
393 *
394 * @param transactionId the transaction id
395 */
396 public void setTransactionId(int transactionId) {
397 this.transactionId = transactionId;
398 }
399
400 /**
401 * Sets hop count.
402 *
403 * @param hopCount the hop count
404 */
405 public void setHopCount(byte hopCount) {
406 this.hopCount = hopCount;
407 }
408
409 /**
410 * Sets link address.
411 *
412 * @param linkAddress the link address
413 */
414 public void setLinkAddress(byte[] linkAddress) {
415 this.linkAddress = linkAddress;
416 }
417
418 /**
419 * Sets peer address.
420 *
421 * @param peerAddress the peer address
422 */
423 public void setPeerAddress(byte[] peerAddress) {
424 this.peerAddress = peerAddress;
425 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700426
427 @Override
428 public String toString() {
429 if (RELAY_MSG_TYPES.contains(msgType)) {
430 // relay message
431 return toStringHelper(getClass())
432 .add("msgType", msgType)
433 .add("hopCount", hopCount)
434 .add("linkAddress", Ip6Address.valueOf(linkAddress))
435 .add("peerAddress", Ip6Address.valueOf(peerAddress))
436 .add("options", options)
437 .toString();
438 } else {
439 return toStringHelper(getClass())
440 .add("msgType", msgType)
441 .add("transactionId", transactionId)
442 .add("options", options)
443 .toString();
444 }
445 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700446}