summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/tests/option_int_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/tests/option_int_unittest.cc')
-rw-r--r--src/lib/dhcp/tests/option_int_unittest.cc571
1 files changed, 571 insertions, 0 deletions
diff --git a/src/lib/dhcp/tests/option_int_unittest.cc b/src/lib/dhcp/tests/option_int_unittest.cc
new file mode 100644
index 0000000..a400173
--- /dev/null
+++ b/src/lib/dhcp/tests/option_int_unittest.cc
@@ -0,0 +1,571 @@
+// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_int.h>
+#include <util/buffer.h>
+
+#include <boost/pointer_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace {
+
+/// Option code being used in many test cases.
+const uint16_t TEST_OPT_CODE = 232;
+
+/// @brief OptionInt test class.
+class OptionIntTest : public ::testing::Test {
+public:
+ /// @brief Constructor.
+ ///
+ /// Initializes the option buffer with some data.
+ OptionIntTest(): buf_(255), out_buf_(255) {
+ for (unsigned i = 0; i < 255; i++) {
+ buf_[i] = 255 - i;
+ }
+ }
+
+ /// @brief Basic test for int8 and uint8 types.
+ ///
+ /// @note this function does not perform type check. Make
+ /// sure that only int8_t or uint8_t type is used.
+ ///
+ /// @param u universe (V4 or V6).
+ /// @tparam T int8_t or uint8_t.
+ template<typename T>
+ void basicTest8(const Option::Universe u) {
+ // Create option that conveys single 8 bit integer value.
+ boost::shared_ptr<OptionInt<T> > opt;
+ // Initialize buffer with this value.
+ buf_[0] = 0xa1;
+ // Constructor may throw in case provided buffer is too short.
+ ASSERT_NO_THROW(
+ opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+ TEST_OPT_CODE,
+ buf_.begin(),
+ buf_.begin() + 1))
+ );
+
+ EXPECT_EQ(u, opt->getUniverse());
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ // Option should return the same value that we initialized the first
+ // byte of the buffer with.
+ EXPECT_EQ(static_cast<T>(0xa1), opt->getValue());
+
+ // test for pack()
+ opt->pack(out_buf_);
+
+ // Data length is 1 byte.
+ EXPECT_EQ(1, opt->len() - opt->getHeaderLen());
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ // The total length is 1 byte for data and 2 bytes or 4 bytes
+ // for option code and option length.
+ if (u == Option::V4) {
+ EXPECT_EQ(3, out_buf_.getLength());
+ } else {
+ EXPECT_EQ(5, out_buf_.getLength());
+ }
+
+ // Check if pack worked properly:
+ InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+ if (u == Option::V4) {
+ // if option type is correct
+ EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+ // if option length is correct
+ EXPECT_EQ(1, out.readUint8());
+ } else {
+ // if option type is correct
+ EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+ // if option length is correct
+ EXPECT_EQ(1, out.readUint16());
+ }
+ // if data is correct
+ EXPECT_EQ(0xa1, out.readUint8() );
+ }
+
+ /// @brief Basic test for int16 and uint16 types.
+ ///
+ /// @note this function does not perform type check. Make
+ /// sure that only int16_t or uint16_t type is used.
+ ///
+ /// @param u universe (V4 or V6)
+ /// @tparam T int16_t or uint16_t.
+ template<typename T>
+ void basicTest16(const Option::Universe u) {
+ // Create option that conveys single 16-bit integer value.
+ boost::shared_ptr<OptionInt<T> > opt;
+ // Initialize buffer with uint16_t value.
+ buf_[0] = 0xa1;
+ buf_[1] = 0xa2;
+ // Constructor may throw in case provided buffer is too short.
+ ASSERT_NO_THROW(
+ opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+ TEST_OPT_CODE,
+ buf_.begin(),
+ buf_.begin() + 2))
+ );
+
+ EXPECT_EQ(u, opt->getUniverse());
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ // Option should return the value equal to the contents of first
+ // and second byte of the buffer.
+ EXPECT_EQ(static_cast<T>(0xa1a2), opt->getValue());
+
+ // Test for pack()
+ opt->pack(out_buf_);
+
+ // Data length is 2 bytes.
+ EXPECT_EQ(2, opt->len() - opt->getHeaderLen());
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ // The total length is 2 bytes for data and 2 or 4 bytes for a header.
+ if (u == Option::V4) {
+ EXPECT_EQ(4, out_buf_.getLength());
+ } else {
+ EXPECT_EQ(6, out_buf_.getLength());
+ }
+
+ // Check if pack worked properly:
+ InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+ if (u == Option::V4) {
+ // if option type is correct
+ EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+ // if option length is correct
+ EXPECT_EQ(2, out.readUint8());
+ } else {
+ // if option type is correct
+ EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+ // if option length is correct
+ EXPECT_EQ(2, out.readUint16());
+ }
+ // if data is correct
+ EXPECT_EQ(0xa1a2, out.readUint16() );
+ }
+
+ /// @brief Basic test for int32 and uint32 types.
+ ///
+ /// @note this function does not perform type check. Make
+ /// sure that only int32_t or uint32_t type is used.
+ ///
+ /// @param u universe (V4 or V6).
+ /// @tparam T int32_t or uint32_t.
+ template<typename T>
+ void basicTest32(const Option::Universe u) {
+ // Create option that conveys single 32-bit integer value.
+ boost::shared_ptr<OptionInt<T> > opt;
+ // Initialize buffer with 32-bit integer value.
+ buf_[0] = 0xa1;
+ buf_[1] = 0xa2;
+ buf_[2] = 0xa3;
+ buf_[3] = 0xa4;
+ // Constructor may throw in case provided buffer is too short.
+ ASSERT_NO_THROW(
+ opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+ TEST_OPT_CODE,
+ buf_.begin(),
+ buf_.begin() + 4))
+ );
+
+ EXPECT_EQ(u, opt->getUniverse());
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ // Option should return the value equal to the value made of
+ // first 4 bytes of the buffer.
+ EXPECT_EQ(static_cast<T>(0xa1a2a3a4), opt->getValue());
+
+ // Test for pack()
+ opt->pack(out_buf_);
+
+ // Data length is 4 bytes.
+ EXPECT_EQ(4, opt->len() - opt->getHeaderLen());
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ // The total length is 4 bytes for data and 2 or 4 bytes for a header.
+ if (u == Option::V4) {
+ EXPECT_EQ(6, out_buf_.getLength());
+ } else {
+ EXPECT_EQ(8, out_buf_.getLength());
+ }
+
+ // Check if pack worked properly:
+ InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+ if (u == Option::V4) {
+ // if option type is correct
+ EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+ // if option length is correct
+ EXPECT_EQ(4, out.readUint8());
+ } else {
+ // if option type is correct
+ EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+ // if option length is correct
+ EXPECT_EQ(4, out.readUint16());
+ }
+ // if data is correct
+ EXPECT_EQ(0xa1a2a3a4, out.readUint32());
+ }
+
+ OptionBuffer buf_; ///< Option buffer
+ OutputBuffer out_buf_; ///< Output buffer
+};
+
+/// @todo: below, there is a bunch of tests for options that
+/// convey unsigned value. We should maybe extend these tests for
+/// signed types too.
+
+TEST_F(OptionIntTest, useInvalidType) {
+ EXPECT_THROW(
+ boost::scoped_ptr<OptionInt<bool> >(new OptionInt<bool>(Option::V6,
+ D6O_ELAPSED_TIME, 10)),
+ InvalidDataType
+ );
+
+ EXPECT_THROW(
+ boost::scoped_ptr<OptionInt<int64_t> >(new OptionInt<int64_t>(Option::V6,
+ D6O_ELAPSED_TIME, 10)),
+ InvalidDataType
+ );
+
+}
+
+TEST_F(OptionIntTest, basicUint8V4) {
+ basicTest8<uint8_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint8V6) {
+ basicTest8<uint8_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicUint16V4) {
+ basicTest16<uint16_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint16V6) {
+ basicTest16<uint16_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicUint32V4) {
+ basicTest32<uint32_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint32V6) {
+ basicTest32<uint32_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt8V4) {
+ basicTest8<int8_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt8V6) {
+ basicTest8<int8_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt16V4) {
+ basicTest16<int16_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt16V6) {
+ basicTest16<int16_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt32V4) {
+ basicTest32<int32_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt32V6) {
+ basicTest32<int32_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, setValueUint8) {
+ boost::shared_ptr<OptionInt<uint8_t> > opt(new OptionInt<uint8_t>(Option::V6,
+ D6O_PREFERENCE, 123));
+ // Check if constructor initialized the option value correctly.
+ EXPECT_EQ(123, opt->getValue());
+ // Override the value.
+ opt->setValue(111);
+
+ EXPECT_EQ(Option::V6, opt->getUniverse());
+ EXPECT_EQ(D6O_PREFERENCE, opt->getType());
+ // Check if the value has been overridden.
+ EXPECT_EQ(111, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueInt8) {
+ boost::shared_ptr<OptionInt<int8_t> > opt(new OptionInt<int8_t>(Option::V6,
+ D6O_PREFERENCE, -123));
+ // Check if constructor initialized the option value correctly.
+ EXPECT_EQ(-123, opt->getValue());
+ // Override the value.
+ opt->setValue(-111);
+
+ EXPECT_EQ(Option::V6, opt->getUniverse());
+ EXPECT_EQ(D6O_PREFERENCE, opt->getType());
+ // Check if the value has been overridden.
+ EXPECT_EQ(-111, opt->getValue());
+}
+
+
+TEST_F(OptionIntTest, setValueUint16) {
+ boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(Option::V6,
+ D6O_ELAPSED_TIME, 123));
+ // Check if constructor initialized the option value correctly.
+ EXPECT_EQ(123, opt->getValue());
+ // Override the value.
+ opt->setValue(0x0102);
+
+ EXPECT_EQ(Option::V6, opt->getUniverse());
+ EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
+ // Check if the value has been overridden.
+ EXPECT_EQ(0x0102, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueInt16) {
+ boost::shared_ptr<OptionInt<int16_t> > opt(new OptionInt<int16_t>(Option::V6,
+ D6O_ELAPSED_TIME, -16500));
+ // Check if constructor initialized the option value correctly.
+ EXPECT_EQ(-16500, opt->getValue());
+ // Override the value.
+ opt->setValue(-20100);
+
+ EXPECT_EQ(Option::V6, opt->getUniverse());
+ EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
+ // Check if the value has been overridden.
+ EXPECT_EQ(-20100, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueUint32) {
+ boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(Option::V6,
+ D6O_CLT_TIME, 123));
+ // Check if constructor initialized the option value correctly.
+ EXPECT_EQ(123, opt->getValue());
+ // Override the value.
+ opt->setValue(0x01020304);
+
+ EXPECT_EQ(Option::V6, opt->getUniverse());
+ EXPECT_EQ(D6O_CLT_TIME, opt->getType());
+ // Check if the value has been overridden.
+ EXPECT_EQ(0x01020304, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueInt32) {
+ boost::shared_ptr<OptionInt<int32_t> > opt(new OptionInt<int32_t>(Option::V6,
+ D6O_CLT_TIME, -120100));
+ // Check if constructor initialized the option value correctly.
+ EXPECT_EQ(-120100, opt->getValue());
+ // Override the value.
+ opt->setValue(-125000);
+
+ EXPECT_EQ(Option::V6, opt->getUniverse());
+ EXPECT_EQ(D6O_CLT_TIME, opt->getType());
+ // Check if the value has been overridden.
+ EXPECT_EQ(-125000, opt->getValue());
+}
+
+TEST_F(OptionIntTest, packSuboptions4) {
+ boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(Option::V4,
+ TEST_OPT_CODE,
+ 0x0102));
+ // Add sub option with some 4 bytes of data (each byte set to 1)
+ OptionPtr sub1(new Option(Option::V4, TEST_OPT_CODE + 1, OptionBuffer(4, 1)));
+ // Add sub option with some 5 bytes of data (each byte set to 2)
+ OptionPtr sub2(new Option(Option::V4, TEST_OPT_CODE + 2, OptionBuffer(5, 2)));
+
+ // Add suboptions.
+ opt->addOption(sub1);
+ opt->addOption(sub2);
+
+ // Prepare reference data: option + suboptions in wire format.
+ uint8_t expected[] = {
+ TEST_OPT_CODE, 15, // option header
+ 0x01, 0x02, // data, uint16_t value = 0x0102
+ TEST_OPT_CODE + 1, 0x04, 0x01, 0x01, 0x01, 0x01, // sub1
+ TEST_OPT_CODE + 2, 0x05, 0x02, 0x02, 0x02, 0x02, 0x02 // sub2
+ };
+
+ // Create on-wire format of option and suboptions.
+ opt->pack(out_buf_);
+ // Compare the on-wire data with the reference buffer.
+ ASSERT_EQ(sizeof(expected), out_buf_.getLength());
+ EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, sizeof(expected)));
+}
+
+TEST_F(OptionIntTest, packSuboptions6) {
+ // option code is really uint16_t, but using uint8_t
+ // for easier conversion to uint8_t array.
+ uint8_t opt_code = 80;
+
+ boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(Option::V6,
+ opt_code, 0x01020304));
+ OptionPtr sub1(new Option(Option::V6, 0xcafe));
+
+ boost::shared_ptr<Option6IAAddr> addr1(
+ new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"), 0x5000, 0x7000));
+
+ opt->addOption(sub1);
+ opt->addOption(addr1);
+
+ ASSERT_EQ(28, addr1->len());
+ ASSERT_EQ(4, sub1->len());
+ ASSERT_EQ(40, opt->len());
+
+ uint8_t expected[] = {
+ 0, opt_code, // type
+ 0, 36, // length
+ 0x01, 0x02, 0x03, 0x04, // uint32_t value
+
+ // iaaddr suboption
+ D6O_IAADDR / 256, D6O_IAADDR % 256, // type
+ 0, 24, // len
+ 0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
+ 0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
+ 0, 0, 0x50, 0, // preferred-lifetime
+ 0, 0, 0x70, 0, // valid-lifetime
+
+ // suboption
+ 0xca, 0xfe, // type
+ 0, 0 // len
+ };
+
+ // Create on-wire format of option and suboptions.
+ opt->pack(out_buf_);
+ // Compare the on-wire data with the reference buffer.
+ ASSERT_EQ(40, out_buf_.getLength());
+ EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, 40));
+}
+
+TEST_F(OptionIntTest, unpackSuboptions4) {
+ // Prepare reference data.
+ const uint8_t expected[] = {
+ TEST_OPT_CODE, 0x0A, // option code and length
+ 0x01, 0x02, 0x03, 0x04, // data, uint32_t value = 0x01020304
+ TEST_OPT_CODE + 1, 0x4, 0x01, 0x01, 0x01, 0x01 // suboption
+ };
+ // Make sure that the buffer size is sufficient to copy the
+ // elements from the array.
+ ASSERT_GE(buf_.size(), sizeof(expected));
+ // Copy the data to a vector so as we can pass it to the
+ // OptionInt's constructor.
+ memcpy(&buf_[0], expected, sizeof(expected));
+
+ // Create an option.
+ boost::shared_ptr<OptionInt<uint32_t> > opt;
+ EXPECT_NO_THROW(
+ opt = boost::shared_ptr<
+ OptionInt<uint32_t> >(new OptionInt<uint32_t>(Option::V4, TEST_OPT_CODE,
+ buf_.begin() + 2,
+ buf_.begin() + sizeof(expected)));
+ );
+ ASSERT_TRUE(opt);
+
+ // Verify that it has expected type and data.
+ EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+ EXPECT_EQ(0x01020304, opt->getValue());
+
+ // Expect that there is the sub option with the particular
+ // option code added.
+ OptionPtr subopt = opt->getOption(TEST_OPT_CODE + 1);
+ ASSERT_TRUE(subopt);
+ // Check that this option has correct universe and code.
+ EXPECT_EQ(Option::V4, subopt->getUniverse());
+ EXPECT_EQ(TEST_OPT_CODE + 1, subopt->getType());
+ // Check the sub option's data.
+ OptionBuffer subopt_buf = subopt->getData();
+ ASSERT_EQ(4, subopt_buf.size());
+ // The data in the input buffer starts at offset 8.
+ EXPECT_TRUE(std::equal(subopt_buf.begin(), subopt_buf.end(), buf_.begin() + 8));
+}
+
+TEST_F(OptionIntTest, unpackSuboptions6) {
+ // option code is really uint16_t, but using uint8_t
+ // for easier conversion to uint8_t array.
+ const uint8_t opt_code = 80;
+ // Prepare reference data.
+ uint8_t expected[] = {
+ 0, opt_code, // type
+ 0, 34, // length
+ 0x01, 0x02, // uint16_t value
+
+ // iaaddr suboption
+ D6O_IAADDR / 256, D6O_IAADDR % 256, // type
+ 0, 24, // len
+ 0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
+ 0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
+ 0, 0, 0x50, 0, // preferred-lifetime
+ 0, 0, 0x70, 0, // valid-lifetime
+
+ // suboption
+ 0xca, 0xfe, // type
+ 0, 0 // len
+ };
+ ASSERT_EQ(38, sizeof(expected));
+
+ // Make sure that the buffer's size is sufficient to
+ // copy the elements from the array.
+ ASSERT_GE(buf_.size(), sizeof(expected));
+ memcpy(&buf_[0], expected, sizeof(expected));
+
+ boost::shared_ptr<OptionInt<uint16_t> > opt;
+ EXPECT_NO_THROW(
+ opt = boost::shared_ptr<
+ OptionInt<uint16_t> >(new OptionInt<uint16_t>(Option::V6, opt_code,
+ buf_.begin() + 4,
+ buf_.begin() + sizeof(expected)));
+ );
+ ASSERT_TRUE(opt);
+
+ EXPECT_EQ(opt_code, opt->getType());
+ EXPECT_EQ(0x0102, opt->getValue());
+
+ // Checks for address option
+ OptionPtr subopt = opt->getOption(D6O_IAADDR);
+ ASSERT_TRUE(subopt);
+ boost::shared_ptr<Option6IAAddr> addr(boost::dynamic_pointer_cast<Option6IAAddr>(subopt));
+ ASSERT_TRUE(addr);
+
+ EXPECT_EQ(D6O_IAADDR, addr->getType());
+ EXPECT_EQ(28, addr->len());
+ EXPECT_EQ(0x5000, addr->getPreferred());
+ EXPECT_EQ(0x7000, addr->getValid());
+ EXPECT_EQ("2001:db8:1234:5678::abcd", addr->getAddress().toText());
+
+ // Checks for dummy option
+ subopt = opt->getOption(0xcafe);
+ ASSERT_TRUE(subopt); // should be non-NULL
+
+ EXPECT_EQ(0xcafe, subopt->getType());
+ EXPECT_EQ(4, subopt->len());
+ // There should be no data at all
+ EXPECT_EQ(0, subopt->getData().size());
+
+ // Try to get non-existent option.
+ subopt = opt->getOption(1);
+ // Expecting NULL which means that option does not exist.
+ ASSERT_FALSE(subopt);
+}
+
+// This test checks that the toText function returns the option in the
+// textual format correctly.
+TEST_F(OptionIntTest, toText) {
+ OptionUint32 option(Option::V4, 128, 345678);
+ EXPECT_EQ("type=128, len=004: 345678 (uint32)", option.toText());
+
+ option.addOption(OptionPtr(new OptionUint16(Option::V4, 1, 234)));
+ option.addOption(OptionPtr(new OptionUint8(Option::V4, 3, 22)));
+ EXPECT_EQ("type=128, len=011: 345678 (uint32),\n"
+ "options:\n"
+ " type=001, len=002: 234 (uint16)\n"
+ " type=003, len=001: 22 (uint8)",
+ option.toText());
+}
+
+} // anonymous namespace