]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#260, !120] Beefed up IfaceMgr unit tests
authorThomas Markwalder <tmark@isc.org>
Mon, 12 Nov 2018 18:43:25 +0000 (13:43 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 20 Nov 2018 18:22:59 +0000 (13:22 -0500)
src/lib/dhcp/iface_mgr.*
    IfaceMgr::isReceiverRunning() - new, public convenience method

src/lib/dhcp/tests/iface_mgr_unittest.cc
    Revamped sendReceive<4/6> tests to test both direct and
    indirect packet reception

    TEST_F(IfaceMgrTest, configureDHCPPacketQueueTest4)
    TEST_F(IfaceMgrTest, configureDHCPPacketQueueTest6) - new tests

src/lib/dhcp/iface_mgr.cc
src/lib/dhcp/iface_mgr.h
src/lib/dhcp/tests/iface_mgr_unittest.cc
src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc
src/lib/dhcp/tests/packet_queue_testutils.h

index 8c6ace93fc6299f14ed2997e716aabad4f601990..c56cdd607677d54769d3ba152590fb7282648ba0 100644 (file)
@@ -292,7 +292,7 @@ void IfaceMgr::closeSockets() {
 }
 
 void IfaceMgr::stopDHCPReceiver() {
-    if (receiver_thread_) {
+    if (isReceiverRunning()) {
         terminate_watch_.markReady();
         receiver_thread_->wait();
         receiver_thread_.reset();
@@ -688,7 +688,7 @@ IfaceMgr::openSockets6(const uint16_t port,
 
 void
 IfaceMgr::startDHCPReceiver(const uint16_t family) {
-    if (receiver_thread_) {
+    if (isReceiverRunning()) {
         isc_throw(InvalidOperation, "a receiver thread already exists");
     }
 
@@ -1741,7 +1741,7 @@ IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
 
 bool
 IfaceMgr::configureDHCPPacketQueue(uint16_t family, data::ConstElementPtr queue_control) {
-    if (receiver_thread_) {
+    if (isReceiverRunning()) {
         isc_throw(InvalidOperation, "Cannot reconfigure queueing"
                                     " while receiver thread is running");
     }
index 37c4d1ffe459da65f6b8524d232d7ed2d0ff5027..9d4ffbb5193b1c68539e9ee016838970eca42378 100644 (file)
@@ -1078,6 +1078,11 @@ public:
     /// the packet queue is flushed.
     void stopDHCPReceiver();
 
+    /// @brief Returns true if there is a receiver currently running.
+    bool isReceiverRunning() const {
+        return (receiver_thread_ != 0);
+    }
+
     /// @brief Configures DHCP packet queue
     ///
     /// If the given configuration enables packet queueing, then the
@@ -1094,7 +1099,6 @@ public:
     bool configureDHCPPacketQueue(const uint16_t family,
                                   data::ConstElementPtr queue_control);
 
-
     // don't use private, we need derived classes in tests
 protected:
 
index fb4114f58666499fba9063d6e42f1857cc64cba9..a10fd8a5c366e43927730f7f81d0f1d675e1f7d4 100644 (file)
@@ -447,6 +447,222 @@ public:
         ++errors_count_;
     }
 
+    /// @brief Tests the ability to send and receive DHCPv6 packets
+    ///
+    /// This test calls @r IfaceMgr::configureDHCPqueue, passing int the
+    /// given queue configuration.  It then calls IfaceMgr::startDHCPReceiver
+    /// and verifies whether or not the receive thread has been started as
+    /// expected.  Next it creates a generic DHCPv6 packet and sends it over
+    /// the loop back interface.  It invokes IfaceMgr::receive6 to receive the
+    /// packet sent, and compares to the packets for equality.
+    ///
+    /// @param dhcp_queue_control dhcp-queue-control contents to use for the test
+    /// @param exp_queue_enabled flag that indicates if packet queuing is expected
+    /// to be enabled.
+    void sendReceive6Test(data::ConstElementPtr dhcp_queue_control, bool exp_queue_enabled) {
+        scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+        // Testing socket operation in a portable way is tricky
+        // without interface detection implemented
+        // let's assume that every supported OS have lo interface
+        IOAddress loAddr("::1");
+        int socket1 = 0, socket2 = 0;
+        EXPECT_NO_THROW(
+            socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
+            socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
+        );
+
+        EXPECT_GE(socket1, 0);
+        EXPECT_GE(socket2, 0);
+
+        // Configure packet queueing as desired.
+        bool queue_enabled = false;
+        ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET6, dhcp_queue_control));
+
+        // Verify that we have a queue only if we expected one.
+        ASSERT_EQ(exp_queue_enabled, queue_enabled);
+
+        // Thread should only start when there is a packet queue.
+        ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET6));
+        ASSERT_TRUE(queue_enabled == ifacemgr->isReceiverRunning());
+
+        // If the thread is already running, trying to start it again should fail.
+        if (queue_enabled) {
+            ASSERT_THROW(ifacemgr->startDHCPReceiver(AF_INET6), InvalidOperation);
+            // Should still have one running.
+            ASSERT_TRUE(ifacemgr->isReceiverRunning());
+        }
+
+        // Let's build our DHCPv6 packet.
+        // prepare dummy payload
+        uint8_t data[128];
+        for (uint8_t i = 0; i < 128; i++) {
+            data[i] = i;
+        }
+
+        Pkt6Ptr sendPkt = Pkt6Ptr(new Pkt6(data, 128));
+        sendPkt->repack();
+        sendPkt->setRemotePort(10547);
+        sendPkt->setRemoteAddr(IOAddress("::1"));
+        sendPkt->setIndex(1);
+        sendPkt->setIface(LOOPBACK);
+
+        // Send the packet.
+        EXPECT_EQ(true, ifacemgr->send(sendPkt));
+
+        // Now, let's try and receive it.
+        Pkt6Ptr rcvPkt;
+        rcvPkt = ifacemgr->receive6(10);
+        ASSERT_TRUE(rcvPkt); // received our own packet
+
+        // let's check that we received what was sent
+        ASSERT_EQ(sendPkt->data_.size(), rcvPkt->data_.size());
+        EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
+                            rcvPkt->data_.size()));
+
+        EXPECT_EQ(sendPkt->getRemoteAddr(), rcvPkt->getRemoteAddr());
+
+        // since we opened 2 sockets on the same interface and none of them is multicast,
+        // none is preferred over the other for sending data, so we really should not
+        // assume the one or the other will always be chosen for sending data. Therefore
+        // we should accept both values as source ports.
+        EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
+
+        // Stop the thread.  This should be no harm/no foul if we're not
+        // queueuing.  Either way, we should not have a thread afterwards.
+        ASSERT_NO_THROW(ifacemgr->stopDHCPReceiver());
+        ASSERT_FALSE(ifacemgr->isReceiverRunning());
+    }
+
+
+    /// @brief Tests the ability to send and receive DHCPv4 packets
+    ///
+    /// This test calls @r IfaceMgr::configureDHCPqueue, passing int the
+    /// given queue configuration.  It then calls IfaceMgr::startDHCPReceiver
+    /// and verifies whether or not the receive thread has been started as
+    /// expected.  Next it creates a DISCOVER packet and sends it over
+    /// the loop back interface.  It invokes IfaceMgr::receive4 to receive the
+    /// packet sent, and compares to the packets for equality.
+    ///
+    /// @param dhcp_queue_control dhcp-queue-control contents to use for the test
+    /// @param exp_queue_enabled flag that indicates if packet queuing is expected
+    /// to be enabled.
+    void sendReceive4Test(data::ConstElementPtr dhcp_queue_control, bool exp_queue_enabled) {
+        scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+        // Testing socket operation in a portable way is tricky
+        // without interface detection implemented.
+        // Let's assume that every supported OS has lo interface
+        IOAddress loAddr("127.0.0.1");
+        int socket1 = 0;
+        EXPECT_NO_THROW(
+            socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
+        );
+
+        EXPECT_GE(socket1, 0);
+
+        // Configure packet queueing as desired.
+        bool queue_enabled = false;
+        ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, dhcp_queue_control));
+
+        // Verify that we have a queue only if we expected one.
+        ASSERT_EQ(exp_queue_enabled, queue_enabled);
+
+        // Thread should only start when there is a packet queue.
+        ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET));
+        ASSERT_TRUE(queue_enabled == ifacemgr->isReceiverRunning());
+
+        // If the thread is already running, trying to start it again should fail.
+        if (queue_enabled) {
+            ASSERT_THROW(ifacemgr->startDHCPReceiver(AF_INET), InvalidOperation);
+            // Should still have one running.
+            ASSERT_TRUE(ifacemgr->isReceiverRunning());
+        }
+
+        // Let's construct the packet to send.
+        boost::shared_ptr<Pkt4> sendPkt(new Pkt4(DHCPDISCOVER, 1234) );
+        sendPkt->setLocalAddr(IOAddress("127.0.0.1"));
+        sendPkt->setLocalPort(DHCP4_SERVER_PORT + 10000 + 1);
+        sendPkt->setRemotePort(DHCP4_SERVER_PORT + 10000);
+        sendPkt->setRemoteAddr(IOAddress("127.0.0.1"));
+        sendPkt->setIndex(1);
+        sendPkt->setIface(string(LOOPBACK));
+        sendPkt->setHops(6);
+        sendPkt->setSecs(42);
+        sendPkt->setCiaddr(IOAddress("192.0.2.1"));
+        sendPkt->setSiaddr(IOAddress("192.0.2.2"));
+        sendPkt->setYiaddr(IOAddress("192.0.2.3"));
+        sendPkt->setGiaddr(IOAddress("192.0.2.4"));
+
+        // Unpack() now checks if mandatory DHCP_MESSAGE_TYPE is present.
+        // Workarounds (creating DHCP Message Type Option by hand) are no longer
+        // needed as setDhcpType() is called in constructor.
+
+        uint8_t sname[] = "That's just a string that will act as SNAME";
+        sendPkt->setSname(sname, strlen((const char*)sname));
+        uint8_t file[] = "/another/string/that/acts/as/a/file_name.txt";
+        sendPkt->setFile(file, strlen((const char*)file));
+
+        ASSERT_NO_THROW(
+            sendPkt->pack();
+        );
+
+        // OK, Send the PACKET!
+        EXPECT_NO_THROW(ifacemgr->send(sendPkt));
+
+        // Now let's try and receive it.
+        boost::shared_ptr<Pkt4> rcvPkt;
+        ASSERT_NO_THROW(rcvPkt = ifacemgr->receive4(10));
+        ASSERT_TRUE(rcvPkt); // received our own packet
+        ASSERT_NO_THROW(
+            rcvPkt->unpack();
+        );
+
+        // let's check that we received what was sent
+        EXPECT_EQ(sendPkt->len(), rcvPkt->len());
+        EXPECT_EQ("127.0.0.1", rcvPkt->getRemoteAddr().toText());
+        EXPECT_EQ(sendPkt->getRemotePort(), rcvPkt->getLocalPort());
+        EXPECT_EQ(sendPkt->getHops(), rcvPkt->getHops());
+        EXPECT_EQ(sendPkt->getOp(),   rcvPkt->getOp());
+        EXPECT_EQ(sendPkt->getSecs(), rcvPkt->getSecs());
+        EXPECT_EQ(sendPkt->getFlags(), rcvPkt->getFlags());
+        EXPECT_EQ(sendPkt->getCiaddr(), rcvPkt->getCiaddr());
+        EXPECT_EQ(sendPkt->getSiaddr(), rcvPkt->getSiaddr());
+        EXPECT_EQ(sendPkt->getYiaddr(), rcvPkt->getYiaddr());
+        EXPECT_EQ(sendPkt->getGiaddr(), rcvPkt->getGiaddr());
+        EXPECT_EQ(sendPkt->getTransid(), rcvPkt->getTransid());
+        EXPECT_TRUE(sendPkt->getSname() == rcvPkt->getSname());
+        EXPECT_TRUE(sendPkt->getFile() == rcvPkt->getFile());
+        EXPECT_EQ(sendPkt->getHtype(), rcvPkt->getHtype());
+        EXPECT_EQ(sendPkt->getHlen(), rcvPkt->getHlen());
+
+        // since we opened 2 sockets on the same interface and none of them is multicast,
+        // none is preferred over the other for sending data, so we really should not
+        // assume the one or the other will always be chosen for sending data. We should
+        // skip checking source port of sent address.
+
+        // Close the socket. Further we will test if errors are reported
+        // properly on attempt to use closed socket.
+        close(socket1);
+
+        // @todo Closing the socket does NOT cause a read error out of the
+        // receiveDHCP<X>Packets() select.  Apparently this is because the
+        // thread is already inside the select when the socket is closed,
+        // and (at least under Centos 7.5), this does not interrupt the
+        // select.  For now, we'll only test this for direct receive.
+        if (!queue_enabled) {
+            EXPECT_THROW(ifacemgr->receive4(10), SocketReadError);
+        }
+
+        // Verify write fails.
+        EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
+
+        // Stop the thread.  This should be no harm/no foul if we're not
+        // queueuing.  Either way, we should not have a thread afterwards.
+        ASSERT_NO_THROW(ifacemgr->stopDHCPReceiver());
+        ASSERT_FALSE(ifacemgr->isReceiverRunning());
+    }
+
     /// Holds the invocation counter for ifaceMgrErrorHandler.
     int errors_count_;
 
