blob: 40a7745d3c6560d78bf8719bada0d7b4fb729167 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 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
21import java.io.UnsupportedEncodingException;
22import java.nio.ByteBuffer;
23import java.util.ArrayList;
24import java.util.List;
25import java.util.ListIterator;
26
Jonathan Hart2a655752015-04-07 16:46:33 -070027import static org.onlab.packet.PacketUtils.*;
28
alshabibc4901cd2014-09-05 16:50:40 -070029/**
30 *
alshabibc4901cd2014-09-05 16:50:40 -070031 */
32public class DHCP extends BasePacket {
33 /**
34 * Dynamic Host Configuration Protocol packet.
35 * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
36 * hops(1) | ------------------------------------------ | xid (4) |
37 * ------------------------------------------ | secs (2) | flags (2) |
38 * ------------------------------------------ | ciaddr (4) |
39 * ------------------------------------------ | yiaddr (4) |
40 * ------------------------------------------ | siaddr (4) |
41 * ------------------------------------------ | giaddr (4) |
42 * ------------------------------------------ | chaddr (16) |
43 * ------------------------------------------ | sname (64) |
44 * ------------------------------------------ | file (128) |
45 * ------------------------------------------ | options (312) |
46 * ------------------------------------------
47 *
48 */
49 // Header + magic without options
50 public static final int MIN_HEADER_LENGTH = 240;
51 public static final byte OPCODE_REQUEST = 0x1;
52 public static final byte OPCODE_REPLY = 0x2;
53
54 public static final byte HWTYPE_ETHERNET = 0x1;
55
56 public enum DHCPOptionCode {
57 OptionCode_SubnetMask((byte) 1), OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime(
58 (byte) 51), OptionCode_MessageType((byte) 53), OptionCode_DHCPServerIp(
59 (byte) 54), OptionCode_RequestedParameters((byte) 55), OptionCode_RenewalTime(
60 (byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID(
61 (byte) 61), OptionCode_END((byte) 255);
62
63 protected byte value;
64
65 private DHCPOptionCode(final byte value) {
66 this.value = value;
67 }
68
69 public byte getValue() {
70 return this.value;
71 }
72 }
73
74 protected byte opCode;
75 protected byte hardwareType;
76 protected byte hardwareAddressLength;
77 protected byte hops;
78 protected int transactionId;
79 protected short seconds;
80 protected short flags;
81 protected int clientIPAddress;
82 protected int yourIPAddress;
83 protected int serverIPAddress;
84 protected int gatewayIPAddress;
85 protected byte[] clientHardwareAddress;
86 protected String serverName;
87 protected String bootFileName;
88 protected List<DHCPOption> options = new ArrayList<DHCPOption>();
89
90 /**
91 * @return the opCode
92 */
93 public byte getOpCode() {
94 return this.opCode;
95 }
96
97 /**
98 * @param opCode
99 * the opCode to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800100 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700101 */
102 public DHCP setOpCode(final byte opCode) {
103 this.opCode = opCode;
104 return this;
105 }
106
107 /**
108 * @return the hardwareType
109 */
110 public byte getHardwareType() {
111 return this.hardwareType;
112 }
113
114 /**
115 * @param hardwareType
116 * the hardwareType to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800117 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700118 */
119 public DHCP setHardwareType(final byte hardwareType) {
120 this.hardwareType = hardwareType;
121 return this;
122 }
123
124 /**
125 * @return the hardwareAddressLength
126 */
127 public byte getHardwareAddressLength() {
128 return this.hardwareAddressLength;
129 }
130
131 /**
132 * @param hardwareAddressLength
133 * the hardwareAddressLength to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800134 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700135 */
136 public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
137 this.hardwareAddressLength = hardwareAddressLength;
138 return this;
139 }
140
141 /**
142 * @return the hops
143 */
144 public byte getHops() {
145 return this.hops;
146 }
147
148 /**
149 * @param hops
150 * the hops to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800151 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700152 */
153 public DHCP setHops(final byte hops) {
154 this.hops = hops;
155 return this;
156 }
157
158 /**
159 * @return the transactionId
160 */
161 public int getTransactionId() {
162 return this.transactionId;
163 }
164
165 /**
166 * @param transactionId
167 * the transactionId to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800168 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700169 */
170 public DHCP setTransactionId(final int transactionId) {
171 this.transactionId = transactionId;
172 return this;
173 }
174
175 /**
176 * @return the seconds
177 */
178 public short getSeconds() {
179 return this.seconds;
180 }
181
182 /**
183 * @param seconds
184 * the seconds to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800185 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700186 */
187 public DHCP setSeconds(final short seconds) {
188 this.seconds = seconds;
189 return this;
190 }
191
192 /**
193 * @return the flags
194 */
195 public short getFlags() {
196 return this.flags;
197 }
198
199 /**
200 * @param flags
201 * the flags to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800202 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700203 */
204 public DHCP setFlags(final short flags) {
205 this.flags = flags;
206 return this;
207 }
208
209 /**
210 * @return the clientIPAddress
211 */
212 public int getClientIPAddress() {
213 return this.clientIPAddress;
214 }
215
216 /**
217 * @param clientIPAddress
218 * the clientIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800219 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700220 */
221 public DHCP setClientIPAddress(final int clientIPAddress) {
222 this.clientIPAddress = clientIPAddress;
223 return this;
224 }
225
226 /**
227 * @return the yourIPAddress
228 */
229 public int getYourIPAddress() {
230 return this.yourIPAddress;
231 }
232
233 /**
234 * @param yourIPAddress
235 * the yourIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800236 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700237 */
238 public DHCP setYourIPAddress(final int yourIPAddress) {
239 this.yourIPAddress = yourIPAddress;
240 return this;
241 }
242
243 /**
244 * @return the serverIPAddress
245 */
246 public int getServerIPAddress() {
247 return this.serverIPAddress;
248 }
249
250 /**
251 * @param serverIPAddress
252 * the serverIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800253 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700254 */
255 public DHCP setServerIPAddress(final int serverIPAddress) {
256 this.serverIPAddress = serverIPAddress;
257 return this;
258 }
259
260 /**
261 * @return the gatewayIPAddress
262 */
263 public int getGatewayIPAddress() {
264 return this.gatewayIPAddress;
265 }
266
267 /**
268 * @param gatewayIPAddress
269 * the gatewayIPAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800270 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700271 */
272 public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
273 this.gatewayIPAddress = gatewayIPAddress;
274 return this;
275 }
276
277 /**
278 * @return the clientHardwareAddress
279 */
280 public byte[] getClientHardwareAddress() {
281 return this.clientHardwareAddress;
282 }
283
284 /**
285 * @param clientHardwareAddress
286 * the clientHardwareAddress to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800287 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700288 */
289 public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
290 this.clientHardwareAddress = clientHardwareAddress;
291 return this;
292 }
293
294 /**
295 * Gets a specific DHCP option parameter.
296 *
tom5f18cf32014-09-13 14:10:57 -0700297 * @param optionCode
alshabibc4901cd2014-09-05 16:50:40 -0700298 * The option code to get
299 * @return The value of the option if it exists, null otherwise
300 */
301 public DHCPOption getOption(final DHCPOptionCode optionCode) {
302 for (final DHCPOption opt : this.options) {
303 if (opt.code == optionCode.value) {
304 return opt;
305 }
306 }
307 return null;
308 }
309
310 /**
311 * @return the options
312 */
313 public List<DHCPOption> getOptions() {
314 return this.options;
315 }
316
317 /**
318 * @param options
319 * the options to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800320 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700321 */
322 public DHCP setOptions(final List<DHCPOption> options) {
323 this.options = options;
324 return this;
325 }
326
327 /**
328 * @return the packetType base on option 53
329 */
330 public DHCPPacketType getPacketType() {
331 final ListIterator<DHCPOption> lit = this.options.listIterator();
332 while (lit.hasNext()) {
333 final DHCPOption option = lit.next();
334 // only care option 53
335 if (option.getCode() == 53) {
336 return DHCPPacketType.getType(option.getData()[0]);
337 }
338 }
339 return null;
340 }
341
342 /**
343 * @return the serverName
344 */
345 public String getServerName() {
346 return this.serverName;
347 }
348
349 /**
350 * @param server
351 * the serverName to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800352 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700353 */
354 public DHCP setServerName(final String server) {
355 this.serverName = server;
356 return this;
357 }
358
359 /**
360 * @return the bootFileName
361 */
362 public String getBootFileName() {
363 return this.bootFileName;
364 }
365
366 /**
367 * @param bootFile
368 * the bootFileName to set
Yuta HIGUCHI2281b3f2014-11-04 00:20:48 -0800369 * @return this
alshabibc4901cd2014-09-05 16:50:40 -0700370 */
371 public DHCP setBootFileName(final String bootFile) {
372 this.bootFileName = bootFile;
373 return this;
374 }
375
376 @Override
377 public byte[] serialize() {
378 // not guaranteed to retain length/exact format
379 this.resetChecksum();
380
381 // minimum size 240 including magic cookie, options generally padded to
382 // 300
383 int optionsLength = 0;
384 for (final DHCPOption option : this.options) {
Yuta HIGUCHIe5ca93b2014-10-23 09:49:00 -0700385 if (option.getCode() == 0 || option.getCode() == ((byte) 255)) {
alshabibc4901cd2014-09-05 16:50:40 -0700386 optionsLength += 1;
387 } else {
388 optionsLength += 2 + (0xff & option.getLength());
389 }
390 }
391 int optionsPadLength = 0;
392 if (optionsLength < 60) {
393 optionsPadLength = 60 - optionsLength;
394 }
395
396 final byte[] data = new byte[240 + optionsLength + optionsPadLength];
397 final ByteBuffer bb = ByteBuffer.wrap(data);
398 bb.put(this.opCode);
399 bb.put(this.hardwareType);
400 bb.put(this.hardwareAddressLength);
401 bb.put(this.hops);
402 bb.putInt(this.transactionId);
403 bb.putShort(this.seconds);
404 bb.putShort(this.flags);
405 bb.putInt(this.clientIPAddress);
406 bb.putInt(this.yourIPAddress);
407 bb.putInt(this.serverIPAddress);
408 bb.putInt(this.gatewayIPAddress);
409 bb.put(this.clientHardwareAddress);
410 if (this.clientHardwareAddress.length < 16) {
411 for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
412 bb.put((byte) 0x0);
413 }
414 }
415 this.writeString(this.serverName, bb, 64);
416 this.writeString(this.bootFileName, bb, 128);
417 // magic cookie
418 bb.put((byte) 0x63);
419 bb.put((byte) 0x82);
420 bb.put((byte) 0x53);
421 bb.put((byte) 0x63);
422 for (final DHCPOption option : this.options) {
423 final int code = option.getCode() & 0xff;
424 bb.put((byte) code);
425 if (code != 0 && code != 255) {
426 bb.put(option.getLength());
427 bb.put(option.getData());
428 }
429 }
430 // assume the rest is padded out with zeroes
431 return data;
432 }
433
alshabibc4901cd2014-09-05 16:50:40 -0700434 @Override
435 public IPacket deserialize(final byte[] data, final int offset,
Jonathan Hart2a655752015-04-07 16:46:33 -0700436 final int length) {
alshabibc4901cd2014-09-05 16:50:40 -0700437 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
438 if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
439 return this;
440 }
441
442 this.opCode = bb.get();
443 this.hardwareType = bb.get();
444 this.hardwareAddressLength = bb.get();
445 this.hops = bb.get();
446 this.transactionId = bb.getInt();
447 this.seconds = bb.getShort();
448 this.flags = bb.getShort();
449 this.clientIPAddress = bb.getInt();
450 this.yourIPAddress = bb.getInt();
451 this.serverIPAddress = bb.getInt();
452 this.gatewayIPAddress = bb.getInt();
453 final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
454 this.clientHardwareAddress = new byte[hardwareAddressLength];
455
456 bb.get(this.clientHardwareAddress);
457 for (int i = hardwareAddressLength; i < 16; ++i) {
458 bb.get();
459 }
460 this.serverName = this.readString(bb, 64);
461 this.bootFileName = this.readString(bb, 128);
462 // read the magic cookie
463 // magic cookie
464 bb.get();
465 bb.get();
466 bb.get();
467 bb.get();
468 // read options
469 while (bb.hasRemaining()) {
470 final DHCPOption option = new DHCPOption();
471 int code = 0xff & bb.get(); // convert signed byte to int in range
472 // [0,255]
473 option.setCode((byte) code);
474 if (code == 0) {
475 // skip these
476 continue;
477 } else if (code != 255) {
478 if (bb.hasRemaining()) {
479 final int l = 0xff & bb.get(); // convert signed byte to
480 // int in range [0,255]
481 option.setLength((byte) l);
482 if (bb.remaining() >= l) {
483 final byte[] optionData = new byte[l];
484 bb.get(optionData);
485 option.setData(optionData);
486 } else {
487 // Skip the invalid option and set the END option
488 code = 0xff;
489 option.setCode((byte) code);
490 option.setLength((byte) 0);
491 }
492 } else {
493 // Skip the invalid option and set the END option
494 code = 0xff;
495 option.setCode((byte) code);
496 option.setLength((byte) 0);
497 }
498 }
499 this.options.add(option);
500 if (code == 255) {
501 // remaining bytes are supposed to be 0, but ignore them just in
502 // case
503 break;
504 }
505 }
506
507 return this;
508 }
509
Jonathan Hart2a655752015-04-07 16:46:33 -0700510 protected void writeString(final String string, final ByteBuffer bb,
511 final int maxLength) {
512 if (string == null) {
513 for (int i = 0; i < maxLength; ++i) {
514 bb.put((byte) 0x0);
515 }
516 } else {
517 byte[] bytes = null;
518 try {
519 bytes = string.getBytes("ascii");
520 } catch (final UnsupportedEncodingException e) {
521 throw new RuntimeException("Failure encoding server name", e);
522 }
523 int writeLength = bytes.length;
524 if (writeLength > maxLength) {
525 writeLength = maxLength;
526 }
527 bb.put(bytes, 0, writeLength);
528 for (int i = writeLength; i < maxLength; ++i) {
529 bb.put((byte) 0x0);
530 }
531 }
532 }
533
534 private static String readString(final ByteBuffer bb, final int maxLength) {
alshabibc4901cd2014-09-05 16:50:40 -0700535 final byte[] bytes = new byte[maxLength];
536 bb.get(bytes);
537 String result = null;
538 try {
539 result = new String(bytes, "ascii").trim();
540 } catch (final UnsupportedEncodingException e) {
541 throw new RuntimeException("Failure decoding string", e);
542 }
543 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();
569 final int hardwareAddressLength = 0xff & dhcp.hardwareAddressLength;
570 dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
571
572 bb.get(dhcp.clientHardwareAddress);
573 for (int i = hardwareAddressLength; i < 16; ++i) {
574 bb.get();
575 }
576 dhcp.serverName = readString(bb, 64);
577 dhcp.bootFileName = readString(bb, 128);
578 // read the magic cookie
579 // magic cookie
580 bb.get();
581 bb.get();
582 bb.get();
583 bb.get();
584
585 // read options
586 boolean foundEndOptionsMarker = false;
587 while (bb.hasRemaining()) {
588 final DHCPOption option = new DHCPOption();
589 int code = 0xff & bb.get(); // convert signed byte to int in range
590 // [0,255]
591 option.setCode((byte) code);
592 if (code == 0) {
593 // skip these
594 continue;
595 } else if (code != 255) {
596 if (bb.hasRemaining()) {
597 final int l = 0xff & bb.get(); // convert signed byte to
598 // int in range [0,255]
599 option.setLength((byte) l);
600 if (bb.remaining() >= l) {
601 final byte[] optionData = new byte[l];
602 bb.get(optionData);
603 option.setData(optionData);
604 dhcp.options.add(option);
605 } else {
606 throw new DeserializationException(
607 "Buffer underflow while reading DHCP option");
608 }
609 }
610 } else if (code == 255) {
611 // remaining bytes are supposed to be 0, but ignore them just in
612 // case
613 foundEndOptionsMarker = true;
614 break;
615 }
616 }
617
618 if (!foundEndOptionsMarker) {
619 throw new DeserializationException("DHCP End options marker was missing");
620 }
621
622 return dhcp;
623 };
624 }
alshabibc4901cd2014-09-05 16:50:40 -0700625}