From f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 14:15:43 +0200 Subject: Adding upstream version 2.4.1. Signed-off-by: Daniel Baumann --- src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc | 318 +++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc (limited to 'src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc') diff --git a/src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc b/src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc new file mode 100644 index 0000000..ce7e077 --- /dev/null +++ b/src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc @@ -0,0 +1,318 @@ +// Copyright (C) 2015-2020 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::stats; +using namespace isc::hooks; +using namespace isc::util; + +namespace { + +/// @brief Port number used in tests. +const uint16_t TEST_PORT = 32000; + +/// @brief Define short name for the test IPC. +typedef Dhcp4o6TestIpc TestIpc; + +/// @brief Test fixture class for DHCPv4 endpoint of DHCPv4o6 IPC. +class Dhcp6to4IpcTest : public ::testing::Test { +public: + + /// @brief Constructor + /// + /// Configures IPC to use a test port. It also provides a fake + /// configuration of interfaces and opens IPv6 sockets. + Dhcp6to4IpcTest() + : iface_mgr_test_config_(true) { + IfaceMgr::instance().openSockets6(); + configurePort(TEST_PORT); + // Install buffer6_send_callout + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle(). + registerCallout("buffer6_send", buffer6_send_callout)); + // Let's wipe all existing statistics. + StatsMgr::instance().removeAll(); + + // Reset the flag which we expect to be set in the callout. + callback_pkt_options_copy_ = false; + } + + /// @brief Destructor + /// + /// Various cleanups. + virtual ~Dhcp6to4IpcTest() { + Dhcp6to4Ipc::client_port = 0; + HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("buffer6_send"); + callback_pkt_.reset(); + bool status = HooksManager::unloadLibraries(); + if (!status) { + std::cerr << "(fixture dtor) unloadLibraries failed" << std::endl; + } + } + + /// @brief Configure DHCP4o6 port. + /// + /// @param port New port. + void configurePort(const uint16_t port); + + /// @brief Creates an instance of the DHCPv4o6 Message option. + /// + /// The option will contain an empty DHCPREQUEST message, with + /// just the Message Type option inside and nothing else. + /// + /// @return Pointer to the instance of the DHCPv4-query Message option. + OptionPtr createDHCPv4MsgOption() const; + + /// @brief Handler for the buffer6_send hook + /// + /// @param callout_handle handle passed by the hooks framework + /// @return always 0 + static int + buffer6_send_callout(CalloutHandle& callout_handle) { + callout_handle.getArgument("response6", callback_pkt_); + if (callback_pkt_) { + callback_pkt_options_copy_ = callback_pkt_->isCopyRetrievedOptions(); + } + return (0); + } + + /// @brief Response Pkt6 shared pointer returned in the callout + static Pkt6Ptr callback_pkt_; + + /// Flag indicating if copying retrieved options was enabled for + /// a received packet during callout execution. + static bool callback_pkt_options_copy_; + +private: + + /// @brief Provides fake configuration of interfaces. + IfaceMgrTestConfig iface_mgr_test_config_; +}; + +Pkt6Ptr Dhcp6to4IpcTest::callback_pkt_; +bool Dhcp6to4IpcTest::callback_pkt_options_copy_; + +void +Dhcp6to4IpcTest::configurePort(const uint16_t port) { + CfgMgr::instance().getStagingCfg()->setDhcp4o6Port(port); +} + +OptionPtr +Dhcp6to4IpcTest::createDHCPv4MsgOption() const { + // Create the DHCPv4 message. + Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 1234)); + // Make a wire representation of the DHCPv4 message. + pkt->pack(); + OutputBuffer& output_buffer = pkt->getBuffer(); + const uint8_t* data = static_cast(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); +} + +// This test verifies that the IPC returns an error when trying to bind +// to the out of range port. +TEST_F(Dhcp6to4IpcTest, invalidPortError) { + // Create instance of the IPC endpoint under test with out-of-range port. + configurePort(65535); + Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance(); + EXPECT_THROW(ipc.open(), isc::OutOfRange); +} + +// This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can +// receive messages. +TEST_F(Dhcp6to4IpcTest, receive) { + // Create instance of the IPC endpoint under test. + Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance(); + // Create instance of the IPC endpoint being used as a source of messages. + TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + + // Reset the IPC. + ASSERT_NO_THROW(ipc.close()); + + // Open both endpoints. + ASSERT_NO_THROW(ipc.open()); + ASSERT_NO_THROW(src_ipc.open()); + + // Get statistics + StatsMgr& mgr = StatsMgr::instance(); + ObservationPtr pkt6_snd = mgr.getObservation("pkt6-sent"); + ObservationPtr d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent"); + EXPECT_FALSE(pkt6_snd); + EXPECT_FALSE(d4_resp); + + // Create message to be sent over IPC. + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_RESPONSE, 1234)); + pkt->addOption(createDHCPv4MsgOption()); + pkt->setIface("eth0"); + pkt->setIndex(ETH0_INDEX); + pkt->setRemoteAddr(IOAddress("2001:db8:1::123")); + ASSERT_NO_THROW(pkt->pack()); + + // Reset the callout cached packet + Dhcp6to4IpcTest::callback_pkt_.reset(); + + // Send and wait up to 1 second to receive it. + ASSERT_NO_THROW(src_ipc.send(pkt)); + ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0)); + + // Make sure that the received packet was configured to return copy of + // retrieved options within a callout. + EXPECT_TRUE(callback_pkt_options_copy_); + + // Get the forwarded packet from the callout + Pkt6Ptr forwarded = Dhcp6to4IpcTest::callback_pkt_; + ASSERT_TRUE(forwarded); + + // Verify the packet received. + EXPECT_EQ(DHCP6_CLIENT_PORT, forwarded->getRemotePort()); + EXPECT_EQ(forwarded->getType(), pkt->getType()); + EXPECT_TRUE(forwarded->getOption(D6O_DHCPV4_MSG)); + EXPECT_EQ("eth0", forwarded->getIface()); + EXPECT_EQ(ETH0_INDEX, forwarded->getIndex()); + EXPECT_EQ("2001:db8:1::123", forwarded->getRemoteAddr().toText()); + + // Verify statistics + pkt6_snd = mgr.getObservation("pkt6-sent"); + d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent"); + ASSERT_TRUE(pkt6_snd); + ASSERT_TRUE(d4_resp); + EXPECT_EQ(1, pkt6_snd->getInteger().first); + EXPECT_EQ(1, d4_resp->getInteger().first); +} + +// This test verifies that the DHCPv4 endpoint of the DHCPv4o6 IPC can +// receive relayed messages. +// This is currently not supported: it is a known defect addressed by #4296. +TEST_F(Dhcp6to4IpcTest, DISABLED_receiveRelayed) { + // Create instance of the IPC endpoint under test. + Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance(); + // Create instance of the IPC endpoint being used as a source of messages. + TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + + // Reset the IPC. + ASSERT_NO_THROW(ipc.close()); + + // Open both endpoints. + ASSERT_NO_THROW(ipc.open()); + ASSERT_NO_THROW(src_ipc.open()); + + // Get statistics + StatsMgr& mgr = StatsMgr::instance(); + ObservationPtr pkt6_snd = mgr.getObservation("pkt6-sent"); + ObservationPtr d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent"); + EXPECT_FALSE(pkt6_snd); + EXPECT_FALSE(d4_resp); + + // Create relayed message to be sent over IPC. + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_RESPONSE, 1234)); + pkt->addOption(createDHCPv4MsgOption()); + Pkt6::RelayInfo relay; + relay.linkaddr_ = IOAddress("3000:1::1"); + relay.peeraddr_ = IOAddress("fe80::1"); + relay.msg_type_ = DHCPV6_RELAY_FORW; + relay.hop_count_ = 1; + pkt->relay_info_.push_back(relay); + pkt->setIface("eth0"); + pkt->setIndex(ETH0_INDEX); + pkt->setRemoteAddr(IOAddress("2001:db8:1::123")); + ASSERT_NO_THROW(pkt->pack()); + + // Reset the callout cached packet + Dhcp6to4IpcTest::callback_pkt_.reset(); + + // Send and wait up to 1 second to receive it. + ASSERT_NO_THROW(src_ipc.send(pkt)); + ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0)); + + // Get the forwarded packet from the callout + Pkt6Ptr forwarded = Dhcp6to4IpcTest::callback_pkt_; + ASSERT_TRUE(forwarded); + + // Verify the packet received. + EXPECT_EQ(DHCP6_CLIENT_PORT, forwarded->getRemotePort()); + EXPECT_EQ(forwarded->getType(), pkt->getType()); + EXPECT_TRUE(forwarded->getOption(D6O_DHCPV4_MSG)); + EXPECT_EQ("eth0", forwarded->getIface()); + EXPECT_EQ(ETH0_INDEX, forwarded->getIndex()); + EXPECT_EQ("2001:db8:1::123", forwarded->getRemoteAddr().toText()); + EXPECT_EQ(DHCP6_CLIENT_PORT, forwarded->getRemotePort()); + + // Verify statistics + pkt6_snd = mgr.getObservation("pkt6-sent"); + d4_resp = mgr.getObservation("pkt6-dhcpv4-response-sent"); + ASSERT_TRUE(pkt6_snd); + ASSERT_TRUE(d4_resp); + EXPECT_EQ(1, pkt6_snd->getInteger().first); + EXPECT_EQ(1, d4_resp->getInteger().first); +} + +// This test verifies the client port is enforced also with DHCP4o6. +TEST_F(Dhcp6to4IpcTest, clientPort) { + // Create instance of the IPC endpoint under test. + Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance(); + // Set the client port. + ipc.client_port = 1234; + // Create instance of the IPC endpoint being used as a source of messages. + TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); + + // Reset the IPC. + ASSERT_NO_THROW(ipc.close()); + + // Open both endpoints. + ASSERT_NO_THROW(ipc.open()); + ASSERT_NO_THROW(src_ipc.open()); + + // Create message to be sent over IPC. + Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_RESPONSE, 1234)); + pkt->addOption(createDHCPv4MsgOption()); + pkt->setIface("eth0"); + pkt->setIndex(ETH0_INDEX); + pkt->setRemoteAddr(IOAddress("2001:db8:1::123")); + ASSERT_NO_THROW(pkt->pack()); + + // Reset the callout cached packet + Dhcp6to4IpcTest::callback_pkt_.reset(); + + // Send and wait up to 1 second to receive it. + ASSERT_NO_THROW(src_ipc.send(pkt)); + ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0)); + + // Make sure that the received packet was configured to return copy of + // retrieved options within a callout. + EXPECT_TRUE(callback_pkt_options_copy_); + + // Get the forwarded packet from the callout + Pkt6Ptr forwarded = Dhcp6to4IpcTest::callback_pkt_; + ASSERT_TRUE(forwarded); + + // Verify the packet received. + EXPECT_EQ(ipc.client_port, forwarded->getRemotePort()); +} + +} // end of anonymous namespace -- cgit v1.2.3