]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1373] added csv-format for the input data
authorRazvan Becheriu <razvan@isc.org>
Wed, 23 Sep 2020 11:03:41 +0000 (14:03 +0300)
committerTomek Mrugalski <tomek@isc.org>
Fri, 25 Sep 2020 16:31:58 +0000 (18:31 +0200)
src/hooks/dhcp/flex_option/flex_option.cc
src/hooks/dhcp/flex_option/flex_option.h
src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc

index 60c90f2d873bc2628c08da49fbf425a0bebc389e..80c7fb4859f5f5b38677efb52c378020ad8542c1 100644 (file)
@@ -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;
 }
 
index e206c71a8aee9713a58fe327c12c0bbd874bdf4b..dcfe6a36f61a959a4350689d6e4d3a03771f6207 100644 (file)
@@ -14,6 +14,7 @@
 #include <dhcp/std_option_defs.h>
 #include <eval/evaluate.h>
 #include <eval/token.h>
+#include <util/strutil.h>
 
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/classification.hpp>
@@ -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<std::string> 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<std::string> 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<std::string> 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<std::string> 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.
index af611248fcce0e2f50e5c358c7d701a447326c7f..9f57a985645eb30e7e43658f2ec9535aa0ed4377 100644 (file)
@@ -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<Pkt4Ptr>(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<Pkt4Ptr>(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()));
 }