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";
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",
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;
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
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) {
}
}
}
+}
+
+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());
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());
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
///
/// 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
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());
+}
+
};
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());
+}
+
}
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_);
+}
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