From: Francis Dupont Date: Fri, 4 Oct 2019 19:46:14 +0000 (+0200) Subject: [219-allow-an-option-value-to-be-set-from-an-expression] Added new tests and an example X-Git-Tag: Kea-1.7.1~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3a1cec329bf6258e34a0e7cd8d337bd81736ceca;p=thirdparty%2Fkea.git [219-allow-an-option-value-to-be-set-from-an-expression] Added new tests and an example --- diff --git a/doc/sphinx/arm/hooks.rst b/doc/sphinx/arm/hooks.rst index 23487b3c89..cedcb341c0 100644 --- a/doc/sphinx/arm/hooks.rst +++ b/doc/sphinx/arm/hooks.rst @@ -1216,14 +1216,15 @@ take a string value representing an expression. :: - "Dhcp6": { + "Dhcp4": { "hook_libraries": [ { "library": "/usr/local/lib/libdhcp_flex_option.so", "parameters": { "options": [ { - "code": 100, - "add": "concat(relay6[0].option[37].hex, 'abc')" + "code": 67, + "add": + "ifelse(option[host-name].exists,concat(option[host-name].text,'.boot'),'')" } ] } @@ -1232,8 +1233,14 @@ take a string value representing an expression. ] } +If (and only if) the query includes a host-name option (code 12), +a boot-file-name option (code 67) is added to the response with the host +name followed by .boot for content. + The flexible option library supports both DHCPv4 and DHCPv6. + + .. _host-cmds: host_cmds: Host Commands diff --git a/src/hooks/dhcp/flex_option/flex_option.dox b/src/hooks/dhcp/flex_option/flex_option.dox index 7f444ca1f2..d168dcf7d8 100644 --- a/src/hooks/dhcp/flex_option/flex_option.dox +++ b/src/hooks/dhcp/flex_option/flex_option.dox @@ -63,7 +63,7 @@ To configure it for kea-dhcp6, the commands are simply as shown below: { "library": "/usr/local/lib/libdhcp_flex_option.so", "parameters": { "options": [ - { + { "code": 100, "add": "concat(relay6[0].option[37].hex, 'abc')" } diff --git a/src/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc b/src/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc index ecd58ac45c..db1cb67ffe 100644 --- a/src/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc +++ b/src/hooks/dhcp/flex_option/libloadtests/callout_unittests.cc @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -114,7 +116,7 @@ TEST_F(CalloutTest, pkt4Send) { EXPECT_NO_THROW(HooksManager::callCallouts(testHooks.hook_index_pkt4_send_, *handle)); EXPECT_EQ(0, handle->getStatus()); - + // Check the result. OptionPtr opt = response->getOption(DHO_HOST_NAME); ASSERT_TRUE(opt); @@ -124,4 +126,49 @@ TEST_F(CalloutTest, pkt4Send) { EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); } +// Simple test which exercises the pkt6_send callout. +TEST_F(CalloutTest, pkt6Send) { + // Move to DHCPv6. + CfgMgr::instance().setFamily(AF_INET6); + + // Prepare load() parameters. + ElementPtr params = Element::createMap(); + ElementPtr options = Element::createList(); + params->set("options", options); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(D6O_BOOTFILE_URL); + option->set("code", code); + ElementPtr supersede = Element::create(string("'abc'")); + option->set("supersede", supersede); + + // Load the library. + addLib(FLEX_OPTION_LIB_SO, params); + loadLibs(); + + // Prepare packets. + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); + EXPECT_FALSE(response->getOption(D6O_BOOTFILE_URL)); + + // Get and setup the callout handle. + EXPECT_TRUE(HooksManager::calloutsPresent(testHooks.hook_index_pkt6_send_)); + CalloutHandlePtr handle = HooksManager::createCalloutHandle(); + handle->setArgument("query6", query); + handle->setArgument("response6", response); + + // Execute the callout. + EXPECT_NO_THROW(HooksManager::callCallouts(testHooks.hook_index_pkt6_send_, + *handle)); + EXPECT_EQ(0, handle->getStatus()); + + // Check the result. + OptionPtr opt = response->getOption(D6O_BOOTFILE_URL); + ASSERT_TRUE(opt); + EXPECT_EQ(D6O_BOOTFILE_URL, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(3, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3)); +} + } // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc index 90a8ee01bd..5126b1fdd0 100644 --- a/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc +++ b/src/hooks/dhcp/flex_option/tests/flex_option_unittests.cc @@ -778,6 +778,8 @@ TEST_F(FlexOptionTest, processAdd) { // Verify that ADD action does not add an already existing option. TEST_F(FlexOptionTest, processAddExisting) { + CfgMgr::instance().setFamily(AF_INET6); + ElementPtr options = Element::createList(); ElementPtr option = Element::createMap(); options->add(option); @@ -852,6 +854,8 @@ TEST_F(FlexOptionTest, processSupersede) { // Verify that SUPERSEDE action supersedes an already existing option. TEST_F(FlexOptionTest, processSupersedeExisting) { + CfgMgr::instance().setFamily(AF_INET6); + ElementPtr options = Element::createList(); ElementPtr option = Element::createMap(); options->add(option); @@ -916,6 +920,8 @@ TEST_F(FlexOptionTest, processSupersedeEmpty) { // Verify that REMOVE action removes an already existing option. TEST_F(FlexOptionTest, processRemove) { + CfgMgr::instance().setFamily(AF_INET6); + ElementPtr options = Element::createList(); ElementPtr option = Element::createMap(); options->add(option); @@ -961,6 +967,8 @@ TEST_F(FlexOptionTest, processRemoveNoOption) { // Verify that REMOVE action does nothing when the expression evaluates to false. TEST_F(FlexOptionTest, processRemoveFalse) { + CfgMgr::instance().setFamily(AF_INET6); + ElementPtr options = Element::createList(); ElementPtr option = Element::createMap(); options->add(option); @@ -971,16 +979,46 @@ TEST_F(FlexOptionTest, processRemoveFalse) { EXPECT_NO_THROW(impl_->testConfigure(options)); EXPECT_TRUE(impl_->getErrMsg().empty()); - Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); - Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 12345)); + Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, 12345)); OptionStringPtr str(new OptionString(Option::V6, D6O_BOOTFILE_URL, "http")); response->addOption(str); string response_txt = response->toText(); - EXPECT_NO_THROW(impl_->process(Option::V6, query, response)); + EXPECT_NO_THROW(impl_->process(Option::V6, query, response)); EXPECT_EQ(response_txt, response->toText()); EXPECT_TRUE(response->getOption(D6O_BOOTFILE_URL)); } +// A more complex check... +TEST_F(FlexOptionTest, processFullTest) { + ElementPtr options = Element::createList(); + ElementPtr option = Element::createMap(); + options->add(option); + ElementPtr code = Element::create(DHO_BOOT_FILE_NAME); + option->set("code", code); + string expr = "ifelse(option[host-name].exists,"; + expr += "concat(option[host-name].text,'.boot'),'')"; + ElementPtr add = Element::create(expr); + option->set("add", add); + EXPECT_NO_THROW(impl_->testConfigure(options)); + EXPECT_TRUE(impl_->getErrMsg().empty()); + + Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345)); + Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345)); + OptionStringPtr str(new OptionString(Option::V4, DHO_HOST_NAME, "foo")); + query->addOption(str); + EXPECT_FALSE(response->getOption(DHO_BOOT_FILE_NAME)); + + EXPECT_NO_THROW(impl_->process(Option::V4, query, response)); + + OptionPtr opt = response->getOption(DHO_BOOT_FILE_NAME); + ASSERT_TRUE(opt); + EXPECT_EQ(DHO_BOOT_FILE_NAME, opt->getType()); + const OptionBuffer& buffer = opt->getData(); + ASSERT_EQ(8, buffer.size()); + EXPECT_EQ(0, memcmp(&buffer[0], "foo.boot", 8)); +} + } // end of anonymous namespace