]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2314] Checkpoint: finished UTs, doing doc
authorFrancis Dupont <fdupont@isc.org>
Mon, 21 Mar 2022 22:36:10 +0000 (23:36 +0100)
committerRazvan Becheriu <razvan@isc.org>
Wed, 23 Mar 2022 07:50:03 +0000 (09:50 +0200)
src/hooks/dhcp/flex_option/flex_option_messages.cc
src/hooks/dhcp/flex_option/flex_option_messages.mes
src/hooks/dhcp/flex_option/tests/sub_option_unittests.cc

index 864e612f363d2d641ff4f3f71616417eee012a71..340bd7f6a437664862516ac87bd6880bce23fea3 100644 (file)
@@ -26,7 +26,7 @@ const char* values[] = {
     "FLEX_OPTION_PROCESS_ERROR", "An error occurred processing query %1: %2",
     "FLEX_OPTION_PROCESS_REMOVE", "Removed option code %1",
     "FLEX_OPTION_PROCESS_SUB_ADD", "Added the sub-option code %1 in option code %2 value by %3",
-    "FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS", "Skip processing of the sub-option code in option code %2 for class '%3'",
+    "FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS", "Skip processing of the sub-option code %1 in option code %2 for class '%3'",
     "FLEX_OPTION_PROCESS_SUB_REMOVE", "Removed sub-option code %1 in option code %2",
     "FLEX_OPTION_PROCESS_SUB_SUPERSEDE", "Supersedes the value of sub-option code %1 in option code %2 by %3",
     "FLEX_OPTION_PROCESS_SUPERSEDE", "Supersedes the value of option code %1 by %2",
index 9c125835f2f133503ca838514016ccfcafd37f0c..dd4acb9ac6a2aa86456af6a749a1f1542b4c8e68 100644 (file)
@@ -30,7 +30,7 @@ This debug message is printed when an sub-option was added into the response
 packet. The sub-option and container option codes, and the value
 (between quotes if printable, in hexadecimal is not) are provided.
 
-% FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS Skip processing of the sub-option code in option code %2 for class '%3'
+% FLEX_OPTION_PROCESS_SUB_CLIENT_CLASS Skip processing of the sub-option code %1 in option code %2 for class '%3'
 This debug message is printed when the processing for a sub-option is skipped
 because the query does not belongs to the client class. The sub-option and
 container option codes, and the client class name are provided.
index 79deccb556c38ce70d38f1fac7f4935b1acd83df..621b296d9a4b8f7b77c1777c79b3fea585db6ecf 100644 (file)
@@ -26,6 +26,7 @@
 
 using namespace std;
 using namespace isc;
+using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::eval;
@@ -906,6 +907,7 @@ TEST_F(FlexSubOptionTest, subOptionConfigComplex) {
     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);
@@ -915,6 +917,15 @@ TEST_F(FlexSubOptionTest, subOptionConfigComplex) {
     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();
 
@@ -926,6 +937,7 @@ TEST_F(FlexSubOptionTest, subOptionConfigComplex) {
     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());
 
@@ -933,6 +945,7 @@ TEST_F(FlexSubOptionTest, subOptionConfigComplex) {
     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());
 
@@ -940,8 +953,17 @@ TEST_F(FlexSubOptionTest, subOptionConfigComplex) {
     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.
@@ -1024,7 +1046,7 @@ TEST_F(FlexSubOptionTest, subProcessAddEnableCSVFormat) {
     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;
@@ -1486,7 +1508,7 @@ TEST_F(FlexSubOptionTest, subProcessAddVivsoMismatch) {
 }
 
 // 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;
@@ -1532,7 +1554,7 @@ TEST_F(FlexSubOptionTest, subProcessAddvendorOpts) {
 }
 
 // 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;
@@ -1585,5 +1607,1422 @@ TEST_F(FlexSubOptionTest, subProcessAddvendorOptsMismatch) {
     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