From: Francis Dupont Date: Fri, 10 Jun 2016 15:04:29 +0000 (+0200) Subject: [4272a] Updated classification doc & reordered code X-Git-Tag: trac4272a_reorg~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7ac1fdee019e61f8e4deb6f0440744b855e17d44;p=thirdparty%2Fkea.git [4272a] Updated classification doc & reordered code --- diff --git a/src/lib/eval/eval.dox b/src/lib/eval/eval.dox index 196068ead9..ee4f8adbbb 100644 --- a/src/lib/eval/eval.dox +++ b/src/lib/eval/eval.dox @@ -126,7 +126,7 @@ instantiated with the appropriate value and put onto the expression vector. are mandatory. If either tool is missing or at too early a version, the configure process will terminate with an error. -@section dhcpEcalToken Supported tokens +@section dhcpEvalToken Supported tokens There are a number of tokens implemented. Each token is derived from isc::eval::Token class and represents a certain expression primitive. @@ -135,12 +135,26 @@ instantiated with the appropriate value and put onto the expression vector. - isc::dhcp::TokenString - represents a constant string, e.g. "MSFT"; - isc::dhcp::TokenHexString - represents a constant string, encoded as hex string, e.g. 0x666f6f which is actually "foo"; + - isc::dhcp::TokenIpAddress -- represents a constant IP address, encoded as + a 4 or 16 byte binary string, e.g., 10.0.0.1 is 0x10000001. - isc::dhcp::TokenOption - represents an option in a packet, e.g. option[123].text; - - isc::dhcp::TokenEqual - represents the equal (==) operator; - - isc::dhcp::TokenSubstring - represents the substring(text, start, length) operator; - isc::dhcp::TokenRelay4Option - represents a sub-option inserted by the DHCPv4 relay, e.g. relay[123].text or relay[123].hex + - isc::dhcp::TokenRelay6Option -- represents a sub-option inserted by + a DHCPv6 relay + - isc::dhcp::TokenPkt4 -- represents a DHCPv4 packet field. + - isc::dhcp::TokenPkt6 -- represents a DHCPv6 packet field (message type + or transaction id). + - isc::dhcp::TokenRelay6Field -- represents a DHCPv6 relay information field. + - isc::dhcp::TokenEqual - represents the equal (==) operator; + - isc::dhcp::TokenSubstring - represents the substring(text, start, length) operator; + - isc::dhcp::TokenConcat -- represents the concat operator which + concatenate two other tokens. + - isc::dhcp::TokenNot -- the logical not operator. + - isc::dhcp::TokenAnd -- the logical and (strict) operator. + - isc::dhcp::TokenOr -- the logical or (strict) operator (strict means + it always evaluates its operands). More operators are expected to be implemented in upcoming releases. diff --git a/src/lib/eval/eval_context.cc b/src/lib/eval/eval_context.cc index 059cb0fe63..ca27893092 100644 --- a/src/lib/eval/eval_context.cc +++ b/src/lib/eval/eval_context.cc @@ -107,7 +107,7 @@ EvalContext::convertNestLevelNumber(const std::string& nest_level, if (n < 0 || n >= HOP_COUNT_LIMIT) { error(loc, "Nest level has invalid value in " + nest_level + ". Allowed range: 0..31"); - } + } } else { error(loc, "Nest level invalid for DHCPv4 packets"); } diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll index ef2c460396..66bf0648c1 100644 --- a/src/lib/eval/lexer.ll +++ b/src/lib/eval/lexer.ll @@ -144,8 +144,6 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]* "option" return isc::eval::EvalParser::make_OPTION(loc); "relay4" return isc::eval::EvalParser::make_RELAY4(loc); "relay6" return isc::eval::EvalParser::make_RELAY6(loc); -"peeraddr" return isc::eval::EvalParser::make_PEERADDR(loc); -"linkaddr" return isc::eval::EvalParser::make_LINKADDR(loc); "text" return isc::eval::EvalParser::make_TEXT(loc); "hex" return isc::eval::EvalParser::make_HEX(loc); "exists" return isc::eval::EvalParser::make_EXISTS(loc); @@ -157,6 +155,11 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]* "giaddr" return isc::eval::EvalParser::make_GIADDR(loc); "yiaddr" return isc::eval::EvalParser::make_YIADDR(loc); "siaddr" return isc::eval::EvalParser::make_SIADDR(loc); +"pkt6" return isc::eval::EvalParser::make_PKT6(loc); +"msgtype" return isc::eval::EvalParser::make_MSGTYPE(loc); +"transid" return isc::eval::EvalParser::make_TRANSID(loc); +"peeraddr" return isc::eval::EvalParser::make_PEERADDR(loc); +"linkaddr" return isc::eval::EvalParser::make_LINKADDR(loc); "substring" return isc::eval::EvalParser::make_SUBSTRING(loc); "all" return isc::eval::EvalParser::make_ALL(loc); "concat" return isc::eval::EvalParser::make_CONCAT(loc); @@ -170,10 +173,6 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]* "]" return isc::eval::EvalParser::make_RBRACKET(loc); "," return isc::eval::EvalParser::make_COMA(loc); -"pkt6" return isc::eval::EvalParser::make_PKT6(loc); -"msgtype" return isc::eval::EvalParser::make_MSGTYPE(loc); -"transid" return isc::eval::EvalParser::make_TRANSID(loc); - . driver.error (loc, "Invalid character: " + std::string(yytext)); <> return isc::eval::EvalParser::make_END(loc); %% diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy index 52e69c0668..2b2696d6df 100644 --- a/src/lib/eval/parser.yy +++ b/src/lib/eval/parser.yy @@ -46,8 +46,6 @@ using namespace isc::eval; OPTION "option" RELAY4 "relay4" RELAY6 "relay6" - PEERADDR "peeraddr" - LINKADDR "linkaddr" LBRACKET "[" RBRACKET "]" DOT "." @@ -62,13 +60,15 @@ using namespace isc::eval; GIADDR "giaddr" YIADDR "yiaddr" SIADDR "siaddr" + PKT6 "pkt6" + MSGTYPE "msgtype" + TRANSID "transid" + PEERADDR "peeraddr" + LINKADDR "linkaddr" SUBSTRING "substring" ALL "all" COMA "," CONCAT "concat" - PKT6 "pkt6" - MSGTYPE "msgtype" - TRANSID "transid" ; %token STRING "constant string" @@ -218,6 +218,16 @@ string_expr : STRING } } + | PKT4 "." pkt4_field + { + TokenPtr pkt4_field(new TokenPkt4($3)); + ctx.expression.push_back(pkt4_field); + } + | PKT6 "." pkt6_field + { + TokenPtr pkt6_field(new TokenPkt6($3)); + ctx.expression.push_back(pkt6_field); + } | RELAY6 "[" nest_level "]" "." relay6_field { switch (ctx.getUniverse()) { @@ -233,16 +243,6 @@ string_expr : STRING } } - | PKT4 "." pkt4_field - { - TokenPtr pkt4_field(new TokenPkt4($3)); - ctx.expression.push_back(pkt4_field); - } - | PKT6 "." pkt6_field - { - TokenPtr pkt6_field(new TokenPkt6($3)); - ctx.expression.push_back(pkt6_field); - } | SUBSTRING "(" string_expr "," start_expr "," length_expr ")" { TokenPtr sub(new TokenSubstring()); @@ -275,6 +275,15 @@ option_repr_type : TEXT } ; +nest_level : INTEGER + { + $$ = ctx.convertNestLevelNumber($1, @1); + } + // Eventually we may add strings to handle different + // ways of choosing from which relay we want to extract + // an option or field. + ; + pkt4_field : CHADDR { $$ = TokenPkt4::CHADDR; @@ -305,6 +314,26 @@ pkt4_field : CHADDR } ; +pkt6_field : MSGTYPE + { + $$ = TokenPkt6::MSGTYPE; + } + | TRANSID + { + $$ = TokenPkt6::TRANSID; + } + ; + +relay6_field : PEERADDR + { + $$ = TokenRelay6Field::PEERADDR; + } + | LINKADDR + { + $$ = TokenRelay6Field::LINKADDR; + } + ; + start_expr : INTEGER { TokenPtr str(new TokenString($1)); @@ -324,23 +353,6 @@ length_expr : INTEGER } ; -relay6_field : PEERADDR { $$ = TokenRelay6Field::PEERADDR; } - | LINKADDR { $$ = TokenRelay6Field::LINKADDR; } - ; - -nest_level : INTEGER - { - $$ = ctx.convertNestLevelNumber($1, @1); - } - // Eventually we may add strings to handle different - // ways of choosing from which relay we want to extract - // an option or field. - ; - -pkt6_field:MSGTYPE { $$ = TokenPkt6::MSGTYPE; } - | TRANSID { $$ = TokenPkt6::TRANSID; } - ; - %% void isc::eval::EvalParser::error(const location_type& loc, diff --git a/src/lib/eval/tests/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc index 9d757d6690..eaa9637163 100644 --- a/src/lib/eval/tests/context_unittest.cc +++ b/src/lib/eval/tests/context_unittest.cc @@ -132,6 +132,62 @@ public: } } + /// @brief checks if the given token is a TokenRelay6Option with + /// the correct nesting level, option code and representation. + /// @param token token to be checked + /// @param expected_level expected nesting level + /// @param expected_code expected option code + /// @param expected_repr expected representation (text, hex, exists) + void checkTokenRelay6Option(const TokenPtr& token, + uint8_t expected_level, + uint16_t expected_code, + TokenOption::RepresentationType expected_repr) { + ASSERT_TRUE(token); + boost::shared_ptr opt = + boost::dynamic_pointer_cast(token); + ASSERT_TRUE(opt); + + EXPECT_EQ(expected_level, opt->getNest()); + EXPECT_EQ(expected_code, opt->getCode()); + EXPECT_EQ(expected_repr, opt->getRepresentation()); + } + + /// @brief This tests attempts to parse the expression then checks + /// if the number of tokens is correct and the TokenRelay6Option + /// is as expected. + /// + /// @param expr expression to be parsed + /// @param exp_level expected level to be parsed + /// @param exp_code expected option code to be parsed + /// @param exp_repr expected representation to be parsed + /// @param exp_tokens expected number of tokens + void testRelay6Option(std::string expr, + uint8_t exp_level, + uint16_t exp_code, + TokenOption::RepresentationType exp_repr, + int exp_tokens) { + EvalContext eval(Option::V6); + + // parse the expression + try { + parsed_ = eval.parseString(expr); + } + catch (const EvalParseError& ex) { + FAIL() <<"Exception thrown: " << ex.what(); + return; + } + + // Parsing should succed and return a token. + EXPECT_TRUE(parsed_); + + // There should be the expected number of tokens. + ASSERT_EQ(exp_tokens, eval.expression.size()); + + // checkt that the first token is TokenRelay6Option and that + // is has the correct attributes + checkTokenRelay6Option(eval.expression.at(0), exp_level, exp_code, exp_repr); + } + /// @brief checks if the given token is Pkt4 of specified type /// @param token token to be checked /// @param type expected type of the Pkt4 field @@ -177,76 +233,51 @@ public: checkTokenPkt4(eval.expression.at(0), exp_type); } - /// @brief checks if the given token is a substring operator - void checkTokenSubstring(const TokenPtr& token) { + /// @brief checks if the given token is Pkt6 of specified type + /// @param token token to be checked + /// @param exp_type expected type of the Pkt6 field + void checkTokenPkt6(const TokenPtr& token, + TokenPkt6::FieldType exp_type) { ASSERT_TRUE(token); - boost::shared_ptr sub = - boost::dynamic_pointer_cast(token); - EXPECT_TRUE(sub); - } - /// @brief checks if the given token is a concat operator - void checkTokenConcat(const TokenPtr& token) { - ASSERT_TRUE(token); - boost::shared_ptr conc = - boost::dynamic_pointer_cast(token); - EXPECT_TRUE(conc); - } + boost::shared_ptr pkt = + boost::dynamic_pointer_cast(token); - /// @brief checks if the given token is a TokenRelay6Option with - /// the correct nesting level, option code and representation. - /// @param token token to be checked - /// @param expected_level expected nesting level - /// @param expected_code expected option code - /// @param expected_repr expected representation (text, hex, exists) - void checkTokenRelay6Option(const TokenPtr& token, - uint8_t expected_level, - uint16_t expected_code, - TokenOption::RepresentationType expected_repr) { - ASSERT_TRUE(token); - boost::shared_ptr opt = - boost::dynamic_pointer_cast(token); - ASSERT_TRUE(opt); + ASSERT_TRUE(pkt); - EXPECT_EQ(expected_level, opt->getNest()); - EXPECT_EQ(expected_code, opt->getCode()); - EXPECT_EQ(expected_repr, opt->getRepresentation()); + EXPECT_EQ(exp_type, pkt->getType()); } - /// @brief This tests attempts to parse the expression then checks - /// if the number of tokens is correct and the TokenRelay6Option - /// is as expected. + /// @brief Test that verifies access to the DHCPv6 packet fields. + /// + /// This test attempts to parse the expression, will check if the number + /// of tokens is exactly as planned and then will try to verify if the + /// first token represents expected the field in DHCPv6 packet. /// /// @param expr expression to be parsed - /// @param exp_level expected level to be parsed - /// @param exp_code expected option code to be parsed - /// @param exp_repr expected representation to be parsed + /// @param exp_type expected field type to be parsed /// @param exp_tokens expected number of tokens - void testRelay6Option(std::string expr, - uint8_t exp_level, - uint16_t exp_code, - TokenOption::RepresentationType exp_repr, - int exp_tokens) { + void testPkt6Field(std::string expr, TokenPkt6::FieldType exp_type, + int exp_tokens) { EvalContext eval(Option::V6); - // parse the expression + // Parse the expression. try { parsed_ = eval.parseString(expr); } catch (const EvalParseError& ex) { - FAIL() <<"Exception thrown: " << ex.what(); + FAIL() << "Exception thrown: " << ex.what(); return; } - // Parsing should succed and return a token. + // Parsing should succeed and return a token. EXPECT_TRUE(parsed_); - // There should be the expected number of tokens. + // There should be the requested number of tokens ASSERT_EQ(exp_tokens, eval.expression.size()); - // checkt that the first token is TokenRelay6Option and that - // is has the correct attributes - checkTokenRelay6Option(eval.expression.at(0), exp_level, exp_code, exp_repr); + // Check that the first token is TokenPkt6 instance and has correct type. + checkTokenPkt6(eval.expression.at(0), exp_type); } /// @brief checks if the given token is a TokenRelay with the @@ -301,19 +332,20 @@ public: checkTokenRelay6Field(eval.expression.at(0), exp_level, exp_type); } - /// @brief checks if the given token is Pkt6 of specified type - /// @param token token to be checked - /// @param exp_type expected type of the Pkt6 field - void checkTokenPkt6(const TokenPtr& token, - TokenPkt6::FieldType exp_type) { + /// @brief checks if the given token is a substring operator + void checkTokenSubstring(const TokenPtr& token) { ASSERT_TRUE(token); + boost::shared_ptr sub = + boost::dynamic_pointer_cast(token); + EXPECT_TRUE(sub); + } - boost::shared_ptr pkt = - boost::dynamic_pointer_cast(token); - - ASSERT_TRUE(pkt); - - EXPECT_EQ(exp_type, pkt->getType()); + /// @brief checks if the given token is a concat operator + void checkTokenConcat(const TokenPtr& token) { + ASSERT_TRUE(token); + boost::shared_ptr conc = + boost::dynamic_pointer_cast(token); + EXPECT_TRUE(conc); } /// @brief checks if the given expression raises the expected message @@ -340,38 +372,6 @@ public: universe_ = universe; } - /// @brief Test that verifies access to the DHCPv6 packet fields. - /// - /// This test attempts to parse the expression, will check if the number - /// of tokens is exactly as planned and then will try to verify if the - /// first token represents expected the field in DHCPv6 packet. - /// - /// @param expr expression to be parsed - /// @param exp_type expected field type to be parsed - /// @param exp_tokens expected number of tokens - void testPkt6Field(std::string expr, TokenPkt6::FieldType exp_type, - int exp_tokens) { - EvalContext eval(Option::V6); - - // Parse the expression. - try { - parsed_ = eval.parseString(expr); - } - catch (const EvalParseError& ex) { - FAIL() << "Exception thrown: " << ex.what(); - return; - } - - // Parsing should succeed and return a token. - EXPECT_TRUE(parsed_); - - // There should be the requested number of tokens - ASSERT_EQ(exp_tokens, eval.expression.size()); - - // Check that the first token is TokenPkt6 instance and has correct type. - checkTokenPkt6(eval.expression.at(0), exp_type); - } - Option::Universe universe_; bool parsed_; ///< Parsing status }; @@ -630,6 +630,30 @@ TEST_F(EvalContextTest, relay4Error) { ":1.1-6: relay4 can only be used in DHCPv4."); } +// Test the parsing of a relay6 option +TEST_F(EvalContextTest, relay6Option) { + EvalContext eval(Option::V6); + + testRelay6Option("relay6[0].option[123].text == 'foo'", + 0, 123, TokenOption::TEXTUAL, 3); +} + +// Test the parsing of existence for a relay6 option +TEST_F(EvalContextTest, relay6OptionExists) { + EvalContext eval(Option::V6); + + testRelay6Option("relay6[1].option[75].exists", + 1, 75, TokenOption::EXISTS, 1); +} + +// Test the parsing of hex for a relay6 option +TEST_F(EvalContextTest, relay6OptionHex) { + EvalContext eval(Option::V6); + + testRelay6Option("relay6[2].option[85].hex == 'foo'", + 2, 85, TokenOption::HEXADECIMAL, 3); +} + // Tests whether chaddr field in DHCPv4 can be accessed. TEST_F(EvalContextTest, pkt4FieldChaddr) { testPkt4Field("pkt4.mac == 0x000102030405", TokenPkt4::CHADDR, 3); @@ -675,6 +699,18 @@ TEST_F(EvalContextTest, pkt6FieldTransid) { testPkt6Field("pkt6.transid == '1'", TokenPkt6::TRANSID, 3); } +// Tests if the linkaddr field in a Relay6 encapsulation can be accessed. +TEST_F(EvalContextTest, relay6FieldLinkAddr) { + testRelay6Field("relay6[0].linkaddr == ::", + 0, TokenRelay6Field::LINKADDR, 3); +} + +// Tests if the peeraddr field in a Relay6 encapsulation can be accessed. +TEST_F(EvalContextTest, relay6FieldPeerAddr) { + testRelay6Field("relay6[1].peeraddr == ::", + 1, TokenRelay6Field::PEERADDR, 3); +} + // Test parsing of logical operators TEST_F(EvalContextTest, logicalOps) { // option.exists @@ -821,42 +857,6 @@ TEST_F(EvalContextTest, concat) { checkTokenConcat(tmp3); } -// Test the parsing of a relay6 option -TEST_F(EvalContextTest, relay6Option) { - EvalContext eval(Option::V6); - - testRelay6Option("relay6[0].option[123].text == 'foo'", - 0, 123, TokenOption::TEXTUAL, 3); -} - -// Test the parsing of existence for a relay6 option -TEST_F(EvalContextTest, relay6OptionExists) { - EvalContext eval(Option::V6); - - testRelay6Option("relay6[1].option[75].exists", - 1, 75, TokenOption::EXISTS, 1); -} - -// Test the parsing of hex for a relay6 option -TEST_F(EvalContextTest, relay6OptionHex) { - EvalContext eval(Option::V6); - - testRelay6Option("relay6[2].option[85].hex == 'foo'", - 2, 85, TokenOption::HEXADECIMAL, 3); -} - -// Tests if the linkaddr field in a Relay6 encapsulation can be accessed. -TEST_F(EvalContextTest, relay6FieldLinkAddr) { - testRelay6Field("relay6[0].linkaddr == ::", - 0, TokenRelay6Field::LINKADDR, 3); -} - -// Tests if the peeraddr field in a Relay6 encapsulation can be accessed. -TEST_F(EvalContextTest, relay6FieldPeerAddr) { - testRelay6Field("relay6[1].peeraddr == ::", - 1, TokenRelay6Field::PEERADDR, 3); -} - // // Test some scanner error cases TEST_F(EvalContextTest, scanErrors) { diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index c4cbec6003..d7a0aaa932 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -835,6 +835,56 @@ TEST_F(TokenTest, relay4RAIOnly) { EXPECT_TRUE(checkFile()); } +// This test checks if we can properly extract an option +// from relay encapsulations. Our packet has two relay +// encapsulations. Both include a common option with the +// original message (option 100) and both include their +// own option (101 and 102). We attempt to extract the +// options and compare them to expected values. We also +// try to extract an option from an encapsulation +// that doesn't exist (level 2), this should result in an empty +// string. +TEST_F(TokenTest, relay6Option) { + // We start by adding a set of relay encapsulations to the + // basic v6 packet. + addRelay6Encapsulations(); + + // Then we work our way through the set of choices + // Level 0 both options it has and the check that + // the checking for an option it doesn't have results + // in an empty string. + verifyRelay6Option(0, 100, TokenOption::TEXTUAL, "hundred.zero"); + verifyRelay6Option(0, 100, TokenOption::EXISTS, "true"); + verifyRelay6Option(0, 101, TokenOption::TEXTUAL, "hundredone.zero"); + verifyRelay6Option(0, 102, TokenOption::TEXTUAL, ""); + verifyRelay6Option(0, 102, TokenOption::EXISTS, "false"); + + // Level 1, again both options it has and the one for level 0 + verifyRelay6Option(1, 100, TokenOption::TEXTUAL, "hundred.one"); + verifyRelay6Option(1, 101, TokenOption::TEXTUAL, ""); + verifyRelay6Option(1, 102, TokenOption::TEXTUAL, "hundredtwo.one"); + + // Level 2, no encapsulation so no options + verifyRelay6Option(2, 100, TokenOption::TEXTUAL, ""); + + // Check that the debug output was correct. Add the strings + // to the test vector in the class and then call checkFile + // for comparison + addString("EVAL_DEBUG_OPTION Pushing option 100 with value 'hundred.zero'"); + addString("EVAL_DEBUG_OPTION Pushing option 100 with value 'true'"); + addString("EVAL_DEBUG_OPTION Pushing option 101 with value 'hundredone.zero'"); + addString("EVAL_DEBUG_OPTION Pushing option 102 with value ''"); + addString("EVAL_DEBUG_OPTION Pushing option 102 with value 'false'"); + + addString("EVAL_DEBUG_OPTION Pushing option 100 with value 'hundred.one'"); + addString("EVAL_DEBUG_OPTION Pushing option 101 with value ''"); + addString("EVAL_DEBUG_OPTION Pushing option 102 with value 'hundredtwo.one'"); + + addString("EVAL_DEBUG_OPTION Pushing option 100 with value ''"); + + EXPECT_TRUE(checkFile()); +} + // Verifies if the DHCPv4 packet fields can be extracted. TEST_F(TokenTest, pkt4Fields) { pkt4_->setGiaddr(IOAddress("192.0.2.1")); @@ -927,6 +977,116 @@ TEST_F(TokenTest, pkt4Fields) { EXPECT_TRUE(checkFile()); } +// Verifies if the DHCPv6 packet fields can be extracted. +TEST_F(TokenTest, pkt6Fields) { + // The default test creates a v6 DHCPV6_SOLICIT packet with a + // transaction id of 12345. + + // Check the message type + ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::MSGTYPE))); + EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_)); + ASSERT_EQ(1, values_.size()); + uint32_t expected = htonl(1); + EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4)); + + // Check the transaction id field + clearStack(); + ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::TRANSID))); + EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_)); + ASSERT_EQ(1, values_.size()); + expected = htonl(12345); + EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4)); + + // Check that working with a v4 packet generates an error + clearStack(); + ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::TRANSID))); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); + + // Check that the debug output was correct. Add the strings + // to the test vector in the class and then call checkFile + // for comparison + addString("EVAL_DEBUG_PKT6 Pushing PKT6 field msgtype with value 0x00000001"); + addString("EVAL_DEBUG_PKT6 Pushing PKT6 field transid with value 0x00003039"); + + EXPECT_TRUE(checkFile()); +} + +// This test checks if we can properly extract the link and peer +// address fields from relay encapsulations. Our packet has +// two relay encapsulations. We attempt to extract the two +// fields from both of the encapsulations and compare them. +// We also try to extract one of the fields from an encapsulation +// that doesn't exist (level 2), this should result in an empty +// string. +TEST_F(TokenTest, relay6Field) { + // Values for the address results + uint8_t zeroaddr[] = { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + uint8_t linkaddr[] = { 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 }; + uint8_t peeraddr[] = { 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2 }; + + // We start by adding a set of relay encapsulations to the + // basic v6 packet. + addRelay6Encapsulations(); + + // Then we work our way through the set of choices + // Level 0 both link and peer address should be 0::0 + verifyRelay6Eval(0, TokenRelay6Field::LINKADDR, 16, zeroaddr); + verifyRelay6Eval(0, TokenRelay6Field::PEERADDR, 16, zeroaddr); + + // Level 1 link and peer should have different non-zero addresses + verifyRelay6Eval(1, TokenRelay6Field::LINKADDR, 16, linkaddr); + verifyRelay6Eval(1, TokenRelay6Field::PEERADDR, 16, peeraddr); + + // Level 2 has no encapsulation so the address should be zero length + verifyRelay6Eval(2, TokenRelay6Field::LINKADDR, 0, zeroaddr); + + // Lets check that the layout of the address returned by the + // token matches that of the TokenIpAddress + TokenPtr trelay; + TokenPtr taddr; + TokenPtr tequal; + ASSERT_NO_THROW(trelay.reset(new TokenRelay6Field(1, TokenRelay6Field::LINKADDR))); + ASSERT_NO_THROW(taddr.reset(new TokenIpAddress("1::1"))); + ASSERT_NO_THROW(tequal.reset(new TokenEqual())); + + EXPECT_NO_THROW(trelay->evaluate(*pkt6_, values_)); + EXPECT_NO_THROW(taddr->evaluate(*pkt6_, values_)); + EXPECT_NO_THROW(tequal->evaluate(*pkt6_, values_)); + + // We should have a single value on the stack and it should be "true" + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("true", values_.top()); + + // be tidy + clearStack(); + + // Check that the debug output was correct. Add the strings + // to the test vector in the class and then call checkFile + // for comparison + addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 0 " + "with value 0x00000000000000000000000000000000"); + addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest 0 " + "with value 0x00000000000000000000000000000000"); + addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 1 " + "with value 0x00010000000000000000000000000001"); + addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest 1 " + "with value 0x00010000000000000000000000000002"); + addString("EVAL_DEBUG_RELAY6_RANGE Pushing PKT6 relay field linkaddr nest 2 " + "with value 0x"); + + addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 1 " + "with value 0x00010000000000000000000000000001"); + addString("EVAL_DEBUG_IPADDRESS Pushing IPAddress " + "0x00010000000000000000000000000001"); + addString("EVAL_DEBUG_EQUAL Popping 0x00010000000000000000000000000001 " + "and 0x00010000000000000000000000000001 pushing result 'true'"); + + EXPECT_TRUE(checkFile()); +} + // This test checks if a token representing an == operator is able to // compare two values (with incorrectly built stack). TEST_F(TokenTest, optionEqualInvalid) { @@ -988,274 +1148,74 @@ TEST_F(TokenTest, optionEqualTrue) { EXPECT_TRUE(checkFile()); } -// This test checks if a token representing a not is able to -// negate a boolean value (with incorrectly built stack). -TEST_F(TokenTest, operatorNotInvalid) { - - ASSERT_NO_THROW(t_.reset(new TokenNot())); +// This test checks if 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())); - // CASE 1: The stack is empty. + // Subsring requires three values on the stack, try + // with 0, 1 and 2 all should throw an exception EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - // CASE 2: The top value is not a boolean - values_.push("foo"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); -} - -// This test checks if a token representing a not operator is able to -// negate a boolean value. -TEST_F(TokenTest, operatorNot) { + values_.push(""); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - ASSERT_NO_THROW(t_.reset(new TokenNot())); + values_.push("0"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - values_.push("true"); + // Three should work + values_.push("0"); EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - // After evaluation there should be the negation of the value. - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("false", values_.top()); - - // Double negation is identity. - 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("true", values_.top()); + EXPECT_EQ("", values_.top()); // Check that the debug output was correct. Add the strings // to the test vector in the class and then call checkFile // for comparison - addString("EVAL_DEBUG_NOT Popping 'true' pushing 'false'"); - addString("EVAL_DEBUG_NOT Popping 'false' pushing 'true'"); + addString("EVAL_DEBUG_SUBSTRING_EMPTY Popping length 0, start 0, " + "string 0x pushing result 0x"); EXPECT_TRUE(checkFile()); } -// This test checks if a token representing an and is able to -// conjugate two values (with incorrectly built stack). -TEST_F(TokenTest, operatorAndInvalid) { - - ASSERT_NO_THROW(t_.reset(new TokenAnd())); +// Test getting the whole string in different ways +TEST_F(TokenTest, substringWholeString) { + // Get the whole string + verifySubstringEval("foobar", "0", "6", "foobar"); - // CASE 1: There's not enough values on the stack. and is an operator that - // takes two parameters. There are 0 on the stack. - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + // Get the whole string with "all" + verifySubstringEval("foobar", "0", "all", "foobar"); - // CASE 2: One value is still not enough. - values_.push("foo"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + // Get the whole string with an extra long number + verifySubstringEval("foobar", "0", "123456", "foobar"); - // CASE 3: The two values must be logical - values_.push("true"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); + // Get the whole string counting from the back + verifySubstringEval("foobar", "-6", "all", "foobar"); - // Swap the 2 values - values_.push("true"); - values_.push("foo"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); + // Check that the debug output was correct. Add the strings + // to the test vector in the class and then call checkFile + // for comparison + addString("EVAL_DEBUG_SUBSTRING Popping length 6, start 0, " + "string 0x666F6F626172 pushing result 0x666F6F626172"); + addString("EVAL_DEBUG_SUBSTRING Popping length all, start 0, " + "string 0x666F6F626172 pushing result 0x666F6F626172"); + addString("EVAL_DEBUG_SUBSTRING Popping length 123456, start 0, " + "string 0x666F6F626172 pushing result 0x666F6F626172"); + addString("EVAL_DEBUG_SUBSTRING Popping length all, start -6, " + "string 0x666F6F626172 pushing result 0x666F6F626172"); + EXPECT_TRUE(checkFile()); } -// This test checks if a token representing an and operator is able to -// conjugate false with another logical -TEST_F(TokenTest, operatorAndFalse) { - - ASSERT_NO_THROW(t_.reset(new TokenAnd())); - - values_.push("true"); - values_.push("false"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - - // After evaluation there should be a single "false" value - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("false", values_.top()); - - // After true and false, check false and true - values_.push("true"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("false", values_.top()); - - // And false and false - values_.push("false"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("false", values_.top()); - - // Check that the debug output was correct. Add the strings - // to the test vector in the class and then call checkFile - // for comparison - addString("EVAL_DEBUG_AND Popping 'false' and 'true' pushing 'false'"); - addString("EVAL_DEBUG_AND Popping 'true' and 'false' pushing 'false'"); - addString("EVAL_DEBUG_AND Popping 'false' and 'false' pushing 'false'"); - EXPECT_TRUE(checkFile()); -} - -// This test checks if a token representing an and is able to -// conjugate two true values. -TEST_F(TokenTest, operatorAndTrue) { - - ASSERT_NO_THROW(t_.reset(new TokenAnd())); - - values_.push("true"); - values_.push("true"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - - // After evaluation there should be a single "true" value - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("true", values_.top()); - - // Check that the debug output was correct. Add the strings - // to the test vector in the class and then call checkFile - // for comparison - addString("EVAL_DEBUG_AND Popping 'true' and 'true' pushing 'true'"); - EXPECT_TRUE(checkFile()); -} - -// This test checks if a token representing an or is able to -// combinate two values (with incorrectly built stack). -TEST_F(TokenTest, operatorOrInvalid) { - - ASSERT_NO_THROW(t_.reset(new TokenOr())); - - // CASE 1: There's not enough values on the stack. or is an operator that - // takes two parameters. There are 0 on the stack. - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - - // CASE 2: One value is still not enough. - values_.push("foo"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - - // CASE 3: The two values must be logical - values_.push("true"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); - - // Swap the 2 values - values_.push("true"); - values_.push("foo"); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); -} - -// This test checks if a token representing an or is able to -// conjugate two false values. -TEST_F(TokenTest, operatorOrFalse) { - - ASSERT_NO_THROW(t_.reset(new TokenOr())); - - values_.push("false"); - values_.push("false"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - - // After evaluation there should be a single "false" value - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("false", values_.top()); - - // Check that the debug output was correct. Add the strings - // to the test vector in the class and then call checkFile - // for comparison - addString("EVAL_DEBUG_OR Popping 'false' and 'false' pushing 'false'"); - EXPECT_TRUE(checkFile()); -} - -// This test checks if a token representing an == operator is able to -// conjugate true with another logical -TEST_F(TokenTest, operatorOrTrue) { - - ASSERT_NO_THROW(t_.reset(new TokenOr())); - - values_.push("false"); - values_.push("true"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - - // After evaluation there should be a single "true" value - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("true", values_.top()); - - // After false or true, checks true or false - values_.push("false"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("true", values_.top()); - - // And true or true - values_.push("true"); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("true", values_.top()); - - // Check that the debug output was correct. Add the strings - // to the test vector in the class and then call checkFile - // for comparison - addString("EVAL_DEBUG_OR Popping 'true' and 'false' pushing 'true'"); - addString("EVAL_DEBUG_OR Popping 'false' and 'true' pushing 'true'"); - addString("EVAL_DEBUG_OR Popping 'true' and 'true' pushing 'true'"); - EXPECT_TRUE(checkFile()); -} - -// This test checks if 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 throw 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()); - - // Check that the debug output was correct. Add the strings - // to the test vector in the class and then call checkFile - // for comparison - addString("EVAL_DEBUG_SUBSTRING_EMPTY Popping length 0, start 0, " - "string 0x pushing result 0x"); - EXPECT_TRUE(checkFile()); -} - -// 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"); - - // Check that the debug output was correct. Add the strings - // to the test vector in the class and then call checkFile - // for comparison - addString("EVAL_DEBUG_SUBSTRING Popping length 6, start 0, " - "string 0x666F6F626172 pushing result 0x666F6F626172"); - addString("EVAL_DEBUG_SUBSTRING Popping length all, start 0, " - "string 0x666F6F626172 pushing result 0x666F6F626172"); - addString("EVAL_DEBUG_SUBSTRING Popping length 123456, start 0, " - "string 0x666F6F626172 pushing result 0x666F6F626172"); - addString("EVAL_DEBUG_SUBSTRING Popping length all, start -6, " - "string 0x666F6F626172 pushing result 0x666F6F626172"); - EXPECT_TRUE(checkFile()); -} - -// 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 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"); // Check that the debug output was correct. Add the strings // to the test vector in the class and then call checkFile @@ -1512,163 +1472,204 @@ TEST_F(TokenTest, concat) { EXPECT_TRUE(checkFile()); } -// This test checks if we can properly extract the link and peer -// address fields from relay encapsulations. Our packet has -// two relay encapsulations. We attempt to extract the two -// fields from both of the encapsulations and compare them. -// We also try to extract one of the fields from an encapsulation -// that doesn't exist (level 2), this should result in an empty -// string. -TEST_F(TokenTest, relay6Field) { - // Values for the address results - uint8_t zeroaddr[] = { 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 }; - uint8_t linkaddr[] = { 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 }; - uint8_t peeraddr[] = { 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2 }; +// This test checks if a token representing a not is able to +// negate a boolean value (with incorrectly built stack). +TEST_F(TokenTest, operatorNotInvalid) { - // We start by adding a set of relay encapsulations to the - // basic v6 packet. - addRelay6Encapsulations(); + ASSERT_NO_THROW(t_.reset(new TokenNot())); - // Then we work our way through the set of choices - // Level 0 both link and peer address should be 0::0 - verifyRelay6Eval(0, TokenRelay6Field::LINKADDR, 16, zeroaddr); - verifyRelay6Eval(0, TokenRelay6Field::PEERADDR, 16, zeroaddr); + // CASE 1: The stack is empty. + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - // Level 1 link and peer should have different non-zero addresses - verifyRelay6Eval(1, TokenRelay6Field::LINKADDR, 16, linkaddr); - verifyRelay6Eval(1, TokenRelay6Field::PEERADDR, 16, peeraddr); + // CASE 2: The top value is not a boolean + values_.push("foo"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); +} - // Level 2 has no encapsulation so the address should be zero length - verifyRelay6Eval(2, TokenRelay6Field::LINKADDR, 0, zeroaddr); +// This test checks if a token representing a not operator is able to +// negate a boolean value. +TEST_F(TokenTest, operatorNot) { - // Lets check that the layout of the address returned by the - // token matches that of the TokenIpAddress - TokenPtr trelay; - TokenPtr taddr; - TokenPtr tequal; - ASSERT_NO_THROW(trelay.reset(new TokenRelay6Field(1, TokenRelay6Field::LINKADDR))); - ASSERT_NO_THROW(taddr.reset(new TokenIpAddress("1::1"))); - ASSERT_NO_THROW(tequal.reset(new TokenEqual())); + ASSERT_NO_THROW(t_.reset(new TokenNot())); - EXPECT_NO_THROW(trelay->evaluate(*pkt6_, values_)); - EXPECT_NO_THROW(taddr->evaluate(*pkt6_, values_)); - EXPECT_NO_THROW(tequal->evaluate(*pkt6_, values_)); + values_.push("true"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - // We should have a single value on the stack and it should be "true" + // After evaluation there should be the negation of the value. ASSERT_EQ(1, values_.size()); - EXPECT_EQ("true", values_.top()); + EXPECT_EQ("false", values_.top()); - // be tidy - clearStack(); + // Double negation is identity. + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("true", values_.top()); // Check that the debug output was correct. Add the strings // to the test vector in the class and then call checkFile // for comparison - addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 0 " - "with value 0x00000000000000000000000000000000"); - addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest 0 " - "with value 0x00000000000000000000000000000000"); - addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 1 " - "with value 0x00010000000000000000000000000001"); - addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest 1 " - "with value 0x00010000000000000000000000000002"); - addString("EVAL_DEBUG_RELAY6_RANGE Pushing PKT6 relay field linkaddr nest 2 " - "with value 0x"); + addString("EVAL_DEBUG_NOT Popping 'true' pushing 'false'"); + addString("EVAL_DEBUG_NOT Popping 'false' pushing 'true'"); + EXPECT_TRUE(checkFile()); +} - addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 1 " - "with value 0x00010000000000000000000000000001"); - addString("EVAL_DEBUG_IPADDRESS Pushing IPAddress " - "0x00010000000000000000000000000001"); - addString("EVAL_DEBUG_EQUAL Popping 0x00010000000000000000000000000001 " - "and 0x00010000000000000000000000000001 pushing result 'true'"); +// This test checks if a token representing an and is able to +// conjugate two values (with incorrectly built stack). +TEST_F(TokenTest, operatorAndInvalid) { + + ASSERT_NO_THROW(t_.reset(new TokenAnd())); + + // CASE 1: There's not enough values on the stack. and is an operator that + // takes two parameters. There are 0 on the stack. + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + // CASE 2: One value is still not enough. + values_.push("foo"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + // CASE 3: The two values must be logical + values_.push("true"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); + + // Swap the 2 values + values_.push("true"); + values_.push("foo"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); +} + +// This test checks if a token representing an and operator is able to +// conjugate false with another logical +TEST_F(TokenTest, operatorAndFalse) { + + ASSERT_NO_THROW(t_.reset(new TokenAnd())); + values_.push("true"); + values_.push("false"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // After evaluation there should be a single "false" value + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("false", values_.top()); + + // After true and false, check false and true + values_.push("true"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("false", values_.top()); + + // And false and false + values_.push("false"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("false", values_.top()); + + // Check that the debug output was correct. Add the strings + // to the test vector in the class and then call checkFile + // for comparison + addString("EVAL_DEBUG_AND Popping 'false' and 'true' pushing 'false'"); + addString("EVAL_DEBUG_AND Popping 'true' and 'false' pushing 'false'"); + addString("EVAL_DEBUG_AND Popping 'false' and 'false' pushing 'false'"); EXPECT_TRUE(checkFile()); } -// This test checks if we can properly extract an option -// from relay encapsulations. Our packet has two relay -// encapsulations. Both include a common option with the -// original message (option 100) and both include their -// own option (101 and 102). We attempt to extract the -// options and compare them to expected values. We also -// try to extract an option from an encapsulation -// that doesn't exist (level 2), this should result in an empty -// string. -TEST_F(TokenTest, relay6Option) { - // We start by adding a set of relay encapsulations to the - // basic v6 packet. - addRelay6Encapsulations(); +// This test checks if a token representing an and is able to +// conjugate two true values. +TEST_F(TokenTest, operatorAndTrue) { - // Then we work our way through the set of choices - // Level 0 both options it has and the check that - // the checking for an option it doesn't have results - // in an empty string. - verifyRelay6Option(0, 100, TokenOption::TEXTUAL, "hundred.zero"); - verifyRelay6Option(0, 100, TokenOption::EXISTS, "true"); - verifyRelay6Option(0, 101, TokenOption::TEXTUAL, "hundredone.zero"); - verifyRelay6Option(0, 102, TokenOption::TEXTUAL, ""); - verifyRelay6Option(0, 102, TokenOption::EXISTS, "false"); + ASSERT_NO_THROW(t_.reset(new TokenAnd())); - // Level 1, again both options it has and the one for level 0 - verifyRelay6Option(1, 100, TokenOption::TEXTUAL, "hundred.one"); - verifyRelay6Option(1, 101, TokenOption::TEXTUAL, ""); - verifyRelay6Option(1, 102, TokenOption::TEXTUAL, "hundredtwo.one"); + values_.push("true"); + values_.push("true"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - // Level 2, no encapsulation so no options - verifyRelay6Option(2, 100, TokenOption::TEXTUAL, ""); + // After evaluation there should be a single "true" value + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("true", values_.top()); // Check that the debug output was correct. Add the strings // to the test vector in the class and then call checkFile // for comparison - addString("EVAL_DEBUG_OPTION Pushing option 100 with value 'hundred.zero'"); - addString("EVAL_DEBUG_OPTION Pushing option 100 with value 'true'"); - addString("EVAL_DEBUG_OPTION Pushing option 101 with value 'hundredone.zero'"); - addString("EVAL_DEBUG_OPTION Pushing option 102 with value ''"); - addString("EVAL_DEBUG_OPTION Pushing option 102 with value 'false'"); + addString("EVAL_DEBUG_AND Popping 'true' and 'true' pushing 'true'"); + EXPECT_TRUE(checkFile()); +} - addString("EVAL_DEBUG_OPTION Pushing option 100 with value 'hundred.one'"); - addString("EVAL_DEBUG_OPTION Pushing option 101 with value ''"); - addString("EVAL_DEBUG_OPTION Pushing option 102 with value 'hundredtwo.one'"); +// This test checks if a token representing an or is able to +// combinate two values (with incorrectly built stack). +TEST_F(TokenTest, operatorOrInvalid) { - addString("EVAL_DEBUG_OPTION Pushing option 100 with value ''"); + ASSERT_NO_THROW(t_.reset(new TokenOr())); + + // CASE 1: There's not enough values on the stack. or is an operator that + // takes two parameters. There are 0 on the stack. + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + // CASE 2: One value is still not enough. + values_.push("foo"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); + + // CASE 3: The two values must be logical + values_.push("true"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); + + // Swap the 2 values + values_.push("true"); + values_.push("foo"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); +} + +// This test checks if a token representing an or is able to +// conjugate two false values. +TEST_F(TokenTest, operatorOrFalse) { + + ASSERT_NO_THROW(t_.reset(new TokenOr())); + + values_.push("false"); + values_.push("false"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + // After evaluation there should be a single "false" value + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("false", values_.top()); + + // Check that the debug output was correct. Add the strings + // to the test vector in the class and then call checkFile + // for comparison + addString("EVAL_DEBUG_OR Popping 'false' and 'false' pushing 'false'"); EXPECT_TRUE(checkFile()); } -// Verifies if the DHCPv6 packet fields can be extracted. -TEST_F(TokenTest, pkt6Fields) { - // The default test creates a v6 DHCPV6_SOLICIT packet with a - // transaction id of 12345. +// This test checks if a token representing an == operator is able to +// conjugate true with another logical +TEST_F(TokenTest, operatorOrTrue) { - // Check the message type - ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::MSGTYPE))); - EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_)); + ASSERT_NO_THROW(t_.reset(new TokenOr())); + + values_.push("false"); + values_.push("true"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // After evaluation there should be a single "true" value ASSERT_EQ(1, values_.size()); - uint32_t expected = htonl(1); - EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4)); + EXPECT_EQ("true", values_.top()); - // Check the transaction id field - clearStack(); - ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::TRANSID))); - EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_)); + // After false or true, checks true or false + values_.push("false"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); ASSERT_EQ(1, values_.size()); - expected = htonl(12345); - EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4)); + EXPECT_EQ("true", values_.top()); - // Check that working with a v4 packet generates an error - clearStack(); - ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::TRANSID))); - EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError); + // And true or true + values_.push("true"); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("true", values_.top()); // Check that the debug output was correct. Add the strings // to the test vector in the class and then call checkFile // for comparison - addString("EVAL_DEBUG_PKT6 Pushing PKT6 field msgtype with value 0x00000001"); - addString("EVAL_DEBUG_PKT6 Pushing PKT6 field transid with value 0x00003039"); - + addString("EVAL_DEBUG_OR Popping 'true' and 'false' pushing 'true'"); + addString("EVAL_DEBUG_OR Popping 'false' and 'true' pushing 'true'"); + addString("EVAL_DEBUG_OR Popping 'true' and 'true' pushing 'true'"); EXPECT_TRUE(checkFile()); } + }; diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index f0e74a04d5..887e2853d9 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -153,6 +153,32 @@ OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) { return (rai->getOption(option_code_)); } +OptionPtr TokenRelay6Option::getOption(const Pkt& pkt) { + + try { + // Check if it's a Pkt6. If it's not the dynamic_cast will + // throw std::bad_cast. + const Pkt6& pkt6 = dynamic_cast(pkt); + + try { + // Now that we have the right type of packet we can + // get the option and return it. + return(pkt6.getRelayOption(option_code_, nest_level_)); + } + catch (const isc::OutOfRange&) { + // The only exception we expect is OutOfRange if the nest + // level is out of range of the encapsulations, for example + // if nest_level_ is 4 and there are only 2 encapsulations. + // We return a NULL in that case. + return (OptionPtr()); + } + + } catch (const std::bad_cast&) { + isc_throw(EvalTypeError, "Specified packet is not Pkt6"); + } + +} + void TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) { @@ -238,6 +264,111 @@ TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) { value.end()))); } +void +TokenPkt6::evaluate(const Pkt& pkt, ValueStack& values) { + + vector binary; + string type_str; + try { + // Check if it's a Pkt6. If it's not the dynamic_cast will throw + // std::bad_cast (failed dynamic_cast returns NULL for pointers and + // throws for references). + const Pkt6& pkt6 = dynamic_cast(pkt); + + switch (type_) { + case MSGTYPE: { + // msg type is an uint8_t integer. We want a 4 byte string so 0 pad. + binary.push_back(0); + binary.push_back(0); + binary.push_back(0); + binary.push_back(pkt6.getType()); + type_str = "msgtype"; + break; + } + case TRANSID: { + // transaction id is an uint32_t integer. We want a 4 byte string so copy + uint32_t transid = pkt6.getTransid(); + binary.push_back(transid >> 24); + binary.push_back((transid >> 16) & 0xFF); + binary.push_back((transid >> 8) & 0xFF); + binary.push_back(transid & 0xFF); + type_str = "transid"; + break; + } + default: + isc_throw(EvalTypeError, "Bad field specified: " + << static_cast(type_) ); + } + + } catch (const std::bad_cast&) { + isc_throw(EvalTypeError, "Specified packet is not Pkt6"); + } + + string value; + value.resize(binary.size()); + memmove(&value[0], &binary[0], binary.size()); + values.push(value); + + // Log what we pushed + LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT6) + .arg(type_str) + .arg("0x" + util::encode::encodeHex(std::vector(value.begin(), + value.end()))); +} + +void +TokenRelay6Field::evaluate(const Pkt& pkt, ValueStack& values) { + + vector binary; + string type_str; + try { + // Check if it's a Pkt6. If it's not the dynamic_cast will + // throw std::bad_cast. + const Pkt6& pkt6 = dynamic_cast(pkt); + + try { + switch (type_) { + // Now that we have the right type of packet we can + // get the option and return it. + case LINKADDR: + type_str = "linkaddr"; + binary = pkt6.getRelay6LinkAddress(nest_level_).toBytes(); + break; + case PEERADDR: + type_str = "peeraddr"; + binary = pkt6.getRelay6PeerAddress(nest_level_).toBytes(); + break; + } + } catch (const isc::OutOfRange&) { + // The only exception we expect is OutOfRange if the nest + // level is invalid. We push "" in that case. + values.push(""); + // Log what we pushed + LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE) + .arg(type_str) + .arg(unsigned(nest_level_)) + .arg("0x"); + return; + } + } catch (const std::bad_cast&) { + isc_throw(EvalTypeError, "Specified packet is not Pkt6"); + } + + string value; + value.resize(binary.size()); + if (!binary.empty()) { + memmove(&value[0], &binary[0], binary.size()); + } + values.push(value); + + // Log what we pushed + LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6) + .arg(type_str) + .arg(unsigned(nest_level_)) + .arg("0x" + util::encode::encodeHex(std::vector(value.begin(), + value.end()))); +} + void TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) { @@ -468,134 +599,3 @@ TokenOr::evaluate(const Pkt& /*pkt*/, ValueStack& values) { .arg('\'' + op2 + '\'') .arg('\'' + values.top() + '\''); } - -OptionPtr TokenRelay6Option::getOption(const Pkt& pkt) { - - try { - // Check if it's a Pkt6. If it's not the dynamic_cast will - // throw std::bad_cast. - const Pkt6& pkt6 = dynamic_cast(pkt); - - try { - // Now that we have the right type of packet we can - // get the option and return it. - return(pkt6.getRelayOption(option_code_, nest_level_)); - } - catch (const isc::OutOfRange&) { - // The only exception we expect is OutOfRange if the nest - // level is out of range of the encapsulations, for example - // if nest_level_ is 4 and there are only 2 encapsulations. - // We return a NULL in that case. - return (OptionPtr()); - } - - } catch (const std::bad_cast&) { - isc_throw(EvalTypeError, "Specified packet is not Pkt6"); - } - -} - -void -TokenRelay6Field::evaluate(const Pkt& pkt, ValueStack& values) { - - vector binary; - string type_str; - try { - // Check if it's a Pkt6. If it's not the dynamic_cast will - // throw std::bad_cast. - const Pkt6& pkt6 = dynamic_cast(pkt); - - try { - switch (type_) { - // Now that we have the right type of packet we can - // get the option and return it. - case LINKADDR: - type_str = "linkaddr"; - binary = pkt6.getRelay6LinkAddress(nest_level_).toBytes(); - break; - case PEERADDR: - type_str = "peeraddr"; - binary = pkt6.getRelay6PeerAddress(nest_level_).toBytes(); - break; - } - } catch (const isc::OutOfRange&) { - // The only exception we expect is OutOfRange if the nest - // level is invalid. We push "" in that case. - values.push(""); - // Log what we pushed - LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE) - .arg(type_str) - .arg(unsigned(nest_level_)) - .arg("0x"); - return; - } - } catch (const std::bad_cast&) { - isc_throw(EvalTypeError, "Specified packet is not Pkt6"); - } - - string value; - value.resize(binary.size()); - if (!binary.empty()) { - memmove(&value[0], &binary[0], binary.size()); - } - values.push(value); - - // Log what we pushed - LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6) - .arg(type_str) - .arg(unsigned(nest_level_)) - .arg("0x" + util::encode::encodeHex(std::vector(value.begin(), - value.end()))); -} - -void -TokenPkt6::evaluate(const Pkt& pkt, ValueStack& values) { - - vector binary; - string type_str; - try { - // Check if it's a Pkt6. If it's not the dynamic_cast will throw - // std::bad_cast (failed dynamic_cast returns NULL for pointers and - // throws for references). - const Pkt6& pkt6 = dynamic_cast(pkt); - - switch (type_) { - case MSGTYPE: { - // msg type is an uint8_t integer. We want a 4 byte string so 0 pad. - binary.push_back(0); - binary.push_back(0); - binary.push_back(0); - binary.push_back(pkt6.getType()); - type_str = "msgtype"; - break; - } - case TRANSID: { - // transaction id is an uint32_t integer. We want a 4 byte string so copy - uint32_t transid = pkt6.getTransid(); - binary.push_back(transid >> 24); - binary.push_back((transid >> 16) & 0xFF); - binary.push_back((transid >> 8) & 0xFF); - binary.push_back(transid & 0xFF); - type_str = "transid"; - break; - } - default: - isc_throw(EvalTypeError, "Bad field specified: " - << static_cast(type_) ); - } - - } catch (const std::bad_cast&) { - isc_throw(EvalTypeError, "Specified packet is not Pkt6"); - } - - string value; - value.resize(binary.size()); - memmove(&value[0], &binary[0], binary.size()); - values.push(value); - - // Log what we pushed - LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT6) - .arg(type_str) - .arg("0x" + util::encode::encodeHex(std::vector(value.begin(), - value.end()))); -} diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index d51ed6d1ec..0f770036c0 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -102,7 +102,7 @@ public: /// The order where Token subtypes are declared should be: /// - literal terminals /// - option & co -/// - pkt & co +/// - pkt field & co /// - == /// - substring & co /// - not, and, or @@ -288,6 +288,52 @@ protected: virtual OptionPtr getOption(const Pkt& pkt); }; +/// @brief Token that represents a value of an option within a DHCPv6 relay +/// encapsulation +/// +/// This represents a reference to a given option similar to TokenOption +/// but from within the information from a relay. In the expresssion +/// relay6[nest-level].option[option-code], nest-level indicates which +/// of the relays to examine and option-code which option to extract. +/// +/// During the evaluation it tries to extract the value of the specified +/// option from the requested relay block. If the relay block doesn't +/// exist or the option is not found an empty string ("") is returned +/// (or "false" when the representation is EXISTS). +/// +/// The nesting level can go from 0 (closest to the server) to 31 +class TokenRelay6Option : public TokenOption { +public: + /// @brief Constructor that takes a nesting level and an option + /// code as paramaters. + /// + /// @param nest_level the nesting for which relay to examine. + /// @param option_code code of the option. + /// @param rep_type Token representation type. + TokenRelay6Option(const uint8_t nest_level, const uint16_t option_code, + const RepresentationType& rep_type) + :TokenOption(option_code, rep_type), nest_level_(nest_level) {} + + /// @brief Returns nest-level + /// + /// This method is used in testing to determine if the parser has + /// instantiated TokenRelay6Option with correct parameters. + /// + /// @return nest-level of the relay block this token expects to use + /// for extraction. + uint8_t getNest() const { + return (nest_level_); + } + +protected: + /// @brief Attempts to obtain specified option from the specified relay block + /// @param pkt DHCPv6 packet that hopefully contains the proper relay block + /// @return option instance if available + virtual OptionPtr getOption(const Pkt& pkt); + + uint8_t nest_level_; ///< nesting level of the relay block to use +}; + /// @brief Token that represents fields of a DHCPv4 packet. /// /// For example in the expression pkt4.chaddr == 0x0102030405 @@ -343,6 +389,119 @@ private: FieldType type_; }; +/// @brief Token that represents fields of DHCPv6 packet. +/// +/// For example in the expression pkt6.msgtype == 1 +/// this token represents the message type of the DHCPv6 packet. +/// The integer values are placed on the value stack as 4 byte +/// strings. +/// +/// Currently supported fields are: +/// - msgtype +/// - transid +class TokenPkt6 : public Token { +public: + /// @brief enum value that determines the field. + enum FieldType { + MSGTYPE, ///< msg type + TRANSID ///< transaction id (integer but manipulated as a string) + }; + + /// @brief Constructor (does nothing) + TokenPkt6(const FieldType type) + : type_(type) {} + + /// @brief Gets a value of the specified packet. + /// + /// The evaluation uses fields that are availabe in the packet. It does not + /// require any values to be present on the stack. + /// + /// @throw EvalTypeError when called for a DHCPv4 packet + /// + /// @param pkt - packet from which to extract the fields + /// @param values - stack of values, 1 result will be pushed + void evaluate(const Pkt& pkt, ValueStack& values); + + /// @brief Returns field type + /// + /// This method is used only in tests. + /// @return type of the field. + FieldType getType() { + return(type_); + } + +private: + /// @brief Specifies field of the DHCPv6 packet to get + FieldType type_; +}; + +/// @brief Token that represents a value of a field within a DHCPv6 relay +/// encapsulation +/// +/// This represents a reference to a field with a given DHCPv6 relay encapsulation. +/// In the expression relay6[nest-level].field-name, nest-level indicates which of +/// the relays to examine and field-name which of the fields to extract. +/// +/// During the evaluation it tries to extract the value of the specified +/// field from the requested relay block. If the relay block doesn't exist +/// an empty string ("") is returned. If the relay block does exist the field +/// is always returned as a 16 byte IPv6 address. As the relay may not have +/// set the field it may be 0s. +/// +/// The nesting level can go from 0 (closest to the server) to 31. +class TokenRelay6Field : public Token { +public: + + /// @brief enum value that determines the field. + enum FieldType { + PEERADDR, ///< Peer address field (IPv6 address) + LINKADDR ///< Link address field (IPv6 address) + }; + + /// @brief Constructor that takes a nesting level and field type + /// as parameters. + /// + /// @param nest_level the nesting level for which relay to examine. + /// @param type which field to extract. + TokenRelay6Field(const uint8_t nest_level, const FieldType type) + : nest_level_(nest_level), type_(type) {} + + /// @brief Extracts the specified field from the requested relay + /// + /// Evaluation uses fields available in the packet. It does not require + /// any values to be present on the stack. + /// + /// @param pkt fields will be extracted from here + /// @param values - stack of values (1 result will be pushed) + void evaluate(const Pkt& pkt, ValueStack& values); + + /// @brief Returns nest-level + /// + /// This method is used in testing to determine if the parser has + /// instantiated TokenRelay6Field with correct parameters. + /// + /// @return nest-level of the relay block this token expects to use + /// for extraction. + uint8_t getNest() const { + return (nest_level_); + } + + /// @brief Returns field type + /// + /// This method is used only in testing to determine if the parser has + /// instantiated TokenRelay6Field with correct parameters. + /// + /// @return type of the field. + FieldType getType() { + return (type_); + } + +protected: + /// @brief Specifies field of the DHCPv6 relay option to get + uint8_t nest_level_; ///< nesting level of the relay block to use + FieldType type_; ///< field to get +}; + /// @brief Token that represents equality operator (compares two other tokens) /// /// For example in the expression option[vendor-class].text == "MSFT" @@ -522,165 +681,6 @@ public: void evaluate(const Pkt& pkt, ValueStack& values); }; -/// @brief Token that represents a value of an option within a DHCPv6 relay -/// encapsulation -/// -/// This represents a reference to a given option similar to TokenOption -/// but from within the information from a relay. In the expresssion -/// relay6[nest-level].option[option-code], nest-level indicates which -/// of the relays to examine and option-code which option to extract. -/// -/// During the evaluation it tries to extract the value of the specified -/// option from the requested relay block. If the relay block doesn't -/// exist or the option is not found an empty string ("") is returned -/// (or "false" when the representation is EXISTS). -/// -/// The nesting level can go from 0 (closest to the server) to 31 -class TokenRelay6Option : public TokenOption { -public: - /// @brief Constructor that takes a nesting level and an option - /// code as paramaters. - /// - /// @param nest_level the nesting for which relay to examine. - /// @param option_code code of the option. - /// @param rep_type Token representation type. - TokenRelay6Option(const uint8_t nest_level, const uint16_t option_code, - const RepresentationType& rep_type) - :TokenOption(option_code, rep_type), nest_level_(nest_level) {} - - /// @brief Returns nest-level - /// - /// This method is used in testing to determine if the parser has - /// instantiated TokenRelay6Option with correct parameters. - /// - /// @return nest-level of the relay block this token expects to use - /// for extraction. - uint8_t getNest() const { - return (nest_level_); - } - -protected: - /// @brief Attempts to obtain specified option from the specified relay block - /// @param pkt DHCPv6 packet that hopefully contains the proper relay block - /// @return option instance if available - virtual OptionPtr getOption(const Pkt& pkt); - - uint8_t nest_level_; ///< nesting level of the relay block to use -}; - -/// @brief Token that represents a value of a field within a DHCPv6 relay -/// encapsulation -/// -/// This represents a reference to a field with a given DHCPv6 relay encapsulation. -/// In the expression relay6[nest-level].field-name, nest-level indicates which of -/// the relays to examine and field-name which of the fields to extract. -/// -/// During the evaluation it tries to extract the value of the specified -/// field from the requested relay block. If the relay block doesn't exist -/// an empty string ("") is returned. If the relay block does exist the field -/// is always returned as a 16 byte IPv6 address. As the relay may not have -/// set the field it may be 0s. -/// -/// The nesting level can go from 0 (closest to the server) to 31. -class TokenRelay6Field : public Token { -public: - - /// @brief enum value that determines the field. - enum FieldType { - PEERADDR, ///< Peer address field (IPv6 address) - LINKADDR ///< Link address field (IPv6 address) - }; - - /// @brief Constructor that takes a nesting level and field type - /// as parameters. - /// - /// @param nest_level the nesting level for which relay to examine. - /// @param type which field to extract. - TokenRelay6Field(const uint8_t nest_level, const FieldType type) - : nest_level_(nest_level), type_(type) {} - - /// @brief Extracts the specified field from the requested relay - /// - /// Evaluation uses fields available in the packet. It does not require - /// any values to be present on the stack. - /// - /// @param pkt fields will be extracted from here - /// @param values - stack of values (1 result will be pushed) - void evaluate(const Pkt& pkt, ValueStack& values); - - /// @brief Returns nest-level - /// - /// This method is used in testing to determine if the parser has - /// instantiated TokenRelay6Field with correct parameters. - /// - /// @return nest-level of the relay block this token expects to use - /// for extraction. - uint8_t getNest() const { - return (nest_level_); - } - - /// @brief Returns field type - /// - /// This method is used only in testing to determine if the parser has - /// instantiated TokenRelay6Field with correct parameters. - /// - /// @return type of the field. - FieldType getType() { - return (type_); - } - -protected: - /// @brief Specifies field of the DHCPv6 relay option to get - uint8_t nest_level_; ///< nesting level of the relay block to use - FieldType type_; ///< field to get -}; - -/// @brief Token that represents fields of DHCPv6 packet. -/// -/// For example in the expression pkt6.msgtype == 1 -/// this token represents the message type of the DHCPv6 packet. -/// The integer values are placed on the value stack as 4 byte -/// strings. -/// -/// Currently supported fields are: -/// - msgtype -/// - transid -class TokenPkt6 : public Token { -public: - /// @brief enum value that determines the field. - enum FieldType { - MSGTYPE, ///< msg type - TRANSID ///< transaction id (integer but manipulated as a string) - }; - - /// @brief Constructor (does nothing) - TokenPkt6(const FieldType type) - : type_(type) {} - - /// @brief Gets a value of the specified packet. - /// - /// The evaluation uses fields that are availabe in the packet. It does not - /// require any values to be present on the stack. - /// - /// @throw EvalTypeError when called for a DHCPv4 packet - /// - /// @param pkt - packet from which to extract the fields - /// @param values - stack of values, 1 result will be pushed - void evaluate(const Pkt& pkt, ValueStack& values); - - /// @brief Returns field type - /// - /// This method is used only in tests. - /// @return type of the field. - FieldType getType() { - return(type_); - } - -private: - /// @brief Specifies field of the DHCPv6 packet to get - FieldType type_; -}; - }; // end of isc::dhcp namespace }; // end of isc namespace