Commit 6f6251bb authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

Merge branch 'trac3201'

parents 2e283ec7 ff2b7624
......@@ -4429,6 +4429,10 @@ Dhcp4/subnet4 [] list (default)
Domain Name (15), DNS Servers (6), IP Address Lease Time
(51), Subnet mask (1), and Routers (3).</simpara>
</listitem>
<listitem>
<simpara><ulink url="http://tools.ietf.org/html/rfc3046">RFC 3046</ulink>:
Relay Agent Information option is supported.</simpara>
</listitem>
</itemizedlist>
</section>
......
......@@ -164,16 +164,27 @@ static const uint16_t DHCP4_SERVER_PORT = 67;
/// extensions field).
static const uint32_t DHCP_OPTIONS_COOKIE = 0x63825363;
/* Relay Agent Information option subtypes: */
static const uint16_t RAI_OPTION_AGENT_CIRCUIT_ID = 1; // RFC3046
static const uint16_t RAI_OPTION_REMOTE_ID = 2; // RFC3046
/* option 3 is reserved and will never be assigned */
static const uint16_t RAI_OPTION_DOCSIS_DEVICE_CLASS = 4; // RFC3256
static const uint16_t RAI_OPTION_LINK_SELECTION = 5; // RFC3527
static const uint16_t RAI_OPTION_SUBSCRIBER_ID = 6; //RFC3993
static const uint16_t RAI_OPTION_RADIUS = 7; //RFC4014
static const uint16_t RAI_OPTION_AUTH = 8; //RFC4030
static const uint16_t RAI_OPTION_VSI = 9; // RFC4243
static const uint16_t RAI_OPTION_RELAY_FLAGS = 10; // RFC5010
static const uint16_t RAI_OPTION_SERVER_ID_OVERRIDE = 11; // RFC5107
static const uint16_t RAI_OPTION_RELAY_ID = 12; //RFC6925
static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT = 151; //RFC6607
static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT_CTRL = 152; //RFC6607
// TODO: Following are leftovers from dhcp.h import from ISC DHCP
// They will be converted to C++-style defines once they will start
// to be used.
#if 0
/* Relay Agent Information option subtypes: */
#define RAI_CIRCUIT_ID 1
#define RAI_REMOTE_ID 2
#define RAI_AGENT_ID 3
#define RAI_LINK_SELECT 5
/* FQDN suboptions: */
#define FQDN_NO_CLIENT_UPDATE 1
#define FQDN_SERVER_UPDATE 2
......
......@@ -128,14 +128,22 @@ LibDHCP::optionFactory(Option::Universe u,
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options,
size_t* relay_msg_offset /* = 0 */,
size_t* relay_msg_len /* = 0 */) {
size_t offset = 0;
size_t length = buf.size();
// Get the list of stdandard option definitions.
const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V6);
// Get the list of standard option definitions.
OptionDefContainer option_defs;
if (option_space == "dhcp6") {
option_defs = LibDHCP::getOptionDefs(Option::V6);
}
// @todo Once we implement other option spaces we should add else clause
// here and gather option definitions for them. For now leaving option_defs
// empty will imply creation of generic Option.
// Get the search index #1. It allows to search for option definitions
// using option code.
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
......@@ -206,11 +214,19 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
}
size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options) {
size_t offset = 0;
// Get the list of stdandard option definitions.
const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V4);
OptionDefContainer option_defs;
if (option_space == "dhcp4") {
option_defs = LibDHCP::getOptionDefs(Option::V4);
}
// @todo Once we implement other option spaces we should add else clause
// here and gather option definitions for them. For now leaving option_defs
// empty will imply creation of generic Option.
// Get the search index #1. It allows to search for option definitions
// using option code.
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
......
......@@ -108,9 +108,12 @@ public:
/// in options container.
///
/// @param buf Buffer to be parsed.
/// @param option_space A name of the option space which holds definitions
/// of to be used to parse options in the packets.
/// @param options Reference to option container. Options will be
/// put here.
static size_t unpackOptions4(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options);
/// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
......@@ -125,6 +128,8 @@ public:
/// iteration its content will be treated as buffer to be parsed.
///
/// @param buf Buffer to be parsed.
/// @param option_space A name of the option space which holds definitions
/// of to be used to parse options in the packets.
/// @param options Reference to option container. Options will be
/// put here.
/// @param relay_msg_offset reference to a size_t structure. If specified,
......@@ -133,6 +138,7 @@ public:
/// length of the relay_msg option will be stored in it.
/// @return offset to the first byte after last parsed option
static size_t unpackOptions6(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options,
size_t* relay_msg_offset = 0,
size_t* relay_msg_len = 0);
......
......@@ -135,10 +135,10 @@ Option::unpackOptions(const OptionBuffer& buf) {
switch (universe_) {
case V4:
LibDHCP::unpackOptions4(buf, options_);
LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), options_);
return;
case V6:
LibDHCP::unpackOptions6(buf, options_);
LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_);
return;
default:
isc_throw(isc::BadValue, "Invalid universe type " << universe_);
......
......@@ -249,6 +249,12 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
// Proceed to the next data field.
data += data_size;
}
// Unpack suboptions if any.
if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
unpackOptions(OptionBuffer(data, data_buf.end()));
}
} else if (data_type != OPT_EMPTY_TYPE) {
// If data_type value is other than OPT_RECORD_TYPE, our option is
// empty (have no data at all) or it comprises one or more
......@@ -316,9 +322,20 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
}
if (data_size > 0) {
buffers.push_back(OptionBuffer(data, data + data_size));
data += data_size;
} else {
isc_throw(OutOfRange, "option buffer truncated");
}
// Unpack suboptions if any.
if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
unpackOptions(OptionBuffer(data, data_buf.end()));
}
}
} else if (data_type == OPT_EMPTY_TYPE) {
// Unpack suboptions if any.
if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
unpackOptions(OptionBuffer(data, data_buf.end()));
}
}
// If everything went ok we can replace old buffer set with new ones.
......
......@@ -118,7 +118,11 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
try {
switch(type_) {
case OPT_EMPTY_TYPE:
return (factoryEmpty(u, type));
if (getEncapsulatedSpace().empty()) {
return (factoryEmpty(u, type));
} else {
return (OptionPtr(new OptionCustom(*this, u, begin, end)));
}
case OPT_BINARY_TYPE:
return (factoryGeneric(u, type, begin, end));
......
......@@ -219,7 +219,7 @@ Pkt4::unpack() {
// a vector as an input.
buffer_in.readVector(opts_buffer, opts_len);
if (callback_.empty()) {
LibDHCP::unpackOptions4(opts_buffer, options_);
LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
} else {
// The last two arguments are set to NULL because they are
// specific to DHCPv6 options parsing. They are unused for
......
......@@ -330,7 +330,7 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
// If custom option parsing function has been set, use this function
// to parse options. Otherwise, use standard function from libdhcp.
if (callback_.empty()) {
LibDHCP::unpackOptions6(opt_buffer, options_);
LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
} else {
// The last two arguments hold the DHCPv6 Relay message offset and
// length. Setting them to NULL because we are dealing with the
......@@ -377,7 +377,7 @@ Pkt6::unpackRelayMsg() {
// If custom option parsing function has been set, use this function
// to parse options. Otherwise, use standard function from libdhcp.
if (callback_.empty()) {
LibDHCP::unpackOptions6(opt_buffer, relay.options_,
LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
&relay_msg_offset, &relay_msg_len);
} else {
callback_(opt_buffer, "dhcp6", relay.options_,
......
......@@ -42,6 +42,12 @@ using namespace isc::dhcp;
using namespace isc::util;
namespace {
// DHCPv6 suboptions of Vendor Options Option.
/// @todo move to src/lib/dhcp/docsis3_option_defs.h once #3194 is merged.
const uint16_t OPTION_CMTS_CAPS = 1025;
const uint16_t OPTION_CM_MAC = 1026;
class LibDhcpTest : public ::testing::Test {
public:
LibDhcpTest() { }
......@@ -105,6 +111,33 @@ public:
testStdOptionDefs(Option::V6, code, begin, end, expected_type,
encapsulates);
}
/// @brief Create a sample DHCPv4 option 43 with suboptions.
static OptionBuffer createVendorOption() {
const uint8_t opt_data[] = {
0x2B, 0x0D, // Vendor-Specific Information (CableLabs)
// Suboptions start here...
0x02, 0x05, // Device Type Option (length = 5)
'D', 'u', 'm', 'm', 'y',
0x04, 0x04, // Serial Number Option (length = 4)
0x42, 0x52, 0x32, 0x32 // Serial number
};
return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
}
/// @brief Create a sample DHCPv4 option 82 with suboptions.
static OptionBuffer createAgentInformationOption() {
const uint8_t opt_data[] = {
0x52, 0x0E, // Agent Information Option (length = 14)
// Suboptions start here...
0x01, 0x04, // Agent Circuit ID (length = 4)
0x20, 0x00, 0x00, 0x02, // ID
0x02, 0x06, // Agent Remote ID
0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14 // ID
};
return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
}
private:
/// @brief Test DHCPv4 or DHCPv6 option definition.
......@@ -177,7 +210,19 @@ const uint8_t v6packed[] = {
0, 2, 0, 3, 105, 106, 107, // SERVER_ID (7 bytes)
0, 14, 0, 0, // RAPID_COMMIT (0 bytes)
0, 6, 0, 4, 108, 109, 110, 111, // ORO (8 bytes)
0, 8, 0, 2, 112, 113 // ELAPSED_TIME (6 bytes)
0, 8, 0, 2, 112, 113, // ELAPSED_TIME (6 bytes)
// Vendor Specific Information Option starts here
0x00, 0x11, // VSI Option Code
0x00, 0x16, // VSI Option Length
0x00, 0x00, 0x11, 0x8B, // Enterprise ID
0x04, 0x01, // CMTS Capabilities Option
0x00, 0x04, // Length
0x01, 0x02,
0x03, 0x00, // DOCSIS Version Number
0x04, 0x02, // CM MAC Address Suboption
0x00, 0x06, // Length
0x74, 0x56, 0x12, 0x29, 0x97, 0xD0, // Actual MAC Address
};
TEST_F(LibDhcpTest, optionFactory) {
......@@ -267,11 +312,23 @@ TEST_F(LibDhcpTest, packOptions6) {
OptionPtr opt4(new Option(Option::V6, 6, buf.begin() + 8, buf.begin() + 12));
OptionPtr opt5(new Option(Option::V6, 8, buf.begin() + 12, buf.begin() + 14));
OptionPtr cm_mac(new Option(Option::V6, OPTION_CM_MAC,
OptionBuffer(v6packed + 54, v6packed + 60)));
OptionPtr cmts_caps(new Option(Option::V6, OPTION_CMTS_CAPS,
OptionBuffer(v6packed + 46, v6packed + 50)));
boost::shared_ptr<OptionInt<uint32_t> >
vsi(new OptionInt<uint32_t>(Option::V6, D6O_VENDOR_OPTS, 4491));
vsi->addOption(cm_mac);
vsi->addOption(cmts_caps);
opts.insert(make_pair(opt1->getType(), opt1));
opts.insert(make_pair(opt1->getType(), opt2));
opts.insert(make_pair(opt1->getType(), opt3));
opts.insert(make_pair(opt1->getType(), opt4));
opts.insert(make_pair(opt1->getType(), opt5));
opts.insert(make_pair(opt1->getType(), vsi));
OutputBuffer assembled(512);
......@@ -293,10 +350,10 @@ TEST_F(LibDhcpTest, unpackOptions6) {
EXPECT_NO_THROW ({
LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)),
options);
"dhcp6", options);
});
EXPECT_EQ(options.size(), 5); // there should be 5 options
EXPECT_EQ(options.size(), 6); // there should be 5 options
isc::dhcp::OptionCollection::const_iterator x = options.find(1);
ASSERT_FALSE(x == options.end()); // option 1 should exist
......@@ -357,6 +414,27 @@ TEST_F(LibDhcpTest, unpackOptions6) {
// Returned value should be equivalent to two byte values: 112, 113
EXPECT_EQ(0x7071, opt_elapsed_time->getValue());
// Check if Vendor Specific Information Option along with suboptions
// have been parsed correctly.
x = options.find(D6O_VENDOR_OPTS);
EXPECT_FALSE(x == options.end());
EXPECT_EQ(D6O_VENDOR_OPTS, x->second->getType());
EXPECT_EQ(26, x->second->len());
// CM MAC Address Option
OptionPtr cm_mac = x->second->getOption(OPTION_CM_MAC);
ASSERT_TRUE(cm_mac);
EXPECT_EQ(OPTION_CM_MAC, cm_mac->getType());
ASSERT_EQ(10, cm_mac->len());
EXPECT_EQ(0, memcmp(&cm_mac->getData()[0], v6packed + 54, 6));
// CMTS Capabilities
OptionPtr cmts_caps = x->second->getOption(OPTION_CMTS_CAPS);
ASSERT_TRUE(cmts_caps);
EXPECT_EQ(OPTION_CMTS_CAPS, cmts_caps->getType());
ASSERT_EQ(8, cmts_caps->len());
EXPECT_EQ(0, memcmp(&cmts_caps->getData()[0], v6packed + 46, 4));
x = options.find(0);
EXPECT_TRUE(x == options.end()); // option 0 not found
......@@ -380,7 +458,12 @@ static uint8_t v4_opts[] = {
60, 3, 10, 11, 12, // Class Id
14, 3, 20, 21, 22, // Merit Dump File
254, 3, 30, 31, 32, // Reserved
128, 3, 40, 41, 42 // Vendor specific
128, 3, 40, 41, 42, // Vendor specific
0x52, 0x19, // RAI
0x01, 0x04, 0x20, 0x00, 0x00, 0x02, // Agent Circuit ID
0x02, 0x06, 0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14, // Agent Remote ID
0x09, 0x09, 0x00, 0x00, 0x11, 0x8B, 0x04, // Vendor Specific Information
0x01, 0x02, 0x03, 0x00 // Vendor Specific Information continued
};
TEST_F(LibDhcpTest, packOptions4) {
......@@ -399,20 +482,53 @@ TEST_F(LibDhcpTest, packOptions4) {
OptionPtr opt4(new Option(Option::V4,254, payload[3]));
OptionPtr opt5(new Option(Option::V4,128, payload[4]));
// Add RAI option, which comprises 3 sub-options.
// Get the option definition for RAI option. This option is represented
// by OptionCustom which requires a definition to be passed to
// the constructor.
OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(Option::V4,
DHO_DHCP_AGENT_OPTIONS);
ASSERT_TRUE(rai_def);
// Create RAI option.
OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4));
// The sub-options are created using the bits of v4_opts buffer because
// we want to use this buffer as a reference to verify that produced
// option in on-wire format is correct.
// Create Ciruit ID sub-option and add to RAI.
OptionPtr circuit_id(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
OptionBuffer(v4_opts + 29,
v4_opts + 33)));
rai->addOption(circuit_id);
// Create Remote ID option and add to RAI.
OptionPtr remote_id(new Option(Option::V4, RAI_OPTION_REMOTE_ID,
OptionBuffer(v4_opts + 35, v4_opts + 41)));
rai->addOption(remote_id);
// Create Vendor Specific Information and add to RAI.
OptionPtr vsi(new Option(Option::V4, RAI_OPTION_VSI,
OptionBuffer(v4_opts + 43, v4_opts + 52)));
rai->addOption(vsi);
isc::dhcp::OptionCollection opts; // list of options
// Note that we insert each option under the same option code into
// the map. This gurantees that options are packed in the same order
// they were added. Otherwise, options would get sorted by code and
// the resulting buffer wouldn't match with the reference buffer.
opts.insert(make_pair(opt1->getType(), opt1));
opts.insert(make_pair(opt1->getType(), opt2));
opts.insert(make_pair(opt1->getType(), opt3));
opts.insert(make_pair(opt1->getType(), opt4));
opts.insert(make_pair(opt1->getType(), opt5));
vector<uint8_t> expVect(v4_opts, v4_opts + sizeof(v4_opts));
opts.insert(make_pair(opt1->getType(), rai));
OutputBuffer buf(100);
EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
ASSERT_EQ(buf.getLength(), sizeof(v4_opts));
EXPECT_EQ(0, memcmp(v4_opts, buf.getData(), sizeof(v4_opts)));
}
TEST_F(LibDhcpTest, unpackOptions4) {
......@@ -421,7 +537,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
isc::dhcp::OptionCollection options; // list of options
ASSERT_NO_THROW(
LibDHCP::unpackOptions4(v4packed, options);
LibDHCP::unpackOptions4(v4packed, "dhcp4", options);
);
isc::dhcp::OptionCollection::const_iterator x = options.find(12);
......@@ -464,6 +580,48 @@ TEST_F(LibDhcpTest, unpackOptions4) {
EXPECT_EQ(5, x->second->len()); // total option length 5
EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 22, 3)); // data len=3
// Checking DHCP Relay Agent Information Option.
x = options.find(DHO_DHCP_AGENT_OPTIONS);
ASSERT_FALSE(x == options.end());
EXPECT_EQ(DHO_DHCP_AGENT_OPTIONS, x->second->getType());
// RAI is represented by OptionCustom.
OptionCustomPtr rai = boost::dynamic_pointer_cast<OptionCustom>(x->second);
ASSERT_TRUE(rai);
// RAI should have 3 sub-options: Circuit ID, Agent Remote ID, Vendor
// Specific Information option. Note that by parsing these suboptions we
// are checking that unpackOptions4 differentiates between standard option
// space called "dhcp4" and other option spaces. These sub-options do not
// belong to standard option space and should be parsed using different
// option definitions.
// @todo Currently, definitions for option space "dhcp-agent-options-space"
// are not defined. Therefore all suboptions will be represented here by
// the generic Option class.
// Check that Circuit ID option is among parsed options.
OptionPtr rai_option = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
ASSERT_TRUE(rai_option);
EXPECT_EQ(RAI_OPTION_AGENT_CIRCUIT_ID, rai_option->getType());
ASSERT_EQ(6, rai_option->len());
EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 29, 4));
// Check that Remote ID option is among parsed options.
rai_option = rai->getOption(RAI_OPTION_REMOTE_ID);
ASSERT_TRUE(rai_option);
EXPECT_EQ(RAI_OPTION_REMOTE_ID, rai_option->getType());
ASSERT_EQ(8, rai_option->len());
EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 35, 6));
// Check that Vendor Specific Information option is among parsed options.
rai_option = rai->getOption(RAI_OPTION_VSI);
ASSERT_TRUE(rai_option);
EXPECT_EQ(RAI_OPTION_VSI, rai_option->getType());
ASSERT_EQ(11, rai_option->len());
EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 43, 11));
// Make sure, that option other than those above is not present.
EXPECT_FALSE(rai->getOption(10));
// Check the same for the global option space.
x = options.find(0);
EXPECT_TRUE(x == options.end()); // option 0 not found
......@@ -472,6 +630,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
x = options.find(2);
EXPECT_TRUE(x == options.end()); // option 2 not found
}
TEST_F(LibDhcpTest, isStandardOption4) {
......@@ -653,8 +812,8 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
LibDhcpTest::testStdOptionDefs4(DHO_DEFAULT_TCP_TTL, begin, begin + 1,
typeid(OptionInt<uint8_t>));
LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin, begin + 4,
typeid(OptionInt<uint32_t>));
LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin,
begin + 4, typeid(OptionInt<uint32_t>));
LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_GARBAGE, begin, begin + 1,
typeid(OptionCustom));
......@@ -668,8 +827,13 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
LibDhcpTest::testStdOptionDefs4(DHO_NTP_SERVERS, begin, end,
typeid(Option4AddrLst));
LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS, begin, end,
typeid(Option),
// The following option requires well formed buffer to be created from.
// Not just a dummy one. This buffer includes some suboptions.
OptionBuffer vendor_opts_buf = createVendorOption();
LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS,
vendor_opts_buf.begin(),
vendor_opts_buf.end(),
typeid(OptionCustom),
"vendor-encapsulated-options-space");
LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NAME_SERVERS, begin, end,
......@@ -744,8 +908,14 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
LibDhcpTest::testStdOptionDefs4(DHO_FQDN, begin, begin + 3,
typeid(Option4ClientFqdn));
LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS, begin, end,
typeid(Option), "dhcp-agent-options-space");
// The following option requires well formed buffer to be created from.
// Not just a dummy one. This buffer includes some suboptions.
OptionBuffer agent_info_buf = createAgentInformationOption();
LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS,
agent_info_buf.begin(),
agent_info_buf.end(),
typeid(OptionCustom),
"dhcp-agent-options-space");
LibDhcpTest::testStdOptionDefs4(DHO_AUTHENTICATE, begin, end,
typeid(Option));
......
......@@ -32,6 +32,59 @@ public:
/// @brief Constructor.
OptionCustomTest() { }
/// @brief Appends DHCPv4 suboption in the on-wire format to the buffer.
///
/// @param buf A buffer to which suboption is appended.
void appendV4Suboption(OptionBuffer& buf) {
const uint8_t subopt_data[] = {
0x01, 0x02, // Option type = 1, length = 2
0x01, 0x02 // Two bytes of data
};
buf.insert(buf.end(), subopt_data, subopt_data + sizeof(subopt_data));
}
/// @brief Check if the parsed option has a suboption.
///
/// @param opt An option in which suboption is expected.
/// @return Assertion result indicating that the suboption is
/// present (success) or missing (failure).
::testing::AssertionResult hasV4Suboption(OptionCustom* opt) {
OptionPtr subopt = opt->getOption(1);
if (!subopt) {
return (::testing::AssertionFailure(::testing::Message()
<< "Suboption of OptionCustom"
" is missing"));
}
return (::testing::AssertionSuccess());
}
/// @brief Appends DHCPv6 suboption in the on-wire format to the buffer.
///
/// @param buf A buffer to which suboption is appended.
void appendV6Suboption(OptionBuffer& buf) {
const uint8_t subopt_data[] = {
0x00, 0x01, // Option type = 1
0x00, 0x04, // Option length = 4
0x01, 0x02, 0x03, 0x04 // Four bytes of data
};
buf.insert(buf.end(), subopt_data, subopt_data + sizeof(subopt_data));
}
/// @brief Check if the parsed option has a suboption.
///
/// @param opt An option in which suboption is expected.
/// @return Assertion result indicating that the suboption is
/// present (success) or missing (failure).
::testing::AssertionResult hasV6Suboption(OptionCustom* opt) {
OptionPtr subopt = opt->getOption(1);
if (!subopt) {
return (::testing::AssertionFailure(::testing::Message()
<< "Suboption of OptionCustom"
" is missing"));
}
return (::testing::AssertionSuccess());
}
/// @brief Write IP address into a buffer.
///
/// @param address address to be written.
......@@ -114,23 +167,30 @@ TEST_F(OptionCustomTest, constructor) {
// The purpose of this test is to verify that 'empty' option definition can
// be used to create an instance of custom option.
TEST_F(OptionCustomTest, emptyData) {
OptionDefinition opt_def("OPTION_FOO", 232, "empty");
OptionDefinition opt_def("option-foo", 232, "empty", "option-foo-space");
// Create a buffer holding 1 suboption.
OptionBuffer buf;
appendV4Suboption(buf);
boost::scoped_ptr<OptionCustom> option;
ASSERT_NO_THROW(
option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
buf.end()));
);
ASSERT_TRUE(option);
// Option is 'empty' so no data fields are expected.