From: Razvan Becheriu Date: Tue, 8 Dec 2020 20:37:50 +0000 (+0200) Subject: [#991] added multi-threading unittests for v4 server X-Git-Tag: Kea-1.9.3~59 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c9742634e1efd985db5884e92ee83dafa489a13e;p=thirdparty%2Fkea.git [#991] added multi-threading unittests for v4 server --- diff --git a/src/bin/dhcp4/tests/dhcp4_client.cc b/src/bin/dhcp4/tests/dhcp4_client.cc index 56813aae5f..a92d9e75f2 100644 --- a/src/bin/dhcp4/tests/dhcp4_client.cc +++ b/src/bin/dhcp4/tests/dhcp4_client.cc @@ -12,11 +12,13 @@ #include #include #include +#include #include #include #include using namespace isc::asiolink; +using namespace isc::util; namespace isc { namespace dhcp { @@ -497,12 +499,10 @@ Dhcp4Client::requestOptions(const uint8_t option1, const uint8_t option2, Pkt4Ptr Dhcp4Client::receiveOneMsg() { - // Return empty pointer if server hasn't responded. - if (srv_->fake_sent_.empty()) { + Pkt4Ptr msg = srv_->receiveOneMsg(); + if (!msg) { return (Pkt4Ptr()); } - Pkt4Ptr msg = srv_->fake_sent_.front(); - srv_->fake_sent_.pop_front(); // Copy the original message to simulate reception over the wire. msg->pack(); @@ -562,6 +562,12 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) { } catch (...) { // Suppress errors, as the DHCPv4 server does. } + + // make sure the server processed all packets in MT. + while (isc::util::MultiThreadingMgr::instance().getThreadPool().count()) { + usleep(100); + } + isc::util::MultiThreadingCriticalSection cs; } void diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index 8f9b131fc8..59a0ecdc26 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ using namespace std; using namespace isc::asiolink; using namespace isc::data; +using namespace isc::util; namespace isc { namespace dhcp { @@ -55,7 +57,7 @@ BaseServerTest::~BaseServerTest() { } Dhcpv4SrvTest::Dhcpv4SrvTest() -:rcode_(-1), srv_(0) { + : rcode_(-1), srv_(0), multi_threading_(false) { // Wipe any existing statistics isc::stats::StatsMgr::instance().removeAll(); @@ -455,6 +457,7 @@ Dhcpv4SrvTest::TearDown() { << ex.what(); } + setMultiThreading(false); } void @@ -620,11 +623,12 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv, const bool commit, const bool open_sockets) { + MultiThreadingCriticalSection cs; ConstElementPtr json; try { json = parseJSON(config); } catch (const std::exception& ex){ - // Fatal falure on parsing error + // Fatal failure on parsing error FAIL() << "parsing failure:" << "config:" << config << std::endl << "error: " << ex.what(); @@ -635,6 +639,9 @@ Dhcpv4SrvTest::configure(const std::string& config, // Disable the re-detect flag disableIfacesReDetect(json); + // Set up multi-threading + configureMultiThreading(multi_threading_, json); + // Configure the server and make sure the config is accepted EXPECT_NO_THROW(status = configureDhcp4Server(srv, json)); ASSERT_TRUE(status); @@ -649,6 +656,13 @@ Dhcpv4SrvTest::configure(const std::string& config, cfg_db->createManagers(); } ); + try { + CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading()); + } catch (const std::exception& ex) { + ADD_FAILURE() << "Error applying multi threading settings: " + << ex.what(); + } + if (commit) { CfgMgr::instance().commit(); } @@ -705,7 +719,7 @@ Dhcpv4SrvTest::configureWithStatus(const std::string& config, NakedDhcpv4Srv& sr } return (std::make_pair(rcode, comment->stringValue())); - } +} Dhcpv4Exchange Dhcpv4SrvTest::createExchange(const Pkt4Ptr& query) { diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index c0f99253d6..533f0a181c 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -148,6 +149,13 @@ public: return (pkt); } + // make sure the server processed all packets in MT. + while (isc::util::MultiThreadingMgr::instance().getThreadPool().count()) { + usleep(100); + } + { + isc::util::MultiThreadingCriticalSection cs; + } // If not, just trigger shutdown and return immediately shutdown(); return (Pkt4Ptr()); @@ -158,7 +166,31 @@ public: /// Pretend to send a packet, but instead just store it in fake_send_ list /// where test can later inspect server's response. virtual void sendPacket(const Pkt4Ptr& pkt) { - fake_sent_.push_back(pkt); + if (isc::util::MultiThreadingMgr::instance().getMode()) { + std::lock_guard lk(mutex_); + fake_sent_.push_back(pkt); + } else { + fake_sent_.push_back(pkt); + } + } + + Pkt4Ptr receiveOneMsg() { + if (isc::util::MultiThreadingMgr::instance().getMode()) { + std::lock_guard lk(mutex_); + return (receiveOneMsgInternal()); + } else { + return (receiveOneMsgInternal()); + } + } + + Pkt4Ptr receiveOneMsgInternal() { + // Return empty pointer if server hasn't responded. + if (fake_sent_.empty()) { + return (Pkt4Ptr()); + } + Pkt4Ptr msg = fake_sent_.front(); + fake_sent_.pop_front(); + return (msg); } /// @brief adds a packet to fake receive queue @@ -207,6 +239,7 @@ public: /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods. std::list fake_received_; + /// @brief packets we pretend to send std::list fake_sent_; using Dhcpv4Srv::adjustIfaceData; @@ -232,6 +265,9 @@ public: using Dhcpv4Srv::alloc_engine_; using Dhcpv4Srv::server_port_; using Dhcpv4Srv::client_port_; + + /// @brief Mutex to protect the packet buffers. + std::mutex mutex_; }; // We need to pass one reference to the Dhcp4Client, which is defined in @@ -268,6 +304,17 @@ public: SHOULD_FAIL // fail = reject the decline }; + class Dhcpv4SrvMTTestGuard { + public: + Dhcpv4SrvMTTestGuard(Dhcpv4SrvTest& test, bool mt_enabled) : test_(test) { + test_.setMultiThreading(mt_enabled); + } + ~Dhcpv4SrvMTTestGuard() { + test_.setMultiThreading(false); + } + Dhcpv4SrvTest& test_; + }; + /// @brief Constructor /// /// Initializes common objects used in many tests. @@ -512,9 +559,18 @@ public: const std::string& client_id_2, ExpectedResult expected_result); + /// @brief Checks if received relay agent info option is echoed back to the + /// client + void relayAgentInfoEcho(); + /// @brief This function cleans up after the test. virtual void TearDown(); + /// @brief Set multi-threading mode. + void setMultiThreading(bool enabled) { + multi_threading_ = enabled; + } + /// @brief A subnet used in most tests Subnet4Ptr subnet_; @@ -524,12 +580,17 @@ public: /// @brief A client-id used in most tests ClientIdPtr client_id_; + /// @brief Return code int rcode_; + /// @brief Comment isc::data::ConstElementPtr comment_; /// @brief Server object under test. NakedDhcpv4Srv srv_; + + /// @brief The multi-threading flag + bool multi_threading_; }; /// @brief Patch the server config to add interface-config/re-detect=false @@ -544,13 +605,35 @@ disableIfacesReDetect(isc::data::ConstElementPtr json) { } } +/// @brief Patch the server config to add multi-threading/enable-multi-threading +/// @param json the server config +inline void +configureMultiThreading(bool enabled, isc::data::ConstElementPtr json) { + isc::data::ConstElementPtr multi_threading = json->get("multi-threading"); + if (!multi_threading) { + isc::data::ElementPtr mutable_cfg = + boost::const_pointer_cast(json); + multi_threading = isc::data::Element::createMap(); + mutable_cfg->set("multi-threading", multi_threading); + } + + isc::data::ElementPtr mutable_cfg = + boost::const_pointer_cast(multi_threading); + if (enabled) { + mutable_cfg->set("enable-multi-threading", isc::data::Element::create(true)); + mutable_cfg->set("thread-pool-size", isc::data::Element::create(4)); + mutable_cfg->set("packet-queue-size", isc::data::Element::create(4)); + } else { + mutable_cfg->set("enable-multi-threading", isc::data::Element::create(false)); + } +} + /// @brief Runs parser in JSON mode, useful for parser testing /// /// @param in string to be parsed /// @return ElementPtr structure representing parsed JSON inline isc::data::ElementPtr -parseJSON(const std::string& in) -{ +parseJSON(const std::string& in) { isc::dhcp::Parser4Context ctx; return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_JSON)); } @@ -564,8 +647,7 @@ parseJSON(const std::string& in) /// @param verbose display the exception message when it fails /// @return ElementPtr structure representing parsed JSON inline isc::data::ElementPtr -parseDHCP4(const std::string& in, bool verbose = false) -{ +parseDHCP4(const std::string& in, bool verbose = false) { try { isc::dhcp::Parser4Context ctx; isc::data::ElementPtr json; @@ -589,8 +671,7 @@ parseDHCP4(const std::string& in, bool verbose = false) /// @param verbose display the exception message when it fails /// @return ElementPtr structure representing parsed JSON inline isc::data::ElementPtr -parseOPTION_DEFS(const std::string& in, bool verbose = false) -{ +parseOPTION_DEFS(const std::string& in, bool verbose = false) { try { isc::dhcp::Parser4Context ctx; return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_OPTION_DEFS)); @@ -603,8 +684,8 @@ parseOPTION_DEFS(const std::string& in, bool verbose = false) } } -}; // end of isc::dhcp::test namespace -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // end of isc::dhcp::test namespace +} // end of isc::dhcp namespace +} // end of isc namespace #endif // DHCP4_TEST_UTILS_H diff --git a/src/bin/dhcp4/tests/direct_client_unittest.cc b/src/bin/dhcp4/tests/direct_client_unittest.cc index dedb3b9386..3d98e4ec7e 100644 --- a/src/bin/dhcp4/tests/direct_client_unittest.cc +++ b/src/bin/dhcp4/tests/direct_client_unittest.cc @@ -104,6 +104,35 @@ public: const std::string& iface, const uint32_t ifindex); + /// @brief This test checks that the message from directly connected client + /// is processed and that client is offered IPv4 address from the subnet + /// which is suitable for the local interface on which the client's message + /// is received. This test uses two subnets, with two active interfaces + /// which IP addresses belong to these subnets. The address offered to the + /// client which message has been sent over eth0 should belong to a + /// different subnet than the address offered for the client sending its + /// message via eth1. + void twoSubnets(); + + /// @brief This test checks that server selects a subnet when receives a + /// message through an interface for which the subnet has been configured. + /// This interface has IPv4 address assigned which belongs to this subnet. + /// This test also verifies that when the message is received through the + /// interface for which there is no suitable subnet, the message is + /// discarded. + void oneSubnet(); + + /// @brief This test verifies that the server uses ciaddr to select a subnet + /// for a client which renews its lease. + void renew(); + + /// This test verifies that when a client in the Rebinding state broadcasts + /// a Request message through an interface for which a subnet is configured, + /// the server responds to this Request. It also verifies that when such a + /// Request is sent through the interface for which there is no subnet + /// configured the client's message is discarded. + void rebind(); + /// @brief classes the client belongs to /// /// This is empty in most cases, but it is needed as a parameter for all @@ -133,7 +162,6 @@ DirectClientTest::configureSubnet(const std::string& prefix) { "\"valid-lifetime\": 4000 }"; configure(config.str()); - } void @@ -197,15 +225,8 @@ DirectClientTest::createClientMessage(const Pkt4Ptr& msg, return (received); } -// This test checks that the message from directly connected client -// is processed and that client is offered IPv4 address from the subnet which -// is suitable for the local interface on which the client's message is -// received. This test uses two subnets, with two active interfaces which IP -// addresses belong to these subnets. The address offered to the client -// which message has been sent over eth0 should belong to a different -// subnet than the address offered for the client sending its message -// via eth1. -TEST_F(DirectClientTest, twoSubnets) { +void +DirectClientTest::twoSubnets() { // Configure IfaceMgr with fake interfaces lo, eth0 and eth1. IfaceMgrTestConfig iface_config(true); // After creating interfaces we have to open sockets as it is required @@ -227,40 +248,55 @@ TEST_F(DirectClientTest, twoSubnets) { // Check that the server did send responses. ASSERT_EQ(2, srv_.fake_sent_.size()); - // Make sure that we received a response. - Pkt4Ptr response = srv_.fake_sent_.front(); - ASSERT_TRUE(response); - srv_.fake_sent_.pop_front(); + // In multi-threading responses can be received out of order. + Pkt4Ptr offer; + Pkt4Ptr ack; + + while (srv_.fake_sent_.size()) { + // Make sure that we received a response. + Pkt4Ptr response = srv_.fake_sent_.front(); + ASSERT_TRUE(response); + srv_.fake_sent_.pop_front(); + + if (response->getType() == DHCPOFFER) { + offer = response; + } else if (response->getType() == DHCPACK) { + ack = response; + } + } // Client should get an Offer (not a NAK). - ASSERT_EQ(DHCPOFFER, response->getType()); + ASSERT_TRUE(offer); + + // Client should get an Ack (not a NAK). + ASSERT_TRUE(ack); + // Check that the offered address belongs to the suitable subnet. Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()-> - getCfgSubnets4()->selectSubnet(response->getYiaddr()); + getCfgSubnets4()->selectSubnet(offer->getYiaddr()); ASSERT_TRUE(subnet); EXPECT_EQ("10.0.0.0", subnet->get().first.toText()); - // A client that sent Request over the other interface should get Ack. - response = srv_.fake_sent_.front(); - ASSERT_TRUE(response); - // Client should get an Ack (not a NAK). - ASSERT_EQ(DHCPACK, response->getType()); // Check that the offered address belongs to the suitable subnet. subnet = CfgMgr::instance().getCurrentCfg()-> - getCfgSubnets4()->selectSubnet(response->getYiaddr()); + getCfgSubnets4()->selectSubnet(ack->getYiaddr()); ASSERT_TRUE(subnet); EXPECT_EQ("192.0.2.0", subnet->get().first.toText()); +} +TEST_F(DirectClientTest, twoSubnets) { + Dhcpv4SrvMTTestGuard guard(*this, false); + twoSubnets(); } -// This test checks that server selects a subnet when receives a message -// through an interface for which the subnet has been configured. This -// interface has IPv4 address assigned which belongs to this subnet. -// This test also verifies that when the message is received through -// the interface for which there is no suitable subnet, the message -// is discarded. -TEST_F(DirectClientTest, oneSubnet) { +TEST_F(DirectClientTest, twoSubnetsMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + twoSubnets(); +} + +void +DirectClientTest::oneSubnet() { // Configure IfaceMgr with fake interfaces lo, eth0 and eth1. IfaceMgrTestConfig iface_config(true); // After creating interfaces we have to open sockets as it is required @@ -298,12 +334,20 @@ TEST_F(DirectClientTest, oneSubnet) { getCfgSubnets4()->selectSubnet(response->getYiaddr()); ASSERT_TRUE(subnet); EXPECT_EQ("10.0.0.0", subnet->get().first.toText()); +} +TEST_F(DirectClientTest, oneSubnet) { + Dhcpv4SrvMTTestGuard guard(*this, false); + oneSubnet(); } -// This test verifies that the server uses ciaddr to select a subnet for a -// client which renews its lease. -TEST_F(DirectClientTest, renew) { +TEST_F(DirectClientTest, oneSubnetMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + oneSubnet(); +} + +void +DirectClientTest::renew() { // Configure IfaceMgr with fake interfaces lo, eth0 and eth1. IfaceMgrTestConfig iface_config(true); // After creating interfaces we have to open sockets as it is required @@ -330,12 +374,18 @@ TEST_F(DirectClientTest, renew) { EXPECT_EQ("10.0.0.10", client.config_.lease_.addr_.toText()); } -// This test verifies that when a client in the Rebinding state broadcasts -// a Request message through an interface for which a subnet is configured, -// the server responds to this Request. It also verifies that when such a -// Request is sent through the interface for which there is no subnet configured -// the client's message is discarded. -TEST_F(DirectClientTest, rebind) { +TEST_F(DirectClientTest, renew) { + Dhcpv4SrvMTTestGuard guard(*this, false); + renew(); +} + +TEST_F(DirectClientTest, renewMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + renew(); +} + +void +DirectClientTest::rebind() { // Configure IfaceMgr with fake interfaces lo, eth0 and eth1. IfaceMgrTestConfig iface_config(true); // After creating interfaces we have to open sockets as it is required @@ -372,4 +422,14 @@ TEST_F(DirectClientTest, rebind) { EXPECT_EQ("10.0.0.10", client.config_.lease_.addr_.toText()); } +TEST_F(DirectClientTest, rebind) { + Dhcpv4SrvMTTestGuard guard(*this, false); + rebind(); +} + +TEST_F(DirectClientTest, rebindMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + rebind(); +} + } diff --git a/src/bin/dhcp4/tests/dora_unittest.cc b/src/bin/dhcp4/tests/dora_unittest.cc index bd7e83a66a..0faf40fe0a 100644 --- a/src/bin/dhcp4/tests/dora_unittest.cc +++ b/src/bin/dhcp4/tests/dora_unittest.cc @@ -150,7 +150,7 @@ namespace { /// respond to requests from unknown clients. /// const char* DORA_CONFIGS[] = { -// Configuration 0 + // Configuration 0 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -178,7 +178,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 1 + // Configuration 1 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -205,7 +205,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 2 + // Configuration 2 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -234,7 +234,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 3 + // Configuration 3 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -251,7 +251,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 4 + // Configuration 4 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -313,7 +313,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 6 + // Configuration 6 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -335,7 +335,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 7 + // Configuration 7 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -379,7 +379,7 @@ const char* DORA_CONFIGS[] = { "]" "}", -// Configuration 8 + // Configuration 8 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -397,7 +397,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 9 + // Configuration 9 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -415,7 +415,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 10 + // Configuration 10 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -433,7 +433,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 11 + // Configuration 11 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -451,7 +451,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 12 + // Configuration 12 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -471,7 +471,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 13 + // Configuration 13 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -490,7 +490,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 14 + // Configuration 14 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -514,7 +514,7 @@ const char* DORA_CONFIGS[] = { "} ]" "}", -// Configuration 15 + // Configuration 15 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -531,7 +531,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 16 + // Configuration 16 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -548,7 +548,7 @@ const char* DORA_CONFIGS[] = { " } ]" "}", -// Configuration 17 + // Configuration 17 "{ \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" "}," @@ -634,15 +634,194 @@ public: /// @c DORA_CONFIGS array. void testMultiStageBoot(const unsigned int config_index); + /// @brief This test verifies that the client in the SELECTING state can get + /// an address when it doesn't request any specific address in the + /// DHCPDISCOVER message. + void selectingDoNotRequestAddress(); + + /// @brief This test verifies that multiple clients may use the DHCPv4 + /// server and obtain unique leases. + void selectingMultipleClients(); + + /// @brief This test verifies that the client in a SELECTING state can + /// request a specific address and that this address will be assigned when + /// available. It also tests that if the client requests an address which is + /// in use the client will get a different address. + void selectingRequestAddress(); + + /// @brief This test verifies that the client will get the address that it + /// has been allocated when the client requests a different address. + void selectingRequestNonMatchingAddress(); + + /// @brief Test that the client in the INIT-REBOOT state can request the IP + /// address it has and the address is returned. Also, check that if the + /// client requests invalid address the server sends a DHCPNAK. + void initRebootRequest(); + + /// @brief Test that the client in the INIT-REBOOT state can request the IP + /// address it has and the address is returned. Also, check that if the + /// client is unknown the server sends a DHCPNAK. + void authoritative(); + + /// @brief Test that the client in the INIT-REBOOT state can request the IP + /// address it has and the address is returned. Also, check that if the + /// client is unknown the request is dropped. + void notAuthoritative(); + + /// @brief Check that the ciaddr returned by the server is correct for + /// DHCPOFFER and DHCPNAK according to RFC2131, section 4.3.1. + void ciaddr(); + + /// @brief This test checks the server behavior in the following situation: + /// - Client A identifies itself to the server using client identifier + /// and the hardware address and requests allocation of the new lease. + /// - Server allocates the lease to the client. + /// - Client B has the same hardware address but is using a different + /// client identifier then Client A. + /// - Client B sends DHCPDISCOVER. + /// - Server should determine that the client B is not client A, because + /// it is using a different client identifier, even though they use the + /// same HW address. As a consequence, the server should offer a + /// different address to the client B. + /// - The client B performs the 4-way exchange again and the server + /// allocates a new address to the client, which should be different + /// than the address used by the client A. + /// - Client B is in the renewing state and it successfully renews its + /// address. + /// - Client A also renews its address successfully. + void twoAllocationsOverlap(); + + /// @brief This is a simple test for the host reservation. It creates a + /// reservation for an address for a single client, identified by the HW + /// address. The test verifies that the client using this HW address will + /// obtain a lease for the reserved address. It also checks that the client + /// using a different HW address will obtain an address from the dynamic + /// pool. + void reservation(); + + /// @brief This test checks that it is possible to make a reservation by + /// DUID carried in the Client Identifier option. + void reservationByDUID(); + + /// @brief This test checks that it is possible to make a reservation by + /// circuit-id inserted by the relay agent. + void reservationByCircuitId(); + + /// @brief This test checks that it is possible to make a reservation by + /// client-id. + void reservationByClientId(); + + /// @brief This test verifies that order in which host identifiers are used + /// to retrieve host reservations can be controlled. + void hostIdentifiersOrder(); + + /// @brief This test checks that setting the match-client-id value to false + /// causes the server to ignore changing client identifier when the client + /// is using consistent HW address. + void ignoreChangingClientId(); + + /// @brief This test checks that the match-client-id parameter doesn't have + /// effect on the lease lookup using the HW address. + void changingHWAddress(); + + /// @brief This test verifies that the server assigns reserved values for + /// the siaddr, sname and file fields carried within DHCPv4 message. + void messageFieldsReservations(); + + /// @brief This test checks the following scenario: + /// 1. Client A performs 4-way exchange and obtains a lease from the dynamic + /// pool. + /// 2. Reservation is created for the client A using an address out of the + /// dynamic pool. + /// 3. Client A renews the lease. + /// 4. Server responds with DHCPNAK to indicate that the client should stop + /// using an address for which it has a lease. Server doesn't want to + /// renew an address for which the client doesn't have a reservation, + /// while it has a reservation for a different address. + /// 5. Client A receives a DHCPNAK and returns to the DHCP server discovery. + /// 6. Client A performs a 4-way exchange with a server and the server + /// allocates a reserved address to the Client A. + /// 7. Client A renews the allocated address and the server returns a + /// DHCPACK. + /// 8. Reservation for the Client A is removed. + /// 9. Client A renews the (previously reserved) lease and the server + /// returns DHCPNAK because the address in use is neither reserved nor + /// belongs to the dynamic pool. + /// 10. Client A returns to the DHCP server discovery. + /// 11. Client A uses 4-way exchange to obtain a lease from the dynamic + /// pool. + /// 12. The new address that the Client A is using is reserved for Client B. + /// Client A still holds this address. + /// 13. Client B uses 4-way exchange to obtain a new lease. + /// 14. The server determines that the Client B has a reservation for the + /// address which is in use by Client A and offers an address different + /// than reserved. + /// 15. Client B requests the allocation of the offered address and the + /// server allocates this address. + /// 16. Client A renews the lease. + /// 17. The server determines that the address that Client A is using is + /// reserved for Client B. The server returns DHCPNAK to the Client A. + /// 18. Client B uses 4-way exchange to obtain the reserved lease but the + /// lease for the Client A hasn't been removed yet. Client B is assigned + /// the same address it has been using. + /// 19. Client A uses 4-way exchange to allocate a new lease. + /// 20. The server allocates a new lease from the dynamic pool but it avoids + /// allocating the address reserved for the Client B. + /// 21. Client B uses 4-way exchange to obtain a new lease. + /// 22. The server finally allocates a reserved address to the Client B. + void reservationsWithConflicts(); + + /// @brief This test verifies that the allocation engine ignores + /// reservations when reservations flags are set to "disabled". + void reservationModeDisabled(); + + /// @brief This test verifies that allocation engine assigns a reserved + /// address to the client which doesn't own this reservation. We want to + /// avoid such cases in the real deployments, but this is just a test that + /// the allocation engine skips checking if the reservation exists when it + /// allocates an address. In the real deployment the reservation simply + /// wouldn't exist. + void reservationIgnoredInDisabledMode(); + + /// @brief This test verifies that in pool reservations are ignored when the + /// reservations-out-of-pool flag is set to true. + void reservationModeOutOfPool(); + + /// @brief This test verifies that the in-pool reservation can be assigned + /// to a client not owning this reservation when the + /// reservations-out-of-pool flag is set to true. + void reservationIgnoredInOutOfPoolMode(); + + /// @brief This test verifies that after a client completes its DORA + /// exchange, appropriate statistics are updated. + void statisticsDORA(); + + /// @brief This test verifies that after a client completes an exchange that + /// result in NAK, appropriate statistics are updated. + void statisticsNAK(); + + /// @brief This test verifies that custom server identifier can be specified + /// for a subnet. + void customServerIdentifier(); + + /// @brief This test verifies that reserved lease is not assigned to a + /// client which identifier doesn't match the identifier in the reservation. + void changingCircuitId(); + + /// @brief Verifies that extended info is stored on the lease when + /// store-extended-info is enabled. + void storeExtendedInfoEnabled(); + + /// @brief Verifies that extended info is not stored on the lease when + /// store-extended-info is disabled. + void storeExtendedInfoDisabled(); + /// @brief Interface Manager's fake configuration control. IfaceMgrTestConfig iface_mgr_test_config_; - }; -/// This test verifies that the client in the SELECTING state can get -/// an address when it doesn't request any specific address in the -/// DHCPDISCOVER message. -TEST_F(DORATest, selectingDoNotRequestAddress) { +void +DORATest::selectingDoNotRequestAddress() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -664,9 +843,18 @@ TEST_F(DORATest, selectingDoNotRequestAddress) { ASSERT_NE(client.config_.lease_.addr_.toText(), "0.0.0.0"); } -/// This test verifies that multiple clients may use the DHCPv4 server -/// and obtain unique leases. -TEST_F(DORATest, selectingMultipleClients) { +TEST_F(DORATest, selectingDoNotRequestAddress) { + Dhcpv4SrvMTTestGuard guard(*this, false); + selectingDoNotRequestAddress(); +} + +TEST_F(DORATest, selectingDoNotRequestAddressMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + selectingDoNotRequestAddress(); +} + +void +DORATest::selectingMultipleClients() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -707,11 +895,18 @@ TEST_F(DORATest, selectingMultipleClients) { EXPECT_NE(lease1.addr_, lease3.addr_); } -// This test verifies that the client in a SELECTING state can request -// a specific address and that this address will be assigned when -// available. It also tests that if the client requests an address which -// is in use the client will get a different address. -TEST_F(DORATest, selectingRequestAddress) { +TEST_F(DORATest, selectingMultipleClients) { + Dhcpv4SrvMTTestGuard guard(*this, false); + selectingMultipleClients(); +} + +TEST_F(DORATest, selectingMultipleClientsMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + selectingMultipleClients(); +} + +void +DORATest::selectingRequestAddress() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -752,9 +947,18 @@ TEST_F(DORATest, selectingRequestAddress) { EXPECT_NE(client.config_.lease_.addr_.toText(), "10.0.0.50"); } -// This test verifies that the client will get the address that it has -// been allocated when the client requests a different address. -TEST_F(DORATest, selectingRequestNonMatchingAddress) { +TEST_F(DORATest, selectingRequestAddress) { + Dhcpv4SrvMTTestGuard guard(*this, false); + selectingRequestAddress(); +} + +TEST_F(DORATest, selectingRequestAddressMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + selectingRequestAddress(); +} + +void +DORATest::selectingRequestNonMatchingAddress() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -793,10 +997,18 @@ TEST_F(DORATest, selectingRequestNonMatchingAddress) { EXPECT_EQ("10.0.0.50", client.config_.lease_.addr_.toText()); } -// Test that the client in the INIT-REBOOT state can request the IP -// address it has and the address is returned. Also, check that if -// if the client requests invalid address the server sends a DHCPNAK. -TEST_F(DORATest, initRebootRequest) { +TEST_F(DORATest, selectingRequestNonMatchingAddress) { + Dhcpv4SrvMTTestGuard guard(*this, false); + selectingRequestNonMatchingAddress(); +} + +TEST_F(DORATest, selectingRequestNonMatchingAddressMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + selectingRequestNonMatchingAddress(); +} + +void +DORATest::initRebootRequest() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -871,10 +1083,18 @@ TEST_F(DORATest, initRebootRequest) { ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText()); } -// Test that the client in the INIT-REBOOT state can request the IP -// address it has and the address is returned. Also, check that if -// if the client is unknown the server sends a DHCPNAK. -TEST_F(DORATest, authoritative) { +TEST_F(DORATest, initRebootRequest) { + Dhcpv4SrvMTTestGuard guard(*this, false); + initRebootRequest(); +} + +TEST_F(DORATest, initRebootRequestMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + initRebootRequest(); +} + +void +DORATest::authoritative() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[15], *client.getServer()); @@ -966,10 +1186,18 @@ TEST_F(DORATest, authoritative) { ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText()); } -// Test that the client in the INIT-REBOOT state can request the IP -// address it has and the address is returned. Also, check that if -// if the client is unknown the request is dropped. -TEST_F(DORATest, notAuthoritative) { +TEST_F(DORATest, authoritative) { + Dhcpv4SrvMTTestGuard guard(*this, false); + authoritative(); +} + +TEST_F(DORATest, authoritativeMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + authoritative(); +} + +void +DORATest::notAuthoritative() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[16], *client.getServer()); @@ -1059,9 +1287,18 @@ TEST_F(DORATest, notAuthoritative) { ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText()); } -// Check that the ciaddr returned by the server is correct for DHCPOFFER and -// DHCPNAK according to RFC2131, section 4.3.1. -TEST_F(DORATest, ciaddr) { +TEST_F(DORATest, notAuthoritative) { + Dhcpv4SrvMTTestGuard guard(*this, false); + notAuthoritative(); +} + +TEST_F(DORATest, notAuthoritativeMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + notAuthoritative(); +} + +void +DORATest::ciaddr() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -1112,6 +1349,16 @@ TEST_F(DORATest, ciaddr) { EXPECT_EQ("0.0.0.0", resp->getCiaddr().toText()); } +TEST_F(DORATest, ciaddr) { + Dhcpv4SrvMTTestGuard guard(*this, false); + ciaddr(); +} + +TEST_F(DORATest, ciaddrMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + ciaddr(); +} + void DORATest::oneAllocationOverlapTest(const std::string& clientid_a, const std::string& clientid_b) { @@ -1152,24 +1399,8 @@ DORATest::oneAllocationOverlapTest(const std::string& clientid_a, ASSERT_EQ(yiaddr, client.config_.lease_.addr_); } -// This test checks the server behavior in the following situation: -// - Client A identifies itself to the server using client identifier -// and the hardware address and requests allocation of the new lease. -// - Server allocates the lease to the client. -// - Client B has the same hardware address but is using a different -// client identifier then Client A. -// - Client B sends DHCPDISCOVER. -// - Server should determine that the client B is not client A, because -// it is using a different client identifier, even though they use the -// same HW address. As a consequence, the server should offer a -// different address to the client B. -// - The client B performs the 4-way exchange again and the server -// allocates a new address to the client, which should be different -// than the address used by the client A. -// - Client B is in the renewing state and it successfully renews its -// address. -// - Client A also renews its address successfully. -TEST_F(DORATest, twoAllocationsOverlap) { +void +DORATest::twoAllocationsOverlap() { // Allocate a lease by client A using the 4-way exchange. Dhcp4Client client_a(Dhcp4Client::SELECTING); client_a.includeClientId("12:34"); @@ -1240,6 +1471,16 @@ TEST_F(DORATest, twoAllocationsOverlap) { ASSERT_NE(client_a.config_.lease_.addr_, client_b.config_.lease_.addr_); } +TEST_F(DORATest, twoAllocationsOverlap) { + Dhcpv4SrvMTTestGuard guard(*this, false); + twoAllocationsOverlap(); +} + +TEST_F(DORATest, twoAllocationsOverlapMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + twoAllocationsOverlap(); +} + // This test checks the server behavior in the following situation: // - Client A identifies itself to the server using the hardware address // and client identifier. @@ -1255,6 +1496,26 @@ TEST_F(DORATest, twoAllocationsOverlap) { // same reason. // - Client A renews its address successfully. TEST_F(DORATest, oneAllocationOverlap1) { + Dhcpv4SrvMTTestGuard guard(*this, false); + oneAllocationOverlapTest("12:34", ""); +} + +// This test checks the server behavior in the following situation: +// - Client A identifies itself to the server using the hardware address +// and client identifier. +// - Client A performs the 4-way exchange and obtains a lease from the server. +// - Client B uses the same HW address as the client A, but it doesn't use +// the client identifier. +// - Client B sends the DHCPDISCOVER to the server. +// The server determines that there is a lease for the client A using the +// same HW address as the client B. Server discards the client's message and +// doesn't offer the lease for the client B to prevent allocation of the +// lease without a unique identifier. +// - The client sends the DHCPREQUEST and the server sends the DHCPNAK for the +// same reason. +// - Client A renews its address successfully. +TEST_F(DORATest, oneAllocationOverlap1MultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); oneAllocationOverlapTest("12:34", ""); } @@ -1262,15 +1523,20 @@ TEST_F(DORATest, oneAllocationOverlap1) { // uses no client identifier, and the client B uses the HW address and the // client identifier. The server behaves as previously. TEST_F(DORATest, oneAllocationOverlap2) { + Dhcpv4SrvMTTestGuard guard(*this, false); oneAllocationOverlapTest("", "12:34"); } -// This is a simple test for the host reservation. It creates a reservation -// for an address for a single client, identified by the HW address. The -// test verifies that the client using this HW address will obtain a -// lease for the reserved address. It also checks that the client using -// a different HW address will obtain an address from the dynamic pool. -TEST_F(DORATest, reservation) { +// This test is similar to oneAllocationOverlap2 but this time the client A +// uses no client identifier, and the client B uses the HW address and the +// client identifier. The server behaves as previously. +TEST_F(DORATest, oneAllocationOverlap2MultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + oneAllocationOverlapTest("", "12:34"); +} + +void +DORATest::reservation() { // Client A is a one which will have a reservation. Dhcp4Client clientA(Dhcp4Client::SELECTING); // Set explicit HW address so as it matches the reservation in the @@ -1309,9 +1575,18 @@ TEST_F(DORATest, reservation) { ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_)); } -// This test checks that it is possible to make a reservation by -// DUID carried in the Client Identifier option. -TEST_F(DORATest, reservationByDUID) { +TEST_F(DORATest, reservation) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservation(); +} + +TEST_F(DORATest, reservationMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservation(); +} + +void +DORATest:: reservationByDUID() { Dhcp4Client client(Dhcp4Client::SELECTING); // Use relay agent. client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2")); @@ -1341,9 +1616,18 @@ TEST_F(DORATest, reservationByDUID) { ASSERT_EQ("10.0.0.8", client.config_.lease_.addr_.toText()); } -// This test checks that it is possible to make a reservation by -// circuit-id inserted by the relay agent. -TEST_F(DORATest, reservationByCircuitId) { +TEST_F(DORATest, reservationByDUID) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationByDUID(); +} + +TEST_F(DORATest, reservationByDUIDMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationByDUID(); +} + +void +DORATest::reservationByCircuitId() { Dhcp4Client client(Dhcp4Client::SELECTING); // Use relay agent so as the circuit-id can be inserted. client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2")); @@ -1365,9 +1649,18 @@ TEST_F(DORATest, reservationByCircuitId) { ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText()); } -// This test checks that it is possible to make a reservation by -// client-id. -TEST_F(DORATest, reservationByClientId) { +TEST_F(DORATest, reservationByCircuitId) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationByCircuitId(); +} + +TEST_F(DORATest, reservationByCircuitIdMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationByCircuitId(); +} + +void +DORATest::reservationByClientId() { Dhcp4Client client(Dhcp4Client::SELECTING); // Use relay agent to make sure that the desired subnet is // selected for our client. @@ -1390,9 +1683,18 @@ TEST_F(DORATest, reservationByClientId) { ASSERT_EQ("10.0.0.1", client.config_.lease_.addr_.toText()); } -// This test verifies that order in which host identifiers are used to -// retrieve host reservations can be controlled. -TEST_F(DORATest, hostIdentifiersOrder) { +TEST_F(DORATest, reservationByClientId) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationByClientId(); +} + +TEST_F(DORATest, reservationByClientIdMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationByClientId(); +} + +void +DORATest::hostIdentifiersOrder() { Dhcp4Client client(Dhcp4Client::SELECTING); client.setHWAddress("aa:bb:cc:dd:ee:ff"); // Use relay agent so as the circuit-id can be inserted. @@ -1468,10 +1770,18 @@ TEST_F(DORATest, hostIdentifiersOrder) { ASSERT_EQ("10.0.0.1", client.config_.lease_.addr_.toText()); } -// This test checks that setting the match-client-id value to false causes -// the server to ignore changing client identifier when the client is -// using consistent HW address. -TEST_F(DORATest, ignoreChangingClientId) { +TEST_F(DORATest, hostIdentifiersOrder) { + Dhcpv4SrvMTTestGuard guard(*this, false); + hostIdentifiersOrder(); +} + +TEST_F(DORATest, hostIdentifiersOrderMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + hostIdentifiersOrder(); +} + +void +DORATest::ignoreChangingClientId() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[3], *client.getServer()); @@ -1507,9 +1817,18 @@ TEST_F(DORATest, ignoreChangingClientId) { EXPECT_FALSE(client.config_.lease_.client_id_); } -// This test checks that the match-client-id parameter doesn't have -// effect on the lease lookup using the HW address. -TEST_F(DORATest, changingHWAddress) { +TEST_F(DORATest, ignoreChangingClientId) { + Dhcpv4SrvMTTestGuard guard(*this, false); + ignoreChangingClientId(); +} + +TEST_F(DORATest, ignoreChangingClientIdMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + ignoreChangingClientId(); +} + +void +DORATest::changingHWAddress() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[3], *client.getServer()); @@ -1547,9 +1866,18 @@ TEST_F(DORATest, changingHWAddress) { EXPECT_FALSE(client.config_.lease_.client_id_); } -// This test verifies that the server assigns reserved values for the -// siaddr, sname and file fields carried within DHCPv4 message. -TEST_F(DORATest, messageFieldsReservations) { +TEST_F(DORATest, changingHWAddress) { + Dhcpv4SrvMTTestGuard guard(*this, false); + changingHWAddress(); +} + +TEST_F(DORATest, changingHWAddressMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + changingHWAddress(); +} + +void +DORATest::messageFieldsReservations() { // Client has a reservation. Dhcp4Client client(Dhcp4Client::SELECTING); // Set explicit HW address so as it matches the reservation in the @@ -1573,45 +1901,18 @@ TEST_F(DORATest, messageFieldsReservations) { EXPECT_EQ("bootfile.efi", client.config_.boot_file_name_); } -// This test checks the following scenario: -// 1. Client A performs 4-way exchange and obtains a lease from the dynamic pool. -// 2. Reservation is created for the client A using an address out of the dynamic -// pool. -// 3. Client A renews the lease. -// 4. Server responds with DHCPNAK to indicate that the client should stop using -// an address for which it has a lease. Server doesn't want to renew an -// address for which the client doesn't have a reservation, while it has -// a reservation for a different address. -// 5. Client A receives a DHCPNAK and returns to the DHCP server discovery. -// 6. Client A performs a 4-way exchange with a server and the server allocates -// a reserved address to the Client A. -// 7. Client A renews the allocated address and the server returns a DHCPACK. -// 8. Reservation for the Client A is removed. -// 9. Client A renews the (previously reserved) lease and the server returns -// DHCPNAK because the address in use is neither reserved nor belongs to -// the dynamic pool. -// 10. Client A returns to the DHCP server discovery. -// 11. Client A uses 4-way exchange to obtain a lease from the dynamic pool. -// 12. The new address that the Client A is using is reserved for Client B. -// Client A still holds this address. -// 13. Client B uses 4-way exchange to obtain a new lease. -// 14. The server determines that the Client B has a reservation for the -// address which is in use by Client A and offers an address different -// than reserved. -// 15. Client B requests the allocation of the offered address and the server -// allocates this address. -// 16. Client A renews the lease. -// 17. The server determines that the address that Client A is using is reserved -// for Client B. The server returns DHCPNAK to the Client A. -// 18. Client B uses 4-way exchange to obtain the reserved lease but the lease -// for the Client A hasn't been removed yet. Client B is assigned the same -// address it has been using. -// 19. Client A uses 4-way exchange to allocate a new lease. -// 20. The server allocates a new lease from the dynamic pool but it avoids -// allocating the address reserved for the Client B. -// 21. Client B uses 4-way exchange to obtain a new lease. -// 22. The server finally allocates a reserved address to the Client B. -TEST_F(DORATest, reservationsWithConflicts) { +TEST_F(DORATest, messageFieldsReservations) { + Dhcpv4SrvMTTestGuard guard(*this, false); + messageFieldsReservations(); +} + +TEST_F(DORATest, messageFieldsReservationsMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + messageFieldsReservations(); +} + +void +DORATest::reservationsWithConflicts() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -1800,9 +2101,18 @@ TEST_F(DORATest, reservationsWithConflicts) { ASSERT_EQ(in_pool_addr, clientB.config_.lease_.addr_); } -// This test verifies that the allocation engine ignores reservations when -// reservations flags are set to "disabled". -TEST_F(DORATest, reservationModeDisabled) { +TEST_F(DORATest, reservationsWithConflicts) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationsWithConflicts(); +} + +TEST_F(DORATest, reservationsWithConflictsMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationsWithConflicts(); +} + +void +DORATest::reservationModeDisabled() { // Client has a reservation. Dhcp4Client client(Dhcp4Client::SELECTING); // Set explicit HW address so as it matches the reservation in the @@ -1836,12 +2146,18 @@ TEST_F(DORATest, reservationModeDisabled) { ASSERT_EQ("10.0.0.65", client.config_.lease_.addr_.toText()); } -// This test verifies that allocation engine assigns a reserved address to -// the client which doesn't own this reservation. We want to avoid such -// cases in the real deployments, but this is just a test that the allocation -// engine skips checking if the reservation exists when it allocates an -// address. In the real deployment the reservation simply wouldn't exist. -TEST_F(DORATest, reservationIgnoredInDisabledMode) { +TEST_F(DORATest, reservationModeDisabled) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationModeDisabled(); +} + +TEST_F(DORATest, reservationModeDisabledMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationModeDisabled(); +} + +void +DORATest::reservationIgnoredInDisabledMode() { // Client has a reservation. Dhcp4Client client(Dhcp4Client::SELECTING); // Set MAC address which doesn't match the reservation configured. @@ -1862,9 +2178,18 @@ TEST_F(DORATest, reservationIgnoredInDisabledMode) { ASSERT_EQ("10.0.0.65", client.config_.lease_.addr_.toText()); } -// This test verifies that in pool reservations are ignored when the -// reservations-out-of-pool flag is set to true. -TEST_F(DORATest, reservationModeOutOfPool) { +TEST_F(DORATest, reservationIgnoredInDisabledMode) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationIgnoredInDisabledMode(); +} + +TEST_F(DORATest, reservationIgnoredInDisabledModeMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationIgnoredInDisabledMode(); +} + +void +DORATest::reservationModeOutOfPool() { // Create the first client for which we have a reservation out of the // dynamic pool. Dhcp4Client clientA(Dhcp4Client::SELECTING); @@ -1901,10 +2226,18 @@ TEST_F(DORATest, reservationModeOutOfPool) { ASSERT_EQ("10.0.0.40", clientB.config_.lease_.addr_.toText()); } -// This test verifies that the in-pool reservation can be assigned to a client -// not owning this reservation when the reservations-out-of-pool flag is set to -// true. -TEST_F(DORATest, reservationIgnoredInOutOfPoolMode) { +TEST_F(DORATest, reservationModeOutOfPool) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationModeOutOfPool(); +} + +TEST_F(DORATest, reservationModeOutOfPoolMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationModeOutOfPool(); +} + +void +DORATest::reservationIgnoredInOutOfPoolMode() { // Create the first client for which we have a reservation out of the // dynamic pool. Dhcp4Client client(Dhcp4Client::SELECTING); @@ -1924,9 +2257,18 @@ TEST_F(DORATest, reservationIgnoredInOutOfPoolMode) { ASSERT_EQ("10.0.0.65", client.config_.lease_.addr_.toText()); } -/// This test verifies that after a client completes its DORA exchange, -/// appropriate statistics are updated. -TEST_F(DORATest, statisticsDORA) { +TEST_F(DORATest, reservationIgnoredInOutOfPoolMode) { + Dhcpv4SrvMTTestGuard guard(*this, false); + reservationIgnoredInOutOfPoolMode(); +} + +TEST_F(DORATest, reservationIgnoredInOutOfPoolModeMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + reservationIgnoredInOutOfPoolMode(); +} + +void +DORATest::statisticsDORA() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -1983,9 +2325,18 @@ TEST_F(DORATest, statisticsDORA) { EXPECT_EQ(5, pkt4_sent->getInteger().first); } -// This test verifies that after a client completes an exchange that result -// in NAK, appropriate statistics are updated. -TEST_F(DORATest, statisticsNAK) { +TEST_F(DORATest, statisticsDORA) { + Dhcpv4SrvMTTestGuard guard(*this, false); + statisticsDORA(); +} + +TEST_F(DORATest, statisticsDORAMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + statisticsDORA(); +} + +void +DORATest::statisticsNAK() { Dhcp4Client client(Dhcp4Client::SELECTING); // Configure DHCP server. configure(DORA_CONFIGS[0], *client.getServer()); @@ -2027,6 +2378,16 @@ TEST_F(DORATest, statisticsNAK) { EXPECT_EQ(1, pkt4_sent->getInteger().first); } +TEST_F(DORATest, statisticsNAK) { + Dhcpv4SrvMTTestGuard guard(*this, false); + statisticsNAK(); +} + +TEST_F(DORATest, statisticsNAKMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + statisticsNAK(); +} + void DORATest::testMultiStageBoot(const unsigned int config_index) { Dhcp4Client client(Dhcp4Client::SELECTING); @@ -2105,17 +2466,24 @@ DORATest::testMultiStageBoot(const unsigned int config_index) { ASSERT_NE(leased_address2, leased_address3); } - // Test that the client using the same hardware address but multiple // client identifiers will obtain multiple leases. TEST_F(DORATest, multiStageBoot) { + Dhcpv4SrvMTTestGuard guard(*this, false); // DORA_CONFIGS[0] to be used for server configuration. testMultiStageBoot(0); } -// This test verifies that custom server identifier can be specified for -// a subnet. -TEST_F(DORATest, customServerIdentifier) { +// Test that the client using the same hardware address but multiple +// client identifiers will obtain multiple leases. +TEST_F(DORATest, multiStageBootMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + // DORA_CONFIGS[0] to be used for server configuration. + testMultiStageBoot(0); +} + +void +DORATest::customServerIdentifier() { Dhcp4Client client1(Dhcp4Client::SELECTING); // Configure DHCP server. ASSERT_NO_THROW(configure(DORA_CONFIGS[7], *client1.getServer())); @@ -2154,9 +2522,18 @@ TEST_F(DORATest, customServerIdentifier) { EXPECT_EQ("3.4.5.6", client3.config_.serverid_.toText()); } -// This test verifies that reserved lease is not assigned to a client which -// identifier doesn't match the identifier in the reservation. -TEST_F(DORATest, changingCircuitId) { +TEST_F(DORATest, customServerIdentifier) { + Dhcpv4SrvMTTestGuard guard(*this, false); + customServerIdentifier(); +} + +TEST_F(DORATest, customServerIdentifierMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + customServerIdentifier(); +} + +void +DORATest::changingCircuitId() { Dhcp4Client client(Dhcp4Client::SELECTING); client.setHWAddress("aa:bb:cc:dd:ee:ff"); // Use relay agent so as the circuit-id can be inserted. @@ -2214,9 +2591,18 @@ TEST_F(DORATest, changingCircuitId) { EXPECT_NE("10.0.0.9", client.config_.lease_.addr_.toText()); } -// Verifies that extended info is stored on the lease when -// store-extended-info is enabled. -TEST_F(DORATest, storeExtendedInfoEnabled) { +TEST_F(DORATest, changingCircuitId) { + Dhcpv4SrvMTTestGuard guard(*this, false); + changingCircuitId(); +} + +TEST_F(DORATest, changingCircuitIdMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + changingCircuitId(); +} + +void +DORATest::storeExtendedInfoEnabled() { Dhcp4Client client(Dhcp4Client::SELECTING); // Use relay agent to make sure that the desired subnet is // selected for our client. @@ -2253,9 +2639,18 @@ TEST_F(DORATest, storeExtendedInfoEnabled) { ASSERT_EQ(expected_context, ss.str()); } -// Verifies that extended info is not stored on the lease when -// store-extended-info is disabled. -TEST_F(DORATest, storeExtendedInfoDisabled) { +TEST_F(DORATest, storeExtendedInfoEnabled) { + Dhcpv4SrvMTTestGuard guard(*this, false); + storeExtendedInfoEnabled(); +} + +TEST_F(DORATest, storeExtendedInfoEnabledMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + storeExtendedInfoEnabled(); +} + +void +DORATest::storeExtendedInfoDisabled() { Dhcp4Client client(Dhcp4Client::SELECTING); // Use relay agent to make sure that the desired subnet is // selected for our client. @@ -2285,7 +2680,15 @@ TEST_F(DORATest, storeExtendedInfoDisabled) { ASSERT_FALSE(lease->getContext()); } +TEST_F(DORATest, storeExtendedInfoDisabled) { + Dhcpv4SrvMTTestGuard guard(*this, false); + storeExtendedInfoDisabled(); +} +TEST_F(DORATest, storeExtendedInfoDisabledMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + storeExtendedInfoDisabled(); +} // Starting tests which require MySQL backend availability. Those tests // will not be executed if Kea has been compiled without the @@ -2315,7 +2718,16 @@ public: // Test that the client using the same hardware address but multiple // client identifiers will obtain multiple leases (MySQL lease database). TEST_F(DORAMySQLTest, multiStageBoot) { - // DORA_CONFIGS[9] to be used for server configuration. + Dhcpv4SrvMTTestGuard guard(*this, false); + // DORA_CONFIGS[8] to be used for server configuration. + testMultiStageBoot(8); +} + +// Test that the client using the same hardware address but multiple +// client identifiers will obtain multiple leases (MySQL lease database). +TEST_F(DORAMySQLTest, multiStageBootMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + // DORA_CONFIGS[8] to be used for server configuration. testMultiStageBoot(8); } @@ -2349,6 +2761,15 @@ public: // Test that the client using the same hardware address but multiple // client identifiers will obtain multiple leases (PostgreSQL lease database). TEST_F(DORAPgSQLTest, multiStageBoot) { + Dhcpv4SrvMTTestGuard guard(*this, false); + // DORA_CONFIGS[9] to be used for server configuration. + testMultiStageBoot(9); +} + +// Test that the client using the same hardware address but multiple +// client identifiers will obtain multiple leases (PostgreSQL lease database). +TEST_F(DORAPgSQLTest, multiStageBootMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); // DORA_CONFIGS[9] to be used for server configuration. testMultiStageBoot(9); } @@ -2382,9 +2803,19 @@ public: // Test that the client using the same hardware address but multiple // client identifiers will obtain multiple leases (CQL lease database). TEST_F(DORACQLTest, multiStageBoot) { + Dhcpv4SrvMTTestGuard guard(*this, false); // DORA_CONFIGS[10] to be used for server configuration. testMultiStageBoot(10); } + +// Test that the client using the same hardware address but multiple +// client identifiers will obtain multiple leases (CQL lease database). +TEST_F(DORACQLTest, multiStageBootMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + // DORA_CONFIGS[10] to be used for server configuration. + testMultiStageBoot(10); +} + #endif } // end of anonymous namespace