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