summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/pkt6.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/pkt6.h')
-rw-r--r--src/lib/dhcp/pkt6.h590
1 files changed, 590 insertions, 0 deletions
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
new file mode 100644
index 0000000..d7448b8
--- /dev/null
+++ b/src/lib/dhcp/pkt6.h
@@ -0,0 +1,590 @@
+// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef PKT6_H
+#define PKT6_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcp/option.h>
+#include <dhcp/pkt.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <iostream>
+#include <set>
+
+#include <time.h>
+
+namespace isc {
+
+namespace dhcp {
+
+class Pkt6;
+
+/// @brief A pointer to Pkt6 packet
+typedef boost::shared_ptr<Pkt6> Pkt6Ptr;
+
+/// @brief Represents a DHCPv6 packet
+///
+/// This class represents a single DHCPv6 packet. It handles both incoming
+/// and transmitted packets, parsing incoming options, options handling
+/// (add, get, remove), on-wire assembly, sanity checks and other operations.
+/// This specific class has several DHCPv6-specific methods, but it uses a lot
+/// of common operations from its base @c Pkt class that is shared with Pkt4.
+///
+/// This class also handles relayed packets. For example, a RELAY-FORW message
+/// with a SOLICIT inside will be represented as SOLICIT and the RELAY-FORW
+/// layers will be stored in relay_info_ vector.
+class Pkt6 : public Pkt {
+public:
+ /// specifies non-relayed DHCPv6 packet header length (over UDP)
+ const static size_t DHCPV6_PKT_HDR_LEN = 4;
+
+ /// specifies relay DHCPv6 packet header length (over UDP)
+ const static size_t DHCPV6_RELAY_HDR_LEN = 34;
+
+ /// DHCPv6 transport protocol
+ enum DHCPv6Proto {
+ UDP = 0, // most packets are UDP
+ TCP = 1 // there are TCP DHCPv6 packets (bulk leasequery, failover)
+ };
+
+ /// @brief defines relay search pattern
+ ///
+ /// Defines order in which options are searched in a message that
+ /// passed through multiple relays. RELAY_SEACH_FROM_CLIENT will
+ /// start search from the relay that was the closest to the client
+ /// (i.e. innermost in the encapsulated message, which also means
+ /// this was the first relay that forwarded packet received by the
+ /// server and this will be the last relay that will handle the
+ /// response that server sent towards the client.).
+ /// RELAY_SEARCH_FROM_SERVER is the opposite. This will be the
+ /// relay closest to the server (i.e. outermost in the encapsulated
+ /// message, which also means it was the last relay that relayed
+ /// the received message and will be the first one to process
+ /// server's response). RELAY_GET_FIRST will try to get option from
+ /// the first relay only (closest to the client), RELAY_GET_LAST will
+ /// try to get option form the last relay (closest to the server).
+ enum RelaySearchOrder {
+ RELAY_SEARCH_FROM_CLIENT = 1,
+ RELAY_SEARCH_FROM_SERVER = 2,
+ RELAY_GET_FIRST = 3,
+ RELAY_GET_LAST = 4
+ };
+
+ /// @brief structure that describes a single relay information
+ ///
+ /// Client sends messages. Each relay along its way will encapsulate the message.
+ /// This structure represents all information added by a single relay.
+ struct RelayInfo {
+
+ /// @brief default constructor
+ RelayInfo();
+
+ /// @brief Returns printable representation of the relay information.
+ /// @return text representation of the structure (used in debug logging)
+ std::string toText() const;
+
+ uint8_t msg_type_; ///< message type (RELAY-FORW oro RELAY-REPL)
+ uint8_t hop_count_; ///< number of traversed relays (up to 32)
+ isc::asiolink::IOAddress linkaddr_;///< fixed field in relay-forw/relay-reply
+ isc::asiolink::IOAddress peeraddr_;///< fixed field in relay-forw/relay-reply
+
+ /// @brief length of the relay_msg_len
+ /// Used when calculating length during pack/unpack
+ uint16_t relay_msg_len_;
+
+ /// options received from a specified relay, except relay-msg option
+ isc::dhcp::OptionCollection options_;
+ };
+
+ /// Constructor, used in replying to a message
+ ///
+ /// @param msg_type type of message (SOLICIT=1, ADVERTISE=2, ...)
+ /// @param transid transaction-id
+ /// @param proto protocol (TCP or UDP)
+ Pkt6(uint8_t msg_type,
+ uint32_t transid,
+ DHCPv6Proto proto = UDP);
+
+ /// Constructor, used in message transmission
+ ///
+ /// Creates new message. Transaction-id will randomized.
+ ///
+ /// @param buf pointer to a buffer of received packet content
+ /// @param len size of buffer of received packet content
+ /// @param proto protocol (usually UDP, but TCP will be supported eventually)
+ Pkt6(const uint8_t* buf, uint32_t len, DHCPv6Proto proto = UDP);
+
+ /// @brief Prepares on-wire format.
+ ///
+ /// Prepares on-wire format of message and all its options.
+ /// Options must be stored in options_ field.
+ /// Output buffer will be stored in data_. Length
+ /// will be set in data_len_.
+ /// The output buffer is cleared before new data is written to it.
+ ///
+ /// @throw BadValue if packet protocol is invalid, InvalidOperation
+ /// if packing fails, or NotImplemented if protocol is TCP (IPv6 over TCP is
+ /// not yet supported).
+ virtual void pack();
+
+ /// @brief Dispatch method that handles binary packet parsing.
+ ///
+ /// This method calls appropriate dispatch function (unpackUDP or
+ /// unpackTCP).
+ ///
+ /// @throw tbd
+ virtual void unpack();
+
+ /// @brief Returns protocol of this packet (UDP or TCP).
+ ///
+ /// @return protocol type
+ DHCPv6Proto getProto() {
+ return (proto_);
+ }
+
+ /// @brief Sets protocol of this packet.
+ ///
+ /// @param proto protocol (UDP or TCP)
+ void setProto(DHCPv6Proto proto = UDP) {
+ proto_ = proto;
+ }
+
+ /// @brief Returns text representation of the given packet identifiers.
+ ///
+ /// @note The parameters are ordered from the one that should be available
+ /// almost at all times, to the one that is optional. This allows for
+ /// providing default values for the parameters that may not be available
+ /// in some places in the code where @c Pkt6::makeLabel is called.
+ ///
+ /// @param duid Pointer to the client identifier or NULL.
+ /// @param transid Numeric transaction id to include in the string.
+ /// @param hwaddr Hardware address to include in the string or NULL.
+ ///
+ /// @return String with text representation of the packet identifiers.
+ static std::string makeLabel(const DuidPtr duid, const uint32_t transid,
+ const HWAddrPtr& hwaddr);
+
+ /// @brief Returns text representation of the given packet identifiers.
+ ///
+ /// This variant of the method does not include transaction id.
+ ///
+ /// @param duid Pointer to the client identifier or NULL.
+ /// @param hwaddr Hardware address to include in the string or NULL.
+ ///
+ /// @return String with text representation of the packet identifiers.
+ static std::string makeLabel(const DuidPtr duid, const HWAddrPtr& hwaddr);
+
+ /// @brief Returns text representation of the primary packet identifiers
+ ///
+ /// This method is intended to be used to provide a consistent way to
+ /// identify packets within log statements. It is an instance-level
+ /// wrapper around static makeLabel(). See this method for string
+ /// content.
+ ///
+ /// @note Currently this method doesn't include the HW address in the
+ /// returned text.
+ ///
+ /// @return string with text representation
+ virtual std::string getLabel() const;
+
+ /// @brief Returns text representation of the packet.
+ ///
+ /// This function is useful mainly for debugging.
+ ///
+ /// @return string with text representation
+ virtual std::string toText() const;
+
+ /// @brief Returns length of the packet.
+ ///
+ /// This function returns size required to hold this packet.
+ /// It includes DHCPv6 header and all options stored in
+ /// options_ field.
+ ///
+ /// Note: It does not return proper length of incoming packets
+ /// before they are unpacked.
+ ///
+ /// @return number of bytes required to assemble this packet
+ virtual size_t len();
+
+ /// @brief Returns message type (e.g. 1 = SOLICIT).
+ ///
+ /// @return message type
+ virtual uint8_t getType() const { return (msg_type_); }
+
+ /// @brief Sets message type (e.g. 1 = SOLICIT).
+ ///
+ /// @param type message type to be set
+ virtual void setType(uint8_t type) { msg_type_=type; };
+
+ /// @brief Retrieves the DUID from the Client Identifier option.
+ ///
+ /// This method is exception safe.
+ ///
+ /// @return Pointer to the DUID or NULL if the option doesn't exist.
+ DuidPtr getClientId() const;
+
+
+protected:
+
+ /// @brief Returns pointer to an option inserted by relay agent.
+ ///
+ /// This is a variant of the @ref Pkt6::getRelayOption function which
+ /// never copies an option returned. This method should be only used by
+ /// the @ref Pkt6 class and derived classes. Any external callers should
+ /// use @ref getRelayOption which copies the option before returning it
+ /// when the @ref Pkt::copy_retrieved_options_ flag is set to true.
+ ///
+ /// @param opt_type Code of the requested option.
+ /// @param relay_level Nesting level as described for
+ /// @ref Pkt6::getRelayOption.
+ ///
+ /// @return Pointer to the option or NULL if such option doesn't exist.
+ OptionPtr getNonCopiedRelayOption(const uint16_t opt_type,
+ const uint8_t relay_level) const;
+
+public:
+
+ /// @brief Returns option inserted by relay
+ ///
+ /// Returns an option from specified relay scope (inserted by a given relay
+ /// if this is received packet or to be decapsulated by a given relay if
+ /// this is a transmitted packet). nesting_level specifies which relay
+ /// scope is to be used. 0 is the outermost encapsulation (relay closest to
+ /// the server). pkt->relay_info_.size() - 1 is the innermost encapsulation
+ /// (relay closest to the client).
+ ///
+ /// @throw isc::OutOfRange if nesting level has invalid value.
+ ///
+ /// @param option_code code of the requested option
+ /// @param nesting_level see description above
+ ///
+ /// @return pointer to the option (or NULL if there is no such option)
+ OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level);
+
+private:
+
+ /// @brief Prepares parameters for loop used in @ref getAnyRelayOption
+ /// and @ref getNonCopiedAnyRelayOption.
+ ///
+ /// The methods retrieving "any" relay option iterate over the relay
+ /// info structures to find the matching option. This method returns
+ /// the index of the first and last relay info structure to be used
+ /// for this iteration. It also returns the direction in which the
+ /// iteration should be performed.
+ ///
+ /// @param order Option search order (see @ref RelaySearchOrder).
+ /// @param [out] start Index of the relay information structure from
+ /// which the search should be started.
+ /// @param [out] end Index of the relay information structure on which
+ /// the option searches should stop.
+ /// @param [out] direction Equals to -1 for backwards searches, and
+ /// equals to 1 for forward searches.
+ void prepareGetAnyRelayOption(const RelaySearchOrder& order,
+ int& start, int& end, int& direction) const;
+
+protected:
+
+ /// @brief Returns pointer to an instance of specified option.
+ ///
+ /// This is a variant of @ref getAnyRelayOption but it never copies
+ /// an option returned. This method should be only used by
+ /// the @ref Pkt6 class and derived classes. Any external callers should
+ /// use @ref getAnyRelayOption which copies the option before returning it
+ /// when the @ref Pkt::copy_retrieved_options_ flag is set to true.
+ ///
+ /// @param option_code Searched option.
+ /// @param order Option search order (see @ref RelaySearchOrder).
+ ///
+ /// @return Option pointer or NULL, if no option matches specified criteria.
+ OptionPtr getNonCopiedAnyRelayOption(const uint16_t option_code,
+ const RelaySearchOrder& order) const;
+
+public:
+
+ /// @brief Return first instance of a specified option
+ ///
+ /// When a client's packet traverses multiple relays, each passing relay may
+ /// insert extra options. This method allows the specific instance of a given
+ /// option to be obtained (e.g. closest to the client, closest to the server,
+ /// etc.) See @ref RelaySearchOrder for a detailed description.
+ ///
+ /// @param option_code searched option
+ /// @param order option search order (see @ref RelaySearchOrder)
+ /// @return option pointer (or NULL if no option matches specified criteria)
+ OptionPtr getAnyRelayOption(const uint16_t option_code,
+ const RelaySearchOrder& order);
+
+ /// @brief return the link address field from a relay option
+ ///
+ /// As with @c Pkt6::getRelayOption this returns information from the
+ /// specified relay scope. The relay_level specifies which relay
+ /// scope is to be used. 0 is the outermost encapsulation (relay closest
+ /// to the server). pkt->relay_info_.size() -1 is the innermost encapsulation
+ /// (relay closest to the client).
+ ///
+ /// @throw isc::OutOfRange if relay level has an invalid value.
+ ///
+ /// @param relay_level see description above
+ ///
+ /// @return pointer to the link address field
+ const isc::asiolink::IOAddress&
+ getRelay6LinkAddress(uint8_t relay_level) const;
+
+ /// @brief return the peer address field from a relay option
+ ///
+ /// As with @c Pkt6::getRelayOption this returns information from the
+ /// specified relay scope. The relay_level specifies which relay
+ /// scope is to be used. 0 is the outermost encapsulation (relay closest
+ /// to the server). pkt->relay_info_.size() -1 is the innermost encapsulation
+ /// (relay closest to the client).
+ ///
+ /// @throw isc::OutOfRange if relay level has an invalid value.
+ ///
+ /// @param relay_level see description above
+ ///
+ /// @return pointer to the peer address field
+ const isc::asiolink::IOAddress&
+ getRelay6PeerAddress(uint8_t relay_level) const;
+
+protected:
+
+ /// @brief Returns all option instances of specified type without
+ /// copying.
+ ///
+ /// This is a variant of @ref getOptions method, which returns a collection
+ /// of options without copying them. This method should be only used by
+ /// the @ref Pkt6 class and derived classes. Any external callers should
+ /// use @ref getOptions which copies option instances before returning them
+ /// when the @ref Pkt::copy_retrieved_options_ flag is set to true.
+ ///
+ /// @param opt_type Option code.
+ ///
+ /// @return Collection of options found.
+ OptionCollection getNonCopiedOptions(const uint16_t opt_type) const;
+
+public:
+
+ /// @brief Returns all instances of specified type.
+ ///
+ /// Returns all instances of options of the specified type. DHCPv6 protocol
+ /// allows (and uses frequently) multiple instances.
+ ///
+ /// @param type option type we are looking for
+ /// @return instance of option collection with requested options
+ isc::dhcp::OptionCollection getOptions(const uint16_t type);
+
+ /// @brief add information about one traversed relay
+ ///
+ /// This adds information about one traversed relay, i.e.
+ /// one relay-forw or relay-repl level of encapsulation.
+ ///
+ /// @param relay structure with necessary relay information
+ void addRelayInfo(const RelayInfo& relay);
+
+ /// @brief Returns name of the DHCPv6 message for a given type number.
+ ///
+ /// As the operation of the method does not depend on any server state, it
+ /// is declared static. There is also non-static getName() method that
+ /// works on Pkt6 objects.
+ ///
+ /// @param type DHCPv6 message type which name should be returned.
+ ///
+ /// @return Pointer to "const" string containing the message name. If
+ /// the message type is unknown the "UNKNOWN" is returned. The caller
+ /// must not release the returned pointer.
+ static const char* getName(const uint8_t type);
+
+ /// @brief Returns name of the DHCPv6 message.
+ ///
+ /// This method requires an object. There is also a static version, which
+ /// requires one parameter (type).
+ ///
+ /// @return Pointer to "const" string containing the message name. If
+ /// the message type is unknown the "UNKNOWN" is returned. The caller
+ /// must not release the returned pointer.
+ const char* getName() const;
+
+ /// @brief copies relay information from client's packet to server's response
+ ///
+ /// This information is not simply copied over. Some parameter are
+ /// removed, msg_type_is updated (RELAY-FORW => RELAY-REPL), etc.
+ ///
+ /// @param question client's packet
+ void copyRelayInfo(const Pkt6Ptr& question);
+
+ /// @brief Relay information.
+ ///
+ /// This is a public field. Otherwise we hit one of the two problems:
+ /// we return reference to an internal field (and that reference could
+ /// be potentially used past Pkt6 object lifetime causing badness) or
+ /// we return a copy (which is inefficient and also causes any updates
+ /// to be impossible). Therefore public field is considered the best
+ /// (or least bad) solution.
+ ///
+ /// This vector is arranged in the order packet is encapsulated, i.e.
+ /// relay[0] was the outermost encapsulation (relay closest to the server),
+ /// relay[last] was the innermost encapsulation (relay closest to the
+ /// client).
+ std::vector<RelayInfo> relay_info_;
+
+protected:
+
+ /// @brief Attempts to generate MAC/Hardware address from IPv6 link-local
+ /// address.
+ ///
+ /// This method uses source IPv6 address for direct messages and the
+ /// peeraddr or the first relay that saw that packet. It may fail if the
+ /// address is not link-local or does not use EUI-64 identifier.
+ ///
+ /// @return Hardware address (or NULL)
+ virtual HWAddrPtr getMACFromSrcLinkLocalAddr();
+
+ /// @brief Extract MAC/Hardware address from client link-layer address
+ // option inserted by a relay agent (RFC6939).
+ ///
+ /// This method extracts the client's hardware address from the
+ // client-linklayer-addr option inserted by the relay agent closest to
+ // the client.
+ ///
+ /// @return Hardware address (or NULL)
+ virtual HWAddrPtr getMACFromIPv6RelayOpt();
+
+ /// @brief Extract MAC/Hardware address from client-id.
+ ///
+ /// This method attempts to extract MAC/Hardware address from DUID sent
+ /// as client-id. This method may fail, as only DUID-LLT and DUID-LL are
+ /// based on link-layer addresses. Client may use other valid DUID types
+ /// and this method will fail.
+ ///
+ /// @return Hardware address (or NULL)
+ virtual HWAddrPtr getMACFromDUID();
+
+ /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
+ /// inserted by the modem itself.
+ ///
+ /// The mechanism extracts that information from DOCSIS option
+ /// (vendor-specific info, vendor-id=4491, suboption 36). Note that
+ /// in a DOCSIS capable network, the MAC address information is provided
+ /// several times. The first is specified by the modem itself. The second
+ /// is added by the CMTS, which acts as a relay agent. This method
+ /// attempts to extract the former. See @ref getMACFromDocsisCMTS
+ /// for a similar method that extracts from the CMTS (relay) options.
+ ///
+ /// @return hardware address (if DOCSIS suboption 36 is present)
+ virtual HWAddrPtr getMACFromDocsisModem();
+
+ /// @brief Attempts to extract MAC/Hardware address from DOCSIS options.
+ ///
+ /// The DHCPv6 mechanism extracts that information from DOCSIS option
+ /// (vendor-specific info, vendor-id=4491, suboption 1026). Note that
+ /// in a DOCSIS capable network, the MAC address information is provided
+ /// several times. The first is specified by the modem itself. The second
+ /// is added by the CMTS, which acts as a relay agent. This method
+ /// attempts to extract the latter. See @ref getMACFromDocsisModem
+ /// for a similar method that extracts from the modem (client) options.
+ ///
+ /// @return hardware address (if DOCSIS suboption 1026 is present)
+ virtual HWAddrPtr getMACFromDocsisCMTS();
+
+ /// @brief Attempts to obtain MAC address from remote-id relay option.
+ ///
+ /// This method is called from getMAC(HWADDR_SOURCE_REMOTE_ID) and should not be
+ /// called directly. It will attempt to extract MAC address information
+ /// from remote-id option inserted by a relay agent closest to the client.
+ /// If this method fails, it will return NULL.
+ ///
+ /// @return hardware address (or NULL)
+ virtual HWAddrPtr getMACFromRemoteIdRelayOption();
+
+ /// @brief Builds on wire packet for TCP transmission.
+ ///
+ /// @todo This function is not implemented yet.
+ ///
+ /// @throw NotImplemented, IPv6 over TCP is not yet supported.
+ void packTCP();
+
+ /// @brief Builds on wire packet for UDP transmission.
+ ///
+ /// @throw InvalidOperation if packing fails
+ void packUDP();
+
+ /// @brief Parses on-wire form of TCP DHCPv6 packet.
+ ///
+ /// Parses received packet, stored in on-wire format in data_.
+ /// data_len_ must be set to indicate data length.
+ /// Will create a collection of option objects that will
+ /// be stored in options_ container.
+ ///
+ /// @todo This function is not implemented yet.
+ ///
+ /// @throw tbd
+ void unpackTCP();
+
+ /// @brief Parses on-wire form of UDP DHCPv6 packet.
+ ///
+ /// Parses received packet, stored in on-wire format in data_.
+ /// data_len_ must be set to indicate data length.
+ /// Will create a collection of option objects that will
+ /// be stored in options_ container.
+ ///
+ /// @throw tbd
+ void unpackUDP();
+
+ /// @brief Unpacks direct (non-relayed) message.
+ ///
+ /// This method unpacks specified buffer range as a direct
+ /// (e.g. solicit or request) message. This method is called from
+ /// unpackUDP() when received message is detected to be direct.
+ ///
+ /// @param begin start of the buffer
+ /// @param end end of the buffer
+ /// @throw tbd
+ void unpackMsg(OptionBuffer::const_iterator begin,
+ OptionBuffer::const_iterator end);
+
+ /// @brief Unpacks relayed message (RELAY-FORW or RELAY-REPL).
+ ///
+ /// This method is called from unpackUDP() when received message
+ /// is detected to be relay-message. It goes iteratively over
+ /// all relays (if there are multiple encapsulation levels).
+ ///
+ /// @throw tbd
+ void unpackRelayMsg();
+
+ /// @brief Calculates overhead introduced in specified relay.
+ ///
+ /// It is used when calculating message size and packing message
+ /// @param relay RelayInfo structure that holds information about relay
+ /// @return number of bytes needed to store relay information
+ uint16_t getRelayOverhead(const RelayInfo& relay) const;
+
+ /// @brief Calculates overhead for all relays defined for this message.
+ /// @return number of bytes needed to store all relay information
+ uint16_t calculateRelaySizes();
+
+ /// @brief Calculates size of the message as if it was not relayed at all.
+ ///
+ /// This is equal to len() if the message was not relayed.
+ /// @return number of bytes required to store the message
+ uint16_t directLen() const;
+
+ /// UDP (usually) or TCP (bulk leasequery or failover)
+ DHCPv6Proto proto_;
+
+ /// DHCPv6 message type
+ uint8_t msg_type_;
+
+}; // Pkt6 class
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif