}
}
+ /// @brief checks if the given token is Pkt4 of specified type
+ /// @param token token to be checked
+ /// @param type expected type of the Pkt4 field
+ void checkTokenPkt4(const TokenPtr& token, TokenPkt4::FieldType type) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenPkt4> pkt =
+ boost::dynamic_pointer_cast<TokenPkt4>(token);
+ ASSERT_TRUE(pkt);
+
+ EXPECT_EQ(type, pkt->getType());
+ }
+
+ /// @brief Test that verifies access to the DHCPv4 packet fields.
+ ///
+ /// This test attempts to parse the expression, will check if the number
+ /// of tokens is exactly as expected and then will try to verify if the
+ /// first token represents the expected field in DHCPv4 packet.
+ ///
+ /// @param expr expression to be parsed
+ /// @param exp_type expected field type to be parsed
+ /// @param exp_tokens expected number of tokens
+ void testPkt4Field(std::string expr,
+ TokenPkt4::FieldType exp_type,
+ int exp_tokens) {
+ EvalContext eval(Option::V4);
+
+ // Parse the expression.
+ try {
+ parsed_ = eval.parseString(expr);
+ }
+ catch (const EvalParseError& ex) {
+ FAIL() << "Exception thrown: " << ex.what();
+ return;
+ }
+
+ // Parsing should succeed and return a token.
+ EXPECT_TRUE(parsed_);
+
+ // There should be exactly the expected number of tokens.
+ ASSERT_EQ(exp_tokens, eval.expression.size());
+
+ // Check that the first token is TokenPkt4 instance and has correct type.
+ checkTokenPkt4(eval.expression.at(0), exp_type);
+ }
+
/// @brief checks if the given token is a substring operator
void checkTokenSubstring(const TokenPtr& token) {
ASSERT_TRUE(token);
"<string>:1.1-6: relay4 can only be used in DHCPv4.");
}
+// Tests whether chaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldChaddr) {
+ testPkt4Field("pkt4.mac == 0x000102030405", TokenPkt4::CHADDR, 3);
+}
+
+// Tests whether hlen field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldHlen) {
+ testPkt4Field("pkt4.hlen == 0x6", TokenPkt4::HLEN, 3);
+}
+
+// Tests whether htype field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldHtype) {
+ testPkt4Field("pkt4.htype == 0x1", TokenPkt4::HTYPE, 3);
+}
+
+// Tests whether ciaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldCiaddr) {
+ testPkt4Field("pkt4.ciaddr == 192.0.2.1", TokenPkt4::CIADDR, 3);
+}
+
+// Tests whether giaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldGiaddr) {
+ testPkt4Field("pkt4.giaddr == 192.0.2.1", TokenPkt4::GIADDR, 3);
+}
+
+// Tests whether yiaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldYiaddr) {
+ testPkt4Field("pkt4.yiaddr == 192.0.2.1", TokenPkt4::YIADDR, 3);
+}
+
+// Tests whether siaddr field in DHCPv4 can be accessed.
+TEST_F(EvalContextTest, pkt4FieldSiaddr) {
+ testPkt4Field("pkt4.siaddr == 192.0.2.1", TokenPkt4::SIADDR, 3);
+}
+
// Test parsing of logical operators
TEST_F(EvalContextTest, logicalOps) {
// option.exists
checkError("foo", "<string>:1.1: Invalid character: f");
checkError(" bar", "<string>:1.2: Invalid character: b");
checkError("relay[12].hex == 'foo'", "<string>:1.1: Invalid character: r");
+ checkError("pkt4.ziaddr", "<string>:1.6: Invalid character: z");
}
// Tests some scanner/parser error cases
using namespace std;
using namespace isc::dhcp;
+using namespace isc::asiolink;
namespace {
EXPECT_EQ("false", values_.top());
}
+// Verifies if the DHCPv4 packet fields can be extracted.
+TEST_F(TokenTest, pkt4Fields) {
+ pkt4_->setGiaddr(IOAddress("192.0.2.1"));
+ pkt4_->setCiaddr(IOAddress("192.0.2.2"));
+ pkt4_->setYiaddr(IOAddress("192.0.2.3"));
+ pkt4_->setSiaddr(IOAddress("192.0.2.4"));
+
+ // We're setting hardware address to uncommon (7 bytes rather than 6 and
+ // hardware type 123) HW address. We'll use it in hlen and htype checks.
+ HWAddrPtr hw(new HWAddr(HWAddr::fromText("01:02:03:04:05:06:07", 123)));
+ pkt4_->setHWAddr(hw);
+
+ // Check hardware address field.
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::CHADDR)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ uint8_t expected_hw[] = { 1, 2, 3, 4, 5, 6, 7 };
+ ASSERT_EQ(7, values_.top().size());
+ EXPECT_EQ(0, memcmp(expected_hw, &values_.top()[0], 7));
+
+ // Check hlen value field.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HLEN)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ ASSERT_EQ(1, values_.top().size());
+ EXPECT_EQ(7, static_cast<uint8_t>(values_.top()[0]));
+
+ // Check htype value.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HTYPE)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ ASSERT_EQ(1, values_.top().size());
+ EXPECT_EQ(123, static_cast<uint8_t>(values_.top()[0]));
+
+ // Check giaddr value.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::GIADDR)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ uint8_t expected_addr[] = { 192, 0, 2, 1 };
+ ASSERT_EQ(4, values_.top().size());
+ EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+ // Check ciaddr value.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::CIADDR)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ expected_addr[3] = 2;
+ ASSERT_EQ(4, values_.top().size());
+ EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+ // Check yiaddr value.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::YIADDR)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ expected_addr[3] = 3;
+ ASSERT_EQ(4, values_.top().size());
+ EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+ // Check siaddr value.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::SIADDR)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ expected_addr[3] = 4;
+ ASSERT_EQ(4, values_.top().size());
+ EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+
+ // Check a DHCPv6 packet throws.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HLEN)));
+ EXPECT_THROW(t_->evaluate(*pkt6_, values_), EvalTypeError);
+}
+
// This test checks if a token representing an == operator is able to
// compare two values (with incorrectly built stack).
TEST_F(TokenTest, optionEqualInvalid) {
#include <eval/eval_log.h>
#include <util/encode/hex.h>
#include <asiolink/io_address.h>
+#include <dhcp/pkt4.h>
#include <boost/lexical_cast.hpp>
#include <cstring>
#include <string>
return (rai->getOption(option_code_));
}
+void
+TokenPkt4::evaluate(const Pkt& pkt, ValueStack& values) {
+
+ vector<uint8_t> binary;
+ try {
+ // Check if it's a Pkt4. If it's not, the dynamic_cast will throw
+ // std::bad_cast (failed dynamic_cast returns NULL for pointers and
+ // throws for references).
+ const Pkt4& pkt4 = dynamic_cast<const Pkt4&>(pkt);
+
+ switch (type_) {
+ case CHADDR: {
+ HWAddrPtr hwaddr = pkt4.getHWAddr();
+ if (!hwaddr) {
+ // This should never happen. Every Pkt4 should always have
+ // a hardware address.
+ isc_throw(EvalTypeError,
+ "Packet does not have hardware address");
+ }
+ binary = hwaddr->hwaddr_;
+ break;
+ }
+ case GIADDR:
+ binary = pkt4.getGiaddr().toBytes();
+ break;
+
+ case CIADDR:
+ binary = pkt4.getCiaddr().toBytes();
+ break;
+
+ case YIADDR:
+ binary = pkt4.getYiaddr().toBytes();
+ break;
+
+ case SIADDR:
+ binary = pkt4.getSiaddr().toBytes();
+ break;
+
+ case HLEN:
+ binary.assign(1, pkt4.getHlen());
+ break;
+
+ case HTYPE:
+ binary.assign(1, pkt4.getHtype());
+ break;
+
+ default:
+ isc_throw(EvalTypeError, "Bad field specified: "
+ << static_cast<int>(type_) );
+ }
+
+ } catch (const std::bad_cast&) {
+ isc_throw(EvalTypeError, "Specified packet is not a Pkt4");
+ }
+
+ string value;
+ value.resize(binary.size());
+ if (!binary.empty()) {
+ memmove(&value[0], &binary[0], binary.size());
+ }
+ values.push(value);
+}
+
void
TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
virtual OptionPtr getOption(const Pkt& pkt);
};
+/// @brief Token that represents fields of a DHCPv4 packet.
+///
+/// For example in the expression pkt4.chaddr == 0x0102030405
+/// this token represents the pkt4.chaddr expression.
+///
+/// Currently supported fields are:
+/// - chaddr (client hardware address, hlen [0..16] octets)
+/// - giaddr (relay agent IP address, 4 octets)
+/// - ciaddr (client IP address, 4 octets)
+/// - yiaddr ('your' (client) IP address, 4 octets)
+/// - siaddr (next server IP address, 4 octets)
+/// - hlen (hardware address length, 1 octet)
+/// - htype (hardware address type, 1 octet)
+class TokenPkt4 : public Token {
+public:
+
+ /// @brief enum value that determines the field.
+ enum FieldType {
+ CHADDR, ///< chaddr field (up to 16 bytes link-layer address)
+ GIADDR, ///< giaddr (IPv4 address)
+ CIADDR, ///< ciaddr (IPv4 address)
+ YIADDR, ///< yiaddr (IPv4 address)
+ SIADDR, ///< siaddr (IPv4 address)
+ HLEN, ///< hlen (hardware address length)
+ HTYPE ///< htype (hardware address type)
+ };
+
+ /// @brief Constructor (does nothing)
+ TokenPkt4(const FieldType type)
+ : type_(type) {}
+
+ /// @brief Gets a value from the specified packet.
+ ///
+ /// Evaluation uses fields available in the packet. It does not require
+ /// any values to be present on the stack.
+ ///
+ /// @throw EvalTypeError when called for DHCPv6 packet
+ ///
+ /// @param pkt - fields will be extracted from here
+ /// @param values - stack of values (1 result will be pushed)
+ void evaluate(const Pkt& pkt, ValueStack& values);
+
+ /// @brief Returns field type
+ ///
+ /// This method is used only in tests.
+ /// @return type of the field.
+ FieldType getType() {
+ return (type_);
+ }
+
+private:
+ /// @brief Specifies field of the DHCPv4 packet
+ FieldType type_;
+};
+
/// @brief Token that represents equality operator (compares two other tokens)
///
/// For example in the expression option[vendor-class].text == "MSFT"