Browse Source

[master] Merged trac5287 (count DHCPv6 relay options both ways)

Francis Dupont 7 years ago
parent
commit
5ae32bc1e4

+ 6 - 3
doc/guide/classify.xml

@@ -484,9 +484,12 @@
       "relay6[nest]" allows access to the encapsulations used by any DHCPv6
       "relay6[nest]" allows access to the encapsulations used by any DHCPv6
       relays that forwarded the packet.  The "nest" level specifies the relay
       relays that forwarded the packet.  The "nest" level specifies the relay
       from which to extract the information, with a value of 0 indicating
       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.
+      the relay closest to the DHCPv6 server. Negative values allow to
+      specify relays counted from the DHCPv6 client, -1 indicating the
+      relay closest to the client. In general negative "nest" level is
+      the same as the number of relays + "nest" level.
+      If the requested encapsulation doesn't exist an empty string ""
+      is returned.  This expression is allowed in DHCPv6 only.
       </para></listitem>
       </para></listitem>
 
 
       <listitem><para>
       <listitem><para>

+ 24 - 5
src/lib/eval/eval_context.cc

@@ -96,15 +96,15 @@ EvalContext::convertOptionName(const std::string& option_name,
     return (option_def->getCode());
     return (option_def->getCode());
 }
 }
 
 
