blob: ed1741e5bd377a0e2a3732deda06c945c58c7fd7 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
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
alshabibc4901cd2014-09-05 16:50:40 -0700508 @Override
509 public IPacket deserialize(final byte[] data, final int offset,
Jonathan Hart2a655752015-04-07 16:46:33 -0700510 final int length) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700511 try {
512 return deserializer().deserialize(data, offset, length);
513 } catch (DeserializationException e) {
514 return null;
alshabibc4901cd2014-09-05 16:50:40 -0700515 }
alshabibc4901cd2014-09-05 16:50:40 -0700516 }
517
Jonathan Hart2a655752015-04-07 16:46:33 -0700518 protected void writeString(final String string, final ByteBuffer bb,
519 final int maxLength) {
520 if (string == null) {
521 for (int i = 0; i < maxLength; ++i) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700522 bb.put(PAD_BYTE);
Jonathan Hart2a655752015-04-07 16:46:33 -0700523 }
524 } else {
Yi Tsengc7403c22017-06-19 16:23:22 -0700525 byte[] bytes;
526 bytes = string.getBytes(StandardCharsets.US_ASCII);
Jonathan Hart2a655752015-04-07 16:46:33 -0700527 int writeLength = bytes.length;
528 if (writeLength > maxLength) {
529 writeLength = maxLength;
530 }
531 bb.put(bytes, 0, writeLength);
532 for (int i = writeLength; i < maxLength; ++i) {
533 bb.put((byte) 0x0);
534 }
535 }
536 }
537
538 private static String readString(final ByteBuffer bb, final int maxLength) {
alshabibc4901cd2014-09-05 16:50:40 -0700539 final byte[] bytes = new byte[maxLength];
540 bb.get(bytes);
Yi Tsengc7403c22017-06-19 16:23:22 -0700541 String result;
542 result = new String(bytes, StandardCharsets.US_ASCII).trim();
alshabibc4901cd2014-09-05 16:50:40 -0700543 return result;
544 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700545
546 /**
547 * Deserializer function for DHCP packets.
548 *
549 * @return deserializer function
550 */
551 public static Deserializer<DHCP> deserializer() {
552 return (data, offset, length) -> {
553 checkInput(data, offset, length, MIN_HEADER_LENGTH);
554
555 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
556 DHCP dhcp = new DHCP();
557
558 dhcp.opCode = bb.get();
559 dhcp.hardwareType = bb.get();
560 dhcp.hardwareAddressLength = bb.get();
561 dhcp.hops = bb.get();
562 dhcp.transactionId = bb.getInt();
563 dhcp.seconds = bb.getShort();
564 dhcp.flags = bb.getShort();
565 dhcp.clientIPAddress = bb.getInt();
566 dhcp.yourIPAddress = bb.getInt();
567 dhcp.serverIPAddress = bb.getInt();
568 dhcp.gatewayIPAddress = bb.getInt();
Yi Tsengc7403c22017-06-19 16:23:22 -0700569 final int hardwareAddressLength = UNSIGNED_BYTE_MASK & dhcp.hardwareAddressLength;
Jonathan Hart2a655752015-04-07 16:46:33 -0700570 dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
571
572 bb.get(dhcp.clientHardwareAddress);
Yi Tsengc7403c22017-06-19 16:23:22 -0700573 for (int i = hardwareAddressLength; i < BASE_HW_ADDR_LEN; ++i) {
Jonathan Hart2a655752015-04-07 16:46:33 -0700574 bb.get();
575 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700576 dhcp.serverName = readString(bb, BASE_SERVER_NAME_LEN);
577 dhcp.bootFileName = readString(bb, BASE_BOOT_FILE_NAME_LEN);
Jonathan Hart2a655752015-04-07 16:46:33 -0700578 // read the magic cookie
579 // magic cookie
Yi Tsengc7403c22017-06-19 16:23:22 -0700580 bb.getInt();
Jonathan Hart2a655752015-04-07 16:46:33 -0700581
582 // read options
583 boolean foundEndOptionsMarker = false;
584 while (bb.hasRemaining()) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700585 DhcpOption option;
586
587 int pos = bb.position();
588 int optCode = UNSIGNED_BYTE_MASK & bb.array()[pos]; // to unsigned integer
589 int optLen;
590 byte[] optData;
591
592 if (optCode == DHCPOptionCode.OptionCode_Pad.value) {
593 // pad, skip
594 // read option code
595 bb.get();
Jonathan Hart2a655752015-04-07 16:46:33 -0700596 continue;
Yi Tsengc7403c22017-06-19 16:23:22 -0700597 }
598 if (optCode == (UNSIGNED_BYTE_MASK & DHCPOptionCode.OptionCode_END.value)) {
599 // end of dhcp options or invalid option and set the END option
600 option = new DhcpOption();
601 option.setCode((byte) optCode);
602 dhcp.options.add(option);
Jonathan Hart2a655752015-04-07 16:46:33 -0700603 foundEndOptionsMarker = true;
604 break;
605 }
Yi Tsengc7403c22017-06-19 16:23:22 -0700606
607 if (bb.remaining() < 2) {
608 // No option length
609 throw new DeserializationException("Buffer underflow while reading DHCP option");
610 }
611
612 optLen = UNSIGNED_BYTE_MASK & bb.array()[pos + 1];
613 if (bb.remaining() < DhcpOption.DEFAULT_LEN + optLen) {
614 // Invalid option length
615 throw new DeserializationException("Buffer underflow while reading DHCP option");
616 }
617 optData = new byte[DhcpOption.DEFAULT_LEN + optLen];
618 bb.get(optData);
619 if (OPTION_DESERIALIZERS.containsKey((byte) optCode)) {
620 option = OPTION_DESERIALIZERS.get((byte) optCode).deserialize(optData, 0, optData.length);
621 dhcp.options.add(option);
622 } else {
623 // default option
624 option = DhcpOption.deserializer().deserialize(optData, 0, optData.length);
625 dhcp.options.add(option);
626 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700627 }
628
629 if (!foundEndOptionsMarker) {
630 throw new DeserializationException("DHCP End options marker was missing");
631 }
632
633 return dhcp;
634 };
635 }
Jian Li5fc14292015-12-04 11:30:46 -0800636
637 @Override
638 public String toString() {
639 return toStringHelper(getClass())
640 .add("opCode", Byte.toString(opCode))
641 .add("hardwareType", Byte.toString(hardwareType))
642 .add("hardwareAddressLength", Byte.toString(hardwareAddressLength))
643 .add("hops", Byte.toString(hops))
644 .add("transactionId", Integer.toString(transactionId))
645 .add("seconds", Short.toString(seconds))
646 .add("flags", Short.toString(flags))
647 .add("clientIPAddress", Integer.toString(clientIPAddress))
648 .add("yourIPAddress", Integer.toString(yourIPAddress))
649 .add("serverIPAddress", Integer.toString(serverIPAddress))
650 .add("gatewayIPAddress", Integer.toString(gatewayIPAddress))
651 .add("clientHardwareAddress", Arrays.toString(clientHardwareAddress))
652 .add("serverName", serverName)
653 .add("bootFileName", bootFileName)
654 .toString();
655 // TODO: need to handle options
656 }
alshabibc4901cd2014-09-05 16:50:40 -0700657}