Browse Source

[4264] relay[123].hex token implemented.

Tomek Mrugalski 9 years ago
parent
commit
6325cf1aef

+ 14 - 0
doc/guide/classify.xml

@@ -164,6 +164,10 @@
 <row><entry>Option Text</entry><entry>option[code].text</entry><entry>The value of the option with code "code" from the packet as text</entry></row>
 -->
 <row><entry>Option Hex</entry><entry>option[code].hex</entry><entry>The value of the option with code "code" from the packet as hex</entry></row>
+<row><entry>DHCPv4 Relay Agent
+sub-option</entry><entry>relay[code].hex</entry><entry>The value of
+sub-option with code "code" from the Relay Agent Information option
+(option 82)</entry></row>
           </tbody>
           </tgroup>
         </table>
@@ -185,6 +189,16 @@
       </para>
 
       <para>
+        "relay[code].hex" attempts to extract value of the sub-option
+        "code" from the option inserted by the Relay Agent Information
+        (82) option. If the packet doesn't contain RAI option, or RAI
+        doesn't contain searched sub-option, the expression returns
+        empty string. The string is presented as a byte string of the
+        option payload without the type code or length fields. This
+        expression is allowed in DHCPv4 only.
+      </para>
+
+      <para>
         <table frame="all" id="classification-expressions-list">
           <title>List of Classification Expressions</title>
           <tgroup cols='3'>

+ 2 - 0
src/lib/eval/eval.dox

@@ -139,6 +139,8 @@ instantiated with the appropriate value and put onto the expression vector.
                     option[123].text;
  - isc::dhcp::TokenEqual - represents equal (==) operator;
  - isc::dhcp::TokenSubstring - represents 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].bin
 
 More operators are expected to be implemented in upcoming releases.
 

+ 7 - 0
src/lib/eval/eval_context.h

@@ -101,6 +101,13 @@ public:
     uint16_t convertOptionName(const std::string& option_name,
                                const isc::eval::location& loc);
 
+    /// @brief Returns the universe (v4 or v6)
+    ///
+    /// @return universe
+    Option::Universe getUniverse() {
+        return (option_universe_);
+    }
+
  private:
     /// @brief Flag determining scanner debugging.
     bool trace_scanning_;

+ 1 - 0
src/lib/eval/lexer.ll

@@ -127,6 +127,7 @@ blank [ \t]
 "text"      return isc::eval::EvalParser::make_TEXT(loc);
 "hex"       return isc::eval::EvalParser::make_HEX(loc);
 "substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
+"relay"     return isc::eval::EvalParser::make_RELAY(loc);
 "all"       return isc::eval::EvalParser::make_ALL(loc);
 "."         return isc::eval::EvalParser::make_DOT(loc);
 "("         return isc::eval::EvalParser::make_LPAREN(loc);

+ 14 - 0
src/lib/eval/parser.yy

@@ -40,6 +40,7 @@ using namespace isc::eval;
   OPTION "option"
   SUBSTRING "substring"
   TEXT "text"
+  RELAY "relay"
   HEX "hex"
   ALL "all"
   DOT "."
@@ -93,6 +94,19 @@ string_expr : STRING
                       TokenPtr opt(new TokenOption($3, $6));
                       ctx.expression.push_back(opt);
                   }
+            | RELAY "[" option_code "]" "." option_repr_type
+                  {
+                     switch (ctx.getUniverse()) {
+                     case Option::V4:
+                     {
+                         TokenPtr opt(new TokenRelay4Option($3, $6));
+                         ctx.expression.push_back(opt);
+                         break;
+                     }
+                     case Option::V6:
+                         error(@1, "relay support for v6 is not implemented");
+                     }
+                  }
             | SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
                   {
                       TokenPtr sub(new TokenSubstring());

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

@@ -89,6 +89,18 @@ public:
         EXPECT_TRUE(sub);
     }
 
