blob: 50b11a16c4c88040bbb97e14dfcae4bc1663f939 [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;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080022import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
Yi Tsengca34e1d2017-07-18 16:16:25 -070023import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
24import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
25import org.onlab.packet.dhcp.Dhcp6IaNaOption;
26import org.onlab.packet.dhcp.Dhcp6IaTaOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000027import org.onlab.packet.dhcp.Dhcp6IaPdOption;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080028import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
Yi Tsengc7403c22017-06-19 16:23:22 -070029import org.onlab.packet.dhcp.Dhcp6Option;
Yi Tsengca34e1d2017-07-18 16:16:25 -070030import org.onlab.packet.dhcp.Dhcp6RelayOption;
Kalhee Kim45fede42017-09-05 19:05:06 +000031import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Yi Tseng60bf35a2017-06-02 17:05:48 -070032
33import java.nio.ByteBuffer;
34import java.util.List;
Yi Tsengca34e1d2017-07-18 16:16:25 -070035import java.util.Map;
Yi Tseng60bf35a2017-06-02 17:05:48 -070036import java.util.Set;
37
Yi Tsengca34e1d2017-07-18 16:16:25 -070038import static com.google.common.base.MoreObjects.toStringHelper;
Yi Tseng60bf35a2017-06-02 17:05:48 -070039import static com.google.common.base.Preconditions.checkNotNull;
40
41/**
42 * Representation of an DHCPv6 Packet.
43 * Base on RFC-3315.
44 */
45public class DHCP6 extends BasePacket {
Yi Tsengb4fdb042017-08-07 13:32:32 -070046 private static final int UNSIGNED_SHORT_MASK = 0xffff;
Yi Tseng60bf35a2017-06-02 17:05:48 -070047 // size of different field of option
48 private static final int OPT_CODE_SIZE = 2;
49 private static final int OPT_LEN_SIZE = 2;
50
51 // default size of DHCPv6 payload (without options)
52 private static final int DHCP6_DEFAULT_SIZE = 4;
53
54 // default size of DHCPv6 relay message payload (without options)
55 private static final int DHCP6_RELAY_MSG_SIZE = 34;
56 private static final int IPV6_ADDR_LEN = 16;
57
58 // masks & offsets for default DHCPv6 header
59 private static final int MSG_TYPE_OFFSET = 24;
60 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
61
62 // Relay message types
Yi Tsengca34e1d2017-07-18 16:16:25 -070063 public static final Set<Byte> RELAY_MSG_TYPES =
Yi Tseng60bf35a2017-06-02 17:05:48 -070064 ImmutableSet.of(MsgType.RELAY_FORW.value,
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080065 MsgType.RELAY_REPL.value
66 );
67 public static final Set<Byte> LEASEQUERY_MSG_TYPES =
68 ImmutableSet.of(MsgType.LEASEQUERY.value,
69 MsgType.LEASEQUERY_REPLY.value
70 );
Yi Tseng60bf35a2017-06-02 17:05:48 -070071
72 /**
73 * DHCPv6 message type.
74 */
75 public enum MsgType {
76 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
77 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
78 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
79 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080080 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13), LEASEQUERY((byte) 14),
81 LEASEQUERY_REPLY((byte) 15);
Yi Tseng60bf35a2017-06-02 17:05:48 -070082
83 protected byte value;
84 MsgType(final byte value) {
85 this.value = value;
86 }
87 public byte value() {
88 return this.value;
89 }
Kalhee Kimea4b6c22017-11-09 14:38:37 +000090 public static MsgType getType(final int value) {
91 switch (value) {
92 case 1:
93 return SOLICIT;
94 case 2:
95 return ADVERTISE;
96 case 3:
97 return REQUEST;
98 case 4:
99 return CONFIRM;
100 case 5:
101 return RENEW;
102 case 6:
103 return REBIND;
104 case 7:
105 return REPLY;
106 case 8:
107 return RELEASE;
108 case 9:
109 return DECLINE;
110 case 10:
111 return RECONFIGURE;
112 case 11:
113 return INFORMATION_REQUEST;
114 case 12:
115 return RELAY_FORW;
116 case 13:
117 return RELAY_REPL;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800118 case 14:
119 return LEASEQUERY;
120 case 15:
121 return LEASEQUERY_REPLY;
Kalhee Kimea4b6c22017-11-09 14:38:37 +0000122 default:
123 return null;
124 }
125 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000126 public static String getMsgTypeStr(final MsgType msgType) {
127 switch (msgType) {
128 case SOLICIT:
129 return "SOLICIT";
130 case ADVERTISE:
131 return "ADVERTISE";
132 case REQUEST:
133 return "REQUEST";
134 case CONFIRM:
135 return "CONFIRM";
136 case RENEW:
137 return "RENEW";
138 case REBIND:
139 return "REBIND";
140 case REPLY:
141 return "REPLY";
142 case RELEASE:
143 return "RELEASE";
144 case DECLINE:
145 return "DECLINE";
146 case RECONFIGURE:
147 return "RECONFIGURE";
148 case INFORMATION_REQUEST:
149 return "INFORMATION_REQUEST";
150 case RELAY_FORW:
151 return "RELAY_FORW";
152 case RELAY_REPL:
153 return "RELAY_REPL";
154 default:
155 return "NULL";
156 }
157 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700158 }
159
160 /**
161 * DHCPv6 option code.
162 */
163 public enum OptionCode {
164 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
165 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
166 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
167 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
168 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
Kalhee Kim45fede42017-09-05 19:05:06 +0000169 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20), IA_PD((short) 25), IAPREFIX((short) 26),
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800170 SUBSCRIBER_ID((short) 38), OPTION_ERO((short) 43), LEASE_QUERY((short) 44),
171 CLIENT_DATA((short) 45), CLIENT_LT((short) 48);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700172
173 protected short value;
174 OptionCode(final short value) {
175 this.value = value;
176 }
177 public short value() {
178 return this.value;
179 }
180 }
181
Yi Tsengca34e1d2017-07-18 16:16:25 -0700182 private static final Map<Short, Deserializer<Dhcp6Option>> OPT_DESERIALIZERS =
Kalhee Kim45fede42017-09-05 19:05:06 +0000183 ImmutableMap.<Short, Deserializer<Dhcp6Option>>builder()
184 .put(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer())
185 .put(OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer())
186 .put(OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer())
187 .put(OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer())
188 .put(OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer())
189 .put(OptionCode.IA_PD.value, Dhcp6IaPdOption.deserializer())
190 .put(OptionCode.INTERFACE_ID.value, Dhcp6InterfaceIdOption.deserializer())
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800191 .put(OptionCode.LEASE_QUERY.value, Dhcp6LeaseQueryOption.deserializer())
192 .put(OptionCode.CLIENT_DATA.value, Dhcp6ClientDataOption.deserializer())
Kalhee Kim45fede42017-09-05 19:05:06 +0000193 .build();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700194
Yi Tseng60bf35a2017-06-02 17:05:48 -0700195 // general field
196 private byte msgType; // 1 byte
Yi Tsengc7403c22017-06-19 16:23:22 -0700197 private List<Dhcp6Option> options;
Yi Tseng60bf35a2017-06-02 17:05:48 -0700198
199 // non-relay field
200 private int transactionId; // 3 bytes
201
202 // relay field
203 private byte hopCount; // 1 byte
204 private byte[] linkAddress; // 16 bytes
205 private byte[] peerAddress; // 16 bytes
206
207 /**
208 * Creates new DHCPv6 object.
209 */
210 public DHCP6() {
211 options = Lists.newArrayList();
212 }
213
214 @Override
215 public byte[] serialize() {
216 int payloadLength = options.stream()
Yi Tsengc7403c22017-06-19 16:23:22 -0700217 .mapToInt(Dhcp6Option::getLength)
Yi Tseng60bf35a2017-06-02 17:05:48 -0700218 .sum();
219
220 // 2 bytes code and 2 bytes length
221 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
222
223 if (RELAY_MSG_TYPES.contains(msgType)) {
224 payloadLength += DHCP6_RELAY_MSG_SIZE;
225 } else {
226 payloadLength += DHCP6_DEFAULT_SIZE;
227 }
228
229 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
230
231 if (RELAY_MSG_TYPES.contains(msgType)) {
232 bb.put(msgType);
233 bb.put(hopCount);
234 bb.put(linkAddress);
235 bb.put(peerAddress);
236 } else {
237 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
238 bb.putInt(defaultHeader);
239 }
240
241 // serialize options
242 options.forEach(option -> {
Yi Tsengca34e1d2017-07-18 16:16:25 -0700243 bb.put(option.serialize());
Yi Tseng60bf35a2017-06-02 17:05:48 -0700244 });
245
246 return bb.array();
247 }
248
249 /**
250 * Returns a deserializer for DHCPv6.
251 *
252 * @return the deserializer for DHCPv6
253 */
254 public static Deserializer<DHCP6> deserializer() {
255 return (data, offset, length) -> {
256 DHCP6 dhcp6 = new DHCP6();
257
258 checkNotNull(data);
259
260 if (offset < 0 || length < 0 ||
261 length > data.length || offset >= data.length ||
262 offset + length > data.length) {
263 throw new DeserializationException("Illegal offset or length");
264 }
265
266 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
267 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
268 throw new DeserializationException(
269 "Buffer underflow while reading DHCPv6 option");
270 }
271
272 // peek message type
Yi Tsengb4fdb042017-08-07 13:32:32 -0700273 dhcp6.msgType = bb.array()[offset];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700274 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
275 bb.get(); // drop message type
276 dhcp6.hopCount = bb.get();
277 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
278 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
279
280 bb.get(dhcp6.linkAddress);
281 bb.get(dhcp6.peerAddress);
282 } else {
283 // get msg type + transaction id (1 + 3 bytes)
284 int defaultHeader = bb.getInt();
285 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
286 }
287
288 dhcp6.options = Lists.newArrayList();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700289 while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
290 // create temporary byte buffer for reading code and length
291 ByteBuffer optByteBuffer =
Yi Tsengb4fdb042017-08-07 13:32:32 -0700292 ByteBuffer.wrap(data, bb.position(), bb.limit() - bb.position());
Yi Tsengca34e1d2017-07-18 16:16:25 -0700293 short code = optByteBuffer.getShort();
Yi Tsengb4fdb042017-08-07 13:32:32 -0700294 int optionLen = UNSIGNED_SHORT_MASK & optByteBuffer.getShort();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700295 if (optByteBuffer.remaining() < optionLen) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700296 throw new DeserializationException(
297 "Buffer underflow while reading DHCPv6 option");
298 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700299 Dhcp6Option option;
300 byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700301 bb.get(optionData);
Yi Tsengca34e1d2017-07-18 16:16:25 -0700302 if (OPT_DESERIALIZERS.containsKey(code)) {
303 option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
304 } else {
305 option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
306 }
307 option.setParent(dhcp6);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700308 dhcp6.options.add(option);
309 }
310
311 return dhcp6;
312 };
313 }
314
Yi Tseng60bf35a2017-06-02 17:05:48 -0700315
316 /**
317 * Gets the message type of this DHCPv6 packet.
318 *
319 * @return the message type
320 */
321 public byte getMsgType() {
322 return msgType;
323 }
324
325 /**
326 * Gets options from this DHCPv6 packet.
327 *
328 * @return DHCPv6 options
329 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700330 public List<Dhcp6Option> getOptions() {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700331 return options;
332 }
333
334 /**
335 * Gets the transaction ID of this DHCPv6 packet.
336 *
337 * @return the transaction ID
338 */
339 public int getTransactionId() {
340 return transactionId;
341 }
342
343 /**
344 * Gets the hop count of this DHCPv6 relay message.
345 *
346 * @return the hop count
347 */
348 public byte getHopCount() {
349 return hopCount;
350 }
351
352 /**
353 * Gets the link address of this DHCPv6 relay message.
354 *
355 * @return the link address
356 */
357 public byte[] getLinkAddress() {
358 return linkAddress;
359 }
360
361 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700362 * Gets IPv6 link address.
363 *
364 * @return the IPv6 link address
365 */
366 public Ip6Address getIp6LinkAddress() {
367 return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
368 }
369
370 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700371 * Gets the peer address of this DHCPv6 relay message.
372 *
373 * @return the link address
374 */
375 public byte[] getPeerAddress() {
376 return peerAddress;
377 }
378
379 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700380 * Gets IPv6 peer address.
381 *
382 * @return the IPv6 peer address
383 */
384 public Ip6Address getIp6PeerAddress() {
385 return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
386 }
387
388 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700389 * Sets message type.
390 *
391 * @param msgType the message type
392 */
393 public void setMsgType(byte msgType) {
394 this.msgType = msgType;
395 }
396
397 /**
398 * Sets options.
399 *
400 * @param options the options
401 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700402 public void setOptions(List<Dhcp6Option> options) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700403 this.options = options;
404 }
405
406 /**
407 * Sets transaction id.
408 *
409 * @param transactionId the transaction id
410 */
411 public void setTransactionId(int transactionId) {
412 this.transactionId = transactionId;
413 }
414
415 /**
416 * Sets hop count.
417 *
418 * @param hopCount the hop count
419 */
420 public void setHopCount(byte hopCount) {
421 this.hopCount = hopCount;
422 }
423
424 /**
425 * Sets link address.
426 *
427 * @param linkAddress the link address
428 */
429 public void setLinkAddress(byte[] linkAddress) {
430 this.linkAddress = linkAddress;
431 }
432
433 /**
434 * Sets peer address.
435 *
436 * @param peerAddress the peer address
437 */
438 public void setPeerAddress(byte[] peerAddress) {
439 this.peerAddress = peerAddress;
440 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700441
442 @Override
443 public String toString() {
444 if (RELAY_MSG_TYPES.contains(msgType)) {
445 // relay message
446 return toStringHelper(getClass())
447 .add("msgType", msgType)
448 .add("hopCount", hopCount)
449 .add("linkAddress", Ip6Address.valueOf(linkAddress))
450 .add("peerAddress", Ip6Address.valueOf(peerAddress))
451 .add("options", options)
452 .toString();
453 } else {
454 return toStringHelper(getClass())
455 .add("msgType", msgType)
456 .add("transactionId", transactionId)
457 .add("options", options)
458 .toString();
459 }
460 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700461}