-uint8_t
+int8_t
 EvalContext::convertNestLevelNumber(const std::string& nest_level,
 EvalContext::convertNestLevelNumber(const std::string& nest_level,
                                     const isc::eval::location& loc)
                                     const isc::eval::location& loc)
 {
 {
-    uint8_t n = convertUint8(nest_level, loc);
+    int8_t n = convertInt8(nest_level, loc);
     if (option_universe_ == Option::V6) {
     if (option_universe_ == Option::V6) {
-        if (n >= HOP_COUNT_LIMIT) {
+        if ((n < - HOP_COUNT_LIMIT) || (n >= HOP_COUNT_LIMIT)) {
             error(loc, "Nest level has invalid value in "
             error(loc, "Nest level has invalid value in "
-                      + nest_level + ". Allowed range: 0..31");
+                      + nest_level + ". Allowed range: -32..31");
         }
         }
     } else {
     } else {
         error(loc, "Nest level invalid for DHCPv4 packets");
         error(loc, "Nest level invalid for DHCPv4 packets");
@@ -123,7 +123,26 @@ EvalContext::convertUint8(const std::string& number,
     } catch (const boost::bad_lexical_cast &) {
     } catch (const boost::bad_lexical_cast &) {
         error(loc, "Invalid integer value in " + number);
         error(loc, "Invalid integer value in " + number);
     }
     }
-    if (n < 0 || n >= std::numeric_limits<uint8_t>::max()) {
+    if (n < 0 || n > std::numeric_limits<uint8_t>::max()) {
+        error(loc, "Invalid value in "
+              + number + ". Allowed range: 0..255");
+    }
+
+    return (static_cast<uint8_t>(n));
+}
+
+int8_t
+EvalContext::convertInt8(const std::string& number,
+                         const isc::eval::location& loc)
+{
+    int n = 0;
+    try {
+        n  = boost::lexical_cast<int>(number);
+    } catch (const boost::bad_lexical_cast &) {
+        error(loc, "Invalid integer value in " + number);
+    }
+    if (n < std::numeric_limits<int8_t>::min() ||
+        n > std::numeric_limits<int8_t>::max()) {
         error(loc, "Invalid value in "
         error(loc, "Invalid value in "
               + number + ". Allowed range: 0..255");
               + number + ". Allowed range: 0..255");
     }
     }

+ 12 - 3
src/lib/eval/eval_context.h

@@ -133,15 +133,24 @@ public:
     static uint8_t convertUint8(const std::string& number,
     static uint8_t convertUint8(const std::string& number,
                                 const isc::eval::location& loc);
                                 const isc::eval::location& loc);
 
 
+    /// @brief Attempts to convert string to signed 8bit integer
+    ///
+    /// @param number string to be converted
+    /// @param loc the location of the token
+    /// @return the integer value
+    /// @throw EvalParseError if conversion fails or the value is out of range.
+    static int8_t convertInt8(const std::string& number,
+                              const isc::eval::location& loc);
+
     /// @brief Nest level conversion
     /// @brief Nest level conversion
     ///
     ///
     /// @param nest_level a string representing the integer nesting level
     /// @param nest_level a string representing the integer nesting level
     /// @param loc the location of the token
     /// @param loc the location of the token
     /// @return the nesting level
     /// @return the nesting level
     /// @throw calls the syntax error function if the value is not in
     /// @throw calls the syntax error function if the value is not in
-    ///        the range 0..31
-    uint8_t convertNestLevelNumber(const std::string& nest_level,
-                                   const isc::eval::location& loc);
+    ///        the range -32..31
+    int8_t convertNestLevelNumber(const std::string& nest_level,
+                                  const isc::eval::location& loc);
 
 
     /// @brief Converts integer to string representation
     /// @brief Converts integer to string representation
     ///
     ///

+ 1 - 1
src/lib/eval/parser.yy

@@ -96,7 +96,7 @@ using namespace isc::eval;
 %type <uint32_t> integer_expr
 %type <uint32_t> integer_expr
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenRelay6Field::FieldType> relay6_field
 %type <TokenRelay6Field::FieldType> relay6_field
-%type <uint8_t> nest_level
+%type <int8_t> nest_level
 %type <TokenPkt::MetadataType> pkt_metadata
 %type <TokenPkt::MetadataType> pkt_metadata
 %type <TokenPkt4::FieldType> pkt4_field
 %type <TokenPkt4::FieldType> pkt4_field
 %type <TokenPkt6::FieldType> pkt6_field
 %type <TokenPkt6::FieldType> pkt6_field

+ 21 - 10
src/lib/eval/tests/context_unittest.cc

@@ -151,7 +151,7 @@ public:
     /// @param expected_code expected option code
     /// @param expected_code expected option code
     /// @param expected_repr expected representation (text, hex, exists)
     /// @param expected_repr expected representation (text, hex, exists)
     void checkTokenRelay6Option(const TokenPtr& token,
     void checkTokenRelay6Option(const TokenPtr& token,
-                                uint8_t expected_level,
+                                int8_t expected_level,
                                 uint16_t expected_code,
                                 uint16_t expected_code,
                                 TokenOption::RepresentationType expected_repr) {
                                 TokenOption::RepresentationType expected_repr) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
@@ -174,7 +174,7 @@ public:
     /// @param exp_repr expected representation to be parsed
     /// @param exp_repr expected representation to be parsed
     /// @param exp_tokens expected number of tokens
     /// @param exp_tokens expected number of tokens
     void testRelay6Option(const std::string& expr,
     void testRelay6Option(const std::string& expr,
-                         uint8_t exp_level,
+                         int8_t exp_level,
                          uint16_t exp_code,
                          uint16_t exp_code,
                          TokenOption::RepresentationType exp_repr,
                          TokenOption::RepresentationType exp_repr,
                          int exp_tokens) {
                          int exp_tokens) {
@@ -345,7 +345,7 @@ public:
     /// @param expected_code expected option code
     /// @param expected_code expected option code
     /// @param expected_repr expected representation (text, hex, exists)
     /// @param expected_repr expected representation (text, hex, exists)
     void checkTokenRelay6Field(const TokenPtr& token,
     void checkTokenRelay6Field(const TokenPtr& token,
-                               uint8_t expected_level,
+                               int8_t expected_level,
                                TokenRelay6Field::FieldType expected_type) {
                                TokenRelay6Field::FieldType expected_type) {
         ASSERT_TRUE(token);
         ASSERT_TRUE(token);
         boost::shared_ptr<TokenRelay6Field> opt =
         boost::shared_ptr<TokenRelay6Field> opt =
@@ -365,7 +365,7 @@ public:
     /// @param exp_type expected field type to be parsed
     /// @param exp_type expected field type to be parsed
     /// @param exp_tokens expected number of tokens
     /// @param exp_tokens expected number of tokens
     void testRelay6Field(const std::string& expr,
     void testRelay6Field(const std::string& expr,
-                         uint8_t exp_level,
+                         int8_t exp_level,
                          TokenRelay6Field::FieldType exp_type,
                          TokenRelay6Field::FieldType exp_type,
                          int exp_tokens) {
                          int exp_tokens) {
         EvalContext eval(Option::V6);
         EvalContext eval(Option::V6);
@@ -934,7 +934,15 @@ TEST_F(EvalContextTest, relay6OptionHex) {
                      2, 85, TokenOption::HEXADECIMAL, 3);
                      2, 85, TokenOption::HEXADECIMAL, 3);
 }
 }
 
 
-// Test the nest level of a relay6 option should be in [0..32[
+// Test the parsing of a relay6 option in reverse order
+TEST_F(EvalContextTest, relay6OptionReverse) {
+    EvalContext eval(Option::V6);
+
+    testRelay6Option("relay6[-1].option[123].text == 'foo'",
+                     -1, 123, TokenOption::TEXTUAL, 3);
+}
+
+// Test the nest level of a relay6 option should be in [-32..32[
 TEST_F(EvalContextTest, relay6OptionLimits) {
 TEST_F(EvalContextTest, relay6OptionLimits) {
     EvalContext eval(Option::V6);
     EvalContext eval(Option::V6);
 
 
@@ -946,11 +954,14 @@ TEST_F(EvalContextTest, relay6OptionLimits) {
 
 
     checkError("relay6[32].option[123].text == 'foo'",
     checkError("relay6[32].option[123].text == 'foo'",
                "<string>:1.8-9: Nest level has invalid value in 32. "
                "<string>:1.8-9: Nest level has invalid value in 32. "
-               "Allowed range: 0..31");
+               "Allowed range: -32..31");
+
+    // min nest level is minus hop count limit
+    testRelay6Option("relay6[-32].option[123].text == 'foo'",
+                     -32, 123, TokenOption::TEXTUAL, 3);
 
 
-    // next level must be a positive number
-    checkError("relay6[-1].option[123].text == 'foo'",
-               "<string>:1.8-9: Invalid value in -1. Allowed range: 0..255");
+    checkError("relay6[-33].option[123].text == 'foo'",
+               "<string>:1.8-10: Nest level has invalid value in -33. Allowed range: -32..31");
 }
 }
 
 
 // Verify that relay6[13].option is not usable in v4
 // Verify that relay6[13].option is not usable in v4
@@ -1041,7 +1052,7 @@ TEST_F(EvalContextTest, relay6FieldPeerAddr) {
                     1, TokenRelay6Field::PEERADDR, 3);
                     1, TokenRelay6Field::PEERADDR, 3);
 }
 }
 
 
-// Verify that relay6[13].<field> is not usable in v4
+// Verify that relay6[0].<field> is not usable in v4
 TEST_F(EvalContextTest, relay6FieldError) {
 TEST_F(EvalContextTest, relay6FieldError) {
     universe_ = Option::V4;
     universe_ = Option::V4;
 
 

+ 53 - 4
src/lib/eval/tests/token_unittest.cc

@@ -128,7 +128,7 @@ public:
     /// @param test_level The nesting level
     /// @param test_level The nesting level
     /// @param test_code The code of the option to extract
     /// @param test_code The code of the option to extract
     /// @param result_addr The expected result of the address as a string
     /// @param result_addr The expected result of the address as a string
-    void verifyRelay6Option(const uint8_t test_level,
+    void verifyRelay6Option(const int8_t test_level,
                             const uint16_t test_code,
                             const uint16_t test_code,
                             const TokenOption::RepresentationType& test_rep,
                             const TokenOption::RepresentationType& test_rep,
                             const std::string& result_string) {
                             const std::string& result_string) {
@@ -159,7 +159,7 @@ public:
     /// @param test_level The nesting level
     /// @param test_level The nesting level
     /// @param test_field The type of the field to extract
     /// @param test_field The type of the field to extract
     /// @param result_addr The expected result of the address as a string
     /// @param result_addr The expected result of the address as a string
-    void verifyRelay6Eval(const uint8_t test_level,
+    void verifyRelay6Eval(const int8_t test_level,
                           const TokenRelay6Field::FieldType test_field,
                           const TokenRelay6Field::FieldType test_field,
                           const int result_len,
                           const int result_len,
                           const uint8_t result_addr[]) {
                           const uint8_t result_addr[]) {
@@ -1135,6 +1135,21 @@ TEST_F(TokenTest, relay6Option) {
     // Level 2, no encapsulation so no options
     // Level 2, no encapsulation so no options
     verifyRelay6Option(2, 100, TokenOption::TEXTUAL, "");
     verifyRelay6Option(2, 100, TokenOption::TEXTUAL, "");
 
 
+    // Level -1, the same as level 1
+    verifyRelay6Option(-1, 100, TokenOption::TEXTUAL, "hundred.one");
+    verifyRelay6Option(-1, 101, TokenOption::TEXTUAL, "");
+    verifyRelay6Option(-1, 102, TokenOption::TEXTUAL, "hundredtwo.one");
+
+    // Level -2, the same as level 0
+    verifyRelay6Option(-2, 100, TokenOption::TEXTUAL, "hundred.zero");
+    verifyRelay6Option(-2, 100, TokenOption::EXISTS, "true");
+    verifyRelay6Option(-2, 101, TokenOption::TEXTUAL, "hundredone.zero");
+    verifyRelay6Option(-2, 102, TokenOption::TEXTUAL, "");
+    verifyRelay6Option(-2, 102, TokenOption::EXISTS, "false");
+
+    // Level -3, no encapsulation so no options
+    verifyRelay6Option(-3, 100, TokenOption::TEXTUAL, "");
+
     // Check that the debug output was correct.  Add the strings
     // Check that the debug output was correct.  Add the strings
     // to the test vector in the class and then call checkFile
     // to the test vector in the class and then call checkFile
     // for comparison
     // for comparison
@@ -1150,6 +1165,18 @@ TEST_F(TokenTest, relay6Option) {
 
 
     addString("EVAL_DEBUG_OPTION Pushing option 100 with value ''");
     addString("EVAL_DEBUG_OPTION Pushing option 100 with value ''");
 
 
+    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 '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 ''");
+
     EXPECT_TRUE(checkFile());
     EXPECT_TRUE(checkFile());
 }
 }
 
 
@@ -1465,6 +1492,17 @@ TEST_F(TokenTest, relay6Field) {
     // Level 2 has no encapsulation so the address should be zero length
     // Level 2 has no encapsulation so the address should be zero length
     verifyRelay6Eval(2, TokenRelay6Field::LINKADDR, 0, zeroaddr);
     verifyRelay6Eval(2, TokenRelay6Field::LINKADDR, 0, zeroaddr);
 
 
+    // Level -1 is the same as level 1
+    verifyRelay6Eval(-1, TokenRelay6Field::LINKADDR, 16, linkaddr);
+    verifyRelay6Eval(-1, TokenRelay6Field::PEERADDR, 16, peeraddr);
+
+    // Level -2 is the same as level 0
+    verifyRelay6Eval(-2, TokenRelay6Field::LINKADDR, 16, zeroaddr);
+    verifyRelay6Eval(-2, TokenRelay6Field::PEERADDR, 16, zeroaddr);
+
+    // Level -3 has no encapsulation so the address should be zero length
+    verifyRelay6Eval(-3, TokenRelay6Field::LINKADDR, 0, zeroaddr);
+
     // Lets check that the layout of the address returned by the
     // Lets check that the layout of the address returned by the
     // token matches that of the TokenIpAddress
     // token matches that of the TokenIpAddress
     TokenPtr trelay;
     TokenPtr trelay;
@@ -1496,8 +1534,19 @@ TEST_F(TokenTest, relay6Field) {
               "with value 0x00010000000000000000000000000001");
               "with value 0x00010000000000000000000000000001");
     addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest 1 "
     addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest 1 "
               "with value 0x00010000000000000000000000000002");
               "with value 0x00010000000000000000000000000002");
-    addString("EVAL_DEBUG_RELAY6_RANGE Pushing PKT6 relay field linkaddr nest 2 "
-              "with value 0x");
+    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_RELAY6 Pushing PKT6 relay field peeraddr nest -1 "
+              "with value 0x00010000000000000000000000000002");
+    addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest -2 "
+              "with value 0x00000000000000000000000000000000");
+    addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field peeraddr nest -2 "
+              "with value 0x00000000000000000000000000000000");
+    addString("EVAL_DEBUG_RELAY6_RANGE Pushing PKT6 relay field linkaddr "
+              "nest -3 with value 0x");
 
 
     addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 1 "
     addString("EVAL_DEBUG_RELAY6 Pushing PKT6 relay field linkaddr nest 1 "
               "with value 0x00010000000000000000000000000001");
               "with value 0x00010000000000000000000000000001");

+ 28 - 7
src/lib/eval/token.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -179,7 +179,17 @@ OptionPtr TokenRelay6Option::getOption(Pkt& pkt) {
         try {
         try {
             // Now that we have the right type of packet we can
             // Now that we have the right type of packet we can
             // get the option and return it.
             // get the option and return it.
-            return(pkt6.getRelayOption(option_code_, nest_level_));
+            if (nest_level_ >= 0) {
+                uint8_t nesting_level = static_cast<uint8_t>(nest_level_);
+                return(pkt6.getRelayOption(option_code_, nesting_level));
+            } else {
+                int nesting_level = pkt6.relay_info_.size() + nest_level_;
+                if (nesting_level < 0) {
+                    return (OptionPtr());
+                }
+                return(pkt6.getRelayOption(option_code_,
+                                           static_cast<uint8_t>(nesting_level)));
+            }
         }
         }
         catch (const isc::OutOfRange&) {
         catch (const isc::OutOfRange&) {
             // The only exception we expect is OutOfRange if the nest
             // The only exception we expect is OutOfRange if the nest
@@ -383,18 +393,29 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
         // Check if it's a Pkt6.  If it's not the dynamic_cast will
         // Check if it's a Pkt6.  If it's not the dynamic_cast will
         // throw std::bad_cast.
         // throw std::bad_cast.
         const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
         const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
+        uint8_t relay_level;
 
 
         try {
         try {
-        switch (type_) {
+            if (nest_level_ >= 0) {
+                relay_level = static_cast<uint8_t>(nest_level_);
+            } else {
+                int nesting_level = pkt6.relay_info_.size() + nest_level_;
+                if (nesting_level < 0) {
+                    // Don't throw OutOfRange here
+                    nesting_level = 32;
+                }
+                relay_level = static_cast<uint8_t>(nesting_level);
+            }
+            switch (type_) {
             // Now that we have the right type of packet we can
             // Now that we have the right type of packet we can
             // get the option and return it.
             // get the option and return it.
             case LINKADDR:
             case LINKADDR:
                 type_str = "linkaddr";
                 type_str = "linkaddr";
-                binary = pkt6.getRelay6LinkAddress(nest_level_).toBytes();
+                binary = pkt6.getRelay6LinkAddress(relay_level).toBytes();
                 break;
                 break;
             case PEERADDR:
             case PEERADDR:
                 type_str = "peeraddr";
                 type_str = "peeraddr";
-                binary = pkt6.getRelay6PeerAddress(nest_level_).toBytes();
+                binary = pkt6.getRelay6PeerAddress(relay_level).toBytes();
                 break;
                 break;
             }
             }
         } catch (const isc::OutOfRange&) {
         } catch (const isc::OutOfRange&) {
@@ -404,7 +425,7 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
             // Log what we pushed
             // Log what we pushed
             LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE)
             LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE)
               .arg(type_str)
               .arg(type_str)
-              .arg(unsigned(nest_level_))
+              .arg(int(nest_level_))
               .arg("0x");
               .arg("0x");
             return;
             return;
         }
         }
@@ -422,7 +443,7 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
     // Log what we pushed
     // Log what we pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6)
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6)
         .arg(type_str)
         .arg(type_str)
-        .arg(unsigned(nest_level_))
+        .arg(int(nest_level_))
         .arg(toHex(value));
         .arg(toHex(value));
 }
 }
 
 

+ 10 - 8
src/lib/eval/token.h

@@ -338,7 +338,8 @@ protected:
 /// exist or the option is not found an empty string ("") is returned
 /// exist or the option is not found an empty string ("") is returned
 /// (or "false" when the representation is EXISTS).
 /// (or "false" when the representation is EXISTS).
 ///
 ///
-/// The nesting level can go from 0 (closest to the server) to 31
+/// The nesting level can go from 0 (closest to the server) to 31,
+/// or from -1 (closest to the client) to -32
 class TokenRelay6Option : public TokenOption {
 class TokenRelay6Option : public TokenOption {
 public:
 public:
     /// @brief Constructor that takes a nesting level and an option
     /// @brief Constructor that takes a nesting level and an option
@@ -347,7 +348,7 @@ public:
     /// @param nest_level the nesting for which relay to examine.
     /// @param nest_level the nesting for which relay to examine.
     /// @param option_code code of the option.
     /// @param option_code code of the option.
     /// @param rep_type Token representation type.
     /// @param rep_type Token representation type.
-    TokenRelay6Option(const uint8_t nest_level, const uint16_t option_code,
+    TokenRelay6Option(const int8_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) {}
         :TokenOption(option_code, rep_type), nest_level_(nest_level) {}
 
 
@@ -358,7 +359,7 @@ public:
     ///
     ///
     /// @return nest-level of the relay block this token expects to use
     /// @return nest-level of the relay block this token expects to use
     /// for extraction.
     /// for extraction.
-    uint8_t getNest() const {
+    int8_t getNest() const {
         return (nest_level_);
         return (nest_level_);
     }
     }
 
 
@@ -368,7 +369,7 @@ protected:
     /// @return option instance if available
     /// @return option instance if available
     virtual OptionPtr getOption(Pkt& pkt);
     virtual OptionPtr getOption(Pkt& pkt);
 
 
-    uint8_t nest_level_; ///< nesting level of the relay block to use
+    int8_t nest_level_; ///< nesting level of the relay block to use
 };
 };
 
 
 /// @brief Token that represents meta data of a DHCP packet.
 /// @brief Token that represents meta data of a DHCP packet.
@@ -534,7 +535,8 @@ private:
 /// is always returned as a 16 byte IPv6 address.  As the relay may not have
 /// is always returned as a 16 byte IPv6 address.  As the relay may not have
 /// set the field it may be 0s.
 /// set the field it may be 0s.
 ///
 ///
-/// The nesting level can go from 0 (closest to the server) to 31.
+/// The nesting level can go from 0 (closest to the server) to 31,
+/// or from -1 (closest to the client) to -32
 class TokenRelay6Field : public Token {
 class TokenRelay6Field : public Token {
 public:
 public:
 
 
@@ -549,7 +551,7 @@ public:
     ///
     ///
     /// @param nest_level the nesting level for which relay to examine.
     /// @param nest_level the nesting level for which relay to examine.
     /// @param type which field to extract.
     /// @param type which field to extract.
-    TokenRelay6Field(const uint8_t nest_level, const FieldType type)
+    TokenRelay6Field(const int8_t nest_level, const FieldType type)
       : nest_level_(nest_level), type_(type) {}
       : nest_level_(nest_level), type_(type) {}
 
 
     /// @brief Extracts the specified field from the requested relay
     /// @brief Extracts the specified field from the requested relay
@@ -568,7 +570,7 @@ public:
     ///
     ///
     /// @return nest-level of the relay block this token expects to use
     /// @return nest-level of the relay block this token expects to use
     /// for extraction.
     /// for extraction.
-    uint8_t getNest() const {
+    int8_t getNest() const {
         return (nest_level_);
         return (nest_level_);
     }
     }
 
 
@@ -584,7 +586,7 @@ public:
 
 
 protected:
 protected:
     /// @brief Specifies field of the DHCPv6 relay option to get
     /// @brief Specifies field of the DHCPv6 relay option to get
-    uint8_t nest_level_; ///< nesting level of the relay block to use
+    int8_t nest_level_; ///< nesting level of the relay block to use
     FieldType type_; ///< field to get
     FieldType type_; ///< field to get
 };
 };