+    /// @brief check if the given token is relay4 with the expected code
+    void checkTokenRelay4(const TokenPtr& token, uint16_t code) {
+        ASSERT_TRUE(token);
+        boost::shared_ptr<TokenRelay4Option> relay4 =
+            boost::dynamic_pointer_cast<TokenRelay4Option>(token);
+        EXPECT_TRUE(relay4);
+
+        if (relay4) {
+            EXPECT_EQ(code, relay4->getCode());
+        }
+    }
+
     /// @brief checks if the given expression raises the expected message
     /// when it is parsed.
     void checkError(const string& expr, const string& msg) {
@@ -274,6 +286,33 @@ TEST_F(EvalContextTest, substring) {
     checkTokenSubstring(tmp4);
 }
 
+// This test checks that the relay[code].hex can be used in expressions.
+TEST_F(EvalContextTest, relay4Option) {
+
+    EvalContext eval(Option::V4);
+    EXPECT_NO_THROW(parsed_ =
+                    eval.parseString("relay[13].hex == 'thirteen'"));
+    EXPECT_TRUE(parsed_);
+    ASSERT_EQ(3, eval.expression.size());
+
+    TokenPtr tmp1 = eval.expression.at(0);
+    TokenPtr tmp2 = eval.expression.at(1);
+    TokenPtr tmp3 = eval.expression.at(2);
+
+    checkTokenRelay4(tmp1, 13);
+    checkTokenString(tmp2, "thirteen");
+    checkTokenEq(tmp3);
+}
+
+// Verify that relay[13] is not usable in v6
+// There will be a separate relay accessor for v6.
+TEST_F(EvalContextTest, relay4Error) {
+    universe_ = Option::V6;
+
+    checkError("relay[13].hex == 'thirteen'",
+               "<string>:1.1-5: relay support for v6 is not implemented");
+}
+
 // Test some scanner error cases
 TEST_F(EvalContextTest, scanErrors) {
     checkError("'", "<string>:1.1: Invalid character: '");

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

@@ -44,6 +44,24 @@ public:
         pkt6_->addOption(option_str6_);
     }
 
+    /// @brief Inserts RAI option with several suboptions
+    ///
+    /// The structure inserted is:
+    ///  - RAI (option 82)
+    ///      - option 1 (containing string "one")
+    ///      - option 13 (containing string "thirteen")
+    void insertRelay4Option() {
+
+        // RAI (Relay Agent Information) option
+        OptionPtr rai(new Option(Option::V4, DHO_DHCP_AGENT_OPTIONS));
+        OptionPtr sub1(new OptionString(Option::V4, 1, "one"));
+        OptionPtr sub13(new OptionString(Option::V4, 13, "thirteen"));
+
+        rai->addOption(sub1);
+        rai->addOption(sub13);
+        pkt4_->addOption(rai);
+    }
+
     TokenPtr t_; ///< Just a convenience pointer
 
     ValueStack values_; ///< evaluated values will be stored here
@@ -574,3 +592,64 @@ TEST_F(TokenTest, substringEquals) {
     EXPECT_EQ("false", values_.top());
 
 }
+
+// This test checks that the existing relay option can be found.
+TEST_F(TokenTest, relayOption) {
+
+    // Insert relay option with sub-options 1 and 13
+    insertRelay4Option();
+
+    // Creating the token should be safe.
+    ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL)));
+
+    // We should be able to evaluate it.
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // we should have one value on the stack
+    ASSERT_EQ(1, values_.size());
+
+    // The option should be found and relay[13] should evaluate to the
+    // content of that sub-option, i.e. "thirteen"
+    EXPECT_EQ("thirteen", values_.top());
+}
+
+// This test checks that the code properly handles cases when
+// there is RAI option, but there's no requested sub-option.
+TEST_F(TokenTest, relayOptionNoSuboption) {
+
+    // Insert relay option with sub-options 1 and 13
+    insertRelay4Option();
+
+    // Creating the token should be safe.
+    ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(15, TokenOption::TEXTUAL)));
+
+    // We should be able to evaluate it.
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // we should have one value on the stack
+    ASSERT_EQ(1, values_.size());
+
+    // The option should NOT be found (there is no sub-option 15),
+    // so the expression should evaluate to ""
+    EXPECT_EQ("", values_.top());
+}
+
+// This test checks that the code properly handles cases when
+// there's no RAI option at all.
+TEST_F(TokenTest, relayOptionNoRai) {
+
+    // We didn't call insertRelay4Option(), so there's no RAI option.
+
+    // Creating the token should be safe.
+    ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL)));
+
+    // We should be able to evaluate it.
+    EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+
+    // we should have one value on the stack
+    ASSERT_EQ(1, values_.size());
+
+    // The option should NOT be found (there is no sub-option 13),
+    // so the expression should evaluate to ""
+    EXPECT_EQ("", values_.top());
+}

