blob: 36dc0a8a7c0f78507bbac002112e5bce7e5537ac [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
Yuta HIGUCHI498e1532014-08-20 21:30:28 -070026import org.apache.commons.lang3.ArrayUtils;
Pavlin Radoslavov1d595c02014-04-16 14:11:54 -070027
Yuta HIGUCHIaa132f52014-06-26 10:18:39 -070028// CHECKSTYLE IGNORE WriteTag FOR NEXT 2 LINES
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080029/**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080030 * @author David Erickson (daviderickson@cs.stanford.edu)
31 */
32public class DHCP extends BasePacket {
33 /**
34 * ------------------------------------------
35 * |op (1) | htype(1) | hlen(1) | hops(1) |
36 * ------------------------------------------
37 * | xid (4) |
38 * ------------------------------------------
39 * | secs (2) | flags (2) |
40 * ------------------------------------------
41 * | ciaddr (4) |
42 * ------------------------------------------
43 * | yiaddr (4) |
44 * ------------------------------------------
45 * | siaddr (4) |
46 * ------------------------------------------
47 * | giaddr (4) |
48 * ------------------------------------------
49 * | chaddr (16) |
50 * ------------------------------------------
51 * | sname (64) |
52 * ------------------------------------------
53 * | file (128) |
54 * ------------------------------------------
55 * | options (312) |
56 * ------------------------------------------
Ray Milkeyb41100a2014-04-10 10:42:15 -070057 * Basic packet.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080058 */
59 // Header + magic without options
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070060 public static final int MIN_HEADER_LENGTH = 240;
61 public static final byte OPCODE_REQUEST = 0x1;
62 public static final byte OPCODE_REPLY = 0x2;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080063
Pavlin Radoslavov608fac32014-04-09 12:40:24 -070064 public static final byte HWTYPE_ETHERNET = 0x1;
Ray Milkey269ffb92014-04-03 14:43:30 -070065
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080066 public enum DHCPOptionCode {
Ray Milkey269ffb92014-04-03 14:43:30 -070067 OptionCode_SubnetMask((byte) 1),
68 OptionCode_RequestedIP((byte) 50),
69 OptionCode_LeaseTime((byte) 51),
70 OptionCode_MessageType((byte) 53),
71 OptionCode_DHCPServerIp((byte) 54),
72 OptionCode_RequestedParameters((byte) 55),
73 OptionCode_RenewalTime((byte) 58),
74 OPtionCode_RebindingTime((byte) 59),
75 OptionCode_ClientID((byte) 61),
76 OptionCode_END((byte) 255);
77
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080078 protected byte value;
Ray Milkey269ffb92014-04-03 14:43:30 -070079
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080080 private DHCPOptionCode(byte value) {
81 this.value = value;
82 }
Ray Milkey269ffb92014-04-03 14:43:30 -070083
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080084 public byte getValue() {
85 return value;
86 }
87 }
Ray Milkey269ffb92014-04-03 14:43:30 -070088
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080089 protected byte opCode;
90 protected byte hardwareType;
91 protected byte hardwareAddressLength;
92 protected byte hops;
93 protected int transactionId;
94 protected short seconds;
95 protected short flags;
96 protected int clientIPAddress;
97 protected int yourIPAddress;
98 protected int serverIPAddress;
99 protected int gatewayIPAddress;
100 protected byte[] clientHardwareAddress;
101 protected String serverName;
102 protected String bootFileName;
103 protected List<DHCPOption> options = new ArrayList<DHCPOption>();
104
105 /**
106 * @return the opCode
107 */
108 public byte getOpCode() {
109 return opCode;
110 }
111
112 /**
113 * @param opCode the opCode to set
114 */
115 public DHCP setOpCode(byte opCode) {
116 this.opCode = opCode;
117 return this;
118 }
119
120 /**
121 * @return the hardwareType
122 */
123 public byte getHardwareType() {
124 return hardwareType;
125 }
126
127 /**
128 * @param hardwareType the hardwareType to set
129 */
130 public DHCP setHardwareType(byte hardwareType) {
131 this.hardwareType = hardwareType;
132 return this;
133 }
134
135 /**
136 * @return the hardwareAddressLength
137 */
138 public byte getHardwareAddressLength() {
139 return hardwareAddressLength;
140 }
141
142 /**
143 * @param hardwareAddressLength the hardwareAddressLength to set
144 */
145 public DHCP setHardwareAddressLength(byte hardwareAddressLength) {
146 this.hardwareAddressLength = hardwareAddressLength;
147 return this;
148 }
149
150 /**
151 * @return the hops
152 */
153 public byte getHops() {
154 return hops;
155 }
156
157 /**
158 * @param hops the hops to set
159 */
160 public DHCP setHops(byte hops) {
161 this.hops = hops;
162 return this;
163 }
164
165 /**
166 * @return the transactionId
167 */
168 public int getTransactionId() {
169 return transactionId;
170 }
171
172 /**
173 * @param transactionId the transactionId to set
174 */
175 public DHCP setTransactionId(int transactionId) {
176 this.transactionId = transactionId;
177 return this;
178 }
179
180 /**
181 * @return the seconds
182 */
183 public short getSeconds() {
184 return seconds;
185 }
186
187 /**
188 * @param seconds the seconds to set
189 */
190 public DHCP setSeconds(short seconds) {
191 this.seconds = seconds;
192 return this;
193 }
194
195 /**
196 * @return the flags
197 */
198 public short getFlags() {
199 return flags;
200 }
201
202 /**
203 * @param flags the flags to set
204 */
205 public DHCP setFlags(short flags) {
206 this.flags = flags;
207 return this;
208 }
209
210 /**
211 * @return the clientIPAddress
212 */
213 public int getClientIPAddress() {
214 return clientIPAddress;
215 }
216
217 /**
218 * @param clientIPAddress the clientIPAddress to set
219 */
220 public DHCP setClientIPAddress(int clientIPAddress) {
221 this.clientIPAddress = clientIPAddress;
222 return this;
223 }
224
225 /**
226 * @return the yourIPAddress
227 */
228 public int getYourIPAddress() {
229 return yourIPAddress;
230 }
231
232 /**
233 * @param yourIPAddress the yourIPAddress to set
234 */
235 public DHCP setYourIPAddress(int yourIPAddress) {
236 this.yourIPAddress = yourIPAddress;
237 return this;
238 }
239
240 /**
241 * @return the serverIPAddress
242 */
243 public int getServerIPAddress() {
244 return serverIPAddress;
245 }
246
247 /**
248 * @param serverIPAddress the serverIPAddress to set
249 */
250 public DHCP setServerIPAddress(int serverIPAddress) {
251 this.serverIPAddress = serverIPAddress;
252 return this;
253 }
254
255 /**
256 * @return the gatewayIPAddress
257 */
258 public int getGatewayIPAddress() {
259 return gatewayIPAddress;
260 }
261
262 /**
263 * @param gatewayIPAddress the gatewayIPAddress to set
264 */
265 public DHCP setGatewayIPAddress(int gatewayIPAddress) {
266 this.gatewayIPAddress = gatewayIPAddress;
267 return this;
268 }
269
270 /**
271 * @return the clientHardwareAddress
272 */
273 public byte[] getClientHardwareAddress() {
Pavlin Radoslavov1d595c02014-04-16 14:11:54 -0700274 return ArrayUtils.clone(this.clientHardwareAddress);
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 Radoslavov1d595c02014-04-16 14:11:54 -0700281 this.clientHardwareAddress = ArrayUtils.clone(clientHardwareAddress);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800282 return this;
283 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700284
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800285 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700286 * Gets a specific DHCP option parameter.
Ray Milkey269ffb92014-04-03 14:43:30 -0700287 *
Sho SHIMIZUa1199fa2014-06-10 18:11:12 -0700288 * @param optionCode The option code to get
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800289 * @return The value of the option if it exists, null otherwise
290 */
291 public DHCPOption getOption(DHCPOptionCode optionCode) {
292 for (DHCPOption opt : options) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700293 if (opt.code == optionCode.value) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800294 return opt;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700295 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800296 }
297 return null;
298 }
299
300 /**
301 * @return the options
302 */
303 public List<DHCPOption> getOptions() {
304 return options;
305 }
306
307 /**
308 * @param options the options to set
309 */
310 public DHCP setOptions(List<DHCPOption> options) {
311 this.options = options;
312 return this;
313 }
314
315 /**
316 * @return the packetType base on option 53
317 */
318 public DHCPPacketType getPacketType() {
319 ListIterator<DHCPOption> lit = options.listIterator();
320 while (lit.hasNext()) {
321 DHCPOption option = lit.next();
322 // only care option 53
323 if (option.getCode() == 53) {
324 return DHCPPacketType.getType(option.getData()[0]);
325 }
326 }
327 return null;
328 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700329
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800330 /**
331 * @return the serverName
332 */
333 public String getServerName() {
334 return serverName;
335 }
336
337 /**
338 * @param serverName the serverName to set
339 */
340 public DHCP setServerName(String serverName) {
341 this.serverName = serverName;
342 return this;
343 }
344
345 /**
346 * @return the bootFileName
347 */
348 public String getBootFileName() {
349 return bootFileName;
350 }
351
352 /**
353 * @param bootFileName the bootFileName to set
354 */
355 public DHCP setBootFileName(String bootFileName) {
356 this.bootFileName = bootFileName;
357 return this;
358 }
359
360 @Override
361 public byte[] serialize() {
362 // not guaranteed to retain length/exact format
363 resetChecksum();
364
365 // minimum size 240 including magic cookie, options generally padded to 300
366 int optionsLength = 0;
367 for (DHCPOption option : this.options) {
Pavlin Radoslavov33048e82014-04-09 14:40:59 -0700368 int code = option.getCode() & 0xff;
369 if (code == 0 || code == 255) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800370 optionsLength += 1;
371 } else {
Ray Milkey149693c2014-05-20 14:58:53 -0700372 optionsLength += 2 + (0xff & option.getLength());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800373 }
374 }
375 int optionsPadLength = 0;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700376 if (optionsLength < 60) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800377 optionsPadLength = 60 - optionsLength;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700378 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800379
Ray Milkey269ffb92014-04-03 14:43:30 -0700380 byte[] data = new byte[240 + optionsLength + optionsPadLength];
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800381 ByteBuffer bb = ByteBuffer.wrap(data);
382 bb.put(this.opCode);
383 bb.put(this.hardwareType);
384 bb.put(this.hardwareAddressLength);
385 bb.put(this.hops);
386 bb.putInt(this.transactionId);
387 bb.putShort(this.seconds);
388 bb.putShort(this.flags);
389 bb.putInt(this.clientIPAddress);
390 bb.putInt(this.yourIPAddress);
391 bb.putInt(this.serverIPAddress);
392 bb.putInt(this.gatewayIPAddress);
393 bb.put(this.clientHardwareAddress);
394 if (this.clientHardwareAddress.length < 16) {
395 for (int i = 0; i < (16 - this.clientHardwareAddress.length); ++i) {
396 bb.put((byte) 0x0);
397 }
398 }
399 writeString(this.serverName, bb, 64);
400 writeString(this.bootFileName, bb, 128);
401 // magic cookie
402 bb.put((byte) 0x63);
403 bb.put((byte) 0x82);
404 bb.put((byte) 0x53);
405 bb.put((byte) 0x63);
406 for (DHCPOption option : this.options) {
407 int code = option.getCode() & 0xff;
408 bb.put((byte) code);
409 if ((code != 0) && (code != 255)) {
410 bb.put(option.getLength());
411 bb.put(option.getData());
412 }
413 }
414 // assume the rest is padded out with zeroes
415 return data;
416 }
417
418 protected void writeString(String string, ByteBuffer bb, int maxLength) {
419 if (string == null) {
420 for (int i = 0; i < maxLength; ++i) {
421 bb.put((byte) 0x0);
422 }
423 } else {
424 byte[] bytes = null;
425 try {
Ray Milkey269ffb92014-04-03 14:43:30 -0700426 bytes = string.getBytes("ascii");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800427 } catch (UnsupportedEncodingException e) {
428 throw new RuntimeException("Failure encoding server name", e);
429 }
430 int writeLength = bytes.length;
431 if (writeLength > maxLength) {
432 writeLength = maxLength;
433 }
434 bb.put(bytes, 0, writeLength);
435 for (int i = writeLength; i < maxLength; ++i) {
436 bb.put((byte) 0x0);
437 }
438 }
439 }
440
441 @Override
442 public IPacket deserialize(byte[] data, int offset, int length) {
443 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
444 if (bb.remaining() < MIN_HEADER_LENGTH) {
445 return this;
446 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700447
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800448 this.opCode = bb.get();
449 this.hardwareType = bb.get();
450 this.hardwareAddressLength = bb.get();
451 this.hops = bb.get();
452 this.transactionId = bb.getInt();
453 this.seconds = bb.getShort();
454 this.flags = bb.getShort();
455 this.clientIPAddress = bb.getInt();
456 this.yourIPAddress = bb.getInt();
457 this.serverIPAddress = bb.getInt();
458 this.gatewayIPAddress = bb.getInt();
459 int hardwareAddressLength = 0xff & this.hardwareAddressLength;
460 this.clientHardwareAddress = new byte[hardwareAddressLength];
461
462 bb.get(this.clientHardwareAddress);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700463 for (int i = hardwareAddressLength; i < 16; ++i) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800464 bb.get();
Ray Milkeyb29e6262014-04-09 16:02:14 -0700465 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800466 this.serverName = readString(bb, 64);
467 this.bootFileName = readString(bb, 128);
468 // read the magic cookie
469 // magic cookie
470 bb.get();
471 bb.get();
472 bb.get();
473 bb.get();
474 // read options
475 while (bb.hasRemaining()) {
476 DHCPOption option = new DHCPOption();
477 int code = 0xff & bb.get(); // convert signed byte to int in range [0,255]
478 option.setCode((byte) code);
479 if (code == 0) {
480 // skip these
481 continue;
482 } else if (code != 255) {
483 if (bb.hasRemaining()) {
484 int l = 0xff & bb.get(); // convert signed byte to int in range [0,255]
485 option.setLength((byte) l);
486 if (bb.remaining() >= l) {
487 byte[] optionData = new byte[l];
488 bb.get(optionData);
489 option.setData(optionData);
490 } else {
491 // Skip the invalid option and set the END option
492 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700493 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800494 option.setLength((byte) 0);
495 }
496 } else {
497 // Skip the invalid option and set the END option
498 code = 0xff;
Ray Milkey269ffb92014-04-03 14:43:30 -0700499 option.setCode((byte) code);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800500 option.setLength((byte) 0);
501 }
502 }
503 this.options.add(option);
504 if (code == 255) {
505 // remaining bytes are supposed to be 0, but ignore them just in case
506 break;
507 }
508 }
509
510 return this;
511 }
512
513 protected String readString(ByteBuffer bb, int maxLength) {
514 byte[] bytes = new byte[maxLength];
515 bb.get(bytes);
516 String result = null;
517 try {
518 result = new String(bytes, "ascii").trim();
519 } catch (UnsupportedEncodingException e) {
520 throw new RuntimeException("Failure decoding string", e);
521 }
522 return result;
523 }
524}