mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-02 15:05:16 +00:00
[3180] Implemented support for callback functions to parse options.
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
#include <hooks/hooks_manager.h>
|
#include <hooks/hooks_manager.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/erase.hpp>
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -197,6 +198,15 @@ Dhcpv4Srv::run() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In order to parse the DHCP options, the server needs to use some
|
||||||
|
// configuration information such as: existing option spaces, option
|
||||||
|
// definitions etc. This is the kind of information which is not
|
||||||
|
// available in the libdhcp, so we need to supply our own implementation
|
||||||
|
// of the option parsing function here, which would rely on the
|
||||||
|
// configuration data.
|
||||||
|
query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
|
||||||
|
_1, _2, _3));
|
||||||
|
|
||||||
bool skip_unpack = false;
|
bool skip_unpack = false;
|
||||||
|
|
||||||
// The packet has just been received so contains the uninterpreted wire
|
// The packet has just been received so contains the uninterpreted wire
|
||||||
@@ -1164,6 +1174,95 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
|
||||||
|
const std::string& option_space,
|
||||||
|
isc::dhcp::OptionCollection& options) {
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
OptionDefContainer option_defs;
|
||||||
|
if (option_space == "dhcp6") {
|
||||||
|
// Get the list of stdandard option definitions.
|
||||||
|
option_defs = LibDHCP::getOptionDefs(Option::V6);
|
||||||
|
} else if (!option_space.empty()) {
|
||||||
|
OptionDefContainerPtr option_defs_ptr =
|
||||||
|
CfgMgr::instance().getOptionDefs(option_space);
|
||||||
|
if (option_defs_ptr != NULL) {
|
||||||
|
option_defs = *option_defs_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get the search index #1. It allows to search for option definitions
|
||||||
|
// using option code.
|
||||||
|
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
|
||||||
|
|
||||||
|
// The buffer being read comprises a set of options, each starting with
|
||||||
|
// a one-byte type code and a one-byte length field.
|
||||||
|
while (offset + 1 <= buf.size()) {
|
||||||
|
uint8_t opt_type = buf[offset++];
|
||||||
|
|
||||||
|
// DHO_END is a special, one octet long option
|
||||||
|
if (opt_type == DHO_END)
|
||||||
|
return (offset); // just return. Don't need to add DHO_END option
|
||||||
|
|
||||||
|
// DHO_PAD is just a padding after DHO_END. Let's continue parsing
|
||||||
|
// in case we receive a message without DHO_END.
|
||||||
|
if (opt_type == DHO_PAD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (offset + 1 >= buf.size()) {
|
||||||
|
// opt_type must be cast to integer so as it is not treated as
|
||||||
|
// unsigned char value (a number is presented in error message).
|
||||||
|
isc_throw(OutOfRange, "Attempt to parse truncated option "
|
||||||
|
<< static_cast<int>(opt_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t opt_len = buf[offset++];
|
||||||
|
if (offset + opt_len > buf.size()) {
|
||||||
|
isc_throw(OutOfRange, "Option parse failed. Tried to parse "
|
||||||
|
<< offset + opt_len << " bytes from " << buf.size()
|
||||||
|
<< "-byte long buffer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all definitions with the particular option code. Note that option code
|
||||||
|
// is non-unique within this container however at this point we expect
|
||||||
|
// to get one option definition with the particular code. If more are
|
||||||
|
// returned we report an error.
|
||||||
|
const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
|
||||||
|
// Get the number of returned option definitions for the option code.
|
||||||
|
size_t num_defs = distance(range.first, range.second);
|
||||||
|
|
||||||
|
OptionPtr opt;
|
||||||
|
if (num_defs > 1) {
|
||||||
|
// Multiple options of the same code are not supported right now!
|
||||||
|
isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
|
||||||
|
" for option type " << static_cast<int>(opt_type)
|
||||||
|
<< " returned. Currently it is not supported to initialize"
|
||||||
|
<< " multiple option definitions for the same option code."
|
||||||
|
<< " This will be supported once support for option spaces"
|
||||||
|
<< " is implemented");
|
||||||
|
} else if (num_defs == 0) {
|
||||||
|
opt = OptionPtr(new Option(Option::V4, opt_type,
|
||||||
|
buf.begin() + offset,
|
||||||
|
buf.begin() + offset + opt_len));
|
||||||
|
opt->setEncapsulatedSpace("dhcp4");
|
||||||
|
} else {
|
||||||
|
// The option definition has been found. Use it to create
|
||||||
|
// the option instance from the provided buffer chunk.
|
||||||
|
const OptionDefinitionPtr& def = *(range.first);
|
||||||
|
assert(def);
|
||||||
|
opt = def->optionFactory(Option::V4, opt_type,
|
||||||
|
buf.begin() + offset,
|
||||||
|
buf.begin() + offset + opt_len,
|
||||||
|
boost::bind(&Dhcpv4Srv::unpackOptions,
|
||||||
|
this, _1, _2, _3));
|
||||||
|
}
|
||||||
|
|
||||||
|
options.insert(std::make_pair(opt_type, opt));
|
||||||
|
offset += opt_len;
|
||||||
|
}
|
||||||
|
return (offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace dhcp
|
} // namespace dhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
@@ -351,6 +351,18 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
/// @brief Implements a callback function to parse options in the message.
|
||||||
|
///
|
||||||
|
/// @param buf a A buffer holding options in on-wire format.
|
||||||
|
/// @param option_space A name of the option space which holds definitions
|
||||||
|
/// of to be used to parse options in the packets.
|
||||||
|
/// @param [out] options A reference to the collection where parsed options
|
||||||
|
/// will be stored.
|
||||||
|
/// @return An offset to the first byte after last parsed option.
|
||||||
|
size_t unpackOptions(const OptionBuffer& buf,
|
||||||
|
const std::string& option_space,
|
||||||
|
isc::dhcp::OptionCollection& options);
|
||||||
|
|
||||||
/// @brief Constructs netmask option based on subnet4
|
/// @brief Constructs netmask option based on subnet4
|
||||||
/// @param subnet subnet for which the netmask will be calculated
|
/// @param subnet subnet for which the netmask will be calculated
|
||||||
///
|
///
|
||||||
|
@@ -229,6 +229,15 @@ bool Dhcpv6Srv::run() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In order to parse the DHCP options, the server needs to use some
|
||||||
|
// configuration information such as: existing option spaces, option
|
||||||
|
// definitions etc. This is the kind of information which is not
|
||||||
|
// available in the libdhcp, so we need to supply our own implementation
|
||||||
|
// of the option parsing function here, which would rely on the
|
||||||
|
// configuration data.
|
||||||
|
query->setCallback(boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
|
||||||
|
_3, _4, _5));
|
||||||
|
|
||||||
bool skip_unpack = false;
|
bool skip_unpack = false;
|
||||||
|
|
||||||
// The packet has just been received so contains the uninterpreted wire
|
// The packet has just been received so contains the uninterpreted wire
|
||||||
@@ -703,7 +712,7 @@ Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
|
|||||||
void
|
void
|
||||||
Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
|
Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
|
||||||
RequirementLevel serverid) {
|
RequirementLevel serverid) {
|
||||||
Option::OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
|
OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
|
||||||
switch (clientid) {
|
switch (clientid) {
|
||||||
case MANDATORY:
|
case MANDATORY:
|
||||||
if (client_ids.size() != 1) {
|
if (client_ids.size() != 1) {
|
||||||
@@ -724,7 +733,7 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
|
OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
|
||||||
switch (serverid) {
|
switch (serverid) {
|
||||||
case FORBIDDEN:
|
case FORBIDDEN:
|
||||||
if (!server_ids.empty()) {
|
if (!server_ids.empty()) {
|
||||||
@@ -870,7 +879,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
|||||||
//
|
//
|
||||||
// @todo: expand this to cover IA_PD and IA_TA once we implement support for
|
// @todo: expand this to cover IA_PD and IA_TA once we implement support for
|
||||||
// prefix delegation and temporary addresses.
|
// prefix delegation and temporary addresses.
|
||||||
for (Option::OptionCollection::iterator opt = question->options_.begin();
|
for (OptionCollection::iterator opt = question->options_.begin();
|
||||||
opt != question->options_.end(); ++opt) {
|
opt != question->options_.end(); ++opt) {
|
||||||
switch (opt->second->getType()) {
|
switch (opt->second->getType()) {
|
||||||
case D6O_IA_NA: {
|
case D6O_IA_NA: {
|
||||||
@@ -1052,8 +1061,8 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
|
|||||||
|
|
||||||
// Get all IAs from the answer. For each IA, holding an address we will
|
// Get all IAs from the answer. For each IA, holding an address we will
|
||||||
// create a corresponding NameChangeRequest.
|
// create a corresponding NameChangeRequest.
|
||||||
Option::OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
|
OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
|
||||||
for (Option::OptionCollection::const_iterator answer_ia =
|
for (OptionCollection::const_iterator answer_ia =
|
||||||
answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
|
answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
|
||||||
// @todo IA_NA may contain multiple addresses. We should process
|
// @todo IA_NA may contain multiple addresses. We should process
|
||||||
// each address individually. Currently we get only one.
|
// each address individually. Currently we get only one.
|
||||||
@@ -1493,7 +1502,7 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
|
|||||||
}
|
}
|
||||||
DuidPtr duid(new DUID(opt_duid->getData()));
|
DuidPtr duid(new DUID(opt_duid->getData()));
|
||||||
|
|
||||||
for (Option::OptionCollection::iterator opt = renew->options_.begin();
|
for (OptionCollection::iterator opt = renew->options_.begin();
|
||||||
opt != renew->options_.end(); ++opt) {
|
opt != renew->options_.end(); ++opt) {
|
||||||
switch (opt->second->getType()) {
|
switch (opt->second->getType()) {
|
||||||
case D6O_IA_NA: {
|
case D6O_IA_NA: {
|
||||||
@@ -1543,7 +1552,7 @@ Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
|
|||||||
DuidPtr duid(new DUID(opt_duid->getData()));
|
DuidPtr duid(new DUID(opt_duid->getData()));
|
||||||
|
|
||||||
int general_status = STATUS_Success;
|
int general_status = STATUS_Success;
|
||||||
for (Option::OptionCollection::iterator opt = release->options_.begin();
|
for (OptionCollection::iterator opt = release->options_.begin();
|
||||||
opt != release->options_.end(); ++opt) {
|
opt != release->options_.end(); ++opt) {
|
||||||
switch (opt->second->getType()) {
|
switch (opt->second->getType()) {
|
||||||
case D6O_IA_NA: {
|
case D6O_IA_NA: {
|
||||||
@@ -1868,5 +1877,99 @@ Dhcpv6Srv::openActiveSockets(const uint16_t port) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
|
||||||
|
const std::string& option_space,
|
||||||
|
isc::dhcp::OptionCollection& options,
|
||||||
|
size_t* relay_msg_offset,
|
||||||
|
size_t* relay_msg_len) {
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t length = buf.size();
|
||||||
|
|
||||||
|
OptionDefContainer option_defs;
|
||||||
|
if (option_space == "dhcp6") {
|
||||||
|
// Get the list of stdandard option definitions.
|
||||||
|
option_defs = LibDHCP::getOptionDefs(Option::V6);
|
||||||
|
} else if (!option_space.empty()) {
|
||||||
|
OptionDefContainerPtr option_defs_ptr =
|
||||||
|
CfgMgr::instance().getOptionDefs(option_space);
|
||||||
|
if (option_defs_ptr != NULL) {
|
||||||
|
option_defs = *option_defs_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the search index #1. It allows to search for option definitions
|
||||||
|
// using option code.
|
||||||
|
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
|
||||||
|
|
||||||
|
// The buffer being read comprises a set of options, each starting with
|
||||||
|
// a two-byte type code and a two-byte length field.
|
||||||
|
while (offset + 4 <= length) {
|
||||||
|
uint16_t opt_type = isc::util::readUint16(&buf[offset]);
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
uint16_t opt_len = isc::util::readUint16(&buf[offset]);
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
if (offset + opt_len > length) {
|
||||||
|
// @todo: consider throwing exception here.
|
||||||
|
return (offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
|
||||||
|
// remember offset of the beginning of the relay-msg option
|
||||||
|
*relay_msg_offset = offset;
|
||||||
|
*relay_msg_len = opt_len;
|
||||||
|
|
||||||
|
// do not create that relay-msg option
|
||||||
|
offset += opt_len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all definitions with the particular option code. Note that option
|
||||||
|
// code is non-unique within this container however at this point we
|
||||||
|
// expect to get one option definition with the particular code. If more
|
||||||
|
// are returned we report an error.
|
||||||
|
const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
|
||||||
|
// Get the number of returned option definitions for the option code.
|
||||||
|
size_t num_defs = distance(range.first, range.second);
|
||||||
|
|
||||||
|
OptionPtr opt;
|
||||||
|
if (num_defs > 1) {
|
||||||
|
// Multiple options of the same code are not supported right now!
|
||||||
|
isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
|
||||||
|
" for option type " << opt_type << " returned. Currently it is not"
|
||||||
|
" supported to initialize multiple option definitions"
|
||||||
|
" for the same option code. This will be supported once"
|
||||||
|
" support for option spaces is implemented");
|
||||||
|
} else if (num_defs == 0) {
|
||||||
|
// @todo Don't crash if definition does not exist because only a few
|
||||||
|
// option definitions are initialized right now. In the future
|
||||||
|
// we will initialize definitions for all options and we will
|
||||||
|
// remove this elseif. For now, return generic option.
|
||||||
|
opt = OptionPtr(new Option(Option::V6, opt_type,
|
||||||
|
buf.begin() + offset,
|
||||||
|
buf.begin() + offset + opt_len));
|
||||||
|
opt->setEncapsulatedSpace("dhcp6");
|
||||||
|
} else {
|
||||||
|
// The option definition has been found. Use it to create
|
||||||
|
// the option instance from the provided buffer chunk.
|
||||||
|
const OptionDefinitionPtr& def = *(range.first);
|
||||||
|
assert(def);
|
||||||
|
opt = def->optionFactory(Option::V6, opt_type,
|
||||||
|
buf.begin() + offset,
|
||||||
|
buf.begin() + offset + opt_len,
|
||||||
|
boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
|
||||||
|
_3, _4, _5));
|
||||||
|
}
|
||||||
|
// add option to options
|
||||||
|
options.insert(std::make_pair(opt_type, opt));
|
||||||
|
offset += opt_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -459,6 +459,24 @@ protected:
|
|||||||
/// simulates transmission of a packet. For that purpose it is protected.
|
/// simulates transmission of a packet. For that purpose it is protected.
|
||||||
virtual void sendPacket(const Pkt6Ptr& pkt);
|
virtual void sendPacket(const Pkt6Ptr& pkt);
|
||||||
|
|
||||||
|
/// @brief Implements a callback function to parse options in the message.
|
||||||
|
///
|
||||||
|
/// @param buf a A buffer holding options in on-wire format.
|
||||||
|
/// @param option_space A name of the option space which holds definitions
|
||||||
|
/// of to be used to parse options in the packets.
|
||||||
|
/// @param [out] options A reference to the collection where parsed options
|
||||||
|
/// will be stored.
|
||||||
|
/// @param relay_msg_offset Reference to a size_t structure. If specified,
|
||||||
|
/// offset to beginning of relay_msg option will be stored in it.
|
||||||
|
/// @param relay_msg_len reference to a size_t structure. If specified,
|
||||||
|
/// length of the relay_msg option will be stored in it.
|
||||||
|
/// @return An offset to the first byte after last parsed option.
|
||||||
|
size_t unpackOptions(const OptionBuffer& buf,
|
||||||
|
const std::string& option_space,
|
||||||
|
isc::dhcp::OptionCollection& options,
|
||||||
|
size_t* relay_msg_offset,
|
||||||
|
size_t* relay_msg_len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @brief Allocation Engine.
|
/// @brief Allocation Engine.
|
||||||
/// Pointer to the allocation engine that we are currently using
|
/// Pointer to the allocation engine that we are currently using
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include <dhcp/option6_client_fqdn.h>
|
#include <dhcp/option6_client_fqdn.h>
|
||||||
#include <dhcp/option6_ia.h>
|
#include <dhcp/option6_ia.h>
|
||||||
#include <dhcp/option6_iaaddr.h>
|
#include <dhcp/option6_iaaddr.h>
|
||||||
|
#include <dhcp/option_int.h>
|
||||||
#include <dhcp/option_int_array.h>
|
#include <dhcp/option_int_array.h>
|
||||||
#include <dhcp/iface_mgr.h>
|
#include <dhcp/iface_mgr.h>
|
||||||
#include <dhcp6/config_parser.h>
|
#include <dhcp6/config_parser.h>
|
||||||
@@ -2157,7 +2158,7 @@ TEST_F(Dhcpv6SrvTest, portsRelayedTraffic) {
|
|||||||
// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
|
// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
|
||||||
// @todo Uncomment this test as part of #3180 work.
|
// @todo Uncomment this test as part of #3180 work.
|
||||||
// Kea code currently fails to handle docsis traffic.
|
// Kea code currently fails to handle docsis traffic.
|
||||||
TEST_F(Dhcpv6SrvTest, DISABLED_docsisTraffic) {
|
TEST_F(Dhcpv6SrvTest, docsisTraffic) {
|
||||||
|
|
||||||
NakedDhcpv6Srv srv(0);
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
@@ -2181,6 +2182,78 @@ TEST_F(Dhcpv6SrvTest, DISABLED_docsisTraffic) {
|
|||||||
/// that is relayed properly, etc.
|
/// that is relayed properly, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that the following option structure can be parsed:
|
||||||
|
// - option (option space 'foobar')
|
||||||
|
// - sub option (option space 'foo')
|
||||||
|
// - sub option (option space 'bar')
|
||||||
|
TEST_F(Dhcpv6SrvTest, unpackOptions) {
|
||||||
|
// Create option definition for each level of encapsulation. Each option
|
||||||
|
// definition is for the option code 1. Options may have the same
|
||||||
|
// option code because they belong to different option spaces.
|
||||||
|
|
||||||
|
// Top level option encapsulates options which belong to 'space-foo'.
|
||||||
|
OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
|
||||||
|
"space-foo"));\
|
||||||
|
// Middle option encapsulates options which belong to 'space-bar'
|
||||||
|
OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
|
||||||
|
"space-bar"));
|
||||||
|
// Low level option doesn't encapsulate any option space.
|
||||||
|
OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
|
||||||
|
"uint8"));
|
||||||
|
|
||||||
|
// Add option definitions to the Configuration Manager. Each goes under
|
||||||
|
// different option space.
|
||||||
|
CfgMgr& cfgmgr = CfgMgr::instance();
|
||||||
|
ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def, "space-foobar"));
|
||||||
|
ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def2, "space-foo"));
|
||||||
|
ASSERT_NO_THROW(cfgmgr.addOptionDef(opt_def3, "space-bar"));
|
||||||
|
|
||||||
|
// Create the buffer holding the structure of options.
|
||||||
|
const char raw_data[] = {
|
||||||
|
// First option starts here.
|
||||||
|
0x00, 0x01, // option code = 1
|
||||||
|
0x00, 0x0F, // option length = 15
|
||||||
|
0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
|
||||||
|
// Sub option starts here.
|
||||||
|
0x00, 0x01, // option code = 1
|
||||||
|
0x00, 0x07, // option length = 7
|
||||||
|
0x01, 0x02, // this option carries uint16 value
|
||||||
|
// Last option starts here.
|
||||||
|
0x00, 0x01, // option code = 1
|
||||||
|
0x00, 0x01, // option length = 1
|
||||||
|
0x00 // This option carries a single uint8 value and has no sub options.
|
||||||
|
};
|
||||||
|
OptionBuffer buf(raw_data, raw_data + sizeof(raw_data));
|
||||||
|
|
||||||
|
// Parse options.
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
OptionCollection options;
|
||||||
|
ASSERT_NO_THROW(srv.unpackOptions(buf, "space-foobar", options, 0, 0));
|
||||||
|
|
||||||
|
// There should be one top level option.
|
||||||
|
ASSERT_EQ(1, options.size());
|
||||||
|
boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
|
||||||
|
boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
|
||||||
|
second);
|
||||||
|
ASSERT_TRUE(option_foobar);
|
||||||
|
EXPECT_EQ(1, option_foobar->getType());
|
||||||
|
EXPECT_EQ(0x00010203, option_foobar->getValue());
|
||||||
|
// There should be a middle level option held in option_foobar.
|
||||||
|
boost::shared_ptr<OptionInt<uint16_t> > option_foo =
|
||||||
|
boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
|
||||||
|
getOption(1));
|
||||||
|
ASSERT_TRUE(option_foo);
|
||||||
|
EXPECT_EQ(1, option_foo->getType());
|
||||||
|
EXPECT_EQ(0x0102, option_foo->getValue());
|
||||||
|
// Finally, there should be a low level option under option_foo.
|
||||||
|
boost::shared_ptr<OptionInt<uint8_t> > option_bar =
|
||||||
|
boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
|
||||||
|
ASSERT_TRUE(option_bar);
|
||||||
|
EXPECT_EQ(1, option_bar->getType());
|
||||||
|
EXPECT_EQ(0x0, option_bar->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
|
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
|
||||||
/// to call processX() methods.
|
/// to call processX() methods.
|
||||||
|
|
||||||
|
@@ -110,6 +110,7 @@ public:
|
|||||||
using Dhcpv6Srv::sanityCheck;
|
using Dhcpv6Srv::sanityCheck;
|
||||||
using Dhcpv6Srv::loadServerID;
|
using Dhcpv6Srv::loadServerID;
|
||||||
using Dhcpv6Srv::writeServerID;
|
using Dhcpv6Srv::writeServerID;
|
||||||
|
using Dhcpv6Srv::unpackOptions;
|
||||||
using Dhcpv6Srv::name_change_reqs_;
|
using Dhcpv6Srv::name_change_reqs_;
|
||||||
|
|
||||||
/// @brief packets we pretend to receive
|
/// @brief packets we pretend to receive
|
||||||
|
@@ -128,7 +128,7 @@ LibDHCP::optionFactory(Option::Universe u,
|
|||||||
|
|
||||||
|
|
||||||
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
||||||
isc::dhcp::Option::OptionCollection& options,
|
isc::dhcp::OptionCollection& options,
|
||||||
size_t* relay_msg_offset /* = 0 */,
|
size_t* relay_msg_offset /* = 0 */,
|
||||||
size_t* relay_msg_len /* = 0 */) {
|
size_t* relay_msg_len /* = 0 */) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
@@ -206,7 +206,7 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
||||||
isc::dhcp::Option::OptionCollection& options) {
|
isc::dhcp::OptionCollection& options) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
|
||||||
// Get the list of stdandard option definitions.
|
// Get the list of stdandard option definitions.
|
||||||
@@ -282,8 +282,8 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
|||||||
|
|
||||||
void
|
void
|
||||||
LibDHCP::packOptions(isc::util::OutputBuffer& buf,
|
LibDHCP::packOptions(isc::util::OutputBuffer& buf,
|
||||||
const Option::OptionCollection& options) {
|
const OptionCollection& options) {
|
||||||
for (Option::OptionCollection::const_iterator it = options.begin();
|
for (OptionCollection::const_iterator it = options.begin();
|
||||||
it != options.end(); ++it) {
|
it != options.end(); ++it) {
|
||||||
it->second->pack(buf);
|
it->second->pack(buf);
|
||||||
}
|
}
|
||||||
|
@@ -100,7 +100,7 @@ public:
|
|||||||
/// @param buf output buffer (assembled options will be stored here)
|
/// @param buf output buffer (assembled options will be stored here)
|
||||||
/// @param options collection of options to store to
|
/// @param options collection of options to store to
|
||||||
static void packOptions(isc::util::OutputBuffer& buf,
|
static void packOptions(isc::util::OutputBuffer& buf,
|
||||||
const isc::dhcp::Option::OptionCollection& options);
|
const isc::dhcp::OptionCollection& options);
|
||||||
|
|
||||||
/// @brief Parses provided buffer as DHCPv4 options and creates Option objects.
|
/// @brief Parses provided buffer as DHCPv4 options and creates Option objects.
|
||||||
///
|
///
|
||||||
@@ -111,7 +111,7 @@ public:
|
|||||||
/// @param options Reference to option container. Options will be
|
/// @param options Reference to option container. Options will be
|
||||||
/// put here.
|
/// put here.
|
||||||
static size_t unpackOptions4(const OptionBuffer& buf,
|
static size_t unpackOptions4(const OptionBuffer& buf,
|
||||||
isc::dhcp::Option::OptionCollection& options);
|
isc::dhcp::OptionCollection& options);
|
||||||
|
|
||||||
/// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
|
/// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
|
||||||
///
|
///
|
||||||
@@ -133,7 +133,7 @@ public:
|
|||||||
/// length of the relay_msg option will be stored in it.
|
/// length of the relay_msg option will be stored in it.
|
||||||
/// @return offset to the first byte after last parsed option
|
/// @return offset to the first byte after last parsed option
|
||||||
static size_t unpackOptions6(const OptionBuffer& buf,
|
static size_t unpackOptions6(const OptionBuffer& buf,
|
||||||
isc::dhcp::Option::OptionCollection& options,
|
isc::dhcp::OptionCollection& options,
|
||||||
size_t* relay_msg_offset = 0,
|
size_t* relay_msg_offset = 0,
|
||||||
size_t* relay_msg_len = 0);
|
size_t* relay_msg_len = 0);
|
||||||
|
|
||||||
|
@@ -126,6 +126,13 @@ void Option::unpack(OptionBufferConstIter begin,
|
|||||||
|
|
||||||
void
|
void
|
||||||
Option::unpackOptions(const OptionBuffer& buf) {
|
Option::unpackOptions(const OptionBuffer& buf) {
|
||||||
|
// If custom option parsing function has been set, use this function
|
||||||
|
// to parse options. Otherwise, use standard function from libdhcp++.
|
||||||
|
if (!callback_.empty()) {
|
||||||
|
callback_(buf, getEncapsulatedSpace(), options_, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (universe_) {
|
switch (universe_) {
|
||||||
case V4:
|
case V4:
|
||||||
LibDHCP::unpackOptions4(buf, options_);
|
LibDHCP::unpackOptions4(buf, options_);
|
||||||
@@ -146,7 +153,7 @@ uint16_t Option::len() {
|
|||||||
int length = getHeaderLen() + data_.size();
|
int length = getHeaderLen() + data_.size();
|
||||||
|
|
||||||
// ... and sum of lengths of all suboptions
|
// ... and sum of lengths of all suboptions
|
||||||
for (Option::OptionCollection::iterator it = options_.begin();
|
for (OptionCollection::iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
@@ -169,7 +176,7 @@ Option::valid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OptionPtr Option::getOption(uint16_t opt_type) {
|
OptionPtr Option::getOption(uint16_t opt_type) {
|
||||||
isc::dhcp::Option::OptionCollection::const_iterator x =
|
isc::dhcp::OptionCollection::const_iterator x =
|
||||||
options_.find(opt_type);
|
options_.find(opt_type);
|
||||||
if ( x != options_.end() ) {
|
if ( x != options_.end() ) {
|
||||||
return (*x).second;
|
return (*x).second;
|
||||||
@@ -178,7 +185,7 @@ OptionPtr Option::getOption(uint16_t opt_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Option::delOption(uint16_t opt_type) {
|
bool Option::delOption(uint16_t opt_type) {
|
||||||
isc::dhcp::Option::OptionCollection::iterator x = options_.find(opt_type);
|
isc::dhcp::OptionCollection::iterator x = options_.find(opt_type);
|
||||||
if ( x != options_.end() ) {
|
if ( x != options_.end() ) {
|
||||||
options_.erase(x);
|
options_.erase(x);
|
||||||
return true; // delete successful
|
return true; // delete successful
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
|
|
||||||
|
#include <boost/function.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -44,6 +45,14 @@ typedef boost::shared_ptr<OptionBuffer> OptionBufferPtr;
|
|||||||
class Option;
|
class Option;
|
||||||
typedef boost::shared_ptr<Option> OptionPtr;
|
typedef boost::shared_ptr<Option> OptionPtr;
|
||||||
|
|
||||||
|
/// A collection of DHCPv6 options
|
||||||
|
typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
|
||||||
|
|
||||||
|
/// This type describes a callback function to parse options from buffer.
|
||||||
|
typedef boost::function< size_t(const OptionBuffer&, const std::string,
|
||||||
|
OptionCollection&, size_t*, size_t*)
|
||||||
|
> UnpackOptionsCallback;
|
||||||
|
|
||||||
|
|
||||||
class Option {
|
class Option {
|
||||||
public:
|
public:
|
||||||
@@ -56,8 +65,6 @@ public:
|
|||||||
/// defines option universe DHCPv4 or DHCPv6
|
/// defines option universe DHCPv4 or DHCPv6
|
||||||
enum Universe { V4, V6 };
|
enum Universe { V4, V6 };
|
||||||
|
|
||||||
/// a collection of DHCPv6 options
|
|
||||||
typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
|
|
||||||
|
|
||||||
/// @brief a factory function prototype
|
/// @brief a factory function prototype
|
||||||
///
|
///
|
||||||
@@ -290,6 +297,29 @@ public:
|
|||||||
data_.assign(first, last);
|
data_.assign(first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Sets the name of the option space encapsulated by this option.
|
||||||
|
///
|
||||||
|
/// @param encapsulated_space name of the option space encapsulated by
|
||||||
|
/// this option.
|
||||||
|
void setEncapsulatedSpace(const std::string& encapsulated_space) {
|
||||||
|
encapsulated_space_ = encapsulated_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns the name of the option space encapsulated by this option.
|
||||||
|
///
|
||||||
|
/// @return name of the option space encapsulated by this option.
|
||||||
|
std::string getEncapsulatedSpace() const {
|
||||||
|
return (encapsulated_space_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set callback function to be used to parse options.
|
||||||
|
///
|
||||||
|
/// @param callback An instance of the callback function or NULL to
|
||||||
|
/// uninstall callback.
|
||||||
|
void setCallback(UnpackOptionsCallback callback) {
|
||||||
|
callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/// just to force that every option has virtual dtor
|
/// just to force that every option has virtual dtor
|
||||||
virtual ~Option();
|
virtual ~Option();
|
||||||
|
|
||||||
@@ -372,6 +402,12 @@ protected:
|
|||||||
/// collection for storing suboptions
|
/// collection for storing suboptions
|
||||||
OptionCollection options_;
|
OptionCollection options_;
|
||||||
|
|
||||||
|
/// Name of the option space being encapsulated by this option.
|
||||||
|
std::string encapsulated_space_;
|
||||||
|
|
||||||
|
/// A callback to be called to unpack options from the packet.
|
||||||
|
UnpackOptionsCallback callback_;
|
||||||
|
|
||||||
/// @todo probably 2 different containers have to be used for v4 (unique
|
/// @todo probably 2 different containers have to be used for v4 (unique
|
||||||
/// options) and v6 (options with the same type can repeat)
|
/// options) and v6 (options with the same type can repeat)
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -36,6 +36,8 @@ Option6IA::Option6IA(uint16_t type, uint32_t iaid)
|
|||||||
isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
|
isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
|
||||||
"a different layout");
|
"a different layout");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEncapsulatedSpace("dhcp6");
|
||||||
}
|
}
|
||||||
|
|
||||||
Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
|
Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
|
||||||
@@ -48,6 +50,8 @@ Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
|
|||||||
"a different layout");
|
"a different layout");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEncapsulatedSpace("dhcp6");
|
||||||
|
|
||||||
unpack(begin, end);
|
unpack(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +117,7 @@ uint16_t Option6IA::len() {
|
|||||||
OPTION6_IA_LEN /* option content (12) */;
|
OPTION6_IA_LEN /* option content (12) */;
|
||||||
|
|
||||||
// length of all suboptions
|
// length of all suboptions
|
||||||
for (Option::OptionCollection::iterator it = options_.begin();
|
for (OptionCollection::iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -35,6 +35,7 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
|
|||||||
uint32_t pref, uint32_t valid)
|
uint32_t pref, uint32_t valid)
|
||||||
:Option(V6, type), addr_(addr), preferred_(pref),
|
:Option(V6, type), addr_(addr), preferred_(pref),
|
||||||
valid_(valid) {
|
valid_(valid) {
|
||||||
|
setEncapsulatedSpace("dhcp6");
|
||||||
if (!addr.isV6()) {
|
if (!addr.isV6()) {
|
||||||
isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
|
isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
|
||||||
}
|
}
|
||||||
@@ -43,6 +44,7 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
|
|||||||
Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
|
Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
|
||||||
OptionBuffer::const_iterator end)
|
OptionBuffer::const_iterator end)
|
||||||
:Option(V6, type), addr_("::") {
|
:Option(V6, type), addr_("::") {
|
||||||
|
setEncapsulatedSpace("dhcp6");
|
||||||
unpack(begin, end);
|
unpack(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ uint16_t Option6IAAddr::len() {
|
|||||||
// length of all suboptions
|
// length of all suboptions
|
||||||
// TODO implement:
|
// TODO implement:
|
||||||
// protected: unsigned short Option::lenHelper(int header_size);
|
// protected: unsigned short Option::lenHelper(int header_size);
|
||||||
for (Option::OptionCollection::iterator it = options_.begin();
|
for (OptionCollection::iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
|
@@ -34,6 +34,7 @@ namespace dhcp {
|
|||||||
Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& prefix,
|
Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& prefix,
|
||||||
uint8_t prefix_len, uint32_t pref, uint32_t valid)
|
uint8_t prefix_len, uint32_t pref, uint32_t valid)
|
||||||
:Option6IAAddr(type, prefix, pref, valid), prefix_len_(prefix_len) {
|
:Option6IAAddr(type, prefix, pref, valid), prefix_len_(prefix_len) {
|
||||||
|
setEncapsulatedSpace("dhcp6");
|
||||||
// Option6IAAddr will check if prefix is IPv6 and will throw if it is not
|
// Option6IAAddr will check if prefix is IPv6 and will throw if it is not
|
||||||
if (prefix_len > 128) {
|
if (prefix_len > 128) {
|
||||||
isc_throw(BadValue, prefix_len << " is not a valid prefix length. "
|
isc_throw(BadValue, prefix_len << " is not a valid prefix length. "
|
||||||
@@ -44,6 +45,7 @@ Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress&
|
|||||||
Option6IAPrefix::Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
|
Option6IAPrefix::Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
|
||||||
OptionBuffer::const_iterator end)
|
OptionBuffer::const_iterator end)
|
||||||
:Option6IAAddr(type, begin, end) {
|
:Option6IAAddr(type, begin, end) {
|
||||||
|
setEncapsulatedSpace("dhcp6");
|
||||||
unpack(begin, end);
|
unpack(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +115,7 @@ uint16_t Option6IAPrefix::len() {
|
|||||||
uint16_t length = OPTION6_HDR_LEN + OPTION6_IAPREFIX_LEN;
|
uint16_t length = OPTION6_HDR_LEN + OPTION6_IAPREFIX_LEN;
|
||||||
|
|
||||||
// length of all suboptions
|
// length of all suboptions
|
||||||
for (Option::OptionCollection::const_iterator it = options_.begin();
|
for (OptionCollection::const_iterator it = options_.begin();
|
||||||
it != options_.end(); ++it) {
|
it != options_.end(); ++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
|
|||||||
Universe u)
|
Universe u)
|
||||||
: Option(u, def.getCode(), OptionBuffer()),
|
: Option(u, def.getCode(), OptionBuffer()),
|
||||||
definition_(def) {
|
definition_(def) {
|
||||||
|
setEncapsulatedSpace(def.getEncapsulatedSpace());
|
||||||
createBuffers();
|
createBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
|
|||||||
const OptionBuffer& data)
|
const OptionBuffer& data)
|
||||||
: Option(u, def.getCode(), data.begin(), data.end()),
|
: Option(u, def.getCode(), data.begin(), data.end()),
|
||||||
definition_(def) {
|
definition_(def) {
|
||||||
|
setEncapsulatedSpace(def.getEncapsulatedSpace());
|
||||||
createBuffers(getData());
|
createBuffers(getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +43,7 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
|
|||||||
OptionBufferConstIter last)
|
OptionBufferConstIter last)
|
||||||
: Option(u, def.getCode(), first, last),
|
: Option(u, def.getCode(), first, last),
|
||||||
definition_(def) {
|
definition_(def) {
|
||||||
|
setEncapsulatedSpace(def.getEncapsulatedSpace());
|
||||||
createBuffers(getData());
|
createBuffers(getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +525,7 @@ OptionCustom::len() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ... and lengths of all suboptions
|
// ... and lengths of all suboptions
|
||||||
for (OptionCustom::OptionCollection::iterator it = options_.begin();
|
for (OptionCollection::iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
|
@@ -113,7 +113,8 @@ OptionDefinition::addRecordField(const OptionDataType data_type) {
|
|||||||
OptionPtr
|
OptionPtr
|
||||||
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
|
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
|
||||||
OptionBufferConstIter begin,
|
OptionBufferConstIter begin,
|
||||||
OptionBufferConstIter end) const {
|
OptionBufferConstIter end,
|
||||||
|
UnpackOptionsCallback callback) const {
|
||||||
try {
|
try {
|
||||||
switch(type_) {
|
switch(type_) {
|
||||||
case OPT_EMPTY_TYPE:
|
case OPT_EMPTY_TYPE:
|
||||||
@@ -124,31 +125,37 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
|
|||||||
|
|
||||||
case OPT_UINT8_TYPE:
|
case OPT_UINT8_TYPE:
|
||||||
return (array_type_ ? factoryGeneric(u, type, begin, end) :
|
return (array_type_ ? factoryGeneric(u, type, begin, end) :
|
||||||
factoryInteger<uint8_t>(u, type, begin, end));
|
factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
|
||||||
|
begin, end, callback));
|
||||||
|
|
||||||
case OPT_INT8_TYPE:
|
case OPT_INT8_TYPE:
|
||||||
return (array_type_ ? factoryGeneric(u, type, begin, end) :
|
return (array_type_ ? factoryGeneric(u, type, begin, end) :
|
||||||
factoryInteger<int8_t>(u, type, begin, end));
|
factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
|
||||||
|
begin, end, callback));
|
||||||
|
|
||||||
case OPT_UINT16_TYPE:
|
case OPT_UINT16_TYPE:
|
||||||
return (array_type_ ?
|
return (array_type_ ?
|
||||||
factoryIntegerArray<uint16_t>(u, type, begin, end) :
|
factoryIntegerArray<uint16_t>(u, type, begin, end) :
|
||||||
factoryInteger<uint16_t>(u, type, begin, end));
|
factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
|
||||||
|
begin, end, callback));
|
||||||
|
|
||||||
case OPT_INT16_TYPE:
|
case OPT_INT16_TYPE:
|
||||||
return (array_type_ ?
|
return (array_type_ ?
|
||||||
factoryIntegerArray<uint16_t>(u, type, begin, end) :
|
factoryIntegerArray<uint16_t>(u, type, begin, end) :
|
||||||
factoryInteger<int16_t>(u, type, begin, end));
|
factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
|
||||||
|
begin, end, callback));
|
||||||
|
|
||||||
case OPT_UINT32_TYPE:
|
case OPT_UINT32_TYPE:
|
||||||
return (array_type_ ?
|
return (array_type_ ?
|
||||||
factoryIntegerArray<uint32_t>(u, type, begin, end) :
|
factoryIntegerArray<uint32_t>(u, type, begin, end) :
|
||||||
factoryInteger<uint32_t>(u, type, begin, end));
|
factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
|
||||||
|
begin, end, callback));
|
||||||
|
|
||||||
case OPT_INT32_TYPE:
|
case OPT_INT32_TYPE:
|
||||||
return (array_type_ ?
|
return (array_type_ ?
|
||||||
factoryIntegerArray<uint32_t>(u, type, begin, end) :
|
factoryIntegerArray<uint32_t>(u, type, begin, end) :
|
||||||
factoryInteger<int32_t>(u, type, begin, end));
|
factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
|
||||||
|
begin, end, callback));
|
||||||
|
|
||||||
case OPT_IPV4_ADDRESS_TYPE:
|
case OPT_IPV4_ADDRESS_TYPE:
|
||||||
// If definition specifies that an option is an array
|
// If definition specifies that an option is an array
|
||||||
@@ -211,8 +218,9 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
|
|||||||
|
|
||||||
OptionPtr
|
OptionPtr
|
||||||
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
|
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
|
||||||
const OptionBuffer& buf) const {
|
const OptionBuffer& buf,
|
||||||
return (optionFactory(u, type, buf.begin(), buf.end()));
|
UnpackOptionsCallback callback) const {
|
||||||
|
return (optionFactory(u, type, buf.begin(), buf.end(), callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionPtr
|
OptionPtr
|
||||||
|
@@ -315,7 +315,8 @@ public:
|
|||||||
/// @throw InvalidOptionValue if data for the option is invalid.
|
/// @throw InvalidOptionValue if data for the option is invalid.
|
||||||
OptionPtr optionFactory(Option::Universe u, uint16_t type,
|
OptionPtr optionFactory(Option::Universe u, uint16_t type,
|
||||||
OptionBufferConstIter begin,
|
OptionBufferConstIter begin,
|
||||||
OptionBufferConstIter end) const;
|
OptionBufferConstIter end,
|
||||||
|
UnpackOptionsCallback callback = NULL) const;
|
||||||
|
|
||||||
/// @brief Option factory.
|
/// @brief Option factory.
|
||||||
///
|
///
|
||||||
@@ -334,7 +335,8 @@ public:
|
|||||||
/// @return instance of the DHCP option.
|
/// @return instance of the DHCP option.
|
||||||
/// @throw InvalidOptionValue if data for the option is invalid.
|
/// @throw InvalidOptionValue if data for the option is invalid.
|
||||||
OptionPtr optionFactory(Option::Universe u, uint16_t type,
|
OptionPtr optionFactory(Option::Universe u, uint16_t type,
|
||||||
const OptionBuffer& buf = OptionBuffer()) const;
|
const OptionBuffer& buf = OptionBuffer(),
|
||||||
|
UnpackOptionsCallback callback = NULL) const;
|
||||||
|
|
||||||
/// @brief Option factory.
|
/// @brief Option factory.
|
||||||
///
|
///
|
||||||
@@ -444,9 +446,14 @@ public:
|
|||||||
/// @throw isc::OutOfRange if provided option buffer length is invalid.
|
/// @throw isc::OutOfRange if provided option buffer length is invalid.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
|
static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
|
||||||
|
const std::string& encapsulated_space,
|
||||||
OptionBufferConstIter begin,
|
OptionBufferConstIter begin,
|
||||||
OptionBufferConstIter end) {
|
OptionBufferConstIter end,
|
||||||
OptionPtr option(new OptionInt<T>(u, type, begin, end));
|
UnpackOptionsCallback callback) {
|
||||||
|
OptionPtr option(new OptionInt<T>(u, type, 0));
|
||||||
|
option->setEncapsulatedSpace(encapsulated_space);
|
||||||
|
option->setCallback(callback);
|
||||||
|
option->unpack(begin, end);
|
||||||
return (option);
|
return (option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -52,6 +52,7 @@ public:
|
|||||||
if (!OptionDataTypeTraits<T>::integer_type) {
|
if (!OptionDataTypeTraits<T>::integer_type) {
|
||||||
isc_throw(dhcp::InvalidDataType, "non-integer type");
|
isc_throw(dhcp::InvalidDataType, "non-integer type");
|
||||||
}
|
}
|
||||||
|
setEncapsulatedSpace(u == Option::V4 ? "dhcp4" : "dhcp6");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Constructor.
|
/// @brief Constructor.
|
||||||
@@ -74,6 +75,7 @@ public:
|
|||||||
if (!OptionDataTypeTraits<T>::integer_type) {
|
if (!OptionDataTypeTraits<T>::integer_type) {
|
||||||
isc_throw(dhcp::InvalidDataType, "non-integer type");
|
isc_throw(dhcp::InvalidDataType, "non-integer type");
|
||||||
}
|
}
|
||||||
|
setEncapsulatedSpace(u == Option::V4 ? "dhcp4" : "dhcp6");
|
||||||
unpack(begin, end);
|
unpack(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +177,7 @@ public:
|
|||||||
// The data length is equal to size of T.
|
// The data length is equal to size of T.
|
||||||
length += sizeof(T);;
|
length += sizeof(T);;
|
||||||
// length of all suboptions
|
// length of all suboptions
|
||||||
for (Option::OptionCollection::iterator it = options_.begin();
|
for (OptionCollection::iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -239,7 +239,7 @@ public:
|
|||||||
uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
|
uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
|
||||||
length += values_.size() * sizeof(T);
|
length += values_.size() * sizeof(T);
|
||||||
// length of all suboptions
|
// length of all suboptions
|
||||||
for (Option::OptionCollection::iterator it = options_.begin();
|
for (OptionCollection::iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
|
@@ -93,7 +93,7 @@ Pkt4::len() {
|
|||||||
size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
|
size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
|
||||||
|
|
||||||
// ... and sum of lengths of all options
|
// ... and sum of lengths of all options
|
||||||
for (Option::OptionCollection::const_iterator it = options_.begin();
|
for (OptionCollection::const_iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
@@ -209,11 +209,15 @@ Pkt4::unpack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
|
size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
|
||||||
vector<uint8_t> optsBuffer;
|
vector<uint8_t> opts_buffer;
|
||||||
|
|
||||||
// First use of readVector.
|
// First use of readVector.
|
||||||
bufferIn.readVector(optsBuffer, opts_len);
|
bufferIn.readVector(opts_buffer, opts_len);
|
||||||
LibDHCP::unpackOptions4(optsBuffer, options_);
|
if (callback_.empty()) {
|
||||||
|
LibDHCP::unpackOptions4(opts_buffer, options_);
|
||||||
|
} else {
|
||||||
|
callback_(opts_buffer, "dhcp4", options_, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// @todo check will need to be called separately, so hooks can be called
|
// @todo check will need to be called separately, so hooks can be called
|
||||||
// after the packet is parsed, but before its content is verified
|
// after the packet is parsed, but before its content is verified
|
||||||
@@ -270,7 +274,7 @@ Pkt4::toText() {
|
|||||||
<< ":" << remote_port_ << ", msgtype=" << static_cast<int>(getType())
|
<< ":" << remote_port_ << ", msgtype=" << static_cast<int>(getType())
|
||||||
<< ", transid=0x" << hex << transid_ << dec << endl;
|
<< ", transid=0x" << hex << transid_ << dec << endl;
|
||||||
|
|
||||||
for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
|
for (isc::dhcp::OptionCollection::iterator opt=options_.begin();
|
||||||
opt != options_.end();
|
opt != options_.end();
|
||||||
++opt) {
|
++opt) {
|
||||||
tmp << " " << opt->second->toText() << std::endl;
|
tmp << " " << opt->second->toText() << std::endl;
|
||||||
@@ -428,7 +432,7 @@ Pkt4::addOption(boost::shared_ptr<Option> opt) {
|
|||||||
|
|
||||||
boost::shared_ptr<isc::dhcp::Option>
|
boost::shared_ptr<isc::dhcp::Option>
|
||||||
Pkt4::getOption(uint8_t type) const {
|
Pkt4::getOption(uint8_t type) const {
|
||||||
Option::OptionCollection::const_iterator x = options_.find(type);
|
OptionCollection::const_iterator x = options_.find(type);
|
||||||
if (x != options_.end()) {
|
if (x != options_.end()) {
|
||||||
return (*x).second;
|
return (*x).second;
|
||||||
}
|
}
|
||||||
@@ -437,7 +441,7 @@ Pkt4::getOption(uint8_t type) const {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
Pkt4::delOption(uint8_t type) {
|
Pkt4::delOption(uint8_t type) {
|
||||||
isc::dhcp::Option::OptionCollection::iterator x = options_.find(type);
|
isc::dhcp::OptionCollection::iterator x = options_.find(type);
|
||||||
if (x != options_.end()) {
|
if (x != options_.end()) {
|
||||||
options_.erase(x);
|
options_.erase(x);
|
||||||
return (true); // delete successful
|
return (true); // delete successful
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#define PKT4_H
|
#define PKT4_H
|
||||||
|
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
|
#include <dhcp/option.h>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
#include <dhcp/option.h>
|
#include <dhcp/option.h>
|
||||||
#include <dhcp/hwaddr.h>
|
#include <dhcp/hwaddr.h>
|
||||||
@@ -482,6 +483,14 @@ public:
|
|||||||
/// @return remote port
|
/// @return remote port
|
||||||
uint16_t getRemotePort() const { return (remote_port_); }
|
uint16_t getRemotePort() const { return (remote_port_); }
|
||||||
|
|
||||||
|
/// @brief Set callback function to be used to parse options.
|
||||||
|
///
|
||||||
|
/// @param callback An instance of the callback function or NULL to
|
||||||
|
/// uninstall callback.
|
||||||
|
void setCallback(UnpackOptionsCallback callback) {
|
||||||
|
callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Update packet timestamp.
|
/// @brief Update packet timestamp.
|
||||||
///
|
///
|
||||||
/// Updates packet timestamp. This method is invoked
|
/// Updates packet timestamp. This method is invoked
|
||||||
@@ -632,11 +641,14 @@ protected:
|
|||||||
/// behavior must be taken into consideration before making
|
/// behavior must be taken into consideration before making
|
||||||
/// changes to this member such as access scope restriction or
|
/// changes to this member such as access scope restriction or
|
||||||
/// data format change etc.
|
/// data format change etc.
|
||||||
isc::dhcp::Option::OptionCollection options_;
|
isc::dhcp::OptionCollection options_;
|
||||||
|
|
||||||
/// packet timestamp
|
/// packet timestamp
|
||||||
boost::posix_time::ptime timestamp_;
|
boost::posix_time::ptime timestamp_;
|
||||||
|
|
||||||
|
/// A callback to be called to unpack options from the packet.
|
||||||
|
UnpackOptionsCallback callback_;
|
||||||
|
|
||||||
}; // Pkt4 class
|
}; // Pkt4 class
|
||||||
|
|
||||||
typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
|
typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <dhcp/dhcp6.h>
|
#include <dhcp/dhcp6.h>
|
||||||
#include <dhcp/libdhcp++.h>
|
#include <dhcp/libdhcp++.h>
|
||||||
|
#include <dhcp/option.h>
|
||||||
#include <dhcp/pkt6.h>
|
#include <dhcp/pkt6.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ OptionPtr Pkt6::getRelayOption(uint16_t opt_type, uint8_t relay_level) {
|
|||||||
<< " There is no info about " << relay_level + 1 << " relay.");
|
<< " There is no info about " << relay_level + 1 << " relay.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Option::OptionCollection::iterator it = relay_info_[relay_level].options_.begin();
|
for (OptionCollection::iterator it = relay_info_[relay_level].options_.begin();
|
||||||
it != relay_info_[relay_level].options_.end(); ++it) {
|
it != relay_info_[relay_level].options_.end(); ++it) {
|
||||||
if ((*it).second->getType() == opt_type) {
|
if ((*it).second->getType() == opt_type) {
|
||||||
return (it->second);
|
return (it->second);
|
||||||
@@ -148,7 +149,7 @@ uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) const {
|
|||||||
uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
|
uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
|
||||||
+ Option::OPTION6_HDR_LEN; // header of the relay-msg option
|
+ Option::OPTION6_HDR_LEN; // header of the relay-msg option
|
||||||
|
|
||||||
for (Option::OptionCollection::const_iterator opt = relay.options_.begin();
|
for (OptionCollection::const_iterator opt = relay.options_.begin();
|
||||||
opt != relay.options_.end(); ++opt) {
|
opt != relay.options_.end(); ++opt) {
|
||||||
len += (opt->second)->len();
|
len += (opt->second)->len();
|
||||||
}
|
}
|
||||||
@@ -171,7 +172,7 @@ uint16_t Pkt6::calculateRelaySizes() {
|
|||||||
uint16_t Pkt6::directLen() const {
|
uint16_t Pkt6::directLen() const {
|
||||||
uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
|
uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
|
||||||
|
|
||||||
for (Option::OptionCollection::const_iterator it = options_.begin();
|
for (OptionCollection::const_iterator it = options_.begin();
|
||||||
it != options_.end();
|
it != options_.end();
|
||||||
++it) {
|
++it) {
|
||||||
length += (*it).second->len();
|
length += (*it).second->len();
|
||||||
@@ -226,7 +227,7 @@ Pkt6::packUDP() {
|
|||||||
// present here as well (vendor-opts for Cable modems,
|
// present here as well (vendor-opts for Cable modems,
|
||||||
// subscriber-id, remote-id, options echoed back from Echo
|
// subscriber-id, remote-id, options echoed back from Echo
|
||||||
// Request Option, etc.)
|
// Request Option, etc.)
|
||||||
for (Option::OptionCollection::const_iterator opt =
|
for (OptionCollection::const_iterator opt =
|
||||||
relay->options_.begin();
|
relay->options_.begin();
|
||||||
opt != relay->options_.end(); ++opt) {
|
opt != relay->options_.end(); ++opt) {
|
||||||
(opt->second)->pack(bufferOut_);
|
(opt->second)->pack(bufferOut_);
|
||||||
@@ -324,7 +325,13 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
|
|||||||
try {
|
try {
|
||||||
OptionBuffer opt_buffer(begin, end);
|
OptionBuffer opt_buffer(begin, end);
|
||||||
|
|
||||||
|
// 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, options_);
|
||||||
|
} else {
|
||||||
|
callback_(opt_buffer, "dhcp6", options_, 0, 0);
|
||||||
|
}
|
||||||
} catch (const Exception& e) {
|
} catch (const Exception& e) {
|
||||||
// @todo: throw exception here once we turn this function to void.
|
// @todo: throw exception here once we turn this function to void.
|
||||||
return (false);
|
return (false);
|
||||||
@@ -361,8 +368,16 @@ Pkt6::unpackRelayMsg() {
|
|||||||
try {
|
try {
|
||||||
// parse the rest as options
|
// parse the rest as options
|
||||||
OptionBuffer opt_buffer(&data_[offset], &data_[offset+bufsize]);
|
OptionBuffer opt_buffer(&data_[offset], &data_[offset+bufsize]);
|
||||||
LibDHCP::unpackOptions6(opt_buffer, relay.options_, &relay_msg_offset,
|
|
||||||
&relay_msg_len);
|
// 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_,
|
||||||
|
&relay_msg_offset, &relay_msg_len);
|
||||||
|
} else {
|
||||||
|
callback_(opt_buffer, "dhcp6", relay.options_,
|
||||||
|
&relay_msg_offset, &relay_msg_len);
|
||||||
|
}
|
||||||
|
|
||||||
/// @todo: check that each option appears at most once
|
/// @todo: check that each option appears at most once
|
||||||
//relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
|
//relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
|
||||||
@@ -438,7 +453,7 @@ Pkt6::toText() {
|
|||||||
<< "]:" << remote_port_ << endl;
|
<< "]:" << remote_port_ << endl;
|
||||||
tmp << "msgtype=" << static_cast<int>(msg_type_) << ", transid=0x" <<
|
tmp << "msgtype=" << static_cast<int>(msg_type_) << ", transid=0x" <<
|
||||||
hex << transid_ << dec << endl;
|
hex << transid_ << dec << endl;
|
||||||
for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
|
for (isc::dhcp::OptionCollection::iterator opt=options_.begin();
|
||||||
opt != options_.end();
|
opt != options_.end();
|
||||||
++opt) {
|
++opt) {
|
||||||
tmp << opt->second->toText() << std::endl;
|
tmp << opt->second->toText() << std::endl;
|
||||||
@@ -448,18 +463,18 @@ Pkt6::toText() {
|
|||||||
|
|
||||||
OptionPtr
|
OptionPtr
|
||||||
Pkt6::getOption(uint16_t opt_type) {
|
Pkt6::getOption(uint16_t opt_type) {
|
||||||
isc::dhcp::Option::OptionCollection::const_iterator x = options_.find(opt_type);
|
isc::dhcp::OptionCollection::const_iterator x = options_.find(opt_type);
|
||||||
if (x!=options_.end()) {
|
if (x!=options_.end()) {
|
||||||
return (*x).second;
|
return (*x).second;
|
||||||
}
|
}
|
||||||
return OptionPtr(); // NULL
|
return OptionPtr(); // NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
isc::dhcp::Option::OptionCollection
|
isc::dhcp::OptionCollection
|
||||||
Pkt6::getOptions(uint16_t opt_type) {
|
Pkt6::getOptions(uint16_t opt_type) {
|
||||||
isc::dhcp::Option::OptionCollection found;
|
isc::dhcp::OptionCollection found;
|
||||||
|
|
||||||
for (Option::OptionCollection::const_iterator x = options_.begin();
|
for (OptionCollection::const_iterator x = options_.begin();
|
||||||
x != options_.end(); ++x) {
|
x != options_.end(); ++x) {
|
||||||
if (x->first == opt_type) {
|
if (x->first == opt_type) {
|
||||||
found.insert(make_pair(opt_type, x->second));
|
found.insert(make_pair(opt_type, x->second));
|
||||||
@@ -475,7 +490,7 @@ Pkt6::addOption(const OptionPtr& opt) {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
Pkt6::delOption(uint16_t type) {
|
Pkt6::delOption(uint16_t type) {
|
||||||
isc::dhcp::Option::OptionCollection::iterator x = options_.find(type);
|
isc::dhcp::OptionCollection::iterator x = options_.find(type);
|
||||||
if (x!=options_.end()) {
|
if (x!=options_.end()) {
|
||||||
options_.erase(x);
|
options_.erase(x);
|
||||||
return (true); // delete successful
|
return (true); // delete successful
|
||||||
|
@@ -88,7 +88,7 @@ public:
|
|||||||
uint16_t relay_msg_len_;
|
uint16_t relay_msg_len_;
|
||||||
|
|
||||||
/// options received from a specified relay, except relay-msg option
|
/// options received from a specified relay, except relay-msg option
|
||||||
isc::dhcp::Option::OptionCollection options_;
|
isc::dhcp::OptionCollection options_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Constructor, used in replying to a message
|
/// Constructor, used in replying to a message
|
||||||
@@ -242,7 +242,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// @param type option type we are looking for
|
/// @param type option type we are looking for
|
||||||
/// @return instance of option collection with requested options
|
/// @return instance of option collection with requested options
|
||||||
isc::dhcp::Option::OptionCollection getOptions(uint16_t type);
|
isc::dhcp::OptionCollection getOptions(uint16_t type);
|
||||||
|
|
||||||
/// Attempts to delete first suboption of requested type
|
/// Attempts to delete first suboption of requested type
|
||||||
///
|
///
|
||||||
@@ -350,7 +350,7 @@ public:
|
|||||||
/// behavior must be taken into consideration before making
|
/// behavior must be taken into consideration before making
|
||||||
/// changes to this member such as access scope restriction or
|
/// changes to this member such as access scope restriction or
|
||||||
/// data format change etc.
|
/// data format change etc.
|
||||||
isc::dhcp::Option::OptionCollection options_;
|
isc::dhcp::OptionCollection options_;
|
||||||
|
|
||||||
/// @brief Update packet timestamp.
|
/// @brief Update packet timestamp.
|
||||||
///
|
///
|
||||||
@@ -388,6 +388,14 @@ public:
|
|||||||
/// be freed by the caller.
|
/// be freed by the caller.
|
||||||
const char* getName() const;
|
const char* getName() const;
|
||||||
|
|
||||||
|
/// @brief Set callback function to be used to parse options.
|
||||||
|
///
|
||||||
|
/// @param callback An instance of the callback function or NULL to
|
||||||
|
/// uninstall callback.
|
||||||
|
void setCallback(UnpackOptionsCallback callback) {
|
||||||
|
callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief copies relay information from client's packet to server's response
|
/// @brief copies relay information from client's packet to server's response
|
||||||
///
|
///
|
||||||
/// This information is not simply copied over. Some parameter are
|
/// This information is not simply copied over. Some parameter are
|
||||||
@@ -534,6 +542,10 @@ protected:
|
|||||||
|
|
||||||
/// packet timestamp
|
/// packet timestamp
|
||||||
boost::posix_time::ptime timestamp_;
|
boost::posix_time::ptime timestamp_;
|
||||||
|
|
||||||
|
/// A callback to be called to unpack options from the packet.
|
||||||
|
UnpackOptionsCallback callback_;
|
||||||
|
|
||||||
}; // Pkt6 class
|
}; // Pkt6 class
|
||||||
|
|
||||||
} // isc::dhcp namespace
|
} // isc::dhcp namespace
|
||||||
|
@@ -254,7 +254,7 @@ TEST_F(LibDhcpTest, optionFactory) {
|
|||||||
|
|
||||||
TEST_F(LibDhcpTest, packOptions6) {
|
TEST_F(LibDhcpTest, packOptions6) {
|
||||||
OptionBuffer buf(512);
|
OptionBuffer buf(512);
|
||||||
isc::dhcp::Option::OptionCollection opts; // list of options
|
isc::dhcp::OptionCollection opts; // list of options
|
||||||
|
|
||||||
// generate content for options
|
// generate content for options
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
@@ -286,7 +286,7 @@ TEST_F(LibDhcpTest, unpackOptions6) {
|
|||||||
// Option is used as a simple option implementation
|
// Option is used as a simple option implementation
|
||||||
// More advanced uses are validated in tests dedicated for
|
// More advanced uses are validated in tests dedicated for
|
||||||
// specific derived classes.
|
// specific derived classes.
|
||||||
isc::dhcp::Option::OptionCollection options; // list of options
|
isc::dhcp::OptionCollection options; // list of options
|
||||||
|
|
||||||
OptionBuffer buf(512);
|
OptionBuffer buf(512);
|
||||||
memcpy(&buf[0], v6packed, sizeof(v6packed));
|
memcpy(&buf[0], v6packed, sizeof(v6packed));
|
||||||
@@ -298,7 +298,7 @@ TEST_F(LibDhcpTest, unpackOptions6) {
|
|||||||
|
|
||||||
EXPECT_EQ(options.size(), 5); // there should be 5 options
|
EXPECT_EQ(options.size(), 5); // there should be 5 options
|
||||||
|
|
||||||
isc::dhcp::Option::OptionCollection::const_iterator x = options.find(1);
|
isc::dhcp::OptionCollection::const_iterator x = options.find(1);
|
||||||
ASSERT_FALSE(x == options.end()); // option 1 should exist
|
ASSERT_FALSE(x == options.end()); // option 1 should exist
|
||||||
EXPECT_EQ(1, x->second->getType()); // this should be option 1
|
EXPECT_EQ(1, x->second->getType()); // this should be option 1
|
||||||
ASSERT_EQ(9, x->second->len()); // it should be of length 9
|
ASSERT_EQ(9, x->second->len()); // it should be of length 9
|
||||||
@@ -399,7 +399,7 @@ TEST_F(LibDhcpTest, packOptions4) {
|
|||||||
OptionPtr opt4(new Option(Option::V4,254, payload[3]));
|
OptionPtr opt4(new Option(Option::V4,254, payload[3]));
|
||||||
OptionPtr opt5(new Option(Option::V4,128, payload[4]));
|
OptionPtr opt5(new Option(Option::V4,128, payload[4]));
|
||||||
|
|
||||||
isc::dhcp::Option::OptionCollection opts; // list of options
|
isc::dhcp::OptionCollection opts; // list of options
|
||||||
opts.insert(make_pair(opt1->getType(), opt1));
|
opts.insert(make_pair(opt1->getType(), opt1));
|
||||||
opts.insert(make_pair(opt1->getType(), opt2));
|
opts.insert(make_pair(opt1->getType(), opt2));
|
||||||
opts.insert(make_pair(opt1->getType(), opt3));
|
opts.insert(make_pair(opt1->getType(), opt3));
|
||||||
@@ -418,13 +418,13 @@ TEST_F(LibDhcpTest, packOptions4) {
|
|||||||
TEST_F(LibDhcpTest, unpackOptions4) {
|
TEST_F(LibDhcpTest, unpackOptions4) {
|
||||||
|
|
||||||
vector<uint8_t> v4packed(v4_opts, v4_opts + sizeof(v4_opts));
|
vector<uint8_t> v4packed(v4_opts, v4_opts + sizeof(v4_opts));
|
||||||
isc::dhcp::Option::OptionCollection options; // list of options
|
isc::dhcp::OptionCollection options; // list of options
|
||||||
|
|
||||||
ASSERT_NO_THROW(
|
ASSERT_NO_THROW(
|
||||||
LibDHCP::unpackOptions4(v4packed, options);
|
LibDHCP::unpackOptions4(v4packed, options);
|
||||||
);
|
);
|
||||||
|
|
||||||
isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
|
isc::dhcp::OptionCollection::const_iterator x = options.find(12);
|
||||||
ASSERT_FALSE(x == options.end()); // option 1 should exist
|
ASSERT_FALSE(x == options.end()); // option 1 should exist
|
||||||
// Option 12 holds a string so let's cast it to an appropriate type.
|
// Option 12 holds a string so let's cast it to an appropriate type.
|
||||||
OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x->second);
|
OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x->second);
|
||||||
|
@@ -951,8 +951,8 @@ TEST_F(OptionDefinitionTest, integerInvalidType) {
|
|||||||
// see if it rejects it.
|
// see if it rejects it.
|
||||||
OptionBuffer buf(1);
|
OptionBuffer buf(1);
|
||||||
EXPECT_THROW(
|
EXPECT_THROW(
|
||||||
OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE,
|
OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, "dhcp6",
|
||||||
buf.begin(), buf.end()),
|
buf.begin(), buf.end(), NULL),
|
||||||
isc::dhcp::InvalidDataType
|
isc::dhcp::InvalidDataType
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -15,10 +15,12 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <dhcp/dhcp6.h>
|
#include <dhcp/dhcp6.h>
|
||||||
|
#include <dhcp/libdhcp++.h>
|
||||||
#include <dhcp/option.h>
|
#include <dhcp/option.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
@@ -35,6 +37,66 @@ using namespace isc::util;
|
|||||||
using boost::scoped_ptr;
|
using boost::scoped_ptr;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/// @brief A class which contains a custom callback function to unpack options.
|
||||||
|
///
|
||||||
|
/// This is a class used by the tests which verify that the custom callback
|
||||||
|
/// functions can be installed to unpack options from a message. When the
|
||||||
|
/// callback function is called, the executed_ member is set to true to allow
|
||||||
|
/// verification that the callback was really called. Internally, this class
|
||||||
|
/// uses libdhcp++ to unpack options so the options parsing algorithm remains
|
||||||
|
/// unchanged after installation of the callback.
|
||||||
|
class CustomUnpackCallback {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// Marks that callback hasn't been called.
|
||||||
|
CustomUnpackCallback()
|
||||||
|
: executed_(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief A callback
|
||||||
|
///
|
||||||
|
/// Contains custom implementation of the callback.
|
||||||
|
///
|
||||||
|
/// @param buf a A buffer holding options in on-wire format.
|
||||||
|
/// @param [out] options A reference to the collection where parsed options
|
||||||
|
/// will be stored.
|
||||||
|
/// @param relay_msg_offset Reference to a size_t structure. If specified,
|
||||||
|
/// offset to beginning of relay_msg option will be stored in it.
|
||||||
|
/// @param relay_msg_len reference to a size_t structure. If specified,
|
||||||
|
/// length of the relay_msg option will be stored in it.
|
||||||
|
/// @return An offset to the first byte after last parsed option.
|
||||||
|
size_t execute(const OptionBuffer& buf,
|
||||||
|
const std::string&,
|
||||||
|
isc::dhcp::OptionCollection& options,
|
||||||
|
size_t* relay_msg_offset,
|
||||||
|
size_t* relay_msg_len) {
|
||||||
|
// Set the executed_ member to true to allow verification that the
|
||||||
|
// callback has been actually called.
|
||||||
|
executed_ = true;
|
||||||
|
// Use default implementation of the unpack algorithm to parse options.
|
||||||
|
return (LibDHCP::unpackOptions6(buf, options, relay_msg_offset,
|
||||||
|
relay_msg_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A flag which indicates if callback function has been called.
|
||||||
|
bool executed_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief A class which derives from option and exposes protected members.
|
||||||
|
class NakedOption : public Option {
|
||||||
|
public:
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// Sets the universe and option type to arbitrary test values.
|
||||||
|
NakedOption() : Option(Option::V6, 258) {
|
||||||
|
}
|
||||||
|
|
||||||
|
using Option::unpackOptions;
|
||||||
|
};
|
||||||
|
|
||||||
class OptionTest : public ::testing::Test {
|
class OptionTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
OptionTest(): buf_(255), outBuf_(255) {
|
OptionTest(): buf_(255), outBuf_(255) {
|
||||||
@@ -505,4 +567,68 @@ TEST_F(OptionTest, equal) {
|
|||||||
|
|
||||||
EXPECT_TRUE(opt2->equal(opt5));
|
EXPECT_TRUE(opt2->equal(opt5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that the name of the option space being encapsulated by
|
||||||
|
// the particular option can be set.
|
||||||
|
TEST_F(OptionTest, setEncapsulatedSpace) {
|
||||||
|
Option optv6(Option::V6, 258);
|
||||||
|
EXPECT_TRUE(optv6.getEncapsulatedSpace().empty());
|
||||||
|
|
||||||
|
optv6.setEncapsulatedSpace("dhcp6");
|
||||||
|
EXPECT_EQ("dhcp6", optv6.getEncapsulatedSpace());
|
||||||
|
|
||||||
|
Option optv4(Option::V4, 125);
|
||||||
|
EXPECT_TRUE(optv4.getEncapsulatedSpace().empty());
|
||||||
|
|
||||||
|
optv4.setEncapsulatedSpace("dhcp4");
|
||||||
|
EXPECT_EQ("dhcp4", optv4.getEncapsulatedSpace());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that it is possible to specify custom implementation of
|
||||||
|
// the option parsing algorithm by installing a callback function.
|
||||||
|
TEST_F(OptionTest, unpackCallback) {
|
||||||
|
// Create a buffer which holds two sub options.
|
||||||
|
const char opt_data[] = {
|
||||||
|
0x00, 0x01, // sub option code = 1
|
||||||
|
0x00, 0x02, // sub option length = 1
|
||||||
|
0x00, 0x01, // sub option data
|
||||||
|
0x00, 0x02, // sub option code = 2
|
||||||
|
0x00, 0x02, // sub option length
|
||||||
|
0x00, 0x01 // sub option data
|
||||||
|
};
|
||||||
|
OptionBuffer opt_buf(opt_data, opt_data + sizeof(opt_data));
|
||||||
|
|
||||||
|
// Make sure that the flag which indicates if the callback function has
|
||||||
|
// been called is not set. Otherwise, our test doesn't make sense.
|
||||||
|
CustomUnpackCallback cb;
|
||||||
|
ASSERT_FALSE(cb.executed_);
|
||||||
|
// Create an option and install a callback.
|
||||||
|
NakedOption option;
|
||||||
|
option.setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
|
||||||
|
_1, _2, _3, _4, _5));
|
||||||
|
// Parse options. It should result in a call to our callback function.
|
||||||
|
// This function uses LibDHCP to parse options so they should be parsed
|
||||||
|
// correctly.
|
||||||
|
ASSERT_NO_THROW(option.unpackOptions(opt_buf));
|
||||||
|
EXPECT_TRUE(option.getOption(1));
|
||||||
|
EXPECT_TRUE(option.getOption(2));
|
||||||
|
EXPECT_FALSE(option.getOption(3));
|
||||||
|
// The callback should have been registered.
|
||||||
|
EXPECT_TRUE(cb.executed_);
|
||||||
|
// Reset the flag because now we are going to uninstall the callback and
|
||||||
|
// verify that it was NOT called.
|
||||||
|
cb.executed_ = false;
|
||||||
|
// Uninstall the callback.
|
||||||
|
option.setCallback(NULL);
|
||||||
|
ASSERT_NO_THROW(option.unpackOptions(opt_buf));
|
||||||
|
// Options should still get unpacked...
|
||||||
|
EXPECT_TRUE(option.getOption(1));
|
||||||
|
EXPECT_TRUE(option.getOption(2));
|
||||||
|
EXPECT_FALSE(option.getOption(3));
|
||||||
|
// ... but not via callback.
|
||||||
|
EXPECT_FALSE(cb.executed_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
#include <dhcp/dhcp4.h>
|
#include <dhcp/dhcp4.h>
|
||||||
|
#include <dhcp/libdhcp++.h>
|
||||||
#include <dhcp/option_string.h>
|
#include <dhcp/option_string.h>
|
||||||
#include <dhcp/pkt4.h>
|
#include <dhcp/pkt4.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
@@ -42,41 +43,59 @@ using boost::scoped_ptr;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
TEST(Pkt4Test, constructor) {
|
/// @brief A class which contains a custom callback function to unpack options.
|
||||||
|
///
|
||||||
|
/// This is a class used by the tests which verify that the custom callback
|
||||||
|
/// functions can be installed to unpack options from a message. When the
|
||||||
|
/// callback function is called, the executed_ member is set to true to allow
|
||||||
|
/// verification that the callback was really called. Internally, this class
|
||||||
|
/// uses libdhcp++ to unpack options so the options parsing algorithm remains
|
||||||
|
/// unchanged after installation of the callback.
|
||||||
|
class CustomUnpackCallback {
|
||||||
|
public:
|
||||||
|
|
||||||
ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
|
/// @brief Constructor
|
||||||
scoped_ptr<Pkt4> pkt;
|
///
|
||||||
|
/// Marks that callback hasn't been called.
|
||||||
// Just some dummy payload.
|
CustomUnpackCallback()
|
||||||
uint8_t testData[250];
|
: executed_(false) {
|
||||||
for (int i = 0; i < 250; i++) {
|
|
||||||
testData[i] = i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Positive case1. Normal received packet.
|
/// @brief A callback
|
||||||
EXPECT_NO_THROW(pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN)));
|
///
|
||||||
|
/// Contains custom implementation of the callback.
|
||||||
EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
|
///
|
||||||
|
/// @param buf a A buffer holding options in on-wire format.
|
||||||
EXPECT_NO_THROW(pkt.reset());
|
/// @param [out] options A reference to the collection where parsed options
|
||||||
|
/// will be stored.
|
||||||
// Positive case2. Normal outgoing packet.
|
/// @return An offset to the first byte after last parsed option.
|
||||||
EXPECT_NO_THROW(pkt.reset(new Pkt4(DHCPDISCOVER, 0xffffffff)));
|
size_t execute(const OptionBuffer& buf,
|
||||||
|
const std::string&,
|
||||||
// DHCPv4 packet must be at least 236 bytes long, with Message Type
|
isc::dhcp::OptionCollection& options) {
|
||||||
// Option taking extra 3 bytes it is 239
|
// Set the executed_ member to true to allow verification that the
|
||||||
EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + 3, pkt->len());
|
// callback has been actually called.
|
||||||
EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
executed_ = true;
|
||||||
EXPECT_EQ(0xffffffff, pkt->getTransid());
|
// Use default implementation of the unpack algorithm to parse options.
|
||||||
EXPECT_NO_THROW(pkt.reset());
|
return (LibDHCP::unpackOptions4(buf, options));
|
||||||
|
|
||||||
// Negative case. Should drop truncated messages.
|
|
||||||
EXPECT_THROW(
|
|
||||||
pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN - 1)),
|
|
||||||
OutOfRange
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A flag which indicates if callback function has been called.
|
||||||
|
bool executed_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// V4 Options being used for pack/unpack testing.
|
||||||
|
/// For test simplicity, all selected options have
|
||||||
|
/// variable length data so as there are no restrictions
|
||||||
|
/// on a length of their data.
|
||||||
|
static uint8_t v4_opts[] = {
|
||||||
|
12, 3, 0, 1, 2, // Hostname
|
||||||
|
14, 3, 10, 11, 12, // Merit Dump File
|
||||||
|
53, 1, 2, // Message Type (required to not throw exception during unpack)
|
||||||
|
60, 3, 20, 21, 22, // Class Id
|
||||||
|
128, 3, 30, 31, 32, // Vendor specific
|
||||||
|
254, 3, 40, 41, 42, // Reserved
|
||||||
|
};
|
||||||
|
|
||||||
// Sample data
|
// Sample data
|
||||||
const uint8_t dummyOp = BOOTREQUEST;
|
const uint8_t dummyOp = BOOTREQUEST;
|
||||||
const uint8_t dummyHtype = 6;
|
const uint8_t dummyHtype = 6;
|
||||||
@@ -110,6 +129,12 @@ const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
|
|||||||
BOOST_STATIC_ASSERT(sizeof(dummyFile) == Pkt4::MAX_FILE_LEN + 1);
|
BOOST_STATIC_ASSERT(sizeof(dummyFile) == Pkt4::MAX_FILE_LEN + 1);
|
||||||
BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);
|
BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);
|
||||||
|
|
||||||
|
|
||||||
|
class Pkt4Test : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
Pkt4Test() {
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Generates test packet.
|
/// @brief Generates test packet.
|
||||||
///
|
///
|
||||||
/// Allocates and generates test packet, with all fixed fields set to non-zero
|
/// Allocates and generates test packet, with all fixed fields set to non-zero
|
||||||
@@ -119,8 +144,7 @@ BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);
|
|||||||
/// on-wire format.
|
/// on-wire format.
|
||||||
///
|
///
|
||||||
/// @return pointer to allocated Pkt4 object.
|
/// @return pointer to allocated Pkt4 object.
|
||||||
boost::shared_ptr<Pkt4>
|
Pkt4Ptr generateTestPacket1() {
|
||||||
generateTestPacket1() {
|
|
||||||
|
|
||||||
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
|
boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
|
||||||
|
|
||||||
@@ -154,8 +178,7 @@ generateTestPacket1() {
|
|||||||
///
|
///
|
||||||
/// @return pointer to allocated Pkt4 object
|
/// @return pointer to allocated Pkt4 object
|
||||||
// Returns a vector containing a DHCPv4 packet header.
|
// Returns a vector containing a DHCPv4 packet header.
|
||||||
vector<uint8_t>
|
vector<uint8_t> generateTestPacket2() {
|
||||||
generateTestPacket2() {
|
|
||||||
|
|
||||||
// That is only part of the header. It contains all "short" fields,
|
// That is only part of the header. It contains all "short" fields,
|
||||||
// larger fields are constructed separately.
|
// larger fields are constructed separately.
|
||||||
@@ -185,7 +208,100 @@ generateTestPacket2() {
|
|||||||
return (buf);
|
return (buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, fixedFields) {
|
/// @brief Verify that the options are correct after parsing.
|
||||||
|
///
|
||||||
|
/// @param pkt A packet holding parsed options.
|
||||||
|
void verifyParsedOptions(const Pkt4Ptr& pkt) {
|
||||||
|
EXPECT_TRUE(pkt->getOption(12));
|
||||||
|
EXPECT_TRUE(pkt->getOption(60));
|
||||||
|
EXPECT_TRUE(pkt->getOption(14));
|
||||||
|
EXPECT_TRUE(pkt->getOption(128));
|
||||||
|
EXPECT_TRUE(pkt->getOption(254));
|
||||||
|
|
||||||
|
boost::shared_ptr<Option> x = pkt->getOption(12);
|
||||||
|
ASSERT_TRUE(x); // option 1 should exist
|
||||||
|
// Option 12 is represented by the OptionString class so let's do
|
||||||
|
// the appropriate conversion.
|
||||||
|
OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x);
|
||||||
|
ASSERT_TRUE(option12);
|
||||||
|
EXPECT_EQ(12, option12->getType()); // this should be option 12
|
||||||
|
ASSERT_EQ(3, option12->getValue().length()); // it should be of length 3
|
||||||
|
EXPECT_EQ(5, option12->len()); // total option length 5
|
||||||
|
EXPECT_EQ(0, memcmp(&option12->getValue()[0], v4_opts + 2, 3)); // data len=3
|
||||||
|
|
||||||
|
x = pkt->getOption(14);
|
||||||
|
ASSERT_TRUE(x); // option 14 should exist
|
||||||
|
// Option 14 is represented by the OptionString class so let's do
|
||||||
|
// the appropriate conversion.
|
||||||
|
OptionStringPtr option14 = boost::static_pointer_cast<OptionString>(x);
|
||||||
|
ASSERT_TRUE(option14);
|
||||||
|
EXPECT_EQ(14, option14->getType()); // this should be option 14
|
||||||
|
ASSERT_EQ(3, option14->getValue().length()); // it should be of length 3
|
||||||
|
EXPECT_EQ(5, option14->len()); // total option length 5
|
||||||
|
EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4_opts + 7, 3)); // data len=3
|
||||||
|
|
||||||
|
x = pkt->getOption(60);
|
||||||
|
ASSERT_TRUE(x); // option 60 should exist
|
||||||
|
EXPECT_EQ(60, x->getType()); // this should be option 60
|
||||||
|
ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
||||||
|
EXPECT_EQ(5, x->len()); // total option length 5
|
||||||
|
EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 15, 3)); // data len=3
|
||||||
|
|
||||||
|
x = pkt->getOption(128);
|
||||||
|
ASSERT_TRUE(x); // option 3 should exist
|
||||||
|
EXPECT_EQ(128, x->getType()); // this should be option 254
|
||||||
|
ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
||||||
|
EXPECT_EQ(5, x->len()); // total option length 5
|
||||||
|
EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 20, 3)); // data len=3
|
||||||
|
|
||||||
|
x = pkt->getOption(254);
|
||||||
|
ASSERT_TRUE(x); // option 3 should exist
|
||||||
|
EXPECT_EQ(254, x->getType()); // this should be option 254
|
||||||
|
ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
||||||
|
EXPECT_EQ(5, x->len()); // total option length 5
|
||||||
|
EXPECT_EQ(0, memcmp(&x->getData()[0], v4_opts + 25, 3)); // data len=3
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(Pkt4Test, constructor) {
|
||||||
|
|
||||||
|
ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
|
||||||
|
scoped_ptr<Pkt4> pkt;
|
||||||
|
|
||||||
|
// Just some dummy payload.
|
||||||
|
uint8_t testData[250];
|
||||||
|
for (int i = 0; i < 250; i++) {
|
||||||
|
testData[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Positive case1. Normal received packet.
|
||||||
|
EXPECT_NO_THROW(pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN)));
|
||||||
|
|
||||||
|
EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(pkt.reset());
|
||||||
|
|
||||||
|
// Positive case2. Normal outgoing packet.
|
||||||
|
EXPECT_NO_THROW(pkt.reset(new Pkt4(DHCPDISCOVER, 0xffffffff)));
|
||||||
|
|
||||||
|
// DHCPv4 packet must be at least 236 bytes long, with Message Type
|
||||||
|
// Option taking extra 3 bytes it is 239
|
||||||
|
EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + 3, pkt->len());
|
||||||
|
EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
||||||
|
EXPECT_EQ(0xffffffff, pkt->getTransid());
|
||||||
|
EXPECT_NO_THROW(pkt.reset());
|
||||||
|
|
||||||
|
// Negative case. Should drop truncated messages.
|
||||||
|
EXPECT_THROW(
|
||||||
|
pkt.reset(new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN - 1)),
|
||||||
|
OutOfRange
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(Pkt4Test, fixedFields) {
|
||||||
|
|
||||||
boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
|
boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
|
||||||
|
|
||||||
@@ -215,7 +331,7 @@ TEST(Pkt4Test, fixedFields) {
|
|||||||
EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
EXPECT_EQ(DHCPDISCOVER, pkt->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, fixedFieldsPack) {
|
TEST_F(Pkt4Test, fixedFieldsPack) {
|
||||||
boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
|
boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
|
||||||
vector<uint8_t> expectedFormat = generateTestPacket2();
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
||||||
|
|
||||||
@@ -235,7 +351,7 @@ TEST(Pkt4Test, fixedFieldsPack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// TODO Uncomment when ticket #1226 is implemented
|
/// TODO Uncomment when ticket #1226 is implemented
|
||||||
TEST(Pkt4Test, fixedFieldsUnpack) {
|
TEST_F(Pkt4Test, fixedFieldsUnpack) {
|
||||||
vector<uint8_t> expectedFormat = generateTestPacket2();
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
||||||
|
|
||||||
expectedFormat.push_back(0x63); // magic cookie
|
expectedFormat.push_back(0x63); // magic cookie
|
||||||
@@ -282,7 +398,7 @@ TEST(Pkt4Test, fixedFieldsUnpack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This test is for hardware addresses (htype, hlen and chaddr fields)
|
// This test is for hardware addresses (htype, hlen and chaddr fields)
|
||||||
TEST(Pkt4Test, hwAddr) {
|
TEST_F(Pkt4Test, hwAddr) {
|
||||||
|
|
||||||
vector<uint8_t> mac;
|
vector<uint8_t> mac;
|
||||||
uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];
|
uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];
|
||||||
@@ -329,7 +445,7 @@ TEST(Pkt4Test, hwAddr) {
|
|||||||
/// longer than 16 bytes should be stored in client-identifier option
|
/// longer than 16 bytes should be stored in client-identifier option
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, msgTypes) {
|
TEST_F(Pkt4Test, msgTypes) {
|
||||||
|
|
||||||
struct msgType {
|
struct msgType {
|
||||||
uint8_t dhcp;
|
uint8_t dhcp;
|
||||||
@@ -366,7 +482,7 @@ TEST(Pkt4Test, msgTypes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This test verifies handling of sname field
|
// This test verifies handling of sname field
|
||||||
TEST(Pkt4Test, sname) {
|
TEST_F(Pkt4Test, sname) {
|
||||||
|
|
||||||
uint8_t sname[Pkt4::MAX_SNAME_LEN];
|
uint8_t sname[Pkt4::MAX_SNAME_LEN];
|
||||||
|
|
||||||
@@ -404,7 +520,7 @@ TEST(Pkt4Test, sname) {
|
|||||||
EXPECT_THROW(pkt4.setSname(NULL, 0), InvalidParameter);
|
EXPECT_THROW(pkt4.setSname(NULL, 0), InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, file) {
|
TEST_F(Pkt4Test, file) {
|
||||||
|
|
||||||
uint8_t file[Pkt4::MAX_FILE_LEN];
|
uint8_t file[Pkt4::MAX_FILE_LEN];
|
||||||
|
|
||||||
@@ -442,20 +558,7 @@ TEST(Pkt4Test, file) {
|
|||||||
EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
|
EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// V4 Options being used for pack/unpack testing.
|
TEST_F(Pkt4Test, options) {
|
||||||
/// For test simplicity, all selected options have
|
|
||||||
/// variable length data so as there are no restrictions
|
|
||||||
/// on a length of their data.
|
|
||||||
static uint8_t v4Opts[] = {
|
|
||||||
12, 3, 0, 1, 2, // Hostname
|
|
||||||
14, 3, 10, 11, 12, // Merit Dump File
|
|
||||||
53, 1, 2, // Message Type (required to not throw exception during unpack)
|
|
||||||
60, 3, 20, 21, 22, // Class Id
|
|
||||||
128, 3, 30, 31, 32, // Vendor specific
|
|
||||||
254, 3, 40, 41, 42, // Reserved
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(Pkt4Test, options) {
|
|
||||||
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 0));
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 0));
|
||||||
|
|
||||||
vector<uint8_t> payload[5];
|
vector<uint8_t> payload[5];
|
||||||
@@ -496,10 +599,10 @@ TEST(Pkt4Test, options) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const OutputBuffer& buf = pkt->getBuffer();
|
const OutputBuffer& buf = pkt->getBuffer();
|
||||||
// Check that all options are stored, they should take sizeof(v4Opts),
|
// Check that all options are stored, they should take sizeof(v4_opts),
|
||||||
// DHCP magic cookie (4 bytes), and OPTION_END added (just one byte)
|
// DHCP magic cookie (4 bytes), and OPTION_END added (just one byte)
|
||||||
ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) +
|
ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) +
|
||||||
sizeof(DHCP_OPTIONS_COOKIE) + sizeof(v4Opts) + 1,
|
sizeof(DHCP_OPTIONS_COOKIE) + sizeof(v4_opts) + 1,
|
||||||
buf.getLength());
|
buf.getLength());
|
||||||
|
|
||||||
// That that this extra data actually contain our options
|
// That that this extra data actually contain our options
|
||||||
@@ -508,8 +611,8 @@ TEST(Pkt4Test, options) {
|
|||||||
// Rewind to end of fixed part.
|
// Rewind to end of fixed part.
|
||||||
ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE);
|
ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE);
|
||||||
|
|
||||||
EXPECT_EQ(0, memcmp(ptr, v4Opts, sizeof(v4Opts)));
|
EXPECT_EQ(0, memcmp(ptr, v4_opts, sizeof(v4_opts)));
|
||||||
EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4Opts))));
|
EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4_opts))));
|
||||||
|
|
||||||
// delOption() checks
|
// delOption() checks
|
||||||
EXPECT_TRUE(pkt->getOption(12)); // Sanity check: option 12 is still there
|
EXPECT_TRUE(pkt->getOption(12)); // Sanity check: option 12 is still there
|
||||||
@@ -520,7 +623,8 @@ TEST(Pkt4Test, options) {
|
|||||||
EXPECT_NO_THROW(pkt.reset());
|
EXPECT_NO_THROW(pkt.reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, unpackOptions) {
|
// This test verifies that the options are unpacked from the packet correctly.
|
||||||
|
TEST_F(Pkt4Test, unpackOptions) {
|
||||||
|
|
||||||
vector<uint8_t> expectedFormat = generateTestPacket2();
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
||||||
|
|
||||||
@@ -529,8 +633,8 @@ TEST(Pkt4Test, unpackOptions) {
|
|||||||
expectedFormat.push_back(0x53);
|
expectedFormat.push_back(0x53);
|
||||||
expectedFormat.push_back(0x63);
|
expectedFormat.push_back(0x63);
|
||||||
|
|
||||||
for (int i = 0; i < sizeof(v4Opts); i++) {
|
for (int i = 0; i < sizeof(v4_opts); i++) {
|
||||||
expectedFormat.push_back(v4Opts[i]);
|
expectedFormat.push_back(v4_opts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now expectedFormat contains fixed format and 5 options
|
// now expectedFormat contains fixed format and 5 options
|
||||||
@@ -542,59 +646,53 @@ TEST(Pkt4Test, unpackOptions) {
|
|||||||
pkt->unpack()
|
pkt->unpack()
|
||||||
);
|
);
|
||||||
|
|
||||||
EXPECT_TRUE(pkt->getOption(12));
|
verifyParsedOptions(pkt);
|
||||||
EXPECT_TRUE(pkt->getOption(60));
|
}
|
||||||
EXPECT_TRUE(pkt->getOption(14));
|
|
||||||
EXPECT_TRUE(pkt->getOption(128));
|
|
||||||
EXPECT_TRUE(pkt->getOption(254));
|
|
||||||
|
|
||||||
boost::shared_ptr<Option> x = pkt->getOption(12);
|
// This test verifies that it is possible to specify custom implementation of
|
||||||
ASSERT_TRUE(x); // option 1 should exist
|
// the option parsing algorithm by installing a callback function.
|
||||||
// Option 12 is represented by the OptionString class so let's do
|
TEST_F(Pkt4Test, unpackOptionsWithCallback) {
|
||||||
// the appropriate conversion.
|
vector<uint8_t> expectedFormat = generateTestPacket2();
|
||||||
OptionStringPtr option12 = boost::static_pointer_cast<OptionString>(x);
|
|
||||||
ASSERT_TRUE(option12);
|
|
||||||
EXPECT_EQ(12, option12->getType()); // this should be option 12
|
|
||||||
ASSERT_EQ(3, option12->getValue().length()); // it should be of length 3
|
|
||||||
EXPECT_EQ(5, option12->len()); // total option length 5
|
|
||||||
EXPECT_EQ(0, memcmp(&option12->getValue()[0], v4Opts + 2, 3)); // data len=3
|
|
||||||
|
|
||||||
x = pkt->getOption(14);
|
expectedFormat.push_back(0x63);
|
||||||
ASSERT_TRUE(x); // option 14 should exist
|
expectedFormat.push_back(0x82);
|
||||||
// Option 14 is represented by the OptionString class so let's do
|
expectedFormat.push_back(0x53);
|
||||||
// the appropriate conversion.
|
expectedFormat.push_back(0x63);
|
||||||
OptionStringPtr option14 = boost::static_pointer_cast<OptionString>(x);
|
|
||||||
ASSERT_TRUE(option14);
|
|
||||||
EXPECT_EQ(14, option14->getType()); // this should be option 14
|
|
||||||
ASSERT_EQ(3, option14->getValue().length()); // it should be of length 3
|
|
||||||
EXPECT_EQ(5, option14->len()); // total option length 5
|
|
||||||
EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4Opts + 7, 3)); // data len=3
|
|
||||||
|
|
||||||
x = pkt->getOption(60);
|
for (int i = 0; i < sizeof(v4_opts); i++) {
|
||||||
ASSERT_TRUE(x); // option 60 should exist
|
expectedFormat.push_back(v4_opts[i]);
|
||||||
EXPECT_EQ(60, x->getType()); // this should be option 60
|
}
|
||||||
ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
||||||
EXPECT_EQ(5, x->len()); // total option length 5
|
|
||||||
EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 15, 3)); // data len=3
|
|
||||||
|
|
||||||
x = pkt->getOption(128);
|
// now expectedFormat contains fixed format and 5 options
|
||||||
ASSERT_TRUE(x); // option 3 should exist
|
|
||||||
EXPECT_EQ(128, x->getType()); // this should be option 254
|
boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
|
||||||
ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
expectedFormat.size()));
|
||||||
EXPECT_EQ(5, x->len()); // total option length 5
|
|
||||||
EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 20, 3)); // data len=3
|
CustomUnpackCallback cb;
|
||||||
|
pkt->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
|
||||||
|
_1, _2, _3));
|
||||||
|
|
||||||
|
ASSERT_FALSE(cb.executed_);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(pkt->unpack());
|
||||||
|
|
||||||
|
EXPECT_TRUE(cb.executed_);
|
||||||
|
verifyParsedOptions(pkt);
|
||||||
|
|
||||||
|
// Reset the indicator to perform another check: uninstall the callback.
|
||||||
|
cb.executed_ = false;
|
||||||
|
// By setting the callback to NULL we effectively uninstall the callback.
|
||||||
|
pkt->setCallback(NULL);
|
||||||
|
// Do another unpack.
|
||||||
|
EXPECT_NO_THROW(pkt->unpack());
|
||||||
|
// Callback should not be executed.
|
||||||
|
EXPECT_FALSE(cb.executed_);
|
||||||
|
|
||||||
x = pkt->getOption(254);
|
|
||||||
ASSERT_TRUE(x); // option 3 should exist
|
|
||||||
EXPECT_EQ(254, x->getType()); // this should be option 254
|
|
||||||
ASSERT_EQ(3, x->getData().size()); // it should be of length 3
|
|
||||||
EXPECT_EQ(5, x->len()); // total option length 5
|
|
||||||
EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts + 25, 3)); // data len=3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test verifies methods that are used for manipulating meta fields
|
// This test verifies methods that are used for manipulating meta fields
|
||||||
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
|
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
|
||||||
TEST(Pkt4Test, metaFields) {
|
TEST_F(Pkt4Test, metaFields) {
|
||||||
|
|
||||||
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
||||||
pkt->setIface("loooopback");
|
pkt->setIface("loooopback");
|
||||||
@@ -608,7 +706,7 @@ TEST(Pkt4Test, metaFields) {
|
|||||||
EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
|
EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, Timestamp) {
|
TEST_F(Pkt4Test, Timestamp) {
|
||||||
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
||||||
|
|
||||||
// Just after construction timestamp is invalid
|
// Just after construction timestamp is invalid
|
||||||
@@ -634,7 +732,7 @@ TEST(Pkt4Test, Timestamp) {
|
|||||||
EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
|
EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Pkt4Test, hwaddr) {
|
TEST_F(Pkt4Test, hwaddr) {
|
||||||
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
||||||
const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
|
const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
|
||||||
const uint8_t hw_type = 123; // hardware type
|
const uint8_t hw_type = 123; // hardware type
|
||||||
@@ -655,7 +753,7 @@ TEST(Pkt4Test, hwaddr) {
|
|||||||
|
|
||||||
// This test verifies that the packet remte and local HW address can
|
// This test verifies that the packet remte and local HW address can
|
||||||
// be set and returned.
|
// be set and returned.
|
||||||
TEST(Pkt4Test, hwaddrSrcRemote) {
|
TEST_F(Pkt4Test, hwaddrSrcRemote) {
|
||||||
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
|
||||||
const uint8_t src_hw[] = { 1, 2, 3, 4, 5, 6 };
|
const uint8_t src_hw[] = { 1, 2, 3, 4, 5, 6 };
|
||||||
const uint8_t dst_hw[] = { 7, 8, 9, 10, 11, 12 };
|
const uint8_t dst_hw[] = { 7, 8, 9, 10, 11, 12 };
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include <dhcp/pkt6.h>
|
#include <dhcp/pkt6.h>
|
||||||
#include <util/range_utilities.h>
|
#include <util/range_utilities.h>
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <util/encode/hex.h>
|
#include <util/encode/hex.h>
|
||||||
@@ -41,7 +42,54 @@ using namespace isc::dhcp;
|
|||||||
using boost::scoped_ptr;
|
using boost::scoped_ptr;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// empty class for now, but may be extended once Addr6 becomes bigger
|
|
||||||
|
/// @brief A class which contains a custom callback function to unpack options.
|
||||||
|
///
|
||||||
|
/// This is a class used by the tests which verify that the custom callback
|
||||||
|
/// functions can be installed to unpack options from a message. When the
|
||||||
|
/// callback function is called, the executed_ member is set to true to allow
|
||||||
|
/// verification that the callback was really called. Internally, this class
|
||||||
|
/// uses libdhcp++ to unpack options so the options parsing algorithm remains
|
||||||
|
/// unchanged after installation of the callback.
|
||||||
|
class CustomUnpackCallback {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// Marks that callback hasn't been called.
|
||||||
|
CustomUnpackCallback()
|
||||||
|
: executed_(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief A callback
|
||||||
|
///
|
||||||
|
/// Contains custom implementation of the callback.
|
||||||
|
///
|
||||||
|
/// @param buf a A buffer holding options in on-wire format.
|
||||||
|
/// @param [out] options A reference to the collection where parsed options
|
||||||
|
/// will be stored.
|
||||||
|
/// @param relay_msg_offset Reference to a size_t structure. If specified,
|
||||||
|
/// offset to beginning of relay_msg option will be stored in it.
|
||||||
|
/// @param relay_msg_len reference to a size_t structure. If specified,
|
||||||
|
/// length of the relay_msg option will be stored in it.
|
||||||
|
/// @return An offset to the first byte after last parsed option.
|
||||||
|
size_t execute(const OptionBuffer& buf,
|
||||||
|
const std::string&,
|
||||||
|
isc::dhcp::OptionCollection& options,
|
||||||
|
size_t* relay_msg_offset,
|
||||||
|
size_t* relay_msg_len) {
|
||||||
|
// Set the executed_ member to true to allow verification that the
|
||||||
|
// callback has been actually called.
|
||||||
|
executed_ = true;
|
||||||
|
// Use default implementation of the unpack algorithm to parse options.
|
||||||
|
return (LibDHCP::unpackOptions6(buf, options, relay_msg_offset,
|
||||||
|
relay_msg_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A flag which indicates if callback function has been called.
|
||||||
|
bool executed_;
|
||||||
|
};
|
||||||
|
|
||||||
class Pkt6Test : public ::testing::Test {
|
class Pkt6Test : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
Pkt6Test() {
|
Pkt6Test() {
|
||||||
@@ -58,6 +106,38 @@ public:
|
|||||||
util::fillRandom(data.begin(), data.end());
|
util::fillRandom(data.begin(), data.end());
|
||||||
return OptionPtr(new Option(Option::V6, code, data));
|
return OptionPtr(new Option(Option::V6, code, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Create a wire representation of the test packet and clone it.
|
||||||
|
Pkt6Ptr packAndClone() {
|
||||||
|
Pkt6Ptr parent(new Pkt6(DHCPV6_SOLICIT, 0x020304));
|
||||||
|
|
||||||
|
OptionPtr opt1(new Option(Option::V6, 1));
|
||||||
|
OptionPtr opt2(new Option(Option::V6, 2));
|
||||||
|
OptionPtr opt3(new Option(Option::V6, 100));
|
||||||
|
// Let's not use zero-length option type 3 as it is IA_NA
|
||||||
|
|
||||||
|
parent->addOption(opt1);
|
||||||
|
parent->addOption(opt2);
|
||||||
|
parent->addOption(opt3);
|
||||||
|
|
||||||
|
EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
|
||||||
|
|
||||||
|
// Calculated length should be 16
|
||||||
|
EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
|
||||||
|
parent->len());
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(parent->pack());
|
||||||
|
|
||||||
|
EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
|
||||||
|
parent->len());
|
||||||
|
|
||||||
|
// Create second packet,based on assembled data from the first one
|
||||||
|
Pkt6Ptr clone(new Pkt6(static_cast<const uint8_t*>
|
||||||
|
(parent->getBuffer().getData()),
|
||||||
|
parent->getBuffer().getLength()));
|
||||||
|
return (clone);
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(Pkt6Test, constructor) {
|
TEST_F(Pkt6Test, constructor) {
|
||||||
@@ -204,38 +284,14 @@ TEST_F(Pkt6Test, unpack_solicit1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Pkt6Test, packUnpack) {
|
TEST_F(Pkt6Test, packUnpack) {
|
||||||
scoped_ptr<Pkt6> parent(new Pkt6(DHCPV6_SOLICIT, 0x020304));
|
// Create an on-wire representation of the test packet and clone it.
|
||||||
|
Pkt6Ptr clone = packAndClone();
|
||||||
OptionPtr opt1(new Option(Option::V6, 1));
|
|
||||||
OptionPtr opt2(new Option(Option::V6, 2));
|
|
||||||
OptionPtr opt3(new Option(Option::V6, 100));
|
|
||||||
// Let's not use zero-length option type 3 as it is IA_NA
|
|
||||||
|
|
||||||
parent->addOption(opt1);
|
|
||||||
parent->addOption(opt2);
|
|
||||||
parent->addOption(opt3);
|
|
||||||
|
|
||||||
EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
|
|
||||||
|
|
||||||
// Calculated length should be 16
|
|
||||||
EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
|
|
||||||
parent->len());
|
|
||||||
|
|
||||||
EXPECT_NO_THROW(parent->pack());
|
|
||||||
|
|
||||||
EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
|
|
||||||
parent->len());
|
|
||||||
|
|
||||||
// Create second packet,based on assembled data from the first one
|
|
||||||
scoped_ptr<Pkt6> clone(new Pkt6(
|
|
||||||
static_cast<const uint8_t*>(parent->getBuffer().getData()),
|
|
||||||
parent->getBuffer().getLength()));
|
|
||||||
|
|
||||||
// Now recreate options list
|
// Now recreate options list
|
||||||
EXPECT_TRUE(clone->unpack());
|
EXPECT_TRUE(clone->unpack());
|
||||||
|
|
||||||
// transid, message-type should be the same as before
|
// transid, message-type should be the same as before
|
||||||
EXPECT_EQ(parent->getTransid(), parent->getTransid());
|
EXPECT_EQ(0x020304, clone->getTransid());
|
||||||
EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
|
EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
|
||||||
|
|
||||||
EXPECT_TRUE(clone->getOption(1));
|
EXPECT_TRUE(clone->getOption(1));
|
||||||
@@ -244,6 +300,48 @@ TEST_F(Pkt6Test, packUnpack) {
|
|||||||
EXPECT_FALSE(clone->getOption(4));
|
EXPECT_FALSE(clone->getOption(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that it is possible to specify custom implementation of
|
||||||
|
// the option parsing algorithm by installing a callback function.
|
||||||
|
TEST_F(Pkt6Test, packUnpackWithCallback) {
|
||||||
|
// Create an on-wire representation of the test packet and clone it.
|
||||||
|
Pkt6Ptr clone = packAndClone();
|
||||||
|
|
||||||
|
// Install the custom callback function. We expect that this function
|
||||||
|
// will be called to parse options in the packet instead of
|
||||||
|
// LibDHCP::unpackOptions6.
|
||||||
|
CustomUnpackCallback cb;
|
||||||
|
clone->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
|
||||||
|
_1, _2, _3, _4, _5));
|
||||||
|
// Make sure that the flag which indicates if the callback function has
|
||||||
|
// been called is not set. Otherwise, our test doesn't make sense.
|
||||||
|
ASSERT_FALSE(cb.executed_);
|
||||||
|
|
||||||
|
// Now recreate options list
|
||||||
|
EXPECT_TRUE(clone->unpack());
|
||||||
|
|
||||||
|
// An object which holds a callback should now have a flag set which
|
||||||
|
// indicates that callback has been called.
|
||||||
|
EXPECT_TRUE(cb.executed_);
|
||||||
|
|
||||||
|
// transid, message-type should be the same as before
|
||||||
|
EXPECT_EQ(0x020304, clone->getTransid());
|
||||||
|
EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
|
||||||
|
|
||||||
|
EXPECT_TRUE(clone->getOption(1));
|
||||||
|
EXPECT_TRUE(clone->getOption(2));
|
||||||
|
EXPECT_TRUE(clone->getOption(100));
|
||||||
|
EXPECT_FALSE(clone->getOption(4));
|
||||||
|
|
||||||
|
// Reset the indicator to perform another check: uninstall the callback.
|
||||||
|
cb.executed_ = false;
|
||||||
|
// By setting the callback to NULL we effectively uninstall the callback.
|
||||||
|
clone->setCallback(NULL);
|
||||||
|
// Do another unpack.
|
||||||
|
EXPECT_TRUE(clone->unpack());
|
||||||
|
// Callback should not be executed.
|
||||||
|
EXPECT_FALSE(cb.executed_);
|
||||||
|
}
|
||||||
|
|
||||||
// This test verifies that options can be added (addOption()), retrieved
|
// This test verifies that options can be added (addOption()), retrieved
|
||||||
// (getOption(), getOptions()) and deleted (delOption()).
|
// (getOption(), getOptions()) and deleted (delOption()).
|
||||||
TEST_F(Pkt6Test, addGetDelOptions) {
|
TEST_F(Pkt6Test, addGetDelOptions) {
|
||||||
@@ -266,12 +364,12 @@ TEST_F(Pkt6Test, addGetDelOptions) {
|
|||||||
// Now there are 2 options of type 2
|
// Now there are 2 options of type 2
|
||||||
parent->addOption(opt3);
|
parent->addOption(opt3);
|
||||||
|
|
||||||
Option::OptionCollection options = parent->getOptions(2);
|
OptionCollection options = parent->getOptions(2);
|
||||||
EXPECT_EQ(2, options.size()); // there should be 2 instances
|
EXPECT_EQ(2, options.size()); // there should be 2 instances
|
||||||
|
|
||||||
// Both options must be of type 2 and there must not be
|
// Both options must be of type 2 and there must not be
|
||||||
// any other type returned
|
// any other type returned
|
||||||
for (Option::OptionCollection::const_iterator x= options.begin();
|
for (OptionCollection::const_iterator x= options.begin();
|
||||||
x != options.end(); ++x) {
|
x != options.end(); ++x) {
|
||||||
EXPECT_EQ(2, x->second->getType());
|
EXPECT_EQ(2, x->second->getType());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user