+ 23 - 1
src/lib/eval/token.cc

@@ -54,9 +54,14 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     values.push(value_);
 }
 
+OptionPtr
+TokenOption::getOption(const Pkt& pkt) {
+    return (pkt.getOption(option_code_));
+}
+
 void
 TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
-    OptionPtr opt = pkt.getOption(option_code_);
+    OptionPtr opt = getOption(pkt);
     std::string opt_str;
     if (opt) {
         if (representation_type_ == TEXTUAL) {
@@ -168,3 +173,20 @@ TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
     // and finally get the substring
     values.push(string_str.substr(start_pos, length));
 }
+
+TokenRelay4Option::TokenRelay4Option(const uint16_t option_code,
+                                     const RepresentationType& rep_type)
+    :TokenOption(option_code, rep_type) {
+}
+
+OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) {
+
+    // Check if there is Relay Agent Option.
+    OptionPtr rai = pkt.getOption(DHO_DHCP_AGENT_OPTIONS);
+    if (!rai) {
+        return (OptionPtr());
+    }
+
+    // If there is, try to return its suboption
+    return (rai->getOption(option_code_));
+}

+ 41 - 1
src/lib/eval/token.h

@@ -183,7 +183,18 @@ public:
         return (option_code_);
     }
 
-private:
+protected:
+    /// @brief Attempts to retrieve an option
+    ///
+    /// For this class it simply attempts to retrieve the option from the packet,
+    /// but there may be derived classes that would attempt to extract it from
+    /// other places (e.g. relay option, or as a suboption of other specific option).
+    ///
+    ///
+    /// @param pkt the option will be retrieved from here
+    /// @return option instance (or NULL if not found)
+    virtual OptionPtr getOption(const Pkt& pkt);
+
     uint16_t option_code_; ///< Code of the option to be extracted
     RepresentationType representation_type_; ///< Representation type.
 };
@@ -269,6 +280,35 @@ public:
     void evaluate(const Pkt& pkt, ValueStack& values);
 };
 
+/// @brief Represents a sub-option inserted by the DHCPv4 relay.
+///
+/// DHCPv4 relays insert sub-options in option 82. This token attempts to extract
+/// such sub-options. Note it is radically different than in DHCPv6 (possible
+/// many encapsulation levels), thus separate classes for v4 and v6.
+///
+/// This token can represent the following expressions:
+/// relay[13].text - Textual representation of sub-option 13 in RAI (option 82)
+/// relay[13].hex  - Binary representation of sub-option 13 in RAI (option 82)
+/// relay[vendor-class].text - Text representation of sub-option X in RAI (option 82)
+/// relay[vendor-class].hex - Binary representation of sub-option X in RAI (option 82)
+class TokenRelay4Option : public TokenOption {
+public:
+
+    /// @brief Construtor for extracting sub-option from RAI (option 82)
+    ///
+    /// @param option_code code of the searched sub-option
+    /// @param rep_type code representation (currently .hex and .text are supported)
+    TokenRelay4Option(const uint16_t option_code,
+                      const RepresentationType& rep_type);
+
+protected:
+    /// @brief Attempts to obtain specified sub-option of option 82 from the packet
+    /// @param pkt DHCPv4 packet (that hopefully contains option 82)
+    /// @return found sub-option from option 82
+    virtual OptionPtr getOption(const Pkt& pkt);
+};
+
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace