diff --git a/AUTHORS b/AUTHORS index 1bfc8458f1..d386688339 100755 --- a/AUTHORS +++ b/AUTHORS @@ -76,12 +76,15 @@ We have received the following contributions: - David Gutierrez Rueda, CERN 2014-12: Support for client link-address option in DHCPv6 (RFC6939) - - Adam Kalmus, Gdank University of Technology + - Adam Kalmus, Gdansk University of Technology 2014-12: Extract MAC address from DUID-LL and DUID-LLT types 2015-01: Extract MAC address from remote-id 2015-05: MySQL schema extended to cover host reservation 2015-10: Common MySQL Connector Pool + - Jinmei Tatuya + 2015-10: Pkt4o6 class improvements + Kea uses log4cplus (http://sourceforge.net/projects/log4cplus/) for logging, Boost (http://www.boost.org/) library for almost everything, and can use Botan (http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for diff --git a/ChangeLog b/ChangeLog index cd2ad8527b..50ee904cc4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +1043. [func] fdupont + Implemented support for hex strings in client classification. + (Trac #4091, git 406153af95404adb96296df09ec6033b484586e3) + +1042. [doc] fdupont + User Guide: parameters having default values may be omitted in the + option definitions. + (Trac #3927, git c7460e849258ec77cf1215a2baf840d98f1ab77b) + 1041. [func] tomek A new library, libkea-eval has been edded. It is not functional yet, but its purpose is to provide a generic expression diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index 07b0123fdf..0f6cdd4560 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -1160,14 +1160,14 @@ It is merely echoed by the server should be left blank. Note that the above set of comments define the format of the new option and do not set its values. - - - In the current release the default values are not propagated to the - parser when the new configuration is being set. Therefore, all - parameters must be specified at all times, even if their values are - left blank. - - + + The name, code and + type parameters are required, all others are + optional. The array default value is + false. The record-types + and encapsulate default values are blank + (i.e. ""). The default space is "dhcp4". + Once the new option format is defined, its value is set in the same way as for a standard option. For example the following diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index 9e10e671d5..11002006b0 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -961,6 +961,8 @@ temporarily override a list of interface names and listen on all interfaces. erp-local-domain-name65fqdnfalse rsoo66emptyfalse client-linklayer-addr79binaryfalse + +dhcp4o6-server-addr88ipv6-addresstrue @@ -1022,6 +1024,15 @@ temporarily override a list of interface names and listen on all interfaces. set of comments define the format of the new option and do not set its values. + + The name, code and + type parameters are required, all others are + optional. The array default value is + false. The record-types + and encapsulate default values are blank + (i.e. ""). The default space is "dhcp6". + + Once the new option format is defined, its value is set in the same way as for a standard option. For example the following commands set a global value that applies to all subnets. @@ -1059,7 +1070,7 @@ temporarily override a list of interface names and listen on all interfaces. "space": "dhcp6", "type": "record", "array": false, - "record-types": "ipv4-address, uint16, boolean, string", + "record-types": "ipv6-address, uint16, boolean, string", "encapsulate": "" }, ... ], diff --git a/doc/guide/logging.xml b/doc/guide/logging.xml index 63c9cf3a38..25dd24749e 100644 --- a/doc/guide/logging.xml +++ b/doc/guide/logging.xml @@ -219,6 +219,11 @@ kea-dhcp4.dhcpsrv - this is a base logger for the libdhcpsrv library. + + kea-dhcp4.eval - this logger is used + to log messages relating to the client classification expression + evaluation code. + kea-dhcp4.hooks - this logger is used to log messages related to management of hooks libraries, e.g. @@ -302,6 +307,11 @@ kea-dhcp6.dhcpsrv - this is a base logger for the libdhcpsrv library. + + kea-dhcp6.eval - this logger is used + to log messages relating to the client classification expression + evaluation code. + kea-dhcp6.hooks - this logger is used to log messages related to management of hooks libraries, e.g. diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index d62e03e39b..46182be90b 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -200,6 +200,12 @@ protected: parser = new OptionDataListParser(config_id, options_, AF_INET); } else if (config_id.compare("match-client-id") == 0) { parser = new BooleanParser(config_id, boolean_values_); + } else if (config_id.compare("4o6-subnet") == 0) { + parser = new StringParser(config_id, string_values_); + } else if (config_id.compare("4o6-interface") == 0) { + parser = new StringParser(config_id, string_values_); + } else if (config_id.compare("4o6-interface-id") == 0) { + parser = new StringParser(config_id, string_values_); } else { isc_throw(NotImplemented, "unsupported parameter: " << config_id); } @@ -305,6 +311,43 @@ protected: << ")"); } + // Try 4o6 specific parameter: 4o6-interface + string iface4o6 = string_values_->getOptionalParam("4o6-interface", ""); + if (!iface4o6.empty()) { + subnet4->get4o6().setIface4o6(iface4o6); + subnet4->get4o6().enabled(true); + } + + // Try 4o6 specific parameter: 4o6-subnet + string subnet4o6 = string_values_->getOptionalParam("4o6-subnet", ""); + if (!subnet4o6.empty()) { + size_t slash = subnet4o6.find("/"); + if (slash == std::string::npos) { + isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:" + + subnet4o6 +", expected format: prefix6/length"); + } + string prefix = subnet4o6.substr(0, slash); + string lenstr = subnet4o6.substr(slash + 1); + + uint8_t len = 128; + try { + len = boost::lexical_cast(lenstr.c_str()); + } catch (const boost::bad_lexical_cast &) { + isc_throw(DhcpConfigError, "Invalid prefix length specified in " + "4o6-subnet parameter: " + subnet4o6 + ", expected 0..128 value"); + } + subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len); + subnet4->get4o6().enabled(true); + } + + // Try 4o6 specific paramter: 4o6-interface-id + std::string ifaceid = string_values_->getOptionalParam("4o6-interface-id", ""); + if (!ifaceid.empty()) { + OptionBuffer tmp(ifaceid.begin(), ifaceid.end()); + OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp)); + subnet4->get4o6().setInterfaceId(opt); + subnet4->get4o6().enabled(true); + } // Try setting up client class (if specified) try { @@ -373,8 +416,8 @@ namespace dhcp { /// @return parser for specified global DHCPv4 parameter /// @throw NotImplemented if trying to create a parser for unknown /// config element - DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id, - ConstElementPtr element) { +DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id, + ConstElementPtr element) { DhcpConfigParser* parser = NULL; if ((config_id.compare("valid-lifetime") == 0) || (config_id.compare("renew-timer") == 0) || diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index d89c8a2ba4..c46d643a50 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -1330,10 +1330,7 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1372,10 +1369,8 @@ TEST_F(Dhcp4ParserTest, optionDefRecord) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"record\"," - " \"array\": False," " \"record-types\": \"uint16, ipv4-address, ipv6-address, string\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1422,19 +1417,13 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo-2\"," " \"code\": 101," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1488,19 +1477,13 @@ TEST_F(Dhcp4ParserTest, optionDefDuplicate) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo-2\"," " \"code\": 100," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1529,9 +1512,7 @@ TEST_F(Dhcp4ParserTest, optionDefArray) { " \"code\": 100," " \"type\": \"uint32\"," " \"array\": True," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1571,8 +1552,6 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulate) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"sub-opts-space\"" " } ]" @@ -1613,10 +1592,7 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidName) { " \"name\": \"invalid%name\"," " \"code\": 100," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1640,10 +1616,7 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidType) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"sting\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1667,10 +1640,8 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidRecordType) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"record\"," - " \"array\": False," " \"record-types\": \"uint32,uint8,sting\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1694,8 +1665,6 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidEncapsulatedSpace) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"invalid%space%name\"" " } ]" @@ -1724,7 +1693,6 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulatedSpaceAndArray) { " \"code\": 100," " \"type\": \"uint32\"," " \"array\": True," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"valid-space-name\"" " } ]" @@ -1750,8 +1718,6 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulateOwnSpace) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"isc\"" " } ]" @@ -1781,10 +1747,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) { " \"name\": \"foo\"," " \"code\": 109," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp4\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp4\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1818,10 +1781,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) { " \"name\": \"routers\"," " \"code\": 3," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp4\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp4\"" " } ]" "}"; json = Element::fromJSON(config); @@ -1843,10 +1803,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) { " \"name\": \"nis-server-addr\"," " \"code\": 65," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp4\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp4\"" " } ]" "}"; json = Element::fromJSON(config); @@ -1879,15 +1836,11 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"dhcp-message\"," - " \"space\": \"dhcp4\"," - " \"code\": 56," " \"data\": \"ABCDEF0105\"," " \"csv-format\": False" " }," " {" " \"name\": \"default-ip-ttl\"," - " \"space\": \"dhcp4\"," - " \"code\": 23," " \"data\": \"01\"," " \"csv-format\": False" " } ]," @@ -1952,26 +1905,19 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"dhcp-message\"," - " \"space\": \"dhcp4\"," - " \"code\": 56," " \"data\": \"ABCDEF0105\"," " \"csv-format\": False" " }," " {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 56," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 56," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]," "\"subnet4\": [ { " " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," @@ -2033,34 +1979,24 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) { "\"option-data\": [ {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 1," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"isc\"," - " \"code\": 2," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 1," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 2," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; @@ -2084,31 +2020,22 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"base-option\"," - " \"space\": \"dhcp4\"," - " \"code\": 222," - " \"data\": \"11\"," - " \"csv-format\": True" + " \"data\": \"11\"" " }," " {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 1," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"isc\"," - " \"code\": 2," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"base-option\"," " \"code\": 222," " \"type\": \"uint8\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"dhcp4\"," " \"encapsulate\": \"isc\"" "}," @@ -2116,19 +2043,13 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) { " \"name\": \"foo\"," " \"code\": 1," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 2," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]," "\"subnet4\": [ { " " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," @@ -2180,8 +2101,6 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) { "\"renew-timer\": 1000, " "\"option-data\": [ {" " \"name\": \"dhcp-message\"," - " \"space\": \"dhcp4\"," - " \"code\": 56," " \"data\": \"AB\"," " \"csv-format\": False" " } ]," @@ -2190,15 +2109,11 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) { " \"subnet\": \"192.0.2.0/24\", " " \"option-data\": [ {" " \"name\": \"dhcp-message\"," - " \"space\": \"dhcp4\"," - " \"code\": 56," " \"data\": \"ABCDEF0105\"," " \"csv-format\": False" " }," " {" " \"name\": \"default-ip-ttl\"," - " \"space\": \"dhcp4\"," - " \"code\": 23," " \"data\": \"01\"," " \"csv-format\": False" " } ]" @@ -2337,8 +2252,6 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) { " \"subnet\": \"192.0.2.0/24\", " " \"option-data\": [ {" " \"name\": \"dhcp-message\"," - " \"space\": \"dhcp4\"," - " \"code\": 56," " \"data\": \"0102030405060708090A\"," " \"csv-format\": False" " } ]" @@ -2348,8 +2261,6 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) { " \"subnet\": \"192.0.3.0/24\", " " \"option-data\": [ {" " \"name\": \"default-ip-ttl\"," - " \"space\": \"dhcp4\"," - " \"code\": 23," " \"data\": \"FF\"," " \"csv-format\": False" " } ]" @@ -2610,34 +2521,24 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) { "\"option-data\": [ {" " \"name\": \"foo\"," " \"space\": \"vendor-encapsulated-options-space\"," - " \"code\": 1," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"vendor-encapsulated-options-space\"," - " \"code\": 2," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 1," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-encapsulated-options-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-encapsulated-options-space\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 2," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-encapsulated-options-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-encapsulated-options-space\"" " } ]" "}"; @@ -2665,17 +2566,12 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"vendor-encapsulated-options\"," - " \"space\": \"dhcp4\"," - " \"code\": 43," - " \"data\": \"\"," " \"csv-format\": False" " }," " {" " \"name\": \"foo\"," " \"space\": \"vendor-encapsulated-options-space\"," - " \"code\": 1," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," @@ -2688,19 +2584,13 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) { " \"name\": \"foo\"," " \"code\": 1," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-encapsulated-options-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-encapsulated-options-space\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 2," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-encapsulated-options-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-encapsulated-options-space\"" " } ]," "\"subnet4\": [ { " " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," @@ -2834,17 +2724,13 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) { " \"name\": \"foo\"," " \"space\": \"vendor-4491\"," " \"code\": 100," - " \"data\": \"this is a string vendor-opt\"," - " \"csv-format\": True" + " \"data\": \"this is a string vendor-opt\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-4491\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-4491\"" " } ]," "\"subnet4\": [ { " " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," @@ -2911,26 +2797,19 @@ buildHooksLibrariesConfig(const std::vector& libraries) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"dhcp-message\"," - " \"space\": \"dhcp4\"," - " \"code\": 56," " \"data\": \"ABCDEF0105\"," " \"csv-format\": False" " }," " {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 56," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 56," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]," "\"subnet4\": [ { " " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," @@ -3782,4 +3661,241 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessingError) { EXPECT_TRUE(errorContainsPosition(status, "")); } + +// Checks if the DHCPv4 is able to parse the configuration without 4o6 parameters +// and does not set 4o6 fields at all. +TEST_F(Dhcp4ParserTest, 4o6default) { + + ConstElementPtr status; + + // Just a plain v4 config (no 4o6 parameters) + string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\" } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + + // check if returned status is OK + checkResult(status, 0); + + // Now check if the configuration was indeed handled and we have + // expected pool configured. + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> + getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); + ASSERT_TRUE(subnet); + + Cfg4o6& dhcp4o6 = subnet->get4o6(); + EXPECT_FALSE(dhcp4o6.enabled()); +} + +// Checks if the DHCPv4 is able to parse the configuration with 4o6 subnet +// defined. +TEST_F(Dhcp4ParserTest, 4o6subnet) { + + ConstElementPtr status; + + // Just a plain v4 config (no 4o6 parameters) + string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-subnet\": \"2001:db8::123/45\" } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + + // check if returned status is OK + checkResult(status, 0); + + // Now check if the configuration was indeed handled and we have + // expected pool configured. + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> + getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); + ASSERT_TRUE(subnet); + + Cfg4o6& dhcp4o6 = subnet->get4o6(); + EXPECT_TRUE(dhcp4o6.enabled()); + EXPECT_EQ(IOAddress("2001:db8::123"), dhcp4o6.getSubnet4o6().first); + EXPECT_EQ(45, dhcp4o6.getSubnet4o6().second); +} + +// Checks if the DHCPv4 is able to parse the configuration with 4o6 subnet +// defined. +TEST_F(Dhcp4ParserTest, 4o6subnetBogus) { + + ConstElementPtr status; + + // Just a plain v4 config (no 4o6 parameters) + string config[] = { + // Bogus configuration 1: missing / in subnet + "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-subnet\": \"2001:db8::123\" } ]," + "\"valid-lifetime\": 4000 }", + + // Bogus configuration 2: incorrect address + "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-subnet\": \"2001:db8:bogus/45\" } ]," + "\"valid-lifetime\": 4000 }", + + // Bogus configuration 3: incorrect prefix lenght + "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-subnet\": \"2001:db8::123/200\" } ]," + "\"valid-lifetime\": 4000 }" + }; + + ElementPtr json1 = Element::fromJSON(config[0]); + ElementPtr json2 = Element::fromJSON(config[0]); + ElementPtr json3 = Element::fromJSON(config[0]); + + // Check that the first config is rejected. + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json1)); + checkResult(status, 1); + + // Check that the second config is rejected. + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json2)); + checkResult(status, 1); + + // Check that the third config is rejected. + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json3)); + checkResult(status, 1); +} + + +// Checks if the DHCPv4 is able to parse the configuration with 4o6 network +// interface defined. +TEST_F(Dhcp4ParserTest, 4o6iface) { + + ConstElementPtr status; + + // Just a plain v4 config (no 4o6 parameters) + string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-interface\": \"ethX\" } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + + // check if returned status is OK + checkResult(status, 0); + + // Now check if the configuration was indeed handled and we have + // expected pool configured. + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> + getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); + ASSERT_TRUE(subnet); + + Cfg4o6& dhcp4o6 = subnet->get4o6(); + EXPECT_TRUE(dhcp4o6.enabled()); + EXPECT_EQ("ethX", dhcp4o6.getIface4o6()); +} + +// Checks if the DHCPv4 is able to parse the configuration with both 4o6 network +// interface and v6 subnet defined. +TEST_F(Dhcp4ParserTest, 4o6subnetIface) { + + ConstElementPtr status; + + // Just a plain v4 config (no 4o6 parameters) + string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-subnet\": \"2001:db8::543/21\"," + " \"4o6-interface\": \"ethX\" } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + + // check if returned status is OK + checkResult(status, 0); + + // Now check if the configuration was indeed handled and we have + // expected subnet configured... + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> + getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); + ASSERT_TRUE(subnet); + + // ... and that subnet has 4o6 network interface specified. + Cfg4o6& dhcp4o6 = subnet->get4o6(); + EXPECT_TRUE(dhcp4o6.enabled()); + EXPECT_EQ(IOAddress("2001:db8::543"), dhcp4o6.getSubnet4o6().first); + EXPECT_EQ(21, dhcp4o6.getSubnet4o6().second); + EXPECT_EQ("ethX", dhcp4o6.getIface4o6()); +} + +// Checks if the DHCPv4 is able to parse the configuration with 4o6 network +// interface-id. +TEST_F(Dhcp4ParserTest, 4o6subnetInterfaceId) { + + ConstElementPtr status; + + // Just a plain v4 config (no 4o6 parameters) + string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ]," + " \"subnet\": \"192.0.2.0/24\"," + " \"4o6-interface-id\": \"vlan123\" } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); + + // check if returned status is OK + checkResult(status, 0); + + // Now check if the configuration was indeed handled and we have + // expected 4o6-interface-id configured. + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> + getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); + ASSERT_TRUE(subnet); + + Cfg4o6& dhcp4o6 = subnet->get4o6(); + EXPECT_TRUE(dhcp4o6.enabled()); + OptionPtr ifaceid = dhcp4o6.getInterfaceId(); + ASSERT_TRUE(ifaceid); + + vector data = ifaceid->getData(); + EXPECT_EQ(7, data.size()); + const char *exp = "vlan123"; + EXPECT_EQ(0, memcmp(&data[0], exp, data.size())); +} + } diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 9cf01e236e..79a8531589 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -361,7 +361,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) { time(NULL) - 100, SubnetID(1))); // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); ASSERT_NO_THROW(lease_mgr.addLease(lease0)); ASSERT_NO_THROW(lease_mgr.addLease(lease1)); @@ -419,7 +419,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) { time(NULL) - 100, SubnetID(1))); // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); ASSERT_NO_THROW(lease_mgr.addLease(lease0)); ASSERT_NO_THROW(lease_mgr.addLease(lease1)); diff --git a/src/bin/dhcp4/tests/decline_unittest.cc b/src/bin/dhcp4/tests/decline_unittest.cc index d420749f20..f2faff9079 100644 --- a/src/bin/dhcp4/tests/decline_unittest.cc +++ b/src/bin/dhcp4/tests/decline_unittest.cc @@ -53,10 +53,7 @@ const char* DECLINE_CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " } ]" " } ]" "}" diff --git a/src/bin/dhcp4/tests/dora_unittest.cc b/src/bin/dhcp4/tests/dora_unittest.cc index 36f3090fc3..051bb43edf 100644 --- a/src/bin/dhcp4/tests/dora_unittest.cc +++ b/src/bin/dhcp4/tests/dora_unittest.cc @@ -77,31 +77,19 @@ const char* DORA_CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " }," " {" " \"name\": \"domain-name-servers\"," - " \"code\": 6," - " \"data\": \"10.0.0.202,10.0.0.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " }," " {" " \"name\": \"log-servers\"," - " \"code\": 7," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " }," " {" " \"name\": \"cookie-servers\"," - " \"code\": 8," - " \"data\": \"10.0.0.202,10.0.0.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " } ]" " } ]" "}", @@ -116,31 +104,19 @@ const char* DORA_CONFIGS[] = { " \"subnet\": \"192.0.2.0/24\", " " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"192.0.2.200,192.0.2.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"192.0.2.200,192.0.2.201\"" " }," " {" " \"name\": \"domain-name-servers\"," - " \"code\": 6," - " \"data\": \"192.0.2.202,192.0.2.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"192.0.2.202,192.0.2.203\"" " }," " {" " \"name\": \"log-servers\"," - " \"code\": 7," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " }," " {" " \"name\": \"cookie-servers\"," - " \"code\": 8," - " \"data\": \"10.0.0.202,10.0.0.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " } ]" " } ]" "}", @@ -174,10 +150,7 @@ const char* DORA_CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " } ]" " } ]" "}", diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc index 13610c299b..ca591c07ee 100644 --- a/src/bin/dhcp4/tests/fqdn_unittest.cc +++ b/src/bin/dhcp4/tests/fqdn_unittest.cc @@ -47,10 +47,7 @@ const char* CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " } ]," " \"reservations\": [" " {" @@ -74,10 +71,7 @@ const char* CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " } ]," " \"reservations\": [" " {" diff --git a/src/bin/dhcp4/tests/inform_unittest.cc b/src/bin/dhcp4/tests/inform_unittest.cc index e1fd87215a..ce5f604381 100644 --- a/src/bin/dhcp4/tests/inform_unittest.cc +++ b/src/bin/dhcp4/tests/inform_unittest.cc @@ -58,31 +58,19 @@ const char* INFORM_CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " }," " {" " \"name\": \"domain-name-servers\"," - " \"code\": 6," - " \"data\": \"10.0.0.202,10.0.0.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " }," " {" " \"name\": \"log-servers\"," - " \"code\": 7," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " }," " {" " \"name\": \"cookie-servers\"," - " \"code\": 8," - " \"data\": \"10.0.0.202,10.0.0.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " } ]" " } ]" "}", @@ -96,31 +84,19 @@ const char* INFORM_CONFIGS[] = { " \"subnet\": \"192.0.2.0/24\", " " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"192.0.2.200,192.0.2.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"192.0.2.200,192.0.2.201\"" " }," " {" " \"name\": \"domain-name-servers\"," - " \"code\": 6," - " \"data\": \"192.0.2.202,192.0.2.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"192.0.2.202,192.0.2.203\"" " }," " {" " \"name\": \"log-servers\"," - " \"code\": 7," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " }," " {" " \"name\": \"cookie-servers\"," - " \"code\": 8," - " \"data\": \"10.0.0.202,10.0.0.203\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.202,10.0.0.203\"" " } ]" " } ]" "}" diff --git a/src/bin/dhcp4/tests/kea_controller_unittest.cc b/src/bin/dhcp4/tests/kea_controller_unittest.cc index 4e75770d5c..96dfa078ed 100644 --- a/src/bin/dhcp4/tests/kea_controller_unittest.cc +++ b/src/bin/dhcp4/tests/kea_controller_unittest.cc @@ -377,7 +377,7 @@ TEST_F(JSONFileBackendTest, timers) { lease_reclaimed->state_ = Lease4::STATE_EXPIRED_RECLAIMED; // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); ASSERT_NO_THROW(lease_mgr.addLease(lease_expired)); ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed)); diff --git a/src/bin/dhcp4/tests/release_unittest.cc b/src/bin/dhcp4/tests/release_unittest.cc index c1ac1fda9a..5b6985610f 100644 --- a/src/bin/dhcp4/tests/release_unittest.cc +++ b/src/bin/dhcp4/tests/release_unittest.cc @@ -53,10 +53,7 @@ const char* RELEASE_CONFIGS[] = { " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]," " \"option-data\": [ {" " \"name\": \"routers\"," - " \"code\": 3," - " \"data\": \"10.0.0.200,10.0.0.201\"," - " \"csv-format\": true," - " \"space\": \"dhcp4\"" + " \"data\": \"10.0.0.200,10.0.0.201\"" " } ]" " } ]" "}" diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index cc269ae91c..c7bf0de63f 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -665,8 +665,8 @@ namespace dhcp { /// @return parser for specified global DHCPv6 parameter /// @throw NotImplemented if trying to create a parser for unknown config /// element - DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id, - ConstElementPtr element) { +DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id, + ConstElementPtr element) { DhcpConfigParser* parser = NULL; if ((config_id.compare("preferred-lifetime") == 0) || (config_id.compare("valid-lifetime") == 0) || diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 108902698e..983fcc0b5b 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -205,10 +205,7 @@ public: " \"name\": \"bool-option\"," " \"code\": 1000," " \"type\": \"boolean\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp6\"" "} ]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ]," @@ -1572,10 +1569,7 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"ipv6-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1612,10 +1606,8 @@ TEST_F(Dhcp6ParserTest, optionDefRecord) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"record\"," - " \"array\": False," " \"record-types\": \"uint16, ipv4-address, ipv6-address, string\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1661,19 +1653,13 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo-2\"," " \"code\": 101," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1725,19 +1711,13 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo-2\"," " \"code\": 100," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1766,9 +1746,7 @@ TEST_F(Dhcp6ParserTest, optionDefArray) { " \"code\": 100," " \"type\": \"uint32\"," " \"array\": True," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1806,8 +1784,6 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulate) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"sub-opts-space\"" " } ]" @@ -1847,10 +1823,7 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidName) { " \"name\": \"invalid%name\"," " \"code\": 100," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1874,10 +1847,7 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidType) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"sting\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1901,10 +1871,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"record\"," - " \"array\": False," " \"record-types\": \"uint32,uint8,sting\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -1928,8 +1896,6 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidEncapsulatedSpace) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"invalid%space%name\"" " } ]" @@ -1958,7 +1924,6 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulatedSpaceAndArray) { " \"code\": 100," " \"type\": \"uint32\"," " \"array\": True," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"valid-space-name\"" " } ]" @@ -1984,8 +1949,6 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulateOwnSpace) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"isc\"," " \"encapsulate\": \"isc\"" " } ]" @@ -2016,10 +1979,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp6\"" " } ]" "}"; ElementPtr json = Element::fromJSON(config); @@ -2053,10 +2013,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) { " \"name\": \"foo\"," " \"code\": 3," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp6\"" " } ]" "}"; json = Element::fromJSON(config); @@ -2078,10 +2035,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) { " \"name\": \"geolocation\"," " \"code\": 63," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp6\"" " } ]" "}"; json = Element::fromJSON(config); @@ -2114,17 +2068,12 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"subscriber-id\"," - " \"space\": \"dhcp6\"," - " \"code\": 38," " \"data\": \"ABCDEF0105\"," " \"csv-format\": False" " }," " {" " \"name\": \"preference\"," - " \"space\": \"dhcp6\"," - " \"code\": 7," - " \"data\": \"01\"," - " \"csv-format\": True" + " \"data\": \"01\"" " } ]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ]," @@ -2196,26 +2145,19 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"subscriber-id\"," - " \"space\": \"dhcp6\"," - " \"code\": 38," " \"data\": \"ABCDEF0105\"," " \"csv-format\": False" " }," " {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 38," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 38," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ]," @@ -2278,34 +2220,24 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) { "\"option-data\": [ {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 110," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"isc\"," - " \"code\": 111," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 110," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 111," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]" "}"; @@ -2330,31 +2262,22 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"base-option\"," - " \"space\": \"dhcp6\"," - " \"code\": 100," - " \"data\": \"11\"," - " \"csv-format\": True" + " \"data\": \"11\"" " }," " {" " \"name\": \"foo\"," " \"space\": \"isc\"," - " \"code\": 110," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"isc\"," - " \"code\": 111," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"base-option\"," " \"code\": 100," " \"type\": \"uint8\"," - " \"array\": False," - " \"record-types\": \"\"," " \"space\": \"dhcp6\"," " \"encapsulate\": \"isc\"" "}," @@ -2362,19 +2285,13 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) { " \"name\": \"foo\"," " \"code\": 110," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 111," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ]," @@ -2429,8 +2346,6 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) { " \"subnet\": \"2001:db8:1::/64\", " " \"option-data\": [ {" " \"name\": \"subscriber-id\"," - " \"space\": \"dhcp6\"," - " \"code\": 38," " \"data\": \"0102030405060708090A\"," " \"csv-format\": False" " } ]" @@ -2440,8 +2355,6 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) { " \"subnet\": \"2001:db8:2::/64\", " " \"option-data\": [ {" " \"name\": \"user-class\"," - " \"space\": \"dhcp6\"," - " \"code\": 15," " \"data\": \"FFFEFDFCFB\"," " \"csv-format\": False" " } ]" @@ -2804,17 +2717,13 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) { " \"name\": \"foo\"," " \"space\": \"vendor-4491\"," " \"code\": 100," - " \"data\": \"this is a string vendor-opt\"," - " \"csv-format\": True" + " \"data\": \"this is a string vendor-opt\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-4491\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-4491\"" " } ]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ]," @@ -2869,34 +2778,24 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) { "\"option-data\": [ {" " \"name\": \"foo\"," " \"space\": \"vendor-opts-space\"," - " \"code\": 110," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"vendor-opts-space\"," - " \"code\": 111," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 110," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-opts-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-opts-space\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 111," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-opts-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-opts-space\"" " } ]" "}"; @@ -2923,42 +2822,29 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) { "\"renew-timer\": 1000," "\"option-data\": [ {" " \"name\": \"vendor-opts\"," - " \"space\": \"dhcp6\"," - " \"code\": 17," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo\"," " \"space\": \"vendor-opts-space\"," - " \"code\": 110," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"vendor-opts-space\"," - " \"code\": 111," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 110," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-opts-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-opts-space\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 111," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-opts-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-opts-space\"" " } ]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ]," @@ -3049,34 +2935,24 @@ buildHooksLibrariesConfig(const std::vector& libraries) { "\"option-data\": [ {" " \"name\": \"foo\"," " \"space\": \"vendor-opts-space\"," - " \"code\": 110," - " \"data\": \"1234\"," - " \"csv-format\": True" + " \"data\": \"1234\"" " }," " {" " \"name\": \"foo2\"," " \"space\": \"vendor-opts-space\"," - " \"code\": 111," - " \"data\": \"192.168.2.1\"," - " \"csv-format\": True" + " \"data\": \"192.168.2.1\"" " } ]," "\"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 110," " \"type\": \"uint32\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-opts-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-opts-space\"" " }," " {" " \"name\": \"foo2\"," " \"code\": 111," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-opts-space\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-opts-space\"" " } ]" "}"); diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index 967bdb2067..7428796271 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -436,7 +436,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaim) { lease1->cltt_ = time(NULL) - 100; // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); ASSERT_NO_THROW(lease_mgr.addLease(lease0)); ASSERT_NO_THROW(lease_mgr.addLease(lease1)); @@ -498,7 +498,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaimRemove) { lease1->cltt_ = time(NULL) - 100; // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); ASSERT_NO_THROW(lease_mgr.addLease(lease0)); ASSERT_NO_THROW(lease_mgr.addLease(lease1)); diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 9fd2a3a933..a5d1c1519c 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -300,15 +300,10 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) { " \"interface\": \"eth0\", " " \"option-data\": [ {" " \"name\": \"dns-servers\"," - " \"space\": \"dhcp6\"," - " \"code\": 23," - " \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\"," - " \"csv-format\": True" + " \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\"" " }," " {" " \"name\": \"subscriber-id\"," - " \"space\": \"dhcp6\"," - " \"code\": 38," " \"data\": \"1234\"," " \"csv-format\": False" " } ]" @@ -1615,17 +1610,12 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsORO) { " \"name\": \"config-file\"," " \"code\": 33," " \"type\": \"string\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"vendor-4491\"," - " \"encapsulate\": \"\"" + " \"space\": \"vendor-4491\"" " } ]," " \"option-data\": [ {" " \"name\": \"config-file\"," " \"space\": \"vendor-4491\"," - " \"code\": 33," - " \"data\": \"normal_erouter_v6.cm\"," - " \"csv-format\": True" + " \"data\": \"normal_erouter_v6.cm\"" " }]," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," @@ -2305,11 +2295,7 @@ TEST_F(Dhcpv6SrvTest, rsooOverride) { " \"option-def\": [ {" " \"name\": \"foo\"," " \"code\": 120," - " \"type\": \"binary\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"type\": \"binary\"" " } ]," " \"option-data\": [ {" " \"code\": 120," diff --git a/src/bin/dhcp6/tests/kea_controller_unittest.cc b/src/bin/dhcp6/tests/kea_controller_unittest.cc index 4778670673..e00c9c9809 100644 --- a/src/bin/dhcp6/tests/kea_controller_unittest.cc +++ b/src/bin/dhcp6/tests/kea_controller_unittest.cc @@ -321,7 +321,7 @@ TEST_F(JSONFileBackendTest, timers) { lease_reclaimed->state_ = Lease6::STATE_EXPIRED_RECLAIMED; // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); ASSERT_NO_THROW(lease_mgr.addLease(lease_expired)); ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed)); diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index 73f1b00590..b892192b9c 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -48,6 +48,7 @@ libkea_dhcp___la_SOURCES += protocol_util.cc protocol_util.h libkea_dhcp___la_SOURCES += pkt.cc pkt.h libkea_dhcp___la_SOURCES += pkt6.cc pkt6.h libkea_dhcp___la_SOURCES += pkt4.cc pkt4.h +libkea_dhcp___la_SOURCES += pkt4o6.cc pkt4o6.h libkea_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc libkea_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc libkea_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h index 6e053f69b1..be4feeffc9 100644 --- a/src/lib/dhcp/dhcp6.h +++ b/src/lib/dhcp/dhcp6.h @@ -110,8 +110,8 @@ //#define D6O_ADDRSEL 84 /* RFC7078 */ //#define D6O_ADDRSEL_TABLE 85 /* RFC7078 */ //#define D6O_V6_PCP_SERVER 86 /* RFC7291 */ -//#define D6O_DHCPV4_MSG 87 /* RFC7341 */ -//#define D6O_DHCPV4_O_DHCPV6_SERVER 88 /* RFC7341 */ +#define D6O_DHCPV4_MSG 87 /* RFC7341 */ +#define D6O_DHCPV4_O_DHCPV6_SERVER 88 /* RFC7341 */ //#define D6O_S46_RULE 89 /* RFC7598 */ //#define D6O_S46_BR 90 /* RFC7598 */ //#define D6O_S46_DMR 91 /* RFC7598 */ @@ -123,10 +123,9 @@ //#define D6O_4RD 97 /* RFC7600 */ //#define D6O_4RD_MAP_RULE 98 /* RFC7600 */ //#define D6O_4RD_NON_MAP_RULE 99 /* RFC7600 */ -/* draft-ietf-dhc-dhcpv6-active-leasequery-04 */ -//#define D6O_LQ_BASE_TIME 100 -//#define D6O_LQ_START_TIME 101 -//#define D6O_LQ_END_TIME 102 +//#define D6O_LQ_BASE_TIME 100 /* RFC7653 */ +//#define D6O_LQ_START_TIME 101 /* RFC7653 */ +//#define D6O_LQ_END_TIME 102 /* RFC7653 */ /* 103-142 unassigned */ //#define D6O_IPV6_ADDRESS_ANDSF 143 /* RFC6153 */ @@ -195,8 +194,8 @@ //#define DHCPV6_RECONFIGURE_REQUEST 18 //#define DHCPV6_RECONFIGURE_REPLY 19 /* RFC 7341 */ -//#define DHCPV6_DHCPV4_QUERY 20 -//#define DHCPV6_DHCPV4_RESPONSE 21 +#define DHCPV6_DHCPV4_QUERY 20 +#define DHCPV6_DHCPV4_RESPONSE 21 /* draft-ietf-dhc-dhcpv6-active-leasequery-04 */ //#define DHCPV6_ACTIVELEASEQUERY 22 //#define DHCPV6_STARTTLS 23 @@ -223,6 +222,11 @@ extern const int dhcpv6_type_name_max; // Taken from http://www.iana.org/assignments/enterprise-numbers #define ENTERPRISE_ID_ISC 2495 +/* DHCPv4-over-DHCPv6 (RFC 7341) inter-process communication. These are option + codes for the ISC vendor specific options used in 4o6 */ +#define ISC_V6_4O6_INTERFACE 60000 +#define ISC_V6_4O6_SRC_ADDRESS 60001 + /* Offsets into IA_*'s where Option spaces commence. */ #define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */ #define IA_TA_OFFSET 4 /* IAID only, 4 octets */ @@ -298,4 +302,7 @@ extern const int dhcpv6_type_name_max; #define IRT_DEFAULT 86400 #define IRT_MINIMUM 600 +/* DHCPv4-query message flags (see RFC7341) */ +#define DHCPV4_QUERY_FLAGS_UNICAST (1 << 23) + #endif /* DHCP6_H */ diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index db484cce36..069f09a04a 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -110,6 +110,11 @@ LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) { initVendorOptsDocsis6(); } + if (vendor_id == ENTERPRISE_ID_ISC && + vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) { + initVendorOptsIsc6(); + } + VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id); if (def == vendor6_defs_.end()) { // No such vendor-id space @@ -737,6 +742,12 @@ LibDHCP::initVendorOptsDocsis6() { initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE); } +void +LibDHCP::initVendorOptsIsc6() { + vendor6_defs_[ENTERPRISE_ID_ISC] = OptionDefContainer(); + initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE); +} + void initOptionSpace(OptionDefContainer& defs, const OptionDefParams* params, size_t params_size) { diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index 380e0aeacb..57fac075bf 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -281,6 +281,9 @@ private: static void initVendorOptsDocsis6(); + /// Initialize private DHCPv6 option definitions. + static void initVendorOptsIsc6(); + /// pointers to factories that produce DHCPv6 options static FactoryMap v4factories_; diff --git a/src/lib/dhcp/pkt.cc b/src/lib/dhcp/pkt.cc index dfd1f8aa33..1af44ba989 100644 --- a/src/lib/dhcp/pkt.cc +++ b/src/lib/dhcp/pkt.cc @@ -50,10 +50,10 @@ Pkt::Pkt(const uint8_t* buf, uint32_t len, const isc::asiolink::IOAddress& local { if (len != 0) { - if (buf == NULL) { - isc_throw(InvalidParameter, "data buffer passed to Pkt is NULL"); - } - data_.resize(len); + if (buf == NULL) { + isc_throw(InvalidParameter, "data buffer passed to Pkt is NULL"); + } + data_.resize(len); memcpy(&data_[0], buf, len); } } diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h index 0c7d009e16..a4c0cc2db9 100644 --- a/src/lib/dhcp/pkt4.h +++ b/src/lib/dhcp/pkt4.h @@ -375,6 +375,14 @@ public: /// (true) or non-relayed (false). bool isRelayed() const; + /// @brief Checks if a DHCPv4 message has beeb transported over DHCPv6 + /// + /// @return Boolean value which indicates whether the message is + /// transported over DHCPv6 (true) or native DHCPv4 (false) + virtual bool isDhcp4o6() const { + return (false); + } + private: /// @brief Generic method that validates and sets HW address. diff --git a/src/lib/dhcp/pkt4o6.cc b/src/lib/dhcp/pkt4o6.cc new file mode 100644 index 0000000000..2dd3d0ff25 --- /dev/null +++ b/src/lib/dhcp/pkt4o6.cc @@ -0,0 +1,60 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include +#include +#include +#include +#include + +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::util; +using namespace std; + +namespace isc { +namespace dhcp { + +Pkt4o6::Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6) + :Pkt4(&pkt4[0], pkt4.size()), pkt6_(pkt6) +{ + static_cast(pkt6->delOption(D6O_DHCPV4_MSG)); + setIface(pkt6->getIface()); + setIndex(pkt6->getIndex()); + setRemoteAddr(pkt6->getRemoteAddr()); +} + +Pkt4o6::Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6) + :Pkt4(*pkt4), pkt6_(pkt6) { +} + +void Pkt4o6::pack() { + // Convert wire-format Pkt4 data in the form of OptionBuffer. + Pkt4::pack(); + OutputBuffer& buf = getBuffer(); + const uint8_t* ptr = static_cast(buf.getData()); + OptionBuffer msg(ptr, ptr + buf.getLength()); + + // Build the DHCPv4 Message option for the DHCPv6 message, and pack the + // entire stuff. + OptionPtr dhcp4_msg(new Option(Option::V6, D6O_DHCPV4_MSG, msg)); + pkt6_->addOption(dhcp4_msg); + pkt6_->pack(); +} + +} // end of namespace isc::dhcp + +} // end of namespace isc diff --git a/src/lib/dhcp/pkt4o6.h b/src/lib/dhcp/pkt4o6.h new file mode 100644 index 0000000000..0e87f8e112 --- /dev/null +++ b/src/lib/dhcp/pkt4o6.h @@ -0,0 +1,83 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef PKT4O6_H +#define PKT4O6_H + +#include +#include + +#include + +namespace isc { + +namespace dhcp { + +/// @brief Represents DHCPv4-over-DHCPv6 packet +/// +/// This class derives from @c Pkt4 in order to be handled by +/// the DHCPv4 server code. It includes a shared pointer to the +/// DHCPv6 message too. +/// +/// This is an implementation of the DHCPv4-query/response DHCPv6 messages +/// defined in RFC 7341 (http://ietf.org/rfc/rfc7341.txt). +/// See also http://kea.isc.org/wiki/Dhcp4o6Design for design discussions. +class Pkt4o6 : public Pkt4 { +public: + + /// @brief Constructor, used in message reception. + /// + /// @param pkt4 Content of the DHCPv4-message option + /// @param pkt6 encapsulating unpacked DHCPv6 message + /// the DHCPv4 message option will be removed + Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6); + + /// @brief Constructor, used in replying to a message + /// + /// @param pkt4 DHCPv4 message + /// @param pkt6 DHCPv6 message + Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6); + + /// @brief Returns encapsulating DHCPv6 message + Pkt6Ptr getPkt6() const { return (pkt6_); } + + /// @brief Prepares on-wire format of DHCPv4-over-DHCPv6 packet. + /// + /// Calls pack() on both DHCPv4 and DHCPv6 parts + /// Inserts the DHCPv4-message option + /// @ref Pkt4::pack and @ref Pkt6::pack + virtual void pack(); + + /// @brief Checks if a DHCPv4 message has been transported over DHCPv6 + /// + /// @return Boolean value which indicates whether the message is + /// transported over DHCPv6 (true) or native DHCPv4 (false) + virtual bool isDhcp4o6() const { + return (true); + } + +private: + /// Encapsulating DHCPv6 message + Pkt6Ptr pkt6_; + +}; // Pkt4o6 class + +/// @brief A pointer to Pkt4o6 object. +typedef boost::shared_ptr Pkt4o6Ptr; + +} // isc::dhcp namespace + +} // isc namespace + +#endif diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc index 91d1e18049..33b4633722 100644 --- a/src/lib/dhcp/pkt6.cc +++ b/src/lib/dhcp/pkt6.cc @@ -289,6 +289,8 @@ Pkt6::unpackUDP() { case DHCPV6_DECLINE: case DHCPV6_RECONFIGURE: case DHCPV6_INFORMATION_REQUEST: + case DHCPV6_DHCPV4_QUERY: + case DHCPV6_DHCPV4_RESPONSE: default: // assume that uknown messages are not using relay format { return (unpackMsg(data_.begin(), data_.end())); @@ -586,6 +588,8 @@ Pkt6::getName(const uint8_t type) { static const char* REPLY = "REPLY"; static const char* REQUEST = "REQUEST"; static const char* SOLICIT = "SOLICIT"; + static const char* DHCPV4_QUERY = "DHCPV4_QUERY"; + static const char* DHCPV4_RESPONSE = "DHCPV4_RESPONSE"; static const char* UNKNOWN = "UNKNOWN"; switch (type) { @@ -634,6 +638,12 @@ Pkt6::getName(const uint8_t type) { case DHCPV6_SOLICIT: return (SOLICIT); + case DHCPV6_DHCPV4_QUERY: + return (DHCPV4_QUERY); + + case DHCPV6_DHCPV4_RESPONSE: + return (DHCPV4_RESPONSE); + default: ; } diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index 92f1976c5d..b9fbb98fc6 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -353,6 +353,9 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = { { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" }, { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, + { "dhcpv4-message", D6O_DHCPV4_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, + { "dhcp4o6-server-addr", D6O_DHCPV4_O_DHCPV6_SERVER, OPT_IPV6_ADDRESS_TYPE, true, + NO_RECORD_DEF, "" }, { "public-key", D6O_PUBLIC_KEY, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" }, { "certificate", D6O_CERTIFICATE, OPT_BINARY_TYPE, false, @@ -371,6 +374,19 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = { const int OPTION_DEF_PARAMS_SIZE6 = sizeof(OPTION_DEF_PARAMS6) / sizeof(OPTION_DEF_PARAMS6[0]); +/// @brief Definitions of vendor-specific DHCPv6 options, defined by ISC. +/// 4o6-* options are used for inter-process communication. For details, see +/// http://kea.isc.org/wiki/Dhcp4o6Design +/// +/// @todo: As those options are defined by ISC, they do not belong in std_option_defs.h. +/// We need to move them to a separate file, e.g. isc_option_defs.h +const OptionDefParams ISC_V6_DEFS[] = { + { "4o6-interface", ISC_V6_4O6_INTERFACE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" }, + { "4o6-source-address", ISC_V6_4O6_SRC_ADDRESS, OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" } +}; + +const int ISC_V6_DEFS_SIZE = sizeof(ISC_V6_DEFS) / sizeof(OptionDefParams); + } // unnamed namespace } // namespace dhcp diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 2e21b53f41..8faf432b14 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -74,6 +74,7 @@ libdhcp___unittests_SOURCES += option_vendor_class_unittest.cc libdhcp___unittests_SOURCES += pkt_captures4.cc pkt_captures6.cc pkt_captures.h libdhcp___unittests_SOURCES += pkt4_unittest.cc libdhcp___unittests_SOURCES += pkt6_unittest.cc +libdhcp___unittests_SOURCES += pkt4o6_unittest.cc libdhcp___unittests_SOURCES += pkt_filter_unittest.cc libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index dc71f99873..048e31f361 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -1200,6 +1200,12 @@ TEST_F(LibDhcpTest, stdOptionDefs6) { fqdn_buf.begin(), fqdn_buf.end(), typeid(OptionCustom)); + LibDhcpTest::testStdOptionDefs6(D6O_DHCPV4_MSG, begin, end, + typeid(Option)); + + LibDhcpTest::testStdOptionDefs6(D6O_DHCPV4_O_DHCPV6_SERVER, begin, end, + typeid(Option6AddrLst)); + LibDhcpTest::testStdOptionDefs6(D6O_PUBLIC_KEY, begin, end, typeid(Option)); diff --git a/src/lib/dhcp/tests/pkt4o6_unittest.cc b/src/lib/dhcp/tests/pkt4o6_unittest.cc new file mode 100644 index 0000000000..53cef15b9e --- /dev/null +++ b/src/lib/dhcp/tests/pkt4o6_unittest.cc @@ -0,0 +1,106 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +using namespace isc::dhcp; + +namespace { + +/// @brief A Fixture class dedicated to testing of the Pkt4o6 class that +/// represents a DHCPv4-over-DHCPv6 packet. +class Pkt4o6Test : public ::testing::Test { +protected: + Pkt4o6Test() : + data6_(6, 0), + pkt6_(new Pkt6(&data6_[0], data6_.size())), + pkt4_(new Pkt4(DHCPDISCOVER, 0x12345678)) + { + pkt4_->pack(); + const uint8_t* cp = static_cast( + pkt4_->getBuffer().getData()); + buffer4_.assign(cp, cp + pkt4_->getBuffer().getLength()); + } + +protected: + // commonly used test data + const std::vector data6_; // data for Pkt6 (content unimportant) + Pkt6Ptr pkt6_; // DHCPv6 message for 4o6 + Pkt4Ptr pkt4_; // DHCPv4 message for 4o6 + OptionBuffer buffer4_; // wire-format data buffer of pkt4_ +}; + +// This test verifies that the constructors are working as expected. +TEST_F(Pkt4o6Test, construct) { + // Construct 4o6 packet, unpack the data to examine it + boost::scoped_ptr pkt4o6(new Pkt4o6(buffer4_, pkt6_)); + pkt4o6->unpack(); + // Inspect its internal to confirm it's built as expected. We also test + // isDhcp4o6() here. + EXPECT_TRUE(pkt4o6->isDhcp4o6()); + EXPECT_EQ(pkt6_, pkt4o6->getPkt6()); + EXPECT_EQ(DHCPDISCOVER, pkt4o6->getType()); + + // Same check for the other constructor. It relies on the internal + // behavior of Pkt4's copy constructor, so we need to first unpack pkt4. + pkt4_.reset(new Pkt4(&buffer4_[0], buffer4_.size())); + pkt4_->unpack(); + pkt4o6.reset(new Pkt4o6(pkt4_, pkt6_)); + EXPECT_TRUE(pkt4o6->isDhcp4o6()); + EXPECT_EQ(pkt6_, pkt4o6->getPkt6()); + EXPECT_EQ(DHCPDISCOVER, pkt4o6->getType()); +} + +// This test verifies that the pack() method handles the building +// process correctly. +TEST_F(Pkt4o6Test, pack) { + // prepare unpacked DHCPv4 packet (see the note in constructor test) + pkt4_.reset(new Pkt4(&buffer4_[0], buffer4_.size())); + pkt4_->unpack(); + + // Construct 4o6 packet to be tested and pack the data. + Pkt4o6 pkt4o6(pkt4_, pkt6_); + pkt4o6.pack(); + + // The packed data should be: + // 4-byte DHCPv6 message header + // 4-byte header part of DHCPv4 message option + // Raw DHCPv4 message (data stored in buffer4_) + EXPECT_EQ(4 + 4 + buffer4_.size(), + pkt4o6.getPkt6()->getBuffer().getLength()); + + // Check the DHCPv4 message option content (Pkt4o6 class is not responsible + // for making it valid, so we won't examine it) + const uint8_t* cp = static_cast( + pkt4o6.getPkt6()->getBuffer().getData()); + EXPECT_EQ(0, cp[4]); + EXPECT_EQ(D6O_DHCPV4_MSG, cp[5]); + EXPECT_EQ((buffer4_.size() >> 8) & 0xff, cp[6]); + EXPECT_EQ(buffer4_.size() & 0xff, cp[7]); + EXPECT_EQ(0, memcmp(&cp[8], &buffer4_[0], buffer4_.size())); +} + +/// @todo: Add a test that handles actual DHCP4o6 traffic capture +/// once we get it. We should add the capture to pkt_captures{4,6}.cc +} diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc index 4cb1548723..6dbc01611b 100644 --- a/src/lib/dhcp/tests/pkt6_unittest.cc +++ b/src/lib/dhcp/tests/pkt6_unittest.cc @@ -594,6 +594,14 @@ TEST_F(Pkt6Test, getName) { EXPECT_STREQ("DECLINE", Pkt6::getName(type)); break; + case DHCPV6_DHCPV4_QUERY: + EXPECT_STREQ("DHCPV4_QUERY", Pkt6::getName(type)); + break; + + case DHCPV6_DHCPV4_RESPONSE: + EXPECT_STREQ("DHCPV4_RESPONSE", Pkt6::getName(type)); + break; + case DHCPV6_INFORMATION_REQUEST: EXPECT_STREQ("INFORMATION_REQUEST", Pkt6::getName(type)); diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index 462945f88f..d39763d91e 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -593,7 +593,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) { // The decodeHex function expects that the string contains an // even number of digits. If we don't meet this requirement, // we have to insert a leading 0. - if (!data_param.empty() && data_param.length() % 2) { + if (!data_param.empty() && ((data_param.length() % 2) != 0)) { data_param = data_param.insert(0, "0"); } util::encode::decodeHex(data_param, binary); diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index 30d530aab3..5eb5aa863f 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -745,6 +745,7 @@ private: /// Instance of option definition being created by this parser. OptionDefinitionPtr option_definition_; + /// Name of the space the option definition belongs to. std::string option_space_name_; diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 55e4ddd716..54ce6a6ee0 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -507,6 +507,83 @@ private: /// @brief A generic pointer to either Subnet4 or Subnet6 object typedef boost::shared_ptr SubnetPtr; +/// @brief This structure contains information about DHCP4o6 (RFC7341) +/// +/// DHCP4o6 is completely optional. If it is not enabled, this structure +/// does not contain any information. +struct Cfg4o6 { + + /// the default constructor. + /// + /// Initializes fields to their default value. + Cfg4o6() + :enabled_(false), subnet4o6_(std::make_pair(asiolink::IOAddress("::"), 128u)) { + } + + /// @brief Returns whether the DHCP4o6 is enabled or not. + /// @return true if enabled + bool enabled() const { + return (enabled_); + } + + /// @brief Sets the DHCP4o6 enabled status. + /// @param enabled specifies if the DHCP4o6 should be enabled or not + void enabled(bool enabled) { + enabled_ = enabled; + } + + /// @brief Returns the DHCP4o6 interface. + /// @return value of the 4o6-interface parameter. + std::string getIface4o6() const { + return (iface4o6_); + } + + /// @brief Sets the 4o6-interface. + /// @param iface name of the network interface the 4o6 traffic is received on + void setIface4o6(const std::string& iface) { + iface4o6_ = iface; + } + + /// @brief Returns prefix/len for the IPv6 subnet. + /// @return prefix/length pair + std::pair getSubnet4o6() const { + return (subnet4o6_); + } + + /// @brief Sets the prefix/length information (content of the 4o6-subnet). + /// @param subnet IOAddress that represents a prefix + /// @param prefix specifies prefix length + void setSubnet4o6(const asiolink::IOAddress& subnet, uint8_t prefix) { + subnet4o6_ = std::make_pair(subnet, prefix); + } + + /// @brief Returns the interface-id. + /// @return the option representing interface-id (or NULL) + OptionPtr getInterfaceId() const { + return (interface_id_); + } + + /// @brief Sets the interface-id + /// @param opt option to be used as interface-id match + void setInterfaceId(const OptionPtr& opt) { + interface_id_ = opt; + } + +private: + + /// Specifies if 4o6 is enabled on this subnet. + bool enabled_; + + /// Specifies the network interface used as v4 subnet selector. + std::string iface4o6_; + + /// Specifies the IPv6 subnet used for v4 subnet selection. + std::pair subnet4o6_; + + /// Specifies the v6 interface-id used for v4 subnet selection. + OptionPtr interface_id_; +}; + /// @brief A configuration holder for IPv4 subnet. /// /// This class represents an IPv4 subnet. @@ -559,6 +636,14 @@ public: return (match_client_id_); } + /// @brief Returns DHCP4o6 configuration parameters. + /// + /// This structure is always available. If the 4o6 is not enabled, its + /// enabled_ field will be set to false. + Cfg4o6& get4o6() { + return (dhcp4o6_); + } + private: /// @brief Returns default address for pool selection @@ -581,6 +666,9 @@ private: /// @brief Should server use client identifiers for client lease /// lookup. bool match_client_id_; + + /// @brief All the information related to DHCP4o6 + Cfg4o6 dhcp4o6_; }; /// @brief A pointer to a @c Subnet4 object diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc index e85bd3787f..4840e6d339 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc @@ -458,10 +458,10 @@ public: std::string error_text_; }; -/// @brief Check Basic parsing of option definitions. +/// @brief Check basic parsing of option definitions. /// /// Note that this tests basic operation of the OptionDefinitionListParser and -/// OptionDefinitionParser. It uses a simple configuration consisting of one +/// OptionDefinitionParser. It uses a simple configuration consisting of /// one definition and verifies that it is parsed and committed to storage /// correctly. TEST_F(ParseConfigTest, basicOptionDefTest) { @@ -481,7 +481,7 @@ TEST_F(ParseConfigTest, basicOptionDefTest) { // Verify that the configuration string parses. int rcode = parseConfiguration(config); - ASSERT_TRUE(rcode == 0); + ASSERT_EQ(0, rcode); // Verify that the option definition can be retrieved. @@ -497,7 +497,73 @@ TEST_F(ParseConfigTest, basicOptionDefTest) { EXPECT_TRUE(def->getEncapsulatedSpace().empty()); } -/// @brief Check Basic parsing of options. +/// @brief Check minimal parsing of option definitions. +/// +/// Same than basic but without optional parameters set to their default. +TEST_F(ParseConfigTest, minimalOptionDefTest) { + + // Configuration string. + std::string config = + "{ \"option-def\": [ {" + " \"name\": \"foo\"," + " \"code\": 100," + " \"type\": \"ipv4-address\"," + " \"space\": \"isc\"" + " } ]" + "}"; + + // Verify that the configuration string parses. + int rcode = parseConfiguration(config); + ASSERT_EQ(0, rcode); + + + // Verify that the option definition can be retrieved. + OptionDefinitionPtr def = + CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100); + ASSERT_TRUE(def); + + // Verify that the option definition is correct. + EXPECT_EQ("foo", def->getName()); + EXPECT_EQ(100, def->getCode()); + EXPECT_FALSE(def->getArrayType()); + EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType()); + EXPECT_TRUE(def->getEncapsulatedSpace().empty()); +} + +/// @brief Check parsing of option definitions using default dhcp6 space. +/// +/// Same than minimal but using the fact the default universe is V6 +/// so the default space is dhcp6 +TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) { + + // Configuration string. + std::string config = + "{ \"option-def\": [ {" + " \"name\": \"foo\"," + " \"code\": 10000," + " \"type\": \"ipv6-address\"" + " } ]" + "}"; + + // Verify that the configuration string parses. + int rcode = parseConfiguration(config); + ASSERT_EQ(0, rcode); + + + // Verify that the option definition can be retrieved. + OptionDefinitionPtr def = + CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("dhcp6", 10000); + ASSERT_TRUE(def); + + // Verify that the option definition is correct. + EXPECT_EQ("foo", def->getName()); + EXPECT_EQ(10000, def->getCode()); + EXPECT_FALSE(def->getArrayType()); + EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType()); + EXPECT_TRUE(def->getEncapsulatedSpace().empty()); +} + +/// @brief Check basic parsing of options. /// /// Note that this tests basic operation of the OptionDataListParser and /// OptionDataParser. It uses a simple configuration consisting of one @@ -511,10 +577,7 @@ TEST_F(ParseConfigTest, basicOptionDataTest) { " \"name\": \"foo\"," " \"code\": 100," " \"type\": \"ipv4-address\"," - " \"array\": False," - " \"record-types\": \"\"," - " \"space\": \"isc\"," - " \"encapsulate\": \"\"" + " \"space\": \"isc\"" " } ], " " \"option-data\": [ {" " \"name\": \"foo\"," @@ -527,13 +590,47 @@ TEST_F(ParseConfigTest, basicOptionDataTest) { // Verify that the configuration string parses. int rcode = parseConfiguration(config); - ASSERT_TRUE(rcode == 0); + ASSERT_EQ(0, rcode); // Verify that the option can be retrieved. OptionPtr opt_ptr = getOptionPtr("isc", 100); ASSERT_TRUE(opt_ptr); - // Verify that the option definition is correct. + // Verify that the option data is correct. + std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)"; + + EXPECT_EQ(val, opt_ptr->toText()); +} + +/// @brief Check minimal parsing of options. +/// +/// Same than basic but without optional parameters set to their default. +TEST_F(ParseConfigTest, minimalOptionDataTest) { + + // Configuration string. + std::string config = + "{ \"option-def\": [ {" + " \"name\": \"foo\"," + " \"code\": 100," + " \"type\": \"ipv4-address\"," + " \"space\": \"isc\"" + " } ], " + " \"option-data\": [ {" + " \"name\": \"foo\"," + " \"space\": \"isc\"," + " \"data\": \"192.0.2.0\"" + " } ]" + "}"; + + // Verify that the configuration string parses. + int rcode = parseConfiguration(config); + ASSERT_EQ(0, rcode); + + // Verify that the option can be retrieved. + OptionPtr opt_ptr = getOptionPtr("isc", 100); + ASSERT_TRUE(opt_ptr); + + // Verify that the option data is correct. std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)"; EXPECT_EQ(val, opt_ptr->toText()); @@ -690,7 +787,6 @@ TEST_F(ParseConfigTest, optionDataNoName) { "{ \"option-data\": [ {" " \"space\": \"dhcp6\"," " \"code\": 23," - " \"csv-format\": True," " \"data\": \"2001:db8:1::1\"" " } ]" "}"; @@ -711,7 +807,6 @@ TEST_F(ParseConfigTest, optionDataNoCode) { "{ \"option-data\": [ {" " \"space\": \"dhcp6\"," " \"name\": \"dns-servers\"," - " \"csv-format\": True," " \"data\": \"2001:db8:1::1\"" " } ]" "}"; @@ -772,9 +867,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) { " \"code\": 2345," " \"type\": \"ipv6-address\"," " \"array\": True," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp6\"" " } ]," " \"option-data\": [ {" " \"name\": \"foo-name\"," @@ -800,9 +893,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) { " \"code\": 2345," " \"type\": \"ipv6-address\"," " \"array\": True," - " \"record-types\": \"\"," - " \"space\": \"dhcp6\"," - " \"encapsulate\": \"\"" + " \"space\": \"dhcp6\"" " } ]," " \"option-data\": [ {" " \"code\": 2345," diff --git a/src/lib/eval/Makefile.am b/src/lib/eval/Makefile.am index 59f9e77c22..0f204f181c 100644 --- a/src/lib/eval/Makefile.am +++ b/src/lib/eval/Makefile.am @@ -12,18 +12,25 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) lib_LTLIBRARIES = libkea-eval.la libkea_eval_la_SOURCES = +libkea_eval_la_SOURCES += eval_log.cc eval_log.h libkea_eval_la_SOURCES += token.cc token.h libkea_eval_la_SOURCES += parser.cc parser.h libkea_eval_la_SOURCES += lexer.cc libkea_eval_la_SOURCES += eval_context.cc +nodist_libkea_eval_la_SOURCES = eval_messages.h eval_messages.cc + libkea_eval_la_CXXFLAGS = $(AM_CXXFLAGS) libkea_eval_la_CPPFLAGS = $(AM_CPPFLAGS) libkea_eval_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la libkea_eval_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libkea_eval_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libkea_eval_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_eval_la_LIBADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) + libkea_eval_la_LDFLAGS = -no-undefined -version-info 3:0:0 -libkea_eval_la_LDFLAGS += $(LOG4CPLUS_LIBS) $(CRYPTO_LDFLAGS) +libkea_eval_la_LDFLAGS += $(CRYPTO_LDFLAGS) EXTRA_DIST = eval.dox EXTRA_DIST += eval_messages.mes @@ -33,6 +40,7 @@ eval_messages.h eval_messages.cc: s-messages s-messages: eval_messages.mes $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/eval/eval_messages.mes + touch $@ # Tell Automake that the eval_messages.{cc,h} source files are created in the # build process, so it must create these before doing anything else. Although @@ -43,7 +51,7 @@ s-messages: eval_messages.mes # first. BUILT_SOURCES = eval_messages.h eval_messages.cc -CLEANFILES = eval_messages.h eval_messages.cc +CLEANFILES = eval_messages.h eval_messages.cc s-messages # If we want to get rid of all flex/bison generated files, we need to use # make maintainer-clean. The proper way to introduce custom commands for diff --git a/src/lib/eval/eval_log.cc b/src/lib/eval/eval_log.cc new file mode 100644 index 0000000000..35128f6ae5 --- /dev/null +++ b/src/lib/eval/eval_log.cc @@ -0,0 +1,26 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +/// Defines the logger used by the Eval (classification) code + +#include + +namespace isc { +namespace dhcp { + +isc::log::Logger eval_logger("eval"); + +} // namespace dhcp +} // namespace isc + diff --git a/src/lib/eval/eval_log.h b/src/lib/eval/eval_log.h new file mode 100644 index 0000000000..fb39198d25 --- /dev/null +++ b/src/lib/eval/eval_log.h @@ -0,0 +1,49 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef EVAL_LOG_H +#define EVAL_LOG_H + +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Eval debug Logging levels +/// +/// Defines the levels used to output debug messages in the eval (classification) code. +/// Note that higher numbers equate to more verbose (and detailed) output. + +// The first level traces normal operations, +const int EVAL_DBG_TRACE = DBGLVL_TRACE_BASIC; + +// The next level traces each call to hook code. +const int EVAL_DBG_CALLS = DBGLVL_TRACE_BASIC_DATA; + +// Additional information on the calls. Report each call to a callout (even +// if there are multiple callouts on a hook) and each status return. +const int EVAL_DBG_EXTENDED_CALLS = DBGLVL_TRACE_DETAIL_DATA; + +/// @brief Eval Logger +/// +/// Define the logger used to log messages. We could define it in multiple +/// modules, but defining in a single module and linking to it saves time and +/// space. +extern isc::log::Logger eval_logger; + +} // namespace dhcp +} // namespace isc + +#endif // EVAL_LOG_H diff --git a/src/lib/eval/eval_messages.mes b/src/lib/eval/eval_messages.mes index cca2b0e19e..3d86f758b4 100644 --- a/src/lib/eval/eval_messages.mes +++ b/src/lib/eval/eval_messages.mes @@ -18,3 +18,8 @@ $NAMESPACE isc::dhcp This debug message indicates that the expression has been evaluated to said value. This message is mostly useful during debugging of the client classification expressions. + +% EVAL_SUBSTRING_BAD_PARAM_CONVERSION starting %1, length %2 +This debug message indicates that the parameter for the starting postion +or length of the substring couldn't be converted to an integer. In this +case the substring routine returns an empty string. diff --git a/src/lib/eval/tests/Makefile.am b/src/lib/eval/tests/Makefile.am index dbf6ce35ee..1dc5b350d2 100644 --- a/src/lib/eval/tests/Makefile.am +++ b/src/lib/eval/tests/Makefile.am @@ -2,6 +2,8 @@ SUBDIRS = . AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DLOGGING_SPEC_FILE=\"$(abs_top_srcdir)/src/lib/dhcpsrv/logging.spec\" + AM_CXXFLAGS = $(KEA_CXXFLAGS) # Some versions of GCC warn about some versions of Boost regarding @@ -24,8 +26,9 @@ if HAVE_GTEST TESTS += libeval_unittests -libeval_unittests_SOURCES = token_unittest.cc main.cc -libeval_unittests_SOURCES += context_unittest.cc +libeval_unittests_SOURCES = context_unittest.cc +libeval_unittests_SOURCES += token_unittest.cc +libeval_unittests_SOURCES += run_unittests.cc libeval_unittests_CXXFLAGS = $(AM_CXXFLAGS) libeval_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) libeval_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/eval/tests/main.cc b/src/lib/eval/tests/run_unittests.cc similarity index 100% rename from src/lib/eval/tests/main.cc rename to src/lib/eval/tests/run_unittests.cc diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index 6b29763c62..7f2d653f68 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -24,6 +24,8 @@ #include #include +#include + using namespace std; using namespace isc::dhcp; @@ -60,6 +62,40 @@ public: OptionPtr option_str4_; ///< A string option for DHCPv4 OptionPtr option_str6_; ///< A string option for DHCPv6 + + /// @brief Verify that the substring eval works properly + /// + /// This function takes the parameters and sets up the value + /// stack then executes the eval and checks the results. + /// + /// @param test_string The string to operate on + /// @param test_start The postion to start when getting a substring + /// @param test_length The length of the substring to get + /// @param result_string The expected result of the eval + void verifySubstringEval(const std::string& test_string, + const std::string& test_start, + const std::string& test_length, + const std::string& result_string) { + + // create the token + ASSERT_NO_THROW(t_.reset(new TokenSubstring())); + + // push values on stack + values_.push(test_string); + values_.push(test_start); + values_.push(test_length); + + // evaluate the token + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // verify results + ASSERT_EQ(1, values_.size()); + EXPECT_EQ(result_string, values_.top()); + + // remove result + values_.pop(); + } + /// @todo: Add more option types here }; @@ -93,6 +129,116 @@ TEST_F(TokenTest, string6) { EXPECT_EQ("foo", values_.top()); } +// This simple test checks that a TokenHexString, representing a constant +// string coded in hexadecimal, can be used in Pkt4 evaluation. +// (The actual packet is not used) +TEST_F(TokenTest, hexstring4) { + TokenPtr empty; + TokenPtr bad; + TokenPtr nodigit; + TokenPtr baddigit; + TokenPtr bell; + TokenPtr foo; + TokenPtr cookie; + + // Store constant empty hexstring "" ("") in the TokenHexString object. + ASSERT_NO_THROW(empty.reset(new TokenHexString(""))); + // Store bad encoded hexstring "0abc" (""). + ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc"))); + // Store hexstring with no digits "0x" (""). + ASSERT_NO_THROW(nodigit.reset(new TokenHexString("0x"))); + // Store hexstring with a bad hexdigit "0xxabc" (""). + ASSERT_NO_THROW(baddigit.reset(new TokenHexString("0xxabc"))); + // Store hexstring with an odd number of hexdigits "0x7" ("\a"). + ASSERT_NO_THROW(bell.reset(new TokenHexString("0x7"))); + // Store constant hexstring "0x666f6f" ("foo"). + ASSERT_NO_THROW(foo.reset(new TokenHexString("0x666f6f"))); + // Store constant hexstring "0x63825363" (DHCP_OPTIONS_COOKIE). + ASSERT_NO_THROW(cookie.reset(new TokenHexString("0x63825363"))); + + // Make sure that tokens can be evaluated without exceptions. + ASSERT_NO_THROW(empty->evaluate(*pkt4_, values_)); + ASSERT_NO_THROW(bad->evaluate(*pkt4_, values_)); + ASSERT_NO_THROW(nodigit->evaluate(*pkt4_, values_)); + ASSERT_NO_THROW(baddigit->evaluate(*pkt4_, values_)); + ASSERT_NO_THROW(bell->evaluate(*pkt4_, values_)); + ASSERT_NO_THROW(foo->evaluate(*pkt4_, values_)); + ASSERT_NO_THROW(cookie->evaluate(*pkt4_, values_)); + + // Check that the evaluation put its value on the values stack. + ASSERT_EQ(7, values_.size()); + uint32_t expected = htonl(DHCP_OPTIONS_COOKIE); + EXPECT_EQ(4, values_.top().size()); + EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4)); + values_.pop(); + EXPECT_EQ("foo", values_.top()); + values_.pop(); + EXPECT_EQ("\a", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); +} + +// This simple test checks that a TokenHexString, representing a constant +// string coded in hexadecimal, can be used in Pkt6 evaluation. +// (The actual packet is not used) +TEST_F(TokenTest, hexstring6) { + TokenPtr empty; + TokenPtr bad; + TokenPtr nodigit; + TokenPtr baddigit; + TokenPtr bell; + TokenPtr foo; + TokenPtr cookie; + + // Store constant empty hexstring "" ("") in the TokenHexString object. + ASSERT_NO_THROW(empty.reset(new TokenHexString(""))); + // Store bad encoded hexstring "0abc" (""). + ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc"))); + // Store hexstring with no digits "0x" (""). + ASSERT_NO_THROW(nodigit.reset(new TokenHexString("0x"))); + // Store hexstring with a bad hexdigit "0xxabc" (""). + ASSERT_NO_THROW(baddigit.reset(new TokenHexString("0xxabc"))); + // Store hexstring with an odd number of hexdigits "0x7" ("\a"). + ASSERT_NO_THROW(bell.reset(new TokenHexString("0x7"))); + // Store constant hexstring "0x666f6f" ("foo"). + ASSERT_NO_THROW(foo.reset(new TokenHexString("0x666f6f"))); + // Store constant hexstring "0x63825363" (DHCP_OPTIONS_COOKIE). + ASSERT_NO_THROW(cookie.reset(new TokenHexString("0x63825363"))); + + // Make sure that tokens can be evaluated without exceptions. + ASSERT_NO_THROW(empty->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(bad->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(nodigit->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(baddigit->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(bell->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(foo->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(cookie->evaluate(*pkt6_, values_)); + + // Check that the evaluation put its value on the values stack. + ASSERT_EQ(7, values_.size()); + uint32_t expected = htonl(DHCP_OPTIONS_COOKIE); + EXPECT_EQ(4, values_.top().size()); + EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4)); + values_.pop(); + EXPECT_EQ("foo", values_.top()); + values_.pop(); + EXPECT_EQ("\a", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); + values_.pop(); + EXPECT_EQ("", values_.top()); +} + // This test checks if a token representing an option value is able to extract // the option from an IPv4 packet and properly store the option's value. TEST_F(TokenTest, optionString4) { @@ -197,3 +343,177 @@ TEST_F(TokenTest, optionEqualTrue) { } }; + +// This test checks if an a token representing a substring request +// throws an exception if there aren't enough values on the stack. +// The stack from the top is: length, start, string. +// The actual packet is not used. +TEST_F(TokenTest, substringNotEnoughValues) { + ASSERT_NO_THROW(t_.reset(new TokenSubstring())); + + // Subsring requires three values on the stack, try + // with 0, 1 and 2 all should thorw an exception + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + values_.push(""); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + values_.push("0"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + // Three should work + values_.push("0"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // As we had an empty string to start with we should have an empty + // one after the evaluate + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("", values_.top()); +} + +// Test getting the whole string in different ways +TEST_F(TokenTest, substringWholeString) { + // Get the whole string + verifySubstringEval("foobar", "0", "6", "foobar"); + + // Get the whole string with "all" + verifySubstringEval("foobar", "0", "all", "foobar"); + + // Get the whole string with an extra long number + verifySubstringEval("foobar", "0", "123456", "foobar"); + + // Get the whole string counting from the back + verifySubstringEval("foobar", "-6", "all", "foobar"); +} + +// Test getting a suffix, in this case the last 3 characters +TEST_F(TokenTest, substringTrailer) { + verifySubstringEval("foobar", "3", "3", "bar"); + verifySubstringEval("foobar", "3", "all", "bar"); + verifySubstringEval("foobar", "-3", "all", "bar"); + verifySubstringEval("foobar", "-3", "123", "bar"); +} + +// Test getting the middle of the string in different ways +TEST_F(TokenTest, substringMiddle) { + verifySubstringEval("foobar", "1", "4", "ooba"); + verifySubstringEval("foobar", "-5", "4", "ooba"); + verifySubstringEval("foobar", "-1", "-4", "ooba"); + verifySubstringEval("foobar", "5", "-4", "ooba"); +} + +// Test getting the last letter in different ways +TEST_F(TokenTest, substringLastLetter) { + verifySubstringEval("foobar", "5", "all", "r"); + verifySubstringEval("foobar", "5", "1", "r"); + verifySubstringEval("foobar", "5", "5", "r"); + verifySubstringEval("foobar", "-1", "all", "r"); + verifySubstringEval("foobar", "-1", "1", "r"); + verifySubstringEval("foobar", "-1", "5", "r"); +} + +// Test we get only what is available if we ask for a longer string +TEST_F(TokenTest, substringLength) { + // Test off the front + verifySubstringEval("foobar", "0", "-4", ""); + verifySubstringEval("foobar", "1", "-4", "f"); + verifySubstringEval("foobar", "2", "-4", "fo"); + verifySubstringEval("foobar", "3", "-4", "foo"); + + // and the back + verifySubstringEval("foobar", "3", "4", "bar"); + verifySubstringEval("foobar", "4", "4", "ar"); + verifySubstringEval("foobar", "5", "4", "r"); + verifySubstringEval("foobar", "6", "4", ""); +} + +// Test that we get nothing if the starting postion is out of the string +TEST_F(TokenTest, substringStartingPosition) { + // Off the front + verifySubstringEval("foobar", "-7", "1", ""); + verifySubstringEval("foobar", "-7", "-11", ""); + verifySubstringEval("foobar", "-7", "all", ""); + + // and the back + verifySubstringEval("foobar", "6", "1", ""); + verifySubstringEval("foobar", "6", "-11", ""); + verifySubstringEval("foobar", "6", "all", ""); +} + +// Check what happens if we use strings that aren't numbers for start or length +// We should return the empty string +TEST_F(TokenTest, substringBadParams) { + verifySubstringEval("foobar", "0ick", "all", ""); + verifySubstringEval("foobar", "ick0", "all", ""); + verifySubstringEval("foobar", "ick", "all", ""); + verifySubstringEval("foobar", "0", "ick", ""); + verifySubstringEval("foobar", "0", "0ick", ""); + verifySubstringEval("foobar", "0", "ick0", ""); + verifySubstringEval("foobar", "0", "allaboard", ""); +} + +// lastly check that we don't get anything if the string is empty or +// we don't ask for any characters from it. +TEST_F(TokenTest, substringReturnEmpty) { + verifySubstringEval("", "0", "all", ""); + verifySubstringEval("foobar", "0", "0", ""); +} + +// Check if we can use the substring and equal tokens together +// We put the result on the stack first then the substring values +// then evaluate the substring which should leave the original +// result on the bottom with the substring result on next. +// Evaulating the equals should produce true for the first +// and false for the second. +// throws an exception if there aren't enough values on the stack. +// The stack from the top is: length, start, string. +// The actual packet is not used. +TEST_F(TokenTest, substringEquals) { + TokenPtr tequal; + + ASSERT_NO_THROW(t_.reset(new TokenSubstring())); + ASSERT_NO_THROW(tequal.reset(new TokenEqual())); + + // The final expected value + values_.push("ooba"); + + // The substring values + // Subsring requires three values on the stack, try + // with 0, 1 and 2 all should thorw an exception + values_.push("foobar"); + values_.push("1"); + values_.push("4"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // we should have two values on the stack + ASSERT_EQ(2, values_.size()); + + // next the equals eval + EXPECT_NO_THROW(tequal->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("true", values_.top()); + + // get rid of the result + values_.pop(); + + // and try it again but with a bad final value + // The final expected value + values_.push("foob"); + + // The substring values + // Subsring requires three values on the stack, try + // with 0, 1 and 2 all should thorw an exception + values_.push("foobar"); + values_.push("1"); + values_.push("4"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // we should have two values on the stack + ASSERT_EQ(2, values_.size()); + + // next the equals eval + EXPECT_NO_THROW(tequal->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("false", values_.top()); + +} diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index 66ebf1c5b1..ea01f11a76 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -13,6 +13,10 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include +#include +#include +#include #include using namespace isc::dhcp; @@ -24,6 +28,40 @@ TokenString::evaluate(const Pkt& /*pkt*/, ValueStack& values) { values.push(value_); } +TokenHexString::TokenHexString(const string& str) : value_("") { + // Check string starts "0x" or "0x" and has at least one additional character. + if ((str.size() < 3) || + (str[0] != '0') || + ((str[1] != 'x') && (str[1] != 'X'))) { + return; + } + string digits = str.substr(2); + + // Transform string of hexadecimal digits into binary format + vector binary; + try { + // The decodeHex function expects that the string contains an + // even number of digits. If we don't meet this requirement, + // we have to insert a leading 0. + if ((digits.length() % 2) != 0) { + digits = digits.insert(0, "0"); + } + util::encode::decodeHex(digits, binary); + } catch (...) { + return; + } + + // Convert to a string (note that binary.size() cannot be 0) + value_.resize(binary.size()); + memmove(&value_[0], &binary[0], binary.size()); +} + +void +TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) { + // Literals only push, nothing to pop + values.push(value_); +} + void TokenOption::evaluate(const Pkt& pkt, ValueStack& values) { OptionPtr opt = pkt.getOption(option_code_); @@ -53,3 +91,76 @@ TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) { else values.push("false"); } + +void +TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) { + + if (values.size() < 3) { + isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " + "3 values for substring operator, got " << values.size()); + } + + string len_str = values.top(); + values.pop(); + string start_str = values.top(); + values.pop(); + string string_str = values.top(); + values.pop(); + + // If we have no string to start with we push an empty string and leave + if (string_str.empty()) { + values.push(""); + return; + } + + // Convert the starting position and length from strings to numbers + // the length may also be "all" in which case simply make it the + // length of the string. + // If we have a problem push an empty string and leave + int start_pos; + int length; + try { + start_pos = boost::lexical_cast(start_str); + if (len_str == "all") { + length = string_str.length(); + } else { + length = boost::lexical_cast(len_str); + } + } catch (const boost::bad_lexical_cast&) { + LOG_DEBUG(eval_logger, EVAL_DBG_TRACE, + EVAL_SUBSTRING_BAD_PARAM_CONVERSION) + .arg(start_str) + .arg(len_str); + + values.push(""); + return; + } + + const int string_length = string_str.length(); + // If the starting postion is outside of the string push an + // empty string and leave + if ((start_pos < -string_length) || (start_pos >= string_length)) { + values.push(""); + return; + } + + // Adjust the values to be something for substr. We first figure out + // the starting postion, then update it and the length to get the + // characters before or after it depending on the sign of length + if (start_pos < 0) { + start_pos = string_length + start_pos; + } + + if (length < 0) { + length = -length; + if (length <= start_pos){ + start_pos -= length; + } else { + length = start_pos; + start_pos = 0; + } + } + + // and finally get the substring + values.push(string_str.substr(start_pos, length)); +} diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index 865d290e20..4b264c2655 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -56,7 +56,7 @@ public: /// - option[123] (a token that extracts value of option 123) /// - == (an operator that compares two other tokens) /// - substring(a,b,c) (an operator that takes three arguments: a string, -/// first and last character) +/// first character and length) class Token { public: @@ -101,6 +101,31 @@ protected: std::string value_; ///< Constant value }; +/// @brief Token representing a constant string in hexadecimal format +/// +/// This token holds value of a constant string giving in an hexadecimal +/// format, for instance 0x666f6f is "foo" +class TokenHexString : public Token { +public: + /// Value is set during token construction. + /// + /// @param str constant string to be represented + /// (must be "0x" or "0X" followed by a string of hexadecimal digits + /// or decoding will fail) + TokenHexString(const std::string& str); + + /// @brief Token evaluation (puts value of the constant string on + /// the stack after decoding or an empty string if decoding fails + /// (note it should not if the parser is correct) + /// + /// @param pkt (ignored) + /// @param values (represented string will be pushed here) + void evaluate(const Pkt& pkt, ValueStack& values); + +protected: + std::string value_; ///< Constant value +}; + /// @brief Token that represents a value of an option /// /// This represents a reference to a given option, e.g. in the expression @@ -160,10 +185,65 @@ public: /// either "true" or "false". It requires at least two parameters to be /// present on stack. /// - /// @throw EvalBadStack if there's less than 2 values on stack + /// @throw EvalBadStack if there are less than 2 values on stack /// - /// @brief pkt (unused) - /// @brief values - stack of values (2 arguments will be poped, 1 result + /// @param pkt (unused) + /// @param values - stack of values (2 arguments will be popped, 1 result + /// will be pushed) + void evaluate(const Pkt& pkt, ValueStack& values); +}; + +/// @brief Token that represents the substring operator (returns a portion +/// of the supplied string) +/// +/// This token represents substring(str, start, len) An operator that takes three +/// arguments: a string, the first character and the length. +class TokenSubstring : public Token { +public: + /// @brief Constructor (does nothing) + TokenSubstring() {} + + /// @brief Extract a substring from a string + /// + /// Evaluation does not use packet information. It requires at least + /// three values to be present on the stack. It will consume the top + /// three values on the stack as parameters and push the resulting substring + /// onto the stack. From the top it expects the values on the stack as: + /// - len + /// - start + /// - str + /// + /// str is the string to extract a substring from. If it is empty, an empty + /// string is pushed onto the value stack. + /// + /// start is the postion from which the code starts extracting the substring. + /// 0 is the first character and a negative number starts from the end, with + /// -1 being the last character. If the starting point is outside of the + /// original string an empty string is pushed onto the value stack. + /// + /// length is the number of characters from the string to extract. + /// "all" means all remaining characters from start to the end of string. + /// A negative number means to go from start towards the beginning of + /// the string, but doesn't include start. + /// If length is longer than the remaining portion of string + /// then the entire remaining portion is placed on the value stack. + /// + /// The following examples all use the base string "foobar", the first number + /// is the starting position and the second is the length. Note that + /// a negative length only selects which characters to extract it does not + /// indicate an attempt to reverse the string. + /// - 0, all => "foobar" + /// - 0, 6 => "foobar" + /// - 0, 4 => "foob" + /// - 2, all => "obar" + /// - 2, 6 => "obar" + /// - -1, all => "r" + /// - -1, -4 => "ooba" + /// + /// @throw EvalBadStack if there are less than 3 values on stack + /// + /// @param pkt (unused) + /// @param values - stack of values (3 arguments will be popped, 1 result /// will be pushed) void evaluate(const Pkt& pkt, ValueStack& values); };