2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

[#3502] Checkpoint: added branch

This commit is contained in:
Francis Dupont
2024-08-07 15:38:27 +02:00
parent 10555aac77
commit be3136c164
9 changed files with 162 additions and 19 deletions

View File

@@ -8,6 +8,7 @@ namespace isc {
namespace dhcp { namespace dhcp {
extern const isc::log::MessageID EVAL_DEBUG_AND = "EVAL_DEBUG_AND"; 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_CONCAT = "EVAL_DEBUG_CONCAT";
extern const isc::log::MessageID EVAL_DEBUG_EQUAL = "EVAL_DEBUG_EQUAL"; extern const isc::log::MessageID EVAL_DEBUG_EQUAL = "EVAL_DEBUG_EQUAL";
extern const isc::log::MessageID EVAL_DEBUG_HEXSTRING = "EVAL_DEBUG_HEXSTRING"; extern const isc::log::MessageID EVAL_DEBUG_HEXSTRING = "EVAL_DEBUG_HEXSTRING";
@@ -64,6 +65,7 @@ namespace {
const char* values[] = { const char* values[] = {
"EVAL_DEBUG_AND", "%1: Popping %2 and %3 pushing %4", "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_CONCAT", "%1: Popping %2 and %3 pushing %4",
"EVAL_DEBUG_EQUAL", "%1: Popping %2 and %3 pushing result %4", "EVAL_DEBUG_EQUAL", "%1: Popping %2 and %3 pushing result %4",
"EVAL_DEBUG_HEXSTRING", "%1: Pushing hex string %2", "EVAL_DEBUG_HEXSTRING", "%1: Pushing hex string %2",

View File

@@ -9,6 +9,7 @@ namespace isc {
namespace dhcp { namespace dhcp {
extern const isc::log::MessageID EVAL_DEBUG_AND; 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_CONCAT;
extern const isc::log::MessageID EVAL_DEBUG_EQUAL; extern const isc::log::MessageID EVAL_DEBUG_EQUAL;
extern const isc::log::MessageID EVAL_DEBUG_HEXSTRING; extern const isc::log::MessageID EVAL_DEBUG_HEXSTRING;

View File

@@ -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 value stack. Then are then combined via logical and and
the result is pushed onto the value stack. 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 # For use with TokenConcat
% EVAL_DEBUG_CONCAT %1: Popping %2 and %3 pushing %4 % EVAL_DEBUG_CONCAT %1: Popping %2 and %3 pushing %4

View File

@@ -11,9 +11,8 @@
namespace isc { namespace isc {
namespace dhcp { namespace dhcp {
bool void
evaluateBool(const Expression& expr, Pkt& pkt) { evaluateRaw(const Expression& expr, Pkt& pkt, ValueStack& values) {
ValueStack values;
for (auto it = expr.cbegin(); it != expr.cend(); ) { for (auto it = expr.cbegin(); it != expr.cend(); ) {
unsigned label = (*it++)->evaluate(pkt, values); unsigned label = (*it++)->evaluate(pkt, values);
if (label == 0) { 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) { if (values.size() != 1) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly " isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
"1 value at the end of evaluation, got " << values.size()); "1 value at the end of evaluation, got " << values.size());
@@ -39,21 +44,7 @@ evaluateBool(const Expression& expr, Pkt& pkt) {
std::string std::string
evaluateString(const Expression& expr, Pkt& pkt) { evaluateString(const Expression& expr, Pkt& pkt) {
ValueStack values; ValueStack values;
for (auto it = expr.cbegin(); it != expr.cend(); ) { evaluateRaw(expr, pkt, values);
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) { if (values.size() != 1) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly " isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
"1 value at the end of evaluation, got " << values.size()); "1 value at the end of evaluation, got " << values.size());

View File

@@ -13,6 +13,16 @@
namespace isc { namespace isc {
namespace dhcp { 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 /// @brief Evaluate a RPN expression for a v4 or v6 packet and return
/// a true or false decision /// a true or false decision
/// ///
@@ -23,9 +33,21 @@ namespace dhcp {
/// stack at the end of the evaluation /// stack at the end of the evaluation
/// @throw EvalTypeError if the value at the top of the stack at the /// @throw EvalTypeError if the value at the top of the stack at the
/// end of the evaluation is not "false" or "true" /// 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); 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); std::string evaluateString(const Expression& expr, Pkt& pkt);
}; // end of isc::dhcp namespace }; // end of isc::dhcp namespace

View File

@@ -512,4 +512,71 @@ TEST_F(ExpressionsTest, evaluateString) {
testExpressionString(Option::V4, "hexstring(0xf01234,'..')", "f0..12..34"); 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());
}
}; };

View File

@@ -3902,4 +3902,18 @@ TEST_F(TokenTest, label) {
ASSERT_TRUE(values_.empty()); 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());
}
} }

View File

@@ -1516,3 +1516,16 @@ unsigned
TokenLabel::evaluate(Pkt&, ValueStack&) { TokenLabel::evaluate(Pkt&, ValueStack&) {
return (0); 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_);
}

View File

@@ -1408,7 +1408,34 @@ protected:
unsigned label_; 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::dhcp namespace
} // end of isc namespace } // end of isc namespace
#endif #endif