Browse Source

[trac4265] Add test code, tidy up main code

Shawn Routhier 9 years ago
parent
commit
7e468146f1

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+1xxx.	[func]		sar
+	Added access to the peer address, link address and option
+	information added by relays in a DHCPv6 message.
+	(Trac $4269, git tbd)
+
 1098.	[func]		kalmus, marcin
 	Implemented IPv6 address/prefix reservations in MySQL.
 	(Trac #4212, git 79481043935789fc6898d4743bede1606f82eb75)

+ 33 - 1
doc/guide/classify.xml

@@ -170,6 +170,24 @@
 sub-option</entry><entry>relay4[code].hex</entry><entry>The value of
 sub-option with code "code" from the DHCPv4 Relay Agent Information option
 (option 82)</entry></row>
+<row>
+  <entry>DHCPv6 Relay Options</entry>
+  <entry>relay6[nest].option[code].hex</entry>
+<!--  <entry>Value of the option</entry> -->
+  <entry>The value of the option with code "code" from the relay encapsulation "nest"</entry>
+</row>
+<row>
+  <entry>DHCPv6 Relay Peer Address</entry>
+  <entry>relay6[nest].peeraddr</entry>
+<!-- <entry>2001:DB8::1</entry> -->n
+  <entry>The value of the peer address field from the relay encapsulation "nest"</entry>
+</row>
+<row>
+  <entry>DHCPv6 Relay Link Address</entry>
+  <entry>relay6[nest].linkaddr</entry>
+<!-- <entry>2001:DB8::1</entry> -->n
+  <entry>The value of the link address field from the relay encapsulation "nest"</entry>
+</row>
           </tbody>
           </tgroup>
         </table>
@@ -211,11 +229,25 @@ sub-option with code "code" from the DHCPv4 Relay Agent Information option
       </para>
 
       <para>
-       "relay4" shares the same representation types than "option", for
+       "relay4" shares the same representation types as "option", for
        instance "relay4[code].exists" is supported.
       </para>
 
       <para>
+       "relay6[nest]" allows access to the encapsulations used by any DHCPv6
+       relays that forwarded the packet.  The "nest" level specifies the relay
+       from which to extract the information, with a value of 0 indicating
+       the relay closest to the DHCPv6 server.  If the requested encapsulation
+       doesn't exist an empty string "" is returned.  This expression is
+       allowed in DHCPv6 only.
+      </para>
+
+      <para>
+       "relay6[nest].option[code]" shares the same representation types as
+       "option", for instance "relay6[nest].option[code].exists" is supported.
+      </para>
+
+      <para>
         <table frame="all" id="classification-expressions-list">
           <title>List of Classification Expressions</title>
           <tgroup cols='3'>

+ 21 - 1
src/lib/dhcp/pkt6.cc

@@ -119,13 +119,33 @@ OptionPtr Pkt6::getRelayOption(uint16_t opt_type, uint8_t relay_level) const {
     }
 
     OptionCollection::const_iterator x = relay_info_[relay_level].options_.find(opt_type);
-    if (x != options_.end()) {
+    if (x != relay_info_[relay_level].options_.end()) {
 	return (*x).second;
       }
 
     return (OptionPtr());
 }
 
+const isc::asiolink::IOAddress&
+Pkt6::getRelay6LinkAddress(uint8_t relay_level) const {
+    if (relay_level >= relay_info_.size()) {
+        isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
+                  << " There is no info about " << relay_level + 1 << " relay.");
+    }
+
+    return (relay_info_[relay_level].linkaddr_);
+}
+
+const isc::asiolink::IOAddress&
+Pkt6::getRelay6PeerAddress(uint8_t relay_level) const {
+    if (relay_level >= relay_info_.size()) {
+        isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
+                  << " There is no info about " << relay_level + 1 << " relay.");
+    }
+
+    return (relay_info_[relay_level].peeraddr_);
+}
+
 uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) const {
     uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
         + Option::OPTION6_HDR_LEN; // header of the relay-msg option

+ 35 - 2
src/lib/dhcp/pkt6.h

@@ -253,6 +253,39 @@ public:
     /// @return option pointer (or NULL if no option matches specified criteria)
     OptionPtr getAnyRelayOption(uint16_t option_code, RelaySearchOrder order);
 
+    /// @brief return the link address field from a relay option
+    ///
+    /// As with @c Pkt6::getRelayOption this returns information from the
+    /// specified relay scope.  The relay_level specifies which relay
+    /// scope is to be used.  0 is the outermost encapsulation (relay closest
+    /// to the server).  pkt->relay_info_.size() -1 is the innermost encapsulation
+    /// (relay closest to the client).
+    ///
+    /// @throw isc::OutOfRange if relay level has an invalid value.
+    ///
+    /// @param relay_level see description above
+    ///
+    /// @return pointer to the link address field
+    const isc::asiolink::IOAddress&
+    getRelay6LinkAddress(uint8_t relay_level) const;
+
+    /// @brief return the peer address field from a relay option
+    ///
+    /// As with @c Pkt6::getRelayOption this returns information from the
+    /// specified relay scope.  The relay_level specifies which relay
+    /// scope is to be used.  0 is the outermost encapsulation (relay closest
+    /// to the server).  pkt->relay_info_.size() -1 is the innermost encapsulation
+    /// (relay closest to the client).
+    ///
+    /// @throw isc::OutOfRange if relay level has an invalid value.
+    ///
+    /// @param relay_level see description above
+    ///
+    /// @return pointer to the peer address field
+    const isc::asiolink::IOAddress&
+    getRelay6PeerAddress(uint8_t relay_level) const;
+
+    /// 
     /// @brief Returns all instances of specified type.
     ///
     /// Returns all instances of options of the specified type. DHCPv6 protocol
@@ -279,13 +312,13 @@ public:
     /// @param type DHCPv6 message type which name should be returned.
     ///
     /// @return Pointer to "const" string containing the message name. If
-    /// the message type is unknnown the "UNKNOWN" is returned. The caller
+    /// the message type is unknown the "UNKNOWN" is returned. The caller
     /// must not release the returned pointer.
     static const char* getName(const uint8_t type);
 
     /// @brief Returns name of the DHCPv6 message.
     ///
-    /// This method requires an object. There is also static version, which
+    /// This method requires an object. There is also a static version, which
     /// requires one parameter (type).
     ///
     /// @return Pointer to "const" string containing the message name. If

+ 11 - 1
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -763,7 +763,7 @@ TEST_F(Pkt6Test, relayPack) {
 
     Pkt6::RelayInfo relay1;
     relay1.msg_type_ = DHCPV6_RELAY_REPL;
-    relay1.hop_count_ = 17; // not very miningful, but useful for testing
+    relay1.hop_count_ = 17; // not very meaningful, but useful for testing
     relay1.linkaddr_ = IOAddress("2001:db8::1");
     relay1.peeraddr_ = IOAddress("fe80::abcd");
 
@@ -830,6 +830,16 @@ TEST_F(Pkt6Test, relayPack) {
     OptionBuffer data = opt->getData();
     ASSERT_EQ(data.size(), sizeof(relay_opt_data));
     EXPECT_EQ(0, memcmp(&data[0], relay_opt_data, sizeof(relay_opt_data)));
+
+    // As we have a nicely built relay packet we can check
+    // that the functions to get the peer and link addreses work
+    EXPECT_EQ("2001:db8::1", clone->getRelay6LinkAddress(0).toText());
+    EXPECT_EQ("fe80::abcd", clone->getRelay6PeerAddress(0).toText());
+
+    vector<uint8_t>binary = clone->getRelay6LinkAddress(0).toBytes();
+    uint8_t expected0[] = {0x20, 1, 0x0d, 0xb8, 0, 0, 0, 0,
+                           0, 0, 0, 0, 0, 0, 0, 1};
+    EXPECT_EQ(0, memcmp(expected0, &binary[0], 16));
 }
 
 

+ 145 - 0
src/lib/eval/tests/context_unittest.cc

@@ -148,6 +148,114 @@ public:
         EXPECT_TRUE(conc);
     }
 
+    /// @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 a TokenRelay with the
+    /// correct nesting level and field type.
+    /// @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 checkTokenRelay6(const TokenPtr& token,
+                          uint8_t expected_level,
+                          TokenRelay6::FieldType expected_type) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenRelay6> opt =
+            boost::dynamic_pointer_cast<TokenRelay6>(token);
+        ASSERT_TRUE(opt);
+
+        EXPECT_EQ(expected_level, opt->getNest());
+        EXPECT_EQ(expected_type, opt->getType());
+    }
+
+    /// @brief This tests attempts to parse the expression then checks
+    /// if the number of tokens is correct and the TokenRelay6 is as
+    /// expected.
+    ///
+    /// @param expr expression to be parsed
+    /// @param exp_level expected level to be parsed
+    /// @param exp_type expected field type to be parsed
+    /// @param exp_tokens expected number of tokens
+    void testRelay6Field(std::string expr,
+                         uint8_t exp_level,
+                         TokenRelay6::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 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 TokenRelay6 and that
+        // is has the correct attributes
+        checkTokenRelay6(eval.expression.at(0), exp_level, exp_type);
+    }
+
     /// @brief checks if the given expression raises the expected message
     /// when it is parsed.
     void checkError(const string& expr, const string& msg) {
@@ -576,6 +684,43 @@ 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, TokenRelay6::LINKADDR, 3);
+}
+
+// Tests if the peeraddr field in a Relay6 encapsulation can be accessed.
+TEST_F(EvalContextTest, relay6FieldPeerAddr) {
+    testRelay6Field("relay6[1].peeraddr == ::",
+                    1, TokenRelay6::PEERADDR, 3);
+}
+
+//
 // Test some scanner error cases
 TEST_F(EvalContextTest, scanErrors) {
     checkError("'", "<string>:1.1: Invalid character: '");

+ 191 - 0
src/lib/eval/tests/token_unittest.cc

@@ -62,6 +62,111 @@ public:
         pkt4_->addOption(rai);
     }
 