@@ -1060,169 +1276,42 @@ TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
     close(socket2);
 }
 
+// Verifies that basic DHPCv6 packet send and receive operates
+// in either direct or indirect mode.
 TEST_F(IfaceMgrTest, sendReceive6) {
+    data::ElementPtr queue_control;
 
-    // testing socket operation in a portable way is tricky
-    // without interface detection implemented
-
-    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
-
-    // let's assume that every supported OS have lo interface
-    IOAddress loAddr("::1");
-    int socket1 = 0, socket2 = 0;
-    EXPECT_NO_THROW(
-        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
-        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
-    );
-
-    EXPECT_GE(socket1, 0);
-    EXPECT_GE(socket2, 0);
-
-    ifacemgr->startDHCPReceiver(AF_INET6);
-
-    // prepare dummy payload
-    uint8_t data[128];
-    for (uint8_t i = 0; i < 128; i++) {
-        data[i] = i;
-    }
-    Pkt6Ptr sendPkt = Pkt6Ptr(new Pkt6(data, 128));
-
-    sendPkt->repack();
-
-    sendPkt->setRemotePort(10547);
-    sendPkt->setRemoteAddr(IOAddress("::1"));
-    sendPkt->setIndex(1);
-    sendPkt->setIface(LOOPBACK);
-
-    Pkt6Ptr rcvPkt;
-
-    EXPECT_EQ(true, ifacemgr->send(sendPkt));
-
-    rcvPkt = ifacemgr->receive6(10);
-
-    ASSERT_TRUE(rcvPkt); // received our own packet
-
-    // let's check that we received what was sent
-    ASSERT_EQ(sendPkt->data_.size(), rcvPkt->data_.size());
-    EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
-                        rcvPkt->data_.size()));
+    // Given an empty pointer, queueing should be disabled.
+    // This should do direct reception.
+    sendReceive6Test(queue_control, false);
 
