blob: ad0965f51aa61ba452519ac763104dcd0d323ed5 [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),
Daniel Park4a0be7b2018-07-31 09:57:41 +090077 OptionCode_MTU((byte) 26), OptionCode_BroadcastAddress((byte) 28),
78 OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime((byte) 51),
79 OptionCode_MessageType((byte) 53), OptionCode_DHCPServerIp((byte) 54),
80 OptionCode_RequestedParameters((byte) 55), OptionCode_RenewalTime((byte) 58),
81 OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID((byte) 61),
82 OptionCode_CircuitID((byte) 82), OptionCode_Classless_Static_Route((byte) 121),
Yi Tsengc7403c22017-06-19 16:23:22 -070083 OptionCode_END((byte) 255);
alshabibc4901cd2014-09-05 16:50:40 -070084
85 protected byte value;
86
Yi Tsengc7403c22017-06-19 16:23:22 -070087 DHCPOptionCode(final byte value) {
alshabibc4901cd2014-09-05 16:50:40 -070088 this.value = value;
89 }
90
91 public byte getValue() {
92 return this.value;
93 }
94 }
95
Yi Tsengc7403c22017-06-19 16:23:22 -070096 public enum MsgType {
97 // From RFC 1533
98 DHCPDISCOVER(1), DHCPOFFER(2), DHCPREQUEST(3), DHCPDECLINE(4), DHCPACK(5),
99 DHCPNAK(6), DHCPRELEASE(7),
100
101 // From RFC2132
102 DHCPINFORM(8),
103
104 // From RFC3203
105 DHCPFORCERENEW(9),
106
107 // From RFC4388
108 DHCPLEASEQUERY(10), DHCPLEASEUNASSIGNED(11), DHCPLEASEUNKNOWN(12),
109 DHCPLEASEACTIVE(13);
110
111 protected int value;
112
113 MsgType(final int value) {
114 this.value = value;
115 }
116
117 public int getValue() {
118 return this.value;
119 }
120
121 public static MsgType getType(final int value) {
122 switch (value) {
123 case 1:
124 return DHCPDISCOVER;
125 case 2:
126 return DHCPOFFER;
127 case 3:
128 return DHCPREQUEST;
129 case 4:
130 return DHCPDECLINE;
131 case 5:
132 return DHCPACK;
133 case 6:
134 return DHCPNAK;
135 case 7:
136 return DHCPRELEASE;
137 case 8:
138 return DHCPINFORM;
139 case 9:
140 return DHCPFORCERENEW;
141 case 10:
142 return DHCPLEASEQUERY;
143 case 11:
144 return DHCPLEASEUNASSIGNED;
145 case 12:
146 return DHCPLEASEUNKNOWN;
147 case 13:
148 return DHCPLEASEACTIVE;
149 default:
150 return null;
151 }
152 }
153 }
154
alshabibc4901cd2014-09-05 16:50:40 -0700155 protected byte opCode;
156 protected byte hardwareType;
157 protected byte hardwareAddressLength;
158 protected byte hops;
159 protected int transactionId;
160 protected short seconds;
161 protected short flags;
162 protected int clientIPAddress;
163 protected int yourIPAddress;
164 protected int serverIPAddress;
165 protected int gatewayIPAddress;
166 protected byte[] clientHardwareAddress;
167 protected String serverName;
168 protected String bootFileName;
Yi Tsengc7403c22017-06-19 16:23:22 -0700169 protected List<DhcpOption> options = new ArrayList<DhcpOption>();
alshabibc4901cd2014-09-05 16:50:40 -0700170
171 /**
172 * @return the opCode
173 */
174 public byte getOpCode() {
175 return this.opCode;
176 }
177
178 /**
179 * @param opCode
180 * the opCode to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800181 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700182 */
183 public DHCP setOpCode(final byte opCode) {
184 this.opCode = opCode;
185 return this;
186 }
187
188 /**
189 * @return the hardwareType
190 */
191 public byte getHardwareType() {
192 return this.hardwareType;
193 }
194
195 /**
196 * @param hardwareType
197 * the hardwareType to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800198 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700199 */
200 public DHCP setHardwareType(final byte hardwareType) {
201 this.hardwareType = hardwareType;
202 return this;
203 }
204
205 /**
206 * @return the hardwareAddressLength
207 */
208 public byte getHardwareAddressLength() {
209 return this.hardwareAddressLength;
210 }
211
212 /**
213 * @param hardwareAddressLength
214 * the hardwareAddressLength to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800215 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700216 */
217 public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
218 this.hardwareAddressLength = hardwareAddressLength;
219 return this;
220 }
221
222 /**
223 * @return the hops
224 */
225 public byte getHops() {
226 return this.hops;
227 }
228
229 /**
230 * @param hops
231 * the hops to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800232 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700233 */
234 public DHCP setHops(final byte hops) {
235 this.hops = hops;
236 return this;
237 }
238
239 /**
240 * @return the transactionId
241 */
242 public int getTransactionId() {
243 return this.transactionId;
244 }
245
246 /**
247 * @param transactionId
248 * the transactionId to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800249 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700250 */
251 public DHCP setTransactionId(final int transactionId) {
252 this.transactionId = transactionId;
253 return this;
254 }
255
256 /**
257 * @return the seconds
258 */
259 public short getSeconds() {
260 return this.seconds;
261 }
262
263 /**
264 * @param seconds
265 * the seconds to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800266 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700267 */
268 public DHCP setSeconds(final short seconds) {
269 this.seconds = seconds;
270 return this;
271 }
272
273 /**
274 * @return the flags
275 */
276 public short getFlags() {
277 return this.flags;
278 }
279
280 /**
281 * @param flags
282 * the flags to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800283 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700284 */
285 public DHCP setFlags(final short flags) {
286 this.flags = flags;
287 return this;
288 }
289
290 /**
291 * @return the clientIPAddress
292 */
293 public int getClientIPAddress() {
294 return this.clientIPAddress;
295 }
296
297 /**
298 * @param clientIPAddress
299 * the clientIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800300 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700301 */
302 public DHCP setClientIPAddress(final int clientIPAddress) {
303 this.clientIPAddress = clientIPAddress;
304 return this;
305 }
306
307 /**
308 * @return the yourIPAddress
309 */
310 public int getYourIPAddress() {
311 return this.yourIPAddress;
312 }
313
314 /**
315 * @param yourIPAddress
316 * the yourIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800317 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700318 */
319 public DHCP setYourIPAddress(final int yourIPAddress) {
320 this.yourIPAddress = yourIPAddress;
321 return this;
322 }
323
324 /**
325 * @return the serverIPAddress
326 */
327 public int getServerIPAddress() {
328 return this.serverIPAddress;
329 }
330
331 /**
332 * @param serverIPAddress
333 * the serverIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800334 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700335 */
336 public DHCP setServerIPAddress(final int serverIPAddress) {
337 this.serverIPAddress = serverIPAddress;
338 return this;
339 }
340
341 /**
342 * @return the gatewayIPAddress
343 */
344 public int getGatewayIPAddress() {
345 return this.gatewayIPAddress;
346 }
347
348 /**
349 * @param gatewayIPAddress
350 * the gatewayIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800351 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700352 */
353 public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
354 this.gatewayIPAddress = gatewayIPAddress;
355 return this;
356 }
357
358 /**
359 * @return the clientHardwareAddress
360 */
361 public byte[] getClientHardwareAddress() {
362 return this.clientHardwareAddress;
363 }
364
365 /**
366 * @param clientHardwareAddress
367 * the clientHardwareAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800368 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700369 */
370 public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
371 this.clientHardwareAddress = clientHardwareAddress;
372 return this;
373 }
374
375 /**
376 * Gets a specific DHCP option parameter.
377 *
tom5f18cf32014-09-13 14:10:57 -0700378 * @param optionCode
alshabibc4901cd2014-09-05 16:50:40 -0700379 * The option code to get
380 * @return The value of the option if it exists, null otherwise
381 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700382 public DhcpOption getOption(final DHCPOptionCode optionCode) {
383 for (final DhcpOption opt : this.options) {
384 if (opt.getCode() == optionCode.getValue()) {
alshabibc4901cd2014-09-05 16:50:40 -0700385 return opt;
386 }
387 }
388 return null;
389 }
390
391 /**
392 * @return the options
393 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700394 public List<DhcpOption> getOptions() {
alshabibc4901cd2014-09-05 16:50:40 -0700395 return this.options;
396 }
397
398 /**
399 * @param options
400 * the options to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800401 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700402 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700403 public DHCP setOptions(final List<DhcpOption> options) {
alshabibc4901cd2014-09-05 16:50:40 -0700404 this.options = options;
405 return this;
406 }
407
408 /**
409 * @return the packetType base on option 53
410 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700411 public MsgType getPacketType() {
412 return this.options.parallelStream()
413 .filter(op -> op.getCode() == DHCPOptionCode.OptionCode_MessageType.getValue())
414 .map(DhcpOption::getData)
415 .filter(data -> data.length != 0)
416 .map(data -> data[0])
417 .map(MsgType::getType)
418 .findFirst()
419 .orElse(null);
alshabibc4901cd2014-09-05 16:50:40 -0700420 }
421
422 /**
423 * @return the serverName
424 */
425 public String getServerName() {
426 return this.serverName;
427 }
428
429 /**
430 * @param server
431 * the serverName to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800432 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700433 */
434 public DHCP setServerName(final String server) {
435 this.serverName = server;
436 return this;
437 }
438
439 /**
440 * @return the bootFileName
441 */
442 public String getBootFileName() {
443 return this.bootFileName;
444 }
445
446 /**
447 * @param bootFile
448 * the bootFileName to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800449 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700450 */
451 public DHCP setBootFileName(final String bootFile) {
452 this.bootFileName = bootFile;
453 return this;
454 }
455
456 @Override
457 public byte[] serialize() {
458 // not guaranteed to retain length/exact format
459 this.resetChecksum();
460
461 // minimum size 240 including magic cookie, options generally padded to
462 // 300
463 int optionsLength = 0;
Yi Tsengc7403c22017-06-19 16:23:22 -0700464 for (final DhcpOption option : this.options) {
465 if (option.getCode() == DHCPOptionCode.OptionCode_Pad.getValue() ||
466 option.getCode() == DHCPOptionCode.OptionCode_END.getValue()) {
alshabibc4901cd2014-09-05 16:50:40 -0700467 optionsLength += 1;
468 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700469 optionsLength += 2 + (UNSIGNED_BYTE_MASK & option.getLength());
alshabibc4901cd2014-09-05 16:50:40 -0700470 }
471 }
472 int optionsPadLength = 0;
Yi Tsengc7403c22017-06-19 16:23:22 -0700473 if (optionsLength < BASE_OPTION_LEN) {
474 optionsPadLength = BASE_OPTION_LEN - optionsLength;
alshabibc4901cd2014-09-05 16:50:40 -0700475 }
476
Yi Tsengc7403c22017-06-19 16:23:22 -0700477 final byte[] data = new byte[MIN_DHCP_LEN + optionsLength + optionsPadLength];
alshabibc4901cd2014-09-05 16:50:40 -0700478 final ByteBuffer bb = ByteBuffer.wrap(data);
479 bb.put(this.opCode);
480 bb.put(this.hardwareType);
481 bb.put(this.hardwareAddressLength);
482 bb.put(this.hops);
483 bb.putInt(this.transactionId);
484 bb.putShort(this.seconds);
485 bb.putShort(this.flags);
486 bb.putInt(this.clientIPAddress);
487 bb.putInt(this.yourIPAddress);
488 bb.putInt(this.serverIPAddress);
489 bb.putInt(this.gatewayIPAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700490 checkArgument(this.clientHardwareAddress.length <= BASE_HW_ADDR_LEN,
samanwita pal3ec8aff2015-08-03 18:04:27 -0700491 "Hardware address is too long (%s bytes)", this.clientHardwareAddress.length);
alshabibc4901cd2014-09-05 16:50:40 -0700492 bb.put(this.clientHardwareAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700493 if (this.clientHardwareAddress.length < BASE_HW_ADDR_LEN) {
494 for (int i = 0; i < BASE_HW_ADDR_LEN - this.clientHardwareAddress.length; ++i) {
495 bb.put(PAD_BYTE);
alshabibc4901cd2014-09-05 16:50:40 -0700496 }
497 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700498 this.writeString(this.serverName, bb, BASE_SERVER_NAME_LEN);
499 this.writeString(this.bootFileName, bb, BASE_BOOT_FILE_NAME_LEN);
alshabibc4901cd2014-09-05 16:50:40 -0700500 // magic cookie
Yi Tsengc7403c22017-06-19 16:23:22 -0700501 bb.putInt(MAGIC_COOKIE);
502 for (final DhcpOption option : this.options) {
503 bb.put(option.serialize());
alshabibc4901cd2014-09-05 16:50:40 -0700504 }
505 // assume the rest is padded out with zeroes
506 return data;
507 }
508
Jonathan Hart2a655752015-04-07 16:46:33 -0700509 protected void writeString(final String string, final ByteBuffer bb,
510 final int maxLength) {
511 if (string == null) {
512 for (int i = 0; i < maxLength; ++i) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700513 bb.put(PAD_BYTE);
Jonathan Hart2a655752015-04-07 16:46:33 -0700514 }
515 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700516 byte[] bytes;
517 bytes = string.getBytes(StandardCharsets.US_ASCII);
Jonathan Hart2a655752015-04-07 16:46:33 -0700518 int writeLength = bytes.length;
519 if (writeLength > maxLength) {
520 writeLength = maxLength;
521 }
522 bb.put(bytes, 0, writeLength);
523 for (int i = writeLength; i < maxLength; ++i) {
524 bb.put((byte) 0x0);
525 }
526 }
527 }
528
529 private static String readString(final ByteBuffer bb, final int maxLength) {
alshabibc4901cd2014-09-05 16:50:40 -0700530 final byte[] bytes = new byte[maxLength];
531 bb.get(bytes);
Yi Tsengc7403c22017-06-19 16:23:22 -0700532 String result;
533 result = new String(bytes, StandardCharsets.US_ASCII).trim();
alshabibc4901cd2014-09-05 16:50:40 -0700534 return result;
535 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700536
537 /**
538 * Deserializer function for DHCP packets.
539 *
540 * @return deserializer function
541 */
542 public static Deserializer<DHCP> deserializer() {
543 return (data, offset, length) -> {
544 checkInput(data, offset, length, MIN_HEADER_LENGTH);
545
546 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
547 DHCP dhcp = new DHCP();
548
549 dhcp.opCode = bb.get();
550 dhcp.hardwareType = bb.get();
551 dhcp.hardwareAddressLength = bb.get();
552 dhcp.hops = bb.get();
553 dhcp.transactionId = bb.getInt();
554 dhcp.seconds = bb.getShort();
555 dhcp.flags = bb.getShort();
556 dhcp.clientIPAddress = bb.getInt();
557 dhcp.yourIPAddress = bb.getInt();
558 dhcp.serverIPAddress = bb.getInt();
559 dhcp.gatewayIPAddress = bb.getInt();
Yi Tsengc7403c22017-06-19 16:23:22 -0700560 final int hardwareAddressLength = UNSIGNED_BYTE_MASK & dhcp.hardwareAddressLength;
Jonathan Hart2a655752015-04-07 16:46:33 -0700561 dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
562
563 bb.get(dhcp.clientHardwareAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700564 for (int i = hardwareAddressLength; i < BASE_HW_ADDR_LEN; ++i) {
Jonathan Hart2a655752015-04-07 16:46:33 -0700565 bb.get();
566 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700567 dhcp.serverName = readString(bb, BASE_SERVER_NAME_LEN);
568 dhcp.bootFileName = readString(bb, BASE_BOOT_FILE_NAME_LEN);
Jonathan Hart2a655752015-04-07 16:46:33 -0700569 // read the magic cookie
570 // magic cookie
Yi Tsengc7403c22017-06-19 16:23:22 -0700571 bb.getInt();
Jonathan Hart2a655752015-04-07 16:46:33 -0700572
573 // read options
574 boolean foundEndOptionsMarker = false;
575 while (bb.hasRemaining()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700576 DhcpOption option;
Yi Tsengc7403c22017-06-19 16:23:22 -0700577 int pos = bb.position();
578 int optCode = UNSIGNED_BYTE_MASK & bb.array()[pos]; // to unsigned integer
579 int optLen;
580 byte[] optData;
581
582 if (optCode == DHCPOptionCode.OptionCode_Pad.value) {
583 // pad, skip
584 // read option code
585 bb.get();
Jonathan Hart2a655752015-04-07 16:46:33 -0700586 continue;
Yi Tsengc7403c22017-06-19 16:23:22 -0700587 }
588 if (optCode == (UNSIGNED_BYTE_MASK & DHCPOptionCode.OptionCode_END.value)) {
589 // end of dhcp options or invalid option and set the END option
590 option = new DhcpOption();
591 option.setCode((byte) optCode);
592 dhcp.options.add(option);
Jonathan Hart2a655752015-04-07 16:46:33 -0700593 foundEndOptionsMarker = true;
594 break;
595 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700596
597 if (bb.remaining() < 2) {
598 // No option length
599 throw new DeserializationException("Buffer underflow while reading DHCP option");
600 }
601
602 optLen = UNSIGNED_BYTE_MASK & bb.array()[pos + 1];
603 if (bb.remaining() < DhcpOption.DEFAULT_LEN + optLen) {
604 // Invalid option length
605 throw new DeserializationException("Buffer underflow while reading DHCP option");
606 }
607 optData = new byte[DhcpOption.DEFAULT_LEN + optLen];
608 bb.get(optData);
609 if (OPTION_DESERIALIZERS.containsKey((byte) optCode)) {
610 option = OPTION_DESERIALIZERS.get((byte) optCode).deserialize(optData, 0, optData.length);
611 dhcp.options.add(option);
612 } else {
613 // default option
614 option = DhcpOption.deserializer().deserialize(optData, 0, optData.length);
615 dhcp.options.add(option);
616 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700617 }
618
619 if (!foundEndOptionsMarker) {
620 throw new DeserializationException("DHCP End options marker was missing");
621 }
622
623 return dhcp;
624 };
625 }
Jian Li5fc14292015-12-04 11:30:46 -0800626
627 @Override
628 public String toString() {
629 return toStringHelper(getClass())
630 .add("opCode", Byte.toString(opCode))
631 .add("hardwareType", Byte.toString(hardwareType))
632 .add("hardwareAddressLength", Byte.toString(hardwareAddressLength))
633 .add("hops", Byte.toString(hops))
634 .add("transactionId", Integer.toString(transactionId))
635 .add("seconds", Short.toString(seconds))
636 .add("flags", Short.toString(flags))
637 .add("clientIPAddress", Integer.toString(clientIPAddress))
638 .add("yourIPAddress", Integer.toString(yourIPAddress))
639 .add("serverIPAddress", Integer.toString(serverIPAddress))
640 .add("gatewayIPAddress", Integer.toString(gatewayIPAddress))
641 .add("clientHardwareAddress", Arrays.toString(clientHardwareAddress))
642 .add("serverName", serverName)
643 .add("bootFileName", bootFileName)
644 .toString();
645 // TODO: need to handle options
646 }
alshabibc4901cd2014-09-05 16:50:40 -0700647}