From: mayya Date: Sun, 10 Jun 2018 16:45:34 +0000 (+0200) Subject: [lib] Extend host structure to support auth keys for reconfiguration. X-Git-Tag: ha_phase2~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2090af48c13f018a69570c61973541c0bb7c2b67;p=thirdparty%2Fkea.git [lib] Extend host structure to support auth keys for reconfiguration. See issue #83 --- diff --git a/src/lib/dhcpsrv/cql_host_data_source.cc b/src/lib/dhcpsrv/cql_host_data_source.cc index ae8787359c..0c0609cb11 100644 --- a/src/lib/dhcpsrv/cql_host_data_source.cc +++ b/src/lib/dhcpsrv/cql_host_data_source.cc @@ -116,6 +116,7 @@ static constexpr char NULL_USER_CONTEXT[] = ""; static constexpr char NULL_RESERVED_IPV6_PREFIX_ADDRESS[] = "::"; static constexpr cass_int32_t NULL_RESERVED_IPV6_PREFIX_LENGTH = 0; static constexpr cass_int32_t NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE = -1; +static constexpr char NULL_RESERVED_KEY[] = ""; static constexpr cass_int32_t NULL_IAID = -1; static constexpr cass_int32_t NULL_OPTION_UNIVERSE = -1; static constexpr cass_int32_t NULL_OPTION_CODE = -1; @@ -372,6 +373,9 @@ private: /// This value corresponds to the @ref Host::IdentifierType value. cass_int32_t reserved_ipv6_prefix_address_type_; + /// @brief Key for authentication + std::string reserved_key_; + /// @brief The reservation's IAID cass_int32_t iaid_; @@ -438,6 +442,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -455,7 +460,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { // host "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " // denormalized reservation, option - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? " + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? " ") " "IF NOT EXISTS " }}, @@ -479,6 +484,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -512,6 +518,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -548,6 +555,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -583,6 +591,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -620,6 +629,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -657,6 +667,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -693,6 +704,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -729,6 +741,7 @@ StatementMap CqlHostExchange::tagged_statements_ = { "reserved_ipv6_prefix_address, " "reserved_ipv6_prefix_length, " "reserved_ipv6_prefix_address_type, " + "reserved_key, " "iaid, " "option_universe, " "option_code, " @@ -811,6 +824,8 @@ CqlHostExchange::createBindForSelect(AnyArray& data, StatementTag /* not used */ data.add(&reserved_ipv6_prefix_length_); // reserved_ipv6_prefix_address_type: int data.add(&reserved_ipv6_prefix_address_type_); + // reserved_key: text + data.add(&reserved_key_); // iaid: int data.add(&iaid_); /// @} @@ -929,6 +944,8 @@ CqlHostExchange::prepareExchange(const HostPtr& host, reserved_ipv6_prefix_length_ = NULL_RESERVED_IPV6_PREFIX_LENGTH; // reserved_ipv6_prefix_address_type: int reserved_ipv6_prefix_address_type_ = NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE; + // reserved_key: text + key_ = NULL_RESERVED_KEY; iaid_ = NULL_IAID; } else { // reserved_ipv6_prefix_address: text @@ -941,6 +958,9 @@ CqlHostExchange::prepareExchange(const HostPtr& host, reserved_ipv6_prefix_address_type_ = reservation->getType() == IPv6Resrv::TYPE_NA ? 0 : 2; + // reserved_key: text + reserved_key_ = reservation->getKeys(); + // iaid: int /// @todo: We don't support iaid in the IPv6Resrv yet. iaid_ = 0; @@ -1060,6 +1080,7 @@ CqlHostExchange::createBindForMutation(const HostPtr& host, data.add(&reserved_ipv6_prefix_address_); data.add(&reserved_ipv6_prefix_length_); data.add(&reserved_ipv6_prefix_address_type_); + data.add(&reserved_key_); data.add(&iaid_); // Option @@ -1227,7 +1248,7 @@ CqlHostExchange::retrieveReservation() const { } return (IPv6Resrv(type, IOAddress(reserved_ipv6_prefix_address_), - reserved_ipv6_prefix_length_)); + reserved_ipv6_prefix_length_, reserved_key_)); } const OptionWrapper diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index 9825aab193..a99955b15b 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -901,6 +901,7 @@ public: static constexpr StatementTag DELETE_LEASE6 = "DELETE_LEASE6"; static constexpr StatementTag GET_LEASE6_EXPIRE = "GET_LEASE6_EXPIRE"; static constexpr StatementTag GET_LEASE6_ADDR = "GET_LEASE6_ADDR"; + static constexpr StatementTag GET_LEASE6_DUID = "GET_LEASE6_DUID"; static constexpr StatementTag GET_LEASE6_DUID_IAID = "GET_LEASE6_DUID_IAID"; static constexpr StatementTag GET_LEASE6_DUID_IAID_SUBID = "GET_LEASE6_DUID_IAID_SUBID"; static constexpr StatementTag GET_LEASE6_LIMIT = "GET_LEASE6_LIMIT"; @@ -1003,7 +1004,7 @@ StatementMap CqlLease6Exchange::tagged_statements_ = { "LIMIT ? " "ALLOW FILTERING "}}, - // Gets an IPv6 lease with specified IPv4 address + // Gets an IPv6 lease with specified IPv6 address {GET_LEASE6_ADDR, {GET_LEASE6_ADDR, "SELECT " @@ -1015,6 +1016,17 @@ StatementMap CqlLease6Exchange::tagged_statements_ = { "AND lease_type = ? " "ALLOW FILTERING "}}, + // Gets an IPv6 lease with specified duid + {GET_LEASE6_DUID, + {GET_LEASE6_DUID, + "SELECT " + "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, " + "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, " + "hwaddr_source, state " + "FROM lease6 " + "WHERE duid = ? " + "ALLOW FILTERING "}}, + // Gets an IPv6 lease(s) with specified duid and iaid {GET_LEASE6_DUID_IAID, {GET_LEASE6_DUID_IAID, @@ -2280,6 +2292,27 @@ CqlLeaseMgr::getLease6(Lease::Type lease_type, const IOAddress &addr) const { return (result); } +Lease6Ptr +CqlLeaseMgr::getLease6(const DUID& duid) const { + std::string duid_data = duid.toText(); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_DUID) + .arg(duid_data); + + // Set up the WHERE clause value + AnyArray data; + + CassBlob duid_data(duid.getDuid()); + + data.add(&duid_data); + + // Get the data. + Lease6Collection result; + std::unique_ptr exchange6(new CqlLease6Exchange(dbconn_)); + exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_DUID, data, result); + + return (result); +} + Lease6Collection CqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID &duid, uint32_t iaid) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_IAID_DUID) diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index 39382b2f4e..f67add57a3 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -353,6 +353,11 @@ public: getLeases6(const asiolink::IOAddress& lower_bound_address, const LeasePageSize& page_size) const override; + /// @brief Returns all IPv6 leases. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection getLeases6(const DUID& duid) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 1361e2266f..5801d9825f 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -757,6 +757,10 @@ of leases beginning with the specified address. A debug message issued when the server is attempting to obtain an IPv6 lease from the MySQL database for the specified address. +% DHCPSRV_MYSQL_GET_DUID obtaining IPv6 lease for duid %1, +A debug message issued when the server is attempting to obtain an IPv6 +lease from the MySQL database for the specified duid. + % DHCPSRV_MYSQL_GET_CLIENTID obtaining IPv4 leases for client ID %1 A debug message issued when the server is attempting to obtain a set of IPv4 leases from the MySQL database for a client with the specified @@ -954,6 +958,10 @@ A debug message issued when the server is attempting to obtain a set of IPv6 leases from the PostgreSQL database for a client with the specified IAID (Identity Association ID) and DUID (DHCP Unique Identifier). +% DHCPSRV_PGSQL_GET_DUID obtaining IPv6 leases for DUID %1, +A debug message issued when the server is attempting to obtain a set of IPv6 +leases from the PostgreSQL database for a client with the specified DUID (DHCP Unique Identifier). + % DHCPSRV_PGSQL_GET_IAID_SUBID_DUID obtaining IPv4 leases for IAID %1, Subnet ID %2, DUID %3, and lease type %4 A debug message issued when the server is attempting to obtain an IPv6 lease from the PostgreSQL database for a client with the specified IAID diff --git a/src/lib/dhcpsrv/host.cc b/src/lib/dhcpsrv/host.cc index 38b29c13f0..12897280da 100644 --- a/src/lib/dhcpsrv/host.cc +++ b/src/lib/dhcpsrv/host.cc @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -22,14 +24,26 @@ namespace dhcp { IPv6Resrv::IPv6Resrv(const Type& type, const asiolink::IOAddress& prefix, const uint8_t prefix_len) - : type_(type), prefix_(asiolink::IOAddress("::")), prefix_len_(128) { + : type_(type), prefix_(asiolink::IOAddress("::")), + prefix_len_(128), keys_("") { // Validate and set the actual values. set(type, prefix, prefix_len); } + +IPv6Resrv::IPv6Resrv(const Type& type, + const asiolink::IOAddress& prefix, + const std::string& keys, + const uint8_t prefix_len) + : type_(type), prefix_(asiolink::IOAddress("::")), + prefix_len_(128), keys_("") { + // Validate and set the actual values. + set(type, prefix, prefix_len, keys); +} + void IPv6Resrv::set(const Type& type, const asiolink::IOAddress& prefix, - const uint8_t prefix_len) { + const uint8_t prefix_len, const std::string& keys) { if (!prefix.isV6() || prefix.isV6Multicast()) { isc_throw(isc::BadValue, "invalid prefix '" << prefix << "' for new IPv6 reservation"); @@ -43,6 +57,9 @@ IPv6Resrv::set(const Type& type, const asiolink::IOAddress& prefix, isc_throw(isc::BadValue, "invalid prefix length '" << static_cast(prefix_len) << "' for reserved IPv6 address, expected 128"); + } else if (!keys.empty()) { + //Don't overwrite with an empty string + keys_ = keys; } type_ = type; @@ -50,11 +67,27 @@ IPv6Resrv::set(const Type& type, const asiolink::IOAddress& prefix, prefix_len_ = prefix_len; } +std::string +IPv6Resrv::getRandomKeyString() +{ + std::array randomString; + + std::random_device rd; + boost::random::mt19937 gen(rd()); + + std::for_each(randomString.begin(), randomString.end() - 1, + [&gen](char& a){ boost::random::uniform_int_distribution dist('!', '~'); + a = dist(gen); } ); + + return std::string(randomString.begin(), randomString.end()); +} + std::string IPv6Resrv::toText() const { std::ostringstream s; s << prefix_; // For PD, append prefix length. + // @todo: add to text for keys if (getType() == TYPE_PD) { s << "/" << static_cast(prefix_len_); } @@ -65,7 +98,8 @@ bool IPv6Resrv::operator==(const IPv6Resrv& other) const { return (type_ == other.type_ && prefix_ == other.prefix_ && - prefix_len_ == other.prefix_len_); + prefix_len_ == other.prefix_len_ && + keys_ == other.keys_ ); } bool diff --git a/src/lib/dhcpsrv/host.h b/src/lib/dhcpsrv/host.h index 5441727006..444c17b07d 100644 --- a/src/lib/dhcpsrv/host.h +++ b/src/lib/dhcpsrv/host.h @@ -63,6 +63,25 @@ public: const asiolink::IOAddress& prefix, const uint8_t prefix_len = 128); + /// @brief Constructor. + /// + /// Creates a reservation from the IPv6 address ,prefix length and + /// key value. If the prefix length is not specified, the default value + /// of 128 is used. This value indicates that the reservation is made + /// for an IPv6 address for a client which supports reconfiguration. + /// + /// @param type Reservation type: NA or PD. + /// @param prefix Address or prefix to be reserved. + /// @param keys to be reserved. + /// @param prefix_len Prefix length. + /// + /// @throw isc::BadValue if prefix is not IPv6 prefix, is a + /// multicast address or the prefix length is greater than 128. + IPv6Resrv(const Type& type, + const asiolink::IOAddress& prefix, + const std::string& keys, + const uint8_t prefix_len = 128); + /// @brief Returns prefix for the reservation. const asiolink::IOAddress& getPrefix() const { return (prefix_); @@ -82,6 +101,14 @@ public: return (type_); } + /// @brief Returns the keys. + /// + /// Keys are used for signing the Reconfigure Message. + /// The accessor should ensure 128 characters + std::string getKeys() const { + return (keys_); + } + /// @brief Sets a new prefix and prefix length. /// /// @param type Reservation type: NA or PD. @@ -91,11 +118,18 @@ public: /// @throw isc::BadValue if prefix is not IPv6 prefix, is a /// multicast address or the prefix length is greater than 128. void set(const Type& type, const asiolink::IOAddress& prefix, - const uint8_t prefix_len); + const uint8_t prefix_len, const std::string& keys = ""); /// @brief Returns information about the reservation in the textual format. std::string toText() const; + /// @brief Generates random 128 bit string + /// + /// Random string is generated by default will be used for + /// the keys to be used for signing Reconfigure Message. + + static std::string getRandomKeyString(); + /// @brief Equality operator. /// /// @param other Reservation to compare to. @@ -111,6 +145,7 @@ private: Type type_; ///< Reservation type. asiolink::IOAddress prefix_; ///< Prefix uint8_t prefix_len_; ///< Prefix length. + std::string keys_; ///< keys for authentication. }; diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index 852e5b2da8..152039dd25 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -500,6 +500,11 @@ public: getLeases6(const asiolink::IOAddress& lower_bound_address, const LeasePageSize& page_size) const = 0; + /// @brief Returns collection of lease for matching DUID + /// + /// @return Lease collection (may be empty if no IPv6 lease found for the DUID). + virtual Lease6Collection getLeases6(const DUID& duid) const = 0; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index a4b4a1114c..c1c84b8aea 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -1048,6 +1048,19 @@ Memfile_LeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, return (collection); } +Lease6Collection +Memfile_LeaseMgr::getLeases6(const DUID& duid) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET6); + + Lease6Collection collection; + for (auto lease = storage6_.begin(); lease != storage6_.end(); ++lease ) { + if ( (**lease).duid_->getDuid() == duid.getDuid() ) + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } + + return (collection); +} + void Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, const size_t max_leases) const { diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 5e227a1d78..6a1008b4e8 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -340,6 +340,11 @@ public: getLeases6(const asiolink::IOAddress& lower_bound_address, const LeasePageSize& page_size) const; + /// @brief Returns all IPv6 leases. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection getLeases6(const DUID& duid) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc index 37eb903b5d..a6cd094abb 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.cc +++ b/src/lib/dhcpsrv/mysql_host_data_source.cc @@ -73,6 +73,10 @@ const size_t SERVER_HOSTNAME_MAX_LEN = 64; /// @brief Maximum length of the boot file name. const size_t BOOT_FILE_NAME_MAX_LEN = 128; +/// @brief Maximum length of keys. +const size_t KEY_MAX_LEN = 128; + +/// @brief Numeric value representing last supported identifier. /// @brief Numeric value representing last supported identifier. /// /// This value is used to validate whether the identifier type stored in @@ -1325,7 +1329,7 @@ class MySqlHostIPv6Exchange : public MySqlHostWithOptionsExchange { private: /// @brief Number of columns holding IPv6 reservation information. - static const size_t RESERVATION_COLUMNS = 5; + static const size_t RESERVATION_COLUMNS = 6; public: @@ -1338,11 +1342,12 @@ public: reservation_id_(0), reserv_type_(0), reserv_type_null_(MLM_FALSE), ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0), - reservation_id_index_(findAvailColumn()), + key_len_(0),reservation_id_index_(findAvailColumn()), address_index_(reservation_id_index_ + 1), prefix_len_index_(reservation_id_index_ + 2), type_index_(reservation_id_index_ + 3), iaid_index_(reservation_id_index_ + 4), + key_index_(reservation_id_index_ + 5), most_recent_reservation_id_(0) { memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_)); @@ -1353,6 +1358,7 @@ public: columns_[prefix_len_index_] = "prefix_len"; columns_[type_index_] = "type"; columns_[iaid_index_] = "dhcp6_iaid"; + columns_[key_index_] = "auth_key"; } /// @brief Returns last fetched reservation id. @@ -1393,7 +1399,11 @@ public: ipv6_address_buffer_[ipv6_address_buffer_len_] = '\0'; std::string address = ipv6_address_buffer_; - IPv6Resrv r(type, IOAddress(address), prefix_len_); + + key_buffer_[key_len_] = '\0'; + std::string key = key_buffer_; + + IPv6Resrv r(type, IOAddress(address), key, prefix_len_); return (r); }; @@ -1487,6 +1497,13 @@ public: bind_[iaid_index_].buffer = reinterpret_cast(&iaid_); bind_[iaid_index_].is_unsigned = MLM_TRUE; + // key for auth VARCHAR(128) + key_len_ = sizeof(key_buffer_) - 1; + bind_[key_index_].buffer_type = MYSQL_TYPE_STRING; + bind_[key_index_].buffer = key_buffer_; + bind_[key_index_].buffer_length = key_len_; + bind_[key_index_].length = &key_len_; + // Add the error flags setErrorIndicators(bind_, error_); @@ -1519,6 +1536,12 @@ private: /// @brief IAID. uint32_t iaid_; + /// @brief Buffer length for holding keys in textual format. + char key_buffer_[KEY_MAX_LEN + 1]; + + /// @brief Length of the keys + unsigned long key_len_; + /// @name Indexes of columns holding information about IPv6 reservations. //@{ /// @brief Index of reservation_id column. @@ -1536,6 +1559,9 @@ private: /// @brief Index of IAID column. size_t iaid_index_; + /// @brief Index of keys column. + size_t key_index_; + //@} /// @brief Reservation id for last processed row. @@ -1546,7 +1572,7 @@ private: /// /// This class is only used to insert IPv6 reservations into the /// ipv6_reservations table. It is not used to retrieve IPv6 reservations. To -/// retrieve IPv6 reservation the @ref MySqlIPv6HostExchange class should be +/// retrieve IPv6 reservation the @ref MySqlHostIPv6Exchange class should be /// used instead. /// /// When a new IPv6 reservation is inserted into the database, an appropriate @@ -1556,7 +1582,7 @@ class MySqlIPv6ReservationExchange { private: /// @brief Set number of columns for ipv6_reservation table. - static const size_t RESRV_COLUMNS = 6; + static const size_t RESRV_COLUMNS = 7; public: @@ -1576,6 +1602,7 @@ public: columns_[2] = "prefix_len"; columns_[3] = "type"; columns_[4] = "dhcp6_iaid"; + columns_[5] = "auth_key"; BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS); } @@ -1642,6 +1669,16 @@ public: bind_[4].buffer = reinterpret_cast(&host_id_); bind_[4].is_unsigned = MLM_TRUE; + // key VARCHAR(128) + // why we need member for len + key_ = resv.getKeys(); + key_len_ = key_.length(); + bind_[5].buffer_type = MYSQL_TYPE_BLOB; + bind_[5].buffer = reinterpret_cast + (const_cast(key_.c_str())); + bind_[5].buffer_length = key_len_; + bind_[5].length = &key_len_; + } catch (const std::exception& ex) { isc_throw(DbOperationError, "Could not create bind array from IPv6 Reservation: " @@ -1662,6 +1699,12 @@ private: /// @brief Address (or prefix). std::string address_; + /// @brief Keys for Authentication + std::string key_; + + /// @brief length of keys for Authentication + unsigned long key_len_; + /// @brief Length of the textual address representation. unsigned long address_len_; @@ -2112,7 +2155,7 @@ TaggedStatementArray tagged_statements = { { "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, " "o6.persistent, o6.user_context, " "r.reservation_id, r.address, r.prefix_len, r.type, " - "r.dhcp6_iaid " + "r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp4_options AS o4 " "ON h.host_id = o4.host_id " @@ -2168,7 +2211,7 @@ TaggedStatementArray tagged_statements = { { "o.option_id, o.code, o.value, o.formatted_value, o.space, " "o.persistent, o.user_context, " "r.reservation_id, r.address, r.prefix_len, r.type, " - "r.dhcp6_iaid " + "r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp6_options AS o " "ON h.host_id = o.host_id " @@ -2210,7 +2253,7 @@ TaggedStatementArray tagged_statements = { { "o.option_id, o.code, o.value, o.formatted_value, o.space, " "o.persistent, o.user_context," "r.reservation_id, r.address, r.prefix_len, r.type, " - "r.dhcp6_iaid " + "r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp6_options AS o " "ON h.host_id = o.host_id " @@ -2237,7 +2280,7 @@ TaggedStatementArray tagged_statements = { { "o.option_id, o.code, o.value, o.formatted_value, o.space, " "o.persistent, o.user_context, " "r.reservation_id, r.address, r.prefix_len, r.type, " - "r.dhcp6_iaid " + "r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp6_options AS o " "ON h.host_id = o.host_id " @@ -2258,8 +2301,8 @@ TaggedStatementArray tagged_statements = { { // Inserts a single IPv6 reservation into 'reservations' table. {MySqlHostDataSourceImpl::INSERT_V6_RESRV, "INSERT INTO ipv6_reservations(address, prefix_len, type, " - "dhcp6_iaid, host_id) " - "VALUES (?,?,?,?,?)"}, + "dhcp6_iaid, host_id, auth_key) " + "VALUES (?,?,?,?,?,?)"}, // Inserts a single DHCPv4 option into 'dhcp4_options' table. // Using fixed scope_id = 3, which associates an option with host. diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 16c3327ce4..663bc0c8c9 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -184,7 +184,7 @@ tagged_statements = { { "state, user_context " "FROM lease6 " "WHERE address = ? AND lease_type = ?"}, - {MySqlLeaseMgr::GET_LEASE6_DUID_IAID, + {MySqlLeaseMgr::GET_LEASE6_DUID_IAID, "SELECT address, duid, valid_lifetime, " "expire, subnet_id, pref_lifetime, " "lease_type, iaid, prefix_len, " @@ -2266,6 +2266,23 @@ MySqlLeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, return (result); } +Lease6Collection +MySqlLeaseMgr::getLeases6(const DUID& duid) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_DUID); + + Lease6Collection result = getLeases6(); + + //erase the ones not containing the matching DUID + for (auto iter = result.begin(); iter != result.end(); + iter++) { + if ((*iter)->duid_->getDuid() != duid.getDuid()) { + result.erase(iter); + } + } + + return (result); +} + void MySqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, const size_t max_leases) const { diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index ede95dcecf..f9d9286c05 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -337,6 +337,11 @@ public: getLeases6(const asiolink::IOAddress& lower_bound_address, const LeasePageSize& page_size) const; + /// @brief Returns all IPv6 leases. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection getLeases6(const DUID& duid) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index f11ab16c9e..bcbddd88af 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -851,7 +851,7 @@ class PgSqlHostIPv6Exchange : public PgSqlHostWithOptionsExchange { private: /// @brief Number of columns holding IPv6 reservation information. - static const size_t RESERVATION_COLUMNS = 5; + static const size_t RESERVATION_COLUMNS = 6; public: @@ -866,6 +866,7 @@ public: prefix_len_index_(reservation_id_index_ + 2), type_index_(reservation_id_index_ + 3), iaid_index_(reservation_id_index_ + 4), + key_index_(reservation_id_index_ + 5), most_recent_reservation_id_(0) { // Provide names of additional columns returned by the queries. @@ -874,6 +875,7 @@ public: columns_[prefix_len_index_] = "prefix_len"; columns_[type_index_] = "type"; columns_[iaid_index_] = "dhcp6_iaid"; + columns_[key_index_] = "auth_key"; BOOST_STATIC_ASSERT(4 < RESERVATION_COLUMNS); } @@ -934,13 +936,17 @@ public: uint16_t prefix_len; getColumnValue(r, row, prefix_len_index_, prefix_len); + // key_: string + string key; + getColumnValue(r, row, key_index_, key); + // @todo once we support populating iaid // iaid: INT // int iaid; // getColumnValue(r, row, iaid_index_, iaid); // Create the reservation. - IPv6Resrv reservation(resv_type, IOAddress(address), prefix_len); + IPv6Resrv reservation(resv_type, IOAddress(address), key, prefix_len); return (reservation); }; @@ -1004,6 +1010,9 @@ private: /// @brief Index of IAID column. size_t iaid_index_; + /// @brief Index of IAID column. + size_t key_index_; + //@} /// @brief Reservation id for last processed row. @@ -1024,7 +1033,7 @@ class PgSqlIPv6ReservationExchange : public PgSqlExchange { private: /// @brief Set number of columns for ipv6_reservation table. - static const size_t RESRV_COLUMNS = 6; + static const size_t RESRV_COLUMNS = 7; public: @@ -1040,7 +1049,8 @@ public: columns_[2] = "prefix_len"; columns_[3] = "type"; columns_[4] = "dhcp6_iaid"; - BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS); + columns_[5] = "auth_key"; + BOOST_STATIC_ASSERT(6 < RESRV_COLUMNS); } /// @brief Populate a bind array representing an IPv6 reservation @@ -1082,6 +1092,10 @@ public: // host_id: BIGINT NOT NULL bind_array->add(host_id); + + // type: VARCHAR(128) NOT NULL + bind_array->add(resv.getKeys()); + } catch (const std::exception& ex) { isc_throw(DbOperationError, "Could not create bind array from IPv6 Reservation: " @@ -1461,7 +1475,7 @@ TaggedStatementArray tagged_statements = { { " o4.persistent, o4.user_context, " " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, " " o6.persistent, o6.user_context, " - " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid " + " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id " "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id " @@ -1522,7 +1536,7 @@ TaggedStatementArray tagged_statements = { { " h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, " " o.option_id, o.code, o.value, o.formatted_value, o.space, " " o.persistent, o.user_context, " - " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid " + " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id " "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id " @@ -1569,7 +1583,7 @@ TaggedStatementArray tagged_statements = { { " o.option_id, o.code, o.value, o.formatted_value, o.space, " " o.persistent, o.user_context, " " r.reservation_id, r.address, r.prefix_len, r.type, " - " r.dhcp6_iaid " + " r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id " "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id " @@ -1597,7 +1611,7 @@ TaggedStatementArray tagged_statements = { { " o.option_id, o.code, o.value, o.formatted_value, o.space, " " o.persistent, o.user_context, " " r.reservation_id, r.address, r.prefix_len, r.type, " - " r.dhcp6_iaid " + " r.dhcp6_iaid, r.auth_key " "FROM hosts AS h " "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id " "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id " @@ -1622,12 +1636,12 @@ TaggedStatementArray tagged_statements = { { //PgSqlHostDataSourceImpl::INSERT_V6_RESRV // Inserts a single IPv6 reservation into 'reservations' table. - {5, + {6, { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 }, "insert_v6_resrv", "INSERT INTO ipv6_reservations(address, prefix_len, type, " - " dhcp6_iaid, host_id) " - "VALUES ($1, $2, $3, $4, $5)" + " dhcp6_iaid, host_id, auth_key) " + "VALUES ($1, $2, $3, $4, $5, $6)" }, // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index fc3bc0dfd0..a2ac2b7465 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -1506,6 +1506,25 @@ PgSqlLeaseMgr::getLeases6(SubnetID subnet_id) const { return (result); } +Lease6Collection +PgSqlLeaseMgr::getLeases6( const DUID& duid ) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_DUID) + .arg(duid.toText()); + + Lease6Collection result = getLeases6(); + + //erase the ones not containing the matching DUID + for (auto iter = result.begin(); iter != result.end(); + iter++) { + if ((*iter)->duid_->getDuid() != duid.getDuid()) { + result.erase(iter); + } + } + + return (result); +} + Lease6Collection PgSqlLeaseMgr::getLeases6() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET6); diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index e80a20f2b2..423f820bcf 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -309,6 +309,11 @@ public: getLeases6(const asiolink::IOAddress& lower_bound_address, const LeasePageSize& page_size) const; + /// @brief Returns all IPv6 leases. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection getLeases6(const DUID& duid) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc index 46c21e5337..2c657f0ccf 100644 --- a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc @@ -585,7 +585,7 @@ TEST_F(CqlHostDataSourceTest, testAddRollback) { destroyCqlSchema(false, true); // Create a host with a reservation. - HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false); + HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false, "key##1"); // Let's assign some DHCPv4 subnet to the host, because we will use the // DHCPv4 subnet to try to retrieve the host after failed insertion. host->setIPv4SubnetID(SubnetID(4)); diff --git a/src/lib/dhcpsrv/tests/host_unittest.cc b/src/lib/dhcpsrv/tests/host_unittest.cc index 674bca8009..983e94a9e7 100644 --- a/src/lib/dhcpsrv/tests/host_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_unittest.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include using namespace isc; @@ -34,6 +35,14 @@ TEST(IPv6ResrvTest, constructorAddress) { EXPECT_EQ("2001:db8:1::cafe", resrv.getPrefix().toText()); EXPECT_EQ(128, resrv.getPrefixLen()); EXPECT_EQ(IPv6Resrv::TYPE_NA, resrv.getType()); + EXPECT_EQ("", resrv.getKeys()); + + //create reservation with keys + IPv6Resrv resrv_keys(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::cafe"), "#ssd@@dce3"); + EXPECT_EQ("2001:db8:1::cafe", resrv_keys.getPrefix().toText()); + EXPECT_EQ(128, resrv_keys.getPrefixLen()); + EXPECT_EQ(IPv6Resrv::TYPE_NA, resrv_keys.getType()); + EXPECT_EQ("#ssd@@dce3", resrv_keys.getKeys()); } // This test verifies that it is possible to create IPv6 prefix @@ -43,9 +52,18 @@ TEST(IPv6ResrvTest, constructorPrefix) { EXPECT_EQ("2001:db8:1::", resrv.getPrefix().toText()); EXPECT_EQ(64, resrv.getPrefixLen()); EXPECT_EQ(IPv6Resrv::TYPE_PD, resrv.getType()); + EXPECT_EQ("", resrv.getKeys()); + + //create reservation with keys + IPv6Resrv resrv_keys(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1::"), "#ssd@@dce3", 64); + EXPECT_EQ("2001:db8:1::", resrv_keys.getPrefix().toText()); + EXPECT_EQ(64, resrv_keys.getPrefixLen()); + EXPECT_EQ(IPv6Resrv::TYPE_PD, resrv_keys.getType()); + EXPECT_EQ("#ssd@@dce3", resrv_keys.getKeys()); } // This test verifies that the toText() function prints correctly. +// @todo: Add test to keys once toText() for keys is implemented. TEST(IPv6ResrvTest, toText) { IPv6Resrv resrv_prefix(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1::"), 64); EXPECT_EQ("2001:db8:1::/64", resrv_prefix.toText()); @@ -102,6 +120,39 @@ TEST(IPv6ResrvTest, setPrefix) { isc::BadValue); } +// This test verifies that it is possible to modify the keys +// +TEST(IPv6ResrvTest, setKeys) { + // Create an address reservation without assigning keys. + IPv6Resrv resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1")); + ASSERT_EQ("2001:db8:1::1", resrv.getPrefix().toText()); + ASSERT_EQ(128, resrv.getPrefixLen()); + ASSERT_EQ(IPv6Resrv::TYPE_NA, resrv.getType()); + ASSERT_EQ("", resrv.getKeys()); + + // Replace default keys with new value. + resrv.set(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"), 128, "first_set_keys_#"); + ASSERT_EQ("2001:db8:1::1", resrv.getPrefix().toText()); + ASSERT_EQ(128, resrv.getPrefixLen()); + ASSERT_EQ(IPv6Resrv::TYPE_NA, resrv.getType()); + ASSERT_EQ("first_set_keys_#", resrv.getKeys()); + + // Modify an existing key for the reservation + resrv.set(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"), 128, "second_set_keys_#"); + ASSERT_EQ("2001:db8:1::1", resrv.getPrefix().toText()); + ASSERT_EQ(128, resrv.getPrefixLen()); + ASSERT_EQ(IPv6Resrv::TYPE_NA, resrv.getType()); + ASSERT_EQ("second_set_keys_#", resrv.getKeys()); + + // Enusre not including the key parameter won't affect + // the current configured keys + resrv.set(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"), 128); + ASSERT_EQ("2001:db8:1::1", resrv.getPrefix().toText()); + ASSERT_EQ(128, resrv.getPrefixLen()); + ASSERT_EQ(IPv6Resrv::TYPE_NA, resrv.getType()); + ASSERT_EQ("second_set_keys_#", resrv.getKeys()); +} + // This test checks that the equality operators work fine. TEST(IPv6ResrvTest, equal) { EXPECT_TRUE(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::"), 64) == @@ -130,6 +181,23 @@ TEST(IPv6ResrvTest, equal) { EXPECT_TRUE(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::1"), 128) != IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::1"), 128)); + EXPECT_TRUE(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::1"), + "key##1", 128) == + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::1"), + "key##1", 128)); + EXPECT_FALSE(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::1"), + "key##1", 128) != + IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::1"), + "key##1", 128)); + + EXPECT_FALSE(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::1"), + "key##1", 128) == + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::1"), + "key##2", 128)); + EXPECT_TRUE(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::1"), + "key##1", 128) != + IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::1"), + "key##2", 128)); } /// @brief Test fixture class for @c Host. @@ -607,12 +675,12 @@ TEST_F(HostTest, addReservations) { EXPECT_FALSE(host->hasIPv6Reservation()); - // Add 4 reservations: 2 for NAs, 2 for PDs. + // Add 4 reservations: 2 for NAs, 2 for PDs ASSERT_NO_THROW( host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::cafe"))); + IOAddress("2001:db8:1::cafe"), "key##1")); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1:1::"), 64)); + IOAddress("2001:db8:1:1::"), "key##2", 64)); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:2::"), 64)); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, @@ -623,9 +691,9 @@ TEST_F(HostTest, addReservations) { // Check that reservations exist. EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::cafe")))); + IOAddress("2001:db8:1::cafe"), "key##1"))); EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1:1::"), + IOAddress("2001:db8:1:1::"), "key##2", 64))); EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:2::"), @@ -637,7 +705,7 @@ TEST_F(HostTest, addReservations) { IPv6ResrvRange addresses = host->getIPv6Reservations(IPv6Resrv::TYPE_NA); ASSERT_EQ(2, std::distance(addresses.first, addresses.second)); EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::cafe")), + IOAddress("2001:db8:1::cafe"), "key##1"), addresses)); EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1")), @@ -648,7 +716,7 @@ TEST_F(HostTest, addReservations) { IPv6ResrvRange prefixes = host->getIPv6Reservations(IPv6Resrv::TYPE_PD); ASSERT_EQ(2, std::distance(prefixes.first, prefixes.second)); EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1:1::"), 64), + IOAddress("2001:db8:1:1::"),"key##2", 64), prefixes)); EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:2::"), 64), @@ -1038,6 +1106,7 @@ TEST_F(HostTest, toText) { } // This test checks that Host object is correctly unparsed, +// @todo: add support for keys TEST_F(HostTest, unparse) { boost::scoped_ptr host; ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address", @@ -1209,4 +1278,32 @@ TEST_F(HostTest, hostId) { EXPECT_EQ(12345, host->getHostId()); } +// Test verifies if getRandomKeyString can generate 1000 keys which are random +TEST_F(HostTest, randomKeys) { + //use hashtable and set size to 1000 + std::unordered_map key_map; + + int dup_element = 0; + const uint16_t max_iter = 1000; + uint16_t iter_num = 0; + size_t max_hash_size = 1000; + + key_map.reserve(max_hash_size); + + for (iter_num = 0; iter_num < max_iter; iter_num++) { + std::string key = IPv6Resrv::getRandomKeyString(); + if (key_map[key]) { + dup_element++; + break; + } + + key_map[key] = 1; + } + + EXPECT_EQ(0, dup_element); +} + + } // end of anonymous namespace + +// Test verifies if diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index d32379fe8d..40e262741a 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -208,6 +208,14 @@ public: return (leases6_); } + /// @brief Returns collection of lease for matching DUID + /// + /// @param duid ignored + /// @return whatever is set in leases6_ field + virtual Lease6Collection getLeases6(const DUID& duid) const { + return (leases6_); + } + /// @brief Returns all IPv6 leases for the particular subnet identifier. /// /// @param subnet_id subnet identifier. diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc index df2122d475..6d787f9a08 100644 --- a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc @@ -571,7 +571,7 @@ TEST_F(PgSqlHostDataSourceTest, testAddRollback) { << " drop command failed :" << PQerrorMessage(conn); // Create a host with a reservation. - HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false); + HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false, "randomKey"); // Let's assign some DHCPv4 subnet to the host, because we will use the // DHCPv4 subnet to try to retrieve the host after failed insertion. host->setIPv4SubnetID(SubnetID(4)); diff --git a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc index 2e7ef08ad8..3d7324db82 100644 --- a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc @@ -706,7 +706,7 @@ GenericHostDataSourceTest::testSubnetId6(int subnets, Host::IdentifierType id) { for (int i = 0; i < subnets; ++i) { // Last boolean value set to false indicates that the same identifier // must be used for each generated host. - host = HostDataSourceUtils::initializeHost6(current_address.toText(), id, true, false); + host = HostDataSourceUtils::initializeHost6(current_address.toText(), id, true, "", false); host->setIPv4SubnetID(i + 1000); host->setIPv6SubnetID(i + 1000); @@ -754,10 +754,14 @@ GenericHostDataSourceTest::testGetByIPv6(Host::IdentifierType id, bool prefix) { ASSERT_TRUE(hdsptr_); // Let's create a couple of hosts... - HostPtr host1 = HostDataSourceUtils::initializeHost6("2001:db8::1", id, prefix); - HostPtr host2 = HostDataSourceUtils::initializeHost6("2001:db8::2", id, prefix); - HostPtr host3 = HostDataSourceUtils::initializeHost6("2001:db8::3", id, prefix); - HostPtr host4 = HostDataSourceUtils::initializeHost6("2001:db8::4", id, prefix); + HostPtr host1 = HostDataSourceUtils::initializeHost6("2001:db8::1", + id, prefix, "key##1"); + HostPtr host2 = HostDataSourceUtils::initializeHost6("2001:db8::2", + id, prefix, "key##2"); + HostPtr host3 = HostDataSourceUtils::initializeHost6("2001:db8::3", + id, prefix, "key##3"); + HostPtr host4 = HostDataSourceUtils::initializeHost6("2001:db8::4", + id, prefix, "key##4"); // ... and add them to the data source. ASSERT_NO_THROW(hdsptr_->add(host1)); @@ -797,10 +801,10 @@ GenericHostDataSourceTest::testGetBySubnetIPv6() { ASSERT_TRUE(hdsptr_); // Let's create a couple of hosts... - HostPtr host1 = HostDataSourceUtils::initializeHost6("2001:db8:1::", Host::IDENT_DUID, true); - HostPtr host2 = HostDataSourceUtils::initializeHost6("2001:db8:2::", Host::IDENT_DUID, true); - HostPtr host3 = HostDataSourceUtils::initializeHost6("2001:db8:3::", Host::IDENT_DUID, true); - HostPtr host4 = HostDataSourceUtils::initializeHost6("2001:db8:4::", Host::IDENT_DUID, true); + HostPtr host1 = HostDataSourceUtils::initializeHost6("2001:db8:1::", Host::IDENT_DUID, true, "key##1"); + HostPtr host2 = HostDataSourceUtils::initializeHost6("2001:db8:2::", Host::IDENT_DUID, true, "key##2"); + HostPtr host3 = HostDataSourceUtils::initializeHost6("2001:db8:3::", Host::IDENT_DUID, true, "key##3"); + HostPtr host4 = HostDataSourceUtils::initializeHost6("2001:db8:4::", Host::IDENT_DUID, true, "key##4"); // ... and add them to the data source. ASSERT_NO_THROW(hdsptr_->add(host1)); @@ -889,10 +893,11 @@ GenericHostDataSourceTest::testAddr6AndPrefix() { ASSERT_TRUE(hdsptr_); // Create a host reservations with prefix reservation (prefix = true) - HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, true); + HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, + true, "key##1", true); // Create IPv6 reservation (for an address) and add it to the host - IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::2"), 128); + IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::2"), "key##2", 128); host->addReservation(resv); // Add this reservation @@ -920,10 +925,10 @@ GenericHostDataSourceTest::testMultipleReservations() { HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, false); // Add some reservations - IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len); - IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::7"), len); - IPv6Resrv resv3(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::8"), len); - IPv6Resrv resv4(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::9"), len); + IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), "key##1", len); + IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::7"), "key##2", len); + IPv6Resrv resv3(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::8"), "key##3", len); + IPv6Resrv resv4(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::9"), "key##4", len); host->addReservation(resv1); host->addReservation(resv2); @@ -951,10 +956,10 @@ GenericHostDataSourceTest::testMultipleReservationsDifferentOrder() { HostPtr host2 = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, false); // Add some reservations - IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len); - IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::7"), len); - IPv6Resrv resv3(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::8"), len); - IPv6Resrv resv4(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::9"), len); + IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), "key##1", len); + IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::7"), "key##2", len); + IPv6Resrv resv3(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::8"), "key##3", len); + IPv6Resrv resv4(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::9"), "key##4", len); host1->addReservation(resv1); host1->addReservation(resv2); @@ -1246,7 +1251,11 @@ GenericHostDataSourceTest::stressTest(unsigned int nOfHosts /* = 0xfffdU */) { ss >> n_host; const std::string prefix = std::string("2001:db8::") + n_host; - hosts.push_back(HostDataSourceUtils::initializeHost6(prefix, Host::IDENT_HWADDR, false)); + const std::string keys = + std::string("arbitary_long_enough_for_stress_test" + "but_less_than_128_characters"); + hosts.push_back(HostDataSourceUtils::initializeHost6(prefix, Host::IDENT_HWADDR, false, keys)); + IPv6ResrvRange range = hosts.back()->getIPv6Reservations(); ASSERT_EQ(1, std::distance(range.first, range.second)); EXPECT_TRUE(HostDataSourceUtils::reservationExists @@ -1410,7 +1419,7 @@ void GenericHostDataSourceTest::testDeleteById6() { ASSERT_TRUE(hdsptr_); // Let's create a v6 host... - HostPtr host1 = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, false); + HostPtr host1 = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, false, "key##1"); SubnetID subnet1 = host1->getIPv6SubnetID(); // ... and add it to the data source. diff --git a/src/lib/dhcpsrv/testutils/host_data_source_utils.cc b/src/lib/dhcpsrv/testutils/host_data_source_utils.cc index 09239970e0..364f26ecb9 100644 --- a/src/lib/dhcpsrv/testutils/host_data_source_utils.cc +++ b/src/lib/dhcpsrv/testutils/host_data_source_utils.cc @@ -81,9 +81,10 @@ HostDataSourceUtils::initializeHost4(const std::string& address, } HostPtr -HostDataSourceUtils::initializeHost6(std::string address, +HostDataSourceUtils::initializeHost6(const std::string address, Host::IdentifierType identifier, bool prefix, + const std::string& key, bool new_identifier) { std::vector ident; switch (identifier) { @@ -111,11 +112,11 @@ HostDataSourceUtils::initializeHost6(std::string address, if (!prefix) { // Create IPv6 reservation (for an address) - IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress(address), 128); + IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress(address), key, 128); host->addReservation(resv); } else { // Create IPv6 reservation for a /64 prefix - IPv6Resrv resv(IPv6Resrv::TYPE_PD, IOAddress(address), 64); + IPv6Resrv resv(IPv6Resrv::TYPE_PD, IOAddress(address), key, 64); host->addReservation(resv); } return (host); diff --git a/src/lib/dhcpsrv/testutils/host_data_source_utils.h b/src/lib/dhcpsrv/testutils/host_data_source_utils.h index f46906f02e..1b5251c751 100644 --- a/src/lib/dhcpsrv/testutils/host_data_source_utils.h +++ b/src/lib/dhcpsrv/testutils/host_data_source_utils.h @@ -38,8 +38,8 @@ public: /// identifier should be generated or the same as previously. /// /// @return generated Host object - static HostPtr initializeHost6(std::string address, Host::IdentifierType id, - bool prefix, bool new_identifier = true); + static HostPtr initializeHost6(const std::string address, Host::IdentifierType id, + bool prefix, const std::string& key = "", bool new_identifier = true); /// @brief Generates a hardware address in text version. /// diff --git a/src/share/database/scripts/mysql/dhcpdb_create.mysql b/src/share/database/scripts/mysql/dhcpdb_create.mysql index 2b166a0c9b..99d7659f03 100644 --- a/src/share/database/scripts/mysql/dhcpdb_create.mysql +++ b/src/share/database/scripts/mysql/dhcpdb_create.mysql @@ -176,6 +176,7 @@ CREATE TABLE IF NOT EXISTS ipv6_reservations ( prefix_len TINYINT(3) UNSIGNED NOT NULL DEFAULT 128, type TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, dhcp6_iaid INT UNSIGNED NULL, + auth_key VARCHAR(128) NOT NULL, host_id INT UNSIGNED NOT NULL, PRIMARY KEY (reservation_id), INDEX fk_ipv6_reservations_host_idx (host_id ASC), diff --git a/src/share/database/scripts/mysql/upgrade_5.2_to_6.0.sh.in b/src/share/database/scripts/mysql/upgrade_5.2_to_6.0.sh.in index 3bf129cce4..41736c9b6e 100644 --- a/src/share/database/scripts/mysql/upgrade_5.2_to_6.0.sh.in +++ b/src/share/database/scripts/mysql/upgrade_5.2_to_6.0.sh.in @@ -23,6 +23,7 @@ ALTER TABLE hosts ADD COLUMN user_context TEXT NULL; # Add user contexts into tables holding DHCP options ALTER TABLE dhcp4_options ADD COLUMN user_context TEXT NULL; ALTER TABLE dhcp6_options ADD COLUMN user_context TEXT NULL; +ALTER TABLE ipv6_reservations ADD COLUMN auth_key VARCHAR(128) NOT NULL; # Create index for searching leases by subnet identifier. CREATE INDEX lease4_by_subnet_id ON lease4 (subnet_id); diff --git a/src/share/database/scripts/pgsql/dhcpdb_create.pgsql b/src/share/database/scripts/pgsql/dhcpdb_create.pgsql index 5e8fd059cf..e5c7e30c3d 100644 --- a/src/share/database/scripts/pgsql/dhcpdb_create.pgsql +++ b/src/share/database/scripts/pgsql/dhcpdb_create.pgsql @@ -335,6 +335,7 @@ CREATE TABLE ipv6_reservations ( prefix_len SMALLINT NOT NULL DEFAULT '128', type SMALLINT NOT NULL DEFAULT '0', dhcp6_iaid INT DEFAULT NULL, + auth_key VARCHAR(128) NOT NULL, host_id INT NOT NULL, CONSTRAINT key_dhcp6_address_prefix_len UNIQUE (address, prefix_len), CONSTRAINT fk_ipv6_reservations_host FOREIGN KEY (host_id) REFERENCES hosts (host_id) ON DELETE CASCADE