From: Marcin Siodelski Date: Mon, 23 Nov 2015 21:01:47 +0000 (+0100) Subject: [4204] Eval parser allows for referencing option by its name. X-Git-Tag: trac4204fd_rebased^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=77e78881aa8da9542e5a01c9715702610e2bd2dc;p=thirdparty%2Fkea.git [4204] Eval parser allows for referencing option by its name. --- diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 60ef9f6f82..e10acfd0f6 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -248,7 +248,6 @@ LibDHCP::clearRuntimeOptionDefs() { runtime_option_defs_.clearItems(); } - bool LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) { if (u == Option::V6) { diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index ec346d8fb4..9bc344aa16 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -121,7 +121,6 @@ public: static OptionDefContainerPtr getRuntimeOptionDefs(const std::string& space); - /// @brief Check if the specified option is a standard option. /// /// @param u universe (V4 or V6) diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc index 96f10cdc29..e3deeccfaf 100644 --- a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc +++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc @@ -50,7 +50,7 @@ ExpressionParser::build(ConstElementPtr expression_cfg) { std::string value; expression_cfg->getValue(value); try { - EvalContext eval_ctx; + EvalContext eval_ctx(global_context_->universe_); eval_ctx.parseString(value); local_expression_.reset(new Expression()); *local_expression_ = eval_ctx.expression; diff --git a/src/lib/eval/eval_context.cc b/src/lib/eval/eval_context.cc index 7dc46fc4cc..ac760860f5 100644 --- a/src/lib/eval/eval_context.cc +++ b/src/lib/eval/eval_context.cc @@ -15,10 +15,12 @@ #include #include #include +#include #include -EvalContext::EvalContext() - : trace_scanning_(false), trace_parsing_(false) +EvalContext::EvalContext(const Option::Universe& option_universe) + : trace_scanning_(false), trace_parsing_(false), + option_universe_(option_universe) { } @@ -32,7 +34,7 @@ EvalContext::parseString(const std::string& str) file_ = ""; string_ = str; scanStringBegin(); - isc::eval::EvalParser parser(*this); + isc::eval::EvalParser parser(*this, option_universe_); parser.set_debug_level(trace_parsing_); int res = parser.parse(); scanStringEnd(); diff --git a/src/lib/eval/eval_context.h b/src/lib/eval/eval_context.h index 6b28e2b161..1e7b9b9e30 100644 --- a/src/lib/eval/eval_context.h +++ b/src/lib/eval/eval_context.h @@ -42,7 +42,11 @@ class EvalContext { public: /// @brief Default constructor. - EvalContext(); + /// + /// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used + /// by the parser to determine which option definitions set should be used + /// to map option names to option codes. + EvalContext(const Option::Universe& option_universe); /// @brief destructor virtual ~EvalContext(); @@ -55,7 +59,7 @@ public: /// @brief Method called after the last tokens are scanned from a string. void scanStringEnd(); - + /// @brief Run the parser on the string specified. /// /// @param str string to be written @@ -87,7 +91,12 @@ public: /// @brief Flag determing parser debugging. bool trace_parsing_; - + + /// @brief Option universe: DHCPv4 or DHCPv6. + /// + /// This is used by the parser to determine which option definitions + /// set should be used to map option name to option code. + Option::Universe option_universe_; }; }; // end of isc::eval namespace diff --git a/src/lib/eval/parser.cc b/src/lib/eval/parser.cc index 137c6d6fa2..d562990f23 100644 --- a/src/lib/eval/parser.cc +++ b/src/lib/eval/parser.cc @@ -49,10 +49,10 @@ #line 51 "parser.cc" // lalr1.cc:412 // Unqualified %code blocks. -#line 39 "parser.yy" // lalr1.cc:413 +#line 40 "parser.yy" // lalr1.cc:413 # include "eval_context.h" -#line 67 "parser.yy" // lalr1.cc:413 +#line 72 "parser.yy" // lalr1.cc:413 namespace { @@ -206,13 +206,14 @@ namespace isc { namespace eval { /// Build a parser object. - EvalParser::EvalParser (EvalContext& ctx_yyarg) + EvalParser::EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg) : #if YYDEBUG yydebug_ (false), yycdebug_ (&std::cerr), #endif - ctx (ctx_yyarg) + ctx (ctx_yyarg), + option_universe (option_universe_yyarg) {} EvalParser::~EvalParser () @@ -344,30 +345,30 @@ namespace isc { namespace eval { { case 15: // "constant string" -#line 64 "parser.yy" // lalr1.cc:636 +#line 69 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 350 "parser.cc" // lalr1.cc:636 +#line 351 "parser.cc" // lalr1.cc:636 break; case 16: // "integer" -#line 64 "parser.yy" // lalr1.cc:636 +#line 69 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 357 "parser.cc" // lalr1.cc:636 +#line 358 "parser.cc" // lalr1.cc:636 break; case 17: // "constant hexstring" -#line 64 "parser.yy" // lalr1.cc:636 +#line 69 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 364 "parser.cc" // lalr1.cc:636 +#line 365 "parser.cc" // lalr1.cc:636 break; case 18: // TOKEN -#line 64 "parser.yy" // lalr1.cc:636 +#line 69 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 371 "parser.cc" // lalr1.cc:636 +#line 372 "parser.cc" // lalr1.cc:636 break; @@ -592,90 +593,124 @@ namespace isc { namespace eval { switch (yyn) { case 3: -#line 105 "parser.yy" // lalr1.cc:859 +#line 110 "parser.yy" // lalr1.cc:859 { TokenPtr eq(new TokenEqual()); ctx.expression.push_back(eq); } -#line 601 "parser.cc" // lalr1.cc:859 +#line 602 "parser.cc" // lalr1.cc:859 break; case 4: -#line 112 "parser.yy" // lalr1.cc:859 +#line 117 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 610 "parser.cc" // lalr1.cc:859 +#line 611 "parser.cc" // lalr1.cc:859 break; case 5: -#line 117 "parser.yy" // lalr1.cc:859 +#line 122 "parser.yy" // lalr1.cc:859 { TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(hex); } -#line 619 "parser.cc" // lalr1.cc:859 +#line 620 "parser.cc" // lalr1.cc:859 break; case 6: -#line 122 "parser.yy" // lalr1.cc:859 +#line 127 "parser.yy" // lalr1.cc:859 { uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx); TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL)); ctx.expression.push_back(opt); } -#line 629 "parser.cc" // lalr1.cc:859 +#line 630 "parser.cc" // lalr1.cc:859 break; case 7: -#line 128 "parser.yy" // lalr1.cc:859 +#line 133 "parser.yy" // lalr1.cc:859 { uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx); TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL)); ctx.expression.push_back(opt); } -#line 639 "parser.cc" // lalr1.cc:859 +#line 640 "parser.cc" // lalr1.cc:859 break; case 8: -#line 134 "parser.yy" // lalr1.cc:859 +#line 139 "parser.yy" // lalr1.cc:859 + { + try { + // This may result in exception if the specified + // name is unknown. + TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe, + TokenOption::TEXTUAL)); + ctx.expression.push_back(opt); + + } catch (const std::exception& ex) { + ctx.error(yystack_[3].location, ex.what()); + } + } +#line 657 "parser.cc" // lalr1.cc:859 + break; + + case 9: +#line 152 "parser.yy" // lalr1.cc:859 + { + try { + // This may result in exception if the specified + // name is unknown. + TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe, + TokenOption::HEXADECIMAL)); + ctx.expression.push_back(opt); + + } catch (const std::exception& ex) { + ctx.error(yystack_[3].location, ex.what()); + } + } +#line 674 "parser.cc" // lalr1.cc:859 + break; + + case 10: +#line 165 "parser.yy" // lalr1.cc:859 { TokenPtr sub(new TokenSubstring()); ctx.expression.push_back(sub); } -#line 648 "parser.cc" // lalr1.cc:859 +#line 683 "parser.cc" // lalr1.cc:859 break; - case 10: -#line 143 "parser.yy" // lalr1.cc:859 + case 12: +#line 174 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 657 "parser.cc" // lalr1.cc:859 +#line 692 "parser.cc" // lalr1.cc:859 break; - case 11: -#line 150 "parser.yy" // lalr1.cc:859 + case 13: +#line 181 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 666 "parser.cc" // lalr1.cc:859 +#line 701 "parser.cc" // lalr1.cc:859 break; - case 12: -#line 155 "parser.yy" // lalr1.cc:859 + case 14: +#line 186 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString("all")); ctx.expression.push_back(str); } -#line 675 "parser.cc" // lalr1.cc:859 +#line 710 "parser.cc" // lalr1.cc:859 break; -#line 679 "parser.cc" // lalr1.cc:859 +#line 714 "parser.cc" // lalr1.cc:859 default: break; } @@ -937,17 +972,19 @@ namespace isc { namespace eval { const signed char EvalParser::yypact_[] = { - -4, -9, -3, -10, -10, -10, 9, -10, 12, 1, - -4, -10, -4, -2, 6, -10, 10, 2, 0, -10, - 11, -10, -10, -6, -10, -10, 8, -10 + -4, -1, 6, -10, -10, -10, 4, -10, 15, -9, + -4, -10, -4, 5, 7, 10, -10, 13, 14, 8, + 2, 9, -10, 16, -10, -10, -10, -10, -6, -10, + -10, 17, -10 }; const unsigned char EvalParser::yydefact_[] = { - 0, 0, 0, 4, 5, 9, 0, 2, 0, 0, - 0, 1, 0, 0, 0, 3, 0, 0, 0, 10, - 0, 6, 7, 0, 12, 11, 0, 8 + 0, 0, 0, 4, 5, 11, 0, 2, 0, 0, + 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, + 0, 0, 12, 0, 8, 9, 6, 7, 0, 14, + 13, 0, 10 }; const signed char @@ -959,45 +996,46 @@ namespace isc { namespace eval { const signed char EvalParser::yydefgoto_[] = { - -1, 6, 7, 8, 20, 26 + -1, 6, 7, 8, 23, 31 }; const unsigned char EvalParser::yytable_[] = { - 1, 2, 24, 14, 9, 15, 21, 22, 10, 11, - 25, 3, 16, 4, 5, 12, 17, 13, 19, 18, - 27, 23 + 1, 2, 29, 15, 11, 16, 13, 14, 24, 25, + 30, 3, 9, 4, 5, 26, 27, 10, 12, 17, + 19, 18, 20, 21, 22, 0, 28, 0, 0, 32 }; - const unsigned char + const signed char EvalParser::yycheck_[] = { - 4, 5, 8, 10, 13, 12, 6, 7, 11, 0, - 16, 15, 14, 17, 18, 3, 10, 16, 16, 9, - 12, 10 + 4, 5, 8, 10, 0, 12, 15, 16, 6, 7, + 16, 15, 13, 17, 18, 6, 7, 11, 3, 14, + 10, 14, 9, 9, 16, -1, 10, -1, -1, 12 }; const unsigned char EvalParser::yystos_[] = { 0, 4, 5, 15, 17, 18, 20, 21, 22, 13, - 11, 0, 3, 16, 22, 22, 14, 10, 9, 16, - 23, 6, 7, 10, 8, 16, 24, 12 + 11, 0, 3, 15, 16, 22, 22, 14, 14, 10, + 9, 9, 16, 23, 6, 7, 6, 7, 10, 8, + 16, 24, 12 }; const unsigned char EvalParser::yyr1_[] = { 0, 19, 20, 21, 22, 22, 22, 22, 22, 22, - 23, 24, 24 + 22, 22, 23, 24, 24 }; const unsigned char EvalParser::yyr2_[] = { - 0, 2, 1, 3, 1, 1, 6, 6, 8, 1, - 1, 1, 1 + 0, 2, 1, 3, 1, 1, 6, 6, 6, 6, + 8, 1, 1, 1, 1 }; @@ -1018,8 +1056,8 @@ namespace isc { namespace eval { const unsigned char EvalParser::yyrline_[] = { - 0, 101, 101, 104, 111, 116, 121, 127, 133, 138, - 142, 149, 154 + 0, 106, 106, 109, 116, 121, 126, 132, 138, 151, + 164, 169, 173, 180, 185 }; // Print the state stack on the debug stream. @@ -1054,8 +1092,8 @@ namespace isc { namespace eval { #line 21 "parser.yy" // lalr1.cc:1167 } } // isc::eval -#line 1058 "parser.cc" // lalr1.cc:1167 -#line 161 "parser.yy" // lalr1.cc:1168 +#line 1096 "parser.cc" // lalr1.cc:1167 +#line 192 "parser.yy" // lalr1.cc:1168 void isc::eval::EvalParser::error(const location_type& loc, diff --git a/src/lib/eval/parser.h b/src/lib/eval/parser.h index 73467d5cf2..023e861dd7 100644 --- a/src/lib/eval/parser.h +++ b/src/lib/eval/parser.h @@ -45,12 +45,13 @@ #include #include #include +#include #include using namespace isc::dhcp; using namespace isc::eval; -#line 54 "parser.h" // lalr1.cc:392 +#line 55 "parser.h" // lalr1.cc:392 # include # include // std::abort @@ -127,7 +128,7 @@ using namespace isc::eval; #line 21 "parser.yy" // lalr1.cc:392 namespace isc { namespace eval { -#line 131 "parser.h" // lalr1.cc:392 +#line 132 "parser.h" // lalr1.cc:392 @@ -514,7 +515,7 @@ namespace isc { namespace eval { /// Build a parser object. - EvalParser (EvalContext& ctx_yyarg); + EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg); virtual ~EvalParser (); /// Parse. @@ -597,7 +598,7 @@ namespace isc { namespace eval { // number is the opposite. If YYTABLE_NINF, syntax error. static const unsigned char yytable_[]; - static const unsigned char yycheck_[]; + static const signed char yycheck_[]; // YYSTOS[STATE-NUM] -- The (internal number of the) accessing // symbol of state STATE-NUM. @@ -717,7 +718,7 @@ namespace isc { namespace eval { enum { yyeof_ = 0, - yylast_ = 21, ///< Last index in yytable_. + yylast_ = 29, ///< Last index in yytable_. yynnts_ = 6, ///< Number of nonterminal symbols. yyfinal_ = 11, ///< Termination state number. yyterror_ = 1, @@ -728,6 +729,7 @@ namespace isc { namespace eval { // User arguments. EvalContext& ctx; + const Option::Universe& option_universe; }; // Symbol number corresponding to token number t. @@ -1083,7 +1085,7 @@ namespace isc { namespace eval { #line 21 "parser.yy" // lalr1.cc:392 } } // isc::eval -#line 1087 "parser.h" // lalr1.cc:392 +#line 1089 "parser.h" // lalr1.cc:392 diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy index 3cec6889d4..29d074ebde 100644 --- a/src/lib/eval/parser.yy +++ b/src/lib/eval/parser.yy @@ -25,6 +25,7 @@ #include #include #include +#include #include using namespace isc::dhcp; @@ -39,6 +40,10 @@ using namespace isc::eval; { # include "eval_context.h" } +// Option universe: DHCPv4 or DHCPv6. This is required to use correct option +// definition set to map option names to codes. +%parse-param { const Option::Universe& option_universe } + %define api.token.prefix {TOKEN_} %token END 0 "end of file" @@ -130,6 +135,32 @@ string_expr : STRING TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL)); ctx.expression.push_back(opt); } + | OPTION "[" STRING "]" DOT TEXT + { + try { + // This may result in exception if the specified + // name is unknown. + TokenPtr opt(new TokenOption($3, option_universe, + TokenOption::TEXTUAL)); + ctx.expression.push_back(opt); + + } catch (const std::exception& ex) { + ctx.error(@3, ex.what()); + } + } + | OPTION "[" STRING "]" DOT HEX + { + try { + // This may result in exception if the specified + // name is unknown. + TokenPtr opt(new TokenOption($3, option_universe, + TokenOption::HEXADECIMAL)); + ctx.expression.push_back(opt); + + } catch (const std::exception& ex) { + ctx.error(@3, ex.what()); + } + } | SUBSTRING "(" string_expr "," start_expr "," length_expr ")" { TokenPtr sub(new TokenSubstring()); diff --git a/src/lib/eval/tests/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc index d1770fe39b..901ec6801a 100644 --- a/src/lib/eval/tests/context_unittest.cc +++ b/src/lib/eval/tests/context_unittest.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -94,7 +95,7 @@ public: /// @brief checks if the given expression raises the expected message /// when it is parsed. void checkError(const string& expr, const string& msg) { - EvalContext eval; + EvalContext eval(Option::V4); parsed_ = false; try { parsed_ = eval.parseString(expr); @@ -115,15 +116,15 @@ public: // Test the parsing of a basic expression TEST_F(EvalContextTest, basic) { - EvalContext tmp; + EvalContext eval(Option::V4); - EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123].text == 'MSFT'")); + EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'MSFT'")); EXPECT_TRUE(parsed_); } // Test the parsing of a string terminal TEST_F(EvalContextTest, string) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'")); EXPECT_TRUE(parsed_); @@ -140,7 +141,7 @@ TEST_F(EvalContextTest, string) { // Test the parsing of a basic expression using integers TEST_F(EvalContextTest, integer) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("substring(option[123].text, 0, 2) == '42'")); @@ -149,7 +150,7 @@ TEST_F(EvalContextTest, integer) { // Test the parsing of a hexstring terminal TEST_F(EvalContextTest, hexstring) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'")); EXPECT_TRUE(parsed_); @@ -164,7 +165,7 @@ TEST_F(EvalContextTest, hexstring) { // Test the parsing of a hexstring terminal with an odd number of // hexadecimal digits TEST_F(EvalContextTest, oddHexstring) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'")); EXPECT_TRUE(parsed_); @@ -178,7 +179,7 @@ TEST_F(EvalContextTest, oddHexstring) { // Test the parsing of an equal expression TEST_F(EvalContextTest, equal) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'")); EXPECT_TRUE(parsed_); @@ -196,7 +197,7 @@ TEST_F(EvalContextTest, equal) { // Test the parsing of an option terminal TEST_F(EvalContextTest, option) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'")); EXPECT_TRUE(parsed_); @@ -204,9 +205,20 @@ TEST_F(EvalContextTest, option) { checkTokenOption(eval.expression.at(0), 123); } +// Test parsing of an option identified by name. +TEST_F(EvalContextTest, optionWithName) { + EvalContext eval(Option::V4); + + // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++. + EXPECT_NO_THROW(parsed_ = eval.parseString("option['host-name'].text == 'foo'")); + EXPECT_TRUE(parsed_); + ASSERT_EQ(3, eval.expression.size()); + checkTokenOption(eval.expression.at(0), 12); +} + // Test parsing of an option represented as hexadecimal string. TEST_F(EvalContextTest, optionHex) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F")); EXPECT_TRUE(parsed_); @@ -216,7 +228,7 @@ TEST_F(EvalContextTest, optionHex) { // Test the parsing of a substring expression TEST_F(EvalContextTest, substring) { - EvalContext eval; + EvalContext eval(Option::V4); EXPECT_NO_THROW(parsed_ = eval.parseString("substring('foobar',2,all) == 'obar'")); @@ -289,13 +301,11 @@ TEST_F(EvalContextTest, parseErrors) { ":1.7: syntax error, " "unexpected (, expecting ["); checkError("option['ab'].text == 'foo'", - ":1.8-11: syntax error, " - "unexpected constant string, " - "expecting integer"); + ":1.8-11: option 'ab' is not defined"); checkError("option[0xa].text == 'ab'", ":1.8-10: syntax error, " "unexpected constant hexstring, " - "expecting integer"); + "expecting constant string or integer"); checkError("substring('foobar') == 'f'", ":1.19: syntax error, " "unexpected ), expecting \",\""); diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index c4d6e07653..c3b70cfb24 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -14,6 +14,9 @@ #include #include +#include +#include +#include #include #include #include @@ -52,6 +55,13 @@ public: pkt6_->addOption(option_str6_); } + /// @brief Destructor. + /// + /// Removes any option definitions registered in runtime. + virtual ~TokenTest() { + LibDHCP::clearRuntimeOptionDefs(); + } + TokenPtr t_; ///< Just a convenience pointer ValueStack values_; ///< evaluated values will be stored here @@ -62,6 +72,23 @@ public: OptionPtr option_str4_; ///< A string option for DHCPv4 OptionPtr option_str6_; ///< A string option for DHCPv6 + /// @brief Create definitions of DHCPv4 options used by unit tests. + void createOptionDefinitions4() { + OptionDefSpaceContainer defs; + OptionDefinitionPtr opt_def4(new OptionDefinition("name-hundred4", + 100, "string")); + defs.addItem(opt_def4, "dhcp4"); + LibDHCP::setRuntimeOptionDefs(defs); + } + + /// @brief Create definitions of DHCPv6 options used by unit tests. + void createOptionDefinitions6() { + OptionDefSpaceContainer defs; + OptionDefinitionPtr opt_def6(new OptionDefinition("name-hundred6", + 100, "string")); + defs.addItem(opt_def6, "dhcp6"); + LibDHCP::setRuntimeOptionDefs(defs); + } /// @brief Verify that the substring eval works properly /// @@ -274,6 +301,28 @@ TEST_F(TokenTest, optionString4) { EXPECT_EQ("hundred4", values_.top()); } +// This test checks if a token representing an option identified by name is +// able to extract this option from an IPv4 packet and properly store the +// option's value. +TEST_F(TokenTest, optionWithNameString4) { + // Create definition of option 100 to provide a mapping between option + // code and option name. + ASSERT_NO_THROW(createOptionDefinitions4()); + + // Create token for option referenced by name. The constructor should + // map the option name to its code. + TokenPtr token; + ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred4", Option::V4, + TokenOption::TEXTUAL))); + + // Evaluate option in the packet. + ASSERT_NO_THROW(token->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + + // Then the content of the option. + EXPECT_EQ("hundred4", values_.top()); +} + // This test checks if a token representing option value is able to extract // the option from an IPv4 packet and properly store its value in a // hexadecimal format. @@ -303,6 +352,28 @@ TEST_F(TokenTest, optionHexString4) { EXPECT_EQ("0x68756E6472656434", values_.top()); } +// This test checks if a token representing an option identified by name is +// able to extract this option from an IPv4 packet and properly store its +// value in the hexadecimal format. +TEST_F(TokenTest, optionWithNameHexString4) { + // Create definition of option 100 to provide a mapping between option + // code and option name. + ASSERT_NO_THROW(createOptionDefinitions4()); + + // Create token for option referenced by name. The constructor should + // map the option name to its code. + TokenPtr token; + ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred4", Option::V4, + TokenOption::HEXADECIMAL))); + + // Evaluate option in the packet. + ASSERT_NO_THROW(token->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + + // Then the content of the option. + EXPECT_EQ("0x68756E6472656434", values_.top()); +} + // This test checks if a token representing an option value is able to extract // the option from an IPv6 packet and properly store the option's value. TEST_F(TokenTest, optionString6) { @@ -331,6 +402,29 @@ TEST_F(TokenTest, optionString6) { EXPECT_EQ("hundred6", values_.top()); } +// This test checks if a token representing an option identified by name is +// able to extract this option from an IPv6 packet and properly store the +// option's value. +TEST_F(TokenTest, optionWithNameString6) { + // Create definition of option 100 to provide a mapping between option + // code and option name. + ASSERT_NO_THROW(createOptionDefinitions6()); + + // Create token for option referenced by name. The constructor should + // map the option name to its code. + TokenPtr token; + ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred6", Option::V6, + TokenOption::TEXTUAL))); + + // Evaluate option in the packet. + ASSERT_NO_THROW(token->evaluate(*pkt6_, values_)); + ASSERT_EQ(1, values_.size()); + + // Then the content of the option. + EXPECT_EQ("hundred6", values_.top()); +} + + // This test checks if a token representing an option value is able to extract // the option from an IPv6 packet and properly store its value in hexadecimal // format. @@ -360,6 +454,28 @@ TEST_F(TokenTest, optionHexString6) { EXPECT_EQ("0x68756E6472656436", values_.top()); } +// This test checks if a token representing an option identified by name is +// able to extract this option from an IPv6 packet and properly store its +// value in the hexadecimal format. +TEST_F(TokenTest, optionWithNameHexString6) { + // Create definition of option 100 to provide a mapping between option + // code and option name. + ASSERT_NO_THROW(createOptionDefinitions6()); + + // Create token for option referenced by name. The constructor should + // map the option name to its code. + TokenPtr token; + ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred6", Option::V6, + TokenOption::HEXADECIMAL))); + + // Evaluate option in the packet. + ASSERT_NO_THROW(token->evaluate(*pkt6_, values_)); + ASSERT_EQ(1, values_.size()); + + // Then the content of the option. + EXPECT_EQ("0x68756E6472656436", values_.top()); +} + // This test checks if a token representing an == operator is able to // compare two values (with incorrectly built stack). TEST_F(TokenTest, optionEqualInvalid) { diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index 85a4806f12..ed76ceb0ae 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -62,6 +64,26 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) { values.push(value_); } +TokenOption::TokenOption(const std::string& option_name, + const Option::Universe& option_universe, + const RepresentationType& rep_type) + : option_code_(0), representation_type_(rep_type) { + OptionDefinitionPtr option_def = LibDHCP::getOptionDef(option_universe, + option_name); + if (!option_def) { + const std::string global_space = + (option_universe == Option::V4) ? "dhcp4" : "dhcp6"; + option_def = LibDHCP::getRuntimeOptionDef(global_space, option_name); + } + + if (!option_def) { + isc_throw(BadValue, "option '" << option_name << "' is not defined"); + } + + option_code_ = option_def->getCode(); +} + + void TokenOption::evaluate(const Pkt& pkt, ValueStack& values) { OptionPtr opt = pkt.getOption(option_code_); diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index 82da09e011..5f3cb14344 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -16,8 +16,10 @@ #define TOKEN_H #include +#include #include #include +#include namespace isc { namespace dhcp { @@ -29,8 +31,8 @@ typedef boost::shared_ptr TokenPtr; /// This is a structure that holds an expression converted to RPN /// -/// For example expression: option[123] == 'foo' will be converted to: -/// [0] = option[123] (TokenOption object) +/// For example expression: option[123].text == 'foo' will be converted to: +/// [0] = option[123].text (TokenOption object) /// [1] = 'foo' (TokenString object) /// [2] = == operator (TokenEqual object) typedef std::vector Expression; @@ -160,16 +162,24 @@ public: }; /// @brief Constructor that takes an option code as a parameter - /// @param option_code code of the option /// - /// Note: There is no constructor that takes option_name, as it would - /// introduce complex dependency of the libkea-eval on libdhcpsrv. - /// - /// @param option_code code of the option to be represented. + /// @param option_code Code of the option to be represented. /// @param rep_type Token representation type. TokenOption(const uint16_t option_code, const RepresentationType& rep_type) : option_code_(option_code), representation_type_(rep_type) {} + /// @brief Constructor that takes option name as a parameter. + /// + /// This constructor will throw exception if there is no definition using + /// specified option name in libdhcp++. + /// + /// @param option_name Name of the option to be represented. + /// @param option_universe Option universe: DHCPv4 or DHCPv6. + /// @param rep_type Token representation type. + TokenOption(const std::string& option_name, + const Option::Universe& option_universe, + const RepresentationType& rep_type); + /// @brief Evaluates the values of the option /// /// This token represents a value of the option, so this method attempts