|
@@ -16,6 +16,7 @@
|
|
|
|
|
|
#include <asiolink/io_address.h>
|
|
|
#include <dhcp/dhcp4.h>
|
|
|
+#include <dhcp/libdhcp++.h>
|
|
|
#include <dhcp/option_string.h>
|
|
|
#include <dhcp/pkt4.h>
|
|
|
#include <exceptions/exceptions.h>
|
|
@@ -42,40 +43,58 @@ using boost::scoped_ptr;
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
-TEST(Pkt4Test, constructor) {
|
|
|
-
|
|
|
- ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
|
|
|
- scoped_ptr<Pkt4> pkt;
|
|
|
-
|
|
|
- // Just some dummy payload.
|
|
|
- uint8_t testData[250];
|
|
|
- for (int i = 0; i < 250; i++) {
|
|
|
- testData[i] = i;
|
|
|
+/// @brief A class which contains a custom callback function to unpack options.
|
|
|
+///
|
|
|
+/// This is a class used by the tests which verify that the custom callback
|
|
|
+/// functions can be installed to unpack options from a message. When the
|
|
|
+/// callback function is called, the executed_ member is set to true to allow
|
|
|
+/// verification that the callback was really called. Internally, this class
|
|
|
+/// uses libdhcp++ to unpack options so the options parsing algorithm remains
|
|
|
+/// unchanged after installation of the callback.
|
|
|
+class CustomUnpackCallback {
|
|
|
+public:
|
|
|
+
|
|
|
+ /// @brief Constructor
|
|
|
+ ///
|
|
|
+ /// Marks that callback hasn't been called.
|
|
|
+ CustomUnpackCallback()
|
|
|
+ : executed_(false) {
|
|
|
}
|
|
|
|
|
|
- // Positive case1. Normal received packet.
|
|
|
- EXPECT_NO_THROW(pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN)));
|
|
|
-
|
|
|
- EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
|
|
|
-
|
|
|
- EXPECT_NO_THROW(pkt.reset());
|
|
|
-
|
|
|
- // Positive case2. Normal outgoing packet.
|
|
|
- EXPECT_NO_THROW(pkt.reset(new Pkt4(DHCPDISCOVER, 0xffffffff)));
|
|
|
+ /// @brief A callback
|
|
|
+ ///
|
|
|
+ /// Contains custom implementation of the callback.
|
|
|
+ ///
|
|
|
+ /// @param buf a A buffer holding options in on-wire format.
|
|
|
+ /// @param [out] options A reference to the collection where parsed options
|
|
|
+ /// will be stored.
|
|
|
+ /// @return An offset to the first byte after last parsed option.
|
|
|
+ size_t execute(const OptionBuffer& buf,
|
|
|
+ const std::string&,
|
|
|
+ isc::dhcp::OptionCollection& options) {
|
|
|
+ // Set the executed_ member to true to allow verification that the
|
|
|
+ // callback has been actually called.
|
|
|
+ executed_ = true;
|
|
|
+ // Use default implementation of the unpack algorithm to parse options.
|
|
|
+ return (LibDHCP::unpackOptions4(buf, options));
|
|
|
+ }
|
|
|
|
|
|
- // DHCPv4 packet must be at least 236 bytes long, with Message Type
|
|
|
- // Option taking extra 3 bytes it is 239
|
|
|
- EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + 3, pkt->len());
|
|
|
- EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
|
|
- EXPECT_EQ(0xffffffff, pkt->getTransid());
|
|
|
- EXPECT_NO_THROW(pkt.reset());
|
|
|
+ /// A flag which indicates if callback function has been called.
|
|
|
+ bool executed_;
|
|
|
+};
|
|
|
|
|
|
- // Negative case. Should drop truncated messages.
|
|
|
- EXPECT_THROW(
|
|
|
- pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN - 1)),
|
|
|
- OutOfRange
|
|
|
- );
|
|
|
-}
|
|
|
+/// V4 Options being used for pack/unpack testing.
|
|
|
+/// For test simplicity, all selected options have
|
|
|
+/// variable length data so as there are no restrictions
|
|
|
+/// on a length of their data.
|
|
|
+static uint8_t v4_opts[] = {
|
|
|
+ 12, 3, 0, 1, 2, // Hostname
|
|
|
+ 14, 3, 10, 11, 12, // Merit Dump File
|
|
|
+ 53, 1, 2, // Message Type (required to not throw exception during unpack)
|
|
|
+ 60, 3, 20, 21, 22, // Class Id
|
|
|
+ 128, 3, 30, 31, 32, // Vendor specific
|
|
|
+ 254, 3, 40, 41, 42, // Reserved
|
|
|
+};
|
|
|
|
|
|
// Sample data
|
|
|
const uint8_t dummyOp = BOOTREQUEST;
|
|
@@ -110,82 +129,179 @@ const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
|
|
|
BOOST_STATIC_ASSERT(sizeof(dummyFile) == Pkt4::MAX_FILE_LEN + 1);
|
|
|
BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);
|
|
|
|
|
|
-/// @brief Generates test packet.
|
|
|
-///
|
|
|
-/// Allocates and generates test packet, with all fixed fields set to non-zero
|
|
|
-/// values. Content is not always reasonable.
|
|
|
-///
|
|
|
-/// See generateTestPacket2() function that returns exactly the same packet in
|
|
|
-/// on-wire format.
|
|
|
-///
|
|
|
-/// @return pointer to allocated Pkt4 object.
|
|
|
-boost::shared_ptr<Pkt4>
|
|
|
-generateTestPacket1() {
|
|
|
-
|
|
|
- boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
|
|
|
-
|
|
|
- vector<uint8_t> vectorMacAddr(dummyMacAddr, dummyMacAddr
|
|
|
- +sizeof(dummyMacAddr));
|
|
|
-
|
|
|
- // hwType = 6(ETHERNET), hlen = 6(MAC address len)
|
|
|
- pkt->setHWAddr(dummyHtype, dummyHlen, vectorMacAddr);
|
|
|
- pkt->setHops(dummyHops); // 13 relays. Wow!
|
|
|
- // Transaction-id is already set.
|
|
|
- pkt->setSecs(dummySecs);
|
|
|
- pkt->setFlags(dummyFlags); // all flags set
|
|
|
- pkt->setCiaddr(dummyCiaddr);
|
|
|
- pkt->setYiaddr(dummyYiaddr);
|
|
|
- pkt->setSiaddr(dummySiaddr);
|
|
|
- pkt->setGiaddr(dummyGiaddr);
|
|
|
- // Chaddr already set with setHWAddr().
|
|
|
- pkt->setSname(dummySname, 64);
|
|
|
- pkt->setFile(dummyFile, 128);
|
|
|
-
|
|
|
- return (pkt);
|
|
|
-}
|
|
|
|
|
|
-/// @brief Generates test packet.
|
|
|
-///
|
|
|
-/// Allocates and generates on-wire buffer that represents test packet, with all
|
|
|
-/// fixed fields set to non-zero values. Content is not always reasonable.
|
|
|
-///
|
|
|
-/// See generateTestPacket1() function that returns exactly the same packet as
|
|
|
-/// Pkt4 object.
|
|
|
-///
|
|
|
-/// @return pointer to allocated Pkt4 object
|
|
|
-// Returns a vector containing a DHCPv4 packet header.
|
|
|
-vector<uint8_t>
|
|
|
-generateTestPacket2() {
|
|
|
-
|
|
|
- // That is only part of the header. It contains all "short" fields,
|
|
|
- // larger fields are constructed separately.
|
|
|
- uint8_t hdr[] = {
|
|
|
- 1, 6, 6, 13, // op, htype, hlen, hops,
|
|
|
- 0x12, 0x34, 0x56, 0x78, // transaction-id
|
|
|
- 0, 42, 0x80, 0x00, // 42 secs, BROADCAST flags
|
|
|
- 192, 0, 2, 1, // ciaddr
|
|
|
- 1, 2, 3, 4, // yiaddr
|
|
|
- 192, 0, 2, 255, // siaddr
|
|
|
- 255, 255, 255, 255, // giaddr
|
|
|
- };
|
|
|
+class Pkt4Test : public ::testing::Test {
|
|
|
+public:
|
|
|
+ Pkt4Test() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Generates test packet.
|
|
|
+ ///
|
|
|
+ /// Allocates and generates test packet, with all fixed fields set to non-zero
|
|
|
+ /// values. Content is not always reasonable.
|
|
|
+ ///
|
|
|
+ /// See generateTestPacket2() function that returns exactly the same packet in
|
|
|
+ /// on-wire format.
|
|
|
+ ///
|
|
|
+ /// @return pointer to allocated Pkt4 object.
|
|
|
+ Pkt4Ptr generateTestPacket1() {
|
|
|
+
|
|
|
+ boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
|
|
|
+
|
|
|
+ vector<uint8_t> vectorMacAddr(dummyMacAddr, dummyMacAddr
|
|
|
+ + sizeof(dummyMacAddr));
|
|
|
+
|
|
|
+ // hwType = 6(ETHERNET), hlen = 6(MAC address len)
|
|
|
+ pkt->setHWAddr(dummyHtype, dummyHlen, vectorMacAddr);
|
|
|
+ pkt->setHops(dummyHops); // 13 relays. Wow!
|
|
|
+ // Transaction-id is already set.
|
|
|
+ pkt->setSecs(dummySecs);
|
|
|
+ pkt->setFlags(dummyFlags); // all flags set
|
|
|
+ pkt->setCiaddr(dummyCiaddr);
|
|
|
+ pkt->setYiaddr(dummyYiaddr);
|
|
|
+ pkt->setSiaddr(dummySiaddr);
|
|
|
+ pkt->setGiaddr(dummyGiaddr);
|
|
|
+ // Chaddr already set with setHWAddr().
|
|
|
+ pkt->setSname(dummySname, 64);
|
|
|
+ pkt->setFile(dummyFile, 128);
|
|
|
+
|
|
|
+ return (pkt);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Generates test packet.
|
|
|
+ ///
|
|
|
+ /// Allocates and generates on-wire buffer that represents test packet, with all
|
|
|
+ /// fixed fields set to non-zero values. Content is not always reasonable.
|
|
|
+ ///
|
|
|
+ /// See generateTestPacket1() function that returns exactly the same packet as
|
|
|
+ /// Pkt4 object.
|
|
|
+ ///
|
|
|
+ /// @return pointer to allocated Pkt4 object
|
|
|
+ // Returns a vector containing a DHCPv4 packet header.
|
|
|
+ vector<uint8_t> generateTestPacket2() {
|
|
|
+
|
|
|
+ // That is only part of the header. It contains all "short" fields,
|
|
|
+ // larger fields are constructed separately.
|
|
|
+ uint8_t hdr[] = {
|
|
|
+ 1, 6, 6, 13, // op, htype, hlen, hops,
|
|
|
+ 0x12, 0x34, 0x56, 0x78, // transaction-id
|
|
|
+ 0, 42, 0x80, 0x00, // 42 secs, BROADCAST flags
|
|
|
+ 192, 0, 2, 1, // ciaddr
|
|
|
+ 1, 2, 3, 4, // yiaddr
|
|
|
+ 192, 0, 2, 255, // siaddr
|
|
|
+ 255, 255, 255, 255, // giaddr
|
|
|
+ };
|
|
|
+
|
|
|
+ // Initialize the vector with the header fields defined above.
|
|
|
+ vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
|
|
|
+
|
|
|
+ // Append the large header fields.
|
|
|
+ copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
|
|
|
+ copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
|
|
|
+ copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
|
|
|
+
|
|
|
+ // Should now have all the header, so check. The "static_cast" is used
|
|
|
+ // to get round an odd bug whereby the linker appears not to find the
|
|
|
+ // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
|
|
|
+ EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
|
|
|
+
|
|
|
+ return (buf);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Verify that the options are correct after parsing.
|
|
|
+ ///
|
|
|
+ /// @param pkt A packet holding parsed options.
|
|
|
+ void verifyParsedOptions(const Pkt4Ptr& pkt) {
|
|
|
+ EXPECT_TRUE(pkt->getOption(12));
|
|
|
+ EXPECT_TRUE(pkt->getOption(60));
|
|
|
+ EXPECT_TRUE(pkt->getOption(14));
|
|
|
+ EXPECT_TRUE(pkt->getOption(128));
|
|
|
+ EXPECT_TRUE(pkt->getOption(254));
|
|
|
+
|
|
|
+ boost::shared_ptr<Option> x = pkt->getOption(12);
|
|
|
+ ASSERT_TRUE(x); // option 1 should exist
|
|
|
+ // Option 12 is represented by the OptionString class so let's do
|
|
|
+ // the appropriate conversion.
|
|
|
+ OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x);
|
|
|
+ ASSERT_TRUE(option12);
|
|
|
+ EXPECT_EQ(12, option12->getType()); // this should be option 12
|
|
|
+ ASSERT_EQ(3, option12->getValue().length()); // it should be of length 3
|
|
|
+ EXPECT_EQ(5, option12->len()); // total option length 5
|
|
|
+ EXPECT_EQ(0, memcmp(&option12->getValue()[0], v4_opts + 2, 3)); // data len=3
|
|
|
+
|
|
|
+ x = pkt->getOption(14);
|
|
|
+ ASSERT_TRUE(x); // option 14 should exist
|
|
|
+ // Option 14 is represented by the OptionString class so let's do
|
|
|
+ // the appropriate conversion.
|
|
|
+ OptionStringPtr option14 = boost::static_pointer_cast<OptionString>(x);
|
|
|
+ ASSERT_TRUE(option14);
|
|
|
+ EXPECT_EQ(14, option14->getType()); // this should be option 14
|
|
|
+ ASSERT_EQ(3, option14->getValue().length()); // it should be of length 3
|
|
|
+ EXPECT_EQ(5, option14->len()); // total option length 5
|
|
|
+ EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4_opts + 7, 3)); // data len=3
|
|
|
+
|
|
|
+ x = pkt->getOption(60);
|
|
|
+ ASSERT_TRUE(x); // option 60 should exist
|
|
|
+ EXPECT_EQ(60, x->getType()); // this should be option 60
|
|
|
+ ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
|
+ EXPECT_EQ(5, x->len()); // total option length 5
|
|
|
+ EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 15, 3)); // data len=3
|
|
|
+
|
|
|
+ x = pkt->getOption(128);
|
|
|
+ ASSERT_TRUE(x); // option 3 should exist
|
|
|
+ EXPECT_EQ(128, x->getType()); // this should be option 254
|
|
|
+ ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
|
+ EXPECT_EQ(5, x->len()); // total option length 5
|
|
|
+ EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 20, 3)); // data len=3
|
|
|
+
|
|
|
+ x = pkt->getOption(254);
|
|
|
+ ASSERT_TRUE(x); // option 3 should exist
|
|
|
+ EXPECT_EQ(254, x->getType()); // this should be option 254
|
|
|
+ ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
|
+ EXPECT_EQ(5, x->len()); // total option length 5
|
|
|
+ EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 25, 3)); // data len=3
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+TEST_F(Pkt4Test, constructor) {
|
|
|
+
|
|
|
+ ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
|
|
|
+ scoped_ptr<Pkt4> pkt;
|
|
|
+
|
|
|
+ // Just some dummy payload.
|
|
|
+ uint8_t testData[250];
|
|
|
+ for (int i = 0; i < 250; i++) {
|
|
|
+ testData[i] = i;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Positive case1. Normal received packet.
|
|
|
+ EXPECT_NO_THROW(pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN)));
|
|
|
+
|
|
|
+ EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
|
|
|
|
|
|
- // Initialize the vector with the header fields defined above.
|
|
|
- vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
|
|
|
+ EXPECT_NO_THROW(pkt.reset());
|
|
|
|
|
|
- // Append the large header fields.
|
|
|
- copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
|
|
|
- copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
|
|
|
- copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
|
|
|
+ // Positive case2. Normal outgoing packet.
|
|
|
+ EXPECT_NO_THROW(pkt.reset(new Pkt4(DHCPDISCOVER, 0xffffffff)));
|
|
|
|
|
|
- // Should now have all the header, so check. The "static_cast" is used
|
|
|
- // to get round an odd bug whereby the linker appears not to find the
|
|
|
- // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
|
|
|
- EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
|
|
|
+ // DHCPv4 packet must be at least 236 bytes long, with Message Type
|
|
|
+ // Option taking extra 3 bytes it is 239
|
|
|
+ EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + 3, pkt->len());
|
|
|
+ EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
|
|
+ EXPECT_EQ(0xffffffff, pkt->getTransid());
|
|
|
+ EXPECT_NO_THROW(pkt.reset());
|
|
|
|
|
|
- return (buf);
|
|
|
+ // Negative case. Should drop truncated messages.
|
|
|
+ EXPECT_THROW(
|
|
|
+ pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN - 1)),
|
|
|
+ OutOfRange
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, fixedFields) {
|
|
|
+
|
|
|
+TEST_F(Pkt4Test, fixedFields) {
|
|
|
|
|
|
boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
|
|
|
|
|
@@ -215,7 +331,7 @@ TEST(Pkt4Test, fixedFields) {
|
|
|
EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, fixedFieldsPack) {
|
|
|
+TEST_F(Pkt4Test, fixedFieldsPack) {
|
|
|
boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
|
|
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
|
|
|
|
@@ -235,7 +351,7 @@ TEST(Pkt4Test, fixedFieldsPack) {
|
|
|
}
|
|
|
|
|
|
/// TODO Uncomment when ticket #1226 is implemented
|
|
|
-TEST(Pkt4Test, fixedFieldsUnpack) {
|
|
|
+TEST_F(Pkt4Test, fixedFieldsUnpack) {
|
|
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
|
|
|
|
|
expectedFormat.push_back(0x63); // magic cookie
|
|
@@ -282,7 +398,7 @@ TEST(Pkt4Test, fixedFieldsUnpack) {
|
|
|
}
|
|
|
|
|
|
// This test is for hardware addresses (htype, hlen and chaddr fields)
|
|
|
-TEST(Pkt4Test, hwAddr) {
|
|
|
+TEST_F(Pkt4Test, hwAddr) {
|
|
|
|
|
|
vector<uint8_t> mac;
|
|
|
uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];
|
|
@@ -329,7 +445,7 @@ TEST(Pkt4Test, hwAddr) {
|
|
|
/// longer than 16 bytes should be stored in client-identifier option
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, msgTypes) {
|
|
|
+TEST_F(Pkt4Test, msgTypes) {
|
|
|
|
|
|
struct msgType {
|
|
|
uint8_t dhcp;
|
|
@@ -366,7 +482,7 @@ TEST(Pkt4Test, msgTypes) {
|
|
|
}
|
|
|
|
|
|
// This test verifies handling of sname field
|
|
|
-TEST(Pkt4Test, sname) {
|
|
|
+TEST_F(Pkt4Test, sname) {
|
|
|
|
|
|
uint8_t sname[Pkt4::MAX_SNAME_LEN];
|
|
|
|
|
@@ -404,7 +520,7 @@ TEST(Pkt4Test, sname) {
|
|
|
EXPECT_THROW(pkt4.setSname(NULL, 0), InvalidParameter);
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, file) {
|
|
|
+TEST_F(Pkt4Test, file) {
|
|
|
|
|
|
uint8_t file[Pkt4::MAX_FILE_LEN];
|
|
|
|
|
@@ -442,20 +558,7 @@ TEST(Pkt4Test, file) {
|
|
|
EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
|
|
|
}
|
|
|
|
|
|
-/// V4 Options being used for pack/unpack testing.
|
|
|
-/// For test simplicity, all selected options have
|
|
|
-/// variable length data so as there are no restrictions
|
|
|
-/// on a length of their data.
|
|
|
-static uint8_t v4Opts[] = {
|
|
|
- 12, 3, 0, 1, 2, // Hostname
|
|
|
- 14, 3, 10, 11, 12, // Merit Dump File
|
|
|
- 53, 1, 2, // Message Type (required to not throw exception during unpack)
|
|
|
- 60, 3, 20, 21, 22, // Class Id
|
|
|
- 128, 3, 30, 31, 32, // Vendor specific
|
|
|
- 254, 3, 40, 41, 42, // Reserved
|
|
|
-};
|
|
|
-
|
|
|
-TEST(Pkt4Test, options) {
|
|
|
+TEST_F(Pkt4Test, options) {
|
|
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 0));
|
|
|
|
|
|
vector<uint8_t> payload[5];
|
|
@@ -496,10 +599,10 @@ TEST(Pkt4Test, options) {
|
|
|
);
|
|
|
|
|
|
const OutputBuffer& buf = pkt->getBuffer();
|
|
|
- // Check that all options are stored, they should take sizeof(v4Opts),
|
|
|
+ // Check that all options are stored, they should take sizeof(v4_opts),
|
|
|
// DHCP magic cookie (4 bytes), and OPTION_END added (just one byte)
|
|
|
ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) +
|
|
|
- sizeof(DHCP_OPTIONS_COOKIE) + sizeof(v4Opts) + 1,
|
|
|
+ sizeof(DHCP_OPTIONS_COOKIE) + sizeof(v4_opts) + 1,
|
|
|
buf.getLength());
|
|
|
|
|
|
// That that this extra data actually contain our options
|
|
@@ -508,8 +611,8 @@ TEST(Pkt4Test, options) {
|
|
|
// Rewind to end of fixed part.
|
|
|
ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE);
|
|
|
|
|
|
- EXPECT_EQ(0, memcmp(ptr, v4Opts, sizeof(v4Opts)));
|
|
|
- EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4Opts))));
|
|
|
+ EXPECT_EQ(0, memcmp(ptr, v4_opts, sizeof(v4_opts)));
|
|
|
+ EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4_opts))));
|
|
|
|
|
|
// delOption() checks
|
|
|
EXPECT_TRUE(pkt->getOption(12)); // Sanity check: option 12 is still there
|
|
@@ -520,7 +623,8 @@ TEST(Pkt4Test, options) {
|
|
|
EXPECT_NO_THROW(pkt.reset());
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, unpackOptions) {
|
|
|
+// This test verifies that the options are unpacked from the packet correctly.
|
|
|
+TEST_F(Pkt4Test, unpackOptions) {
|
|
|
|
|
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
|
|
|
|
@@ -529,8 +633,8 @@ TEST(Pkt4Test, unpackOptions) {
|
|
|
expectedFormat.push_back(0x53);
|
|
|
expectedFormat.push_back(0x63);
|
|
|
|
|
|
- for (int i = 0; i < sizeof(v4Opts); i++) {
|
|
|
- expectedFormat.push_back(v4Opts[i]);
|
|
|
+ for (int i = 0; i < sizeof(v4_opts); i++) {
|
|
|
+ expectedFormat.push_back(v4_opts[i]);
|
|
|
}
|
|
|
|
|
|
// now expectedFormat contains fixed format and 5 options
|
|
@@ -542,59 +646,53 @@ TEST(Pkt4Test, unpackOptions) {
|
|
|
pkt->unpack()
|
|
|
);
|
|
|
|
|
|
- EXPECT_TRUE(pkt->getOption(12));
|
|
|
- EXPECT_TRUE(pkt->getOption(60));
|
|
|
- EXPECT_TRUE(pkt->getOption(14));
|
|
|
- EXPECT_TRUE(pkt->getOption(128));
|
|
|
- EXPECT_TRUE(pkt->getOption(254));
|
|
|
+ verifyParsedOptions(pkt);
|
|
|
+}
|
|
|
+
|
|
|
+// This test verifies that it is possible to specify custom implementation of
|
|
|
+// the option parsing algorithm by installing a callback function.
|
|
|
+TEST_F(Pkt4Test, unpackOptionsWithCallback) {
|
|
|
+ vector<uint8_t> expectedFormat = generateTestPacket2();
|
|
|
+
|
|
|
+ expectedFormat.push_back(0x63);
|
|
|
+ expectedFormat.push_back(0x82);
|
|
|
+ expectedFormat.push_back(0x53);
|
|
|
+ expectedFormat.push_back(0x63);
|
|
|
+
|
|
|
+ for (int i = 0; i < sizeof(v4_opts); i++) {
|
|
|
+ expectedFormat.push_back(v4_opts[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // now expectedFormat contains fixed format and 5 options
|
|
|
+
|
|
|
+ boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
|
|
|
+ expectedFormat.size()));
|
|
|
+
|
|
|
+ CustomUnpackCallback cb;
|
|
|
+ pkt->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
|
|
|
+ _1, _2, _3));
|
|
|
+
|
|
|
+ ASSERT_FALSE(cb.executed_);
|
|
|
+
|
|
|
+ EXPECT_NO_THROW(pkt->unpack());
|
|
|
+
|
|
|
+ EXPECT_TRUE(cb.executed_);
|
|
|
+ verifyParsedOptions(pkt);
|
|
|
+
|
|
|
+ // Reset the indicator to perform another check: uninstall the callback.
|
|
|
+ cb.executed_ = false;
|
|
|
+ // By setting the callback to NULL we effectively uninstall the callback.
|
|
|
+ pkt->setCallback(NULL);
|
|
|
+ // Do another unpack.
|
|
|
+ EXPECT_NO_THROW(pkt->unpack());
|
|
|
+ // Callback should not be executed.
|
|
|
+ EXPECT_FALSE(cb.executed_);
|
|
|
|
|
|
- boost::shared_ptr<Option> x = pkt->getOption(12);
|
|
|
- ASSERT_TRUE(x); // option 1 should exist
|
|
|
- // Option 12 is represented by the OptionString class so let's do
|
|
|
- // the appropriate conversion.
|
|
|
- OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x);
|
|
|
- ASSERT_TRUE(option12);
|
|
|
- EXPECT_EQ(12, option12->getType()); // this should be option 12
|
|
|
- ASSERT_EQ(3, option12->getValue().length()); // it should be of length 3
|
|
|
- EXPECT_EQ(5, option12->len()); // total option length 5
|
|
|
- EXPECT_EQ(0, memcmp(&option12->getValue()[0], v4Opts + 2, 3)); // data len=3
|
|
|
-
|
|
|
- x = pkt->getOption(14);
|
|
|
- ASSERT_TRUE(x); // option 14 should exist
|
|
|
- // Option 14 is represented by the OptionString class so let's do
|
|
|
- // the appropriate conversion.
|
|
|
- OptionStringPtr option14 = boost::static_pointer_cast<OptionString>(x);
|
|
|
- ASSERT_TRUE(option14);
|
|
|
- EXPECT_EQ(14, option14->getType()); // this should be option 14
|
|
|
- ASSERT_EQ(3, option14->getValue().length()); // it should be of length 3
|
|
|
- EXPECT_EQ(5, option14->len()); // total option length 5
|
|
|
- EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4Opts + 7, 3)); // data len=3
|
|
|
-
|
|
|
- x = pkt->getOption(60);
|
|
|
- ASSERT_TRUE(x); // option 60 should exist
|
|
|
- EXPECT_EQ(60, x->getType()); // this should be option 60
|
|
|
- ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
|
- EXPECT_EQ(5, x->len()); // total option length 5
|
|
|
- EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 15, 3)); // data len=3
|
|
|
-
|
|
|
- x = pkt->getOption(128);
|
|
|
- ASSERT_TRUE(x); // option 3 should exist
|
|
|
- EXPECT_EQ(128, x->getType()); // this should be option 254
|
|
|
- ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
|
- EXPECT_EQ(5, x->len()); // total option length 5
|
|
|
- EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 20, 3)); // data len=3
|
|
|
-
|
|
|
- x = pkt->getOption(254);
|
|
|
- ASSERT_TRUE(x); // option 3 should exist
|
|
|
- EXPECT_EQ(254, x->getType()); // this should be option 254
|
|
|
- ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
|
- EXPECT_EQ(5, x->len()); // total option length 5
|
|
|
- EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 25, 3)); // data len=3
|
|
|
}
|
|
|
|
|
|
// This test verifies methods that are used for manipulating meta fields
|
|
|
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
|
|
|
-TEST(Pkt4Test, metaFields) {
|
|
|
+TEST_F(Pkt4Test, metaFields) {
|
|
|
|
|
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
|
|
pkt->setIface("loooopback");
|
|
@@ -608,7 +706,7 @@ TEST(Pkt4Test, metaFields) {
|
|
|
EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, Timestamp) {
|
|
|
+TEST_F(Pkt4Test, Timestamp) {
|
|
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
|
|
|
|
|
// Just after construction timestamp is invalid
|
|
@@ -634,7 +732,7 @@ TEST(Pkt4Test, Timestamp) {
|
|
|
EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
|
|
|
}
|
|
|
|
|
|
-TEST(Pkt4Test, hwaddr) {
|
|
|
+TEST_F(Pkt4Test, hwaddr) {
|
|
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
|
|
const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
|
|
|
const uint8_t hw_type = 123; // hardware type
|
|
@@ -655,7 +753,7 @@ TEST(Pkt4Test, hwaddr) {
|
|
|
|
|
|
// This test verifies that the packet remte and local HW address can
|
|
|
// be set and returned.
|
|
|
-TEST(Pkt4Test, hwaddrSrcRemote) {
|
|
|
+TEST_F(Pkt4Test, hwaddrSrcRemote) {
|
|
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
|
|
const uint8_t src_hw[] = { 1, 2, 3, 4, 5, 6 };
|
|
|
const uint8_t dst_hw[] = { 7, 8, 9, 10, 11, 12 };
|