/* * Copyright (c) 2017, Matias Fontanini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef TINS_DHCPV6_H #define TINS_DHCPV6_H #include <cstring> #include <tins/pdu.h> #include <tins/macros.h> #include <tins/endianness.h> #include <tins/small_uint.h> #include <tins/ipv6_address.h> #include <tins/pdu_option.h> namespace Tins { namespace Memory { class OutputMemoryStream; } // Memory /** * \class DHCPv6 * \brief Represents a DHCPv6 PDU. */ class TINS_API DHCPv6 : public PDU { public: /** * Represents a DHCPv6 option. */ typedef PDUOption<uint16_t, DHCPv6> option; /** * The message types. */ enum MessageType { SOLICIT = 1, ADVERTISE, REQUEST, CONFIRM, RENEW, REBIND, REPLY, RELEASE, DECLINE, RECONFIGURE, INFO_REQUEST, RELAY_FORWARD, RELAY_REPLY, LEASE_QUERY, LEASE_QUERY_REPLY, LEASE_QUERY_DONE, LEASE_QUERY_DATA }; /** * The DHCPv6 options. */ enum OptionTypes { CLIENTID = 1, SERVERID, IA_NA, IA_TA, IA_ADDR, OPTION_REQUEST, PREFERENCE, ELAPSED_TIME, RELAY_MSG, AUTH = 11, UNICAST, STATUS_CODE, RAPID_COMMIT, USER_CLASS, VENDOR_CLASS, VENDOR_OPTS, INTERFACE_ID, RECONF_MSG, RECONF_ACCEPT, SIP_SERVER_D, SIP_SERVER_A, DNS_SERVERS, DOMAIN_LIST, IA_PD, IAPREFIX, NIS_SERVERS, NISP_SERVERS, NIS_DOMAIN_NAME, NISP_DOMAIN_NAME, SNTP_SERVERS, INFORMATION_REFRESH_TIME, BCMCS_SERVER_D, BCMCS_SERVER_A, GEOCONF_CIVIC = 36, REMOTE_ID, SUBSCRIBER_ID, CLIENT_FQDN, PANA_AGENT, NEW_POSIX_TIMEZONE, NEW_TZDB_TIMEZONE, ERO, LQ_QUERY, CLIENT_DATA, CLT_TIME, LQ_RELAY_DATA, LQ_CLIENT_LINK, MIP6_HNIDF, MIP6_VDINF, V6_LOST, CAPWAP_AC_V6, RELAY_ID, NTP_SERVER, V6_ACCESS_DOMAIN, SIP_UA_CS_LIST, BOOTFILE_URL, BOOTFILE_PARAM, CLIENT_ARCH_TYPE, NII, GEOLOCATION, AFTR_NAME, ERP_LOCAL_DOMAIN_NAME, RSOO, PD_EXCLUDE, VSS, MIP6_IDINF, MIP6_UDINF, MIP6_HNP, MIP6_HAA, MIP6_HAF, RDNSS_SELECTION, KRB_PRINCIPAL_NAME, KRB_REALM_NAME, KRB_DEFAULT_REALM_NAME, KRB_KDC }; /** * The type used to store the DHCPv6 options. */ typedef std::vector<option> options_type; /** * The type used to store IP addresses. */ typedef IPv6Address ipaddress_type; /** * This PDU's flag. */ static const PDU::PDUType pdu_flag = PDU::DHCPv6; /** * The type used to store the Identity Association for Non-Temporary * Addresses option. */ struct ia_na_type { typedef std::vector<uint8_t> options_type; uint32_t id, t1, t2; options_type options; ia_na_type(uint32_t id = 0, uint32_t t1 = 0, uint32_t t2 = 0, const options_type& options = options_type()) : id(id), t1(t1), t2(t2), options(options) {} static ia_na_type from_option(const option& opt); }; /** * The type used to store the Identity Association for Temporary * Addresses option. */ struct ia_ta_type { typedef std::vector<uint8_t> options_type; uint32_t id; options_type options; ia_ta_type(uint32_t id = 0, const options_type& options = options_type()) : id(id), options(options) {} static ia_ta_type from_option(const option& opt); }; /** * The type used to store the Identity Association Address option. */ struct ia_address_type { typedef std::vector<uint8_t> options_type; ipaddress_type address; uint32_t preferred_lifetime, valid_lifetime; options_type options; ia_address_type(ipaddress_type address = ipaddress_type(), uint32_t preferred_lifetime = 0, uint32_t valid_lifetime = 0, const options_type& options = options_type()) : address(address), preferred_lifetime(preferred_lifetime), valid_lifetime(valid_lifetime), options(options) {} static ia_address_type from_option(const option& opt); }; /** * The type used to store the Authentication option. */ struct authentication_type { typedef std::vector<uint8_t> auth_info_type; uint8_t protocol, algorithm, rdm; uint64_t replay_detection; auth_info_type auth_info; authentication_type(uint8_t protocol = 0, uint8_t algorithm = 0, uint8_t rdm = 0, uint64_t replay_detection = 0, const auth_info_type& auth_info = auth_info_type()) : protocol(protocol), algorithm(algorithm), rdm(rdm), replay_detection(replay_detection), auth_info(auth_info) {} static authentication_type from_option(const option& opt); }; /** * The type used to store the Status Code option. */ struct status_code_type { uint16_t code; std::string message; status_code_type(uint16_t code = 0, const std::string& message = "") : code(code), message(message) { } static status_code_type from_option(const option& opt); }; /** * The type used to store the Vendor-specific Information option. */ struct vendor_info_type { typedef std::vector<uint8_t> data_type; uint32_t enterprise_number; data_type data; vendor_info_type(uint32_t enterprise_number = 0, const data_type& data = data_type()) : enterprise_number(enterprise_number), data(data) { } static vendor_info_type from_option(const option& opt); }; /** * The type used to store the User Class option's user class data. */ typedef std::vector<uint8_t> class_option_data_type; /** * The type used to store the User Class option. */ //typedef std::vector<class_option_data_type> user_class_type; struct user_class_type { typedef std::vector<class_option_data_type> data_type; data_type data; user_class_type(const data_type& data = data_type()) : data(data) { } static user_class_type from_option(const option& opt); }; /** * The type used to store the Vendor Class option. */ struct vendor_class_type { typedef std::vector<class_option_data_type> class_data_type; uint32_t enterprise_number; class_data_type vendor_class_data; vendor_class_type(uint32_t enterprise_number = 0, const class_data_type& vendor_class_data = class_data_type()) : enterprise_number(enterprise_number), vendor_class_data(vendor_class_data) { } static vendor_class_type from_option(const option& opt); }; /** * The type used to represent DUIDs Based on Link-layer Address Plus * Time. */ struct duid_llt { static const uint16_t duid_id = 1; typedef std::vector<uint8_t> lladdress_type; uint16_t hw_type; uint32_t time; lladdress_type lladdress; duid_llt(uint16_t hw_type = 0, uint32_t time = 0, const lladdress_type& lladdress = lladdress_type()) : hw_type(hw_type), time(time), lladdress(lladdress) {} PDU::serialization_type serialize() const; static duid_llt from_bytes(const uint8_t* buffer, uint32_t total_sz); }; /** * The type used to represent DUIDs Based on Enterprise Number */ struct duid_en { static const uint16_t duid_id = 2; typedef std::vector<uint8_t> identifier_type; uint32_t enterprise_number; identifier_type identifier; duid_en(uint32_t enterprise_number = 0, const identifier_type& identifier = identifier_type()) : enterprise_number(enterprise_number), identifier(identifier) {} PDU::serialization_type serialize() const; static duid_en from_bytes(const uint8_t* buffer, uint32_t total_sz); }; /** * The type used to represent DUIDs Based on Link-layer Address. */ struct duid_ll { static const uint16_t duid_id = 3; typedef std::vector<uint8_t> lladdress_type; uint16_t hw_type; lladdress_type lladdress; duid_ll(uint16_t hw_type = 0, const lladdress_type& lladdress = lladdress_type()) : hw_type(hw_type), lladdress(lladdress) {} PDU::serialization_type serialize() const; static duid_ll from_bytes(const uint8_t* buffer, uint32_t total_sz); }; /** * Type type used to represent DUIDs. This will be stored as the * value for the Client/Server Identifier options. */ struct duid_type { typedef PDU::serialization_type data_type; uint16_t id; data_type data; duid_type(uint16_t id = 0, const data_type& data = data_type()) : id(id), data(data) {} duid_type(const duid_llt& identifier) : id(duid_llt::duid_id), data(identifier.serialize()) {} duid_type(const duid_en& identifier) : id(duid_en::duid_id), data(identifier.serialize()) {} duid_type(const duid_ll& identifier) : id(duid_ll::duid_id), data(identifier.serialize()) {} static duid_type from_option(const option& opt); }; /** * The type used to store the Option Request option. */ typedef std::vector<uint16_t> option_request_type; /** * The type used to store the Relay Message option. */ typedef std::vector<uint8_t> relay_msg_type; /** * The type used to store the Interface-ID option. */ typedef std::vector<uint8_t> interface_id_type; /** * \brief Extracts metadata for this protocol based on the buffer provided * * \param buffer Pointer to a buffer * \param total_sz Size of the buffer pointed by buffer */ static metadata extract_metadata(const uint8_t *buffer, uint32_t total_sz); /** * Default constructor. */ DHCPv6(); /** * \brief Constructs a DHCPv6 object from a buffer. * * If there is not enough size for the DHCPv6 header, or any * of the TLV options contains an invalid size field, a * malformed_packet exception is thrown. * * \param buffer The buffer from which this PDU will be constructed. * \param total_sz The total size of the buffer. */ DHCPv6(const uint8_t* buffer, uint32_t total_sz); // Getters /** * \brief Getter for the message type field. * * \return The stored message type field. */ MessageType msg_type() const { return static_cast<MessageType>(header_data_[0]); } /** * \brief Getter for the hop count field. * * \return The stored hop count field. */ uint8_t hop_count() const { return header_data_[1]; } /** * \brief Getter for the transaction id field. * * \return The stored transaction id field. */ small_uint<24> transaction_id() const { return (header_data_[1] << 16) | (header_data_[2] << 8) | header_data_[3]; } /** * \brief Getter for the peer address field. * * \return The stored peer address field. */ const ipaddress_type& peer_address() const { return peer_addr_; } /** * \brief Getter for the link address field. * * \return The stored link address field. */ const ipaddress_type& link_address() const { return link_addr_; } /** * \brief Getter for the DHCPv6 options. * * \return The stored options. */ const options_type& options() const { return options_; } // Setters /** * \brief Setter for the message type field. * * \param type The new message type. */ void msg_type(MessageType type); /** * \brief Setter for the hop count field. * * \param count The new hop count. */ void hop_count(uint8_t count); /** * \brief Setter for the transaction id field. * * \param id The new transaction id. */ void transaction_id(small_uint<24> id); /** * \brief Setter for the peer address field. * * \param count The new peer address. */ void peer_address(const ipaddress_type& addr); /** * \brief Setter for the link address field. * * \param count The new link address. */ void link_address(const ipaddress_type& addr); // Option getters /** * \brief Getter for the Identity Association for Non-Temporary * Addresses option. * * This method will throw an option_not_found exception if the * option is not found. */ ia_na_type ia_na() const; /** * \brief Getter for the Identity Association for Temporary * Addresses option. * * This method will throw an option_not_found exception if the * option is not found. */ ia_ta_type ia_ta() const; /** * \brief Getter for the Identity Association Address option. * * This method will throw an option_not_found exception if the * option is not found. */ ia_address_type ia_address() const; /** * \brief Getter for the Option Request option. * * This method will throw an option_not_found exception if the * option is not found. */ option_request_type option_request() const; /** * \brief Getter for the Preference option. * * This method will throw an option_not_found exception if the * option is not found. */ uint8_t preference() const; /** * \brief Getter for the Elapsed Time option. * * This method will throw an option_not_found exception if the * option is not found. */ uint16_t elapsed_time() const; /** * \brief Getter for the Relay Message option. * * This method will throw an option_not_found exception if the * option is not found. */ relay_msg_type relay_message() const; /** * \brief Getter for the Authentication option. * * This method will throw an option_not_found exception if the * option is not found. */ authentication_type authentication() const; /** * \brief Getter for the Server Unicast option. * * This method will throw an option_not_found exception if the * option is not found. */ ipaddress_type server_unicast() const; /** * \brief Getter for the Server Unicast option. * * This method will throw an option_not_found exception if the * option is not found. */ status_code_type status_code() const; /** * \brief Getter for the Rapid Commit option. * * This method will throw an option_not_found exception if the * option is not found. */ bool has_rapid_commit() const; /** * \brief Getter for the User Class option. * * This method will throw an option_not_found exception if the * option is not found. */ user_class_type user_class() const; /** * \brief Getter for the Vendor Class option. * * This method will throw an option_not_found exception if the * option is not found. */ vendor_class_type vendor_class() const; /** * \brief Getter for the Vendor-specific Information option. * * This method will throw an option_not_found exception if the * option is not found. */ vendor_info_type vendor_info() const; /** * \brief Getter for the Interface ID option. * * This method will throw an option_not_found exception if the * option is not found. */ interface_id_type interface_id() const; /** * \brief Getter for the Reconfigure Message option. * * This method will throw an option_not_found exception if the * option is not found. */ uint8_t reconfigure_msg() const; /** * \brief Getter for the Reconfigure Accept option. * * This method will throw an option_not_found exception if the * option is not found. */ bool has_reconfigure_accept() const; /** * \brief Getter for the Client Identifier option. * * This method will throw an option_not_found exception if the * option is not found. */ duid_type client_id() const; /** * \brief Getter for the Server Identifier option. * * This method will throw an option_not_found exception if the * option is not found. */ duid_type server_id() const; // Option setters /** * \brief Setter for the Identity Association for Non-Temporary * Addresses option. * * \param value The new IA_NA option data. */ void ia_na(const ia_na_type& value); /** * \brief Setter for the Identity Association for Temporary * Addresses option. * * \param value The new IA_TA option data. */ void ia_ta(const ia_ta_type& value); /** * \brief Setter for the Identity Association Address option. * * \param value The new IA Address option data. */ void ia_address(const ia_address_type& value); /** * \brief Setter for the Identity Association Address option. * * \param value The new Option Request option data. */ void option_request(const option_request_type& value); /** * \brief Setter for the Preference option. * * \param value The new Preference option data. */ void preference(uint8_t value); /** * \brief Setter for the Elapsed Time option. * * \param value The new Elapsed Time option data. */ void elapsed_time(uint16_t value); /** * \brief Setter for the Relay Message option. * * \param value The new Relay Message option data. */ void relay_message(const relay_msg_type& value); /** * \brief Setter for the Authentication option. * * \param value The new Authentication option data. */ void authentication(const authentication_type& value); /** * \brief Setter for the Server Unicast option. * * \param value The new Server Unicast option data. */ void server_unicast(const ipaddress_type& value); /** * \brief Setter for the Status Code option. * * \param value The new Status Code option data. */ void status_code(const status_code_type& value); /** * \brief Adds a Rapid Commit option. */ void rapid_commit(); /** * \brief Setter for the User Class option. * * \param value The new User Class option data. */ void user_class(const user_class_type& value); /** * \brief Setter for the Vendor Class option. * * \param value The new Vendor Class option data. */ void vendor_class(const vendor_class_type& value); /** * \brief Setter for the Vendor-specific Information option. * * \param value The new Vendor-specific Information option data. */ void vendor_info(const vendor_info_type& value); /** * \brief Setter for the Interface ID option. * * \param value The new Interface ID option data. */ void interface_id(const interface_id_type& value); /** * \brief Setter for the Reconfigure Message option. * * \param value The new Reconfigure Message option data. */ void reconfigure_msg(uint8_t value); /** * \brief Adds a Reconfigure Accept option. */ void reconfigure_accept(); /** * \brief Setter for the Client Identifier option. * * \param value The new Client Identifier option data. */ void client_id(const duid_type& value); /** * \brief Setter for the Server Identifier option. * * \param value The new Server Identifier option data. */ void server_id(const duid_type& value); // Other stuff /** * Indicates whether this is a relay agent/server message */ bool is_relay_message() const; /** * \brief Adds a DHCPv6 option. * * The option is added after the last option in the option * fields. * * \param opt The option to be added */ void add_option(const option& opt); /** * \brief Removes a DHCPv6 option. * * If there are multiple options of the given type, only the first one * will be removed. * * \param type The type of the option to be removed. * \return true if the option was removed, false otherwise. */ bool remove_option(OptionTypes type); /** * \brief Searchs for an option that matchs the given type. * * If the option is not found, a null pointer is returned. * Deleting the returned pointer will result in <b>undefined * behaviour</b>. * * \param type The option identifier to be searched. */ const option* search_option(OptionTypes type) const; // PDU stuff /** * \brief Returns the header size. * * This method overrides PDU::header_size. \sa PDU::header_size */ uint32_t header_size() const; /** * \brief Check whether ptr points to a valid response for this PDU. * * \sa PDU::matches_response * \param ptr The pointer to the buffer. * \param total_sz The size of the buffer. */ bool matches_response(const uint8_t* ptr, uint32_t total_sz) const; /** * \brief Getter for the PDU's type. * \sa PDU::pdu_type */ PDUType pdu_type() const { return pdu_flag; } /** * \sa PDU::clone */ DHCPv6* clone() const { return new DHCPv6(*this); } private: void write_serialization(uint8_t* buffer, uint32_t total_sz); void write_option(const option& option, Memory::OutputMemoryStream& stream) const; options_type::const_iterator search_option_iterator(OptionTypes type) const; options_type::iterator search_option_iterator(OptionTypes type); template <template <typename> class Functor> const option* safe_search_option(OptionTypes opt, uint32_t size) const { const option* option = search_option(opt); if (!option || Functor<uint32_t>()(option->data_size(), size)) { throw option_not_found(); } return option; } template<typename T> T search_and_convert(OptionTypes opt) const { const option* option = search_option(opt); if (!option) { throw option_not_found(); } return option->to<T>(); } uint8_t header_data_[4]; uint32_t options_size_; ipaddress_type link_addr_, peer_addr_; options_type options_; }; } // Tins #endif // TINS_DHCPV6_H