blob: aea1ab1561fc6ab6866da2546e4c2818bd2e23fc [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 * ------------------------------------------
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080054 */
55 // Header + magic without options
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070056 public static final int MIN_HEADER_LENGTH = 240;
57 public static final byte OPCODE_REQUEST = 0x1;
58 public static final byte OPCODE_REPLY = 0x2;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080059
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070060 public static final byte HWTYPE_ETHERNET = 0x1;
Ray Milkey269ffb92014-04-03 14:43:30 -070061
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062 public enum DHCPOptionCode {
Ray Milkey269ffb92014-04-03 14:43:30 -070063 OptionCode_SubnetMask((byte) 1),
64 OptionCode_RequestedIP((byte) 50),
65 OptionCode_LeaseTime((byte) 51),
66 OptionCode_MessageType((byte) 53),
67 OptionCode_DHCPServerIp((byte) 54),
68 OptionCode_RequestedParameters((byte) 55),
69 OptionCode_RenewalTime((byte) 58),
70 OPtionCode_RebindingTime((byte) 59),
71 OptionCode_ClientID((byte) 61),
72 OptionCode_END((byte) 255);
73
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080074 protected byte value;
Ray Milkey269ffb92014-04-03 14:43:30 -070075
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080076 private DHCPOptionCode(byte value) {
77 this.value = value;
78 }
Ray Milkey269ffb92014-04-03 14:43:30 -070079
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080080 public byte getValue() {
81 return value;
82 }
83 }
Ray Milkey269ffb92014-04-03 14:43:30 -070084
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080085 protected byte opCode;
86 protected byte hardwareType;
87 protected byte hardwareAddressLength;
88 protected byte hops;
89 protected int transactionId;
90 protected short seconds;
91 protected short flags;
92 protected int clientIPAddress;
93 protected int yourIPAddress;
94 protected int serverIPAddress;
95 protected int gatewayIPAddress;
96 protected byte[] clientHardwareAddress;
97 protected String serverName;
98 protected String bootFileName;
99 protected List<DHCPOption> options = new ArrayList<DHCPOption>();
100
101 /**
102 * @return the opCode
103 */
104 public byte getOpCode() {
105 return opCode;
106 }
107
108 /**
109 * @param opCode the opCode to set
110 */
111 public DHCP setOpCode(byte opCode) {
112 this.opCode = opCode;
113 return this;
114 }
115
116 /**
117 * @return the hardwareType
118 */
119 public byte getHardwareType() {
120 return hardwareType;
121 }
122
123 /**
124 * @param hardwareType the hardwareType to set
125 */
126 public DHCP setHardwareType(byte hardwareType) {
127 this.hardwareType = hardwareType;
128 return this;
129 }
130
131 /**
132 * @return the hardwareAddressLength
133 */
134 public byte getHardwareAddressLength() {
135 return hardwareAddressLength;
136 }
137
138 /**
139 * @param hardwareAddressLength the hardwareAddressLength to set
140 */
141 public DHCP setHardwareAddressLength(byte hardwareAddressLength) {
142 this.hardwareAddressLength = hardwareAddressLength;
143 return this;
144 }
145
146 /**
147 * @return the hops
148 */
149 public byte getHops() {
150 return hops;
151 }
152
153 /**
154 * @param hops the hops to set
155 */
156 public DHCP setHops(byte hops) {
157 this.hops = hops;
158 return this;
159 }
160
161 /**
162 * @return the transactionId
163 */
164 public int getTransactionId() {
165 return transactionId;
166 }
167
168 /**
169 * @param transactionId the transactionId to set
170 */
171 public DHCP setTransactionId(int transactionId) {
172 this.transactionId = transactionId;
173 return this;
174 }
175
176 /**
177 * @return the seconds
178 */
179 public short getSeconds() {
180 return seconds;
181 }
182
183 /**
184 * @param seconds the seconds to set
185 */
186 public DHCP setSeconds(short seconds) {
187 this.seconds = seconds;
188 return this;
189 }
190
191 /**
192 * @return the flags
193 */
194 public short getFlags() {
195 return flags;
196 }
197
198 /**
199 * @param flags the flags to set
200 */
201 public DHCP setFlags(short flags) {
202 this.flags = flags;
203 return this;
204 }
205
206 /**
207 * @return the clientIPAddress
208 */
209 public int getClientIPAddress() {
210 return clientIPAddress;
211 }
212
213 /**
214 * @param clientIPAddress the clientIPAddress to set
215 */
216 public DHCP setClientIPAddress(int clientIPAddress) {
217 this.clientIPAddress = clientIPAddress;
218 return this;
219 }
220
221 /**
222 * @return the yourIPAddress
223 */
224 public int getYourIPAddress() {
225 return yourIPAddress;
226 }
227
228 /**
229 * @param yourIPAddress the yourIPAddress to set
230 */
231 public DHCP setYourIPAddress(int yourIPAddress) {
232 this.yourIPAddress = yourIPAddress;
233 return this;
234 }
235
236 /**
237 * @return the serverIPAddress
238 */
239 public int getServerIPAddress() {
240 return serverIPAddress;
241 }
242
243 /**
244 * @param serverIPAddress the serverIPAddress to set
245 */
246 public DHCP setServerIPAddress(int serverIPAddress) {
247 this.serverIPAddress = serverIPAddress;
248 return this;
249 }
250
251 /**
252 * @return the gatewayIPAddress
253 */
254 public int getGatewayIPAddress() {
255 return gatewayIPAddress;
256 }
257
258 /**
259 * @param gatewayIPAddress the gatewayIPAddress to set
260 */
261 public DHCP setGatewayIPAddress(int gatewayIPAddress) {
262 this.gatewayIPAddress = gatewayIPAddress;
263 return this;
264 }
265
266 /**
267 * @return the clientHardwareAddress
268 */
269 public byte[] getClientHardwareAddress() {
270 return clientHardwareAddress;
271 }
272
273 /**
274 * @param clientHardwareAddress the clientHardwareAddress to set
275 */
276 public DHCP setClientHardwareAddress(byte[] clientHardwareAddress) {
277 this.clientHardwareAddress = clientHardwareAddress;
278 return this;
279 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700280
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800281 /**
282 * Gets a specific DHCP option parameter
Ray Milkey269ffb92014-04-03 14:43:30 -0700283 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800284 * @param opetionCode The option code to get
285 * @return The value of the option if it exists, null otherwise
286 */
287 public DHCPOption getOption(DHCPOptionCode optionCode) {
288 for (DHCPOption opt : options) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700289 if (opt.code == optionCode.value) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800290 return opt;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700291 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800292 }
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 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700325
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800326 /**
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) {
Pavlin Radoslavov33048e82014-04-09 14:40:59 -0700364 int code = option.getCode() & 0xff;
365 if (code == 0 || code == 255) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800366 optionsLength += 1;
367 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700368 optionsLength += 2 + (int) (0xff & option.getLength());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800369 }
370 }
371 int optionsPadLength = 0;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700372 if (optionsLength < 60) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800373 optionsPadLength = 60 - optionsLength;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700374 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800375
Ray Milkey269ffb92014-04-03 14:43:30 -0700376 byte[] data = new byte[240 + optionsLength + optionsPadLength];
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800377 ByteBuffer bb = ByteBuffer.wrap(data);
378 bb.put(this.opCode);
379 bb.put(this.hardwareType);
380 bb.put(this.hardwareAddressLength);
381 bb.put(this.hops);
382 bb.putInt(this.transactionId);
383 bb.putShort(this.seconds);
384 bb.putShort(this.flags);
385 bb.putInt(this.clientIPAddress);
386 bb.putInt(this.yourIPAddress);
387 bb.putInt(this.serverIPAddress);
388 bb.putInt(this.gatewayIPAddress);
389 bb.put(this.clientHardwareAddress);
390 if (this.clientHardwareAddress.length < 16) {
391 for (int i = 0; i < (16 - this.clientHardwareAddress.length); ++i) {
392 bb.put((byte) 0x0);
393 }
394 }
395 writeString(this.serverName, bb, 64);
396 writeString(this.bootFileName, bb, 128);
397 // magic cookie
398 bb.put((byte) 0x63);
399 bb.put((byte) 0x82);
400 bb.put((byte) 0x53);
401 bb.put((byte) 0x63);
402 for (DHCPOption option : this.options) {
403 int code = option.getCode() & 0xff;
404 bb.put((byte) code);
405 if ((code != 0) && (code != 255)) {
406 bb.put(option.getLength());
407 bb.put(option.getData());
408 }
409 }
410 // assume the rest is padded out with zeroes
411 return data;
412 }
413
414 protected void writeString(String string, ByteBuffer bb, int maxLength) {
415 if (string == null) {
416 for (int i = 0; i < maxLength; ++i) {
417 bb.put((byte) 0x0);
418 }
419 } else {
420 byte[] bytes = null;
421 try {
Ray Milkey269ffb92014-04-03 14:43:30 -0700422 bytes = string.getBytes("ascii");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800423 } catch (UnsupportedEncodingException e) {
424 throw new RuntimeException("Failure encoding server name", e);
425 }
426 int writeLength = bytes.length;
427 if (writeLength > maxLength) {
428 writeLength = maxLength;
429 }
430 bb.put(bytes, 0, writeLength);
431 for (int i = writeLength; i < maxLength; ++i) {
432 bb.put((byte) 0x0);
433 }
434 }
435 }
436
437 @Override
438 public IPacket deserialize(byte[] data, int offset, int length) {
439 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
440 if (bb.remaining() < MIN_HEADER_LENGTH) {
441 return this;
442 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700443
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800444 this.opCode = bb.get();
445 this.hardwareType = bb.get();
446 this.hardwareAddressLength = bb.get();
447 this.hops = bb.get();
448 this.transactionId = bb.getInt();
449 this.seconds = bb.getShort();
450 this.flags = bb.getShort();
451 this.clientIPAddress = bb.getInt();
452 this.yourIPAddress = bb.getInt();
453 this.serverIPAddress = bb.getInt();
454 this.gatewayIPAddress = bb.getInt();
455 int hardwareAddressLength = 0xff & this.hardwareAddressLength;
456 this.clientHardwareAddress = new byte[hardwareAddressLength];
457
458 bb.get(this.clientHardwareAddress);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700459 for (int i = hardwareAddressLength; i < 16; ++i) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800460 bb.get();
Ray Milkeyb29e6262014-04-09 16:02:14 -0700461 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800462 this.serverName = readString(bb, 64);
463 this.bootFileName = readString(bb, 128);
464 // read the magic cookie
465 // magic cookie
466 bb.get();
467 bb.get();
468 bb.get();
469 bb.get();
470 // read options
471 while (bb.hasRemaining()) {
472 DHCPOption option = new DHCPOption();
473 int code = 0xff & bb.get(); // convert signed byte to int in range [0,255]
474 option.setCode((byte) code);
475 if (code == 0) {
476 // skip these
477 continue;
478 } else if (code != 255) {
479 if (bb.hasRemaining()) {
480 int l = 0xff & bb.get(); // convert signed byte to int in range [0,255]
481 option.setLength((byte) l);
482 if (bb.remaining() >= l) {
483 byte[] optionData = new byte[l];
484 bb.get(optionData);
485 option.setData(optionData);
486 } else {
487 // Skip the invalid option and set the END option
488 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800490 option.setLength((byte) 0);
491 }
492 } else {
493 // Skip the invalid option and set the END option
494 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700495 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800496 option.setLength((byte) 0);
497 }
498 }
499 this.options.add(option);
500 if (code == 255) {
501 // remaining bytes are supposed to be 0, but ignore them just in case
502 break;
503 }
504 }
505
506 return this;
507 }
508
509 protected String readString(ByteBuffer bb, int maxLength) {
510 byte[] bytes = new byte[maxLength];
511 bb.get(bytes);
512 String result = null;
513 try {
514 result = new String(bytes, "ascii").trim();
515 } catch (UnsupportedEncodingException e) {
516 throw new RuntimeException("Failure decoding string", e);
517 }
518 return result;
519 }
520}