From: Thomas Markwalder Date: Mon, 12 Nov 2018 18:43:25 +0000 (-0500) Subject: [#260, !120] Beefed up IfaceMgr unit tests X-Git-Tag: 204-move-models-base~4^2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=803d464cafee29e40e580ed760d5cf4d63035371;p=thirdparty%2Fkea.git [#260, !120] Beefed up IfaceMgr unit tests 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 --- diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 8c6ace93fc..c56cdd6076 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -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"); } diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 37c4d1ffe4..9d4ffbb519 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -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: diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index fb4114f586..a10fd8a5c3 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -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 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 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 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 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 + // receiveDHCPPackets() 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 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 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 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 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 - // receiveDHCPPackets() 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 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 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()); +} + } diff --git a/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc b/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc index 34f7ea4ad2..e0cb2ece58 100644 --- a/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc @@ -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(capacity))); return (config); diff --git a/src/lib/dhcp/tests/packet_queue_testutils.h b/src/lib/dhcp/tests/packet_queue_testutils.h index 2004378372..44a926ae8a 100644 --- a/src/lib/dhcp/tests/packet_queue_testutils.h +++ b/src/lib/dhcp/tests/packet_queue_testutils.h @@ -46,7 +46,7 @@ template 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