summaryrefslogtreecommitdiffstats
path: root/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc')
-rw-r--r--src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc3173
1 files changed, 3173 insertions, 0 deletions
diff --git a/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc b/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc
new file mode 100644
index 0000000..e593239
--- /dev/null
+++ b/src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc
@@ -0,0 +1,3173 @@
+// Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file This file contains tests which verify flexible sub-option.
+
+#include <config.h>
+#include <flex_option.h>
+#include <flex_option_log.h>
+#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option4_addrlst.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option_string.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <eval/eval_context.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks.h>
+
+#include <tests/test_flex_option.h>
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::eval;
+using namespace isc::hooks;
+using namespace isc::flex_option;
+using namespace isc::flex_option::test;
+
+namespace {
+
+/// @brief Test fixture for testing the Flex Option library.
+class FlexSubOptionTest : public BaseFlexOptionTest { };
+
+// Verify that the sub-options configuration must be a list.
+TEST_F(FlexSubOptionTest, configNotList) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createMap();
+ option->set("sub-options", sub_options);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'sub-options' must be a list: { }",
+ impl_->getErrMsg());
+}
+
+// Verify that the sub-options configuration can be the empty list.
+TEST_F(FlexSubOptionTest, configEmpty) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ EXPECT_TRUE(impl_->getOptionConfigMap().empty());
+ EXPECT_TRUE(impl_->getSubOptionConfigMap().empty());
+}
+
+// Verify that a sub-option configuration must exist.
+TEST_F(FlexSubOptionTest, noSubOptionConfig) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option;
+ sub_options->add(sub_option);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("null sub-option element", impl_->getErrMsg());
+}
+
+// Verify that a sub-option configuration must be a map.
+TEST_F(FlexSubOptionTest, subOptionConfigNotMap) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createList();
+ sub_options->add(sub_option);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("sub-option element is not a map", impl_->getErrMsg());
+}
+
+// Verify that multiple actions are not accepted.
+TEST_F(FlexSubOptionTest, configMultipleAction) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_HOST_NAME);
+ option->set("code", code);
+
+ // add and sub-options.
+ ElementPtr add = Element::create(string("'abc'"));
+ option->set("add", add);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ ostringstream errmsg;
+ errmsg << "'sub-options' and 'add' are incompatible in the same entry";
+ EXPECT_EQ(errmsg.str(), impl_->getErrMsg());
+}
+
+// Verify that an unknown option keyword is rejected.
+TEST_F(FlexSubOptionTest, subOptionConfigUnknown) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr unknown = Element::create(string("'ab'"));
+ // The right keyword is remove...
+ sub_option->set("delete", unknown);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("unknown parameter 'delete'", impl_->getErrMsg());
+}
+
+// Verify that a sub-option configuration must have code or name.
+TEST_F(FlexSubOptionTest, subOptionConfigNoCodeName) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ ostringstream errmsg;
+ errmsg << "'code' or 'name' must be specified: " << sub_option->str();
+ EXPECT_EQ(errmsg.str(), impl_->getErrMsg());
+}
+
+// Verify that a sub-option configuration must retrieve a space.
+TEST_F(FlexSubOptionTest, subOptionConfigNoSpace) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("container is not defined: can't get space", impl_->getErrMsg());
+}
+
+// Verify that the v4 sub-option code must be an integer in [0..255].
+TEST_F(FlexSubOptionTest, subOptionConfigBadCode4) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ code = Element::create(false);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'code' must be an integer: false", impl_->getErrMsg());
+
+ code = Element::create(-1);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), OutOfRange);
+ EXPECT_EQ("invalid 'code' value -1 not in [0..255]", impl_->getErrMsg());
+
+ code = Element::create(256);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), OutOfRange);
+ EXPECT_EQ("invalid 'code' value 256 not in [0..255]", impl_->getErrMsg());
+
+ code = Element::create(1);
+ sub_option->set("code", code);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ code = Element::create(254);
+ sub_option->set("code", code);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+}
+
+// Verify that the v6 option code must be an integer in [0..65535].
+TEST_F(FlexSubOptionTest, subOptionConfigBadCode6) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ code = Element::create(false);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'code' must be an integer: false", impl_->getErrMsg());
+
+ code = Element::create(-1);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), OutOfRange);
+ EXPECT_EQ("invalid 'code' value -1 not in [0..65535]", impl_->getErrMsg());
+
+ code = Element::create(65536);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), OutOfRange);
+ EXPECT_EQ("invalid 'code' value 65536 not in [0..65535]", impl_->getErrMsg());
+
+ code = Element::create(1);
+ sub_option->set("code", code);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ code = Element::create(65535);
+ sub_option->set("code", code);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+}
+
+// Verify that the space must be a string.
+TEST_F(FlexSubOptionTest, subOptionConfigBadSpace) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(true);
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'space' must be a string: true", impl_->getErrMsg());
+}
+
+// Verify that the space must be valid.
+TEST_F(FlexSubOptionTest, subOptionConfigInvalidSpace) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("-bad-"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ sub_option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'-bad-' is not a valid space name", impl_->getErrMsg());
+}
+
+// Verify that the name must be a string.
+TEST_F(FlexSubOptionTest, subOptionConfigBadName) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(true);
+ sub_option->set("name", name);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'name' must be a string: true", impl_->getErrMsg());
+}
+
+// Verify that the name must not be empty.
+TEST_F(FlexSubOptionTest, subOptionConfigEmptyName) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string());
+ sub_option->set("name", name);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'name' must not be empty", impl_->getErrMsg());
+}
+
+// Verify that the name must be a known option.
+TEST_F(FlexSubOptionTest, subOptionConfigUnknownName) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("foobar"));
+ sub_option->set("name", name);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("no known 'foobar' sub-option in 'my-space' space",
+ impl_->getErrMsg());
+}
+
+// Verify that the definition is not required when csv-format is not specified.
+TEST_F(FlexSubOptionTest, subOptionConfigUnknownCodeNoCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(109));
+ auto smap = map[109];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the definition is not required when csv-format is false.
+TEST_F(FlexSubOptionTest, subOptionConfigUnknownCodeDisableCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ // Disable csv-format.
+ sub_option->set("csv-format", Element::create(false));
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(109));
+ auto smap = map[109];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the code must be a known sub-option when csv-format is true.
+TEST_F(FlexSubOptionTest, subOptionConfigUnknownCodeEnableCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ // Enable csv-format.
+ sub_option->set("csv-format", Element::create(true));
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("no known sub-option with code '222' in 'my-space' space",
+ impl_->getErrMsg());
+}
+
+// Verify that the name can be an user defined sub-option.
+TEST_F(FlexSubOptionTest, subOptionConfigDefinedName) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-option", 222,
+ "my-space", "string"));
+ defs.addItem(def);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(109));
+ auto smap = map[109];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the name can be a last resort space defined sub-option.
+TEST_F(FlexSubOptionTest, subOptionConfigLastResortName) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-option", 222,
+ VENDOR_ENCAPSULATED_OPTION_SPACE,
+ "string"));
+ defs.addItem(def);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(DHO_VENDOR_ENCAPSULATED_OPTIONS));
+ auto smap = map[DHO_VENDOR_ENCAPSULATED_OPTIONS];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the sub-option definition can be fetched from the last
+// resort space.
+TEST_F(FlexSubOptionTest, subOptionConfigLastResortCode) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ // Enable csv-format.
+ sub_option->set("csv-format", Element::create(true));
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ string msg = "no known sub-option with code '222' in '";
+ msg += VENDOR_ENCAPSULATED_OPTION_SPACE;
+ msg += "' space";
+ EXPECT_EQ(msg, impl_->getErrMsg());
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-option", 222,
+ VENDOR_ENCAPSULATED_OPTION_SPACE,
+ "string"));
+ defs.addItem(def);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(DHO_VENDOR_ENCAPSULATED_OPTIONS));
+ auto smap = map[DHO_VENDOR_ENCAPSULATED_OPTIONS];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the name can be a vendor defined sub-option.
+TEST_F(FlexSubOptionTest, subOptionConfigVendorName) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-option", 222,
+ "vendor-1234", "string"));
+ defs.addItem(def);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("vendor-1234"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(D6O_VENDOR_OPTS));
+ auto smap = map[D6O_VENDOR_OPTS];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the sub-option definition can be fetched from a custom
+// vendor space.
+TEST_F(FlexSubOptionTest, subOptionConfigVendorCode) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("vendor-1234"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ // Enable csv-format.
+ sub_option->set("csv-format", Element::create(true));
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("no known sub-option with code '222' in 'vendor-1234' space",
+ impl_->getErrMsg());
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-option", 222,
+ "vendor-1234", "string"));
+ defs.addItem(def);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(D6O_VENDOR_OPTS));
+ auto smap = map[D6O_VENDOR_OPTS];
+ EXPECT_EQ(1, smap.count(222));
+}
+
+// Verify that the name can be a vendor standard sub-option.
+TEST_F(FlexSubOptionTest, subOptionConfigDosSISName) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ // VENDOR_ID_CABLE_LABS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("tftp-servers"));
+ sub_option->set("name", name);
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(DHO_VIVSO_SUBOPTIONS));
+ auto smap = map[DHO_VIVSO_SUBOPTIONS];
+ // DOCSIS3_V4_TFTP_SERVERS is 2
+ EXPECT_EQ(1, smap.count(2));
+}
+
+// Verify that the sub-option definition can be fetched from a standard
+// vendor space.
+TEST_F(FlexSubOptionTest, subOptionConfigDosSISCode) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ // VENDOR_ID_CABLE_LABS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(DOCSIS3_V4_TFTP_SERVERS);
+ sub_option->set("code", sub_code);
+ // Enable csv-format.
+ sub_option->set("csv-format", Element::create(true));
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(DHO_VIVSO_SUBOPTIONS));
+ auto smap = map[DHO_VIVSO_SUBOPTIONS];
+ // DOCSIS3_V4_TFTP_SERVERS is 2
+ EXPECT_EQ(1, smap.count(2));
+}
+
+// Verify that the name must match the code.
+TEST_F(FlexSubOptionTest, subOptionConfigCodeNameMismatch) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ // VENDOR_ID_CABLE_LABS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("tftp-servers"));
+ sub_option->set("name", name);
+ // DOCSIS3_V4_TFTP_SERVERS is 2
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ string expected = "sub-option 'tftp-servers' is defined as code: 2, ";
+ expected += "not the specified code: 222";
+ EXPECT_EQ(expected, impl_->getErrMsg());
+}
+
+// Verify that the csv-format must be a boolean.
+TEST_F(FlexSubOptionTest, subOptionConfigBadCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr csv_format = Element::create(123);
+ sub_option->set("csv-format", csv_format);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'csv-format' must be a boolean: 123", impl_->getErrMsg());
+}
+
+// Verify that the container-add must be a boolean.
+TEST_F(FlexSubOptionTest, subOptionConfigBadContainerAdd) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr container_add = Element::create(123);
+ sub_option->set("container-add", container_add);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'container-add' must be a boolean: 123", impl_->getErrMsg());
+}
+
+// Verify that the container-remove must be a boolean.
+TEST_F(FlexSubOptionTest, subOptionConfigBadContainerRemove) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr container_remove = Element::create(123);
+ sub_option->set("container-remove", container_remove);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'container-remove' must be a boolean: 123", impl_->getErrMsg());
+}
+
+// Verify that multiple sub-option actions are not accepted.
+TEST_F(FlexSubOptionTest, subOptionConfigMultipleAction) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+
+ // Add it a second time.
+ sub_options->add(sub_option);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("sub-option 222 of option 109 was already specified",
+ impl_->getErrMsg());
+}
+
+// Verify that the add value must be a string.
+TEST_F(FlexSubOptionTest, subOptionConfigAddNotString) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr add = Element::create(true);
+ sub_option->set("add", add);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'add' must be a string: true", impl_->getErrMsg());
+}
+
+// Verify that the add value must not be empty.
+TEST_F(FlexSubOptionTest, subOptionConfigEmptyAdd) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr add = Element::create(string());
+ sub_option->set("add", add);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'add' must not be empty", impl_->getErrMsg());
+}
+
+// Verify that the add value must parse.
+TEST_F(FlexSubOptionTest, subOptionConfigBadAdd) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr add = Element::create(string("ifelse('a','b','c')"));
+ sub_option->set("add", add);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ string expected = "can't parse add expression [ifelse('a','b','c')] ";
+ expected += "error: <string>:1.11: syntax error, ";
+ expected += "unexpected \",\", expecting == or +";
+ EXPECT_EQ(expected, impl_->getErrMsg());
+}
+
+// Verify that the supersede value must be a string.
+TEST_F(FlexSubOptionTest, subOptionConfigSupersedeNotString) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr supersede = Element::create(true);
+ sub_option->set("supersede", supersede);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'supersede' must be a string: true", impl_->getErrMsg());
+}
+
+// Verify that the supersede value must not be empty.
+TEST_F(FlexSubOptionTest, subOptionConfigEmptySupersede) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr supersede = Element::create(string());
+ sub_option->set("supersede", supersede);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'supersede' must not be empty", impl_->getErrMsg());
+}
+
+// Verify that the supersede value must parse.
+TEST_F(FlexSubOptionTest, subOptionConfigBadSupersede) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr supersede = Element::create(string("ifelse('a','b','c')"));
+ sub_option->set("supersede", supersede);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ string expected = "can't parse supersede expression [ifelse('a','b','c')] ";
+ expected += "error: <string>:1.11: syntax error, ";
+ expected += "unexpected \",\", expecting == or +";
+ EXPECT_EQ(expected, impl_->getErrMsg());
+}
+
+// Verify that the remove value must be a string.
+TEST_F(FlexSubOptionTest, subOptionConfigRemoveNotString) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr remove = Element::createMap();
+ sub_option->set("remove", remove);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'remove' must be a string: { }", impl_->getErrMsg());
+}
+
+// Verify that the remove value must not be empty.
+TEST_F(FlexSubOptionTest, subOptionConfigEmptyRemove) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr remove = Element::create(string());
+ sub_option->set("remove", remove);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'remove' must not be empty", impl_->getErrMsg());
+}
+
+// Verify that the remove value must parse.
+TEST_F(FlexSubOptionTest, subOptionConfigBadRemove) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ ElementPtr remove = Element::create(string("'abc'"));
+ sub_option->set("remove", remove);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ string expected = "can't parse remove expression ['abc'] error: ";
+ expected += "<string>:1.6: syntax error, unexpected end of file, ";
+ expected += "expecting == or +";
+ EXPECT_EQ(expected, impl_->getErrMsg());
+}
+
+// Verify that a complex example must parse.
+TEST_F(FlexSubOptionTest, subOptionConfigComplex) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+
+ ElementPtr sub_option1 = Element::createMap();
+ sub_options->add(sub_option1);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option1->set("space", space);
+ ElementPtr sub_code = Element::create(1);
+ sub_option1->set("code", sub_code);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option1->set("add", add);
+
+ ElementPtr sub_option2 = Element::createMap();
+ sub_options->add(sub_option2);
+ sub_option2->set("space", space);
+ sub_code = Element::create(2);
+ sub_option2->set("code", sub_code);
+ ElementPtr supersede = Element::create(string("'def'"));
+ sub_option2->set("supersede", supersede);
+ sub_option2->set("container-add", Element::create(false));
+
+ ElementPtr sub_option3 = Element::createMap();
+ sub_options->add(sub_option3);
+ sub_option3->set("space", space);
+ sub_code = Element::create(3);
+ sub_option3->set("code", sub_code);
+ ElementPtr remove = Element::create(string("'a' == 'b'"));
+ sub_option3->set("remove", remove);
+
+ ElementPtr sub_option4 = Element::createMap();
+ sub_options->add(sub_option4);
+ sub_option4->set("space", space);
+ sub_code = Element::create(4);
+ sub_option4->set("code", sub_code);
+ remove = Element::create(string("'b' == 'a'"));
+ sub_option4->set("remove", remove);
+ sub_option4->set("container-remove", Element::create(false));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(109));
+ auto smap = map[109];
+ FlexOptionImpl::SubOptionConfigPtr sub_cfg;
+ ASSERT_NO_THROW(sub_cfg = smap.at(1));
+ ASSERT_TRUE(sub_cfg);
+ EXPECT_EQ(1, sub_cfg->getCode());
+ EXPECT_EQ(FlexOptionImpl::ADD, sub_cfg->getAction());
+ EXPECT_EQ(FlexOptionImpl::ADD, sub_cfg->getContainerAction());
+ EXPECT_EQ("'abc'", sub_cfg->getText());
+ EXPECT_EQ(109, sub_cfg->getContainerCode());
+
+ ASSERT_NO_THROW(sub_cfg = smap.at(2));
+ ASSERT_TRUE(sub_cfg);
+ EXPECT_EQ(2, sub_cfg->getCode());
+ EXPECT_EQ(FlexOptionImpl::SUPERSEDE, sub_cfg->getAction());
+ EXPECT_EQ(FlexOptionImpl::NONE, sub_cfg->getContainerAction());
+ EXPECT_EQ("'def'", sub_cfg->getText());
+ EXPECT_EQ(109, sub_cfg->getContainerCode());
+
+ ASSERT_NO_THROW(sub_cfg = smap.at(3));
+ ASSERT_TRUE(sub_cfg);
+ EXPECT_EQ(3, sub_cfg->getCode());
+ EXPECT_EQ(FlexOptionImpl::REMOVE, sub_cfg->getAction());
+ EXPECT_EQ(FlexOptionImpl::REMOVE, sub_cfg->getContainerAction());
+ EXPECT_EQ("'a' == 'b'", sub_cfg->getText());
+ EXPECT_EQ(109, sub_cfg->getContainerCode());
+
+ ASSERT_NO_THROW(sub_cfg = smap.at(4));
+ ASSERT_TRUE(sub_cfg);
+ EXPECT_EQ(4, sub_cfg->getCode());
+ EXPECT_EQ(FlexOptionImpl::REMOVE, sub_cfg->getAction());
+ EXPECT_EQ(FlexOptionImpl::NONE, sub_cfg->getContainerAction());
+ EXPECT_EQ("'b' == 'a'", sub_cfg->getText());
+ EXPECT_EQ(109, sub_cfg->getContainerCode());
+}
+
+// Empty sub-option config list doing nothing is the same as empty option list.
+
+// Verify that NONE action really does nothing.
+TEST_F(FlexSubOptionTest, subProcessNone) {
+ auto rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
+ DHO_DHCP_AGENT_OPTIONS);
+ FlexOptionImpl::OptionConfigPtr
+ opt_cfg(new FlexOptionImpl::OptionConfig(DHO_DHCP_AGENT_OPTIONS,
+ rai_def));
+ // RAI_OPTION_AGENT_CIRCUIT_ID is 1 but has no definition.
+ OptionDefinitionPtr def;
+ FlexOptionImpl::SubOptionConfigPtr
+ sub_cfg(new FlexOptionImpl::SubOptionConfig(1, def, opt_cfg));
+ auto& map = impl_->getMutableSubOptionConfigMap();
+ auto& smap = map[DHO_DHCP_AGENT_OPTIONS];
+ smap[1] = sub_cfg;
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+}
+
+// Verify that ADD action adds the specified sub-option in csv format.
+TEST_F(FlexSubOptionTest, subProcessAddEnableCSVFormat) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "fqdn", true));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'example.com'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("csv-format", Element::create(true));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ // The fqdn array is the most complex encoding of one element...
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(13, buffer.size());
+ EXPECT_EQ(7, buffer[0]);
+ EXPECT_EQ(0, memcmp(&buffer[1], "example", 7));
+ EXPECT_EQ(3, buffer[8]);
+ EXPECT_EQ(0, memcmp(&buffer[9], "com", 3));
+ EXPECT_EQ(0, buffer[12]);
+}
+
+// Verify that ADD action does nothing when the container does not exist and
+// container-add is false.
+TEST_F(FlexSubOptionTest, subProcessAddNoContainer) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("container-add", Element::create(false));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that ADD action adds the specified sub-option in raw format.
+TEST_F(FlexSubOptionTest, subProcessAddDisableCSVFormat) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "fqdn", true));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("0x076578616d706c6503636f6d00"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ // csv-format is disabled by default.
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(13, buffer.size());
+ EXPECT_EQ(7, buffer[0]);
+ EXPECT_EQ(0, memcmp(&buffer[1], "example", 7));
+ EXPECT_EQ(3, buffer[8]);
+ EXPECT_EQ(0, memcmp(&buffer[9], "com", 3));
+ EXPECT_EQ(0, buffer[12]);
+}
+
+// Verify that ADD action adds the specified sub-option in an already
+// existing container option.
+TEST_F(FlexSubOptionTest, subProcessAdd) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ // Only one option with code 222.
+ EXPECT_EQ(1, response->options_.count(222));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+
+ // Only one sub-option.
+ auto const& opts = opt->getOptions();
+ EXPECT_EQ(1, opts.size());
+}
+
+// Verify that ADD action does not add an already existing sub-option.
+TEST_F(FlexSubOptionTest, subProcessAddExisting) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt"));
+ container->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+
+ // Only one sub-option.
+ auto const& opts = opt->getOptions();
+ EXPECT_EQ(1, opts.size());
+}
+
+// Verify that ADD action does not add an empty value.
+TEST_F(FlexSubOptionTest, subProcessAddEmpty) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("''"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that ADD action can handle vendor-encapsulated-options 43.
+TEST_F(FlexSubOptionTest, subProcessAdd43) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ VENDOR_ENCAPSULATED_OPTION_SPACE,
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'foobar'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that ADD action can handle DocSIS Vivso.
+TEST_F(FlexSubOptionTest, subProcessAddDocSISVIVSO) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'10.1.2.3'"));
+ sub_option->set("add", add);
+ // DocSIS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("tftp-servers"));
+ sub_option->set("name", name);
+ sub_option->set("csv-format", Element::create(true));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(DOCSIS3_V4_TFTP_SERVERS);
+ ASSERT_TRUE(sub);
+ Option4AddrLstPtr addr = boost::dynamic_pointer_cast<Option4AddrLst>(sub);
+ ASSERT_TRUE(addr);
+ auto const& addrs = addr->getAddresses();
+ ASSERT_EQ(1, addrs.size());
+ EXPECT_EQ("10.1.2.3", addrs[0].toText());
+}
+
+// Verify that ADD action can handle DocSIS vendor-opts.
+TEST_F(FlexSubOptionTest, subProcessAddDocSISVendorOps) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'foobar'"));
+ sub_option->set("add", add);
+ // DocSIS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ code = Element::create(DOCSIS3_V6_VENDOR_NAME);
+ sub_option->set("code", code);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(DOCSIS3_V6_VENDOR_NAME);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that ADD action can handle the Vivso option.
+TEST_F(FlexSubOptionTest, subProcessAddVivso) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'foobar'"));
+ sub_option->set("add", add);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(123456, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that ADD action can handle the Vivso option with vendor mismatch.
+TEST_F(FlexSubOptionTest, subProcessAddVivsoMismatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'foobar'"));
+ sub_option->set("add", add);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionVendorPtr vendor(new OptionVendor(Option::V4, 67890));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V4, 2, "xyzt"));
+ vendor->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(67890, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ EXPECT_FALSE(sub);
+ sub = vendor->getOption(2);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+}
+
+// Verify that ADD action can handle the vendor-opts option.
+TEST_F(FlexSubOptionTest, subProcessAddVendorOpts) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'foobar'"));
+ sub_option->set("add", add);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(123456, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that ADD action can handle the vendor-opts option with vendor mismatch.
+TEST_F(FlexSubOptionTest, subProcessAddVendorOptsMismatch) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr add = Element::create(string("'foobar'"));
+ sub_option->set("add", add);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+ OptionVendorPtr vendor(new OptionVendor(Option::V6, 67890));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V6, 2, "xyzt"));
+ vendor->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(67890, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ EXPECT_FALSE(sub);
+ sub = vendor->getOption(2);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+}
+
+// Verify that SUPERSEDE action adds the specified sub-option in csv format.
+TEST_F(FlexSubOptionTest, subProcessSupersedeEnableCSVFormat) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "fqdn", true));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr supersede = Element::create(string("'example.com'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("csv-format", Element::create(true));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ // The fqdn array is the most complex encoding of one element...
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(13, buffer.size());
+ EXPECT_EQ(7, buffer[0]);
+ EXPECT_EQ(0, memcmp(&buffer[1], "example", 7));
+ EXPECT_EQ(3, buffer[8]);
+ EXPECT_EQ(0, memcmp(&buffer[9], "com", 3));
+ EXPECT_EQ(0, buffer[12]);
+}
+
+// Verify that SUPERSEDE action does nothing when the container does not exist
+// and container-add is false.
+TEST_F(FlexSubOptionTest, subProcessSupersedeNoContainer) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr supersede = Element::create(string("'abc'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("container-add", Element::create(false));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that SUPERSEDE action adds the specified sub-option in raw format.
+TEST_F(FlexSubOptionTest, subProcessSupersedeDisableCSVFormat) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "fqdn", true));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr supersede = Element::create(string("0x076578616d706c6503636f6d00"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ // csv-format is disabled by default.
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(13, buffer.size());
+ EXPECT_EQ(7, buffer[0]);
+ EXPECT_EQ(0, memcmp(&buffer[1], "example", 7));
+ EXPECT_EQ(3, buffer[8]);
+ EXPECT_EQ(0, memcmp(&buffer[9], "com", 3));
+ EXPECT_EQ(0, buffer[12]);
+}
+
+// Verify that SUPERSEDE action adds the specified sub-option in an already
+// existing container option.
+TEST_F(FlexSubOptionTest, subProcessSupersede) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr supersede = Element::create(string("'abc'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ // Only one option with code 222.
+ EXPECT_EQ(1, response->options_.count(222));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+
+ // Only one sub-option.
+ auto const& opts = opt->getOptions();
+ EXPECT_EQ(1, opts.size());
+}
+
+// Verify that SUPERSEDE action replaces an already existing sub-option.
+TEST_F(FlexSubOptionTest, subProcessSupersedeExisting) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr supersede = Element::create(string("'abc'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt"));
+ container->addOption(str);
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ // Only one option with code 222.
+ EXPECT_EQ(1, response->options_.count(222));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+
+ // Only one sub-option.
+ auto const& opts = opt->getOptions();
+ EXPECT_EQ(1, opts.size());
+}
+
+// Verify that SUPERSEDE action does not add an empty value.
+TEST_F(FlexSubOptionTest, subProcessSupersedeEmpty) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr supersede = Element::create(string("''"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that SUPERSEDE action can handle vendor-encapsulated-options 43.
+TEST_F(FlexSubOptionTest, subProcessSupersede43) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ VENDOR_ENCAPSULATED_OPTION_SPACE,
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("'foobar'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that SUPERSEDE action can handle DocSIS Vivso.
+TEST_F(FlexSubOptionTest, subProcessSupersedeDocSISVIVSO) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("10.1.2.3"));
+ sub_option->set("supersede", supersede);
+ // DocSIS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("tftp-servers"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(DOCSIS3_V4_TFTP_SERVERS);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ uint8_t expected[] = { 10, 1, 2, 3 };
+ EXPECT_EQ(0, memcmp(&buffer[0], expected, 4));
+}
+
+// Verify that SUPERSEDE action can handle DocSIS vendor-opts.
+TEST_F(FlexSubOptionTest, subProcessSupersedeDocSISVendorOps) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("'foobar'"));
+ sub_option->set("supersede", supersede);
+ // DocSIS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ code = Element::create(DOCSIS3_V6_VENDOR_NAME);
+ sub_option->set("code", code);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(VENDOR_ID_CABLE_LABS, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(DOCSIS3_V6_VENDOR_NAME);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that SUPERSEDE action can handle the Vivso option.
+TEST_F(FlexSubOptionTest, subProcessSupersedeVivso) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("'foobar'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(123456, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that SUPERSEDE action can handle the Vivso option with vendor mismatch.
+TEST_F(FlexSubOptionTest, subProcessSupersedeVivsoMismatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("'foobar'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionVendorPtr vendor(new OptionVendor(Option::V4, 67890));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V4, 2, "xyzt"));
+ vendor->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(67890, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ EXPECT_FALSE(sub);
+ sub = vendor->getOption(2);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+}
+
+// Verify that SUPERSEDE action can handle the vendor-opts option.
+TEST_F(FlexSubOptionTest, subProcessSupersedeVendorOpts) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("'foobar'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(123456, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(6, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "foobar", 6));
+}
+
+// Verify that SUPERSEDE action can handle the vendor-opts option with vendor mismatch.
+TEST_F(FlexSubOptionTest, subProcessSupersedeVendorOptsMismatch) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr supersede = Element::create(string("'foobar'"));
+ sub_option->set("supersede", supersede);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+ OptionVendorPtr vendor(new OptionVendor(Option::V6, 67890));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V6, 2, "xyzt"));
+ vendor->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(67890, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ EXPECT_FALSE(sub);
+ sub = vendor->getOption(2);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+}
+
+// Verify that REMOVE action removes an already existing sub-option.
+TEST_F(FlexSubOptionTest, subProcessRemove) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt"));
+ container->addOption(str);
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that REMOVE action removes an already existing sub-option but
+// leaves the container option when container-remove is false.
+TEST_F(FlexSubOptionTest, subProcessRemoveLeaveContainer) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("container-remove", Element::create(false));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ string response_txt = response->toText();
+ EXPECT_TRUE(response->getOption(222));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "xyzt"));
+ container->addOption(str);
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_FALSE(opt->getOption(1));
+ EXPECT_TRUE(opt->getOptions().empty());
+}
+
+// Verify that REMOVE action removes an already existing sub-option but
+// leaves the container option when it is not empty.
+TEST_F(FlexSubOptionTest, subProcessRemoveContainerNotEmpty) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 2, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "abc"));
+ container->addOption(str);
+ string response_txt = response->toText();
+ str.reset(new OptionString(Option::V4, 2, "xyzt"));
+ container->addOption(str);
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_FALSE(opt->getOption(2));
+ EXPECT_EQ(1, opt->getOptions().size());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that REMOVE action does not removes the container option when the
+// sub-option does not exist.
+TEST_F(FlexSubOptionTest, subProcessRemoveContainerNoSubOption) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("container-remove", Element::create(false));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_TRUE(response->getOption(222));
+}
+
+// Verify that REMOVE action does nothing when the expression evaluates to false.
+TEST_F(FlexSubOptionTest, subProcessRemoveFalse) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr remove = Element::create(string("'abc' == 'xyz'"));
+ sub_option->set("remove", remove);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionPtr container(new Option(Option::V4, 222));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(222));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "abc"));
+ container->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that REMOVE action can handle vendor-encapsulated-options 43.
+TEST_F(FlexSubOptionTest, subProcessRemove43) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ VENDOR_ENCAPSULATED_OPTION_SPACE,
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ OptionPtr container(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS));
+ response->addOption(container);
+ EXPECT_TRUE(response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS));
+ OptionStringPtr str(new OptionString(Option::V4, 1, "abc"));
+ container->addOption(str);
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(DHO_VENDOR_ENCAPSULATED_OPTIONS));
+}
+
+// Verify that REMOVE action can handle DocSIS Vivso.
+TEST_F(FlexSubOptionTest, subProcessRemoveDocSISVIVSO) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("member('ALL')"));
+ sub_option->set("remove", remove);
+ // DocSIS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("tftp-servers"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ query->addClass("ALL");
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ OptionVendorPtr vendor(new OptionVendor(Option::V4, 4491));
+ response->addOption(vendor);
+ Option4AddrLstPtr tftp(new Option4AddrLst(DOCSIS3_V4_TFTP_SERVERS,
+ IOAddress("10.1.2.3")));
+ vendor->addOption(tftp);
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(DHO_VIVSO_SUBOPTIONS));
+}
+
+// Verify that REMOVE action can handle DocSIS vendor-opts.
+TEST_F(FlexSubOptionTest, subProcessRemoveDocSISVendorOps) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ // DocSIS is 4491
+ ElementPtr space = Element::create(string("vendor-4491"));
+ sub_option->set("space", space);
+ code = Element::create(DOCSIS3_V6_VENDOR_NAME);
+ sub_option->set("code", code);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+ string response_txt = response->toText();
+ OptionVendorPtr vendor(new OptionVendor(Option::V6, 4491));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V6, DOCSIS3_V6_VENDOR_NAME,
+ "foobar"));
+ vendor->addOption(str);
+ EXPECT_TRUE(response->getOption(D6O_VENDOR_OPTS));
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(D6O_VENDOR_OPTS));
+}
+
+// Verify that REMOVE action can handle the Vivso option.
+TEST_F(FlexSubOptionTest, subProcessRemoveVivso) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ OptionVendorPtr vendor(new OptionVendor(Option::V4, 123456));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V4, 1, "foobar"));
+ vendor->addOption(str);
+ EXPECT_TRUE(response->getOption(DHO_VIVSO_SUBOPTIONS));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(DHO_VIVSO_SUBOPTIONS));
+}
+
+// Verify that REMOVE action can handle the Vivso option with vendor mismatch.
+TEST_F(FlexSubOptionTest, subProcessRemoveVivsoMismatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(DHO_VIVSO_SUBOPTIONS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("member('ALL')"));
+ sub_option->set("remove", remove);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ query->addClass("ALL");
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ OptionVendorPtr vendor(new OptionVendor(Option::V4, 67890));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V4, 2, "xyzt"));
+ vendor->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(DHO_VIVSO_SUBOPTIONS);
+ ASSERT_TRUE(opt);
+ vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(67890, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ EXPECT_FALSE(sub);
+ sub = vendor->getOption(2);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+}
+
+// Verify that REMOVE action can handle the vendor-opts option.
+TEST_F(FlexSubOptionTest, subProcessRemoveVendorOpts) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+ string response_txt = response->toText();
+ OptionVendorPtr vendor(new OptionVendor(Option::V6, 123456));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V6, 1, "foobar"));
+ vendor->addOption(str);
+ EXPECT_TRUE(response->getOption(D6O_VENDOR_OPTS));
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(D6O_VENDOR_OPTS));
+}
+
+// Verify that REMOVE action can handle the vendor-opts option with vendor mismatch.
+TEST_F(FlexSubOptionTest, subProcessRemoveVendorOptsMismatch) {
+ CfgMgr::instance().setFamily(AF_INET6);
+
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1,
+ "vendor-123456", "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(D6O_VENDOR_OPTS);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr remove = Element::create(string("'abc' == 'abc'"));
+ sub_option->set("remove", remove);
+ ElementPtr space = Element::create(string("vendor-123456"));
+ sub_option->set("space", space);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345));
+ Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345));
+ OptionVendorPtr vendor(new OptionVendor(Option::V6, 67890));
+ response->addOption(vendor);
+ OptionStringPtr str(new OptionString(Option::V6, 2, "xyzt"));
+ vendor->addOption(str);
+ string response_txt = response->toText();
+
+ EXPECT_NO_THROW(impl_->process<Pkt6Ptr>(Option::V6, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ OptionPtr opt = response->getOption(D6O_VENDOR_OPTS);
+ ASSERT_TRUE(opt);
+ vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
+ ASSERT_TRUE(vendor);
+ EXPECT_EQ(67890, vendor->getVendorId());
+ OptionPtr sub = vendor->getOption(1);
+ EXPECT_FALSE(sub);
+ sub = vendor->getOption(2);
+ ASSERT_TRUE(sub);
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(4, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "xyzt", 4));
+}
+
+// Verify that the client class must be a string.
+TEST_F(FlexSubOptionTest, subOptionConfigBadClass) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(true);
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ sub_option->set("code", code);
+ sub_option->set("client-class", Element::create(true));
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'client-class' must be a string: true", impl_->getErrMsg());
+}
+
+// Verify that a valid client class is accepted.
+TEST_F(FlexSubOptionTest, subOptionConfigGuardValid) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(109);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'ab'"));
+ sub_option->set("add", add);
+ ElementPtr sub_code = Element::create(222);
+ sub_option->set("code", sub_code);
+ sub_option->set("client-class", Element::create(string("foobar")));
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ auto map = impl_->getSubOptionConfigMap();
+ EXPECT_EQ(1, map.count(109));
+ auto smap = map[109];
+ FlexOptionImpl::SubOptionConfigPtr sub_cfg;
+ ASSERT_NO_THROW(sub_cfg = smap.at(222));
+ ASSERT_TRUE(sub_cfg);
+ EXPECT_EQ(222, sub_cfg->getCode());
+ EXPECT_EQ(109, sub_cfg->getContainerCode());
+ EXPECT_EQ("foobar", sub_cfg->getClass());
+}
+
+// Verify that a guarded action is skipped when query does not belong to the
+// client class of the container option.
+TEST_F(FlexSubOptionTest, subOptionConfigGuardOptiondNoMatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ option->set("client-class", Element::create(string("foobar")));
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that a guarded action is applied when query belongs to the class
+// class of the container option.
+TEST_F(FlexSubOptionTest, subOptionConfigGuardOptiondMatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ option->set("client-class", Element::create(string("foobar")));
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ query->addClass("foobar");
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+// Verify that a guarded action is skipped when query does not belong to the
+// client class of the sub-option.
+TEST_F(FlexSubOptionTest, subOptionConfigGuardSubOptiondNoMatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("client-class", Element::create(string("foobar")));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ string response_txt = response->toText();
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ EXPECT_EQ(response_txt, response->toText());
+ EXPECT_FALSE(response->getOption(222));
+}
+
+// Verify that a guarded action is applied when query belongs to the class
+// class of the sub-option.
+TEST_F(FlexSubOptionTest, subOptionConfigGuardSubOptiondMatch) {
+ OptionDefSpaceContainer defs;
+ OptionDefinitionPtr def(new OptionDefinition("my-container", 222,
+ DHCP4_OPTION_SPACE, "empty",
+ "my-space"));
+ defs.addItem(def);
+ OptionDefinitionPtr sdef(new OptionDefinition("my-option", 1, "my-space",
+ "string"));
+ defs.addItem(sdef);
+ EXPECT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
+
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr code = Element::create(222);
+ option->set("code", code);
+ ElementPtr sub_options = Element::createList();
+ option->set("sub-options", sub_options);
+ ElementPtr sub_option = Element::createMap();
+ sub_options->add(sub_option);
+ ElementPtr space = Element::create(string("my-space"));
+ sub_option->set("space", space);
+ ElementPtr add = Element::create(string("'abc'"));
+ sub_option->set("add", add);
+ ElementPtr name = Element::create(string("my-option"));
+ sub_option->set("name", name);
+ sub_option->set("client-class", Element::create(string("foobar")));
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty()) << impl_->getErrMsg();
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ query->addClass("foobar");
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(222));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(222);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(222, opt->getType());
+ OptionPtr sub = opt->getOption(1);
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(1, sub->getType());
+ const OptionBuffer& buffer = sub->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+}
+
+} // end of anonymous namespace