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 "
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 "
// 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_));
// 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" ("").
// 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.
// 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.
// 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.
// (The actual packet is not used)
TEST_F(TokenTest, addressToText) {
TokenPtr address((new TokenIpAddressToText()));
+ EXPECT_EQ(0, address->getLabel());
std::vector<uint8_t> bytes;
std::string value = "10.0.0.1";
// (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<uint8_t> bytes;
std::string value = "0123456789";
// 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")
// 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_));
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);
// 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());
// 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 };
// 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);
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()));
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);
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.
// 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
// 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
// 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
// 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
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);
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.
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.
TEST_F(TokenTest, member) {
ASSERT_NO_THROW(t_.reset(new TokenMember("foo")));
+ EXPECT_EQ(0, t_->getLabel());
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
// 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");
// 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");
// 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_));
// 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);
// 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(".");
// 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);
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());
+}
+
}
using isc::util::encode::toHex;
-void
+unsigned
TokenString::evaluate(Pkt& pkt, ValueStack& values) {
// Literals only push, nothing to pop
values.push(value_);
LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_STRING)
.arg(pkt.getLabel())
.arg('\'' + value_ + '\'');
+
+ return (0);
}
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_);
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.");
.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.");
.arg(pkt.getLabel())
.arg('\'' + op + '\'')
.arg('\'' + result + '\'');
+
+ return (0);
}
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_);
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
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.");
size_t size = op.size();
if (!size) {
- return;
+ return (0);
}
values.pop();
LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT32TOTEXT)
.arg(pkt.getLabel())
.arg(op);
+
+ return (0);
}
OptionPtr
return (pkt.getOption(option_code_));
}
-void
+unsigned
TokenOption::evaluate(Pkt& pkt, ValueStack& values) {
OptionPtr opt = getOption(pkt);
std::string opt_str;
.arg(option_code_)
.arg('\'' + opt_str + '\'');
}
+
+ return (0);
}
std::string
: 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) {
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.
}
-void
+unsigned
TokenPkt::evaluate(Pkt& pkt, ValueStack& values) {
string value;
vector<uint8_t> binary;
.arg(pkt.getLabel())
.arg(type_str)
.arg(print_hex ? toHex(value) : value);
+
+ return (0);
}
-void
+unsigned
TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
vector<uint8_t> binary;
string value;
.arg(pkt.getLabel())
.arg(type_str)
.arg(toHex(value));
+
+ return (0);
}
-void
+unsigned
TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) {
string value;
string type_str;
.arg(pkt.getLabel())
.arg(type_str)
.arg(toHex(value));
+
+ return (0);
}
-void
+unsigned
TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) {
vector<uint8_t> binary;
string type_str;
.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");
.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 "
.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 "
.arg(start_str)
.arg("0x")
.arg("0x");
- return;
+ return (0);
}
// Convert the starting position and length from strings to numbers
.arg(start_str)
.arg(toHex(string_str))
.arg("0x");
- return;
+ return (0);
}
// Adjust the values to be something for substr. We first figure out
.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 "
.arg(delim_str)
.arg(string_str)
.arg("0x");
- return;
+ return (0);
}
// Convert the field position from string to number
.arg(delim_str)
.arg(string_str)
.arg(toHex(values.top()));
- return;
+ return (0);
}
// Split the string into fields.
.arg(delim_str)
.arg(string_str)
.arg("0x");
- return;
+ return (0);
}
// Push the desired field.
.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 "
.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 "
.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 "
.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.");
.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 "
.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 "
.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");
.arg(pkt.getLabel())
.arg(client_class_)
.arg('\'' + values.top() + '\'');
+
+ return (0);
}
TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr,
}
}
-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_) {
.arg(pkt.getLabel())
.arg(code)
.arg(txt);
- return;
+ return (0);
}
if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
.arg(vendor_id_)
.arg(vendor->getVendorId())
.arg(txt);
- return;
+ return (0);
}
switch (field_) {
.arg(vendor->getVendorId())
.arg(util::encode::encodeHex(std::vector<uint8_t>(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.
.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:
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_) {
.arg(pkt.getLabel())
.arg(code)
.arg(txt);
- return;
+ return (0);
}
if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
.arg(vendor_id_)
.arg(vendor->getVendorId())
.arg(txt);
- return;
+ return (0);
}
switch (field_) {
.arg(vendor->getVendorId())
.arg(util::encode::encodeHex(std::vector<uint8_t>(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.
.arg(vendor->getVendorId())
.arg("true");
values.push("true");
- return;
+ break;
case DATA:
{
size_t max = vendor->getTuplesNum();
.arg(max)
.arg("");
values.push("");
- return;
+ break;
}
OpaqueDataTuple tuple = vendor->getTuple(index_);
.arg(txt);
values.push(txt);
- return;
+ break;
}
default:
isc_throw(EvalTypeError, "Invalid field specified." << field_);
}
+
+ return (0);
}
TokenInteger::TokenInteger(const uint32_t value)
return (parent->getOption(sub_option_code_));
}
-void
+unsigned
TokenSubOption::evaluate(Pkt& pkt, ValueStack& values) {
OptionPtr parent = getOption(pkt);
std::string txt;
.arg(sub_option_code_)
.arg('\'' + txt + '\'');
}
+
+ return (0);
}
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.");
}
.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);
}
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
///
/// @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.
///
/// @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
///
/// @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
///
/// @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
///
/// @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
///
/// @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)
///
/// @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
/// @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
/// @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
/// @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
/// @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
/// @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
/// @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
///
/// @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
///
///
/// @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
///
///
/// @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
///
///
/// @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
///
///
/// @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
///
/// @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
/// @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 {
/// @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)
/// @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
///
/// @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
/// @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
///
/// @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
/// @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
/// @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
///
/// @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
///
///
/// @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.
///
/// @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_;
///
/// @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
///
/// @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.
///
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