From f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 14:15:43 +0200 Subject: Adding upstream version 2.4.1. Signed-off-by: Daniel Baumann --- src/lib/dhcp/pkt6.h | 627 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 627 insertions(+) create mode 100644 src/lib/dhcp/pkt6.h (limited to 'src/lib/dhcp/pkt6.h') diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h new file mode 100644 index 0000000..1ae23be --- /dev/null +++ b/src/lib/dhcp/pkt6.h @@ -0,0 +1,627 @@ +// 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 +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +namespace isc { + +namespace dhcp { + +class Pkt6; + +/// @brief A pointer to Pkt6 packet +typedef boost::shared_ptr 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; + + /// @brief Returns all option instances inserted by relay agent. + /// + /// This is a variant of the @ref Pkt6::getRelayOptions 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 Collection of options found. + OptionCollection getNonCopiedRelayOptions(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); + + /// @brief Returns options inserted by relay + /// + /// Returns options 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 Collection of options found. + OptionCollection getRelayOptions(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; + + /// @brief Returns pointers to instances of specified option. + /// + /// This is a variant of @ref getAllRelayOptions 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 Collection of options found. + OptionCollection getNonCopiedAllRelayOptions(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 first instances of a specified option + /// + /// When a client's packet traverses multiple relays, each passing + /// relay may insert extra options. This method allows the + /// specific instances of a given option to be obtained in the + /// specified order (e.g. first closest to the client, first + /// 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 Collection of options found. + OptionCollection getAllRelayOptions(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; + + /// @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 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 -- cgit v1.2.3