2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 13:37:55 +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 {
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",

View File

@@ -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;

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 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

View File

@@ -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());

View File

@@ -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

View File

@@ -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());
}
};

View File

@@ -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());
}
}

View File

@@ -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_);
}

View File

@@ -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