1 // Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #ifndef PKT4_H
8 #define PKT4_H
9 
10 #include <asiolink/io_address.h>
11 #include <dhcp/duid.h>
12 #include <util/buffer.h>
13 #include <dhcp/option.h>
14 #include <dhcp/classify.h>
15 #include <dhcp/pkt.h>
16 
17 #include <boost/shared_ptr.hpp>
18 
19 #include <iostream>
20 #include <vector>
21 #include <set>
22 #include <list>
23 
24 #include <time.h>
25 
26 namespace isc {
27 
28 namespace dhcp {
29 
30 /// @brief Represents DHCPv4 packet
31 ///
32 /// This class represents a single DHCPv4 packet. It handles both incoming
33 /// and transmitted packets, parsing incoming options, options handling
34 /// (add, get, remove), on-wire assembly, sanity checks and other operations.
35 /// This specific class has several DHCPv4-specific methods, but it uses a lot
36 /// of common operations from its base @c Pkt class that is shared with Pkt6.
37 class Pkt4 : public Pkt {
38 public:
39 
40     /// length of the CHADDR field in DHCPv4 message
41     const static size_t MAX_CHADDR_LEN = 16;
42 
43     /// length of the SNAME field in DHCPv4 message
44     const static size_t MAX_SNAME_LEN = 64;
45 
46     /// length of the FILE field in DHCPv4 message
47     const static size_t MAX_FILE_LEN = 128;
48 
49     /// specifies DHCPv4 packet header length (fixed part)
50     const static size_t DHCPV4_PKT_HDR_LEN = 236;
51 
52     /// Mask for the value of flags field in the DHCPv4 message
53     /// to check whether client requested broadcast response.
54     const static uint16_t FLAG_BROADCAST_MASK = 0x8000;
55 
56     /// Constructor, used in replying to a message.
57     ///
58     /// @param msg_type type of message (e.g. DHCPDISOVER=1)
59     /// @param transid transaction-id
60     Pkt4(uint8_t msg_type, uint32_t transid);
61 
62     /// @brief Constructor, used in message reception.
63     ///
64     /// Creates new message. Pkt4 will copy data to bufferIn_
65     /// buffer on creation.
66     ///
67     /// @param data pointer to received data
68     /// @param len size of buffer to be allocated for this packet.
69     Pkt4(const uint8_t* data, size_t len);
70 
71     /// @brief Prepares on-wire format of DHCPv4 packet.
72     ///
73     /// Prepares on-wire format of message and all its options.
74     /// Options must be stored in options_ field.
75     /// Output buffer will be stored in buffer_out_.
76     /// The buffer_out_ is cleared before writing to the buffer.
77     ///
78     /// @throw InvalidOperation if packing fails
79     virtual void pack();
80 
81     /// @brief Parses on-wire form of DHCPv4 packet.
82     ///
83     /// Parses received packet, stored in on-wire format in bufferIn_.
84     ///
85     /// Will create a collection of option objects that will
86     /// be stored in options_ container.
87     ///
88     /// Method with throw exception if packet parsing fails.
89     virtual void unpack();
90 
91     /// @brief Returns text representation of the primary packet identifiers
92     ///
93     /// This method is intended to be used to provide a consistent way to
94     /// identify packets within log statements.  It is an instance-level
95     /// wrapper around static makeLabel(). See this method for string
96     /// content.
97     ///
98     /// This method is exception safe.
99     ///
100     /// @return string with text representation
101     std::string getLabel() const;
102 
103     /// @brief Returns text representation of the given packet identifiers
104     ///
105     /// @param hwaddr - hardware address to include in the string, it may be
106     /// NULL.
107     /// @param client_id - client id to include in the string, it may be NULL.
108     /// to include in the string
109     /// @param transid - numeric transaction id to include in the string
110     ///
111     /// @return string with text representation
112     static std::string makeLabel(const HWAddrPtr& hwaddr,
113                                  const ClientIdPtr& client_id,
114                                  const uint32_t transid);
115 
116     /// @brief Returns text representation of the given packet identifiers.
117     ///
118     /// This variant of the method does not include transaction id.
119     ///
120     /// @param hwaddr hardware address to include in the string, it may be
121     /// NULL.
122     /// @param client_id client id to include in the string, it may be NULL.
123     static std::string makeLabel(const HWAddrPtr& hwaddr, const ClientIdPtr& client_id);
124 
125     /// @brief Returns text representation of the packet.
126     ///
127     /// This function is useful mainly for debugging.
128     ///
129     /// @return string with text representation
130     std::string toText() const;
131 
132     /// @brief Returns the size of the required buffer to build the packet.
133     ///
134     /// Returns the size of the required buffer to build the packet with
135     /// the current set of packet options.
136     ///
137     /// @return number of bytes required to build this packet
138     size_t len();
139 
140     /// @brief Sets hops field.
141     ///
142     /// @param hops value to be set
setHops(uint8_t hops)143     void setHops(uint8_t hops) { hops_ = hops; };
144 
145     /// @brief Returns hops field.
146     ///
147     /// @return hops field
getHops()148     uint8_t getHops() const { return (hops_); };
149 
150     // Note: There's no need to manipulate OP field directly,
151     // thus no setOp() method. See op_ comment.
152 
153     /// @brief Returns op field.
154     ///
155     /// @return op field
getOp()156     uint8_t getOp() const { return (op_); };
157 
158     /// @brief Sets secs field.
159     ///
160     /// @param secs value to be set
setSecs(uint16_t secs)161     void setSecs(uint16_t secs) { secs_ = secs; };
162 
163     /// @brief Returns secs field.
164     ///
165     /// @return secs field
getSecs()166     uint16_t getSecs() const { return (secs_); };
167 
168     /// @brief Sets flags field.
169     ///
170     /// @param flags value to be set
setFlags(uint16_t flags)171     void setFlags(uint16_t flags) { flags_ = flags; };
172 
173     /// @brief Returns flags field.
174     ///
175     /// @return flags field
getFlags()176     uint16_t getFlags() const { return (flags_); };
177 
178     /// @brief Returns ciaddr field.
179     ///
180     /// @return ciaddr field
181     const isc::asiolink::IOAddress&
getCiaddr()182     getCiaddr() const { return (ciaddr_); };
183 
184     /// @brief Sets ciaddr field.
185     ///
186     /// @param ciaddr value to be set
187     void
setCiaddr(const isc::asiolink::IOAddress & ciaddr)188     setCiaddr(const isc::asiolink::IOAddress& ciaddr) { ciaddr_ = ciaddr; };
189 
190     /// @brief Returns siaddr field.
191     ///
192     /// @return siaddr field
193     const isc::asiolink::IOAddress&
getSiaddr()194     getSiaddr() const { return (siaddr_); };
195 
196     /// @brief Sets siaddr field.
197     ///
198     /// @param siaddr value to be set
199     void
setSiaddr(const isc::asiolink::IOAddress & siaddr)200     setSiaddr(const isc::asiolink::IOAddress& siaddr) { siaddr_ = siaddr; };
201 
202     /// @brief Returns yiaddr field.
203     ///
204     /// @return yiaddr field
205     const isc::asiolink::IOAddress&
getYiaddr()206     getYiaddr() const { return (yiaddr_); };
207 
208     /// @brief Sets yiaddr field.
209     ///
210     /// @param yiaddr value to be set
211     void
setYiaddr(const isc::asiolink::IOAddress & yiaddr)212     setYiaddr(const isc::asiolink::IOAddress& yiaddr) { yiaddr_ = yiaddr; };
213 
214     /// @brief Returns giaddr field.
215     ///
216     /// @return giaddr field
217     const isc::asiolink::IOAddress&
getGiaddr()218     getGiaddr() const { return (giaddr_); };
219 
220     /// @brief Sets giaddr field.
221     ///
222     /// @param giaddr value to be set
223     void
setGiaddr(const isc::asiolink::IOAddress & giaddr)224     setGiaddr(const isc::asiolink::IOAddress& giaddr) { giaddr_ = giaddr; };
225 
226     /// @brief Returns DHCP message type (e.g. 1 = DHCPDISCOVER).
227     ///
228     /// This method is exception safe. For packets without DHCP Message Type
229     /// option, it returns DHCP_NOTYPE (0).
230     ///
231     /// @return message type
232     uint8_t getType() const;
233 
234     /// @brief Sets DHCP message type (e.g. 1 = DHCPDISCOVER).
235     ///
236     /// @param type message type to be set
237     void setType(uint8_t type);
238 
239     /// @brief Returns name of the DHCP message for a given type number.
240     ///
241     /// This method is exception safe. For messages without DHCP Message Type
242     /// options, it returns UNKNOWN.
243     ///
244     /// @param type DHCPv4 message type which name should be returned.
245     ///
246     /// @return Pointer to the "const" string containing DHCP message name.
247     /// If the message type is unsupported the "UNKNOWN" is returned.
248     /// The caller must not release the returned pointer.
249     static const char* getName(const uint8_t type);
250 
251     /// @brief Returns name of the DHCP message.
252     ///
253     /// @return Pointer to the "const" string containing DHCP message name.
254     /// If the message type is unsupported the "UNKNOWN" is returned.
255     /// The caller must not release the returned pointer.
256     const char* getName() const;
257 
258     /// @brief Returns sname field
259     ///
260     /// Note: This is 64 bytes long field. It doesn't have to be
261     /// null-terminated. Do not use strlen() or similar on it.
262     ///
263     /// @return sname field
264     const OptionBuffer
getSname()265     getSname() const { return (std::vector<uint8_t>(sname_, &sname_[MAX_SNAME_LEN])); };
266 
267     /// @brief Sets sname field.
268     ///
269     /// @param sname value to be set
270     /// @param sname_len length of the sname buffer (up to MAX_SNAME_LEN)
271     void setSname(const uint8_t* sname, size_t sname_len);
272 
273     /// @brief Returns file field
274     ///
275     /// Note: This is 128 bytes long field. It doesn't have to be
276     /// null-terminated. Do not use strlen() or similar on it.
277     ///
278     /// @return pointer to file field
279     const OptionBuffer
getFile()280     getFile() const { return (std::vector<uint8_t>(file_, &file_[MAX_FILE_LEN])); };
281 
282     /// Sets file field
283     ///
284     /// @param file value to be set
285     /// @param file_len length of the file buffer (up to MAX_FILE_LEN)
286     void
287     setFile(const uint8_t* file, size_t file_len);
288 
289     /// @brief Sets hardware address.
290     ///
291     /// Sets parameters of hardware address. hlen specifies
292     /// length of mac_addr buffer. Content of mac_addr buffer
293     /// will be copied to appropriate field.
294     ///
295     /// Note: mac_addr must be a buffer of at least hlen bytes.
296     ///
297     /// @param htype hardware type (will be sent in htype field)
298     /// @param hlen hardware length (will be sent in hlen field)
299     /// @param mac_addr pointer to hardware address
300     void setHWAddr(uint8_t htype, uint8_t hlen,
301                    const std::vector<uint8_t>& mac_addr);
302 
303     /// @brief Sets hardware address
304     ///
305     /// Sets hardware address, based on existing HWAddr structure
306     /// @param addr already filled in HWAddr structure
307     /// @throw BadValue if addr is null
308     void setHWAddr(const HWAddrPtr& addr);
309 
310     /// Returns htype field
311     ///
312     /// @return hardware type
313     uint8_t
314     getHtype() const;
315 
316     /// Returns hlen field
317     ///
318     /// @return hardware address length
319     uint8_t
320     getHlen() const;
321 
322     /// @brief returns hardware address information
323     /// @return hardware address structure
getHWAddr()324     HWAddrPtr getHWAddr() const { return (hwaddr_); }
325 
326     /// @brief Add an option.
327     ///
328     /// @throw BadValue if option with that type is already present.
329     ///
330     /// @param opt option to be added
331     virtual void
332     addOption(const OptionPtr& opt);
333 
334     /// @brief Sets local HW address.
335     ///
336     /// Sets the source HW address for the outgoing packet or
337     /// destination HW address for the incoming packet.
338     ///
339     /// @note mac_addr must be a buffer of at least hlen bytes.
340     ///
341     /// @param htype hardware type (will be sent in htype field)
342     /// @param hlen hardware length (will be sent in hlen field)
343     /// @param mac_addr pointer to hardware address
344     void setLocalHWAddr(const uint8_t htype, const uint8_t hlen,
345                         const std::vector<uint8_t>& mac_addr);
346 
347     /// @brief Sets local HW address.
348     ///
349     /// Sets hardware address from an existing HWAddr structure.
350     /// The local address is a source address for outgoing
351     /// packet and destination address for incoming packet.
352     ///
353     /// @param addr structure representing HW address.
354     ///
355     /// @throw BadValue if addr is null
356     void setLocalHWAddr(const HWAddrPtr& addr);
357 
358     /// @brief Returns local HW address.
359     ///
360     /// @return local HW addr.
getLocalHWAddr()361     HWAddrPtr getLocalHWAddr() const {
362         return (local_hwaddr_);
363     }
364 
365     /// @brief Returns a reference to option codes which unpacking
366     /// will be deferred.
367     ///
368     /// Only options 43 and 224-254 are subject of deferred
369     /// unpacking: when the packet unpacking is performed, each time
370     /// such an option is found, it is unpacked as an unknown option
371     /// and the code added in this list.
372     ///
373     /// @return List of codes of options which unpacking is deferred.
getDeferredOptions()374     std::list<uint16_t>& getDeferredOptions() {
375         return (deferred_options_);
376     }
377 
378     /// @brief Checks if a DHCPv4 message has been relayed.
379     ///
380     /// This function returns a boolean value which indicates whether a DHCPv4
381     /// message has been relayed (if true is returned) or not (if false).
382     ///
383     /// The message is considered relayed if the giaddr field is non-zero and
384     /// non-broadcast.
385     ///
386     /// @return Boolean value which indicates whether the message is relayed
387     /// (true) or non-relayed (false).
388     bool isRelayed() const;
389 
390     /// @brief Checks if a DHCPv4 message has been transported over DHCPv6
391     ///
392     /// @return Boolean value which indicates whether the message is
393     /// transported over DHCPv6 (true) or native DHCPv4 (false)
isDhcp4o6()394     virtual bool isDhcp4o6() const {
395         return (false);
396     }
397 
398 private:
399 
400     /// @brief Generic method that validates and sets HW address.
401     ///
402     /// This is a generic method used by all modifiers of this class
403     /// which set class members representing HW address.
404     ///
405     /// @param htype hardware type.
406     /// @param hlen hardware length.
407     /// @param mac_addr pointer to actual hardware address.
408     /// @param [out] hw_addr pointer to a class member to be modified.
409     ///
410     /// @trow isc::OutOfRange if invalid HW address specified.
411     virtual void setHWAddrMember(const uint8_t htype, const uint8_t hlen,
412                                  const std::vector<uint8_t>& mac_addr,
413                                  HWAddrPtr& hw_addr);
414 
415 protected:
416 
417     /// converts DHCP message type to BOOTP op type
418     ///
419     /// @param dhcpType DHCP message type (e.g. DHCPDISCOVER)
420     ///
421     /// @return BOOTP type (BOOTREQUEST or BOOTREPLY)
422     uint8_t
423     DHCPTypeToBootpType(uint8_t dhcpType);
424 
425     /// @brief No-op
426     ///
427     /// This method returns hardware address generated from the IPv6 link-local
428     /// address. As there is no IPv4-equivalent, it always returns NULL.
429     /// We need this stub implementation here, to keep all the get hardware
430     /// address logic in the base class.
431     ///
432     /// @return always NULL
getMACFromSrcLinkLocalAddr()433     virtual HWAddrPtr getMACFromSrcLinkLocalAddr() {
434         return (HWAddrPtr());
435     }
436 
437     /// @brief No-op
438     ///
439     /// This method returns hardware address extracted from an IPv6 relay agent.
440     /// option. As there is no IPv4-equivalent, it always returns NULL.
441     /// We need this stub implementation here, to keep all the get hardware
442     /// address logic in the base class.
443     ///
444     /// @return always NULL
getMACFromIPv6RelayOpt()445     virtual HWAddrPtr getMACFromIPv6RelayOpt() {
446         return (HWAddrPtr());
447     }
448 
449     /// @brief No-op
450     ///
451     /// This is a DHCPv4 version of the function that attempts to extract
452     /// MAC address from the options inserted by a cable modem. It is currently
453     /// not implemented for v4.
454     ///
455     /// @return always NULL
getMACFromDocsisModem()456     virtual HWAddrPtr getMACFromDocsisModem() {
457         return (HWAddrPtr());
458     }
459 
460     /// @brief No-op
461     ///
462     /// This method returns hardware address extracted from DUID.
463     /// Currently it is a no-op, even though there's RFC that defines how to
464     /// use DUID in DHCPv4 (see RFC4361). We may implement it one day.
465     ///
466     /// @return always NULL
getMACFromDUID()467     virtual HWAddrPtr getMACFromDUID(){
468         return (HWAddrPtr());
469     }
470 
471     /// @brief No-op
472     ///
473     /// This is a DHCPv4 version of the function that attempts to extract
474     /// MAC address from the options inserted by a CMTS. It is currently
475     /// not implemented for v4.
476     ///
477     /// @return always NULL
getMACFromDocsisCMTS()478     virtual HWAddrPtr getMACFromDocsisCMTS() {
479         return (HWAddrPtr());
480     }
481 
482     /// @brief No-op
483     ///
484     /// This method returns hardware address extracted from remote-id relay option.
485     /// Currently it is a no-op, it always returns NULL.
486     ///
487     /// @return always NULL
getMACFromRemoteIdRelayOption()488     virtual HWAddrPtr getMACFromRemoteIdRelayOption(){
489         return(HWAddrPtr());
490     }
491 
492     /// @brief local HW address (dst if receiving packet, src if sending packet)
493     HWAddrPtr local_hwaddr_;
494 
495     // @brief List of deferred option codes
496     std::list<uint16_t> deferred_options_;
497 
498     /// @brief message operation code
499     ///
500     /// Note: This is legacy BOOTP field. There's no need to manipulate it
501     /// directly. Its value is set based on DHCP message type. Note that
502     /// DHCPv4 protocol reuses BOOTP message format, so this field is
503     /// kept due to BOOTP format. This is NOT DHCPv4 type (DHCPv4 message
504     /// type is kept in message type option).
505     uint8_t op_;
506 
507     /// @brief link-layer address and hardware information
508     /// represents 3 fields: htype (hardware type, 1 byte), hlen (length of the
509     /// hardware address, up to 16) and chaddr (hardware address field,
510     /// 16 bytes)
511     HWAddrPtr hwaddr_;
512 
513     /// Number of relay agents traversed
514     uint8_t hops_;
515 
516     /// elapsed (number of seconds since beginning of transmission)
517     uint16_t secs_;
518 
519     /// flags
520     uint16_t flags_;
521 
522     /// ciaddr field (32 bits): Client's IP address
523     isc::asiolink::IOAddress ciaddr_;
524 
525     /// yiaddr field (32 bits): Client's IP address ("your"), set by server
526     isc::asiolink::IOAddress yiaddr_;
527 
528     /// siaddr field (32 bits): next server IP address in boot process(e.g.TFTP)
529     isc::asiolink::IOAddress siaddr_;
530 
531     /// giaddr field (32 bits): Gateway IP address
532     isc::asiolink::IOAddress giaddr_;
533 
534     /// sname field (64 bytes)
535     uint8_t sname_[MAX_SNAME_LEN];
536 
537     /// file field (128 bytes)
538     uint8_t file_[MAX_FILE_LEN];
539 
540     // end of real DHCPv4 fields
541 }; // Pkt4 class
542 
543 /// @brief A pointer to Pkt4 object.
544 typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
545 
546 } // isc::dhcp namespace
547 
548 } // isc namespace
549 
550 #endif
551