Bump upstreams for Silicon
[netvirt.git] / dhcpservice / api / src / main / java / org / opendaylight / netvirt / dhcpservice / api / DHCP.java
1 /*
2  * Copyright (c) 2015, 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netvirt.dhcpservice.api;
10
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;
18
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;
25 import java.util.Map;
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;
38
39 public class DHCP extends Packet {
40     private static final Logger LOG = LoggerFactory.getLogger(DHCP.class);
41
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;
59
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))
78             .build();
79
80     private final Map<String, byte[]> fieldValues;
81
82     public DHCP() {
83         this(false);
84     }
85
86     public DHCP(boolean writeAccess) {
87         super(writeAccess);
88         fieldValues = new HashMap<>();
89         hdrFieldCoordMap = FIELD_COORDINATES;
90         hdrFieldsMap = fieldValues;
91         corrupted = false;
92
93         setOp(BOOTREPLY);
94         setHtype(HTYPE_ETHER);
95         setHlen((byte)6);
96         setHops((byte)0);
97         setXid(0);
98         setSecs((short) 0);
99         setFlags((short) 0);
100         setCiaddr(0);
101         setYiaddr(0);
102         setSiaddr(0);
103         setGiaddr(0);
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();
110     }
111
112     public byte getHtype() {
113         return (BitBufferHelper.getByte(fieldValues.get(HTYPE)));
114     }
115
116     public byte getHlen() {
117         return (BitBufferHelper.getByte(fieldValues.get(HLEN)));
118     }
119
120     public int getXid() {
121         return (BitBufferHelper.getInt(fieldValues.get(XID)));
122     }
123
124     public short getFlags() {
125         return (BitBufferHelper.getShort(fieldValues.get(FLAGS)));
126     }
127
128     public byte[] getCiaddr() {
129         return fieldValues.get(CIADDR);
130     }
131
132
133     public byte[] getGiaddr() {
134         return fieldValues.get(GIADDR);
135     }
136
137     public byte[] getChaddr() {
138         return fieldValues.get(CHADDR);
139     }
140
141     public byte[] getOptions() {
142         return fieldValues.get(OPTIONS);
143     }
144
145 //    TODO:
146 //    public byte[] getPadding() {
147 //        return this.pad;
148 //    }
149
150     // Setters
151     @Override
152     public void setHeaderField(String headerField, byte[] readValue) {
153         if (headerField.equals(OPTIONS) && (readValue == null || readValue.length == 0)) {
154             hdrFieldsMap.remove(headerField);
155             return;
156         }
157         hdrFieldsMap.put(headerField, readValue);
158     }
159
160     public DHCP setOp(byte dhcpOp) {
161         byte[] op = BitBufferHelper.toByteArray(dhcpOp);
162         fieldValues.put(OP, op);
163         return this;
164     }
165
166     public DHCP setHtype(byte dhcpHtype) {
167         byte[] htype = BitBufferHelper.toByteArray(dhcpHtype);
168         fieldValues.put(HTYPE, htype);
169         return this;
170     }
171
172     public DHCP setHlen(byte dhcpHlen) {
173         byte[] hlen = BitBufferHelper.toByteArray(dhcpHlen);
174         fieldValues.put(HLEN, hlen);
175         return this;
176     }
177
178     public DHCP setHops(byte dhcpHops) {
179         byte[] hops = BitBufferHelper.toByteArray(dhcpHops);
180         fieldValues.put(HOPS, hops);
181         return this;
182     }
183
184     public DHCP setXid(int dhcpXid) {
185         byte[] xid = BitBufferHelper.toByteArray(dhcpXid);
186         fieldValues.put(XID, xid);
187         return this;
188     }
189
190     public DHCP setSecs(short dhcpSecs) {
191         byte[] secs = BitBufferHelper.toByteArray(dhcpSecs);
192         fieldValues.put(SECS, secs);
193         return this;
194     }
195
196     public DHCP setFlags(short dhcpFlags) {
197         byte[] flags = BitBufferHelper.toByteArray(dhcpFlags);
198         fieldValues.put(FLAGS, flags);
199         return this;
200     }
201
202     public DHCP setCiaddr(byte[] ciaddr) {
203         fieldValues.put(CIADDR, ciaddr);
204         return this;
205     }
206
207     public DHCP setCiaddr(int dhcpCiaddr) {
208         byte[] ciaddr = BitBufferHelper.toByteArray(dhcpCiaddr);
209         fieldValues.put(CIADDR, ciaddr);
210         return this;
211     }
212
213     public DHCP setYiaddr(int dhcpYiaddr) {
214         byte[] yiaddr = BitBufferHelper.toByteArray(dhcpYiaddr);
215         fieldValues.put(YIADDR, yiaddr);
216         return this;
217     }
218
219     public DHCP setYiaddr(String dhcpYiaddr) {
220         byte[] yiaddr = NetUtils.parseInetAddress(dhcpYiaddr).getAddress();
221         fieldValues.put(YIADDR, yiaddr);
222         return this;
223     }
224
225     public DHCP setSiaddr(int dhcpSiaddr) {
226         byte[] siaddr = BitBufferHelper.toByteArray(dhcpSiaddr);
227         fieldValues.put(SIADDR, siaddr);
228         return this;
229     }
230
231     public DHCP setSiaddr(String dhcpSiaddr) {
232         byte[] siaddr = NetUtils.parseInetAddress(dhcpSiaddr).getAddress();
233         fieldValues.put(SIADDR, siaddr);
234         return this;
235     }
236
237     public DHCP setGiaddr(byte[] giaddr) {
238         fieldValues.put(GIADDR, giaddr);
239         return this;
240     }
241
242     public DHCP setGiaddr(int dhcpGiaddr) {
243         byte[] giaddr = BitBufferHelper.toByteArray(dhcpGiaddr);
244         fieldValues.put(GIADDR, giaddr);
245         return this;
246     }
247
248     public DHCP setChaddr(byte[] chaddr) {
249         fieldValues.put(CHADDR, chaddr);
250         return this;
251     }
252
253     public DHCP setSname(byte[] sname) {
254         fieldValues.put(SNAME, sname);
255         return this;
256     }
257
258     public DHCP setFile(byte[] file) {
259         fieldValues.put(FILE, file);
260         return this;
261     }
262
263     public DHCP setMcookie(int dhcpMc) {
264         byte[] mc = BitBufferHelper.toByteArray(dhcpMc);
265         fieldValues.put(MCOOKIE, mc);
266         return this;
267     }
268
269     public DHCP setOptions(byte[] options) {
270         fieldValues.put(OPTIONS, options);
271         return this;
272     }
273
274 //    public void setPadding(byte[] pad) {
275 //        this.pad = pad;
276 //    }
277
278     /**
279      * This method deserializes the data bits obtained from the wire into the
280      * respective header and payload which are of type Packet.
281      *
282      * @param data       byte[] data from wire to deserialize
283      * @param bitOffset  int    bit position where packet header starts in data
284      *        array
285      * @param size       int    size of packet in bits
286      * @return Packet
287      * @throws PacketException the packet deserialization failed
288      *
289      * <p>Note: Copied from org.opendaylight.controller.sal.packet.Packet</p>
290      */
291     @Override
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 {
296
297         // Deserialize the header fields one by one
298         int startOffset = 0;
299         int numBits = 0;
300         for (Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
301                 .entrySet()) {
302             String hdrField = pairs.getKey();
303             startOffset = bitOffset + this.getfieldOffset(hdrField);
304             if (hdrField.equals(OPTIONS)) {
305                 numBits = (size - DHCP_NOOPT_HDR_SIZE) * 8;
306             } else {
307                 numBits = this.getfieldnumBits(hdrField);
308             }
309             byte[] hdrFieldBytes = null;
310             try {
311                 hdrFieldBytes = BitBufferHelper.getBits(data, startOffset,
312                         numBits);
313             } catch (BufferException e) {
314                 throw new PacketException(e.getMessage());
315             }
316
317             /*
318              * Store the raw read value, checks the payload type and set the
319              * payloadClass accordingly
320              */
321             this.setHeaderField(hdrField, hdrFieldBytes);
322
323             if (LOG.isTraceEnabled()) {
324                 LOG.trace("{}: {}: {} (offset {} bitsize {})",
325                         this.getClass().getSimpleName(), hdrField,
326                         HexEncode.bytesToHexString(hdrFieldBytes),
327                         startOffset, numBits);
328             }
329         }
330
331         // Deserialize the payload now
332         int payloadStart = startOffset + numBits;
333         int payloadSize = data.length * Byte.SIZE - payloadStart;
334
335         if (payloadFactory != null) {
336             payload = payloadFactory.get();
337             payload.deserialize(data, payloadStart, payloadSize);
338             payload.setParent(this);
339         } else {
340             /*
341              *  The payload class was not set, it means no class for parsing
342              *  this payload is present. Let's store the raw payload if any.
343              */
344             int start = payloadStart / Byte.SIZE;
345             int stop = start + payloadSize / Byte.SIZE;
346             rawPayload = Arrays.copyOfRange(data, start, stop);
347         }
348         // Take care of computation that can be done only after deserialization
349         postDeserializeCustomOperation(data, payloadStart - getHeaderSize());
350
351         return this;
352     }
353
354     @Override
355     public byte[] serialize() throws PacketException {
356         this.setOptions(this.dhcpOptions.serialize());
357         byte[] data = super.serialize();
358         // Check for OPT_END at end of options
359         if (data.length > DHCP_MAX_SIZE) {
360             // shouldn't have happened
361             // Add exception?
362             LOG.error("DHCP Packet too big");
363         } else if (data[data.length - 1] != (byte)255) {
364             // DHCP Options not ended properly
365             //throw new PacketException("Missing DHCP Option END");
366             LOG.error("Missing DHCP Option END");
367         } else if (data.length < DHCP_MIN_SIZE) {
368             byte[] padding = new byte[DHCP_MIN_SIZE - data.length];
369             LOG.debug("DHCP Pkt too small: {}, padding added {}",
370                     data.length, padding.length);
371             data = ArrayUtils.addAll(data, padding);
372         }
373         return data;
374     }
375
376     @Override
377     /**
378      * Gets the number of bits for the fieldname specified
379      * If the fieldname has variable length like "Options", then this value is computed using the header length
380      * @param fieldname - String
381      * @return number of bits for fieldname - int
382      */
383     public int getfieldnumBits(String fieldName) {
384         if (fieldName.equals(OPTIONS)) {
385             byte[] barr = fieldValues.get(OPTIONS);
386             return (barr.length) * Byte.SIZE;
387         }
388         return hdrFieldCoordMap.get(fieldName).getRight();
389     }
390
391     @Override
392     public int getHeaderSize() {
393         byte[] barr = fieldValues.get(OPTIONS);
394         int len = 0;
395         if (barr != null) {
396             len = barr.length;
397         }
398         return (DHCP_NOOPT_HDR_SIZE + len) * 8;
399     }
400
401     @Override
402     protected void postDeserializeCustomOperation(byte[] data, int startBitOffset) {
403         //TODO: Anything need to be done here?
404         // Check for MAGIC_COOKIE. This means we only support DHCP, not BOOTP
405         int cookie = BitBufferHelper.getInt(fieldValues.get(MCOOKIE));
406         if (cookie != MAGIC_COOKIE) {
407             LOG.debug("Not DHCP packet");
408             // Throw exception?
409         }
410         // parse options into DHCPOptions
411         this.dhcpOptions.deserialize(this.getOptions());
412         // reset options byte array, this will also drop padding
413         this.setOptions(this.dhcpOptions.serialize());
414     }
415
416     // Set/get operations for Options
417     public void setMsgType(byte type) {
418         dhcpOptions.setOptionByte(OPT_MESSAGE_TYPE, type);
419     }
420
421     public byte getMsgType() {
422         return dhcpOptions.getOptionByte(OPT_MESSAGE_TYPE);
423     }
424
425     public void setOptionBytes(byte code, byte[] opt) {
426         dhcpOptions.setOption(code, opt);
427     }
428
429     public byte[] getOptionBytes(byte code) {
430         return dhcpOptions.getOptionBytes(code);
431     }
432
433     public void setOptionInt(byte code, int opt) {
434         dhcpOptions.setOptionInt(code, opt);
435     }
436
437     public InetAddress getOptionInetAddr(byte code) {
438         return dhcpOptions.getOptionInetAddr(code);
439     }
440
441     public void setOptionInetAddr(byte code, String addr) throws UnknownHostException {
442         dhcpOptions.setOptionStrAddr(code, addr);
443     }
444
445     public void setOptionStrAddrs(byte code, List<String> opt) throws UnknownHostException {
446         dhcpOptions.setOptionStrAddrs(code, opt);
447     }
448
449     public void setOptionString(byte code, String str) {
450         dhcpOptions.setOptionString(code, str);
451     }
452
453     public boolean containsOption(byte code) {
454         // TODO Auto-generated method stub
455         return dhcpOptions.containsOption(code);
456     }
457
458     public void unsetOption(byte code) {
459         dhcpOptions.unsetOption(code);
460     }
461
462     @Override
463     public String toString() {
464         StringBuilder ret = new StringBuilder();
465         ret.append(super.toString()).append(dhcpOptions);
466
467         return ret.toString();
468     }
469 }