-    EXPECT_EQ(sendPkt->getRemoteAddr(), rcvPkt->getRemoteAddr());
+    // Now let's populate queue control.
+    queue_control = makeQueueConfig("kea-ring6", 500, false);
+    // With queueing disabled, we should use direct reception.
+    sendReceive6Test(queue_control, false);
 
-    // since we opened 2 sockets on the same interface and none of them is multicast,
-    // none is preferred over the other for sending data, so we really should not
-    // assume the one or the other will always be chosen for sending data. Therefore
-    // we should accept both values as source ports.
-    EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
-
-    ifacemgr->stopDHCPReceiver();
+    // Queuing enabled, indirection reception should work.
+    queue_control = makeQueueConfig("kea-ring6", 500, true);
+    sendReceive6Test(queue_control, true);
 }
 
+// Verifies that basic DHPCv4 packet send and receive operates
+// in either direct or indirect mode.
 TEST_F(IfaceMgrTest, sendReceive4) {
+    data::ElementPtr queue_control;
 
-    // testing socket operation in a portable way is tricky
-    // without interface detection implemented
+    // Given an empty pointer, queueing should be disabled.
+    // This should do direct reception.
+    sendReceive4Test(queue_control, false);
 
-    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+    // Now let's populate queue control.
+    queue_control = makeQueueConfig("kea-ring4", 500, false);
+    // With queueing disabled, we should use direct reception.
+    sendReceive4Test(queue_control, false);
 
-    // let's assume that every supported OS have lo interface
-    IOAddress loAddr("127.0.0.1");
-    int socket1 = 0;
-    EXPECT_NO_THROW(
-        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
-    );
-
-    EXPECT_GE(socket1, 0);
-
-#if 0
-    ifacemgr->startDHCPReceiver(AF_INET);
-#endif
-
-    boost::shared_ptr<Pkt4> sendPkt(new Pkt4(DHCPDISCOVER, 1234) );
-
-    sendPkt->setLocalAddr(IOAddress("127.0.0.1"));
-
-    sendPkt->setLocalPort(DHCP4_SERVER_PORT + 10000 + 1);
-    sendPkt->setRemotePort(DHCP4_SERVER_PORT + 10000);
-    sendPkt->setRemoteAddr(IOAddress("127.0.0.1"));
-    sendPkt->setIndex(1);
-    sendPkt->setIface(string(LOOPBACK));
-    sendPkt->setHops(6);
-    sendPkt->setSecs(42);
-    sendPkt->setCiaddr(IOAddress("192.0.2.1"));
-    sendPkt->setSiaddr(IOAddress("192.0.2.2"));
-    sendPkt->setYiaddr(IOAddress("192.0.2.3"));
-    sendPkt->setGiaddr(IOAddress("192.0.2.4"));
-
-    // Unpack() now checks if mandatory DHCP_MESSAGE_TYPE is present.
-    // Workarounds (creating DHCP Message Type Option by hand) are no longer
-    // needed as setDhcpType() is called in constructor.
-
-    uint8_t sname[] = "That's just a string that will act as SNAME";
-    sendPkt->setSname(sname, strlen((const char*)sname));
-    uint8_t file[] = "/another/string/that/acts/as/a/file_name.txt";
-    sendPkt->setFile(file, strlen((const char*)file));
-
-    ASSERT_NO_THROW(
-        sendPkt->pack();
-    );
-
-    boost::shared_ptr<Pkt4> rcvPkt;
-
-    EXPECT_NO_THROW(ifacemgr->send(sendPkt));
-
-    ASSERT_NO_THROW(rcvPkt = ifacemgr->receive4(10));
-    ASSERT_TRUE(rcvPkt); // received our own packet
-
-    ASSERT_NO_THROW(
-        rcvPkt->unpack();
-    );
-
-    // let's check that we received what was sent
-    EXPECT_EQ(sendPkt->len(), rcvPkt->len());
-
-    EXPECT_EQ("127.0.0.1", rcvPkt->getRemoteAddr().toText());
-    EXPECT_EQ(sendPkt->getRemotePort(), rcvPkt->getLocalPort());
-
-    // now let's check content
-    EXPECT_EQ(sendPkt->getHops(), rcvPkt->getHops());
-    EXPECT_EQ(sendPkt->getOp(),   rcvPkt->getOp());
-    EXPECT_EQ(sendPkt->getSecs(), rcvPkt->getSecs());
-    EXPECT_EQ(sendPkt->getFlags(), rcvPkt->getFlags());
-    EXPECT_EQ(sendPkt->getCiaddr(), rcvPkt->getCiaddr());
-    EXPECT_EQ(sendPkt->getSiaddr(), rcvPkt->getSiaddr());
-    EXPECT_EQ(sendPkt->getYiaddr(), rcvPkt->getYiaddr());
-    EXPECT_EQ(sendPkt->getGiaddr(), rcvPkt->getGiaddr());
-    EXPECT_EQ(sendPkt->getTransid(), rcvPkt->getTransid());
-    EXPECT_TRUE(sendPkt->getSname() == rcvPkt->getSname());
-    EXPECT_TRUE(sendPkt->getFile() == rcvPkt->getFile());
-    EXPECT_EQ(sendPkt->getHtype(), rcvPkt->getHtype());
-    EXPECT_EQ(sendPkt->getHlen(), rcvPkt->getHlen());
-
-    // since we opened 2 sockets on the same interface and none of them is multicast,
-    // none is preferred over the other for sending data, so we really should not
-    // assume the one or the other will always be chosen for sending data. We should
-    // skip checking source port of sent address.
-
-
-    // Close the socket. Further we will test if errors are reported
-    // properly on attempt to use closed socket.
-    close(socket1);
-
-#if 0
-    // @todo Closing the socket does NOT cause a read error out of the
-    // receiveDHCP<X>Packets() select.  Apparently this is because the
-    // thread is already inside the select when the socket is closed,
-    // and (at least under Centos 7.5), this does not interrupt the
-    // select.
-    EXPECT_THROW(ifacemgr->receive4(10), SocketReadError);
-#endif
-
-    EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
-
-#if 0
-    ifacemgr->stopDHCPReceiver();
-#endif
+    // Queuing enabled, indirection reception should work.
+    queue_control = makeQueueConfig("kea-ring4", 500, true);
+    sendReceive4Test(queue_control, true);
 }
 
 // Verifies that it is possible to set custom packet filter object
