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 com.google.common.collect.ImmutableMap;
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 import java.util.Arrays;
23 import java.util.HashMap;
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 final ImmutableMap<String, Pair<Integer, Integer>> FIELD_COORDINATES =
61 ImmutableMap.<String, Pair<Integer, Integer>>builderWithExpectedSize(16)
62 .put(OP, new ImmutablePair<>(0, 8))
63 .put(HTYPE, new ImmutablePair<>(8, 8))
64 .put(HLEN, new ImmutablePair<>(16, 8))
65 .put(HOPS, new ImmutablePair<>(24, 8))
66 .put(XID, new ImmutablePair<>(32, 32))
67 .put(SECS, new ImmutablePair<>(64, 16))
68 .put(FLAGS, new ImmutablePair<>(80, 16))
69 .put(CIADDR, new ImmutablePair<>(96, 32))
70 .put(YIADDR, new ImmutablePair<>(128, 32))
71 .put(SIADDR, new ImmutablePair<>(160, 32))
72 .put(GIADDR, new ImmutablePair<>(192, 32))
73 .put(CHADDR, new ImmutablePair<>(224, 128))
74 .put(SNAME, new ImmutablePair<>(352, 512))
75 .put(FILE, new ImmutablePair<>(864, 1024))
76 .put(MCOOKIE, new ImmutablePair<>(1888, 32))
77 .put(OPTIONS, new ImmutablePair<>(1920, 0))
80 private final Map<String, byte[]> fieldValues;
86 public DHCP(boolean writeAccess) {
88 fieldValues = new HashMap<>();
89 hdrFieldCoordMap = FIELD_COORDINATES;
90 hdrFieldsMap = fieldValues;
94 setHtype(HTYPE_ETHER);
104 setChaddr(new byte[16]);
105 setSname(new byte[64]);
106 setFile(new byte[128]);
107 setMcookie(MAGIC_COOKIE);
108 setOptions(new byte[0]);
109 this.dhcpOptions = new DHCPOptions();
112 public byte getHtype() {
113 return (BitBufferHelper.getByte(fieldValues.get(HTYPE)));
116 public byte getHlen() {
117 return (BitBufferHelper.getByte(fieldValues.get(HLEN)));
120 public int getXid() {
121 return (BitBufferHelper.getInt(fieldValues.get(XID)));
124 public short getFlags() {
125 return (BitBufferHelper.getShort(fieldValues.get(FLAGS)));
128 public byte[] getCiaddr() {
129 return fieldValues.get(CIADDR);
133 public byte[] getGiaddr() {
134 return fieldValues.get(GIADDR);
137 public byte[] getChaddr() {
138 return fieldValues.get(CHADDR);
141 public byte[] getOptions() {
142 return fieldValues.get(OPTIONS);
146 // public byte[] getPadding() {
152 public void setHeaderField(String headerField, byte[] readValue) {
153 if (headerField.equals(OPTIONS) && (readValue == null || readValue.length == 0)) {
154 hdrFieldsMap.remove(headerField);
157 hdrFieldsMap.put(headerField, readValue);
160 public DHCP setOp(byte dhcpOp) {
161 byte[] op = BitBufferHelper.toByteArray(dhcpOp);
162 fieldValues.put(OP, op);
166 public DHCP setHtype(byte dhcpHtype) {
167 byte[] htype = BitBufferHelper.toByteArray(dhcpHtype);
168 fieldValues.put(HTYPE, htype);
172 public DHCP setHlen(byte dhcpHlen) {
173 byte[] hlen = BitBufferHelper.toByteArray(dhcpHlen);
174 fieldValues.put(HLEN, hlen);
178 public DHCP setHops(byte dhcpHops) {
179 byte[] hops = BitBufferHelper.toByteArray(dhcpHops);
180 fieldValues.put(HOPS, hops);
184 public DHCP setXid(int dhcpXid) {
185 byte[] xid = BitBufferHelper.toByteArray(dhcpXid);
186 fieldValues.put(XID, xid);
190 public DHCP setSecs(short dhcpSecs) {
191 byte[] secs = BitBufferHelper.toByteArray(dhcpSecs);
192 fieldValues.put(SECS, secs);
196 public DHCP setFlags(short dhcpFlags) {
197 byte[] flags = BitBufferHelper.toByteArray(dhcpFlags);
198 fieldValues.put(FLAGS, flags);
202 public DHCP setCiaddr(byte[] ciaddr) {
203 fieldValues.put(CIADDR, ciaddr);
207 public DHCP setCiaddr(int dhcpCiaddr) {
208 byte[] ciaddr = BitBufferHelper.toByteArray(dhcpCiaddr);
209 fieldValues.put(CIADDR, ciaddr);
213 public DHCP setYiaddr(int dhcpYiaddr) {
214 byte[] yiaddr = BitBufferHelper.toByteArray(dhcpYiaddr);
215 fieldValues.put(YIADDR, yiaddr);
219 public DHCP setYiaddr(String dhcpYiaddr) {
220 byte[] yiaddr = NetUtils.parseInetAddress(dhcpYiaddr).getAddress();
221 fieldValues.put(YIADDR, yiaddr);
225 public DHCP setSiaddr(int dhcpSiaddr) {
226 byte[] siaddr = BitBufferHelper.toByteArray(dhcpSiaddr);
227 fieldValues.put(SIADDR, siaddr);
231 public DHCP setSiaddr(String dhcpSiaddr) {
232 byte[] siaddr = NetUtils.parseInetAddress(dhcpSiaddr).getAddress();
233 fieldValues.put(SIADDR, siaddr);
237 public DHCP setGiaddr(byte[] giaddr) {
238 fieldValues.put(GIADDR, giaddr);
242 public DHCP setGiaddr(int dhcpGiaddr) {
243 byte[] giaddr = BitBufferHelper.toByteArray(dhcpGiaddr);
244 fieldValues.put(GIADDR, giaddr);
248 public DHCP setChaddr(byte[] chaddr) {
249 fieldValues.put(CHADDR, chaddr);
253 public DHCP setSname(byte[] sname) {
254 fieldValues.put(SNAME, sname);
258 public DHCP setFile(byte[] file) {
259 fieldValues.put(FILE, file);
263 public DHCP setMcookie(int dhcpMc) {
264 byte[] mc = BitBufferHelper.toByteArray(dhcpMc);
265 fieldValues.put(MCOOKIE, mc);
269 public DHCP setOptions(byte[] options) {
270 fieldValues.put(OPTIONS, options);
274 // public void setPadding(byte[] pad) {
279 * This method deserializes the data bits obtained from the wire into the
280 * respective header and payload which are of type Packet.
282 * @param data byte[] data from wire to deserialize
283 * @param bitOffset int bit position where packet header starts in data
285 * @param size int size of packet in bits
287 * @throws PacketException the packet deserialization failed
289 * <p>Note: Copied from org.opendaylight.controller.sal.packet.Packet</p>
292 // We can’t do much about PacketException (yet; see https://git.opendaylight.org/gerrit/65837)
293 @SuppressWarnings("checkstyle:AvoidHidingCauseException")
294 public Packet deserialize(byte[] data, int bitOffset, int size)
295 throws PacketException {
297 // Deserialize the header fields one by one
300 for (Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
302 String hdrField = pairs.getKey();
303 startOffset = bitOffset + this.getfieldOffset(hdrField);
304 if (hdrField.equals(OPTIONS)) {
305 numBits = (size - DHCP_NOOPT_HDR_SIZE) * 8;
307 numBits = this.getfieldnumBits(hdrField);
309 byte[] hdrFieldBytes = null;
311 hdrFieldBytes = BitBufferHelper.getBits(data, startOffset,
313 } catch (BufferException e) {
314 throw new PacketException(e.getMessage());
318 * Store the raw read value, checks the payload type and set the
319 * payloadClass accordingly
321 this.setHeaderField(hdrField, hdrFieldBytes);
323 if (LOG.isTraceEnabled()) {
324 LOG.trace("{}: {}: {} (offset {} bitsize {})",
325 this.getClass().getSimpleName(), hdrField,
326 HexEncode.bytesToHexString(hdrFieldBytes),
327 startOffset, numBits);
331 // Deserialize the payload now
332 int payloadStart = startOffset + numBits;
333 int payloadSize = data.length * Byte.SIZE - payloadStart;
335 if (payloadClass != null) {
337 payload = payloadClass.newInstance();
338 } catch (InstantiationException | IllegalAccessException e) {
339 throw new RuntimeException(
340 "Error parsing payload for Ethernet packet", e);
342 payload.deserialize(data, payloadStart, payloadSize);
343 payload.setParent(this);
346 * The payload class was not set, it means no class for parsing
347 * this payload is present. Let's store the raw payload if any.
349 int start = payloadStart / Byte.SIZE;
350 int stop = start + payloadSize / Byte.SIZE;
351 rawPayload = Arrays.copyOfRange(data, start, stop);
353 // Take care of computation that can be done only after deserialization
354 postDeserializeCustomOperation(data, payloadStart - getHeaderSize());
360 public byte[] serialize() throws PacketException {
361 this.setOptions(this.dhcpOptions.serialize());
362 byte[] data = super.serialize();
363 // Check for OPT_END at end of options
364 if (data.length > DHCP_MAX_SIZE) {
365 // shouldn't have happened
367 LOG.error("DHCP Packet too big");
368 } else if (data[data.length - 1] != (byte)255) {
369 // DHCP Options not ended properly
370 //throw new PacketException("Missing DHCP Option END");
371 LOG.error("Missing DHCP Option END");
372 } else if (data.length < DHCP_MIN_SIZE) {
373 byte[] padding = new byte[DHCP_MIN_SIZE - data.length];
374 LOG.debug("DHCP Pkt too small: {}, padding added {}",
375 data.length, padding.length);
376 data = ArrayUtils.addAll(data, padding);
383 * Gets the number of bits for the fieldname specified
384 * If the fieldname has variable length like "Options", then this value is computed using the header length
385 * @param fieldname - String
386 * @return number of bits for fieldname - int
388 public int getfieldnumBits(String fieldName) {
389 if (fieldName.equals(OPTIONS)) {
390 byte[] barr = fieldValues.get(OPTIONS);
391 return (barr.length) * Byte.SIZE;
393 return hdrFieldCoordMap.get(fieldName).getRight();
397 public int getHeaderSize() {
398 byte[] barr = fieldValues.get(OPTIONS);
403 return (DHCP_NOOPT_HDR_SIZE + len) * 8;
407 protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
408 //TODO: Anything need to be done here?
409 // Check for MAGIC_COOKIE. This means we only support DHCP, not BOOTP
410 int cookie = BitBufferHelper.getInt(fieldValues.get(MCOOKIE));
411 if (cookie != MAGIC_COOKIE) {
412 LOG.debug("Not DHCP packet");
415 // parse options into DHCPOptions
416 this.dhcpOptions.deserialize(this.getOptions());
417 // reset options byte array, this will also drop padding
418 this.setOptions(this.dhcpOptions.serialize());
421 // Set/get operations for Options
422 public void setMsgType(byte type) {
423 dhcpOptions.setOptionByte(OPT_MESSAGE_TYPE, type);
426 public byte getMsgType() {
427 return dhcpOptions.getOptionByte(OPT_MESSAGE_TYPE);
430 public void setOptionBytes(byte code, byte[] opt) {
431 dhcpOptions.setOption(code, opt);
434 public byte[] getOptionBytes(byte code) {
435 return dhcpOptions.getOptionBytes(code);
438 public void setOptionInt(byte code, int opt) {
439 dhcpOptions.setOptionInt(code, opt);
442 public InetAddress getOptionInetAddr(byte code) {
443 return dhcpOptions.getOptionInetAddr(code);
446 public void setOptionInetAddr(byte code, String addr) throws UnknownHostException {
447 dhcpOptions.setOptionStrAddr(code, addr);
450 public void setOptionStrAddrs(byte code, List<String> opt) throws UnknownHostException {
451 dhcpOptions.setOptionStrAddrs(code, opt);
454 public void setOptionString(byte code, String str) {
455 dhcpOptions.setOptionString(code, str);
458 public boolean containsOption(byte code) {
459 // TODO Auto-generated method stub
460 return dhcpOptions.containsOption(code);
463 public void unsetOption(byte code) {
464 dhcpOptions.unsetOption(code);
468 public String toString() {
469 StringBuilder ret = new StringBuilder();
470 ret.append(super.toString()).append(dhcpOptions);
472 return ret.toString();