From: Marcin Siodelski Date: Mon, 9 Nov 2015 12:46:25 +0000 (+0100) Subject: [4106] Allow for transmitting vendor option in the DHCPv4o6 message. X-Git-Tag: trac4296_base~2^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=36d31194337fe0855ef37900633e71a20e7a08b9;p=thirdparty%2Fkea.git [4106] Allow for transmitting vendor option in the DHCPv4o6 message. --- diff --git a/src/lib/dhcpsrv/dhcp4o6_ipc.cc b/src/lib/dhcpsrv/dhcp4o6_ipc.cc index 2ea6fe190f..cbae906e88 100644 --- a/src/lib/dhcpsrv/dhcp4o6_ipc.cc +++ b/src/lib/dhcpsrv/dhcp4o6_ipc.cc @@ -21,7 +21,7 @@ #include #include #include - +#include #include #include #include @@ -138,27 +138,35 @@ Pkt6Ptr Dhcp4o6IpcBase::receive() { // Get interface name and remote address pkt->unpack(); - OptionVendorPtr vendor = - boost::dynamic_pointer_cast(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(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(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(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(option_vendor->delOption(ISC_V6_4O6_INTERFACE)); + static_cast(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(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(); diff --git a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc index 5fa4b69ce0..e334897b1a 100644 --- a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc @@ -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 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(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