using namespace std;
using namespace isc;
+using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::eval;
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);
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();
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_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_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.
EXPECT_EQ(0, buffer[12]);
}
-// Verify that ADD action does not when the container does not exist and
+// Verify that ADD action does nothing when the container does not exist and
// container-add is false.
TEST_F(FlexSubOptionTest, subProcessAddNoContainer) {
OptionDefSpaceContainer defs;
}
// Verify that ADD action can handle the vendor-opts option.
-TEST_F(FlexSubOptionTest, subProcessAddvendorOpts) {
+TEST_F(FlexSubOptionTest, subProcessAddVendorOpts) {
CfgMgr::instance().setFamily(AF_INET6);
OptionDefSpaceContainer defs;
}
// Verify that ADD action can handle the vendor-opts option with vendor mismatch.
-TEST_F(FlexSubOptionTest, subProcessAddvendorOptsMismatch) {
+TEST_F(FlexSubOptionTest, subProcessAddVendorOptsMismatch) {
CfgMgr::instance().setFamily(AF_INET6);
OptionDefSpaceContainer defs;
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));
+}
+
+// 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));
+
+ 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(43, 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));
+}
+
+// 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));
+ 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
-// TEST_F(FlexSubOptionTest, subP