|
@@ -0,0 +1,393 @@
|
|
|
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
|
|
+//
|
|
|
+// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
+// purpose with or without fee is hereby granted, provided that the above
|
|
|
+// copyright notice and this permission notice appear in all copies.
|
|
|
+//
|
|
|
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
|
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
+// PERFORMANCE OF THIS SOFTWARE.
|
|
|
+
|
|
|
+#include <config.h>
|
|
|
+
|
|
|
+#include <exceptions/exceptions.h>
|
|
|
+#include <dhcp/option_vendor_class.h>
|
|
|
+#include <util/buffer.h>
|
|
|
+#include <gtest/gtest.h>
|
|
|
+
|
|
|
+using namespace isc;
|
|
|
+using namespace isc::dhcp;
|
|
|
+using namespace isc::util;
|
|
|
+
|
|
|
+namespace {
|
|
|
+
|
|
|
+// This test checks that the DHCPv4 option constructor sets the default
|
|
|
+// properties to the expected values. This constructor should add an
|
|
|
+// empty opaque data tuple (it is essentially the same as adding a 1-byte
|
|
|
+// long field which carries a value of 0).
|
|
|
+TEST(OptionVendorClass, constructor4) {
|
|
|
+ OptionVendorClass vendor_class(Option::V4, 1234);
|
|
|
+ EXPECT_EQ(1234, vendor_class.getVendorId());
|
|
|
+ // Option length is 1 byte for header + 1 byte for option size +
|
|
|
+ // 4 bytes of enterprise id + 1 byte for opaque data.
|
|
|
+ EXPECT_EQ(7, vendor_class.len());
|
|
|
+ // There should be one empty tuple.
|
|
|
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
|
|
|
+ EXPECT_EQ(0, vendor_class.getTuple(0).getLength());
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that the DHCPv6 option constructor sets the default
|
|
|
+// properties to the expected values.
|
|
|
+TEST(OptionVendorClass, constructor6) {
|
|
|
+ OptionVendorClass vendor_class(Option::V6, 2345);
|
|
|
+ EXPECT_EQ(2345, vendor_class.getVendorId());
|
|
|
+ // Option length is 2 bytes for option code + 2 bytes for option size +
|
|
|
+ // 4 bytes of enterprise id.
|
|
|
+ EXPECT_EQ(8, vendor_class.len());
|
|
|
+ // There should be no tuples.
|
|
|
+ EXPECT_EQ(0, vendor_class.getTuplesNum());
|
|
|
+}
|
|
|
+
|
|
|
+// This test verifies that it is possible to append the opaque data tuple
|
|
|
+// to the option and then retrieve it.
|
|
|
+TEST(OptionVendorClass, addTuple) {
|
|
|
+ OptionVendorClass vendor_class(Option::V6, 2345);
|
|
|
+ // Initially there should be no tuples (for DHCPv6).
|
|
|
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
|
|
|
+ // Create a new tuple and add it to the option.
|
|
|
+ OpaqueDataTuple tuple;
|
|
|
+ tuple = "xyz";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+ // The option should now hold one tuple.
|
|
|
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
|
|
|
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
|
|
|
+ // Add another tuple.
|
|
|
+ tuple = "abc";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+ // The option should now hold exactly two tuples in the order in which
|
|
|
+ // they were added.
|
|
|
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
|
|
|
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
|
|
|
+ EXPECT_EQ("abc", vendor_class.getTuple(1).getText());
|
|
|
+
|
|
|
+ // Attempt to add the tuple with 1 byte long length field should fail
|
|
|
+ // for DHCPv6 option.
|
|
|
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_1_BYTE);
|
|
|
+ EXPECT_THROW(vendor_class.addTuple(tuple2), isc::BadValue);
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that it is possible to replace existing tuple.
|
|
|
+TEST(OptionVendorClass, setTuple) {
|
|
|
+ OptionVendorClass vendor_class(Option::V4, 1234);
|
|
|
+ // The DHCPv4 option should carry one empty tuple.
|
|
|
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
|
|
|
+ ASSERT_TRUE(vendor_class.getTuple(0).getText().empty());
|
|
|
+ // Replace the empty tuple with non-empty one.
|
|
|
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
|
|
|
+ tuple = "xyz";
|
|
|
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
|
|
|
+ // There should be one tuple with updated data.
|
|
|
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
|
|
|
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
|
|
|
+
|
|
|
+ // Add another one.
|
|
|
+ tuple = "abc";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
|
|
|
+ ASSERT_EQ("abc", vendor_class.getTuple(1).getText());
|
|
|
+
|
|
|
+ // Try to replace them with new tuples.
|
|
|
+ tuple = "new_xyz";
|
|
|
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
|
|
|
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
|
|
|
+ EXPECT_EQ("new_xyz", vendor_class.getTuple(0).getText());
|
|
|
+
|
|
|
+ tuple = "new_abc";
|
|
|
+ ASSERT_NO_THROW(vendor_class.setTuple(1, tuple));
|
|
|
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
|
|
|
+ EXPECT_EQ("new_abc", vendor_class.getTuple(1).getText());
|
|
|
+
|
|
|
+ // For out of range position, exception should be thrown.
|
|
|
+ tuple = "foo";
|
|
|
+ EXPECT_THROW(vendor_class.setTuple(2, tuple), isc::OutOfRange);
|
|
|
+
|
|
|
+ // Attempt to add the tuple with 2 byte long length field should fail
|
|
|
+ // for DHCPv4 option.
|
|
|
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_2_BYTES);
|
|
|
+ EXPECT_THROW(vendor_class.addTuple(tuple2), isc::BadValue);
|
|
|
+}
|
|
|
+
|
|
|
+// Check that the returned length of the DHCPv4 option is correct.
|
|
|
+TEST(OptionVendorClass, len4) {
|
|
|
+ OptionVendorClass vendor_class(Option::V4, 1234);
|
|
|
+ ASSERT_EQ(7, vendor_class.len());
|
|
|
+ // Replace the default empty tuple.
|
|
|
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
|
|
|
+ tuple = "xyz";
|
|
|
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
|
|
|
+ // The total length should get increased by the size of 'xyz'.
|
|
|
+ EXPECT_EQ(10, vendor_class.len());
|
|
|
+ // Add another tuple.
|
|
|
+ tuple = "abc";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+ // The total size now grows by the additional enterprise id and the
|
|
|
+ // 1 byte of the tuple length field and 3 bytes of 'abc'.
|
|
|
+ EXPECT_EQ(18, vendor_class.len());
|
|
|
+}
|
|
|
+
|
|
|
+// Check that the returned length of the DHCPv6 option is correct.
|
|
|
+TEST(OptionVendorClass, len6) {
|
|
|
+ OptionVendorClass vendor_class(Option::V6, 1234);
|
|
|
+ ASSERT_EQ(8, vendor_class.len());
|
|
|
+ // Add first tuple.
|
|
|
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
|
|
|
+ tuple = "xyz";
|
|
|
+ ASSERT_NO_THROW(vendor_class.addTuple(tuple));
|
|
|
+ // The total length grows by 2 bytes of the length field and 3 bytes
|
|
|
+ // consumed by 'xyz'.
|
|
|
+ EXPECT_EQ(13, vendor_class.len());
|
|
|
+ // Add another tuple and check that the total size gets increased.
|
|
|
+ tuple = "abc";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+ EXPECT_EQ(18, vendor_class.len());
|
|
|
+}
|
|
|
+
|
|
|
+// Check that the option is rendered to the buffer in wire format.
|
|
|
+TEST(OptionVendorClass, pack4) {
|
|
|
+ OptionVendorClass vendor_class(Option::V4, 1234);
|
|
|
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
|
|
|
+ // By default, there is an empty tuple in the option. Let's replace
|
|
|
+ // it with the tuple with some data.
|
|
|
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
|
|
|
+ tuple = "Hello world";
|
|
|
+ vendor_class.setTuple(0, tuple);
|
|
|
+ // And add another tuple so as resulting option is a bit more complex.
|
|
|
+ tuple = "foo";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+
|
|
|
+ // Render the data to the buffer.
|
|
|
+ OutputBuffer buf(10);
|
|
|
+ ASSERT_NO_THROW(vendor_class.pack(buf));
|
|
|
+ ASSERT_EQ(26, buf.getLength());
|
|
|
+
|
|
|
+ // Prepare reference data.
|
|
|
+ const uint8_t ref[] = {
|
|
|
+ 0x7C, 0x18, // option 124, length 24
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x0B, // tuple length is 11
|
|
|
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
|
|
|
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 3, // tuple length is 3
|
|
|
+ 0x66, 0x6F, 0x6F // foo
|
|
|
+ };
|
|
|
+ // Compare the buffer with reference data.
|
|
|
+ EXPECT_EQ(0, memcmp(static_cast<const void*>(ref),
|
|
|
+ static_cast<const void*>(buf.getData()), 26));
|
|
|
+}
|
|
|
+
|
|
|
+// Check that the DHCPv6 option is rendered to the buffer in wire format.
|
|
|
+TEST(OptionVendorClass, pack6) {
|
|
|
+ OptionVendorClass vendor_class(Option::V6, 1234);
|
|
|
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
|
|
|
+ // Add tuple.
|
|
|
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
|
|
|
+ tuple = "Hello world";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+ // And add another tuple so as resulting option is a bit more complex.
|
|
|
+ tuple = "foo";
|
|
|
+ vendor_class.addTuple(tuple);
|
|
|
+
|
|
|
+ // Render the data to the buffer.
|
|
|
+ OutputBuffer buf(10);
|
|
|
+ ASSERT_NO_THROW(vendor_class.pack(buf));
|
|
|
+ ASSERT_EQ(26, buf.getLength());
|
|
|
+
|
|
|
+ // Prepare reference data.
|
|
|
+ const uint8_t ref[] = {
|
|
|
+ 0x00, 0x10, 0x00, 0x16, // option 16, length 22
|
|
|
+ 0x00, 0x00, 0x04, 0xD2, // enterprise id 1234
|
|
|
+ 0x00, 0x0B, // tuple length is 11
|
|
|
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
|
|
|
+ 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<const void*>(ref),
|
|
|
+ static_cast<const void*>(buf.getData()),
|
|
|
+ buf.getLength()));
|
|
|
+}
|
|
|
+
|
|
|
+// This function checks that the DHCPv4 option with two opaque data tuples
|
|
|
+// is parsed correctly.
|
|
|
+TEST(OptionVendorClass, unpack4) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x0B, // tuple length is 11
|
|
|
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
|
|
|
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 3, // tuple length is 3
|
|
|
+ 0x66, 0x6F, 0x6F // foo
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ OptionVendorClassPtr vendor_class;
|
|
|
+ ASSERT_NO_THROW(
|
|
|
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
|
|
|
+ buf.begin(),
|
|
|
+ buf.end()));
|
|
|
+ );
|
|
|
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
|
|
|
+ EXPECT_EQ(1234, vendor_class->getVendorId());
|
|
|
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
|
|
|
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
|
|
|
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
|
|
|
+}
|
|
|
+
|
|
|
+// This function checks that the DHCPv4 option with two opaque data tuples
|
|
|
+// is parsed correctly.
|
|
|
+TEST(OptionVendorClass, unpack6) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x00, 0x0B, // tuple length is 11
|
|
|
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
|
|
|
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
|
|
|
+ 0x00, 0x03, // tuple length is 3
|
|
|
+ 0x66, 0x6F, 0x6F // foo
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ OptionVendorClassPtr vendor_class;
|
|
|
+ ASSERT_NO_THROW(
|
|
|
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
|
|
|
+ buf.begin(),
|
|
|
+ buf.end()));
|
|
|
+ );
|
|
|
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
|
|
|
+ EXPECT_EQ(1234, vendor_class->getVendorId());
|
|
|
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
|
|
|
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
|
|
|
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// This test checks that the DHCPv4 option with opaque data of size 0
|
|
|
+// is correctly parsed.
|
|
|
+TEST(OptionVendorClass, unpack4EmptyTuple) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x00, // tuple length is 0
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ OptionVendorClassPtr vendor_class;
|
|
|
+ ASSERT_NO_THROW(
|
|
|
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
|
|
|
+ buf.begin(),
|
|
|
+ buf.end()));
|
|
|
+ );
|
|
|
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
|
|
|
+ EXPECT_EQ(1234, vendor_class->getVendorId());
|
|
|
+ ASSERT_EQ(1, vendor_class->getTuplesNum());
|
|
|
+ EXPECT_TRUE(vendor_class->getTuple(0).getText().empty());
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that the DHCPv6 option with opaque data of size 0
|
|
|
+// is correctly parsed.
|
|
|
+TEST(OptionVendorClass, unpack6EmptyTuple) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x00, 0x00 // tuple length is 0
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ OptionVendorClassPtr vendor_class;
|
|
|
+ ASSERT_NO_THROW(
|
|
|
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
|
|
|
+ buf.begin(),
|
|
|
+ buf.end()));
|
|
|
+ );
|
|
|
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
|
|
|
+ EXPECT_EQ(1234, vendor_class->getVendorId());
|
|
|
+ ASSERT_EQ(1, vendor_class->getTuplesNum());
|
|
|
+ EXPECT_TRUE(vendor_class->getTuple(0).getText().empty());
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that exception is thrown when parsing truncated DHCPv4
|
|
|
+// V-I Vendor Class option.
|
|
|
+TEST(OptionVendorClass, unpack4Truncated) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x0B, // tuple length is 11
|
|
|
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
|
|
|
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ EXPECT_THROW(OptionVendorClass (Option::V4, buf.begin(), buf.end()),
|
|
|
+ isc::OutOfRange);
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that exception is thrown when parsing truncated DHCPv6
|
|
|
+// Vendor Class option.
|
|
|
+TEST(OptionVendorClass, unpack6Truncated) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
|
|
|
+ 0x00, 0x0B, // tuple length is 11
|
|
|
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
|
|
|
+ 0x77, 0x6F, 0x72, 0x6C // worl (truncated d!)
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ EXPECT_THROW(OptionVendorClass (Option::V6, buf.begin(), buf.end()),
|
|
|
+ isc::dhcp::OpaqueDataTupleError);
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that exception is thrown when parsing DHCPv4 V-I Vendor
|
|
|
+// Class option which doesn't have opaque data length.
|
|
|
+TEST(OptionVendorClass, unpack4NoTuple) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2 // enterprise id 1234
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ ASSERT_THROW(OptionVendorClass (Option::V4, buf.begin(), buf.end()),
|
|
|
+ isc::OutOfRange);
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks that the DHCPv6 Vendor Class option containing no opaque
|
|
|
+// data is parsed correctly.
|
|
|
+TEST(OptionVendorClass, unpack6NoTuple) {
|
|
|
+ // Prepare data to decode.
|
|
|
+ const uint8_t buf_data[] = {
|
|
|
+ 0, 0, 0x4, 0xD2 // enterprise id 1234
|
|
|
+ };
|
|
|
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
|
|
|
+
|
|
|
+ OptionVendorClassPtr vendor_class;
|
|
|
+ ASSERT_NO_THROW(
|
|
|
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
|
|
|
+ buf.begin(),
|
|
|
+ buf.end()));
|
|
|
+ );
|
|
|
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
|
|
|
+ EXPECT_EQ(1234, vendor_class->getVendorId());
|
|
|
+ EXPECT_EQ(0, vendor_class->getTuplesNum());
|
|
|
+}
|
|
|
+
|
|
|
+} // end of anonymous namespace
|
|
|
+
|