blob: 9ace6499855f15c932564bf6080ecaa5e5cc7b5f [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 *
43 * @author David Erickson (daviderickson@cs.stanford.edu)
44 */
45public class DHCP extends BasePacket {
46 /**
47 * Dynamic Host Configuration Protocol packet.
48 * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
49 * hops(1) | ------------------------------------------ | xid (4) |
50 * ------------------------------------------ | secs (2) | flags (2) |
51 * ------------------------------------------ | ciaddr (4) |
52 * ------------------------------------------ | yiaddr (4) |
53 * ------------------------------------------ | siaddr (4) |
54 * ------------------------------------------ | giaddr (4) |
55 * ------------------------------------------ | chaddr (16) |
56 * ------------------------------------------ | sname (64) |
57 * ------------------------------------------ | file (128) |
58 * ------------------------------------------ | options (312) |
59 * ------------------------------------------
60 *
61 */
62 // Header + magic without options
63 public static final int MIN_HEADER_LENGTH = 240;
64 public static final byte OPCODE_REQUEST = 0x1;
65 public static final byte OPCODE_REPLY = 0x2;
66
67 public static final byte HWTYPE_ETHERNET = 0x1;
68
69 public enum DHCPOptionCode {
70 OptionCode_SubnetMask((byte) 1), OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime(
71 (byte) 51), OptionCode_MessageType((byte) 53), OptionCode_DHCPServerIp(
72 (byte) 54), OptionCode_RequestedParameters((byte) 55), OptionCode_RenewalTime(
73 (byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID(
74 (byte) 61), OptionCode_END((byte) 255);
75
76 protected byte value;
77
78 private DHCPOptionCode(final byte value) {
79 this.value = value;
80 }
81
82 public byte getValue() {
83 return this.value;
84 }
85 }
86
87 protected byte opCode;
88 protected byte hardwareType;
89 protected byte hardwareAddressLength;
90 protected byte hops;
91 protected int transactionId;
92 protected short seconds;
93 protected short flags;
94 protected int clientIPAddress;
95 protected int yourIPAddress;
96 protected int serverIPAddress;
97 protected int gatewayIPAddress;
98 protected byte[] clientHardwareAddress;
99 protected String serverName;
100 protected String bootFileName;
101 protected List<DHCPOption> options = new ArrayList<DHCPOption>();
102
103 /**
104 * @return the opCode
105 */
106 public byte getOpCode() {
107 return this.opCode;
108 }
109
110 /**
111 * @param opCode
112 * the opCode to set
113 */
114 public DHCP setOpCode(final byte opCode) {
115 this.opCode = opCode;
116 return this;
117 }
118
119 /**
120 * @return the hardwareType
121 */
122 public byte getHardwareType() {
123 return this.hardwareType;
124 }
125
126 /**
127 * @param hardwareType
128 * the hardwareType to set
129 */
130 public DHCP setHardwareType(final byte hardwareType) {
131 this.hardwareType = hardwareType;
132 return this;
133 }
134
135 /**
136 * @return the hardwareAddressLength
137 */
138 public byte getHardwareAddressLength() {
139 return this.hardwareAddressLength;
140 }
141
142 /**
143 * @param hardwareAddressLength
144 * the hardwareAddressLength to set
145 */
146 public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
147 this.hardwareAddressLength = hardwareAddressLength;
148 return this;
149 }
150
151 /**
152 * @return the hops
153 */
154 public byte getHops() {
155 return this.hops;
156 }
157
158 /**
159 * @param hops
160 * the hops to set
161 */
162 public DHCP setHops(final byte hops) {
163 this.hops = hops;
164 return this;
165 }
166
167 /**
168 * @return the transactionId
169 */
170 public int getTransactionId() {
171 return this.transactionId;
172 }
173
174 /**
175 * @param transactionId
176 * the transactionId to set
177 */
178 public DHCP setTransactionId(final int transactionId) {
179 this.transactionId = transactionId;
180 return this;
181 }
182
183 /**
184 * @return the seconds
185 */
186 public short getSeconds() {
187 return this.seconds;
188 }
189
190 /**
191 * @param seconds
192 * the seconds to set
193 */
194 public DHCP setSeconds(final short seconds) {
195 this.seconds = seconds;
196 return this;
197 }
198
199 /**
200 * @return the flags
201 */
202 public short getFlags() {
203 return this.flags;
204 }
205
206 /**
207 * @param flags
208 * the flags to set
209 */
210 public DHCP setFlags(final short flags) {
211 this.flags = flags;
212 return this;
213 }
214
215 /**
216 * @return the clientIPAddress
217 */
218 public int getClientIPAddress() {
219 return this.clientIPAddress;
220 }
221
222 /**
223 * @param clientIPAddress
224 * the clientIPAddress to set
225 */
226 public DHCP setClientIPAddress(final int clientIPAddress) {
227 this.clientIPAddress = clientIPAddress;
228 return this;
229 }
230
231 /**
232 * @return the yourIPAddress
233 */
234 public int getYourIPAddress() {
235 return this.yourIPAddress;
236 }
237
238 /**
239 * @param yourIPAddress
240 * the yourIPAddress to set
241 */
242 public DHCP setYourIPAddress(final int yourIPAddress) {
243 this.yourIPAddress = yourIPAddress;
244 return this;
245 }
246
247 /**
248 * @return the serverIPAddress
249 */
250 public int getServerIPAddress() {
251 return this.serverIPAddress;
252 }
253
254 /**
255 * @param serverIPAddress
256 * the serverIPAddress to set
257 */
258 public DHCP setServerIPAddress(final int serverIPAddress) {
259 this.serverIPAddress = serverIPAddress;
260 return this;
261 }
262
263 /**
264 * @return the gatewayIPAddress
265 */
266 public int getGatewayIPAddress() {
267 return this.gatewayIPAddress;
268 }
269
270 /**
271 * @param gatewayIPAddress
272 * the gatewayIPAddress to set
273 */
274 public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
275 this.gatewayIPAddress = gatewayIPAddress;
276 return this;
277 }
278
279 /**
280 * @return the clientHardwareAddress
281 */
282 public byte[] getClientHardwareAddress() {
283 return this.clientHardwareAddress;
284 }
285
286 /**
287 * @param clientHardwareAddress
288 * the clientHardwareAddress to set
289 */
290 public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
291 this.clientHardwareAddress = clientHardwareAddress;
292 return this;
293 }
294
295 /**
296 * Gets a specific DHCP option parameter.
297 *
298 * @param opetionCode
299 * The option code to get
300 * @return The value of the option if it exists, null otherwise
301 */
302 public DHCPOption getOption(final DHCPOptionCode optionCode) {
303 for (final DHCPOption opt : this.options) {
304 if (opt.code == optionCode.value) {
305 return opt;
306 }
307 }
308 return null;
309 }
310
311 /**
312 * @return the options
313 */
314 public List<DHCPOption> getOptions() {
315 return this.options;
316 }
317
318 /**
319 * @param options
320 * the options to set
321 */
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
352 */
353 public DHCP setServerName(final String server) {
354 this.serverName = server;
355 return this;
356 }
357
358 /**
359 * @return the bootFileName
360 */
361 public String getBootFileName() {
362 return this.bootFileName;
363 }
364
365 /**
366 * @param bootFile
367 * the bootFileName to set
368 */
369 public DHCP setBootFileName(final String bootFile) {
370 this.bootFileName = bootFile;
371 return this;
372 }
373
374 @Override
375 public byte[] serialize() {
376 // not guaranteed to retain length/exact format
377 this.resetChecksum();
378
379 // minimum size 240 including magic cookie, options generally padded to
380 // 300
381 int optionsLength = 0;
382 for (final DHCPOption option : this.options) {
383 if (option.getCode() == 0 || option.getCode() == 255) {
384 optionsLength += 1;
385 } else {
386 optionsLength += 2 + (0xff & option.getLength());
387 }
388 }
389 int optionsPadLength = 0;
390 if (optionsLength < 60) {
391 optionsPadLength = 60 - optionsLength;
392 }
393
394 final byte[] data = new byte[240 + optionsLength + optionsPadLength];
395 final ByteBuffer bb = ByteBuffer.wrap(data);
396 bb.put(this.opCode);
397 bb.put(this.hardwareType);
398 bb.put(this.hardwareAddressLength);
399 bb.put(this.hops);
400 bb.putInt(this.transactionId);
401 bb.putShort(this.seconds);
402 bb.putShort(this.flags);
403 bb.putInt(this.clientIPAddress);
404 bb.putInt(this.yourIPAddress);
405 bb.putInt(this.serverIPAddress);
406 bb.putInt(this.gatewayIPAddress);
407 bb.put(this.clientHardwareAddress);
408 if (this.clientHardwareAddress.length < 16) {
409 for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
410 bb.put((byte) 0x0);
411 }
412 }
413 this.writeString(this.serverName, bb, 64);
414 this.writeString(this.bootFileName, bb, 128);
415 // magic cookie
416 bb.put((byte) 0x63);
417 bb.put((byte) 0x82);
418 bb.put((byte) 0x53);
419 bb.put((byte) 0x63);
420 for (final DHCPOption option : this.options) {
421 final int code = option.getCode() & 0xff;
422 bb.put((byte) code);
423 if (code != 0 && code != 255) {
424 bb.put(option.getLength());
425 bb.put(option.getData());
426 }
427 }
428 // assume the rest is padded out with zeroes
429 return data;
430 }
431
432 protected void writeString(final String string, final ByteBuffer bb,
433 final int maxLength) {
434 if (string == null) {
435 for (int i = 0; i < maxLength; ++i) {
436 bb.put((byte) 0x0);
437 }
438 } else {
439 byte[] bytes = null;
440 try {
441 bytes = string.getBytes("ascii");
442 } catch (final UnsupportedEncodingException e) {
443 throw new RuntimeException("Failure encoding server name", e);
444 }
445 int writeLength = bytes.length;
446 if (writeLength > maxLength) {
447 writeLength = maxLength;
448 }
449 bb.put(bytes, 0, writeLength);
450 for (int i = writeLength; i < maxLength; ++i) {
451 bb.put((byte) 0x0);
452 }
453 }
454 }
455
456 @Override
457 public IPacket deserialize(final byte[] data, final int offset,
458 final int length) {
459 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
460 if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
461 return this;
462 }
463
464 this.opCode = bb.get();
465 this.hardwareType = bb.get();
466 this.hardwareAddressLength = bb.get();
467 this.hops = bb.get();
468 this.transactionId = bb.getInt();
469 this.seconds = bb.getShort();
470 this.flags = bb.getShort();
471 this.clientIPAddress = bb.getInt();
472 this.yourIPAddress = bb.getInt();
473 this.serverIPAddress = bb.getInt();
474 this.gatewayIPAddress = bb.getInt();
475 final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
476 this.clientHardwareAddress = new byte[hardwareAddressLength];
477
478 bb.get(this.clientHardwareAddress);
479 for (int i = hardwareAddressLength; i < 16; ++i) {
480 bb.get();
481 }
482 this.serverName = this.readString(bb, 64);
483 this.bootFileName = this.readString(bb, 128);
484 // read the magic cookie
485 // magic cookie
486 bb.get();
487 bb.get();
488 bb.get();
489 bb.get();
490 // read options
491 while (bb.hasRemaining()) {
492 final DHCPOption option = new DHCPOption();
493 int code = 0xff & bb.get(); // convert signed byte to int in range
494 // [0,255]
495 option.setCode((byte) code);
496 if (code == 0) {
497 // skip these
498 continue;
499 } else if (code != 255) {
500 if (bb.hasRemaining()) {
501 final int l = 0xff & bb.get(); // convert signed byte to
502 // int in range [0,255]
503 option.setLength((byte) l);
504 if (bb.remaining() >= l) {
505 final byte[] optionData = new byte[l];
506 bb.get(optionData);
507 option.setData(optionData);
508 } else {
509 // Skip the invalid option and set the END option
510 code = 0xff;
511 option.setCode((byte) code);
512 option.setLength((byte) 0);
513 }
514 } else {
515 // Skip the invalid option and set the END option
516 code = 0xff;
517 option.setCode((byte) code);
518 option.setLength((byte) 0);
519 }
520 }
521 this.options.add(option);
522 if (code == 255) {
523 // remaining bytes are supposed to be 0, but ignore them just in
524 // case
525 break;
526 }
527 }
528
529 return this;
530 }
531
532 protected String readString(final ByteBuffer bb, final int maxLength) {
533 final byte[] bytes = new byte[maxLength];
534 bb.get(bytes);
535 String result = null;
536 try {
537 result = new String(bytes, "ascii").trim();
538 } catch (final UnsupportedEncodingException e) {
539 throw new RuntimeException("Failure decoding string", e);
540 }
541 return result;
542 }
543}