From: Marcin Siodelski Date: Fri, 13 May 2016 08:43:43 +0000 (+0200) Subject: [4281] It is allowed to use formatted option values in the host database. X-Git-Tag: trac4106_update_base~7^2~3^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=29bfd06c381cba48ae8cd1755ede58bb5fed7973;p=thirdparty%2Fkea.git [4281] It is allowed to use formatted option values in the host database. --- diff --git a/src/lib/dhcpsrv/cfg_option.cc b/src/lib/dhcpsrv/cfg_option.cc index 5b13ebae41..4532e5a217 100644 --- a/src/lib/dhcpsrv/cfg_option.cc +++ b/src/lib/dhcpsrv/cfg_option.cc @@ -16,6 +16,7 @@ namespace dhcp { bool OptionDescriptor::equals(const OptionDescriptor& other) const { return (persistent_ == other.persistent_ && + formatted_value_ == other.formatted_value_ && option_->equals(other.option_)); } diff --git a/src/lib/dhcpsrv/cfg_option.h b/src/lib/dhcpsrv/cfg_option.h index a8d4400a7a..52f85f96bd 100644 --- a/src/lib/dhcpsrv/cfg_option.h +++ b/src/lib/dhcpsrv/cfg_option.h @@ -31,24 +31,46 @@ namespace dhcp { /// to DHCP client only on request (persistent = false) or always /// (persistent = true). struct OptionDescriptor { - /// Option instance. + /// @brief Option instance. OptionPtr option_; - /// Persistent flag, if true option is always sent to the client, - /// if false option is sent to the client on request. + + /// @briefPersistence flag. + /// + /// If true option is always sent to the client, if false option is + /// sent to the client on request. bool persistent_; + /// @brief Option value in textual (CSV) format. + /// + /// This field is used to convey option value in human readable format, + /// the same as used to specify option value in the server configuration. + /// This value is optional and can be held in the host reservations + /// database instead of the binary format. + /// + /// Note that this value is carried in the option descriptor, rather than + /// @c Option instance because it is a server specific value. + /// + /// An example of the formatted value is: 2001:db8:1::1, 23, some text + /// for the option which carries IPv6 address, a number and a text. + std::string formatted_value_; + /// @brief Constructor. /// /// @param opt option /// @param persist if true option is always sent. - OptionDescriptor(const OptionPtr& opt, bool persist) - : option_(opt), persistent_(persist) {}; + /// @param formatted_value option value in the textual format. Default + /// value is empty indicating that the value is not set. + OptionDescriptor(const OptionPtr& opt, bool persist, + const std::string& formatted_value = "") + : option_(opt), persistent_(persist), + formatted_value_(formatted_value) {}; /// @brief Constructor /// /// @param persist if true option is always sent. OptionDescriptor(bool persist) - : option_(OptionPtr()), persistent_(persist) {}; + : option_(OptionPtr()), persistent_(persist), + formatted_value_() {}; /// @brief Checks if the one descriptor is equal to another. /// diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc index 48d5eb09f6..ada7c49647 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.cc +++ b/src/lib/dhcpsrv/mysql_host_data_source.cc @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include @@ -83,15 +85,15 @@ TaggedStatement tagged_statements[] = { // Inserts a single DHCPv4 option into 'dhcp4_options' table. {MySqlHostDataSource::INSERT_V4_OPTION, - "INSERT INTO dhcp4_options(option_id, code, value, space, persistent, " - "dhcp_client_class, dhcp4_subnet_id, host_id) " - " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"}, + "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, " + "persistent, dhcp_client_class, dhcp4_subnet_id, host_id) " + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"}, // Inserts a single DHCPv6 option into 'dhcp6_options' table. {MySqlHostDataSource::INSERT_V6_OPTION, - "INSERT INTO dhcp6_options(option_id, code, value, space, persistent, " - "dhcp_client_class, dhcp6_subnet_id, host_id) " - " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"}, + "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, " + "persistent, dhcp_client_class, dhcp6_subnet_id, host_id) " + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"}, // Retrieves host information along with IPv6 reservations associated // with this host. If the host exists in multiple subnets, all hosts @@ -789,8 +791,17 @@ private: if (most_recent_option_id_ < option_id_) { most_recent_option_id_ = option_id_; - space_[space_length_] = '\0'; - std::string space(space_); + std::string space; + if (space_null_ == MLM_FALSE) { + space_[space_length_] = '\0'; + space.assign(space_); + } + + std::string formatted_value; + if (formatted_value_null_ == MLM_FALSE) { + formatted_value_[formatted_value_length_] = '\0'; + formatted_value.assign(formatted_value_); + } OptionDefinitionPtr def; if ((space == DHCP4_OPTION_SPACE) || (space == DHCP6_OPTION_SPACE)) { @@ -809,17 +820,26 @@ private: def = LibDHCP::getRuntimeOptionDef(space, code_); } + OptionPtr option; - OptionBuffer buf(value_, value_ + value_length_); - if (def) { - option = def->optionFactory(universe_, code_, buf.begin(), - buf.end()); - } else { + + if (!def) { + OptionBuffer buf(value_, value_ + value_length_); option.reset(new Option(universe_, code_, buf.begin(), buf.end())); + } else { + if (formatted_value.empty()) { + OptionBuffer buf(value_, value_ + value_length_); + option = def->optionFactory(universe_, code_, buf.begin(), + buf.end()); + } else { + std::vector split_vec; + boost::split(split_vec, formatted_value, boost::is_any_of(",")); + option = def->optionFactory(universe_, code_, split_vec); + } } - OptionDescriptor desc(option, persistent_); + OptionDescriptor desc(option, persistent_, formatted_value); cfg->add(desc, space); } } @@ -1442,16 +1462,16 @@ private: class MySqlOptionExchange { private: - static const size_t OPTION_COLUMNS = 8; + static const size_t OPTION_COLUMNS = 9; public: MySqlOptionExchange() - : type_(0), value_len_(0), space_(), space_len_(0), + : type_(0), value_len_(0), formatted_value_len_(0), space_(), space_len_(0), persistent_(false), client_class_(), client_class_len_(0), subnet_id_(0), host_id_(0), option_() { - BOOST_STATIC_ASSERT(7 < OPTION_COLUMNS); + BOOST_STATIC_ASSERT(8 < OPTION_COLUMNS); } @@ -1482,7 +1502,8 @@ public: // value: BLOB NULL OutputBuffer buf(opt_desc.option_->len()); opt_desc.option_->pack(buf); - if (buf.getLength() > opt_desc.option_->getHeaderLen()) { + if ((buf.getLength() > opt_desc.option_->getHeaderLen()) && + opt_desc.formatted_value_.empty()) { const char* buf_ptr = static_cast(buf.getData()); value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(), buf_ptr + buf.getLength()); @@ -1497,44 +1518,56 @@ public: bind_[2].buffer_type = MYSQL_TYPE_NULL; } + // formatted_value: TEXT NULL, + if (!opt_desc.formatted_value_.empty()) { + formatted_value_len_ = opt_desc.formatted_value_.size(); + bind_[3].buffer_type = MYSQL_TYPE_STRING; + bind_[3].buffer = const_cast(opt_desc.formatted_value_.c_str()); + bind_[3].buffer_length = formatted_value_len_; + bind_[3].length = &formatted_value_len_; + + } else { + bind_[3].buffer_type = MYSQL_TYPE_NULL; + } + // space: VARCHAR(128) NULL space_ = opt_space; space_len_ = space_.size(); - bind_[3].buffer_type = MYSQL_TYPE_STRING; - bind_[3].buffer = const_cast(space_.c_str()); - bind_[3].buffer_length = space_len_; - bind_[3].length = &space_len_; + bind_[4].buffer_type = MYSQL_TYPE_STRING; + bind_[4].buffer = const_cast(space_.c_str()); + bind_[4].buffer_length = space_len_; + bind_[4].length = &space_len_; // persistent: TINYINT(1) NOT NULL DEFAULT 0 persistent_ = opt_desc.persistent_; - bind_[4].buffer_type = MYSQL_TYPE_TINY; - bind_[4].buffer = reinterpret_cast(&persistent_); - bind_[4].is_unsigned = MLM_TRUE; + bind_[5].buffer_type = MYSQL_TYPE_TINY; + bind_[5].buffer = reinterpret_cast(&persistent_); + bind_[5].is_unsigned = MLM_TRUE; // dhcp_client_class: VARCHAR(128) NULL /// @todo Assign actual value to client class string. client_class_len_ = client_class_.size(); - bind_[5].buffer_type = MYSQL_TYPE_STRING; - bind_[5].buffer = const_cast(client_class_.c_str()); - bind_[5].buffer_length = client_class_len_; - bind_[5].length = &client_class_len_; + bind_[6].buffer_type = MYSQL_TYPE_STRING; + bind_[6].buffer = const_cast(client_class_.c_str()); + bind_[6].buffer_length = client_class_len_; + bind_[6].length = &client_class_len_; // dhcp4_subnet_id: INT UNSIGNED NULL if (subnet_id.isSpecified()) { subnet_id_ = subnet_id; - bind_[6].buffer_type = MYSQL_TYPE_LONG; - bind_[6].buffer = reinterpret_cast(subnet_id_); - bind_[6].is_unsigned = MLM_TRUE; + bind_[7].buffer_type = MYSQL_TYPE_LONG; + bind_[7].buffer = reinterpret_cast(subnet_id_); + bind_[7].is_unsigned = MLM_TRUE; } else { - bind_[6].buffer_type = MYSQL_TYPE_NULL; + bind_[7].buffer_type = MYSQL_TYPE_NULL; } // host_id: INT UNSIGNED NOT NULL host_id_ = host_id; - bind_[7].buffer_type = MYSQL_TYPE_LONG; - bind_[7].buffer = reinterpret_cast(&host_id_); - bind_[7].is_unsigned = MLM_TRUE; + bind_[8].buffer_type = MYSQL_TYPE_LONG; + bind_[8].buffer = reinterpret_cast(&host_id_); + bind_[8].is_unsigned = MLM_TRUE; } catch (const std::exception& ex) { isc_throw(DbOperationError, @@ -1554,6 +1587,9 @@ private: size_t value_len_; + /// Formatted option value length. + unsigned long formatted_value_len_; + std::string space_; size_t space_len_; diff --git a/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc index 992a9ef957..94c3ff8ce7 100644 --- a/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc @@ -354,6 +354,7 @@ GenericHostDataSourceTest::compareOptions(const ConstCfgOptionPtr& cfg1, BOOST_FOREACH(OptionDescriptor desc1, *options1) { OptionDescriptor desc2 = cfg2->get(space, desc1.option_->getType()); ASSERT_EQ(desc1.persistent_, desc2.persistent_); + ASSERT_EQ(desc1.formatted_value_, desc2.formatted_value_); Option* option1 = desc1.option_.get(); Option* option2 = desc2.option_.get(); @@ -376,33 +377,49 @@ GenericHostDataSourceTest::compareOptions(const ConstCfgOptionPtr& cfg1, } } +OptionDescriptor +GenericHostDataSourceTest::createEmptyOption(const Option::Universe& universe, + const uint16_t option_type, + const bool persist) const { + OptionPtr option(new Option(universe, option_type)); + OptionDescriptor desc(option, persist); + return (desc); +} + OptionDescriptor GenericHostDataSourceTest::createVendorOption(const Option::Universe& universe, const bool persist, + const bool formatted, const uint32_t vendor_id) const { OptionVendorPtr option(new OptionVendor(universe, vendor_id)); - OptionDescriptor desc(option, persist); + + std::ostringstream s; + if (formatted) { + s << vendor_id; + } + + OptionDescriptor desc(option, persist, s.str()); return (desc); } void -GenericHostDataSourceTest::addTestOptions(const HostPtr& host) const { +GenericHostDataSourceTest::addTestOptions(const HostPtr& host, + const bool formatted) const { // Add DHCPv4 options. CfgOptionPtr opts = host->getCfgOption4(); opts->add(createOption(Option::V4, DHO_BOOT_FILE_NAME, - true, "my-boot-file"), + true, formatted, "my-boot-file"), DHCP4_OPTION_SPACE); opts->add(createOption(Option::V4, DHO_DEFAULT_IP_TTL, - false, 64), + false, formatted, 64), DHCP4_OPTION_SPACE); - opts->add(createOption(Option::V4, 1, false, 312131), + opts->add(createOption(Option::V4, 1, false, formatted, 312131), "vendor-encapsulated-options"); - opts->add(createAddressOption(254, false, "192.0.2.3"), + opts->add(createAddressOption(254, false, formatted, "192.0.2.3"), DHCP4_OPTION_SPACE); - opts->add(createOption