Commit d92767e4 authored by Francis Dupont's avatar Francis Dupont
Browse files

[4268a] Rebased TokenPkt4 code

parent 3b5f6d63
......@@ -132,6 +132,51 @@ public:
}
}
/// @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);
......@@ -430,6 +475,41 @@ TEST_F(EvalContextTest, relay4Error) {
"<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
......@@ -588,6 +668,7 @@ TEST_F(EvalContextTest, scanErrors) {
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
......
......@@ -20,6 +20,7 @@
using namespace std;
using namespace isc::dhcp;
using namespace isc::asiolink;
namespace {
......@@ -603,6 +604,84 @@ TEST_F(TokenTest, relay4RAIOnly) {
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) {
......
......@@ -8,6 +8,7 @@
#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>
......@@ -123,6 +124,69 @@ OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) {
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) {
......
......@@ -288,6 +288,61 @@ protected:
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"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment