]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3502] Checkpoint: added label
authorFrancis Dupont <fdupont@isc.org>
Wed, 7 Aug 2024 10:37:30 +0000 (12:37 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 21 Aug 2024 13:12:37 +0000 (15:12 +0200)
src/lib/eval/evaluate.cc
src/lib/eval/tests/token_unittest.cc
src/lib/eval/token.cc
src/lib/eval/token.h

index 27bad0e70400d3ccc870f0ce1d1a3518df885f5a..6b77280d5f8f80a59e7fa99ff93fc861c90402ee 100644 (file)
 namespace isc {
 namespace dhcp {
 
-bool evaluateBool(const Expression& expr, Pkt& pkt) {
+bool
+evaluateBool(const Expression& expr, Pkt& pkt) {
     ValueStack values;
-    for (auto const& it : expr) {
-        it->evaluate(pkt, values);
+    for (auto it = expr.cbegin(); it != expr.cend(); ) {
+        unsigned label = (*it++)->evaluate(pkt, values);
+        if (label == 0) {
+            continue;
+        }
+        // Scan for the given label.
+        for (;;) {
+            if (it == expr.cend()) {
+                isc_throw(EvalBadLabel, "can't reach label " << label);
+            }
+            if ((*it++)->getLabel() == label) {
+                break;
+            }
+        }
     }
     if (values.size() != 1) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
@@ -26,8 +39,20 @@ bool evaluateBool(const Expression& expr, Pkt& pkt) {
 std::string
 evaluateString(const Expression& expr, Pkt& pkt) {
     ValueStack values;
-    for (auto const& it : expr) {
-        it->evaluate(pkt, values);
+    for (auto it = expr.cbegin(); it != expr.cend(); ) {
+        unsigned label = (*it++)->evaluate(pkt, values);
+        if (label == 0) {
+            continue;
+        }
+        // Scan for the given label.
+        for (;;) {
+            if (it == expr.cend()) {
+                isc_throw(EvalBadLabel, "can't reach label " << label);
+            }
+            if ((*it++)->getLabel() == label) {
+                break;
+            }
+        }
     }
     if (values.size() != 1) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
index 2b87ef8ac01cfa16bdadaba63a57697529b4c771..40c9d4a412e623b838271b500e47b0d78f5553ec 100644 (file)
@@ -591,6 +591,7 @@ TEST_F(TokenTest, string4) {
 
     // Store constant string "foo" in the TokenString object.
     ASSERT_NO_THROW(t_.reset(new TokenString("foo")));
+    EXPECT_EQ(0, t_->getLabel());
 
     // Make sure that the token can be evaluated without exceptions.
     ASSERT_NO_THROW(t_->evaluate(*pkt4_, values_));
@@ -695,6 +696,7 @@ TEST_F(TokenTest, hexstring4) {
 
     // Store constant empty hexstring "" ("") in the TokenHexString object.
     ASSERT_NO_THROW(empty.reset(new TokenHexString("")));
+    EXPECT_EQ(0, empty->getLabel());
     // Store bad encoded hexstring "0abc" ("").
     ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc")));
     // Store hexstring with no digits "0x" ("").
@@ -821,6 +823,7 @@ TEST_F(TokenTest, hexstring6) {
 // is not used)
 TEST_F(TokenTest, lcase) {
     ASSERT_NO_THROW(t_.reset(new TokenLowerCase()));
+    EXPECT_EQ(0, t_->getLabel());
     values_.push("LoWeR");
 
     // Make sure that the token can be evaluated without exceptions.
@@ -874,6 +877,7 @@ TEST_F(TokenTest, lcaseComplex) {
 // is not used)
 TEST_F(TokenTest, ucase) {
     ASSERT_NO_THROW(t_.reset(new TokenUpperCase()));
+    EXPECT_EQ(0, t_->getLabel());
     values_.push("uPpEr");
 
     // Make sure that the token can be evaluated without exceptions.
@@ -937,6 +941,7 @@ TEST_F(TokenTest, ipaddress) {
 
     // IP addresses
     ASSERT_NO_THROW(ip4.reset(new TokenIpAddress("10.0.0.1")));
+    EXPECT_EQ(0, ip4->getLabel());
     ASSERT_NO_THROW(ip6.reset(new TokenIpAddress("2001:db8::1")));
 
     // Make sure that tokens can be evaluated without exceptions.
@@ -982,6 +987,7 @@ TEST_F(TokenTest, ipaddress) {
 // (The actual packet is not used)
 TEST_F(TokenTest, addressToText) {
     TokenPtr address((new TokenIpAddressToText()));
+    EXPECT_EQ(0, address->getLabel());
     std::vector<uint8_t> bytes;
 
     std::string value = "10.0.0.1";
@@ -1041,11 +1047,17 @@ TEST_F(TokenTest, addressToText) {
 // (The actual packet is not used)
 TEST_F(TokenTest, integerToText) {
     TokenPtr int8token((new TokenInt8ToText()));
+    EXPECT_EQ(0, int8token->getLabel());
     TokenPtr int16token((new TokenInt16ToText()));
+    EXPECT_EQ(0, int16token->getLabel());
     TokenPtr int32token((new TokenInt32ToText()));
+    EXPECT_EQ(0, int32token->getLabel());
     TokenPtr uint8token((new TokenUInt8ToText()));
+    EXPECT_EQ(0, uint8token->getLabel());
     TokenPtr uint16token((new TokenUInt16ToText()));
+    EXPECT_EQ(0, uint16token->getLabel());
     TokenPtr uint32token((new TokenUInt32ToText()));
+    EXPECT_EQ(0, uint32token->getLabel());
 
     std::vector<uint8_t> bytes;
     std::string value = "0123456789";
@@ -1240,6 +1252,7 @@ TEST_F(TokenTest, optionString4) {
 
     // The packets we use have option 100 with a string in them.
     ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::TEXTUAL)));
+    EXPECT_EQ(0, found->getLabel());
     ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::TEXTUAL)));
 
     // This should evaluate to the content of the option 100 (i.e. "hundred4")
@@ -1469,6 +1482,7 @@ TEST_F(TokenTest, relay4OptionNoSuboption) {
 
     // Creating the token should be safe.
     ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(15, TokenOption::TEXTUAL)));
+    EXPECT_EQ(0, t_->getLabel());
 
     // We should be able to evaluate it.
     EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
@@ -1668,6 +1682,7 @@ TEST_F(TokenTest, relay6Option) {
 TEST_F(TokenTest, relay6OptionError) {
     // Create a relay6 option token
     ASSERT_NO_THROW(t_.reset(new TokenRelay6Option(0, 13, TokenOption::TEXTUAL)));
+    EXPECT_EQ(0, t_->getLabel());
 
     // A DHCPv6 packet is required
     EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
@@ -1681,6 +1696,7 @@ TEST_F(TokenTest, pkt4MetaData) {
 
     // Check interface (expect eth0)
     ASSERT_NO_THROW(t_.reset(new TokenPkt(TokenPkt::IFACE)));
+    EXPECT_EQ(0, t_->getLabel());
     EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
     ASSERT_EQ(1, values_.size());
     ASSERT_EQ("eth0", values_.top());
@@ -1808,6 +1824,7 @@ TEST_F(TokenTest, pkt4Fields) {
 
     // Check hardware address field.
     ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::CHADDR)));
+    EXPECT_EQ(0, t_->getLabel());
     EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
     ASSERT_EQ(1, values_.size());
     uint8_t expected_hw[] = { 1, 2, 3, 4, 5, 6, 7 };
@@ -1927,6 +1944,7 @@ TEST_F(TokenTest, pkt6Fields) {
 
     // Check the message type
     ASSERT_NO_THROW(t_.reset(new TokenPkt6(TokenPkt6::MSGTYPE)));
+    EXPECT_EQ(0, t_->getLabel());
     EXPECT_NO_THROW(t_->evaluate(*pkt6_, values_));
     ASSERT_EQ(1, values_.size());
     uint32_t expected = htonl(1);
@@ -2010,6 +2028,7 @@ TEST_F(TokenTest, relay6Field) {
     TokenPtr taddr;
     TokenPtr tequal;
     ASSERT_NO_THROW(trelay.reset(new TokenRelay6Field(1, TokenRelay6Field::LINKADDR)));
+    EXPECT_EQ(0, trelay->getLabel());
     ASSERT_NO_THROW(taddr.reset(new TokenIpAddress("1::1")));
     ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
 
@@ -2083,6 +2102,7 @@ TEST_F(TokenTest, relay6Field) {
 TEST_F(TokenTest, relay6FieldError) {
     // Create a valid relay6 field token
     ASSERT_NO_THROW(t_.reset(new TokenRelay6Field(0, TokenRelay6Field::LINKADDR)));
+    EXPECT_EQ(0, t_->getLabel());
 
     // a DHCPv6 packet is required
     ASSERT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
@@ -2095,6 +2115,7 @@ TEST_F(TokenTest, relay6FieldError) {
 TEST_F(TokenTest, optionEqualInvalid) {
 
     ASSERT_NO_THROW(t_.reset(new TokenEqual()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // CASE 1: There's not enough values on the stack. == is an operator that
     // takes two parameters. There are 0 on the stack.
@@ -2157,6 +2178,7 @@ TEST_F(TokenTest, optionEqualTrue) {
 // The actual packet is not used.
 TEST_F(TokenTest, substringNotEnoughValues) {
     ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // Substring requires three values on the stack, try
     // with 0, 1 and 2 all should throw an exception
@@ -2486,6 +2508,7 @@ TEST_F(TokenTest, substringEquals) {
 // The actual packet is not used.
 TEST_F(TokenTest, concat) {
     ASSERT_NO_THROW(t_.reset(new TokenConcat()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // Concat requires two values on the stack, try
     // with 0 and 1 both should throw an exception
@@ -2515,6 +2538,7 @@ TEST_F(TokenTest, concat) {
 // The actual packet is not used.
 TEST_F(TokenTest, tohexstring) {
     ASSERT_NO_THROW(t_.reset(new TokenToHexString()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // Hexstring requires two values on the stack, try
     // with 0 and 1 both should throw an exception
@@ -2544,6 +2568,7 @@ TEST_F(TokenTest, tohexstring) {
 // to select the branch following the condition.
 TEST_F(TokenTest, ifElse) {
     ASSERT_NO_THROW(t_.reset(new TokenIfElse()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // Ifelse requires three values on the stack, try
     // with 0, 1 and 2 all should throw an exception
@@ -2584,6 +2609,7 @@ TEST_F(TokenTest, ifElse) {
 TEST_F(TokenTest, operatorNotInvalid) {
 
     ASSERT_NO_THROW(t_.reset(new TokenNot()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // CASE 1: The stack is empty.
     EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
@@ -2624,6 +2650,7 @@ TEST_F(TokenTest, operatorNot) {
 TEST_F(TokenTest, operatorAndInvalid) {
 
     ASSERT_NO_THROW(t_.reset(new TokenAnd()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // CASE 1: There's not enough values on the stack. and is an operator that
     // takes two parameters. There are 0 on the stack.
@@ -2704,6 +2731,7 @@ TEST_F(TokenTest, operatorAndTrue) {
 TEST_F(TokenTest, operatorOrInvalid) {
 
     ASSERT_NO_THROW(t_.reset(new TokenOr()));
+    EXPECT_EQ(0, t_->getLabel());
 
     // CASE 1: There's not enough values on the stack. or is an operator that
     // takes two parameters. There are 0 on the stack.
@@ -2783,6 +2811,7 @@ TEST_F(TokenTest, operatorOrTrue) {
 TEST_F(TokenTest, member) {
 
     ASSERT_NO_THROW(t_.reset(new TokenMember("foo")));
+    EXPECT_EQ(0, t_->getLabel());
 
     EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
 
@@ -2811,6 +2840,10 @@ TEST_F(TokenTest, member) {
 
 // This test verifies if expression vendor[4491].exists works properly in DHCPv4.
 TEST_F(TokenTest, vendor4SpecificVendorExists) {
+    ASSERT_NO_THROW(t_.reset(new TokenVendor(Option::V4, 4491,
+                                             TokenOption::EXISTS)));
+    EXPECT_EQ(0, t_->getLabel());
+
     // Case 1: no option, should evaluate to false
     testVendorExists(Option::V4, 4491, 0, "false");
 
@@ -3074,6 +3107,10 @@ TEST_F(TokenTest, vendor6SuboptionHex) {
 // This test verifies that "vendor-class[4491].exists" expression can be used
 // in DHCPv4.
 TEST_F(TokenTest, vendorClass4SpecificVendorExists) {
+    ASSERT_NO_THROW(t_.reset(new TokenVendorClass(Option::V4, 4491,
+                                                  TokenVendor::ENTERPRISE_ID)));
+    EXPECT_EQ(0, t_->getLabel());
+
     // Case 1: no option present, should fail
     testVendorClassExists(Option::V4, 4491, 0, "false");
 
@@ -3493,6 +3530,7 @@ TEST_F(TokenTest, subOption) {
 
     // Creating the token should be safe.
     ASSERT_NO_THROW(t_.reset(new TokenSubOption(DHO_DHCP_AGENT_OPTIONS, 13, TokenOption::TEXTUAL)));
+    EXPECT_EQ(0, t_->getLabel());
 
     // We should be able to evaluate it.
     EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
@@ -3643,6 +3681,9 @@ TEST_F(TokenTest, subOptionOptionOnly) {
 
 // Checks if various values can be represented as integer tokens
 TEST_F(TokenTest, integer) {
+    ASSERT_NO_THROW(t_.reset(new TokenInteger(0)));
+    EXPECT_EQ(0, t_->getLabel());
+
     testInteger(encode(0), 0);
     testInteger(encode(6), 6);
     testInteger(encode(255), 255);
@@ -3653,6 +3694,9 @@ TEST_F(TokenTest, integer) {
 
 // Verify TokenSplit::eval, single delimiter.
 TEST_F(TokenTest, split) {
+    ASSERT_NO_THROW(t_.reset(new TokenSplit()));
+    EXPECT_EQ(0, t_->getLabel());
+
     // Get the whole string
     std::string input(".two.three..five.");
     std::string delims(".");
@@ -3801,6 +3845,9 @@ TEST_F(TokenTest, invalidRegEx) {
 
 // Verify TokenMatch works as expected.
 TEST_F(TokenTest, match) {
+    ASSERT_NO_THROW(t_.reset(new TokenMatch("foo")));
+    EXPECT_EQ(0, t_->getLabel());
+
     testMatch("foo", "foo", true);
     // Full match is required.
     testMatch("foo", "foobar", false);
@@ -3830,4 +3877,19 @@ TEST_F(TokenTest, match) {
     testMatch("[[:alpha:]]{1,9}", "Fo0bar", false);
 }
 
+// Verify TokenLabel.
+TEST_F(TokenTest, label) {
+    // 0 is not a valid label.
+    ASSERT_THROW(t_.reset(new TokenLabel(0)), EvalParseError);
+
+    // Evaluation does and uses nothing.
+    ASSERT_NO_THROW(t_.reset(new TokenLabel(123)));
+    EXPECT_EQ(123, t_->getLabel());
+    clearStack(false);
+    unsigned next(345);
+    EXPECT_NO_THROW(next = t_->evaluate(*pkt4_, values_));
+    ASSERT_EQ(0, next);
+    ASSERT_TRUE(values_.empty());
+}
+
 }
index dd439c119f53ba94640bb7ba0cac896a28c52d57..2249652a5b89c452a2e7dcb63d16d85bd84edcce 100644 (file)
@@ -36,7 +36,7 @@ using namespace std;
 
 using isc::util::encode::toHex;
 
-void
+unsigned
 TokenString::evaluate(Pkt& pkt, ValueStack& values) {
     // Literals only push, nothing to pop
     values.push(value_);
@@ -45,6 +45,8 @@ TokenString::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_STRING)
         .arg(pkt.getLabel())
         .arg('\'' + value_ + '\'');
+
+    return (0);
 }
 
 TokenHexString::TokenHexString(const string& str) : value_("") {
@@ -75,7 +77,7 @@ TokenHexString::TokenHexString(const string& str) : value_("") {
     memmove(&value_[0], &binary[0], binary.size());
 }
 
-void
+unsigned
 TokenHexString::evaluate(Pkt& pkt, ValueStack& values) {
     // Literals only push, nothing to pop
     values.push(value_);
@@ -84,9 +86,11 @@ TokenHexString::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_HEXSTRING)
         .arg(pkt.getLabel())
         .arg(toHex(value_));
+
+    return (0);
 }
 
-void
+unsigned
 TokenLowerCase::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -103,9 +107,11 @@ TokenLowerCase::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg('\'' + op + '\'')
         .arg('\'' + result + '\'');
+
+    return (0);
 }
 
-void
+unsigned
 TokenUpperCase::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -122,6 +128,8 @@ TokenUpperCase::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg('\'' + op + '\'')
         .arg('\'' + result + '\'');
+
+    return (0);
 }
 
 TokenIpAddress::TokenIpAddress(const string& addr) : value_("") {
@@ -139,7 +147,7 @@ TokenIpAddress::TokenIpAddress(const string& addr) : value_("") {
     memmove(&value_[0], &binary[0], binary.size());
 }
 
-void
+unsigned
 TokenIpAddress::evaluate(Pkt& pkt, ValueStack& values) {
     // Literals only push, nothing to pop
     values.push(value_);
@@ -148,9 +156,11 @@ TokenIpAddress::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IPADDRESS)
         .arg(pkt.getLabel())
         .arg(toHex(value_));
+
+    return (0);
 }
 
-void
+unsigned
 TokenIpAddressToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -160,7 +170,7 @@ TokenIpAddressToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+        return (0);
     }
 
     values.pop();
@@ -183,9 +193,11 @@ TokenIpAddressToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IPADDRESSTOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
-void
+unsigned
 TokenInt8ToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -195,7 +207,7 @@ TokenInt8ToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+      return (0);
     }
 
     values.pop();
@@ -213,9 +225,11 @@ TokenInt8ToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_INT8TOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
-void
+unsigned
 TokenInt16ToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -225,7 +239,7 @@ TokenInt16ToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+      return (0);
     }
 
     values.pop();
@@ -244,9 +258,11 @@ TokenInt16ToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_INT16TOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
-void
+unsigned
 TokenInt32ToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -256,7 +272,7 @@ TokenInt32ToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+      return (0);
     }
 
     values.pop();
@@ -275,9 +291,11 @@ TokenInt32ToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_INT32TOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
-void
+unsigned
 TokenUInt8ToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -287,7 +305,7 @@ TokenUInt8ToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+      return (0);
     }
 
     values.pop();
@@ -305,9 +323,11 @@ TokenUInt8ToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT8TOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
-void
+unsigned
 TokenUInt16ToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -317,7 +337,7 @@ TokenUInt16ToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+      return (0);
     }
 
     values.pop();
@@ -336,9 +356,11 @@ TokenUInt16ToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT16TOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
-void
+unsigned
 TokenUInt32ToText::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -348,7 +370,7 @@ TokenUInt32ToText::evaluate(Pkt& pkt, ValueStack& values) {
     size_t size = op.size();
 
     if (!size) {
-        return;
+      return (0);
     }
 
     values.pop();
@@ -367,6 +389,8 @@ TokenUInt32ToText::evaluate(Pkt& pkt, ValueStack& values) {
     LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT32TOTEXT)
         .arg(pkt.getLabel())
         .arg(op);
+
+    return (0);
 }
 
 OptionPtr
@@ -374,7 +398,7 @@ TokenOption::getOption(Pkt& pkt) {
     return (pkt.getOption(option_code_));
 }
 
-void
+unsigned
 TokenOption::evaluate(Pkt& pkt, ValueStack& values) {
     OptionPtr opt = getOption(pkt);
     std::string opt_str;
@@ -412,6 +436,8 @@ TokenOption::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(option_code_)
             .arg('\'' + opt_str + '\'');
     }
+
+    return (0);
 }
 
 std::string
@@ -429,7 +455,8 @@ TokenRelay4Option::TokenRelay4Option(const uint16_t option_code,
     : TokenOption(option_code, rep_type) {
 }
 
-OptionPtr TokenRelay4Option::getOption(Pkt& pkt) {
+OptionPtr
+TokenRelay4Option::getOption(Pkt& pkt) {
     // Check if there is Relay Agent Option.
     OptionPtr rai = pkt.getOption(DHO_DHCP_AGENT_OPTIONS);
     if (!rai) {
@@ -440,7 +467,8 @@ OptionPtr TokenRelay4Option::getOption(Pkt& pkt) {
     return (rai->getOption(option_code_));
 }
 
-OptionPtr TokenRelay6Option::getOption(Pkt& pkt) {
+OptionPtr
+TokenRelay6Option::getOption(Pkt& pkt) {
     try {
         // Check if it's a Pkt6.  If it's not the dynamic_cast will
         // throw std::bad_cast.
@@ -475,7 +503,7 @@ OptionPtr TokenRelay6Option::getOption(Pkt& pkt) {
 
 }
 
-void
+unsigned
 TokenPkt::evaluate(Pkt& pkt, ValueStack& values) {
     string value;
     vector<uint8_t> binary;
@@ -524,9 +552,11 @@ TokenPkt::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg(type_str)
         .arg(print_hex ? toHex(value) : value);
+
+    return (0);
 }
 
-void
+unsigned
 TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
     vector<uint8_t> binary;
     string value;
@@ -603,9 +633,11 @@ TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg(type_str)
         .arg(toHex(value));
+
+    return (0);
 }
 
-void
+unsigned
 TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) {
     string value;
     string type_str;
@@ -643,9 +675,11 @@ TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg(type_str)
         .arg(toHex(value));
+
+    return (0);
 }
 
-void
+unsigned
 TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
     vector<uint8_t> binary;
     string type_str;
@@ -688,7 +722,7 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
                 .arg(type_str)
                 .arg(int(nest_level_))
                 .arg("0x");
-            return;
+            return (0);
         }
     } catch (const std::bad_cast&) {
         isc_throw(EvalTypeError, "Specified packet is not Pkt6");
@@ -707,9 +741,11 @@ TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(type_str)
         .arg(int(nest_level_))
         .arg(toHex(value));
+
+    return (0);
 }
 
-void
+unsigned
 TokenEqual::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 2) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -732,9 +768,11 @@ TokenEqual::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(toHex(op1))
         .arg(toHex(op2))
         .arg('\'' + values.top() + '\'');
+
+    return (0);
 }
 
-void
+unsigned
 TokenSubstring::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 3) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -759,7 +797,7 @@ TokenSubstring::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(start_str)
             .arg("0x")
             .arg("0x");
-        return;
+        return (0);
     }
 
     // Convert the starting position and length from strings to numbers
@@ -800,7 +838,7 @@ TokenSubstring::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(start_str)
             .arg(toHex(string_str))
             .arg("0x");
-        return;
+        return (0);
     }
 
     // Adjust the values to be something for substr.  We first figure out
@@ -830,9 +868,11 @@ TokenSubstring::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(start_str)
         .arg(toHex(string_str))
         .arg(toHex(values.top()));
+
+    return (0);
 }
 
-void
+unsigned
 TokenSplit::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 3) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -858,7 +898,7 @@ TokenSplit::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(delim_str)
             .arg(string_str)
             .arg("0x");
-        return;
+        return (0);
     }
 
     // Convert the field position from string to number
@@ -883,7 +923,7 @@ TokenSplit::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(delim_str)
             .arg(string_str)
             .arg(toHex(values.top()));
-        return;
+        return (0);
     }
 
     // Split the string into fields.
@@ -903,7 +943,7 @@ TokenSplit::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(delim_str)
             .arg(string_str)
             .arg("0x");
-        return;
+        return (0);
     }
 
     // Push the desired field.
@@ -916,9 +956,11 @@ TokenSplit::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(delim_str)
             .arg(string_str)
             .arg(toHex(values.top()));
+
+    return (0);
 }
 
-void
+unsigned
 TokenConcat::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 2) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -939,9 +981,11 @@ TokenConcat::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(toHex(op1))
         .arg(toHex(op2))
         .arg(toHex(values.top()));
+
+    return (0);
 }
 
-void
+unsigned
 TokenIfElse::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 3) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -976,9 +1020,11 @@ TokenIfElse::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(toHex(iftrue))
             .arg(toHex(iffalse));
     }
+
+    return (0);
 }
 
-void
+unsigned
 TokenToHexString::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 2) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -1010,9 +1056,11 @@ TokenToHexString::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(toHex(binary))
         .arg(separator)
         .arg(tmp.str());
+
+    return (0);
 }
 
-void
+unsigned
 TokenNot::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
@@ -1033,9 +1081,11 @@ TokenNot::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg('\'' + op + '\'')
         .arg('\'' + values.top() + '\'');
+
+    return (0);
 }
 
-void
+unsigned
 TokenAnd::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 2) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -1061,9 +1111,11 @@ TokenAnd::evaluate(Pkt& pkt, ValueStack& values) {
         .arg('\'' + op1 + '\'')
         .arg('\'' + op2 + '\'')
         .arg('\'' + values.top() + '\'');
+
+    return (0);
 }
 
-void
+unsigned
 TokenOr::evaluate(Pkt& pkt, ValueStack& values) {
     if (values.size() < 2) {
         isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
@@ -1089,9 +1141,11 @@ TokenOr::evaluate(Pkt& pkt, ValueStack& values) {
         .arg('\'' + op1 + '\'')
         .arg('\'' + op2 + '\'')
         .arg('\'' + values.top() + '\'');
+
+    return (0);
 }
 
-void
+unsigned
 TokenMember::evaluate(Pkt& pkt, ValueStack& values) {
     if (pkt.inClass(client_class_)) {
         values.push("true");
@@ -1104,6 +1158,8 @@ TokenMember::evaluate(Pkt& pkt, ValueStack& values) {
         .arg(pkt.getLabel())
         .arg(client_class_)
         .arg('\'' + values.top() + '\'');
+
+    return (0);
 }
 
 TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr,
@@ -1120,15 +1176,18 @@ TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field
     }
 }
 
-uint32_t TokenVendor::getVendorId() const {
+uint32_t
+TokenVendor::getVendorId() const {
     return (vendor_id_);
 }
 
-TokenVendor::FieldType TokenVendor::getField() const {
+TokenVendor::FieldType
+TokenVendor::getField() const {
     return (field_);
 }
 
-void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
+unsigned
+TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
     // Get the option first.
     uint16_t code = 0;
     switch (universe_) {
@@ -1149,7 +1208,7 @@ void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(pkt.getLabel())
             .arg(code)
             .arg(txt);
-        return;
+        return (0);
     }
 
     if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
@@ -1161,7 +1220,7 @@ void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(vendor_id_)
             .arg(vendor->getVendorId())
             .arg(txt);
-        return;
+        return (0);
     }
 
     switch (field_) {
@@ -1177,13 +1236,12 @@ void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(vendor->getVendorId())
             .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(),
                                                               txt.end())));
-        return;
+        break;
     }
     case SUBOPTION:
         /// This is vendor[X].option[Y].exists, let's try to
         /// extract the option
-        TokenOption::evaluate(pkt, values);
-        return;
+        return (TokenOption::evaluate(pkt, values));
     case EXISTS:
         // We already passed all the checks: the option is there and has specified
         // enterprise-id.
@@ -1192,15 +1250,18 @@ void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(vendor->getVendorId())
             .arg("true");
         values.push("true");
-        return;
+        break;
     case DATA:
         // This is for vendor-class option, we can skip it here.
         isc_throw(EvalTypeError, "Field None is not valid for vendor-class");
-        return;
+        break;
     }
+
+    return (0);
 }
 
-OptionPtr TokenVendor::getOption(Pkt& pkt) {
+OptionPtr
+TokenVendor::getOption(Pkt& pkt) {
    uint16_t code = 0;
     switch (universe_) {
     case Option::V4:
@@ -1233,11 +1294,13 @@ TokenVendorClass::TokenVendorClass(Option::Universe u, uint32_t vendor_id,
     field_ = field;
 }
 
-uint16_t TokenVendorClass::getDataIndex() const {
+uint16_t
+TokenVendorClass::getDataIndex() const {
     return (index_);
 }
 
-void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
+unsigned
+TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
     // Get the option first.
     uint16_t code = 0;
     switch (universe_) {
@@ -1258,7 +1321,7 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(pkt.getLabel())
             .arg(code)
             .arg(txt);
-        return;
+        return (0);
     }
 
     if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
@@ -1270,7 +1333,7 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(vendor_id_)
             .arg(vendor->getVendorId())
             .arg(txt);
-        return;
+        return (0);
     }
 
     switch (field_) {
@@ -1286,12 +1349,12 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(vendor->getVendorId())
             .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(),
                                                               txt.end())));
-        return;
+        break;
     }
     case SUBOPTION:
         // Extract sub-options
         isc_throw(EvalTypeError, "Field None is not valid for vendor-class");
-        return;
+        break;
     case EXISTS:
         // We already passed all the checks: the option is there and has specified
         // enterprise-id.
@@ -1300,7 +1363,7 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(vendor->getVendorId())
             .arg("true");
         values.push("true");
-        return;
+        break;
     case DATA:
     {
         size_t max = vendor->getTuplesNum();
@@ -1314,7 +1377,7 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
                 .arg(max)
                 .arg("");
             values.push("");
-            return;
+            break;
         }
 
         OpaqueDataTuple tuple = vendor->getTuple(index_);
@@ -1328,11 +1391,13 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(txt);
 
         values.push(txt);
-        return;
+        break;
     }
     default:
         isc_throw(EvalTypeError, "Invalid field specified." << field_);
     }
+
+    return (0);
 }
 
 TokenInteger::TokenInteger(const uint32_t value)
@@ -1347,7 +1412,7 @@ TokenSubOption::getSubOption(const OptionPtr& parent) {
     return (parent->getOption(sub_option_code_));
 }
 
-void
+unsigned
 TokenSubOption::evaluate(Pkt& pkt, ValueStack& values) {
     OptionPtr parent = getOption(pkt);
     std::string txt;
@@ -1400,6 +1465,8 @@ TokenSubOption::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(sub_option_code_)
             .arg('\'' + txt + '\'');
     }
+
+    return (0);
 }
 
 TokenMatch::TokenMatch(const std::string& reg_exp) : reg_exp_str_(reg_exp) {
@@ -1411,8 +1478,8 @@ TokenMatch::TokenMatch(const std::string& reg_exp) : reg_exp_str_(reg_exp) {
     }
 }
 
-void
-TokenMatch::evaluate(Pkt& pkt, ValueStack& values) {
+unsigned
+TokenMatch::evaluate(Pkt&, ValueStack& values) {
     if (values.size() == 0) {
         isc_throw(EvalBadStack, "Incorrect empty stack.");
     }
@@ -1435,4 +1502,17 @@ TokenMatch::evaluate(Pkt& pkt, ValueStack& values) {
             .arg(ex.what());
     }
     values.push(txt);
+
+    return (0);
+}
+
+TokenLabel::TokenLabel(const unsigned label) : label_(label) {
+    if (label == 0) {
+        isc_throw(EvalParseError, "label must be not zero");
+    }
+}
+
+unsigned
+TokenLabel::evaluate(Pkt&, ValueStack&) {
+    return (0);
 }
index 9f0c10d99bf805aee5341469ba330a1d60cb6993..e8e6fe1b5c61ab7439b54dfd77796a8155a0899d 100644 (file)
@@ -49,6 +49,13 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief EvalBadLabel is thrown when a label can't be found.
+class EvalBadLabel : public Exception {
+public:
+    EvalBadLabel(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief Base class for all tokens
 ///
 /// It provides an interface for all tokens and storage for string representation
@@ -75,11 +82,19 @@ public:
     ///
     /// @param pkt - packet being classified
     /// @param values - stack of values with previously evaluated tokens
-    virtual void evaluate(Pkt& pkt, ValueStack& values) = 0;
+    /// @return next label or 0 when means evaluate next token if any.
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values) = 0;
 
     /// @brief Virtual destructor
     virtual ~Token() {}
 
+    /// @brief Return the label of this token.
+    ///
+    /// @return the label for a TokenLabel, 0 otherwise.
+    virtual unsigned getLabel() const {
+        return (0U);
+    }
+
     /// @brief Coverts a (string) value to a boolean
     ///
     /// Only "true" and "false" are expected.
@@ -122,7 +137,7 @@ public:
     ///
     /// @param pkt (ignored)
     /// @param values (represented string will be pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
 protected:
     std::string value_; ///< Constant value
@@ -147,7 +162,7 @@ public:
     ///
     /// @param pkt (ignored)
     /// @param values (represented string will be pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
 protected:
     std::string value_; ///< Constant value
@@ -167,7 +182,7 @@ public:
     ///
     /// @param pkt (ignored)
     /// @param values (represented string will be pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing a constant upper case string
@@ -184,7 +199,7 @@ public:
     ///
     /// @param pkt (ignored)
     /// @param values (represented string will be pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing an unsigned 32 bit integer
@@ -232,7 +247,7 @@ public:
     ///
     /// @param pkt (ignored)
     /// @param values (represented IP address will be pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
 protected:
     ///< Constant value (empty string if the IP address cannot be converted)
@@ -253,7 +268,7 @@ public:
     ///
     /// @param pkt (ignored)
     /// @param values (represented IP address as a string will be pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing an 8 bit integer as a string
@@ -271,7 +286,7 @@ public:
     /// @param pkt (ignored)
     /// @param values (represented 8 bit integer as a string will be pushed
     /// here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing a 16 bit integer as a string
@@ -289,7 +304,7 @@ public:
     /// @param pkt (ignored)
     /// @param values (represented 16 bit integer as a string will be pushed
     /// here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing a 32 bit integer as a string
@@ -307,7 +322,7 @@ public:
     /// @param pkt (ignored)
     /// @param values (represented 32 bit integer as a string will be pushed
     /// here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing an 8 bit unsigned integer as a string
@@ -325,7 +340,7 @@ public:
     /// @param pkt (ignored)
     /// @param values (represented 8 bit unsigned integer as a string will be
     /// pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing a 16 bit unsigned integer as a string
@@ -343,7 +358,7 @@ public:
     /// @param pkt (ignored)
     /// @param values (represented 16 bit unsigned integer as a string will be
     /// pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token representing a 32 bit unsigned integer as a string
@@ -361,7 +376,7 @@ public:
     /// @param pkt (ignored)
     /// @param values (represented 32 bit unsigned integer as a string will be
     /// pushed here)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents a value of an option
@@ -407,7 +422,7 @@ public:
     ///
     /// @param pkt specified option will be extracted from this packet (if present)
     /// @param values value of the option will be pushed here (or "")
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns option-code
     ///
@@ -559,7 +574,7 @@ public:
     ///
     /// @param pkt - metadata will be extracted from here
     /// @param values - stack of values (1 result will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns metadata type
     ///
@@ -616,7 +631,7 @@ public:
     ///
     /// @param pkt - fields will be extracted from here
     /// @param values - stack of values (1 result will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns field type
     ///
@@ -662,7 +677,7 @@ public:
     ///
     /// @param pkt - packet from which to extract the fields
     /// @param values - stack of values, 1 result will be pushed
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns field type
     ///
@@ -716,7 +731,7 @@ public:
     ///
     /// @param pkt fields will be extracted from here
     /// @param values - stack of values (1 result will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns nest-level
     ///
@@ -766,7 +781,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (2 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents the substring operator (returns a portion
@@ -823,7 +838,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (3 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 class TokenSplit : public Token {
@@ -867,7 +882,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (3 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents concat operator (concatenates two other tokens)
@@ -891,7 +906,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (2 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents an alternative
@@ -922,7 +937,7 @@ public:
     ///
     /// @param pkt (unused)
     /// @param values - stack of values (two items are removed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that converts to hexadecimal string
@@ -961,7 +976,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (2 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents logical negation operator
@@ -986,7 +1001,7 @@ public:
     ///
     /// @param pkt (unused)
     /// @param values - stack of values (logical top value negated)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents logical and operator
@@ -1011,7 +1026,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (2 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents logical or operator
@@ -1036,7 +1051,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (2 arguments will be popped, 1 result
     ///        will be pushed)
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 };
 
 /// @brief Token that represents client class membership
@@ -1056,7 +1071,7 @@ public:
     ///
     /// @param pkt the class name will be check from this packet's client classes
     /// @param values true (if found) or false (if not found) will be pushed here
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns client class name
     ///
@@ -1155,7 +1170,7 @@ public:
     ///
     /// @param pkt - vendor options will be searched for here.
     /// @param values - the evaluated value will be pushed here.
-    virtual void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
 protected:
     /// @brief Attempts to get a suboption.
@@ -1251,7 +1266,7 @@ protected:
     ///
     /// @param pkt - vendor options will be searched for here.
     /// @param values - the evaluated value will be pushed here.
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Data chunk index.
     uint16_t index_;
@@ -1302,7 +1317,7 @@ public:
     ///
     /// @param pkt specified parent option will be extracted from this packet
     /// @param values value of the sub-option will be pushed here (or "")
-    virtual void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns sub-option-code
     ///
@@ -1344,7 +1359,7 @@ public:
     /// @param pkt (unused)
     /// @param values - stack of values (1 popped, 1 pushed)
     /// @throw EvalBadStack if there is no value on the stack
-    void evaluate(Pkt& pkt, ValueStack& values);
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
 
     /// @brief Returns regular expression.
     ///
@@ -1364,6 +1379,35 @@ private:
     std::regex reg_exp_;
 };
 
+/// @brief Token label i.e. target of branches.
+///
+/// For instance label(123): at evaluation when a branch returns 123
+/// remaining expression is scanned until label(123) is reached.
+class TokenLabel : public Token {
+public:
+    /// @brief Constructor
+    ///
+    /// @param label the label (unsigned > 0)
+    /// @throw EvalParseError when label is 0
+    TokenLabel(const unsigned label);
+
+    /// @brief Returns label.
+    ///
+    /// @return the label
+    virtual unsigned getLabel() const {
+        return (label_);
+    }
+
+    /// @brief Does nothing.
+    ///
+    /// @param pkt (unused)
+    /// @param values - stack of values (unused)
+    virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
+
+protected:
+    unsigned label_;
+};
+
 } // end of isc::dhcp namespace
 } // end of isc namespace