a735ba82c9f2f943ee516c52f73cdc2c080733a4
[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 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;
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 Map<String, Pair<Integer, Integer>> fieldCoordinates =
61             new LinkedHashMap<String, Pair<Integer, Integer>>() {
62         private static final long serialVersionUID = 1L;
63         {
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));
80         }
81     };
82
83     private final Map<String, byte[]> fieldValues;
84
85     public DHCP() {
86         this(false);
87     }
88
89     public DHCP(boolean writeAccess) {
90         super(writeAccess);
91         fieldValues = new HashMap<>();
92         hdrFieldCoordMap = fieldCoordinates;
93         hdrFieldsMap = fieldValues;
94         corrupted = false;
95
96         setOp(BOOTREPLY);
97         setHtype(HTYPE_ETHER);
98         setHlen((byte)6);
99         setHops((byte)0);
100         setXid(0);
101         setSecs((short) 0);
102         setFlags((short) 0);
103         setCiaddr(0);
104         setYiaddr(0);
105         setSiaddr(0);
106         setGiaddr(0);
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();
113     }
114
115     //Getters
116     public byte getOp() {
117         return (BitBufferHelper.getByte(fieldValues.get(OP)));
118     }
119
120     public byte getHtype() {
121         return (BitBufferHelper.getByte(fieldValues.get(HTYPE)));
122     }
123
124     public byte getHlen() {
125         return (BitBufferHelper.getByte(fieldValues.get(HLEN)));
126     }
127
128     public byte getHops() {
129         return (BitBufferHelper.getByte(fieldValues.get(HOPS)));
130     }
131
132     public int getXid() {
133         return (BitBufferHelper.getInt(fieldValues.get(XID)));
134     }
135
136     public short getSecs() {
137         return (BitBufferHelper.getShort(fieldValues.get(SECS)));
138     }
139
140     public short getFlags() {
141         return (BitBufferHelper.getShort(fieldValues.get(FLAGS)));
142     }
143
144     public byte[] getCiaddr() {
145         return fieldValues.get(CIADDR);
146     }
147
148     public byte[] getYiaddr() {
149         return fieldValues.get(YIADDR);
150     }
151
152
153     public byte[] getSiaddr() {
154         return fieldValues.get(SIADDR);
155     }
156
157     public InetAddress getSiaddrAsInetAddr() {
158         return DHCPUtils.byteArrayToInetAddr(fieldValues.get(SIADDR));
159     }
160
161     public byte[] getGiaddr() {
162         return fieldValues.get(GIADDR);
163     }
164
165     public byte[] getChaddr() {
166         return fieldValues.get(CHADDR);
167     }
168
169     public byte[] getSname() {
170         return fieldValues.get(SNAME);
171     }
172
173     public byte[] getFile() {
174         return fieldValues.get(FILE);
175     }
176
177     public int getMCookie() {
178         return (BitBufferHelper.getInt(fieldValues.get(MCOOKIE)));
179     }
180
181     public byte[] getOptions() {
182         return fieldValues.get(OPTIONS);
183     }
184
185 //    TODO:
186 //    public byte[] getPadding() {
187 //        return this.pad;
188 //    }
189
190     // Setters
191     @Override
192     public void setHeaderField(String headerField, byte[] readValue) {
193         if (headerField.equals(OPTIONS) && (readValue == null || readValue.length == 0)) {
194             hdrFieldsMap.remove(headerField);
195             return;
196         }
197         hdrFieldsMap.put(headerField, readValue);
198     }
199
200     public DHCP setOp(byte dhcpOp) {
201         byte[] op = BitBufferHelper.toByteArray(dhcpOp);
202         fieldValues.put(OP, op);
203         return this;
204     }
205
206     public DHCP setHtype(byte dhcpHtype) {
207         byte[] htype = BitBufferHelper.toByteArray(dhcpHtype);
208         fieldValues.put(HTYPE, htype);
209         return this;
210     }
211
212     public DHCP setHlen(byte dhcpHlen) {
213         byte[] hlen = BitBufferHelper.toByteArray(dhcpHlen);
214         fieldValues.put(HLEN, hlen);
215         return this;
216     }
217
218     public DHCP setHops(byte dhcpHops) {
219         byte[] hops = BitBufferHelper.toByteArray(dhcpHops);
220         fieldValues.put(HOPS, hops);
221         return this;
222     }
223
224     public DHCP setXid(int dhcpXid) {
225         byte[] xid = BitBufferHelper.toByteArray(dhcpXid);
226         fieldValues.put(XID, xid);
227         return this;
228     }
229
230     public DHCP setSecs(short dhcpSecs) {
231         byte[] secs = BitBufferHelper.toByteArray(dhcpSecs);
232         fieldValues.put(SECS, secs);
233         return this;
234     }
235
236     public DHCP setFlags(short dhcpFlags) {
237         byte[] flags = BitBufferHelper.toByteArray(dhcpFlags);
238         fieldValues.put(FLAGS, flags);
239         return this;
240     }
241
242     public DHCP setCiaddr(byte[] ciaddr) {
243         fieldValues.put(CIADDR, ciaddr);
244         return this;
245     }
246
247     public DHCP setCiaddr(int dhcpCiaddr) {
248         byte[] ciaddr = BitBufferHelper.toByteArray(dhcpCiaddr);
249         fieldValues.put(CIADDR, ciaddr);
250         return this;
251     }
252
253     public DHCP setCiaddr(InetAddress dhcpCiaddr) {
254         byte[] ciaddr = dhcpCiaddr.getAddress();
255         fieldValues.put(CIADDR, ciaddr);
256         return this;
257     }
258
259     public DHCP setCiaddr(String dhcpCiaddr) {
260         byte[] ciaddr = NetUtils.parseInetAddress(dhcpCiaddr).getAddress();
261         fieldValues.put(CIADDR, ciaddr);
262         return this;
263     }
264
265     public DHCP setYiaddr(byte[] yiaddr) {
266         fieldValues.put(YIADDR, yiaddr);
267         return this;
268     }
269
270     public DHCP setYiaddr(int dhcpYiaddr) {
271         byte[] yiaddr = BitBufferHelper.toByteArray(dhcpYiaddr);
272         fieldValues.put(YIADDR, yiaddr);
273         return this;
274     }
275
276     public DHCP setYiaddr(InetAddress dhcpYiaddr) {
277         byte[] yiaddr = dhcpYiaddr.getAddress();
278         fieldValues.put(YIADDR, yiaddr);
279         return this;
280     }
281
282     public DHCP setYiaddr(String dhcpYiaddr) {
283         byte[] yiaddr = NetUtils.parseInetAddress(dhcpYiaddr).getAddress();
284         fieldValues.put(YIADDR, yiaddr);
285         return this;
286     }
287
288     public DHCP setSiaddr(byte[] siaddr) {
289         fieldValues.put(SIADDR, siaddr);
290         return this;
291     }
292
293     public DHCP setSiaddr(int dhcpSiaddr) {
294         byte[] siaddr = BitBufferHelper.toByteArray(dhcpSiaddr);
295         fieldValues.put(SIADDR, siaddr);
296         return this;
297     }
298
299     public DHCP setSiaddr(InetAddress dhcpSiaddr) {
300         byte[] siaddr = dhcpSiaddr.getAddress();
301         fieldValues.put(SIADDR, siaddr);
302         return this;
303     }
304
305     public DHCP setSiaddr(String dhcpSiaddr) {
306         byte[] siaddr = NetUtils.parseInetAddress(dhcpSiaddr).getAddress();
307         fieldValues.put(SIADDR, siaddr);
308         return this;
309     }
310
311     public DHCP setGiaddr(byte[] giaddr) {
312         fieldValues.put(GIADDR, giaddr);
313         return this;
314     }
315
316     public DHCP setGiaddr(int dhcpGiaddr) {
317         byte[] giaddr = BitBufferHelper.toByteArray(dhcpGiaddr);
318         fieldValues.put(GIADDR, giaddr);
319         return this;
320     }
321
322     public DHCP setGiaddr(InetAddress dhcpGiaddr) {
323         byte[] giaddr = dhcpGiaddr.getAddress();
324         fieldValues.put(GIADDR, giaddr);
325         return this;
326     }
327
328     public DHCP setGiaddr(String dhcpGiaddr) {
329         byte[] giaddr = NetUtils.parseInetAddress(dhcpGiaddr).getAddress();
330         fieldValues.put(GIADDR, giaddr);
331         return this;
332     }
333
334     public DHCP setChaddr(byte[] chaddr) {
335         fieldValues.put(CHADDR, chaddr);
336         return this;
337     }
338
339     public DHCP setSname(byte[] sname) {
340         fieldValues.put(SNAME, sname);
341         return this;
342     }
343
344     public DHCP setFile(byte[] file) {
345         fieldValues.put(FILE, file);
346         return this;
347     }
348
349     public DHCP setMcookie(int dhcpMc) {
350         byte[] mc = BitBufferHelper.toByteArray(dhcpMc);
351         fieldValues.put(MCOOKIE, mc);
352         return this;
353     }
354
355     public DHCP setOptions(byte[] options) {
356         fieldValues.put(OPTIONS, options);
357         return this;
358     }
359
360 //    public void setPadding(byte[] pad) {
361 //        this.pad = pad;
362 //    }
363
364     /**
365      * This method deserializes the data bits obtained from the wire into the
366      * respective header and payload which are of type Packet.
367      *
368      * @param data       byte[] data from wire to deserialize
369      * @param bitOffset  int    bit position where packet header starts in data
370      *        array
371      * @param size       int    size of packet in bits
372      * @return Packet
373      * @throws PacketException the packet deserialization failed
374      *
375      * <p>Note: Copied from org.opendaylight.controller.sal.packet.Packet</p>
376      */
377     @Override
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 {
382
383         // Deserialize the header fields one by one
384         int startOffset = 0;
385         int numBits = 0;
386         for (Entry<String, Pair<Integer, Integer>> pairs : hdrFieldCoordMap
387                 .entrySet()) {
388             String hdrField = pairs.getKey();
389             startOffset = bitOffset + this.getfieldOffset(hdrField);
390             if (hdrField.equals(OPTIONS)) {
391                 numBits = (size - DHCP_NOOPT_HDR_SIZE) * 8;
392             } else {
393                 numBits = this.getfieldnumBits(hdrField);
394             }
395             byte[] hdrFieldBytes = null;
396             try {
397                 hdrFieldBytes = BitBufferHelper.getBits(data, startOffset,
398                         numBits);
399             } catch (BufferException e) {
400                 throw new PacketException(e.getMessage());
401             }
402
403             /*
404              * Store the raw read value, checks the payload type and set the
405              * payloadClass accordingly
406              */
407             this.setHeaderField(hdrField, hdrFieldBytes);
408
409             if (LOG.isTraceEnabled()) {
410                 LOG.trace("{}: {}: {} (offset {} bitsize {})",
411                         this.getClass().getSimpleName(), hdrField,
412                         HexEncode.bytesToHexString(hdrFieldBytes),
413                         startOffset, numBits);
414             }
415         }
416
417         // Deserialize the payload now
418         int payloadStart = startOffset + numBits;
419         int payloadSize = data.length * Byte.SIZE - payloadStart;
420
421         if (payloadClass != null) {
422             try {
423                 payload = payloadClass.newInstance();
424             } catch (InstantiationException | IllegalAccessException e) {
425                 throw new RuntimeException(
426                         "Error parsing payload for Ethernet packet", e);
427             }
428             payload.deserialize(data, payloadStart, payloadSize);
429             payload.setParent(this);
430         } else {
431             /*
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.
434              */
435             int start = payloadStart / Byte.SIZE;
436             int stop = start + payloadSize / Byte.SIZE;
437             rawPayload = Arrays.copyOfRange(data, start, stop);
438         }
439         // Take care of computation that can be done only after deserialization
440         postDeserializeCustomOperation(data, payloadStart - getHeaderSize());
441
442         return this;
443     }
444
445     @Override
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
452             // Add exception?
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);
463         }
464         return data;
465     }
466
467     @Override
468     /**
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
473      */
474     public int getfieldnumBits(String fieldName) {
475         if (fieldName.equals(OPTIONS)) {
476             byte[] barr = fieldValues.get(OPTIONS);
477             return (barr.length) * Byte.SIZE;
478         }
479         return hdrFieldCoordMap.get(fieldName).getRight();
480     }
481
482     @Override
483     public int getHeaderSize() {
484         byte[] barr = fieldValues.get(OPTIONS);
485         int len = 0;
486         if (barr != null) {
487             len = barr.length;
488         }
489         return (DHCP_NOOPT_HDR_SIZE + len) * 8;
490     }
491
492     @Override
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");
499             // Throw exception?
500         }
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());
505     }
506
507     // Set/get operations for Options
508     public void setMsgType(byte type) {
509         dhcpOptions.setOptionByte(OPT_MESSAGE_TYPE, type);
510     }
511
512     public byte getMsgType() {
513         return dhcpOptions.getOptionByte(OPT_MESSAGE_TYPE);
514     }
515
516     public void setOptionByte(byte code, byte opt) {
517         dhcpOptions.setOptionByte(code, opt);
518     }
519
520     public byte getOptionByte(byte code) {
521         return dhcpOptions.getOptionByte(code);
522     }
523
524     public void setOptionBytes(byte code, byte[] opt) {
525         dhcpOptions.setOption(code, opt);
526     }
527
528     public byte[] getOptionBytes(byte code) {
529         return dhcpOptions.getOptionBytes(code);
530     }
531
532     public void setOptionShort(byte code, short opt) {
533         dhcpOptions.setOptionShort(code, opt);
534     }
535
536     public short getOptionShort(byte code) {
537         return dhcpOptions.getOptionShort(code);
538     }
539
540     public void setOptionInt(byte code, int opt) {
541         dhcpOptions.setOptionInt(code, opt);
542     }
543
544     public int getOptionInt(byte code) {
545         return dhcpOptions.getOptionInt(code);
546     }
547
548     public InetAddress getOptionInetAddr(byte code) {
549         return dhcpOptions.getOptionInetAddr(code);
550     }
551
552     public void setOptionInetAddr(byte code, InetAddress addr) {
553         dhcpOptions.setOptionInetAddr(code, addr);
554     }
555
556     public void setOptionInetAddr(byte code, String addr) throws UnknownHostException {
557         dhcpOptions.setOptionStrAddr(code, addr);
558     }
559
560     public String getOptionStrAddr(byte code) {
561         return dhcpOptions.getOptionStrAddr(code);
562     }
563
564     public void setOptionStrAddrs(byte code, List<String> opt) throws UnknownHostException {
565         dhcpOptions.setOptionStrAddrs(code, opt);
566     }
567
568     public void setOptionString(byte code, String str) {
569         dhcpOptions.setOptionString(code, str);
570     }
571
572     public boolean containsOption(byte code) {
573         // TODO Auto-generated method stub
574         return dhcpOptions.containsOption(code);
575     }
576
577     public void unsetOption(byte code) {
578         dhcpOptions.unsetOption(code);
579     }
580
581     @Override
582     public String toString() {
583         StringBuilder ret = new StringBuilder();
584         ret.append(super.toString()).append(dhcpOptions);
585
586         return ret.toString();
587     }
588 }