From: Piotrek Zadroga Date: Wed, 22 Mar 2023 12:07:23 +0000 (+0100) Subject: [#939] Adding more unit tests X-Git-Tag: Kea-2.3.6~33 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b1137ad6d7551a0b5379004f61dab879697f515f;p=thirdparty%2Fkea.git [#939] Adding more unit tests Also removing OutOfRange check from OptionOpaqueDataTuples#unpack(begin, end). Also updating AUTHORS and the ChangeLog. --- diff --git a/AUTHORS b/AUTHORS index b82b030e92..5d79917a53 100644 --- a/AUTHORS +++ b/AUTHORS @@ -31,7 +31,7 @@ Primary developers: - Slawek Figiel (documentation) - Dan Theisen (documentation, option handling, shell scripts) - Marcin Godzina (documentation, release engineering, testing) - - Piotrek Zadroga (documentation) + - Piotrek Zadroga (documentation, options handling) Former developers who are no longer active: - Stephen Morris (Hooks, MySQL) diff --git a/ChangeLog b/ChangeLog index b5742af4ce..ed2d36e83d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2116. [func] piotrek + Added support of Secure Zero Touch Provisioning options as per + RFC8572. Kea can now handle DHCPv4 Option code #143 and DHCPv6 + Option code #136. + 2115. [func] tmark Added the parameter, offer-lifetime, to kea-dhcp4. When greater than zero, the server temporarily allocates and diff --git a/src/lib/dhcp/option_opaque_data_tuples.cc b/src/lib/dhcp/option_opaque_data_tuples.cc index f70c435667..ea8fa7c91a 100644 --- a/src/lib/dhcp/option_opaque_data_tuples.cc +++ b/src/lib/dhcp/option_opaque_data_tuples.cc @@ -55,10 +55,9 @@ OptionOpaqueDataTuples::pack(isc::util::OutputBuffer& buf, bool check) const { void OptionOpaqueDataTuples::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { - if (std::distance(begin, end) < getMinimalLength() - getHeaderLen()) { - isc_throw(OutOfRange, "parsed data tuples option data truncated to" - " size " << std::distance(begin, end)); - } + // We are skipping typical OutOfRange check for Option#unpack(begin, end), + // since empty collection of tuples is also a valid case where + // std::distance(begin, end) = 0 // Start reading opaque data. size_t offset = 0; diff --git a/src/lib/dhcp/option_opaque_data_tuples.h b/src/lib/dhcp/option_opaque_data_tuples.h index 99fce83d52..8bd93af99d 100644 --- a/src/lib/dhcp/option_opaque_data_tuples.h +++ b/src/lib/dhcp/option_opaque_data_tuples.h @@ -149,13 +149,6 @@ private: /// @brief length of the field which holds he size of the tuple. OpaqueDataTuple::LengthFieldType length_field_type_; - /// @brief Returns minimal length of the option for the given universe. - /// Currently this class is only used for a DHCPv6 option it may be expanded - /// for DHCPv4 in the future. - uint16_t getMinimalLength() const { - return (4); - } - /// @brief Collection of opaque data tuples carried by the option. TuplesCollection tuples_; diff --git a/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc b/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc index cf2f7bf036..6a2c1cb814 100644 --- a/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc +++ b/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc @@ -17,6 +17,16 @@ using namespace isc::util; namespace { +// This test checks that the DHCPv4 option constructor sets the default +// properties to the expected values. +TEST(OptionOpaqueDataTuples, constructor4) { + OptionOpaqueDataTuples data_tuple(Option::V4, 200); + // Option length is 1 byte for option code + 1 byte for option size + EXPECT_EQ(2, data_tuple.len()); + // There should be no tuples. + EXPECT_EQ(0, data_tuple.getTuplesNum()); +} + // This test checks that the DHCPv6 option constructor sets the default // properties to the expected values. TEST(OptionOpaqueDataTuples, constructor6) { @@ -98,6 +108,23 @@ TEST(OptionOpaqueDataTuples, setTuple) { EXPECT_THROW(data_tuple.setTuple(2, tuple), isc::OutOfRange); } +// Check that the returned length of the DHCPv4 option is correct. +TEST(OptionOpaqueDataTuples, len4) { + OptionOpaqueDataTuples data_tuple(Option::V4, 200); + ASSERT_EQ(2, data_tuple.len()); + // Add first tuple. + OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE); + tuple = "xyz"; + ASSERT_NO_THROW(data_tuple.addTuple(tuple)); + // The total length grows by 1 byte of the length field and 3 bytes + // consumed by 'xyz'. + EXPECT_EQ(6, data_tuple.len()); + // Add another tuple and check that the total size gets increased. + tuple = "abc"; + data_tuple.addTuple(tuple); + EXPECT_EQ(10, data_tuple.len()); +} + // Check that the returned length of the DHCPv4 option is correct when // LTF is passed explicitly in constructor. TEST(OptionOpaqueDataTuples, len4_constructor_with_ltf) { @@ -134,6 +161,71 @@ TEST(OptionOpaqueDataTuples, len6) { EXPECT_EQ(14, data_tuple.len()); } +// Check that the DHCPv4 option is rendered to the buffer in wire format. +TEST(OptionOpaqueDataTuples, pack4) { + OptionOpaqueDataTuples data_tuple(Option::V4, 200); + ASSERT_EQ(0, data_tuple.getTuplesNum()); + // Add tuple. + OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE); + tuple = "Hello world"; + data_tuple.addTuple(tuple); + // And add another tuple so as resulting option is a bit more complex. + tuple = "foo"; + data_tuple.addTuple(tuple); + + // Render the data to the buffer. + OutputBuffer buf(10); + ASSERT_NO_THROW(data_tuple.pack(buf)); + ASSERT_EQ(18, buf.getLength()); + + // Prepare reference data. + const uint8_t ref[] = { + 0xC8, 0x10, // option 200, length 16 + 0x0B, // tuple length is 11 + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello + 0x77, 0x6F, 0x72, 0x6C, 0x64, // world + 0x03, // tuple length is 3 + 0x66, 0x6F, 0x6F // foo + }; + // Compare the buffer with reference data. + EXPECT_EQ(0, memcmp(static_cast(ref), + static_cast(buf.getData()), + buf.getLength())); +} + +// Check that the DHCPv4 option is rendered to the buffer in wire format, +// when tuple's length field is coded on 2 octets. +TEST(OptionOpaqueDataTuples, pack4_with_ltf) { + OptionOpaqueDataTuples data_tuple(Option::V4, 143, OpaqueDataTuple::LENGTH_2_BYTES); + ASSERT_EQ(0, data_tuple.getTuplesNum()); + // Add tuple. + OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES); + tuple = "Hello world"; + data_tuple.addTuple(tuple); + // And add another tuple so as resulting option is a bit more complex. + tuple = "foo"; + data_tuple.addTuple(tuple); + + // Render the data to the buffer. + OutputBuffer buf(10); + ASSERT_NO_THROW(data_tuple.pack(buf)); + ASSERT_EQ(20, buf.getLength()); + + // Prepare reference data. + const uint8_t ref[] = { + 0x8F, 0x12, // option 143, length 18 + 0x00, 0x0B, // tuple length is 11 + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello + 0x77, 0x6F, 0x72, 0x6C, 0x64, // world + 0x00, 0x03, // tuple length is 3 + 0x66, 0x6F, 0x6F // foo + }; + // Compare the buffer with reference data. + EXPECT_EQ(0, memcmp(static_cast(ref), + static_cast(buf.getData()), + buf.getLength())); +} + // Check that the DHCPv6 option is rendered to the buffer in wire format. TEST(OptionOpaqueDataTuples, pack6) { OptionOpaqueDataTuples data_tuple(Option::V6, 60); @@ -166,6 +258,33 @@ TEST(OptionOpaqueDataTuples, pack6) { buf.getLength())); } +// This function checks that the DHCPv4 option with two opaque data tuples +// is parsed correctly. +TEST(OptionOpaqueDataTuples, unpack4) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x0B, // tuple length is 11 + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello + 0x77, 0x6F, 0x72, 0x6C, 0x64, // world + 0x03, // tuple length is 3 + 0x66, 0x6F, 0x6F // foo + }; + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + + OptionOpaqueDataTuplesPtr data_tuple; + ASSERT_NO_THROW( + data_tuple = OptionOpaqueDataTuplesPtr( + new OptionOpaqueDataTuples(Option::V4, + 143, + buf.begin(), + buf.end())); + ); + EXPECT_EQ(DHO_V4_SZTP_REDIRECT, data_tuple->getType()); + ASSERT_EQ(2, data_tuple->getTuplesNum()); + EXPECT_EQ("Hello world", data_tuple->getTuple(0).getText()); + EXPECT_EQ("foo", data_tuple->getTuple(1).getText()); +} + // This function checks that the DHCPv4 option with two opaque data tuples // is parsed correctly. Tuple's LTF is passed explicitly in constructor. TEST(OptionOpaqueDataTuples, unpack4_constructor_with_ltf) { @@ -220,6 +339,46 @@ TEST(OptionOpaqueDataTuples, unpack6) { EXPECT_EQ("foo", data_tuple->getTuple(1).getText()); } +// This test checks that the DHCPv4 option with opaque data of size 0 +// is correctly parsed. +TEST(OptionOpaqueDataTuples, unpack4EmptyTuple) { + // Prepare data to decode. + const uint8_t buf_data[] = {0x00}; // tuple length is 0 + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + + OptionOpaqueDataTuplesPtr data_tuple; + ASSERT_NO_THROW( + data_tuple = OptionOpaqueDataTuplesPtr(new OptionOpaqueDataTuples(Option::V4, + 124, + buf.begin(), + buf.end())); + ); + EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, data_tuple->getType()); + ASSERT_EQ(1, data_tuple->getTuplesNum()); + EXPECT_TRUE(data_tuple->getTuple(0).getText().empty()); +} + +// This test checks that the DHCPv4 option with opaque data of size 0 +// is correctly parsed. Tuple's LTF is passed explicitly in constructor. +TEST(OptionOpaqueDataTuples, unpack4EmptyTuple_constructor_with_ltf) { + // Prepare data to decode. + const uint8_t buf_data[] = {0x00, 0x00}; // tuple length is 0 + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + + OptionOpaqueDataTuplesPtr data_tuple; + ASSERT_NO_THROW( + data_tuple = OptionOpaqueDataTuplesPtr( + new OptionOpaqueDataTuples(Option::V4, + 143, + buf.begin(), + buf.end(), + OpaqueDataTuple::LENGTH_2_BYTES)); + ); + EXPECT_EQ(DHO_V4_SZTP_REDIRECT, data_tuple->getType()); + ASSERT_EQ(1, data_tuple->getTuplesNum()); + EXPECT_TRUE(data_tuple->getTuple(0).getText().empty()); +} + // This test checks that the DHCPv6 option with opaque data of size 0 // is correctly parsed. TEST(OptionOpaqueDataTuples, unpack6EmptyTuple) { @@ -239,6 +398,39 @@ TEST(OptionOpaqueDataTuples, unpack6EmptyTuple) { EXPECT_TRUE(data_tuple->getTuple(0).getText().empty()); } +// This test checks that exception is thrown when parsing truncated DHCPv4 option +TEST(OptionOpaqueDataTuples, unpack4Truncated) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x0B, // tuple length is 11 + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello + 0x77, 0x6F, 0x72, 0x6C // worl (truncated d!) + }; + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + + EXPECT_THROW(OptionOpaqueDataTuples (Option::V4, 200, buf.begin(), buf.end()), + isc::dhcp::OpaqueDataTupleError); +} + +// This test checks that exception is thrown when parsing truncated DHCPv4 option, +// when tuple's length field is coded on 2 octets. +TEST(OptionOpaqueDataTuples, unpack4Truncated_with_ltf) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x00, 0x0B, // tuple length is 11 + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello + 0x77, 0x6F, 0x72, 0x6C // worl (truncated d!) + }; + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + + EXPECT_THROW(OptionOpaqueDataTuples (Option::V4, + 143, + buf.begin(), + buf.end(), + OpaqueDataTuple::LENGTH_2_BYTES), + isc::dhcp::OpaqueDataTupleError); +} + // This test checks that exception is thrown when parsing truncated DHCPv6 // bootfile-param option TEST(OptionOpaqueDataTuples, unpack6Truncated) { @@ -254,6 +446,27 @@ TEST(OptionOpaqueDataTuples, unpack6Truncated) { isc::dhcp::OpaqueDataTupleError); } +// This test checks that the DHCPv4 option containing no opaque +// data is parsed correctly. +TEST(OptionOpaqueDataTuples, unpack4NoTuple) { + // Prepare data to decode. + const uint8_t buf_data[] = { + }; + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + + OptionOpaqueDataTuplesPtr data_tuple; + ASSERT_NO_THROW( + data_tuple = OptionOpaqueDataTuplesPtr( + new OptionOpaqueDataTuples(Option::V4, + 143, + buf.begin(), + buf.end(), + OpaqueDataTuple::LENGTH_2_BYTES)); + ); + EXPECT_EQ(DHO_V4_SZTP_REDIRECT, data_tuple->getType()); + EXPECT_EQ(0, data_tuple->getTuplesNum()); +} + // This test checks that the DHCPv6 bootfile-param option containing no opaque // data is parsed correctly. TEST(OptionOpaqueDataTuples, unpack6NoTuple) { @@ -273,6 +486,30 @@ TEST(OptionOpaqueDataTuples, unpack6NoTuple) { EXPECT_EQ(0, data_tuple->getTuplesNum()); } +// Verifies correctness of the text representation of the DHCPv4 option. +TEST(OptionOpaqueDataTuples, toText4) { + OptionOpaqueDataTuples data_tuple(Option::V4, 143, OpaqueDataTuple::LENGTH_2_BYTES); + ASSERT_EQ(0, data_tuple.getTuplesNum()); + // Lets add a tuple + OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES); + tuple = "Hello world"; + data_tuple.addTuple(tuple); + // And add another tuple so as resulting option is a bit more complex. + tuple = "foo"; + data_tuple.addTuple(tuple); + // Check that the text representation of the option is as expected. + EXPECT_EQ("type=143, len=18," + " data-len0=11, data0='Hello world'," + " data-len1=3, data1='foo'", + data_tuple.toText()); + + // Check that indentation works. + EXPECT_EQ(" type=143, len=18," + " data-len0=11, data0='Hello world'," + " data-len1=3, data1='foo'", + data_tuple.toText(2)); +} + // Verifies correctness of the text representation of the DHCPv6 option. TEST(OptionOpaqueDataTuples, toText6) { OptionOpaqueDataTuples data_tuple(Option::V6, 60);