Parcourir la source

[4272a] Updated classification doc & reordered code

Francis Dupont il y a 9 ans
Parent
commit
7ac1fdee01

+ 17 - 3
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.
 

+ 1 - 1
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");
     }

+ 5 - 6
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));
 <<EOF>>    return isc::eval::EvalParser::make_END(loc);
 %%

+ 44 - 32
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 <std::string> 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,

+ 127 - 127
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<TokenRelay6Option> opt =
+            boost::dynamic_pointer_cast<TokenRelay6Option>(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<TokenSubstring> sub =
-            boost::dynamic_pointer_cast<TokenSubstring>(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<TokenConcat> conc =
-            boost::dynamic_pointer_cast<TokenConcat>(token);
-        EXPECT_TRUE(conc);
-    }
+        boost::shared_ptr<TokenPkt6> pkt =
+            boost::dynamic_pointer_cast<TokenPkt6>(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<TokenRelay6Option> opt =
-            boost::dynamic_pointer_cast<TokenRelay6Option>(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<TokenSubstring> sub =
+            boost::dynamic_pointer_cast<TokenSubstring>(token);
+        EXPECT_TRUE(sub);
+    }
 
-        boost::shared_ptr<TokenPkt6> pkt =
-            boost::dynamic_pointer_cast<TokenPkt6>(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<TokenConcat> conc =
+            boost::dynamic_pointer_cast<TokenConcat>(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) {
                "<string>: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) {

+ 365 - 364
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());
 }
+
 };

+ 131 - 131
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<const Pkt6&>(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) {
 
@@ -239,6 +265,111 @@ TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) {
 }
 
 void
+TokenPkt6::evaluate(const Pkt& pkt, ValueStack& values) {
+
+    vector<uint8_t> 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<const Pkt6&>(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<int>(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<uint8_t>(value.begin(),
+                                                                 value.end())));
+}
+
+void
+TokenRelay6Field::evaluate(const Pkt& pkt, ValueStack& values) {
+
+    vector<uint8_t> 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<const Pkt6&>(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<uint8_t>(value.begin(),
+                                                                 value.end())));
+}
+
+void
 TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
 
     if (values.size() < 2) {
@@ -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<const Pkt6&>(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<uint8_t> 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<const Pkt6&>(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<uint8_t>(value.begin(),
-                                                                 value.end())));
-}
-
-void
-TokenPkt6::evaluate(const Pkt& pkt, ValueStack& values) {
-
-    vector<uint8_t> 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<const Pkt6&>(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<int>(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<uint8_t>(value.begin(),
-                                                                 value.end())));
-}

+ 160 - 160
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