// 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