blob: 9e6f38f4e00cc87fd19d7f6e06f0bcf684242171 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
alshabibc4901cd2014-09-05 16:50:40 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
alshabibc4901cd2014-09-05 16:50:40 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
16
17
alshabibc4901cd2014-09-05 16:50:40 -070018
19package org.onlab.packet;
20
Yi Tsengc7403c22017-06-19 16:23:22 -070021import com.google.common.collect.ImmutableMap;
22import org.onlab.packet.dhcp.DhcpOption;
23import org.onlab.packet.dhcp.DhcpRelayAgentOption;
24
alshabibc4901cd2014-09-05 16:50:40 -070025import java.nio.ByteBuffer;
Yi Tsengc7403c22017-06-19 16:23:22 -070026import java.nio.charset.StandardCharsets;
alshabibc4901cd2014-09-05 16:50:40 -070027import java.util.ArrayList;
Jian Li5fc14292015-12-04 11:30:46 -080028import java.util.Arrays;
alshabibc4901cd2014-09-05 16:50:40 -070029import java.util.List;
Yi Tsengc7403c22017-06-19 16:23:22 -070030import java.util.Map;
alshabibc4901cd2014-09-05 16:50:40 -070031
samanwita pal3ec8aff2015-08-03 18:04:27 -070032import static com.google.common.base.Preconditions.checkArgument;
33import static org.onlab.packet.PacketUtils.checkInput;
Jian Li5fc14292015-12-04 11:30:46 -080034import static com.google.common.base.MoreObjects.toStringHelper;
Jonathan Hart2a655752015-04-07 16:46:33 -070035
alshabibc4901cd2014-09-05 16:50:40 -070036/**
Jian Li5fc14292015-12-04 11:30:46 -080037 * Representation of an DHCP Packet.
alshabibc4901cd2014-09-05 16:50:40 -070038 */
39public class DHCP extends BasePacket {
40 /**
41 * Dynamic Host Configuration Protocol packet.
42 * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
43 * hops(1) | ------------------------------------------ | xid (4) |
44 * ------------------------------------------ | secs (2) | flags (2) |
45 * ------------------------------------------ | ciaddr (4) |
46 * ------------------------------------------ | yiaddr (4) |
47 * ------------------------------------------ | siaddr (4) |
48 * ------------------------------------------ | giaddr (4) |
49 * ------------------------------------------ | chaddr (16) |
50 * ------------------------------------------ | sname (64) |
51 * ------------------------------------------ | file (128) |
52 * ------------------------------------------ | options (312) |
53 * ------------------------------------------
54 *
55 */
56 // Header + magic without options
57 public static final int MIN_HEADER_LENGTH = 240;
58 public static final byte OPCODE_REQUEST = 0x1;
59 public static final byte OPCODE_REPLY = 0x2;
alshabibc4901cd2014-09-05 16:50:40 -070060 public static final byte HWTYPE_ETHERNET = 0x1;
61
Yi Tsengc7403c22017-06-19 16:23:22 -070062 private static final Map<Byte, Deserializer<? extends DhcpOption>> OPTION_DESERIALIZERS =
63 ImmutableMap.of(DHCPOptionCode.OptionCode_CircuitID.value, DhcpRelayAgentOption.deserializer());
64 private static final int UNSIGNED_BYTE_MASK = 0xff;
65 private static final int BASE_OPTION_LEN = 60;
66 private static final int MIN_DHCP_LEN = 240;
67 private static final int BASE_HW_ADDR_LEN = 16;
68 private static final byte PAD_BYTE = 0;
69 private static final int BASE_SERVER_NAME_LEN = 64;
70 private static final int BASE_BOOT_FILE_NAME_LEN = 128;
71 private static final int MAGIC_COOKIE = 0x63825363;
72
alshabibc4901cd2014-09-05 16:50:40 -070073 public enum DHCPOptionCode {
Yi Tsengc7403c22017-06-19 16:23:22 -070074 OptionCode_Pad((byte) 0), OptionCode_SubnetMask((byte) 1),
75 OptionCode_RouterAddress((byte) 3), OptionCode_DomainServer((byte) 6),
76 OptionCode_HostName((byte) 12), OptionCode_DomainName((byte) 15),
77 OptionCode_BroadcastAddress((byte) 28), OptionCode_RequestedIP((byte) 50),
78 OptionCode_LeaseTime((byte) 51), OptionCode_MessageType((byte) 53),
samanwita palb300ca82015-07-16 14:36:05 -070079 OptionCode_DHCPServerIp((byte) 54), OptionCode_RequestedParameters((byte) 55),
Yi Tsengc7403c22017-06-19 16:23:22 -070080 OptionCode_RenewalTime((byte) 58), OPtionCode_RebindingTime((byte) 59),
81 OptionCode_ClientID((byte) 61), OptionCode_CircuitID((byte) 82),
82 OptionCode_END((byte) 255);
alshabibc4901cd2014-09-05 16:50:40 -070083
84 protected byte value;
85
Yi Tsengc7403c22017-06-19 16:23:22 -070086 DHCPOptionCode(final byte value) {
alshabibc4901cd2014-09-05 16:50:40 -070087 this.value = value;
88 }
89
90 public byte getValue() {
91 return this.value;
92 }
93 }
94
Yi Tsengc7403c22017-06-19 16:23:22 -070095 public enum MsgType {
96 // From RFC 1533
97 DHCPDISCOVER(1), DHCPOFFER(2), DHCPREQUEST(3), DHCPDECLINE(4), DHCPACK(5),
98 DHCPNAK(6), DHCPRELEASE(7),
99
100 // From RFC2132
101 DHCPINFORM(8),
102
103 // From RFC3203
104 DHCPFORCERENEW(9),
105
106 // From RFC4388
107 DHCPLEASEQUERY(10), DHCPLEASEUNASSIGNED(11), DHCPLEASEUNKNOWN(12),
108 DHCPLEASEACTIVE(13);
109
110 protected int value;
111
112 MsgType(final int value) {
113 this.value = value;
114 }
115
116 public int getValue() {
117 return this.value;
118 }
119
120 public static MsgType getType(final int value) {
121 switch (value) {
122 case 1:
123 return DHCPDISCOVER;
124 case 2:
125 return DHCPOFFER;
126 case 3:
127 return DHCPREQUEST;
128 case 4:
129 return DHCPDECLINE;
130 case 5:
131 return DHCPACK;
132 case 6:
133 return DHCPNAK;
134 case 7:
135 return DHCPRELEASE;
136 case 8:
137 return DHCPINFORM;
138 case 9:
139 return DHCPFORCERENEW;
140 case 10:
141 return DHCPLEASEQUERY;
142 case 11:
143 return DHCPLEASEUNASSIGNED;
144 case 12:
145 return DHCPLEASEUNKNOWN;
146 case 13:
147 return DHCPLEASEACTIVE;
148 default:
149 return null;
150 }
151 }
152 }
153
alshabibc4901cd2014-09-05 16:50:40 -0700154 protected byte opCode;
155 protected byte hardwareType;
156 protected byte hardwareAddressLength;
157 protected byte hops;
158 protected int transactionId;
159 protected short seconds;
160 protected short flags;
161 protected int clientIPAddress;
162 protected int yourIPAddress;
163 protected int serverIPAddress;
164 protected int gatewayIPAddress;
165 protected byte[] clientHardwareAddress;
166 protected String serverName;
167 protected String bootFileName;
Yi Tsengc7403c22017-06-19 16:23:22 -0700168 protected List<DhcpOption> options = new ArrayList<DhcpOption>();
alshabibc4901cd2014-09-05 16:50:40 -0700169
170 /**
171 * @return the opCode
172 */
173 public byte getOpCode() {
174 return this.opCode;
175 }
176
177 /**
178 * @param opCode
179 * the opCode to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800180 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700181 */
182 public DHCP setOpCode(final byte opCode) {
183 this.opCode = opCode;
184 return this;
185 }
186
187 /**
188 * @return the hardwareType
189 */
190 public byte getHardwareType() {
191 return this.hardwareType;
192 }
193
194 /**
195 * @param hardwareType
196 * the hardwareType to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800197 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700198 */
199 public DHCP setHardwareType(final byte hardwareType) {
200 this.hardwareType = hardwareType;
201 return this;
202 }
203
204 /**
205 * @return the hardwareAddressLength
206 */
207 public byte getHardwareAddressLength() {
208 return this.hardwareAddressLength;
209 }
210
211 /**
212 * @param hardwareAddressLength
213 * the hardwareAddressLength to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800214 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700215 */
216 public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
217 this.hardwareAddressLength = hardwareAddressLength;
218 return this;
219 }
220
221 /**
222 * @return the hops
223 */
224 public byte getHops() {
225 return this.hops;
226 }
227
228 /**
229 * @param hops
230 * the hops to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800231 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700232 */
233 public DHCP setHops(final byte hops) {
234 this.hops = hops;
235 return this;
236 }
237
238 /**
239 * @return the transactionId
240 */
241 public int getTransactionId() {
242 return this.transactionId;
243 }
244
245 /**
246 * @param transactionId
247 * the transactionId to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800248 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700249 */
250 public DHCP setTransactionId(final int transactionId) {
251 this.transactionId = transactionId;
252 return this;
253 }
254
255 /**
256 * @return the seconds
257 */
258 public short getSeconds() {
259 return this.seconds;
260 }
261
262 /**
263 * @param seconds
264 * the seconds to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800265 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700266 */
267 public DHCP setSeconds(final short seconds) {
268 this.seconds = seconds;
269 return this;
270 }
271
272 /**
273 * @return the flags
274 */
275 public short getFlags() {
276 return this.flags;
277 }
278
279 /**
280 * @param flags
281 * the flags to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800282 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700283 */
284 public DHCP setFlags(final short flags) {
285 this.flags = flags;
286 return this;
287 }
288
289 /**
290 * @return the clientIPAddress
291 */
292 public int getClientIPAddress() {
293 return this.clientIPAddress;
294 }
295
296 /**
297 * @param clientIPAddress
298 * the clientIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800299 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700300 */
301 public DHCP setClientIPAddress(final int clientIPAddress) {
302 this.clientIPAddress = clientIPAddress;
303 return this;
304 }
305
306 /**
307 * @return the yourIPAddress
308 */
309 public int getYourIPAddress() {
310 return this.yourIPAddress;
311 }
312
313 /**
314 * @param yourIPAddress
315 * the yourIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800316 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700317 */
318 public DHCP setYourIPAddress(final int yourIPAddress) {
319 this.yourIPAddress = yourIPAddress;
320 return this;
321 }
322
323 /**
324 * @return the serverIPAddress
325 */
326 public int getServerIPAddress() {
327 return this.serverIPAddress;
328 }
329
330 /**
331 * @param serverIPAddress
332 * the serverIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800333 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700334 */
335 public DHCP setServerIPAddress(final int serverIPAddress) {
336 this.serverIPAddress = serverIPAddress;
337 return this;
338 }
339
340 /**
341 * @return the gatewayIPAddress
342 */
343 public int getGatewayIPAddress() {
344 return this.gatewayIPAddress;
345 }
346
347 /**
348 * @param gatewayIPAddress
349 * the gatewayIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800350 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700351 */
352 public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
353 this.gatewayIPAddress = gatewayIPAddress;
354 return this;
355 }
356
357 /**
358 * @return the clientHardwareAddress
359 */
360 public byte[] getClientHardwareAddress() {
361 return this.clientHardwareAddress;
362 }
363
364 /**
365 * @param clientHardwareAddress
366 * the clientHardwareAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800367 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700368 */
369 public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
370 this.clientHardwareAddress = clientHardwareAddress;
371 return this;
372 }
373
374 /**
375 * Gets a specific DHCP option parameter.
376 *
tom5f18cf32014-09-13 14:10:57 -0700377 * @param optionCode
alshabibc4901cd2014-09-05 16:50:40 -0700378 * The option code to get
379 * @return The value of the option if it exists, null otherwise
380 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700381 public DhcpOption getOption(final DHCPOptionCode optionCode) {
382 for (final DhcpOption opt : this.options) {
383 if (opt.getCode() == optionCode.getValue()) {
alshabibc4901cd2014-09-05 16:50:40 -0700384 return opt;
385 }
386 }
387 return null;
388 }
389
390 /**
391 * @return the options
392 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700393 public List<DhcpOption> getOptions() {
alshabibc4901cd2014-09-05 16:50:40 -0700394 return this.options;
395 }
396
397 /**
398 * @param options
399 * the options to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800400 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700401 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700402 public DHCP setOptions(final List<DhcpOption> options) {
alshabibc4901cd2014-09-05 16:50:40 -0700403 this.options = options;
404 return this;
405 }
406
407 /**
408 * @return the packetType base on option 53
409 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700410 public MsgType getPacketType() {
411 return this.options.parallelStream()
412 .filter(op -> op.getCode() == DHCPOptionCode.OptionCode_MessageType.getValue())
413 .map(DhcpOption::getData)
414 .filter(data -> data.length != 0)
415 .map(data -> data[0])
416 .map(MsgType::getType)
417 .findFirst()
418 .orElse(null);
alshabibc4901cd2014-09-05 16:50:40 -0700419 }
420
421 /**
422 * @return the serverName
423 */
424 public String getServerName() {
425 return this.serverName;
426 }
427
428 /**
429 * @param server
430 * the serverName to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800431 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700432 */
433 public DHCP setServerName(final String server) {
434 this.serverName = server;
435 return this;
436 }
437
438 /**
439 * @return the bootFileName
440 */
441 public String getBootFileName() {
442 return this.bootFileName;
443 }
444
445 /**
446 * @param bootFile
447 * the bootFileName to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800448 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700449 */
450 public DHCP setBootFileName(final String bootFile) {
451 this.bootFileName = bootFile;
452 return this;
453 }
454
455 @Override
456 public byte[] serialize() {
457 // not guaranteed to retain length/exact format
458 this.resetChecksum();
459
460 // minimum size 240 including magic cookie, options generally padded to
461 // 300
462 int optionsLength = 0;
Yi Tsengc7403c22017-06-19 16:23:22 -0700463 for (final DhcpOption option : this.options) {
464 if (option.getCode() == DHCPOptionCode.OptionCode_Pad.getValue() ||
465 option.getCode() == DHCPOptionCode.OptionCode_END.getValue()) {
alshabibc4901cd2014-09-05 16:50:40 -0700466 optionsLength += 1;
467 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700468 optionsLength += 2 + (UNSIGNED_BYTE_MASK & option.getLength());
alshabibc4901cd2014-09-05 16:50:40 -0700469 }
470 }
471 int optionsPadLength = 0;
Yi Tsengc7403c22017-06-19 16:23:22 -0700472 if (optionsLength < BASE_OPTION_LEN) {
473 optionsPadLength = BASE_OPTION_LEN - optionsLength;
alshabibc4901cd2014-09-05 16:50:40 -0700474 }
475
Yi Tsengc7403c22017-06-19 16:23:22 -0700476 final byte[] data = new byte[MIN_DHCP_LEN + optionsLength + optionsPadLength];
alshabibc4901cd2014-09-05 16:50:40 -0700477 final ByteBuffer bb = ByteBuffer.wrap(data);
478 bb.put(this.opCode);
479 bb.put(this.hardwareType);
480 bb.put(this.hardwareAddressLength);
481 bb.put(this.hops);
482 bb.putInt(this.transactionId);
483 bb.putShort(this.seconds);
484 bb.putShort(this.flags);
485 bb.putInt(this.clientIPAddress);
486 bb.putInt(this.yourIPAddress);
487 bb.putInt(this.serverIPAddress);
488 bb.putInt(this.gatewayIPAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700489 checkArgument(this.clientHardwareAddress.length <= BASE_HW_ADDR_LEN,
samanwita pal3ec8aff2015-08-03 18:04:27 -0700490 "Hardware address is too long (%s bytes)", this.clientHardwareAddress.length);
alshabibc4901cd2014-09-05 16:50:40 -0700491 bb.put(this.clientHardwareAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700492 if (this.clientHardwareAddress.length < BASE_HW_ADDR_LEN) {
493 for (int i = 0; i < BASE_HW_ADDR_LEN - this.clientHardwareAddress.length; ++i) {
494 bb.put(PAD_BYTE);
alshabibc4901cd2014-09-05 16:50:40 -0700495 }
496 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700497 this.writeString(this.serverName, bb, BASE_SERVER_NAME_LEN);
498 this.writeString(this.bootFileName, bb, BASE_BOOT_FILE_NAME_LEN);
alshabibc4901cd2014-09-05 16:50:40 -0700499 // magic cookie
Yi Tsengc7403c22017-06-19 16:23:22 -0700500 bb.putInt(MAGIC_COOKIE);
501 for (final DhcpOption option : this.options) {
502 bb.put(option.serialize());
alshabibc4901cd2014-09-05 16:50:40 -0700503 }
504 // assume the rest is padded out with zeroes
505 return data;
506 }
507
Jonathan Hart2a655752015-04-07 16:46:33 -0700508 protected void writeString(final String string, final ByteBuffer bb,
509 final int maxLength) {
510 if (string == null) {
511 for (int i = 0; i < maxLength; ++i) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700512 bb.put(PAD_BYTE);
Jonathan Hart2a655752015-04-07 16:46:33 -0700513 }
514 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700515 byte[] bytes;
516 bytes = string.getBytes(StandardCharsets.US_ASCII);
Jonathan Hart2a655752015-04-07 16:46:33 -0700517 int writeLength = bytes.length;
518 if (writeLength > maxLength) {
519 writeLength = maxLength;
520 }
521 bb.put(bytes, 0, writeLength);
522 for (int i = writeLength; i < maxLength; ++i) {
523 bb.put((byte) 0x0);
524 }
525 }
526 }
527
528 private static String readString(final ByteBuffer bb, final int maxLength) {
alshabibc4901cd2014-09-05 16:50:40 -0700529 final byte[] bytes = new byte[maxLength];
530 bb.get(bytes);
Yi Tsengc7403c22017-06-19 16:23:22 -0700531 String result;
532 result = new String(bytes, StandardCharsets.US_ASCII).trim();
alshabibc4901cd2014-09-05 16:50:40 -0700533 return result;
534 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700535
536 /**
537 * Deserializer function for DHCP packets.
538 *
539 * @return deserializer function
540 */
541 public static Deserializer<DHCP> deserializer() {
542 return (data, offset, length) -> {
543 checkInput(data, offset, length, MIN_HEADER_LENGTH);
544
545 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
546 DHCP dhcp = new DHCP();
547
548 dhcp.opCode = bb.get();
549 dhcp.hardwareType = bb.get();
550 dhcp.hardwareAddressLength = bb.get();
551 dhcp.hops = bb.get();
552 dhcp.transactionId = bb.getInt();
553 dhcp.seconds = bb.getShort();
554 dhcp.flags = bb.getShort();
555 dhcp.clientIPAddress = bb.getInt();
556 dhcp.yourIPAddress = bb.getInt();
557 dhcp.serverIPAddress = bb.getInt();
558 dhcp.gatewayIPAddress = bb.getInt();
Yi Tsengc7403c22017-06-19 16:23:22 -0700559 final int hardwareAddressLength = UNSIGNED_BYTE_MASK & dhcp.hardwareAddressLength;
Jonathan Hart2a655752015-04-07 16:46:33 -0700560 dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
561
562 bb.get(dhcp.clientHardwareAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700563 for (int i = hardwareAddressLength; i < BASE_HW_ADDR_LEN; ++i) {
Jonathan Hart2a655752015-04-07 16:46:33 -0700564 bb.get();
565 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700566 dhcp.serverName = readString(bb, BASE_SERVER_NAME_LEN);
567 dhcp.bootFileName = readString(bb, BASE_BOOT_FILE_NAME_LEN);
Jonathan Hart2a655752015-04-07 16:46:33 -0700568 // read the magic cookie
569 // magic cookie
Yi Tsengc7403c22017-06-19 16:23:22 -0700570 bb.getInt();
Jonathan Hart2a655752015-04-07 16:46:33 -0700571
572 // read options
573 boolean foundEndOptionsMarker = false;
574 while (bb.hasRemaining()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700575 DhcpOption option;
Yi Tsengc7403c22017-06-19 16:23:22 -0700576 int pos = bb.position();
577 int optCode = UNSIGNED_BYTE_MASK & bb.array()[pos]; // to unsigned integer
578 int optLen;
579 byte[] optData;
580
581 if (optCode == DHCPOptionCode.OptionCode_Pad.value) {
582 // pad, skip
583 // read option code
584 bb.get();
Jonathan Hart2a655752015-04-07 16:46:33 -0700585 continue;
Yi Tsengc7403c22017-06-19 16:23:22 -0700586 }
587 if (optCode == (UNSIGNED_BYTE_MASK & DHCPOptionCode.OptionCode_END.value)) {
588 // end of dhcp options or invalid option and set the END option
589 option = new DhcpOption();
590 option.setCode((byte) optCode);
591 dhcp.options.add(option);
Jonathan Hart2a655752015-04-07 16:46:33 -0700592 foundEndOptionsMarker = true;
593 break;
594 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700595
596 if (bb.remaining() < 2) {
597 // No option length
598 throw new DeserializationException("Buffer underflow while reading DHCP option");
599 }
600
601 optLen = UNSIGNED_BYTE_MASK & bb.array()[pos + 1];
602 if (bb.remaining() < DhcpOption.DEFAULT_LEN + optLen) {
603 // Invalid option length
604 throw new DeserializationException("Buffer underflow while reading DHCP option");
605 }
606 optData = new byte[DhcpOption.DEFAULT_LEN + optLen];
607 bb.get(optData);
608 if (OPTION_DESERIALIZERS.containsKey((byte) optCode)) {
609 option = OPTION_DESERIALIZERS.get((byte) optCode).deserialize(optData, 0, optData.length);
610 dhcp.options.add(option);
611 } else {
612 // default option
613 option = DhcpOption.deserializer().deserialize(optData, 0, optData.length);
614 dhcp.options.add(option);
615 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700616 }
617
618 if (!foundEndOptionsMarker) {
619 throw new DeserializationException("DHCP End options marker was missing");
620 }
621
622 return dhcp;
623 };
624 }
Jian Li5fc14292015-12-04 11:30:46 -0800625
626 @Override
627 public String toString() {
628 return toStringHelper(getClass())
629 .add("opCode", Byte.toString(opCode))
630 .add("hardwareType", Byte.toString(hardwareType))
631 .add("hardwareAddressLength", Byte.toString(hardwareAddressLength))
632 .add("hops", Byte.toString(hops))
633 .add("transactionId", Integer.toString(transactionId))
634 .add("seconds", Short.toString(seconds))
635 .add("flags", Short.toString(flags))
636 .add("clientIPAddress", Integer.toString(clientIPAddress))
637 .add("yourIPAddress", Integer.toString(yourIPAddress))
638 .add("serverIPAddress", Integer.toString(serverIPAddress))
639 .add("gatewayIPAddress", Integer.toString(gatewayIPAddress))
640 .add("clientHardwareAddress", Arrays.toString(clientHardwareAddress))
641 .add("serverName", serverName)
642 .add("bootFileName", bootFileName)
643 .toString();
644 // TODO: need to handle options
645 }
alshabibc4901cd2014-09-05 16:50:40 -0700646}