blob: e2a109ee00c6b1d31986d11baab10a0f879d048d [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() {
271 return clientHardwareAddress;
272 }
273
274 /**
275 * @param clientHardwareAddress the clientHardwareAddress to set
276 */
277 public DHCP setClientHardwareAddress(byte[] clientHardwareAddress) {
278 this.clientHardwareAddress = clientHardwareAddress;
279 return this;
280 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700281
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800282 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700283 * Gets a specific DHCP option parameter.
Ray Milkey269ffb92014-04-03 14:43:30 -0700284 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800285 * @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) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700290 if (opt.code == optionCode.value) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800291 return opt;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700292 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800293 }
294 return null;
295 }
296
297 /**
298 * @return the options
299 */
300 public List<DHCPOption> getOptions() {
301 return options;
302 }
303
304 /**
305 * @param options the options to set
306 */
307 public DHCP setOptions(List<DHCPOption> options) {
308 this.options = options;
309 return this;
310 }
311
312 /**
313 * @return the packetType base on option 53
314 */
315 public DHCPPacketType getPacketType() {
316 ListIterator<DHCPOption> lit = options.listIterator();
317 while (lit.hasNext()) {
318 DHCPOption option = lit.next();
319 // only care option 53
320 if (option.getCode() == 53) {
321 return DHCPPacketType.getType(option.getData()[0]);
322 }
323 }
324 return null;
325 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700326
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800327 /**
328 * @return the serverName
329 */
330 public String getServerName() {
331 return serverName;
332 }
333
334 /**
335 * @param serverName the serverName to set
336 */
337 public DHCP setServerName(String serverName) {
338 this.serverName = serverName;
339 return this;
340 }
341
342 /**
343 * @return the bootFileName
344 */
345 public String getBootFileName() {
346 return bootFileName;
347 }
348
349 /**
350 * @param bootFileName the bootFileName to set
351 */
352 public DHCP setBootFileName(String bootFileName) {
353 this.bootFileName = bootFileName;
354 return this;
355 }
356
357 @Override
358 public byte[] serialize() {
359 // not guaranteed to retain length/exact format
360 resetChecksum();
361
362 // minimum size 240 including magic cookie, options generally padded to 300
363 int optionsLength = 0;
364 for (DHCPOption option : this.options) {
Pavlin Radoslavov33048e82014-04-09 14:40:59 -0700365 int code = option.getCode() & 0xff;
366 if (code == 0 || code == 255) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800367 optionsLength += 1;
368 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700369 optionsLength += 2 + (int) (0xff & option.getLength());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800370 }
371 }
372 int optionsPadLength = 0;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700373 if (optionsLength < 60) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800374 optionsPadLength = 60 - optionsLength;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700375 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800376
Ray Milkey269ffb92014-04-03 14:43:30 -0700377 byte[] data = new byte[240 + optionsLength + optionsPadLength];
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800378 ByteBuffer bb = ByteBuffer.wrap(data);
379 bb.put(this.opCode);
380 bb.put(this.hardwareType);
381 bb.put(this.hardwareAddressLength);
382 bb.put(this.hops);
383 bb.putInt(this.transactionId);
384 bb.putShort(this.seconds);
385 bb.putShort(this.flags);
386 bb.putInt(this.clientIPAddress);
387 bb.putInt(this.yourIPAddress);
388 bb.putInt(this.serverIPAddress);
389 bb.putInt(this.gatewayIPAddress);
390 bb.put(this.clientHardwareAddress);
391 if (this.clientHardwareAddress.length < 16) {
392 for (int i = 0; i < (16 - this.clientHardwareAddress.length); ++i) {
393 bb.put((byte) 0x0);
394 }
395 }
396 writeString(this.serverName, bb, 64);
397 writeString(this.bootFileName, bb, 128);
398 // magic cookie
399 bb.put((byte) 0x63);
400 bb.put((byte) 0x82);
401 bb.put((byte) 0x53);
402 bb.put((byte) 0x63);
403 for (DHCPOption option : this.options) {
404 int code = option.getCode() & 0xff;
405 bb.put((byte) code);
406 if ((code != 0) && (code != 255)) {
407 bb.put(option.getLength());
408 bb.put(option.getData());
409 }
410 }
411 // assume the rest is padded out with zeroes
412 return data;
413 }
414
415 protected void writeString(String string, ByteBuffer bb, int maxLength) {
416 if (string == null) {
417 for (int i = 0; i < maxLength; ++i) {
418 bb.put((byte) 0x0);
419 }
420 } else {
421 byte[] bytes = null;
422 try {
Ray Milkey269ffb92014-04-03 14:43:30 -0700423 bytes = string.getBytes("ascii");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800424 } catch (UnsupportedEncodingException e) {
425 throw new RuntimeException("Failure encoding server name", e);
426 }
427 int writeLength = bytes.length;
428 if (writeLength > maxLength) {
429 writeLength = maxLength;
430 }
431 bb.put(bytes, 0, writeLength);
432 for (int i = writeLength; i < maxLength; ++i) {
433 bb.put((byte) 0x0);
434 }
435 }
436 }
437
438 @Override
439 public IPacket deserialize(byte[] data, int offset, int length) {
440 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
441 if (bb.remaining() < MIN_HEADER_LENGTH) {
442 return this;
443 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700444
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800445 this.opCode = bb.get();
446 this.hardwareType = bb.get();
447 this.hardwareAddressLength = bb.get();
448 this.hops = bb.get();
449 this.transactionId = bb.getInt();
450 this.seconds = bb.getShort();
451 this.flags = bb.getShort();
452 this.clientIPAddress = bb.getInt();
453 this.yourIPAddress = bb.getInt();
454 this.serverIPAddress = bb.getInt();
455 this.gatewayIPAddress = bb.getInt();
456 int hardwareAddressLength = 0xff & this.hardwareAddressLength;
457 this.clientHardwareAddress = new byte[hardwareAddressLength];
458
459 bb.get(this.clientHardwareAddress);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700460 for (int i = hardwareAddressLength; i < 16; ++i) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800461 bb.get();
Ray Milkeyb29e6262014-04-09 16:02:14 -0700462 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800463 this.serverName = readString(bb, 64);
464 this.bootFileName = readString(bb, 128);
465 // read the magic cookie
466 // magic cookie
467 bb.get();
468 bb.get();
469 bb.get();
470 bb.get();
471 // read options
472 while (bb.hasRemaining()) {
473 DHCPOption option = new DHCPOption();
474 int code = 0xff & bb.get(); // convert signed byte to int in range [0,255]
475 option.setCode((byte) code);
476 if (code == 0) {
477 // skip these
478 continue;
479 } else if (code != 255) {
480 if (bb.hasRemaining()) {
481 int l = 0xff & bb.get(); // convert signed byte to int in range [0,255]
482 option.setLength((byte) l);
483 if (bb.remaining() >= l) {
484 byte[] optionData = new byte[l];
485 bb.get(optionData);
486 option.setData(optionData);
487 } else {
488 // Skip the invalid option and set the END option
489 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700490 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800491 option.setLength((byte) 0);
492 }
493 } else {
494 // Skip the invalid option and set the END option
495 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700496 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800497 option.setLength((byte) 0);
498 }
499 }
500 this.options.add(option);
501 if (code == 255) {
502 // remaining bytes are supposed to be 0, but ignore them just in case
503 break;
504 }
505 }
506
507 return this;
508 }
509
510 protected String readString(ByteBuffer bb, int maxLength) {
511 byte[] bytes = new byte[maxLength];
512 bb.get(bytes);
513 String result = null;
514 try {
515 result = new String(bytes, "ascii").trim();
516 } catch (UnsupportedEncodingException e) {
517 throw new RuntimeException("Failure decoding string", e);
518 }
519 return result;
520 }
521}