#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>
// 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
}
// 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
" 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);
}
" 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();
/// @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.
///
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) {
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;
// 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));
+ }
}
}
// 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