+    /// @brief Adds relay encapsulations with some suboptions
+    ///
+    /// This will add 2 relay encapsulations all will have
+    /// msg_type of RELAY_FORW
+    /// Relay 0 (closest to server) will have
+    /// linkaddr = peeraddr = 0, hop-count = 1
+    /// option 100 "hundred.zero", option 101 "hundredone.zero"
+    /// Relay 1 (closest to client) will have
+    /// linkaddr 1::1= peeraddr = 1::2, hop-count = 0
+    /// option 100 "hundred.one", option 102 "hundredtwo.one"
+    void addRelay6Encapsulations() {
+        // First relay
+        Pkt6::RelayInfo relay0;
+        relay0.msg_type_ = DHCPV6_RELAY_FORW;
+        relay0.hop_count_ = 1;
+        relay0.linkaddr_ = isc::asiolink::IOAddress("::");
+        relay0.peeraddr_ = isc::asiolink::IOAddress("::");
+        OptionPtr optRelay01(new OptionString(Option::V6, 100,
+                                              "hundred.zero"));
+        OptionPtr optRelay02(new OptionString(Option::V6, 101,
+                                              "hundredone.zero"));
+
+        relay0.options_.insert(make_pair(optRelay01->getType(), optRelay01));
+        relay0.options_.insert(make_pair(optRelay02->getType(), optRelay02));
+
+        pkt6_->addRelayInfo(relay0);
+        // Second relay
+        Pkt6::RelayInfo relay1;
+        relay1.msg_type_ = DHCPV6_RELAY_FORW;
+        relay1.hop_count_ = 0;
+        relay1.linkaddr_ = isc::asiolink::IOAddress("1::1");
+        relay1.peeraddr_ = isc::asiolink::IOAddress("1::2");
+        OptionPtr optRelay11(new OptionString(Option::V6, 100,
+                                              "hundred.one"));
+        OptionPtr optRelay12(new OptionString(Option::V6, 102,
+                                              "hundredtwo.one"));
+
+        relay1.options_.insert(make_pair(optRelay11->getType(), optRelay11));
+        relay1.options_.insert(make_pair(optRelay12->getType(), optRelay12));
+        pkt6_->addRelayInfo(relay1);
+    }
+
+    /// @brief Verify that the relay6 option evaluatiosn work properly
+    ///
+    /// Given the nesting level and option code extract the option
+    /// and compare it to the expected string.
+    ///
+    /// @param test_level The nesting level
+    /// @param test_code The code of the option to extract
+    /// @param result_addr The expected result of the address as a string
+    void verifyRelay6Option(const uint8_t test_level,
+                            const uint16_t test_code,
+                       	    const TokenOption::RepresentationType& test_rep,
+                            const std::string& result_string) {
+        // Create the token
+        ASSERT_NO_THROW(t_.reset(new TokenRelay6Option(test_level,
+                                                       test_code,
+                                                       test_rep)));
+
+        // We should be able to evaluate it
+        EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+
+        // We should have one value on the stack
+        ASSERT_EQ(1, values_.size());
+
+        // And it should match the expected result
+        // Invalid nesting levels result in a 0 length string
+        EXPECT_EQ(result_string, values_.top());
+
+        // Then we clear the stack
+        clearStack();
+    }
+
+    /// @brief Verify that the relay6 field evaluations work properly
+    ///
+    /// Given the nesting level, the field to extract and the expected
+    /// address create a token and evaluate it then compare the addresses
+    ///
+    /// @param test_level The nesting level
+    /// @param test_field The type of the field to extract
+    /// @param result_addr The expected result of the address as a string
+    void verifyRelay6Eval(const uint8_t test_level,
+                          const TokenRelay6::FieldType test_field,
+                          const int result_len,
+                          const uint8_t result_addr[]) {
+        // Create the token
+        ASSERT_NO_THROW(t_.reset(new TokenRelay6(test_level, test_field)));
+
+        // We should be able to evaluate it
+        EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
+
+        // We should have one value on the stack
+        ASSERT_EQ(1, values_.size());
+
+        // And it should match the expected result
+        // Invalid nesting levels result in a 0 length string
+        EXPECT_EQ(result_len, values_.top().size());
+        if (result_len != 0) {
+            EXPECT_EQ(0, memcmp(result_addr, &values_.top()[0], result_len));
+        }
+
+        // Then we clear the stack
+        clearStack();
+    }
+
     /// @brief Convenience function. Removes token and values stacks.
     void clearStack() {
         while (!values_.empty()) {
@@ -1012,3 +1117,89 @@ TEST_F(TokenTest, concat) {
     ASSERT_EQ(1, values_.size());
     EXPECT_EQ("foobar", values_.top());
 }
+
+// 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, TokenRelay6::LINKADDR, 16, zeroaddr);
+    verifyRelay6Eval(0, TokenRelay6::PEERADDR, 16, zeroaddr);
+
+    // Level 1 link and peer should have different non-zero addresses
+    verifyRelay6Eval(1, TokenRelay6::LINKADDR, 16, linkaddr);
+    verifyRelay6Eval(1, TokenRelay6::PEERADDR, 16, peeraddr);
+
+    // Level 2 has no encapsulation so the address should be zero length
+    verifyRelay6Eval(2, TokenRelay6::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 TokenRelay6(1, TokenRelay6::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();
+}
+
+// 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, "");
+}

+ 35 - 3
src/lib/eval/token.cc

@@ -322,7 +322,39 @@ OptionPtr TokenRelay6Option::getOption(const Pkt& pkt) {
 }
 
 void
-TokenRelay6::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
-  // test routine, need to add code in pkt6 to get the proper fields
-  values.push("");
+TokenRelay6::evaluate(const Pkt& pkt, ValueStack& values) {
+
+    vector<uint8_t> binary;
+    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:
+                binary = pkt6.getRelay6LinkAddress(nest_level_).toBytes();
+                break;
+            case 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("");
+            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);
 }

+ 13 - 2
src/lib/eval/token.h

@@ -490,7 +490,7 @@ public:
     /// @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)
+                      const RepresentationType& rep_type)
         :TokenOption(option_code, rep_type), nest_level_(nest_level) {}
 
     /// @brief Returns nest-level
@@ -529,7 +529,7 @@ protected:
 /// The nesting level can go from 0 (closest to the server) to 31.
 class TokenRelay6 : public Token {
 public:
-  
+
     /// @brief enum value that determines the field.
     enum FieldType {
         PEERADDR, ///< Peer address field (IPv6 address)
@@ -553,6 +553,17 @@ public:
     /// @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 TokenRelay6 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