2 * Copyright (c) 2015, 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netvirt.dhcpservice.api;
11 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.BOOTREPLY;
12 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.DHCP_MAX_SIZE;
13 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.DHCP_MIN_SIZE;
14 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.DHCP_NOOPT_HDR_SIZE;
15 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.HTYPE_ETHER;
16 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.MAGIC_COOKIE;
17 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.OPT_MESSAGE_TYPE;
19 import java.net.InetAddress;
20 import java.net.UnknownHostException;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.LinkedHashMap;
24 import java.util.List;
26 import java.util.Map.Entry;
27 import org.apache.commons.lang3.ArrayUtils;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
31 import org.opendaylight.openflowplugin.libraries.liblldp.BufferException;
32 import org.opendaylight.openflowplugin.libraries.liblldp.HexEncode;
33 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
34 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
35 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 public class DHCP extends Packet {
40 private static final Logger LOG = LoggerFactory.getLogger(DHCP.class);
42 private static final String OP = "Op";
43 private static final String HTYPE = "Htype";
44 private static final String HLEN = "Hlen";
45 private static final String HOPS = "Hops";
46 private static final String XID = "Xid";
47 private static final String SECS = "Secs";
48 private static final String FLAGS = "Flags";
49 private static final String CIADDR = "Ciaddr";
50 private static final String YIADDR = "Yiaddr";
51 private static final String SIADDR = "Siaddr";
52 private static final String GIADDR = "Giaddr";
53 private static final String CHADDR = "Chaddr";
54 private static final String SNAME = "Sname";
55 private static final String FILE = "File";
56 private static final String MCOOKIE = "Mcookie";
57 private static final String OPTIONS = "Options";
58 private DHCPOptions dhcpOptions = null;
60 private static Map<String, Pair<Integer, Integer>> fieldCoordinates =
61 new LinkedHashMap<String, Pair<Integer, Integer>>() {
62 private static final long serialVersionUID = 1L;
64 put(OP, new ImmutablePair<>(0, 8));
65 put(HTYPE, new ImmutablePair<>(8, 8));
66 put(HLEN, new ImmutablePair<>(16, 8));
67 put(HOPS, new ImmutablePair<>(24, 8));
68 put(XID, new ImmutablePair<>(32, 32));
69 put(SECS, new ImmutablePair<>(64, 16));
70 put(FLAGS, new ImmutablePair<>(80, 16));
71 put(CIADDR, new ImmutablePair<>(96, 32));
72 put(YIADDR, new ImmutablePair<>(128, 32));
73 put(SIADDR, new ImmutablePair<>(160, 32));
74 put(GIADDR, new ImmutablePair<>(192, 32));
75 put(CHADDR, new ImmutablePair<>(224, 128));
76 put(SNAME, new ImmutablePair<>(352, 512));
77 put(FILE, new ImmutablePair<>(864, 1024));
78 put(MCOOKIE, new ImmutablePair<>(1888, 32));
79 put(OPTIONS, new ImmutablePair<>(1920, 0));
83 private final Map<String, byte[]> fieldValues;
89 public DHCP(boolean writeAccess) {
91 fieldValues = new HashMap<>();
92 hdrFieldCoordMap = fieldCoordinates;
93 hdrFieldsMap = fieldValues;
97 setHtype(HTYPE_ETHER);
107 setChaddr(new byte[16]);
108 setSname(new byte[64]);
109 setFile(new byte[128]);
110 setMcookie(MAGIC_COOKIE);
111 setOptions(new byte[0]);
112 this.dhcpOptions = new DHCPOptions();
116 public byte getOp() {
117 return (BitBufferHelper.getByte(fieldValues.get(OP)));
120 public byte getHtype() {
121 return (BitBufferHelper.getByte(fieldValues.get(HTYPE)));
124 public byte getHlen() {
125 return (BitBufferHelper.getByte(fieldValues.get(HLEN)));
128 public byte getHops() {
129 return (BitBufferHelper.getByte(fieldValues.get(HOPS)));
132 public int getXid() {
133 return (BitBufferHelper.getInt(fieldValues.get(XID)));
136 public short getSecs() {
137 return (BitBufferHelper.getShort(fieldValues.get(SECS)));
140 public short getFlags() {
141 return (BitBufferHelper.getShort(fieldValues.get(FLAGS)));
144 public byte[] getCiaddr() {
145 return fieldValues.get(CIADDR);
148 public byte[] getYiaddr() {
149 return fieldValues.get(YIADDR);
153 public byte[] getSiaddr() {
154 return fieldValues.get(SIADDR);
157 public InetAddress getSiaddrAsInetAddr() {
158 return DHCPUtils.byteArrayToInetAddr(fieldValues.get(SIADDR));
161 public byte[] getGiaddr() {
162 return fieldValues.get(GIADDR);
165 public byte[] getChaddr() {
166 return fieldValues.get(CHADDR);
169 public byte[] getSname() {
170 return fieldValues.get(SNAME);
173 public byte[] getFile() {
174 return fieldValues.get(FILE);
177 public int getMCookie() {
178 return (BitBufferHelper.getInt(fieldValues.get(MCOOKIE)));
181 public byte[] getOptions() {
182 return fieldValues.get(OPTIONS);
186 // public byte[] getPadding() {
192 public void setHeaderField(String headerField, byte[] readValue) {
193 if (headerField.equals(OPTIONS) && (readValue == null || readValue.length == 0)) {
194 hdrFieldsMap.remove(headerField);
197 hdrFieldsMap.put(headerField, readValue);
200 public DHCP setOp(byte dhcpOp) {
201 byte[] op = BitBufferHelper.toByteArray(dhcpOp);
202 fieldValues.put(OP, op);
206 public DHCP setHtype(byte dhcpHtype) {
207 byte[] htype = BitBufferHelper.toByteArray(dhcpHtype);
208 fieldValues.put(HTYPE, htype);
212 public DHCP setHlen(byte dhcpHlen) {
213 byte[] hlen = BitBufferHelper.toByteArray(dhcpHlen);
214 fieldValues.put(HLEN, hlen);
218 public DHCP setHops(byte dhcpHops) {
219 byte[] hops = BitBufferHelper.toByteArray(dhcpHops);
220 fieldValues.put(HOPS, hops);
224 public DHCP setXid(int dhcpXid) {
225 byte[] xid = BitBufferHelper.toByteArray(dhcpXid);
226 fieldValues.put(XID, xid);
230 public DHCP setSecs(short dhcpSecs) {
231 byte[] secs = BitBufferHelper.toByteArray(dhcpSecs);
232 fieldValues.put(SECS, secs);
236 public DHCP setFlags(short dhcpFlags) {
237 byte[] flags = BitBufferHelper.toByteArray(dhcpFlags);
238 fieldValues.put(FLAGS, flags);
242 public DHCP setCiaddr(byte[] ciaddr) {
243 fieldValues.put(CIADDR, ciaddr);
247 public DHCP setCiaddr(int dhcpCiaddr) {
248 byte[] ciaddr = BitBufferHelper.toByteArray(dhcpCiaddr);
249 fieldValues.put(CIADDR, ciaddr);
253 public DHCP setCiaddr(InetAddress dhcpCiaddr) {
254 byte[] ciaddr = dhcpCiaddr.getAddress();
255 fieldValues.put(CIADDR, ciaddr);
259 public DHCP setCiaddr(String dhcpCiaddr) {
260 byte[] ciaddr = NetUtils.parseInetAddress(dhcpCiaddr).getAddress();
261 fieldValues.put(CIADDR, ciaddr);
265 public DHCP setYiaddr(byte[] yiaddr) {
266 fieldValues.put(YIADDR, yiaddr);
270 public DHCP setYiaddr(int dhcpYiaddr) {
271 byte[] yiaddr = BitBufferHelper.toByteArray(dhcpYiaddr);
272 fieldValues.put(YIADDR, yiaddr);
276 public DHCP setYiaddr(InetAddress dhcpYiaddr) {
277 byte[] yiaddr = dhcpYiaddr.getAddress();
278 fieldValues.put(YIADDR, yiaddr);
282 public DHCP setYiaddr(String dhcpYiaddr) {
283 byte[] yiaddr = NetUtils.parseInetAddress(dhcpYiaddr).getAddress();
284 fieldValues.put(YIADDR, yiaddr);
288 public DHCP setSiaddr(byte[] siaddr) {
289 fieldValues.put(SIADDR, siaddr);
293 public DHCP setSiaddr(int dhcpSiaddr) {
294 byte[] siaddr = BitBufferHelper.toByteArray(dhcpSiaddr);
295 fieldValues.put(SIADDR, siaddr);
299 public DHCP setSiaddr(InetAddress dhcpSiaddr) {
300 byte[] siaddr = dhcpSiaddr.getAddress();
301 fieldValues.put(SIADDR, siaddr);
305 public DHCP setSiaddr(String dhcpSiaddr) {
306 byte[] siaddr = NetUtils.parseInetAddress(dhcpSiaddr).getAddress();
307 fieldValues.put(SIADDR, siaddr);
311 public DHCP setGiaddr(byte[] giaddr) {
312 fieldValues.put(GIADDR, giaddr);
316 public DHCP setGiaddr(int dhcpGiaddr) {
317 byte[] giaddr = BitBufferHelper.toByteArray(dhcpGiaddr);
318 fieldValues.put(GIADDR, giaddr);
322 public DHCP setGiaddr(InetAddress dhcpGiaddr) {
323 byte[] giaddr = dhcpGiaddr.getAddress();
324 fieldValues.put(GIADDR, giaddr);
328 public DHCP setGiaddr(String dhcpGiaddr) {
329 byte[] giaddr = NetUtils.parseInetAddress(dhcpGiaddr).getAddress();
330 fieldValues.put(GIADDR, giaddr);
334 public DHCP setChaddr(byte[] chaddr) {
335 fieldValues.put(CHADDR, chaddr);
339 public DHCP setSname(byte[] sname) {
340 fieldValues.put(SNAME, sname);
344 public DHCP setFile(byte[] file) {
345 fieldValues.put(FILE, file);
349 public DHCP setMcookie(int dhcpMc) {
350 byte[] mc = BitBufferHelper.toByteArray(dhcpMc);
351 fieldValues.put(MCOOKIE, mc);
355 public DHCP setOptions(byte[] options) {
356 fieldValues.put(OPTIONS, options);
360 // public void setPadding(byte[] pad) {
365 * This method deserializes the data bits obtained from the wire into the
366 * respective header and payload which are of type Packet.
368 * @param data byte[] data from wire to deserialize
369 * @param bitOffset int bit position where packet header starts in data
371 * @param size int size of packet in bits
373 * @throws PacketException the packet deserialization failed
375 * <p>Note: Copied from org.opendaylight.controller.sal.packet.Packet</p>
378 // We can’t do much about PacketException (yet; see https://git.opendaylight.org/gerrit/65837)
379 @SuppressWarnings("checkstyle:AvoidHidingCauseException")
380 public Packet deserialize(byte[] data, int bitOffset, int size)
381 throws PacketException {
383 // Deserialize the header fields one by one
386 for (Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
388 String hdrField = pairs.getKey();
389 startOffset = bitOffset + this.getfieldOffset(hdrField);
390 if (hdrField.equals(OPTIONS)) {
391 numBits = (size - DHCP_NOOPT_HDR_SIZE) * 8;
393 numBits = this.getfieldnumBits(hdrField);
395 byte[] hdrFieldBytes = null;
397 hdrFieldBytes = BitBufferHelper.getBits(data, startOffset,
399 } catch (BufferException e) {
400 throw new PacketException(e.getMessage());
404 * Store the raw read value, checks the payload type and set the
405 * payloadClass accordingly
407 this.setHeaderField(hdrField, hdrFieldBytes);
409 if (LOG.isTraceEnabled()) {
410 LOG.trace("{}: {}: {} (offset {} bitsize {})",
411 this.getClass().getSimpleName(), hdrField,
412 HexEncode.bytesToHexString(hdrFieldBytes),
413 startOffset, numBits);
417 // Deserialize the payload now
418 int payloadStart = startOffset + numBits;
419 int payloadSize = data.length * Byte.SIZE - payloadStart;
421 if (payloadClass != null) {
423 payload = payloadClass.newInstance();
424 } catch (InstantiationException | IllegalAccessException e) {
425 throw new RuntimeException(
426 "Error parsing payload for Ethernet packet", e);
428 payload.deserialize(data, payloadStart, payloadSize);
429 payload.setParent(this);
432 * The payload class was not set, it means no class for parsing
433 * this payload is present. Let's store the raw payload if any.
435 int start = payloadStart / Byte.SIZE;
436 int stop = start + payloadSize / Byte.SIZE;
437 rawPayload = Arrays.copyOfRange(data, start, stop);
439 // Take care of computation that can be done only after deserialization
440 postDeserializeCustomOperation(data, payloadStart - getHeaderSize());
446 public byte[] serialize() throws PacketException {
447 this.setOptions(this.dhcpOptions.serialize());
448 byte[] data = super.serialize();
449 // Check for OPT_END at end of options
450 if (data.length > DHCP_MAX_SIZE) {
451 // shouldn't have happened
453 LOG.error("DHCP Packet too big");
454 } else if (data[data.length - 1] != (byte)255) {
455 // DHCP Options not ended properly
456 //throw new PacketException("Missing DHCP Option END");
457 LOG.error("Missing DHCP Option END");
458 } else if (data.length < DHCP_MIN_SIZE) {
459 byte[] padding = new byte[DHCP_MIN_SIZE - data.length];
460 LOG.debug("DHCP Pkt too small: {}, padding added {}",
461 data.length, padding.length);
462 data = ArrayUtils.addAll(data, padding);
469 * Gets the number of bits for the fieldname specified
470 * If the fieldname has variable length like "Options", then this value is computed using the header length
471 * @param fieldname - String
472 * @return number of bits for fieldname - int
474 public int getfieldnumBits(String fieldName) {
475 if (fieldName.equals(OPTIONS)) {
476 byte[] barr = fieldValues.get(OPTIONS);
477 return (barr.length) * Byte.SIZE;
479 return hdrFieldCoordMap.get(fieldName).getRight();
483 public int getHeaderSize() {
484 byte[] barr = fieldValues.get(OPTIONS);
489 return (DHCP_NOOPT_HDR_SIZE + len) * 8;
493 protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
494 //TODO: Anything need to be done here?
495 // Check for MAGIC_COOKIE. This means we only support DHCP, not BOOTP
496 int cookie = BitBufferHelper.getInt(fieldValues.get(MCOOKIE));
497 if (cookie != MAGIC_COOKIE) {
498 LOG.debug("Not DHCP packet");
501 // parse options into DHCPOptions
502 this.dhcpOptions.deserialize(this.getOptions());
503 // reset options byte array, this will also drop padding
504 this.setOptions(this.dhcpOptions.serialize());
507 // Set/get operations for Options
508 public void setMsgType(byte type) {
509 dhcpOptions.setOptionByte(OPT_MESSAGE_TYPE, type);
512 public byte getMsgType() {
513 return dhcpOptions.getOptionByte(OPT_MESSAGE_TYPE);
516 public void setOptionByte(byte code, byte opt) {
517 dhcpOptions.setOptionByte(code, opt);
520 public byte getOptionByte(byte code) {
521 return dhcpOptions.getOptionByte(code);
524 public void setOptionBytes(byte code, byte[] opt) {
525 dhcpOptions.setOption(code, opt);
528 public byte[] getOptionBytes(byte code) {
529 return dhcpOptions.getOptionBytes(code);
532 public void setOptionShort(byte code, short opt) {
533 dhcpOptions.setOptionShort(code, opt);
536 public short getOptionShort(byte code) {
537 return dhcpOptions.getOptionShort(code);
540 public void setOptionInt(byte code, int opt) {
541 dhcpOptions.setOptionInt(code, opt);
544 public int getOptionInt(byte code) {
545 return dhcpOptions.getOptionInt(code);
548 public InetAddress getOptionInetAddr(byte code) {
549 return dhcpOptions.getOptionInetAddr(code);
552 public void setOptionInetAddr(byte code, InetAddress addr) {
553 dhcpOptions.setOptionInetAddr(code, addr);
556 public void setOptionInetAddr(byte code, String addr) throws UnknownHostException {
557 dhcpOptions.setOptionStrAddr(code, addr);
560 public String getOptionStrAddr(byte code) {
561 return dhcpOptions.getOptionStrAddr(code);
564 public void setOptionStrAddrs(byte code, List<String> opt) throws UnknownHostException {
565 dhcpOptions.setOptionStrAddrs(code, opt);
568 public void setOptionString(byte code, String str) {
569 dhcpOptions.setOptionString(code, str);
572 public boolean containsOption(byte code) {
573 // TODO Auto-generated method stub
574 return dhcpOptions.containsOption(code);
577 public void unsetOption(byte code) {
578 dhcpOptions.unsetOption(code);
582 public String toString() {
583 StringBuilder ret = new StringBuilder();
584 ret.append(super.toString()).append(dhcpOptions);
586 return ret.toString();