blob: 2a116b156e8709b42602ac4847ebd89ecc9210c1 [file] [log] [blame]
alshabibc4901cd2014-09-05 16:50:40 -07001/*******************************************************************************
2 * Copyright 2014 Open Networking Laboratory
3 *
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/**
17 * Copyright 2011, Big Switch Networks, Inc.
18 * Originally created by David Erickson, Stanford University
19 *
20 * Licensed under the Apache License, Version 2.0 (the "License"); you may
21 * not use this file except in compliance with the License. You may obtain
22 * a copy of the License at
23 *
24 * http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
28 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
29 * License for the specific language governing permissions and limitations
30 * under the License.
31 **/
32
33package org.onlab.packet;
34
35import java.io.UnsupportedEncodingException;
36import java.nio.ByteBuffer;
37import java.util.ArrayList;
38import java.util.List;
39import java.util.ListIterator;
40
41/**
42 *
alshabibc4901cd2014-09-05 16:50:40 -070043 */
44public class DHCP extends BasePacket {
45 /**
46 * Dynamic Host Configuration Protocol packet.
47 * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
48 * hops(1) | ------------------------------------------ | xid (4) |
49 * ------------------------------------------ | secs (2) | flags (2) |
50 * ------------------------------------------ | ciaddr (4) |
51 * ------------------------------------------ | yiaddr (4) |
52 * ------------------------------------------ | siaddr (4) |
53 * ------------------------------------------ | giaddr (4) |
54 * ------------------------------------------ | chaddr (16) |
55 * ------------------------------------------ | sname (64) |
56 * ------------------------------------------ | file (128) |
57 * ------------------------------------------ | options (312) |
58 * ------------------------------------------
59 *
60 */
61 // Header + magic without options
62 public static final int MIN_HEADER_LENGTH = 240;
63 public static final byte OPCODE_REQUEST = 0x1;
64 public static final byte OPCODE_REPLY = 0x2;
65
66 public static final byte HWTYPE_ETHERNET = 0x1;
67
68 public enum DHCPOptionCode {
69 OptionCode_SubnetMask((byte) 1), OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime(
70 (byte) 51), OptionCode_MessageType((byte) 53), OptionCode_DHCPServerIp(
71 (byte) 54), OptionCode_RequestedParameters((byte) 55), OptionCode_RenewalTime(
72 (byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID(
73 (byte) 61), OptionCode_END((byte) 255);
74
75 protected byte value;
76
77 private DHCPOptionCode(final byte value) {
78 this.value = value;
79 }
80
81 public byte getValue() {
82 return this.value;
83 }
84 }
85
86 protected byte opCode;
87 protected byte hardwareType;
88 protected byte hardwareAddressLength;
89 protected byte hops;
90 protected int transactionId;
91 protected short seconds;
92 protected short flags;
93 protected int clientIPAddress;
94 protected int yourIPAddress;
95 protected int serverIPAddress;
96 protected int gatewayIPAddress;
97 protected byte[] clientHardwareAddress;
98 protected String serverName;
99 protected String bootFileName;
100 protected List<DHCPOption> options = new ArrayList<DHCPOption>();
101
102 /**
103 * @return the opCode
104 */
105 public byte getOpCode() {
106 return this.opCode;
107 }
108
109 /**
110 * @param opCode
111 * the opCode to set
112 */
113 public DHCP setOpCode(final byte opCode) {
114 this.opCode = opCode;
115 return this;
116 }
117
118 /**
119 * @return the hardwareType
120 */
121 public byte getHardwareType() {
122 return this.hardwareType;
123 }
124
125 /**
126 * @param hardwareType
127 * the hardwareType to set
128 */
129 public DHCP setHardwareType(final byte hardwareType) {
130 this.hardwareType = hardwareType;
131 return this;
132 }
133
134 /**
135 * @return the hardwareAddressLength
136 */
137 public byte getHardwareAddressLength() {
138 return this.hardwareAddressLength;
139 }
140
141 /**
142 * @param hardwareAddressLength
143 * the hardwareAddressLength to set
144 */
145 public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
146 this.hardwareAddressLength = hardwareAddressLength;
147 return this;
148 }
149
150 /**
151 * @return the hops
152 */
153 public byte getHops() {
154 return this.hops;
155 }
156
157 /**
158 * @param hops
159 * the hops to set
160 */
161 public DHCP setHops(final byte hops) {
162 this.hops = hops;
163 return this;
164 }
165
166 /**
167 * @return the transactionId
168 */
169 public int getTransactionId() {
170 return this.transactionId;
171 }
172
173 /**
174 * @param transactionId
175 * the transactionId to set
176 */
177 public DHCP setTransactionId(final int transactionId) {
178 this.transactionId = transactionId;
179 return this;
180 }
181
182 /**
183 * @return the seconds
184 */
185 public short getSeconds() {
186 return this.seconds;
187 }
188
189 /**
190 * @param seconds
191 * the seconds to set
192 */
193 public DHCP setSeconds(final short seconds) {
194 this.seconds = seconds;
195 return this;
196 }
197
198 /**
199 * @return the flags
200 */
201 public short getFlags() {
202 return this.flags;
203 }
204
205 /**
206 * @param flags
207 * the flags to set
208 */
209 public DHCP setFlags(final short flags) {
210 this.flags = flags;
211 return this;
212 }
213
214 /**
215 * @return the clientIPAddress
216 */
217 public int getClientIPAddress() {
218 return this.clientIPAddress;
219 }
220
221 /**
222 * @param clientIPAddress
223 * the clientIPAddress to set
224 */
225 public DHCP setClientIPAddress(final int clientIPAddress) {
226 this.clientIPAddress = clientIPAddress;
227 return this;
228 }
229
230 /**
231 * @return the yourIPAddress
232 */
233 public int getYourIPAddress() {
234 return this.yourIPAddress;
235 }
236
237 /**
238 * @param yourIPAddress
239 * the yourIPAddress to set
240 */
241 public DHCP setYourIPAddress(final int yourIPAddress) {
242 this.yourIPAddress = yourIPAddress;
243 return this;
244 }
245
246 /**
247 * @return the serverIPAddress
248 */
249 public int getServerIPAddress() {
250 return this.serverIPAddress;
251 }
252
253 /**
254 * @param serverIPAddress
255 * the serverIPAddress to set
256 */
257 public DHCP setServerIPAddress(final int serverIPAddress) {
258 this.serverIPAddress = serverIPAddress;
259 return this;
260 }
261
262 /**
263 * @return the gatewayIPAddress
264 */
265 public int getGatewayIPAddress() {
266 return this.gatewayIPAddress;
267 }
268
269 /**
270 * @param gatewayIPAddress
271 * the gatewayIPAddress to set
272 */
273 public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
274 this.gatewayIPAddress = gatewayIPAddress;
275 return this;
276 }
277
278 /**
279 * @return the clientHardwareAddress
280 */
281 public byte[] getClientHardwareAddress() {
282 return this.clientHardwareAddress;
283 }
284
285 /**
286 * @param clientHardwareAddress
287 * the clientHardwareAddress to set
288 */
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
320 */
321 public DHCP setOptions(final List<DHCPOption> options) {
322 this.options = options;
323 return this;
324 }
325
326 /**
327 * @return the packetType base on option 53
328 */
329 public DHCPPacketType getPacketType() {
330 final ListIterator<DHCPOption> lit = this.options.listIterator();
331 while (lit.hasNext()) {
332 final DHCPOption option = lit.next();
333 // only care option 53
334 if (option.getCode() == 53) {
335 return DHCPPacketType.getType(option.getData()[0]);
336 }
337 }
338 return null;
339 }
340
341 /**
342 * @return the serverName
343 */
344 public String getServerName() {
345 return this.serverName;
346 }
347
348 /**
349 * @param server
350 * the serverName to set
351 */
352 public DHCP setServerName(final String server) {
353 this.serverName = server;
354 return this;
355 }
356
357 /**
358 * @return the bootFileName
359 */
360 public String getBootFileName() {
361 return this.bootFileName;
362 }
363
364 /**
365 * @param bootFile
366 * the bootFileName to set
367 */
368 public DHCP setBootFileName(final String bootFile) {
369 this.bootFileName = bootFile;
370 return this;
371 }
372
373 @Override
374 public byte[] serialize() {
375 // not guaranteed to retain length/exact format
376 this.resetChecksum();
377
378 // minimum size 240 including magic cookie, options generally padded to
379 // 300
380 int optionsLength = 0;
381 for (final DHCPOption option : this.options) {
382 if (option.getCode() == 0 || option.getCode() == 255) {
383 optionsLength += 1;
384 } else {
385 optionsLength += 2 + (0xff & option.getLength());
386 }
387 }
388 int optionsPadLength = 0;
389 if (optionsLength < 60) {
390 optionsPadLength = 60 - optionsLength;
391 }
392
393 final byte[] data = new byte[240 + optionsLength + optionsPadLength];
394 final ByteBuffer bb = ByteBuffer.wrap(data);
395 bb.put(this.opCode);
396 bb.put(this.hardwareType);
397 bb.put(this.hardwareAddressLength);
398 bb.put(this.hops);
399 bb.putInt(this.transactionId);
400 bb.putShort(this.seconds);
401 bb.putShort(this.flags);
402 bb.putInt(this.clientIPAddress);
403 bb.putInt(this.yourIPAddress);
404 bb.putInt(this.serverIPAddress);
405 bb.putInt(this.gatewayIPAddress);
406 bb.put(this.clientHardwareAddress);
407 if (this.clientHardwareAddress.length < 16) {
408 for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
409 bb.put((byte) 0x0);
410 }
411 }
412 this.writeString(this.serverName, bb, 64);
413 this.writeString(this.bootFileName, bb, 128);
414 // magic cookie
415 bb.put((byte) 0x63);
416 bb.put((byte) 0x82);
417 bb.put((byte) 0x53);
418 bb.put((byte) 0x63);
419 for (final DHCPOption option : this.options) {
420 final int code = option.getCode() & 0xff;
421 bb.put((byte) code);
422 if (code != 0 && code != 255) {
423 bb.put(option.getLength());
424 bb.put(option.getData());
425 }
426 }
427 // assume the rest is padded out with zeroes
428 return data;
429 }
430
431 protected void writeString(final String string, final ByteBuffer bb,
432 final int maxLength) {
433 if (string == null) {
434 for (int i = 0; i < maxLength; ++i) {
435 bb.put((byte) 0x0);
436 }
437 } else {
438 byte[] bytes = null;
439 try {
440 bytes = string.getBytes("ascii");
441 } catch (final UnsupportedEncodingException e) {
442 throw new RuntimeException("Failure encoding server name", e);
443 }
444 int writeLength = bytes.length;
445 if (writeLength > maxLength) {
446 writeLength = maxLength;
447 }
448 bb.put(bytes, 0, writeLength);
449 for (int i = writeLength; i < maxLength; ++i) {
450 bb.put((byte) 0x0);
451 }
452 }
453 }
454
455 @Override
456 public IPacket deserialize(final byte[] data, final int offset,
457 final int length) {
458 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
459 if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
460 return this;
461 }
462
463 this.opCode = bb.get();
464 this.hardwareType = bb.get();
465 this.hardwareAddressLength = bb.get();
466 this.hops = bb.get();
467 this.transactionId = bb.getInt();
468 this.seconds = bb.getShort();
469 this.flags = bb.getShort();
470 this.clientIPAddress = bb.getInt();
471 this.yourIPAddress = bb.getInt();
472 this.serverIPAddress = bb.getInt();
473 this.gatewayIPAddress = bb.getInt();
474 final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
475 this.clientHardwareAddress = new byte[hardwareAddressLength];
476
477 bb.get(this.clientHardwareAddress);
478 for (int i = hardwareAddressLength; i < 16; ++i) {
479 bb.get();
480 }
481 this.serverName = this.readString(bb, 64);
482 this.bootFileName = this.readString(bb, 128);
483 // read the magic cookie
484 // magic cookie
485 bb.get();
486 bb.get();
487 bb.get();
488 bb.get();
489 // read options
490 while (bb.hasRemaining()) {
491 final DHCPOption option = new DHCPOption();
492 int code = 0xff & bb.get(); // convert signed byte to int in range
493 // [0,255]
494 option.setCode((byte) code);
495 if (code == 0) {
496 // skip these
497 continue;
498 } else if (code != 255) {
499 if (bb.hasRemaining()) {
500 final int l = 0xff & bb.get(); // convert signed byte to
501 // int in range [0,255]
502 option.setLength((byte) l);
503 if (bb.remaining() >= l) {
504 final byte[] optionData = new byte[l];
505 bb.get(optionData);
506 option.setData(optionData);
507 } else {
508 // Skip the invalid option and set the END option
509 code = 0xff;
510 option.setCode((byte) code);
511 option.setLength((byte) 0);
512 }
513 } else {
514 // Skip the invalid option and set the END option
515 code = 0xff;
516 option.setCode((byte) code);
517 option.setLength((byte) 0);
518 }
519 }
520 this.options.add(option);
521 if (code == 255) {
522 // remaining bytes are supposed to be 0, but ignore them just in
523 // case
524 break;
525 }
526 }
527
528 return this;
529 }
530
531 protected String readString(final ByteBuffer bb, final int maxLength) {
532 final byte[] bytes = new byte[maxLength];
533 bb.get(bytes);
534 String result = null;
535 try {
536 result = new String(bytes, "ascii").trim();
537 } catch (final UnsupportedEncodingException e) {
538 throw new RuntimeException("Failure decoding string", e);
539 }
540 return result;
541 }
542}