From: Razvan Becheriu Date: Wed, 23 Sep 2020 11:03:41 +0000 (+0300) Subject: [#1373] added csv-format for the input data X-Git-Tag: Kea-1.9.0~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be22f2876870f12e922cfc68973ef61907143c8d;p=thirdparty%2Fkea.git [#1373] added csv-format for the input data --- diff --git a/src/hooks/dhcp/flex_option/flex_option.cc b/src/hooks/dhcp/flex_option/flex_option.cc index 60c90f2d87..80c7fb4859 100644 --- a/src/hooks/dhcp/flex_option/flex_option.cc +++ b/src/hooks/dhcp/flex_option/flex_option.cc @@ -75,7 +75,7 @@ namespace isc { namespace flex_option { FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code) - : code_(code), action_(NONE) { + : code_(code), action_(NONE), csv_format_(true) { } FlexOptionImpl::OptionConfig::~OptionConfig() { @@ -115,6 +115,7 @@ FlexOptionImpl::parseOptionConfig(ConstElementPtr option) { } ConstElementPtr code_elem = option->get("code"); ConstElementPtr name_elem = option->get("name"); + ConstElementPtr csv_format_elem = option->get("csv-format"); if (!code_elem && !name_elem) { isc_throw(BadValue, "'code' or 'name' must be specified: " << option->str()); @@ -189,6 +190,15 @@ FlexOptionImpl::parseOptionConfig(ConstElementPtr option) { isc_throw(BadValue, "option " << code << " was already specified"); } + bool csv_format = true; + if (csv_format_elem) { + if (csv_format_elem->getType() != Element::boolean) { + isc_throw(BadValue, "'csv-format' must be a boolean: " + << csv_format_elem->str()); + } + csv_format = csv_format_elem->boolValue(); + } + Option::Universe universe; if (family == AF_INET) { universe = Option::V4; @@ -197,6 +207,11 @@ FlexOptionImpl::parseOptionConfig(ConstElementPtr option) { } OptionConfigPtr opt_cfg(new OptionConfig(code)); + + if (csv_format_elem) { + opt_cfg->setCSVFormat(csv_format); + } + // opt_cfg initial action is NONE. parseAction(option, opt_cfg, universe, "add", ADD, EvalContext::PARSER_STRING); @@ -208,6 +223,7 @@ FlexOptionImpl::parseOptionConfig(ConstElementPtr option) { if (opt_cfg->getAction() == NONE) { isc_throw(BadValue, "no action: " << option->str()); } + option_config_map_[code] = opt_cfg; } diff --git a/src/hooks/dhcp/flex_option/flex_option.h b/src/hooks/dhcp/flex_option/flex_option.h index e206c71a8a..dcfe6a36f6 100644 --- a/src/hooks/dhcp/flex_option/flex_option.h +++ b/src/hooks/dhcp/flex_option/flex_option.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -108,6 +109,20 @@ public: return (expr_); } + /// @brief Set csv format. + /// + /// @param flag the csv format. + void setCSVFormat(bool flag) { + csv_format_ = flag; + } + + /// @brief Return csv format. + /// + /// @return the csv format. + bool getCSVFormat() const { + return (csv_format_); + } + private: /// @brief The code. uint16_t code_; @@ -120,6 +135,9 @@ public: /// @brief The match expression. isc::dhcp::ExpressionPtr expr_; + + /// @brief The csv format flag of the option. + bool csv_format_; }; /// @brief The type of shared pointers to option config. @@ -179,29 +197,31 @@ public: break; } - def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode()); + if (opt_cfg->getCSVFormat()) { + def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode()); - if (!def) { - def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode()); - } + if (!def) { + def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode()); + } - if (!def) { - def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode()); - } + if (!def) { + def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode()); + } - if (!def) { + if (!def) { + buffer.assign(value.begin(), value.end()); + opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(), + buffer)); + } else { + std::vector split_vec = + isc::util::str::tokens(value, ",", true); + opt = def->optionFactory(universe, opt_cfg->getCode(), + split_vec); + } + } else { buffer.assign(value.begin(), value.end()); opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(), buffer)); - } else { - std::vector split_vec; - if (def->getType() != isc::dhcp::OPT_STRING_TYPE) { - boost::split(split_vec, value, boost::is_any_of(",")); - } else { - split_vec.push_back(value); - } - opt = def->optionFactory(universe, opt_cfg->getCode(), - split_vec); } // Add the option. @@ -220,29 +240,31 @@ public: opt = response->getOption(opt_cfg->getCode()); } - def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode()); + if (opt_cfg->getCSVFormat()) { + def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode()); - if (!def) { - def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode()); - } + if (!def) { + def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode()); + } - if (!def) { - def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode()); - } + if (!def) { + def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode()); + } - if (!def) { + if (!def) { + buffer.assign(value.begin(), value.end()); + opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(), + buffer)); + } else { + std::vector split_vec = + isc::util::str::tokens(value, ",", true); + opt = def->optionFactory(universe, opt_cfg->getCode(), + split_vec); + } + } else { buffer.assign(value.begin(), value.end()); opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(), buffer)); - } else { - std::vector split_vec; - if (def->getType() != isc::dhcp::OPT_STRING_TYPE) { - boost::split(split_vec, value, boost::is_any_of(",")); - } else { - split_vec.push_back(value); - } - opt = def->optionFactory(universe, opt_cfg->getCode(), - split_vec); } // Add the option. diff --git a/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc index af611248fc..9f57a98564 100644 --- a/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc +++ b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc @@ -311,6 +311,19 @@ TEST_F(FlexOptionTest, optionConfigCodeNameMismatch) { EXPECT_EQ(expected, impl_->getErrMsg()); } +// Verify that the csv-format must be a boolean. +TEST_F(FlexOptionTest, optionConfigBadCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr csv_format = Element::create(123); + option->set("csv-format", csv_format); + ElementPtr code = Element::create(12); + option->set("code", code); + EXPECT_THROW(impl_->testConfigure(options), BadValue); + EXPECT_EQ("'csv-format' must be a boolean: 123", impl_->getErrMsg()); +} + // Verify that an option can be configured only once. TEST_F(FlexOptionTest, optionConfigTwice) { ElementPtr options = Element::createList(); @@ -799,6 +812,55 @@ TEST_F(FlexOptionTest, processAdd) { EXPECT_EQ(0, buffer_fqdn[12]); } +// Verify that ADD action adds the specified option in binary format. +TEST_F(FlexOptionTest, processAddDisableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr csv_format = Element::create(false); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr add = Element::create(string("'abc'")); + option->set("add", add); + option->set("csv-format", csv_format); + + option = Element::createMap(); + options->add(option); + code = Element::create(DHO_DOMAIN_SEARCH); + option->set("code", code); + add = Element::create(string("0x076578616d706c6503636f6d00")); + option->set("add", add); + option->set("csv-format", csv_format); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH)); + + EXPECT_NO_THROW(impl_->process(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + opt = response->getOption(DHO_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + // Verify that ADD action does not add an already existing option. TEST_F(FlexOptionTest, processAddExisting) { CfgMgr::instance().setFamily(AF_INET6); @@ -895,6 +957,55 @@ TEST_F(FlexOptionTest, processSupersede) { EXPECT_EQ(0, buffer_fqdn[12]); } +// Verify that SUPERSEDE action supersedes the specified option in binary format. +TEST_F(FlexOptionTest, processSupersedeDisableCSVFormat) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr csv_format = Element::create(false); + ElementPtr code = Element::create(DHO_HOST_NAME); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + option->set("csv-format", csv_format); + + option = Element::createMap(); + options->add(option); + code = Element::create(DHO_DOMAIN_SEARCH); + option->set("code", code); + supersede = Element::create(string("0x076578616d706c6503636f6d00")); + option->set("supersede", supersede); + option->set("csv-format", csv_format); + + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + EXPECT_FALSE(response->getOption(DHO_HOST_NAME)); + EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH)); + + EXPECT_NO_THROW(impl_->process(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_HOST_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_HOST_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); + + opt = response->getOption(DHO_DOMAIN_SEARCH); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType()); + const OptionBuffer& buffer_fqdn = opt->getData(); + ASSERT_EQ(13, buffer_fqdn.size()); + EXPECT_EQ(7, buffer_fqdn[0]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7)); + EXPECT_EQ(3, buffer_fqdn[8]); + EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3)); + EXPECT_EQ(0, buffer_fqdn[12]); +} + // Verify that SUPERSEDE action supersedes an already existing option. TEST_F(FlexOptionTest, processSupersedeExisting) { CfgMgr::instance().setFamily(AF_INET6); @@ -1112,8 +1223,8 @@ TEST_F(FlexOptionTest, processFullAddWithComplexString) { ASSERT_TRUE(opt); EXPECT_EQ(D6O_NEW_POSIX_TIMEZONE, opt->getType()); const OptionBuffer& buffer = opt->getData(); - EXPECT_EQ(37, buffer.size()); - std::string data("EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00"); + EXPECT_EQ(35, buffer.size()); + std::string data("EST5EDT4,M3.2.0/02:00,M11.1.0/02:00"); EXPECT_EQ(0, memcmp(&buffer[0], &data[0], buffer.size())); } @@ -1142,8 +1253,8 @@ TEST_F(FlexOptionTest, processFullSupersedeWithComplexString) { ASSERT_TRUE(opt); EXPECT_EQ(D6O_NEW_POSIX_TIMEZONE, opt->getType()); const OptionBuffer& buffer = opt->getData(); - EXPECT_EQ(37, buffer.size()); - std::string data("EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00"); + EXPECT_EQ(35, buffer.size()); + std::string data("EST5EDT4,M3.2.0/02:00,M11.1.0/02:00"); EXPECT_EQ(0, memcmp(&buffer[0], &data[0], buffer.size())); }