Commit 36d31194 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[4106] Allow for transmitting vendor option in the DHCPv4o6 message.

parent 7097e81a
......@@ -21,7 +21,7 @@
#include <dhcp/option_string.h>
#include <dhcp/option_vendor.h>
#include <dhcpsrv/dhcp4o6_ipc.h>
#include <boost/pointer_cast.hpp>
#include <errno.h>
#include <netinet/in.h>
#include <string>
......@@ -138,27 +138,35 @@ Pkt6Ptr Dhcp4o6IpcBase::receive() {
// Get interface name and remote address
pkt->unpack();
OptionVendorPtr vendor =
boost::dynamic_pointer_cast<OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
// Vendor option must exist.
if (!vendor) {
isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
<< " not present in the DHCP4o6 message sent between the "
" servers");
// Vendor option is initially NULL. If we find the instance of the vendor
// option with the ISC enterprise id this pointer will point to it.
OptionVendorPtr option_vendor;
// Get all vendor option and look for the one with the ISC enterprise id.
OptionCollection vendor_options = pkt->getOptions(D6O_VENDOR_OPTS);
for (OptionCollection::const_iterator opt = vendor_options.begin();
opt != vendor_options.end(); ++opt) {
option_vendor = boost::dynamic_pointer_cast<OptionVendor>(opt->second);
if (option_vendor) {
if (option_vendor->getVendorId() == ENTERPRISE_ID_ISC) {
break;
}
} else {
option_vendor.reset();
}
}
// The vendor option must require appropriate enterprise-id.
if (vendor->getVendorId() != ENTERPRISE_ID_ISC) {
// Vendor option must exist.
if (!option_vendor) {
isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
<< " in the DHCP4o6 message contains invalid enterprise-id '"
<< vendor->getVendorId() << "'. Expected enterprise-id '"
<< ENTERPRISE_ID_ISC << "'");
<< " with ISC enterprise id is not present in the DHCP4o6"
" message sent between the servers");
}
// The option carrying interface name is required.
OptionStringPtr ifname = boost::dynamic_pointer_cast<
OptionString>(vendor->getOption(ISC_V6_4O6_INTERFACE));
OptionString>(option_vendor->getOption(ISC_V6_4O6_INTERFACE));
if (!ifname) {
isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
<< " doesn't contain the " << ISC_V6_4O6_INTERFACE
......@@ -175,8 +183,8 @@ Pkt6Ptr Dhcp4o6IpcBase::receive() {
}
// Get the option holding source IPv6 address.
OptionCustomPtr srcs =
boost::dynamic_pointer_cast<OptionCustom>(vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
OptionCustomPtr srcs = boost::dynamic_pointer_cast<
OptionCustom>(option_vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
if (!srcs) {
isc_throw(Dhcp4o6IpcError, "option " << D6O_VENDOR_OPTS
<< " doesn't contain the " << ISC_V6_4O6_SRC_ADDRESS
......@@ -184,11 +192,21 @@ Pkt6Ptr Dhcp4o6IpcBase::receive() {
" between Kea servers");
}
// Update the packet and return it
static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
// Update the packet.
pkt->setRemoteAddr(srcs->readAddress());
pkt->setIface(iface->getName());
pkt->setIndex(iface->getIndex());
// Remove options that have been added by the IPC sender.
static_cast<void>(option_vendor->delOption(ISC_V6_4O6_INTERFACE));
static_cast<void>(option_vendor->delOption(ISC_V6_4O6_SRC_ADDRESS));
// If there are no more options, the IPC sender has probably created the
// vendor option, in which case we should remove it here.
if (option_vendor->getOptions().empty()) {
static_cast<void>(pkt->delOption(D6O_VENDOR_OPTS));
}
return (pkt);
}
......@@ -206,17 +224,24 @@ void Dhcp4o6IpcBase::send(const Pkt6Ptr& pkt) {
" IPC socket is closed");
}
// Get a vendor option
OptionVendorPtr vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
// Check if vendor option exists.
OptionVendorPtr option_vendor = dynamic_pointer_cast<
OptionVendor>(pkt->getOption(D6O_VENDOR_OPTS));
// If vendor option doesn't exist or its enterprise id is not ISC's
// enterprise id, let's create it.
if (!option_vendor || (option_vendor->getVendorId() != ENTERPRISE_ID_ISC)) {
option_vendor.reset(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
pkt->addOption(option_vendor);
}
// Push interface name and source address in it
vendor->addOption(OptionStringPtr(new OptionString(Option::V6,
option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6,
ISC_V6_4O6_INTERFACE,
pkt->getIface())));
vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
pkt->getRemoteAddr())));
pkt->addOption(vendor);
option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
pkt->getRemoteAddr())));
// Get packet content
OutputBuffer& buf = pkt->getBuffer();
buf.clear();
......
......@@ -171,19 +171,43 @@ protected:
/// @brief Creates an instance of the DHCPv4o6 message with vendor option.
///
/// The vendor option appended to the message has ISC entprise id and
/// comprises option @c ISC_V6_4O6_INTERFACE and @c ISC_V6_4O6_SRC_ADDRESS.
/// @param msg_type Message type.
/// @param postfix Postfix to be appended to the remote address. See the
/// documentation of @c createDHCPv4o6Message for details.
///
/// @return Pointer to the created message.
static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
const uint16_t postfix,
const uint32_t enterprise_id);
/// @brief Creates an instance of the DHCPv4o6 message with ISC
/// vendor option.
///
/// This is useful to test scenarios when the IPC is forwarding messages
/// that contain options also inserted by IPC. The duplicate options are
/// allowed and IPC should deal with this with no error.
/// that contain vendor option with ISC enterprise ID.
///
/// @param msg_type Message type.
/// @param postfix Postfix to be appended to the remote address. See the
/// documentation of @c createDHCPv4o6Message for details.
///
/// @return Pointer to the created message.
static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
const uint16_t postfix);
static Pkt6Ptr createDHCPv4o6MsgWithISCVendorOption(const uint16_t msg_type,
const uint16_t postfix);
/// @brief Creates an instance of the DHCPv4o6 message with vendor
/// option holding enterprise id of 32000.
///
/// This is useful to test scenarios when the IPC is forwarding messages
/// that contain some vendor option and when IPC also appends the ISC
/// vendor option to carry some DHCPv4o6 specific information.
///
/// @param msg_type Message type.
/// @param postfix Postfix to be appended to the remote address. See the
/// documentation of @c createDHCPv4o6Message for details.
///
/// @return Pointer to the created message.
static Pkt6Ptr createDHCPv4o6MsgWithAnyVendorOption(const uint16_t msg_type,
const uint16_t postfix);
/// @brief Creates an instance of the DHCPv4-query Message option.
///
......@@ -254,28 +278,33 @@ Dhcp4o6IpcBaseTest::createDHCPv4o6Message(const uint16_t msg_type,
Pkt6Ptr
Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(const uint16_t msg_type,
const uint16_t postfix) {
const uint16_t postfix,
const uint32_t enterprise_id) {
Pkt6Ptr pkt = createDHCPv4o6Message(msg_type, postfix);
// Create vendor option with ISC enterprise id.
OptionVendorPtr option_vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC));
// Add interface. Such interface doesn't have to exist in the system because
// IPC should not use this option when it is received. It should rather use
// the option that the sender side is appending to the message.
option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6,
ISC_V6_4O6_INTERFACE,
"non-existing")));
// Add some remote address.
option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS,
IOAddress("3000::10"))));
OptionVendorPtr option_vendor(new OptionVendor(Option::V6, enterprise_id));
// Add some option to the vendor option.
option_vendor->addOption(OptionPtr(new Option(Option::V6, 100)));
// Add vendor option to the message.
pkt->addOption(option_vendor);
return (pkt);
}
Pkt6Ptr
Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithISCVendorOption(const uint16_t msg_type,
const uint16_t postfix) {
return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, ENTERPRISE_ID_ISC));
}
Pkt6Ptr
Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithAnyVendorOption(const uint16_t msg_type,
const uint16_t postfix) {
return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, 32000));
}
OptionPtr
Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(const int src) {
......@@ -311,11 +340,18 @@ Dhcp4o6IpcBaseTest::testSendReceive(const uint16_t iterations_num,
uint16_t msg_type = (src == 6 ? DHCPV6_DHCPV4_QUERY :
DHCPV6_DHCPV4_RESPONSE);
std::vector<bool> has_vendor_option;
// Send the number of messages configured for the test.
for (uint16_t i = 1; i <= iterations_num; ++i) {
// Create the DHCPv4o6 message.
Pkt6Ptr pkt = create_msg_fun(msg_type, i);
// Remember if the vendor option exists in the source packet. The
// received packet should also contain this option if it exists
// in the source packet.
has_vendor_option.push_back(static_cast<bool>(pkt->getOption(D6O_VENDOR_OPTS)));
// Actaully send the message through the IPC.
ASSERT_NO_THROW(ipc_src.send(pkt))
<< "Failed to send the message over the IPC for iteration " << i;
......@@ -344,6 +380,24 @@ Dhcp4o6IpcBaseTest::testSendReceive(const uint16_t iterations_num,
// Check that encapsulated DHCPv4 message has been received.
EXPECT_TRUE(pkt_received->getOption(D6O_DHCPV4_MSG));
if (has_vendor_option[i - 1]) {
// Make sure that the vendor option wasn't deleted when the packet was
// received.
OptionPtr option_vendor = pkt_received->getOption(D6O_VENDOR_OPTS);
ASSERT_TRUE(option_vendor)
<< "vendor option deleted in the received DHCPv4o6 packet for"
" iteration " << i;
// ISC_V6_4O6_INTERFACE shouldn't be present.
EXPECT_FALSE(option_vendor->getOption(ISC_V6_4O6_INTERFACE));
// ISC_V6_4O6_SRC_ADDRESS shouldn't be present.
EXPECT_FALSE(option_vendor->getOption(ISC_V6_4O6_SRC_ADDRESS));
} else {
EXPECT_FALSE(pkt_received->getOption(D6O_VENDOR_OPTS));
}
}
}
......@@ -374,22 +428,49 @@ Dhcp4o6IpcBaseTest::testReceiveError(const Pkt6Ptr& pkt) {
// This test verifies that the IPC can transmit messages between the
// DHCPv6 and DHCPv4 server.
// DHCPv4 and DHCPv6 server.
TEST_F(Dhcp4o6IpcBaseTest, send4To6) {
testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V4, ENDPOINT_TYPE_V6,
&createDHCPv4o6Message);
}
// This test verifies taht the IPC can transmit messages between the
// DHCPv4 and DHCPv6 server.
// This test verifies that the IPC can transmit messages between the
// DHCPv6 and DHCPv4 server.
TEST_F(Dhcp4o6IpcBaseTest, send6To4) {
testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V6, ENDPOINT_TYPE_V4,
&createDHCPv4o6Message);
}
TEST_F(Dhcp4o6IpcBaseTest, send6To4WithVendorOption) {
// This test verifies that the IPC will transmit message already containing
// vendor option with ISC enterprise ID, between the DHCPv6 and DHCPv4
// server.
TEST_F(Dhcp4o6IpcBaseTest, send6To4WithISCVendorOption) {
testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V6, ENDPOINT_TYPE_V4,
&createDHCPv4o6MsgWithVendorOption);
&createDHCPv4o6MsgWithISCVendorOption);
}
// This test verifies that the IPC will transmit message already containing
// vendor option with ISC enterprise ID, between the DHCPv6 and DHCPv4
// server.
TEST_F(Dhcp4o6IpcBaseTest, send4To6WithISCVendorOption) {
testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V4, ENDPOINT_TYPE_V6,
&createDHCPv4o6MsgWithISCVendorOption);
}
// This test verifies that the IPC will transmit message already containing
// vendor option with enterprise id different than ISC, between the DHCPv6
// and DHCPv4 server.
TEST_F(Dhcp4o6IpcBaseTest, send6To4WithAnyVendorOption) {
testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V6, ENDPOINT_TYPE_V4,
&createDHCPv4o6MsgWithAnyVendorOption);
}
// This test verifies that the IPC will transmit message already containing
// vendor option with enterprise id different than ISC, between the DHCPv4
// and DHCPv6 server.
TEST_F(Dhcp4o6IpcBaseTest, send4To6WithAnyVendorOption) {
testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V4, ENDPOINT_TYPE_V6,
&createDHCPv4o6MsgWithAnyVendorOption);
}
// This test checks that the values of the socket descriptor are correct
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment