diff options
Diffstat (limited to 'src/lib/dhcp/tests/option_int_unittest.cc')
-rw-r--r-- | src/lib/dhcp/tests/option_int_unittest.cc | 571 |
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 |