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