Browse Source

[5287] Updated clasify doc

Francis Dupont 7 years ago
parent
commit
01495d1575

+ 6 - 3
doc/guide/classify.xml

@@ -484,9 +484,12 @@
       "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.
+      the relay closest to the DHCPv6 server. Negative values allow to
+      specify relays counted from the DHCPv6 client, -1 indicating the
+      relay closest. In general negative "nest" level is the same than
+      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>
 
       <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());
 }
 
-uint8_t
+int8_t
 EvalContext::convertNestLevelNumber(const std::string& nest_level,
                                     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 (n >= HOP_COUNT_LIMIT) {
+        if ((n < - HOP_COUNT_LIMIT) || (n >= HOP_COUNT_LIMIT)) {
             error(loc, "Nest level has invalid value in "
-                      + nest_level + ". Allowed range: 0..31");
+                      + nest_level + ". Allowed range: -32..31");
         }
     } else {
         error(loc, "Nest level invalid for DHCPv4 packets");
@@ -123,7 +123,26 @@ EvalContext::convertUint8(const std::string& number,
     } catch (const boost::bad_lexical_cast &) {
         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 "
               + 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,
                                 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
     ///
     /// @param nest_level a string representing the integer nesting level
     /// @param loc the location of the token
     /// @return the nesting level
     /// @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
     ///

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

@@ -96,7 +96,7 @@ using namespace isc::eval;
 %type <uint32_t> integer_expr
 %type <TokenOption::RepresentationType> option_repr_type
 %type <TokenRelay6Field::FieldType> relay6_field
-%type <uint8_t> nest_level
+%type <int8_t> nest_level
 %type <TokenPkt::MetadataType> pkt_metadata
 %type <TokenPkt4::FieldType> pkt4_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_repr expected representation (text, hex, exists)
     void checkTokenRelay6Option(const TokenPtr& token,
-                                uint8_t expected_level,
+                                int8_t expected_level,
                                 uint16_t expected_code,
                                 TokenOption::RepresentationType expected_repr) {
         ASSERT_TRUE(token);
@@ -174,7 +174,7 @@ public:
     /// @param exp_repr expected representation to be parsed
     /// @param exp_tokens expected number of tokens
     void testRelay6Option(const std::string& expr,
-                         uint8_t exp_level,
+                         int8_t exp_level,
                          uint16_t exp_code,
                          TokenOption::RepresentationType exp_repr,
                          int exp_tokens) {
@@ -345,7 +345,7 @@ public:
     /// @param expected_code expected option code
     /// @param expected_repr expected representation (text, hex, exists)
     void checkTokenRelay6Field(const TokenPtr& token,
-                               uint8_t expected_level,
+                               int8_t expected_level,
                                TokenRelay6Field::FieldType expected_type) {
         ASSERT_TRUE(token);
         boost::shared_ptr<TokenRelay6Field> opt =
@@ -365,7 +365,7 @@ public:
     /// @param exp_type expected field type to be parsed
     /// @param exp_tokens expected number of tokens
     void testRelay6Field(const std::string& expr,
-                         uint8_t exp_level,
+                         int8_t exp_level,
                          TokenRelay6Field::FieldType exp_type,
                          int exp_tokens) {
         EvalContext eval(Option::V6);
@@ -934,7 +934,15 @@ TEST_F(EvalContextTest, relay6OptionHex) {
                      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) {
     EvalContext eval(Option::V6);
 
@@ -946,11 +954,14 @@ TEST_F(EvalContextTest, relay6OptionLimits) {
 
     checkError("relay6[32].option[123].text == 'foo'",
                "<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
@@ -1041,7 +1052,7 @@ TEST_F(EvalContextTest, relay6FieldPeerAddr) {
                     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) {
     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_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,
+    void verifyRelay6Option(const int8_t test_level,
                             const uint16_t test_code,
                             const TokenOption::RepresentationType& test_rep,
                             const std::string& result_string) {
@@ -159,7 +159,7 @@ public:
     /// @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,
+    void verifyRelay6Eval(const int8_t test_level,
                           const TokenRelay6Field::FieldType test_field,
                           const int result_len,
                           const uint8_t result_addr[]) {
@@ -1135,6 +1135,21 @@ TEST_F(TokenTest, relay6Option) {
     // Level 2, no encapsulation so no options
     verifyRelay6Option(2, 100, TokenOption::TEXTUAL, "");
 
+    // Level -1, the same than 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 than 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
     // to the test vector in the class and then call checkFile
     // 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 '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());
 }
 
@@ -1465,6 +1492,17 @@ TEST_F(TokenTest, relay6Field) {
     // Level 2 has no encapsulation so the address should be zero length
     verifyRelay6Eval(2, TokenRelay6Field::LINKADDR, 0, zeroaddr);
 
+    // Level -1 is the same than level 1
+    verifyRelay6Eval(-1, TokenRelay6Field::LINKADDR, 16, linkaddr);
+    verifyRelay6Eval(-1, TokenRelay6Field::PEERADDR, 16, peeraddr);
+
+    // Level -2 is the same than 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
     // token matches that of the TokenIpAddress
     TokenPtr trelay;
@@ -1496,8 +1534,19 @@ TEST_F(TokenTest, relay6Field) {
               "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_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 "
               "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
 // 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 {
             // Now that we have the right type of packet we can
             // 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&) {
             // 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
         // throw std::bad_cast.
         const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
+        uint8_t relay_level;
 
         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
             // get the option and return it.
             case LINKADDR:
                 type_str = "linkaddr";
-                binary = pkt6.getRelay6LinkAddress(nest_level_).toBytes();
+                binary = pkt6.getRelay6LinkAddress(relay_level).toBytes();
                 break;
             case PEERADDR:
                 type_str = "peeraddr";
-                binary = pkt6.getRelay6PeerAddress(nest_level_).toBytes();
+                binary = pkt6.getRelay6PeerAddress(relay_level).toBytes();
                 break;
             }
         } catch (const isc::OutOfRange&) {
@@ -404,7 +425,7 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
             // Log what we pushed
             LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE)
               .arg(type_str)
-              .arg(unsigned(nest_level_))
+              .arg(int(nest_level_))
               .arg("0x");
             return;
         }
@@ -422,7 +443,7 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
     // Log what we pushed
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6)
         .arg(type_str)
-        .arg(unsigned(nest_level_))
+        .arg(int(nest_level_))
         .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
 /// (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 {
 public:
     /// @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 option_code code of the option.
     /// @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)
         :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
     /// for extraction.
-    uint8_t getNest() const {
+    int8_t getNest() const {
         return (nest_level_);
     }
 
@@ -368,7 +369,7 @@ protected:
     /// @return option instance if available
     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.
@@ -534,7 +535,8 @@ private:
 /// 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.
+/// 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 {
 public:
 
@@ -549,7 +551,7 @@ public:
     ///
     /// @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)
+    TokenRelay6Field(const int8_t nest_level, const FieldType type)
       : nest_level_(nest_level), type_(type) {}
 
     /// @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
     /// for extraction.
-    uint8_t getNest() const {
+    int8_t getNest() const {
         return (nest_level_);
     }
 
@@ -584,7 +586,7 @@ public:
 
 protected:
     /// @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
 };