@@ -2960,5 +3049,143 @@ TEST_F(IfaceMgrTest, DISABLED_getSocket) {
     ifacemgr->closeSockets();
 }
 
+// Verifies DHCPv4 behavior of configureDHCPPacketQueue()
+TEST_F(IfaceMgrTest, configureDHCPPacketQueueTest4) {
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // First let's make sure there is no queue and no thread.
+    ASSERT_FALSE(ifacemgr->getPacketQueue4());
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    bool queue_enabled = false;
+    // Given an empty pointer, we should default to no queue.
+    data::ConstElementPtr queue_control;
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, queue_control));
+    EXPECT_FALSE(queue_enabled);
+    EXPECT_FALSE(ifacemgr->getPacketQueue4());
+    // configureDHCPPacketQueue() should never start the thread.
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Verify that calling startDHCPReceiver with no queue, does NOT start the thread.
+    ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET));
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Now let's try with a populated queue control, but with enable-queue = false.
+    queue_control = makeQueueConfig("kea-ring4", 500, false);
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, queue_control));
+    EXPECT_FALSE(queue_enabled);
+    EXPECT_FALSE(ifacemgr->getPacketQueue4());
+    // configureDHCPPacketQueue() should never start the thread.
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Now let's enable the queue.
+    queue_control = makeQueueConfig("kea-ring4", 500, true);
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, queue_control));
+    ASSERT_TRUE(queue_enabled);
+    // Verify we have correctly created the queue.
+    checkInfo(ifacemgr->getPacketQueue4(),
+              "{ \"capacity\": 500, \"queue-type\": \"kea-ring4\", \"size\": 0 }");
+    // configureDHCPPacketQueue() should never start the thread.
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Calling startDHCPReceiver with a queue, should start the thread.
+    ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET));
+    ASSERT_TRUE(ifacemgr->isReceiverRunning());
+
+    // Verify that calling startDHCPReceiver when the thread is running, throws.
+    ASSERT_THROW(ifacemgr->startDHCPReceiver(AF_INET), InvalidOperation);
+
+    // Create a disabled config.
+    queue_control = makeQueueConfig("kea-ring4", 500, false);
+
+    // Trying to reconfigure with a running thread should throw.
+    ASSERT_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, queue_control),
+                 InvalidOperation);
+
+    // We should still have our queue and the thread should still be running.
+    EXPECT_TRUE(ifacemgr->getPacketQueue4());
+    ASSERT_TRUE(ifacemgr->isReceiverRunning());
+
+    // Now let's stop stop the thread.
+    ASSERT_NO_THROW(ifacemgr->stopDHCPReceiver());
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+    // Stopping the thread should not destroy the queue.
+    ASSERT_TRUE(ifacemgr->getPacketQueue4());
+
+    // Reconfigure with the queue turned off.  We should have neither queue nor thread.
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, queue_control));
+    EXPECT_FALSE(ifacemgr->getPacketQueue4());
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+}
+
+// Verifies DHCPv6 behavior of configureDHCPPacketQueue()
+TEST_F(IfaceMgrTest, configureDHCPPacketQueueTest6) {
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // First let's make sure there is no queue and no thread.
+    ASSERT_FALSE(ifacemgr->getPacketQueue6());
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    bool queue_enabled = false;
+    // Given an empty pointer, we should default to no queue.
+    data::ConstElementPtr queue_control;
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET, queue_control));
+    EXPECT_FALSE(queue_enabled);
+    EXPECT_FALSE(ifacemgr->getPacketQueue6());
+    // configureDHCPPacketQueue() should never start the thread.
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Verify that calling startDHCPReceiver with no queue, does NOT start the thread.
+    ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET));
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Now let's try with a populated queue control, but with enable-queue = false.
+    queue_control = makeQueueConfig("kea-ring6", 500, false);
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET6, queue_control));
+    EXPECT_FALSE(queue_enabled);
+    EXPECT_FALSE(ifacemgr->getPacketQueue6());
+    // configureDHCPPacketQueue() should never start the thread.
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Now let's enable the queue.
+    queue_control = makeQueueConfig("kea-ring6", 500, true);
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET6, queue_control));
+    ASSERT_TRUE(queue_enabled);
+    // Verify we have correctly created the queue.
+    checkInfo(ifacemgr->getPacketQueue6(),
+              "{ \"capacity\": 500, \"queue-type\": \"kea-ring6\", \"size\": 0 }");
+    // configureDHCPPacketQueue() should never start the thread.
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+
+    // Calling startDHCPReceiver with a queue, should start the thread.
+    ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET6));
+    ASSERT_TRUE(ifacemgr->isReceiverRunning());
+
+    // Verify that calling startDHCPReceiver when the thread is running, throws.
+    ASSERT_THROW(ifacemgr->startDHCPReceiver(AF_INET6), InvalidOperation);
+
+    // Create a disabled config.
+    queue_control = makeQueueConfig("kea-ring6", 500, false);
+
+    // Trying to reconfigure with a running thread should throw.
+    ASSERT_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET6, queue_control),
+                 InvalidOperation);
+
+    // We should still have our queue and the thread should still be running.
+    EXPECT_TRUE(ifacemgr->getPacketQueue6());
+    ASSERT_TRUE(ifacemgr->isReceiverRunning());
+
+    // Now let's stop stop the thread.
+    ASSERT_NO_THROW(ifacemgr->stopDHCPReceiver());
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+    // Stopping the thread should not destroy the queue.
+    ASSERT_TRUE(ifacemgr->getPacketQueue6());
+
+    // Reconfigure with the queue turned off.  We should have neither queue nor thread.
+    ASSERT_NO_THROW(queue_enabled = ifacemgr->configureDHCPPacketQueue(AF_INET6, queue_control));
+    EXPECT_FALSE(ifacemgr->getPacketQueue6());
+    ASSERT_FALSE(ifacemgr->isReceiverRunning());
+}
+
 
 }
