From be3136c164b0d3569235320bf92cfe28fa3e99c6 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Wed, 7 Aug 2024 15:38:27 +0200 Subject: [PATCH] [#3502] Checkpoint: added branch --- src/lib/eval/eval_messages.cc | 2 + src/lib/eval/eval_messages.h | 1 + src/lib/eval/eval_messages.mes | 6 +++ src/lib/eval/evaluate.cc | 27 ++++------ src/lib/eval/evaluate.h | 22 ++++++++ src/lib/eval/tests/evaluate_unittest.cc | 67 +++++++++++++++++++++++++ src/lib/eval/tests/token_unittest.cc | 14 ++++++ src/lib/eval/token.cc | 13 +++++ src/lib/eval/token.h | 29 ++++++++++- 9 files changed, 162 insertions(+), 19 deletions(-) diff --git a/src/lib/eval/eval_messages.cc b/src/lib/eval/eval_messages.cc index 5b9312b4b4..7e7ad0e721 100644 --- a/src/lib/eval/eval_messages.cc +++ b/src/lib/eval/eval_messages.cc @@ -8,6 +8,7 @@ namespace isc { namespace dhcp { extern const isc::log::MessageID EVAL_DEBUG_AND = "EVAL_DEBUG_AND"; +extern const isc::log::MessageID EVAL_DEBUG_BRANCH = "EVAL_DEBUG_BRANCH"; extern const isc::log::MessageID EVAL_DEBUG_CONCAT = "EVAL_DEBUG_CONCAT"; extern const isc::log::MessageID EVAL_DEBUG_EQUAL = "EVAL_DEBUG_EQUAL"; extern const isc::log::MessageID EVAL_DEBUG_HEXSTRING = "EVAL_DEBUG_HEXSTRING"; @@ -64,6 +65,7 @@ namespace { const char* values[] = { "EVAL_DEBUG_AND", "%1: Popping %2 and %3 pushing %4", + "EVAL_DEBUG_BRANCH", "Branching to %1", "EVAL_DEBUG_CONCAT", "%1: Popping %2 and %3 pushing %4", "EVAL_DEBUG_EQUAL", "%1: Popping %2 and %3 pushing result %4", "EVAL_DEBUG_HEXSTRING", "%1: Pushing hex string %2", diff --git a/src/lib/eval/eval_messages.h b/src/lib/eval/eval_messages.h index def72149f8..f9b2ec5acf 100644 --- a/src/lib/eval/eval_messages.h +++ b/src/lib/eval/eval_messages.h @@ -9,6 +9,7 @@ namespace isc { namespace dhcp { extern const isc::log::MessageID EVAL_DEBUG_AND; +extern const isc::log::MessageID EVAL_DEBUG_BRANCH; extern const isc::log::MessageID EVAL_DEBUG_CONCAT; extern const isc::log::MessageID EVAL_DEBUG_EQUAL; extern const isc::log::MessageID EVAL_DEBUG_HEXSTRING; diff --git a/src/lib/eval/eval_messages.mes b/src/lib/eval/eval_messages.mes index a9cb7f898e..ad81a3d229 100644 --- a/src/lib/eval/eval_messages.mes +++ b/src/lib/eval/eval_messages.mes @@ -14,6 +14,12 @@ This debug message indicates that two values are popped from the value stack. Then are then combined via logical and and the result is pushed onto the value stack. +# For use with TokenBranch +% EVAL_DEBUG_BRANCH Branching to %1 +Logged at debug log level 55. +This debug message indicates that an unconditional branch is performed +to the displayed target. + # For use with TokenConcat % EVAL_DEBUG_CONCAT %1: Popping %2 and %3 pushing %4 diff --git a/src/lib/eval/evaluate.cc b/src/lib/eval/evaluate.cc index 6b77280d5f..4b1cfdd600 100644 --- a/src/lib/eval/evaluate.cc +++ b/src/lib/eval/evaluate.cc @@ -11,9 +11,8 @@ namespace isc { namespace dhcp { -bool -evaluateBool(const Expression& expr, Pkt& pkt) { - ValueStack values; +void +evaluateRaw(const Expression& expr, Pkt& pkt, ValueStack& values) { for (auto it = expr.cbegin(); it != expr.cend(); ) { unsigned label = (*it++)->evaluate(pkt, values); if (label == 0) { @@ -29,6 +28,12 @@ evaluateBool(const Expression& expr, Pkt& pkt) { } } } +} + +bool +evaluateBool(const Expression& expr, Pkt& pkt) { + ValueStack values; + evaluateRaw(expr, pkt, values); if (values.size() != 1) { isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly " "1 value at the end of evaluation, got " << values.size()); @@ -39,21 +44,7 @@ evaluateBool(const Expression& expr, Pkt& pkt) { std::string evaluateString(const Expression& expr, Pkt& pkt) { ValueStack 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; - } - } - } + evaluateRaw(expr, pkt, values); if (values.size() != 1) { isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly " "1 value at the end of evaluation, got " << values.size()); diff --git a/src/lib/eval/evaluate.h b/src/lib/eval/evaluate.h index b2f6eb23e1..ea2a0ed72e 100644 --- a/src/lib/eval/evaluate.h +++ b/src/lib/eval/evaluate.h @@ -13,6 +13,16 @@ namespace isc { namespace dhcp { +/// @brief Evaluate a RPN expression for a v4 or v6 packet +/// +/// For tests and as the common part of the two next routines. +/// +/// @param expr the RPN expression, i.e., a vector of parsed tokens +/// @param pkt The v4 or v6 packet +/// @param values The stack of values +/// @throw EvalBadLabel if there is a foreard branch to a not found target. +void evaluateRaw(const Expression& expr, Pkt& pkt, ValueStack& values); + /// @brief Evaluate a RPN expression for a v4 or v6 packet and return /// a true or false decision /// @@ -23,9 +33,21 @@ namespace dhcp { /// stack at the end of the evaluation /// @throw EvalTypeError if the value at the top of the stack at the /// end of the evaluation is not "false" or "true" +/// @throw EvalBadLabel if there is a foreard branch to a not found target. bool evaluateBool(const Expression& expr, Pkt& pkt); +/// @brief Evaluate a RPN expression for a v4 or v6 packet and return +/// a string value +/// +/// @param expr the RPN expression, i.e., a vector of parsed tokens +/// @param pkt The v4 or v6 packet +/// @return the string value +/// @throw EvalStackError if there is not exactly one element on the value +/// stack at the end of the evaluation +/// @throw EvalTypeError if the value at the top of the stack at the +/// end of the evaluation is not "false" or "true" +/// @throw EvalBadLabel if there is a foreard branch to a not found target. std::string evaluateString(const Expression& expr, Pkt& pkt); }; // end of isc::dhcp namespace diff --git a/src/lib/eval/tests/evaluate_unittest.cc b/src/lib/eval/tests/evaluate_unittest.cc index 37c4f83d31..ecf4db79e1 100644 --- a/src/lib/eval/tests/evaluate_unittest.cc +++ b/src/lib/eval/tests/evaluate_unittest.cc @@ -512,4 +512,71 @@ TEST_F(ExpressionsTest, evaluateString) { testExpressionString(Option::V4, "hexstring(0xf01234,'..')", "f0..12..34"); } +// Tests the not found label. +TEST_F(ExpressionsTest, notFoundLabel) { + TokenPtr branch; + ASSERT_NO_THROW(branch.reset(new TokenBranch(123))); + e_.push_back(branch); + ValueStack values; + ASSERT_THROW(evaluateRaw(e_, *pkt4_, values), EvalBadLabel); + + // Add a different label and a string. + TokenPtr label; + ASSERT_NO_THROW(label.reset(new TokenLabel(111))); + e_.push_back(label); + TokenPtr foo; + ASSERT_NO_THROW(foo.reset(new TokenString("foo"))); + e_.push_back(foo); + ASSERT_THROW(evaluateRaw(e_, *pkt4_, values), EvalBadLabel); +} + +// Tests the backward label. +TEST_F(ExpressionsTest, backwardLabel) { + // Add the label before the branch. + TokenPtr label; + ASSERT_NO_THROW(label.reset(new TokenLabel(123))); + e_.push_back(label); + + TokenPtr branch; + ASSERT_NO_THROW(branch.reset(new TokenBranch(123))); + e_.push_back(branch); + ValueStack values; + ASSERT_THROW(evaluateRaw(e_, *pkt4_, values), EvalBadLabel); + + // Add a different label and a string. + TokenPtr label2; + ASSERT_NO_THROW(label2.reset(new TokenLabel(111))); + e_.push_back(label2); + TokenPtr foo; + ASSERT_NO_THROW(foo.reset(new TokenString("foo"))); + e_.push_back(foo); + ASSERT_THROW(evaluateRaw(e_, *pkt4_, values), EvalBadLabel); +} + +// Tests the found label. +TEST_F(ExpressionsTest, label) { + TokenPtr branch; + ASSERT_NO_THROW(branch.reset(new TokenBranch(123))); + e_.push_back(branch); + TokenPtr label; + ASSERT_NO_THROW(label.reset(new TokenLabel(123))); + e_.push_back(label); + TokenPtr foo; + ASSERT_NO_THROW(foo.reset(new TokenString("foo"))); + e_.push_back(foo); + string result; + ASSERT_NO_THROW(result = evaluateString(e_, *pkt6_)); + EXPECT_EQ("foo", result); + + // The branch is to the first occurence (of course the parser + // produces only one). + e_.push_back(label); + TokenPtr bar; + ASSERT_NO_THROW(bar.reset(new TokenString("bar"))); + e_.push_back(bar); + ValueStack values; + ASSERT_NO_THROW(evaluateRaw(e_, *pkt4_, values)); + EXPECT_EQ(2, values.size()); +} + }; diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index aa23b48aff..3b71f6db89 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -3902,4 +3902,18 @@ TEST_F(TokenTest, label) { ASSERT_TRUE(values_.empty()); } +// Verify TokenBranch. +TEST_F(TokenTest, branch) { + // 0 is not a valid branch. + ASSERT_THROW(t_.reset(new TokenBranch(0)), EvalParseError); + + // Evaluation does and uses nothing. + ASSERT_NO_THROW(t_.reset(new TokenBranch(123))); + EXPECT_EQ(0, t_->getLabel()); + unsigned next(0); + ASSERT_NO_THROW(next = t_->evaluate(*pkt4_, values_)); + EXPECT_EQ(123, next); + ASSERT_TRUE(values_.empty()); +} + } diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index 2249652a5b..f6d2b6cbd9 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -1516,3 +1516,16 @@ unsigned TokenLabel::evaluate(Pkt&, ValueStack&) { return (0); } + +TokenBranch::TokenBranch(const unsigned target) : target_(target) { + if (target == 0) { + isc_throw(EvalParseError, "target must be not zero"); + } +} + +unsigned +TokenBranch::evaluate(Pkt&, ValueStack&) { + LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_BRANCH) + .arg(target_); + return (target_); +} diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index e8e6fe1b5c..c7ca87512f 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -1408,7 +1408,34 @@ protected: unsigned label_; }; +/// @brief Token unconditional branch. +/// +/// Unconditionally branch to a forward target. +class TokenBranch : public Token { +public: + /// @brief Constructor + /// + /// @param target the label to branch to + /// @throw EvalParseError when target is 0 + TokenBranch(const unsigned target); + + /// @brief Returns branchtarget + /// + /// @return the label of the branch target + unsigned getTarget() const { + return (target_); + } + + /// @brief Only return the target + /// + /// @param pkt (unused) + /// @param values - stack of values (unused) + virtual unsigned evaluate(Pkt& pkt, ValueStack& values); + +protected: + unsigned target_; +}; + } // end of isc::dhcp namespace } // end of isc namespace - #endif