Browse Source

[4106] Improvements and comments to the DHCPv4o6 IPC tests.

Marcin Siodelski 9 years ago
parent
commit
f80c534182
2 changed files with 191 additions and 25 deletions
  1. 1 0
      src/lib/dhcpsrv/dhcp4o6_ipc.cc
  2. 190 25
      src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc

+ 1 - 0
src/lib/dhcpsrv/dhcp4o6_ipc.cc

@@ -199,6 +199,7 @@ void Dhcp4o6IpcBase::send(Pkt6Ptr pkt) {
 
     // Send
     static_cast<void>(::send(socket_fd_, buf.getData(), buf.getLength(), 0));
+
     return;
 }
 

+ 190 - 25
src/lib/dhcpsrv/tests/dhcp4o6_ipc_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014, 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -14,50 +14,96 @@
 
 #include <config.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/option_vendor.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcpsrv/dhcp4o6_ipc.h>
 #include <boost/bind.hpp>
 #include <gtest/gtest.h>
+#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 Defines the DHCPv4 endpoint of IPC.
+const int ENDPOINT_TYPE_V4 = 4;
+
+/// @brief Defines the DHCPv6 endpoint of IPC.
+const int ENDPOINT_TYPE_V6 = 6;
+
+/// @brief Implements a simple IPC for the test.
 class TestIpc : public  Dhcp4o6IpcBase {
 public:
 
-    TestIpc(const uint16_t port, const int side);
+    /// @brief Constructor.
+    ///
+    /// @param port Port.
+    /// @param endpoint_type Type of the IPC endpoint. It should be 4 or 6.
+    TestIpc(const uint16_t port, const int endpoint_type);
 
+    /// @brief Opens the IPC socket and registers it in @c IfaceMgr.
+    ///
+    /// This method opens the IPC socket and registers it as external
+    /// socket in the IfaceMgr. The @c TestIpc::receiveHandler is used as a
+    /// callback to be called by the @c IfaceMgr when the data is received
+    /// over the socket.
     virtual void open();
 
-    Pkt6Ptr getPktReceived() const {
-        return (pkt_received_);
+    /// @brief Pops and returns a received message.
+    ///
+    /// @return Pointer to the received message over the IPC.
+    Pkt6Ptr popPktReceived() {
+        // Copy the received message.
+        Pkt6Ptr pkt_copy(pkt_received_);
+        // Set the received message to NULL (pop).
+        pkt_received_.reset();
+        // Return the copy.
+        return (pkt_copy);
     }
 
 private:
 
+    /// @brief Callback for the IPC socket.
+    ///
+    /// This callback is called by the @c IfaceMgr when the data is received
+    /// over the IPC socket.
     void receiveHandler();
 
+    /// @brief Port number.
     uint16_t port_;
-    int side_;
-    Pkt6Ptr pkt_received_;
 
+    /// @brief Endpoint type, i.e. 4 or 6.
+    int endpoint_type_;
+
+    /// @brief Pointer to the last received message.
+    Pkt6Ptr pkt_received_;
 };
 
-TestIpc::TestIpc(const uint16_t port, const int side)
-    : port_(port), side_(side), pkt_received_() {
+TestIpc::TestIpc(const uint16_t port, const int endpoint_type)
+    : port_(port), endpoint_type_(endpoint_type), pkt_received_() {
 }
 
 void
 TestIpc::open() {
-    socket_fd_ = Dhcp4o6IpcBase::open(port_, side_);
-    if (socket_fd_ >= 0) {
-        IfaceMgr::instance().addExternalSocket(socket_fd_,
-                                               boost::bind(&TestIpc::receiveHandler, this));
+    // Use the base IPC to open the socket.
+    socket_fd_ = Dhcp4o6IpcBase::open(port_, endpoint_type_);
+    // If the socket has been opened correctly, register it in the @c IfaceMgr.
+    if (socket_fd_ != -1) {
+        IfaceMgr& iface_mgr = IfaceMgr::instance();
+        iface_mgr.addExternalSocket(socket_fd_,
+                                    boost::bind(&TestIpc::receiveHandler,
+                                                this));
     }
 }
 
@@ -66,13 +112,44 @@ TestIpc::receiveHandler() {
     pkt_received_ = receive();
 }
 
+/// @brief Test fixture class for @c Dhcp4o6IpcBase.
 class Dhcp4o6IpcBaseTest : public ::testing::Test {
-public:
+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.
+    std::string concatenate(const std::string& prefix,
+                            const uint16_t postfix) const;
+
+    /// @brief Creates an instance of the DHCPv4-query Message option.
+    ///
+    /// @param src Type of the source endpoint. It can be 4 or 6.
+    /// @return Pointer to the instance of the DHCPv4-query Message option.
+    OptionPtr createDHCPv4MsgOption(const int src) const;
+
+    /// @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.
+    void testSendReceive(const uint16_t iterations_num, const int src,
+                         const int dest);
+
 private:
 
+    /// @brief Holds the fake configuration of the interfaces.
     IfaceMgrTestConfig iface_mgr_test_config_;
 
 };
@@ -81,22 +158,110 @@ Dhcp4o6IpcBaseTest::Dhcp4o6IpcBaseTest()
     : iface_mgr_test_config_(true) {
 }
 
-TEST_F(Dhcp4o6IpcBaseTest, basic) {
-    TestIpc ipc4(TEST_PORT, 4);
-    TestIpc ipc6(TEST_PORT, 6);
+std::string
+Dhcp4o6IpcBaseTest::concatenate(const std::string& prefix,
+                                const uint16_t postfix) const {
+    std::ostringstream s;
+    s << prefix << postfix;
+    return (s.str());
+}
+
+OptionPtr
+Dhcp4o6IpcBaseTest::createDHCPv4MsgOption(const int src) const {
+    // Create the DHCPv4 message.
+    Pkt4Ptr pkt(new Pkt4(src == 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(const uint16_t iterations_num,
+                                    const int src,
+                                    const int dest) {
+    // 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());
+
+    // Depnding 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 == 6 ? DHCPV6_DHCPV4_QUERY :
+                         DHCPV6_DHCPV4_RESPONSE);
+
+    // Send the number of messages configured for the test.
+    for (uint16_t i = 1; i <= iterations_num; ++i) {
+        // 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 interation.
+        pkt->setIface(concatenate("eth", i % 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::", i)));
+
+        // Add DHCPv4 Message option to make sure it is conveyed by the IPC.
+        pkt->addOption(createDHCPv4MsgOption(src));
+
+        // 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;
+    }
+
+    // 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());
 
-    ASSERT_NO_THROW(ipc4.open());
-    ASSERT_NO_THROW(ipc6.open());
+        // Check that the interface is correct.
+        EXPECT_EQ(concatenate("eth", i % 2), pkt_received->getIface());
 
-    Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_QUERY, 1234));
-    pkt->setIface("eth0");
+        // Check taht the address conveyed is correct.
+        EXPECT_EQ(concatenate("2001:db8:1::", i),
+                  pkt_received->getRemoteAddr().toText());
 
-    ASSERT_NO_THROW(ipc6.send(pkt));
+        // Check that encapsulated DHCPv4 message has been received.
+        EXPECT_TRUE(pkt_received->getOption(D6O_DHCPV4_MSG));
+    }
+}
+
+// This test verifies that the IPC can transmit messages between the
+// DHCPv6 and DHCPv4 server.
+TEST_F(Dhcp4o6IpcBaseTest, send4To6) {
+    testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V4, ENDPOINT_TYPE_V6);
+}
 
-    ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
-    
-    Pkt6Ptr pkt_received = ipc4.getPktReceived();
-    ASSERT_TRUE(pkt_received);
+// This test verifies taht the IPC can transmit messages between the
+// DHCPv4 and DHCPv6 server.
+TEST_F(Dhcp4o6IpcBaseTest, send6To4) {
+    testSendReceive(TEST_ITERATIONS, ENDPOINT_TYPE_V6, ENDPOINT_TYPE_V4);
 }
 
 } // end of anonymous namespace