index 34f7ea4ad226ed0f6c547e5b4befab62ed3d5174..e0cb2ece583b67934de8081cbb7216c8cb589684 100644 (file)
@@ -21,9 +21,11 @@ using namespace isc::dhcp::test;
 ///
 /// @param queue_type logical name of the queue implemenation type
 /// @param capacity maximum queue capacity
+/// @param enable_queue bool value to ascribe to the 'enable-queue' parameter, defaults to true
 data::ElementPtr
-isc::dhcp::test::makeQueueConfig(const std::string& queue_type, size_t capacity) {
+isc::dhcp::test::makeQueueConfig(const std::string& queue_type, size_t capacity, bool enable_queue /* = true */) {
     data::ElementPtr config = data::Element::createMap();
+    config->set("enable-queue", data::Element::create(enable_queue));
     config->set("queue-type", data::Element::create(queue_type));
     config->set("capacity", data::Element::create(static_cast<long int>(capacity)));
     return (config);
index 20043783727b75e47a1e9978da1b535d6f7a3ff2..44a926ae8a499234045c593c7b00c7b17ac3cdd7 100644 (file)
@@ -46,7 +46,7 @@ template<typename PacketQueuePtrType> void checkIntStat(PacketQueuePtrType queue
     EXPECT_EQ(exp_value, value) << "stat: " << name << " is wrong" << std::endl;;
 }
 
-extern data::ElementPtr makeQueueConfig(const std::string& queue_type, size_t capacity);
+extern data::ElementPtr makeQueueConfig(const std::string& queue_type, size_t capacity, bool enable_queue=true);
 
 }; // end of namespace isc::dhcp::test
 }; // end of namespace isc::dhcp