blob: 82ba8507cf1da4ffd58cff6e1e9da16da7853ce7 [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
Pavlin Radoslavov1d595c02014-04-16 14:11:54 -070026import org.apache.commons.lang.ArrayUtils;
27
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080028/**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080029 * @author David Erickson (daviderickson@cs.stanford.edu)
30 */
31public class DHCP extends BasePacket {
32 /**
33 * ------------------------------------------
34 * |op (1) | htype(1) | hlen(1) | hops(1) |
35 * ------------------------------------------
36 * | xid (4) |
37 * ------------------------------------------
38 * | secs (2) | flags (2) |
39 * ------------------------------------------
40 * | ciaddr (4) |
41 * ------------------------------------------
42 * | yiaddr (4) |
43 * ------------------------------------------
44 * | siaddr (4) |
45 * ------------------------------------------
46 * | giaddr (4) |
47 * ------------------------------------------
48 * | chaddr (16) |
49 * ------------------------------------------
50 * | sname (64) |
51 * ------------------------------------------
52 * | file (128) |
53 * ------------------------------------------
54 * | options (312) |
55 * ------------------------------------------
Ray Milkeyb41100a2014-04-10 10:42:15 -070056 * Basic packet.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080057 */
58 // Header + magic without options
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070059 public static final int MIN_HEADER_LENGTH = 240;
60 public static final byte OPCODE_REQUEST = 0x1;
61 public static final byte OPCODE_REPLY = 0x2;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070063 public static final byte HWTYPE_ETHERNET = 0x1;
Ray Milkey269ffb92014-04-03 14:43:30 -070064
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080065 public enum DHCPOptionCode {
Ray Milkey269ffb92014-04-03 14:43:30 -070066 OptionCode_SubnetMask((byte) 1),
67 OptionCode_RequestedIP((byte) 50),
68 OptionCode_LeaseTime((byte) 51),
69 OptionCode_MessageType((byte) 53),
70 OptionCode_DHCPServerIp((byte) 54),
71 OptionCode_RequestedParameters((byte) 55),
72 OptionCode_RenewalTime((byte) 58),
73 OPtionCode_RebindingTime((byte) 59),
74 OptionCode_ClientID((byte) 61),
75 OptionCode_END((byte) 255);
76
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080077 protected byte value;
Ray Milkey269ffb92014-04-03 14:43:30 -070078
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080079 private DHCPOptionCode(byte value) {
80 this.value = value;
81 }
Ray Milkey269ffb92014-04-03 14:43:30 -070082
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080083 public byte getValue() {
84 return value;
85 }
86 }
Ray Milkey269ffb92014-04-03 14:43:30 -070087
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080088 protected byte opCode;
89 protected byte hardwareType;
90 protected byte hardwareAddressLength;
91 protected byte hops;
92 protected int transactionId;
93 protected short seconds;
94 protected short flags;
95 protected int clientIPAddress;
96 protected int yourIPAddress;
97 protected int serverIPAddress;
98 protected int gatewayIPAddress;
99 protected byte[] clientHardwareAddress;
100 protected String serverName;
101 protected String bootFileName;
102 protected List<DHCPOption> options = new ArrayList<DHCPOption>();
103
104 /**
105 * @return the opCode
106 */
107 public byte getOpCode() {
108 return opCode;
109 }
110
111 /**
112 * @param opCode the opCode to set
113 */
114 public DHCP setOpCode(byte opCode) {
115 this.opCode = opCode;
116 return this;
117 }
118
119 /**
120 * @return the hardwareType
121 */
122 public byte getHardwareType() {
123 return hardwareType;
124 }
125
126 /**
127 * @param hardwareType the hardwareType to set
128 */
129 public DHCP setHardwareType(byte hardwareType) {
130 this.hardwareType = hardwareType;
131 return this;
132 }
133
134 /**
135 * @return the hardwareAddressLength
136 */
137 public byte getHardwareAddressLength() {
138 return hardwareAddressLength;
139 }
140
141 /**
142 * @param hardwareAddressLength the hardwareAddressLength to set
143 */
144 public DHCP setHardwareAddressLength(byte hardwareAddressLength) {
145 this.hardwareAddressLength = hardwareAddressLength;
146 return this;
147 }
148
149 /**
150 * @return the hops
151 */
152 public byte getHops() {
153 return hops;
154 }
155
156 /**
157 * @param hops the hops to set
158 */
159 public DHCP setHops(byte hops) {
160 this.hops = hops;
161 return this;
162 }
163
164 /**
165 * @return the transactionId
166 */
167 public int getTransactionId() {
168 return transactionId;
169 }
170
171 /**
172 * @param transactionId the transactionId to set
173 */
174 public DHCP setTransactionId(int transactionId) {
175 this.transactionId = transactionId;
176 return this;
177 }
178
179 /**
180 * @return the seconds
181 */
182 public short getSeconds() {
183 return seconds;
184 }
185
186 /**
187 * @param seconds the seconds to set
188 */
189 public DHCP setSeconds(short seconds) {
190 this.seconds = seconds;
191 return this;
192 }
193
194 /**
195 * @return the flags
196 */
197 public short getFlags() {
198 return flags;
199 }
200
201 /**
202 * @param flags the flags to set
203 */
204 public DHCP setFlags(short flags) {
205 this.flags = flags;
206 return this;
207 }
208
209 /**
210 * @return the clientIPAddress
211 */
212 public int getClientIPAddress() {
213 return clientIPAddress;
214 }
215
216 /**
217 * @param clientIPAddress the clientIPAddress to set
218 */
219 public DHCP setClientIPAddress(int clientIPAddress) {
220 this.clientIPAddress = clientIPAddress;
221 return this;
222 }
223
224 /**
225 * @return the yourIPAddress
226 */
227 public int getYourIPAddress() {
228 return yourIPAddress;
229 }
230
231 /**
232 * @param yourIPAddress the yourIPAddress to set
233 */
234 public DHCP setYourIPAddress(int yourIPAddress) {
235 this.yourIPAddress = yourIPAddress;
236 return this;
237 }
238
239 /**
240 * @return the serverIPAddress
241 */
242 public int getServerIPAddress() {
243 return serverIPAddress;
244 }
245
246 /**
247 * @param serverIPAddress the serverIPAddress to set
248 */
249 public DHCP setServerIPAddress(int serverIPAddress) {
250 this.serverIPAddress = serverIPAddress;
251 return this;
252 }
253
254 /**
255 * @return the gatewayIPAddress
256 */
257 public int getGatewayIPAddress() {
258 return gatewayIPAddress;
259 }
260
261 /**
262 * @param gatewayIPAddress the gatewayIPAddress to set
263 */
264 public DHCP setGatewayIPAddress(int gatewayIPAddress) {
265 this.gatewayIPAddress = gatewayIPAddress;
266 return this;
267 }
268
269 /**
270 * @return the clientHardwareAddress
271 */
272 public byte[] getClientHardwareAddress() {
Pavlin Radoslavov1d595c02014-04-16 14:11:54 -0700273 return ArrayUtils.clone(this.clientHardwareAddress);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800274 }
275
276 /**
277 * @param clientHardwareAddress the clientHardwareAddress to set
278 */
279 public DHCP setClientHardwareAddress(byte[] clientHardwareAddress) {
Pavlin Radoslavov1d595c02014-04-16 14:11:54 -0700280 this.clientHardwareAddress = ArrayUtils.clone(clientHardwareAddress);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800281 return this;
282 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700283
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800284 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700285 * Gets a specific DHCP option parameter.
Ray Milkey269ffb92014-04-03 14:43:30 -0700286 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800287 * @param opetionCode The option code to get
288 * @return The value of the option if it exists, null otherwise
289 */
290 public DHCPOption getOption(DHCPOptionCode optionCode) {
291 for (DHCPOption opt : options) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700292 if (opt.code == optionCode.value) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800293 return opt;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700294 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800295 }
296 return null;
297 }
298
299 /**
300 * @return the options
301 */
302 public List<DHCPOption> getOptions() {
303 return options;
304 }
305
306 /**
307 * @param options the options to set
308 */
309 public DHCP setOptions(List<DHCPOption> options) {
310 this.options = options;
311 return this;
312 }
313
314 /**
315 * @return the packetType base on option 53
316 */
317 public DHCPPacketType getPacketType() {
318 ListIterator<DHCPOption> lit = options.listIterator();
319 while (lit.hasNext()) {
320 DHCPOption option = lit.next();
321 // only care option 53
322 if (option.getCode() == 53) {
323 return DHCPPacketType.getType(option.getData()[0]);
324 }
325 }
326 return null;
327 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700328
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800329 /**
330 * @return the serverName
331 */
332 public String getServerName() {
333 return serverName;
334 }
335
336 /**
337 * @param serverName the serverName to set
338 */
339 public DHCP setServerName(String serverName) {
340 this.serverName = serverName;
341 return this;
342 }
343
344 /**
345 * @return the bootFileName
346 */
347 public String getBootFileName() {
348 return bootFileName;
349 }
350
351 /**
352 * @param bootFileName the bootFileName to set
353 */
354 public DHCP setBootFileName(String bootFileName) {
355 this.bootFileName = bootFileName;
356 return this;
357 }
358
359 @Override
360 public byte[] serialize() {
361 // not guaranteed to retain length/exact format
362 resetChecksum();
363
364 // minimum size 240 including magic cookie, options generally padded to 300
365 int optionsLength = 0;
366 for (DHCPOption option : this.options) {
Pavlin Radoslavov33048e82014-04-09 14:40:59 -0700367 int code = option.getCode() & 0xff;
368 if (code == 0 || code == 255) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800369 optionsLength += 1;
370 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700371 optionsLength += 2 + (int) (0xff & option.getLength());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800372 }
373 }
374 int optionsPadLength = 0;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700375 if (optionsLength < 60) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800376 optionsPadLength = 60 - optionsLength;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700377 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800378
Ray Milkey269ffb92014-04-03 14:43:30 -0700379 byte[] data = new byte[240 + optionsLength + optionsPadLength];
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800380 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 writeString(this.serverName, bb, 64);
399 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 (DHCPOption option : this.options) {
406 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(String string, ByteBuffer bb, int maxLength) {
418 if (string == null) {
419 for (int i = 0; i < maxLength; ++i) {
420 bb.put((byte) 0x0);
421 }
422 } else {
423 byte[] bytes = null;
424 try {
Ray Milkey269ffb92014-04-03 14:43:30 -0700425 bytes = string.getBytes("ascii");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800426 } catch (UnsupportedEncodingException e) {
427 throw new RuntimeException("Failure encoding server name", e);
428 }
429 int writeLength = bytes.length;
430 if (writeLength > maxLength) {
431 writeLength = maxLength;
432 }
433 bb.put(bytes, 0, writeLength);
434 for (int i = writeLength; i < maxLength; ++i) {
435 bb.put((byte) 0x0);
436 }
437 }
438 }
439
440 @Override
441 public IPacket deserialize(byte[] data, int offset, int length) {
442 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
443 if (bb.remaining() < MIN_HEADER_LENGTH) {
444 return this;
445 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700446
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800447 this.opCode = bb.get();
448 this.hardwareType = bb.get();
449 this.hardwareAddressLength = bb.get();
450 this.hops = bb.get();
451 this.transactionId = bb.getInt();
452 this.seconds = bb.getShort();
453 this.flags = bb.getShort();
454 this.clientIPAddress = bb.getInt();
455 this.yourIPAddress = bb.getInt();
456 this.serverIPAddress = bb.getInt();
457 this.gatewayIPAddress = bb.getInt();
458 int hardwareAddressLength = 0xff & this.hardwareAddressLength;
459 this.clientHardwareAddress = new byte[hardwareAddressLength];
460
461 bb.get(this.clientHardwareAddress);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700462 for (int i = hardwareAddressLength; i < 16; ++i) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800463 bb.get();
Ray Milkeyb29e6262014-04-09 16:02:14 -0700464 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800465 this.serverName = readString(bb, 64);
466 this.bootFileName = readString(bb, 128);
467 // read the magic cookie
468 // magic cookie
469 bb.get();
470 bb.get();
471 bb.get();
472 bb.get();
473 // read options
474 while (bb.hasRemaining()) {
475 DHCPOption option = new DHCPOption();
476 int code = 0xff & bb.get(); // convert signed byte to int in range [0,255]
477 option.setCode((byte) code);
478 if (code == 0) {
479 // skip these
480 continue;
481 } else if (code != 255) {
482 if (bb.hasRemaining()) {
483 int l = 0xff & bb.get(); // convert signed byte to int in range [0,255]
484 option.setLength((byte) l);
485 if (bb.remaining() >= l) {
486 byte[] optionData = new byte[l];
487 bb.get(optionData);
488 option.setData(optionData);
489 } else {
490 // Skip the invalid option and set the END option
491 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700492 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800493 option.setLength((byte) 0);
494 }
495 } else {
496 // Skip the invalid option and set the END option
497 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700498 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800499 option.setLength((byte) 0);
500 }
501 }
502 this.options.add(option);
503 if (code == 255) {
504 // remaining bytes are supposed to be 0, but ignore them just in case
505 break;
506 }
507 }
508
509 return this;
510 }
511
512 protected String readString(ByteBuffer bb, int maxLength) {
513 byte[] bytes = new byte[maxLength];
514 bb.get(bytes);
515 String result = null;
516 try {
517 result = new String(bytes, "ascii").trim();
518 } catch (UnsupportedEncodingException e) {
519 throw new RuntimeException("Failure decoding string", e);
520 }
521 return result;
522 }
523}