diff options
Diffstat (limited to 'src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc')
-rw-r--r-- | src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc new file mode 100644 index 0000000..192bb63 --- /dev/null +++ b/src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc @@ -0,0 +1,584 @@ +// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <dhcp/iface_mgr.h> +#include <dhcp/pkt6.h> +#include <dhcp/tests/iface_mgr_test_config.h> +#include <dhcp/option6_addrlst.h> +#include <dhcp/option_string.h> +#include <dhcp/option_vendor.h> +#include <dhcp/option_int.h> +#include <dhcpsrv/dhcp4o6_ipc.h> +#include <dhcpsrv/testutils/dhcp4o6_test_ipc.h> + +#include <gtest/gtest.h> +#include <functional> +#include <sstream> +#include <string> + +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::util; + +namespace { + +/// @brief Test port. +const uint16_t TEST_PORT = 12345; + +/// @brief Number of iterations used by the tests. +const uint16_t TEST_ITERATIONS = 10; + +/// @brief Type definition for the function creating DHCP message. +typedef std::function<Pkt6Ptr(uint16_t, uint16_t)> CreateMsgFun; + +/// @brief Define short name for test IPC class. +typedef Dhcp4o6TestIpc TestIpc; + +/// @brief Test fixture class for @c Dhcp4o6IpcBase. +class Dhcp4o6IpcBaseTest : public ::testing::Test { +protected: + + /// @brief Constructor. + /// + /// Replaces the real configuration of interfaces with a fake configuration. + /// The IPC uses the @c IfaceMgr to check whether the interfaces which names + /// are carried within DHCP options exist in the system. Providing the fake + /// configuration for the @c IfaceMgr guarantees that the configuration is + /// consistent on any machine running the unit tests. + Dhcp4o6IpcBaseTest(); + + /// @brief Concatenates the prefix and postfix. + /// + /// @param prefix Prefix. + /// @param postfix Postfix. + /// @return String representing concatenated prefix and postfix. + static std::string concatenate(const std::string& prefix, uint16_t postfix); + + /// @brief Creates an instance of the DHCPv4o6 message. + //// + /// @param msg_type Message type. + /// @param postfix Postfix to be appended to the remote address. For example, + /// for postfix = 5 the resulting remote address will be 2001:db8:1::5. + /// The postfix value is also used to generate the postfix for the interface. + /// The possible interface names are "eth0" and "eth1". For even postfix values + /// the "eth0" will be used, for odd postfix values "eth1" will be used. + /// + /// @return Pointer to the created message. + static Pkt6Ptr createDHCPv4o6Message(uint16_t msg_type, + uint16_t postfix = 0); + + /// @brief Creates an instance of the DHCPv4o6 message with vendor option. + /// + /// @param msg_type Message type. + /// @param postfix Postfix to be appended to the remote address. See the + /// documentation of @c createDHCPv4o6Message for details. + /// @param enterprise_id Enterprise ID for the vendor option. + /// + /// @return Pointer to the created message. + static Pkt6Ptr createDHCPv4o6MsgWithVendorOption(uint16_t msg_type, + uint16_t postfix, + 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 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 createDHCPv4o6MsgWithISCVendorOption(uint16_t msg_type, + 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(uint16_t msg_type, + uint16_t postfix); + + /// @brief Creates an instance of the DHCPv4o6 Message option. + /// + /// @param src Type of the source endpoint. It can be 4 or 6. + /// @return Pointer to the instance of the option. + static OptionPtr createDHCPv4MsgOption(TestIpc::EndpointType src); + + /// @brief Tests sending and receiving packets over the IPC. + /// + /// @param iterations_num Number of packets to be sent over the IPC. + /// @param src Type of the source IPC endpoint. It can be 4 or 6. + /// @param dest Type of the destination IPC endpoint. It can be 4 or 6. + /// @param create_msg_fun Function called to create the packet. + void testSendReceive(uint16_t iterations_num, + TestIpc::EndpointType src, + TestIpc::EndpointType dest, + const CreateMsgFun& create_msg_fun); + + /// @brief Tests that error is reported when invalid message is received. + /// + /// @param pkt Pointer to the invalid message. + void testReceiveError(const Pkt6Ptr& pkt); + +private: + + /// @brief Holds the fake configuration of the interfaces. + IfaceMgrTestConfig iface_mgr_test_config_; + +}; + +Dhcp4o6IpcBaseTest::Dhcp4o6IpcBaseTest() + : iface_mgr_test_config_(true) { +} + +std::string +Dhcp4o6IpcBaseTest::concatenate(const std::string& prefix, + uint16_t postfix) { + std::ostringstream s; + s << prefix << postfix; + return (s.str()); +} + +Pkt6Ptr +Dhcp4o6IpcBaseTest::createDHCPv4o6Message(uint16_t msg_type, + uint16_t postfix) { + // Create the DHCPv4o6 message. + Pkt6Ptr pkt(new Pkt6(msg_type, 0)); + + // The interface name is carried in the dedicated option between + // the servers. The receiving server will check that such interface + // is present in the system. The fake configuration we're using for + // this test includes two interfaces: "eth0" and "eth1". Therefore, + // we pick one or another, depending on the index of the iteration. + pkt->setIface(concatenate("eth", postfix % 2)); + pkt->setIndex(ETH0_INDEX + postfix % 2); + + // The remote address of the sender of the DHCPv6 packet is carried + // between the servers in the dedicated option. We use different + // address for each iteration to make sure that the IPC delivers the + // right address. + pkt->setRemoteAddr(IOAddress(concatenate("2001:db8:1::", postfix))); + + // The remote port of the sender of the DHCPv6 packet is carried + // between the servers in the dedicated option. + pkt->setRemotePort(10000 + (postfix % 1000)); + + // Determine the endpoint type using the message type. + TestIpc::EndpointType src = (msg_type == DHCPV6_DHCPV4_QUERY) ? + TestIpc::ENDPOINT_TYPE_V6 : TestIpc::ENDPOINT_TYPE_V4; + + // Add DHCPv4 Message option to make sure it is conveyed by the IPC. + pkt->addOption(createDHCPv4MsgOption(src)); + + return (pkt); +} + +Pkt6Ptr +Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithVendorOption(uint16_t msg_type, + uint16_t postfix, + 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)); + + // 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(uint16_t msg_type, + uint16_t postfix) { + return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, ENTERPRISE_ID_ISC)); +} + +Pkt6Ptr +Dhcp4o6IpcBaseTest::createDHCPv4o6MsgWithAnyVendorOption(uint16_t msg_type, + uint16_t postfix) { + return (createDHCPv4o6MsgWithVendorOption(msg_type, postfix, 32000)); +} + +OptionPtr +Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(TestIpc::EndpointType src) { + // Create the DHCPv4 message. + Pkt4Ptr pkt(new Pkt4(src == TestIpc::ENDPOINT_TYPE_V4 ? DHCPACK : DHCPREQUEST, + 1234)); + // Make a wire representation of the DHCPv4 message. + pkt->pack(); + OutputBuffer& output_buffer = pkt->getBuffer(); + const uint8_t* data = static_cast<const uint8_t*>(output_buffer.getData()); + OptionBuffer option_buffer(data, data + output_buffer.getLength()); + + // Create the DHCPv4 Message option holding the created message. + OptionPtr opt_msg(new Option(Option::V6, D6O_DHCPV4_MSG, option_buffer)); + return (opt_msg); +} + +void +Dhcp4o6IpcBaseTest::testSendReceive(uint16_t iterations_num, + TestIpc::EndpointType src, + TestIpc::EndpointType dest, + const CreateMsgFun& create_msg_fun) { + // Create IPC instances representing the source and destination endpoints. + TestIpc ipc_src(TEST_PORT, src); + TestIpc ipc_dest(TEST_PORT, dest); + + // Open the IPC on both ends. + ASSERT_NO_THROW(ipc_src.open()); + ASSERT_NO_THROW(ipc_dest.open()); + + // Depending if we're sending from DHCPv6 to DHCPv4 or the opposite + // direction we use different message type. This is not really required + // for testing IPC, but it better simulates the real use case. + uint16_t msg_type = (src == TestIpc::ENDPOINT_TYPE_V6 ? 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))); + + // Actually send the message through the IPC. + ASSERT_NO_THROW(ipc_src.send(pkt)) + << "Failed to send the message over the IPC for iteration " << i; + } + + // Try to receive all messages. + for (uint16_t i = 1; i <= iterations_num; ++i) { + + // Call receive with a timeout. The data should appear on the socket + // within this time. + ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0)); + + // Pop the received message. + Pkt6Ptr pkt_received = ipc_dest.popPktReceived(); + ASSERT_TRUE(pkt_received); + + // Check that the message type is correct. + EXPECT_EQ(msg_type, pkt_received->getType()); + + // Check that the interface is correct. + EXPECT_EQ(concatenate("eth", i % 2), pkt_received->getIface()); + EXPECT_EQ(ETH0_INDEX + i % 2, pkt_received->getIndex()); + + // Check that the address conveyed is correct. + EXPECT_EQ(concatenate("2001:db8:1::", i), + pkt_received->getRemoteAddr().toText()); + + // Check that the port conveyed is correct. + EXPECT_EQ(10000 + (i % 1000), pkt_received->getRemotePort()); + + // 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)); + } + } +} + +void +Dhcp4o6IpcBaseTest::testReceiveError(const Pkt6Ptr& pkt) { + TestIpc ipc_src(TEST_PORT, TestIpc::ENDPOINT_TYPE_V6); + TestIpc ipc_dest(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + + // Open the IPC on both ends. + ASSERT_NO_THROW(ipc_src.open()); + ASSERT_NO_THROW(ipc_dest.open()); + + pkt->setIface("eth0"); + pkt->setIndex(ETH0_INDEX); + pkt->setRemoteAddr(IOAddress("2001:db8:1::1")); + pkt->setRemotePort(TEST_PORT); + pkt->addOption(createDHCPv4MsgOption(TestIpc::ENDPOINT_TYPE_V6)); + + OutputBuffer& buf = pkt->getBuffer(); + buf.clear(); + ASSERT_NO_THROW(pkt->pack()); + + ASSERT_NE(-1, ::send(ipc_src.getSocketFd(), buf.getData(), + buf.getLength(), 0)); + + // Call receive with a timeout. The data should appear on the socket + // within this time. + ASSERT_THROW(IfaceMgr::instance().receive6(1, 0), Dhcp4o6IpcError); +} + + +// This test verifies that the IPC can transmit messages between the +// DHCPv4 and DHCPv6 server. +TEST_F(Dhcp4o6IpcBaseTest, send4To6) { + testSendReceive(TEST_ITERATIONS, TestIpc::ENDPOINT_TYPE_V4, + TestIpc::ENDPOINT_TYPE_V6, &createDHCPv4o6Message); +} + +// This test verifies that the IPC can transmit messages between the +// DHCPv6 and DHCPv4 server. +TEST_F(Dhcp4o6IpcBaseTest, send6To4) { + testSendReceive(TEST_ITERATIONS, TestIpc::ENDPOINT_TYPE_V6, + TestIpc::ENDPOINT_TYPE_V4, &createDHCPv4o6Message); +} + +// 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, TestIpc::ENDPOINT_TYPE_V6, + TestIpc::ENDPOINT_TYPE_V4, + &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, TestIpc::ENDPOINT_TYPE_V4, + TestIpc::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, TestIpc::ENDPOINT_TYPE_V6, + TestIpc::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, TestIpc::ENDPOINT_TYPE_V4, + TestIpc::ENDPOINT_TYPE_V6, + &createDHCPv4o6MsgWithAnyVendorOption); +} + +// This test checks that the values of the socket descriptor are correct +// when the socket is opened and then closed. +TEST_F(Dhcp4o6IpcBaseTest, openClose) { + TestIpc ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + EXPECT_EQ(-1, ipc.getSocketFd()); + + ASSERT_NO_THROW(ipc.open()); + EXPECT_NE(-1, ipc.getSocketFd()); + + ASSERT_NO_THROW(ipc.close()); + EXPECT_EQ(-1, ipc.getSocketFd()); +} + +// This test verifies that it is call open() while the socket is already +// opened. If the port changes, the new socket should be opened. +TEST_F(Dhcp4o6IpcBaseTest, openMultipleTimes) { + TestIpc ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V6); + ASSERT_NO_THROW(ipc.open()); + int sock_fd = ipc.getSocketFd(); + ASSERT_NE(-1, sock_fd); + ASSERT_EQ(TEST_PORT, ipc.getPort()); + + ASSERT_NO_THROW(ipc.open()); + ASSERT_EQ(sock_fd, ipc.getSocketFd()); + ASSERT_EQ(TEST_PORT, ipc.getPort()); + + ipc.setDesiredPort(TEST_PORT + 10); + ASSERT_NO_THROW(ipc.open()); + ASSERT_NE(-1, ipc.getSocketFd()); + + EXPECT_EQ(TEST_PORT + 10, ipc.getPort()); +} + +// This test verifies that the socket remains open if there is a failure +// to open a new socket. +TEST_F(Dhcp4o6IpcBaseTest, openError) { + TestIpc ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + ASSERT_NO_THROW(ipc.open()); + int sock_fd = ipc.getSocketFd(); + ASSERT_NE(-1, sock_fd); + + TestIpc ipc_bound(TEST_PORT + 10, TestIpc::ENDPOINT_TYPE_V4); + ASSERT_NO_THROW(ipc_bound.open()); + ASSERT_NE(-1, ipc_bound.getSocketFd()); + + ipc.setDesiredPort(TEST_PORT + 10); + ASSERT_THROW(ipc.open(), isc::dhcp::Dhcp4o6IpcError); + + EXPECT_EQ(sock_fd, ipc.getSocketFd()); + EXPECT_EQ(TEST_PORT, ipc.getPort()); + + ASSERT_NO_THROW(ipc_bound.close()); + ASSERT_NO_THROW(ipc.open()); + EXPECT_NE(-1, ipc.getSocketFd()); + EXPECT_EQ(TEST_PORT + 10, ipc.getPort()); +} + +// This test verifies that receiving packet over the IPC fails when there +// is no vendor option present. +TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutVendorOption) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + testReceiveError(pkt); +} + +// This test verifies that receiving packet over the IPC fails when the +// enterprise ID carried in the vendor option is invalid. +TEST_F(Dhcp4o6IpcBaseTest, receiveInvalidEnterpriseId) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + OptionVendorPtr option_vendor(new OptionVendor(Option::V6, 1)); + option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6, + ISC_V6_4O6_INTERFACE, + "eth0"))); + option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, + IOAddress("2001:db8:1::1")))); + + pkt->addOption(option_vendor); + testReceiveError(pkt); +} + +// This test verifies that receiving packet over the IPC fails when the +// interface option is not present. +TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutInterfaceOption) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + OptionVendorPtr option_vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC)); + option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, + IOAddress("2001:db8:1::1")))); + option_vendor->addOption(OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + TEST_PORT))); + + pkt->addOption(option_vendor); + testReceiveError(pkt); +} + +// This test verifies that receiving packet over the IPC fails when the +// interface which name is carried in the option is not present in the +// system. +TEST_F(Dhcp4o6IpcBaseTest, receiveWithInvalidInterface) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + OptionVendorPtr option_vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC)); + option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6, + ISC_V6_4O6_INTERFACE, + "ethX"))); + option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, + IOAddress("2001:db8:1::1")))); + option_vendor->addOption(OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + TEST_PORT))); + + pkt->addOption(option_vendor); + testReceiveError(pkt); +} + + +// This test verifies that receiving packet over the IPC fails when the +// source address option is not present. +TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutSourceAddressOption) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + OptionVendorPtr option_vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC)); + option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6, + ISC_V6_4O6_INTERFACE, + "eth0"))); + option_vendor->addOption(OptionUint16Ptr(new OptionUint16(Option::V6, + ISC_V6_4O6_SRC_PORT, + TEST_PORT))); + + pkt->addOption(option_vendor); + testReceiveError(pkt); +} + +// This test verifies that receiving packet over the IPC fails when the +// source port option is not present. +TEST_F(Dhcp4o6IpcBaseTest, receiveWithoutSourcePortOption) { + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 0)); + OptionVendorPtr option_vendor(new OptionVendor(Option::V6, ENTERPRISE_ID_ISC)); + option_vendor->addOption(OptionStringPtr(new OptionString(Option::V6, + ISC_V6_4O6_INTERFACE, + "eth0"))); + option_vendor->addOption(Option6AddrLstPtr(new Option6AddrLst(ISC_V6_4O6_SRC_ADDRESS, + IOAddress("2001:db8:1::1")))); + + pkt->addOption(option_vendor); + testReceiveError(pkt); +} + +// This test verifies that send method throws exception when the packet +// is NULL. +TEST_F(Dhcp4o6IpcBaseTest, sendNullMessage) { + TestIpc ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + ASSERT_NO_THROW(ipc.open()); + + // NULL message. + EXPECT_THROW(ipc.send(Pkt6Ptr()), Dhcp4o6IpcError); +} + +// This test verifies that send method throws exception when the IPC +// socket is not opened. +TEST_F(Dhcp4o6IpcBaseTest, sendOverClosedSocket) { + TestIpc ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + + // Create a message. + Pkt6Ptr pkt(createDHCPv4o6Message(DHCPV6_DHCPV4_QUERY)); + + // Sending over the closed socket should fail. + EXPECT_THROW(ipc.send(pkt), Dhcp4o6IpcError); +} + +// This test verifies that send method throws exception when the IPC +// socket has been unexpectedly closed. +TEST_F(Dhcp4o6IpcBaseTest, sendOverUnexpectedlyClosedSocket) { + TestIpc ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + ASSERT_NO_THROW(ipc.open()); + + // Close the socket behind the scenes. The IPC doesn't know that the + // socket has been closed and it still holds the descriptor. + ::close(ipc.getSocketFd()); + + // Create a message. + Pkt6Ptr pkt(createDHCPv4o6Message(DHCPV6_DHCPV4_QUERY)); + + // Sending over the closed socket should fail. + EXPECT_THROW(ipc.send(pkt), Dhcp4o6IpcError); +} + +} // end of anonymous namespace |