blob: 22d6c7ad0cc30a9cd558ea6ba6f27f9bddf0e11f [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
Ray Milkey269ffb92014-04-03 14:43:30 -07002 * 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 **/
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080017
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070018package net.onrc.onos.core.packet;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080019
20import java.io.UnsupportedEncodingException;
21import java.nio.ByteBuffer;
22import java.util.ArrayList;
23import java.util.List;
24import java.util.ListIterator;
25
26/**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080027 * @author David Erickson (daviderickson@cs.stanford.edu)
28 */
29public class DHCP extends BasePacket {
30 /**
31 * ------------------------------------------
32 * |op (1) | htype(1) | hlen(1) | hops(1) |
33 * ------------------------------------------
34 * | xid (4) |
35 * ------------------------------------------
36 * | secs (2) | flags (2) |
37 * ------------------------------------------
38 * | ciaddr (4) |
39 * ------------------------------------------
40 * | yiaddr (4) |
41 * ------------------------------------------
42 * | siaddr (4) |
43 * ------------------------------------------
44 * | giaddr (4) |
45 * ------------------------------------------
46 * | chaddr (16) |
47 * ------------------------------------------
48 * | sname (64) |
49 * ------------------------------------------
50 * | file (128) |
51 * ------------------------------------------
52 * | options (312) |
53 * ------------------------------------------
Ray Milkeyb41100a2014-04-10 10:42:15 -070054 * Basic packet.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080055 */
56 // Header + magic without options
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070057 public static final int MIN_HEADER_LENGTH = 240;
58 public static final byte OPCODE_REQUEST = 0x1;
59 public static final byte OPCODE_REPLY = 0x2;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080060
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070061 public static final byte HWTYPE_ETHERNET = 0x1;
Ray Milkey269ffb92014-04-03 14:43:30 -070062
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080063 public enum DHCPOptionCode {
Ray Milkey269ffb92014-04-03 14:43:30 -070064 OptionCode_SubnetMask((byte) 1),
65 OptionCode_RequestedIP((byte) 50),
66 OptionCode_LeaseTime((byte) 51),
67 OptionCode_MessageType((byte) 53),
68 OptionCode_DHCPServerIp((byte) 54),
69 OptionCode_RequestedParameters((byte) 55),
70 OptionCode_RenewalTime((byte) 58),
71 OPtionCode_RebindingTime((byte) 59),
72 OptionCode_ClientID((byte) 61),
73 OptionCode_END((byte) 255);
74
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080075 protected byte value;
Ray Milkey269ffb92014-04-03 14:43:30 -070076
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080077 private DHCPOptionCode(byte value) {
78 this.value = value;
79 }
Ray Milkey269ffb92014-04-03 14:43:30 -070080
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080081 public byte getValue() {
82 return value;
83 }
84 }
Ray Milkey269ffb92014-04-03 14:43:30 -070085
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080086 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 opCode;
107 }
108
109 /**
110 * @param opCode the opCode to set
111 */
112 public DHCP setOpCode(byte opCode) {
113 this.opCode = opCode;
114 return this;
115 }
116
117 /**
118 * @return the hardwareType
119 */
120 public byte getHardwareType() {
121 return hardwareType;
122 }
123
124 /**
125 * @param hardwareType the hardwareType to set
126 */
127 public DHCP setHardwareType(byte hardwareType) {
128 this.hardwareType = hardwareType;
129 return this;
130 }
131
132 /**
133 * @return the hardwareAddressLength
134 */
135 public byte getHardwareAddressLength() {
136 return hardwareAddressLength;
137 }
138
139 /**
140 * @param hardwareAddressLength the hardwareAddressLength to set
141 */
142 public DHCP setHardwareAddressLength(byte hardwareAddressLength) {
143 this.hardwareAddressLength = hardwareAddressLength;
144 return this;
145 }
146
147 /**
148 * @return the hops
149 */
150 public byte getHops() {
151 return hops;
152 }
153
154 /**
155 * @param hops the hops to set
156 */
157 public DHCP setHops(byte hops) {
158 this.hops = hops;
159 return this;
160 }
161
162 /**
163 * @return the transactionId
164 */
165 public int getTransactionId() {
166 return transactionId;
167 }
168
169 /**
170 * @param transactionId the transactionId to set
171 */
172 public DHCP setTransactionId(int transactionId) {
173 this.transactionId = transactionId;
174 return this;
175 }
176
177 /**
178 * @return the seconds
179 */
180 public short getSeconds() {
181 return seconds;
182 }
183
184 /**
185 * @param seconds the seconds to set
186 */
187 public DHCP setSeconds(short seconds) {
188 this.seconds = seconds;
189 return this;
190 }
191
192 /**
193 * @return the flags
194 */
195 public short getFlags() {
196 return flags;
197 }
198
199 /**
200 * @param flags the flags to set
201 */
202 public DHCP setFlags(short flags) {
203 this.flags = flags;
204 return this;
205 }
206
207 /**
208 * @return the clientIPAddress
209 */
210 public int getClientIPAddress() {
211 return clientIPAddress;
212 }
213
214 /**
215 * @param clientIPAddress the clientIPAddress to set
216 */
217 public DHCP setClientIPAddress(int clientIPAddress) {
218 this.clientIPAddress = clientIPAddress;
219 return this;
220 }
221
222 /**
223 * @return the yourIPAddress
224 */
225 public int getYourIPAddress() {
226 return yourIPAddress;
227 }
228
229 /**
230 * @param yourIPAddress the yourIPAddress to set
231 */
232 public DHCP setYourIPAddress(int yourIPAddress) {
233 this.yourIPAddress = yourIPAddress;
234 return this;
235 }
236
237 /**
238 * @return the serverIPAddress
239 */
240 public int getServerIPAddress() {
241 return serverIPAddress;
242 }
243
244 /**
245 * @param serverIPAddress the serverIPAddress to set
246 */
247 public DHCP setServerIPAddress(int serverIPAddress) {
248 this.serverIPAddress = serverIPAddress;
249 return this;
250 }
251
252 /**
253 * @return the gatewayIPAddress
254 */
255 public int getGatewayIPAddress() {
256 return gatewayIPAddress;
257 }
258
259 /**
260 * @param gatewayIPAddress the gatewayIPAddress to set
261 */
262 public DHCP setGatewayIPAddress(int gatewayIPAddress) {
263 this.gatewayIPAddress = gatewayIPAddress;
264 return this;
265 }
266
267 /**
268 * @return the clientHardwareAddress
269 */
270 public byte[] getClientHardwareAddress() {
Pavlin Radoslavov0a9d5c32014-04-15 17:45:23 -0700271 if (this.clientHardwareAddress == null) {
272 return null;
273 }
274 return this.clientHardwareAddress.clone();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800275 }
276
277 /**
278 * @param clientHardwareAddress the clientHardwareAddress to set
279 */
280 public DHCP setClientHardwareAddress(byte[] clientHardwareAddress) {
Pavlin Radoslavov0a9d5c32014-04-15 17:45:23 -0700281 if (clientHardwareAddress == null) {
282 this.clientHardwareAddress = null;
283 } else {
284 this.clientHardwareAddress = clientHardwareAddress.clone();
285 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800286 return this;
287 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700288
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800289 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700290 * Gets a specific DHCP option parameter.
Ray Milkey269ffb92014-04-03 14:43:30 -0700291 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800292 * @param opetionCode The option code to get
293 * @return The value of the option if it exists, null otherwise
294 */
295 public DHCPOption getOption(DHCPOptionCode optionCode) {
296 for (DHCPOption opt : options) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700297 if (opt.code == optionCode.value) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800298 return opt;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700299 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800300 }
301 return null;
302 }
303
304 /**
305 * @return the options
306 */
307 public List<DHCPOption> getOptions() {
308 return options;
309 }
310
311 /**
312 * @param options the options to set
313 */
314 public DHCP setOptions(List<DHCPOption> options) {
315 this.options = options;
316 return this;
317 }
318
319 /**
320 * @return the packetType base on option 53
321 */
322 public DHCPPacketType getPacketType() {
323 ListIterator<DHCPOption> lit = options.listIterator();
324 while (lit.hasNext()) {
325 DHCPOption option = lit.next();
326 // only care option 53
327 if (option.getCode() == 53) {
328 return DHCPPacketType.getType(option.getData()[0]);
329 }
330 }
331 return null;
332 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700333
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800334 /**
335 * @return the serverName
336 */
337 public String getServerName() {
338 return serverName;
339 }
340
341 /**
342 * @param serverName the serverName to set
343 */
344 public DHCP setServerName(String serverName) {
345 this.serverName = serverName;
346 return this;
347 }
348
349 /**
350 * @return the bootFileName
351 */
352 public String getBootFileName() {
353 return bootFileName;
354 }
355
356 /**
357 * @param bootFileName the bootFileName to set
358 */
359 public DHCP setBootFileName(String bootFileName) {
360 this.bootFileName = bootFileName;
361 return this;
362 }
363
364 @Override
365 public byte[] serialize() {
366 // not guaranteed to retain length/exact format
367 resetChecksum();
368
369 // minimum size 240 including magic cookie, options generally padded to 300
370 int optionsLength = 0;
371 for (DHCPOption option : this.options) {
Pavlin Radoslavov33048e82014-04-09 14:40:59 -0700372 int code = option.getCode() & 0xff;
373 if (code == 0 || code == 255) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800374 optionsLength += 1;
375 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700376 optionsLength += 2 + (int) (0xff & option.getLength());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800377 }
378 }
379 int optionsPadLength = 0;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700380 if (optionsLength < 60) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800381 optionsPadLength = 60 - optionsLength;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700382 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800383
Ray Milkey269ffb92014-04-03 14:43:30 -0700384 byte[] data = new byte[240 + optionsLength + optionsPadLength];
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800385 ByteBuffer bb = ByteBuffer.wrap(data);
386 bb.put(this.opCode);
387 bb.put(this.hardwareType);
388 bb.put(this.hardwareAddressLength);
389 bb.put(this.hops);
390 bb.putInt(this.transactionId);
391 bb.putShort(this.seconds);
392 bb.putShort(this.flags);
393 bb.putInt(this.clientIPAddress);
394 bb.putInt(this.yourIPAddress);
395 bb.putInt(this.serverIPAddress);
396 bb.putInt(this.gatewayIPAddress);
397 bb.put(this.clientHardwareAddress);
398 if (this.clientHardwareAddress.length < 16) {
399 for (int i = 0; i < (16 - this.clientHardwareAddress.length); ++i) {
400 bb.put((byte) 0x0);
401 }
402 }
403 writeString(this.serverName, bb, 64);
404 writeString(this.bootFileName, bb, 128);
405 // magic cookie
406 bb.put((byte) 0x63);
407 bb.put((byte) 0x82);
408 bb.put((byte) 0x53);
409 bb.put((byte) 0x63);
410 for (DHCPOption option : this.options) {
411 int code = option.getCode() & 0xff;
412 bb.put((byte) code);
413 if ((code != 0) && (code != 255)) {
414 bb.put(option.getLength());
415 bb.put(option.getData());
416 }
417 }
418 // assume the rest is padded out with zeroes
419 return data;
420 }
421
422 protected void writeString(String string, ByteBuffer bb, int maxLength) {
423 if (string == null) {
424 for (int i = 0; i < maxLength; ++i) {
425 bb.put((byte) 0x0);
426 }
427 } else {
428 byte[] bytes = null;
429 try {
Ray Milkey269ffb92014-04-03 14:43:30 -0700430 bytes = string.getBytes("ascii");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800431 } catch (UnsupportedEncodingException e) {
432 throw new RuntimeException("Failure encoding server name", e);
433 }
434 int writeLength = bytes.length;
435 if (writeLength > maxLength) {
436 writeLength = maxLength;
437 }
438 bb.put(bytes, 0, writeLength);
439 for (int i = writeLength; i < maxLength; ++i) {
440 bb.put((byte) 0x0);
441 }
442 }
443 }
444
445 @Override
446 public IPacket deserialize(byte[] data, int offset, int length) {
447 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
448 if (bb.remaining() < MIN_HEADER_LENGTH) {
449 return this;
450 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700451
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800452 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 int hardwareAddressLength = 0xff & this.hardwareAddressLength;
464 this.clientHardwareAddress = new byte[hardwareAddressLength];
465
466 bb.get(this.clientHardwareAddress);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700467 for (int i = hardwareAddressLength; i < 16; ++i) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800468 bb.get();
Ray Milkeyb29e6262014-04-09 16:02:14 -0700469 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800470 this.serverName = readString(bb, 64);
471 this.bootFileName = 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 DHCPOption option = new DHCPOption();
481 int code = 0xff & bb.get(); // convert signed byte to int in range [0,255]
482 option.setCode((byte) code);
483 if (code == 0) {
484 // skip these
485 continue;
486 } else if (code != 255) {
487 if (bb.hasRemaining()) {
488 int l = 0xff & bb.get(); // convert signed byte to int in range [0,255]
489 option.setLength((byte) l);
490 if (bb.remaining() >= l) {
491 byte[] optionData = new byte[l];
492 bb.get(optionData);
493 option.setData(optionData);
494 } else {
495 // Skip the invalid option and set the END option
496 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700497 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800498 option.setLength((byte) 0);
499 }
500 } else {
501 // Skip the invalid option and set the END option
502 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700503 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800504 option.setLength((byte) 0);
505 }
506 }
507 this.options.add(option);
508 if (code == 255) {
509 // remaining bytes are supposed to be 0, but ignore them just in case
510 break;
511 }
512 }
513
514 return this;
515 }
516
517 protected String readString(ByteBuffer bb, int maxLength) {
518 byte[] bytes = new byte[maxLength];
519 bb.get(bytes);
520 String result = null;
521 try {
522 result = new String(bytes, "ascii").trim();
523 } catch (UnsupportedEncodingException e) {
524 throw new RuntimeException("Failure decoding string", e);
525 }
526 return result;
527 }
528}