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