From: Francis Dupont Date: Wed, 7 Aug 2024 10:37:30 +0000 (+0200) Subject: [#3502] Checkpoint: added label X-Git-Tag: Kea-2.7.2~54 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=3688fcf2692ab12327f7a379d21d8eeb68fbf10f;p=thirdparty%2Fkea.git [#3502] Checkpoint: added label --- diff --git a/src/lib/eval/evaluate.cc b/src/lib/eval/evaluate.cc index 27bad0e704..6b77280d5f 100644 --- a/src/lib/eval/evaluate.cc +++ b/src/lib/eval/evaluate.cc @@ -11,10 +11,23 @@ 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 " diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index 2b87ef8ac0..40c9d4a412 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -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 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 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()); +} + } diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index dd439c119f..2249652a5b 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -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 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 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 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(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(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); } diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index 9f0c10d99b..e8e6fe1b5c 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -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