From: Tomek Mrugalski Date: Tue, 16 Jun 2015 19:08:56 +0000 (+0200) Subject: [3795] Packet statistics implemented for DHCPv6 X-Git-Tag: trac3910_base~2^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d1df0ceb1d192d0808b1b158b0ed27b1ca65b2f7;p=thirdparty%2Fkea.git [3795] Packet statistics implemented for DHCPv6 --- diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index a899787db7..5140dfa152 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -80,6 +80,7 @@ kea_dhcp6_LDADD += $(top_builddir)/src/lib/util/libkea-util.la kea_dhcp6_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la kea_dhcp6_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la kea_dhcp6_LDADD += $(top_builddir)/src/bin/cfgrpt/libcfgrpt.la +kea_dhcp6_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la kea_dhcp6dir = $(pkgdatadir) kea_dhcp6_DATA = dhcp6.spec diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 24bdfce800..581b4868d5 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -330,6 +331,13 @@ bool Dhcpv6Srv::run() { .arg(query->getLocalPort()) .arg(query->getIface()); + // Log reception of the packet. We need to increase it early, as + // any failures in unpacking will cause the packet to be dropped. + // we will increase type specific packets further down the road. + // See processStatsReceived(). + isc::stats::StatsMgr::instance().addValue("pkt6-received", + static_cast(1)); + } else { LOG_DEBUG(packet_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_INTERRUPTED) .arg(timeout); @@ -431,12 +439,26 @@ bool Dhcpv6Srv::run() { .arg(query->getLocalAddr().toText()) .arg(query->getIface()) .arg(e.what()); + + // Increase the statistics of parse failues and dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt6-parse-failed", + static_cast(1)); + isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop", + static_cast(1)); continue; } } + + // Update statistics accordingly for received packet. + processStatsReceived(query); + // Check if received query carries server identifier matching // server identifier being used by the server. if (!testServerID(query)) { + + // Increase the statistic of dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop", + static_cast(1)); continue; } @@ -444,6 +466,10 @@ bool Dhcpv6Srv::run() { // The Solicit, Confirm, Rebind and Information Request will be // discarded if sent to unicast address. if (!testUnicast(query)) { + + // Increase the statistic of dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop", + static_cast(1)); continue; } @@ -541,6 +567,10 @@ bool Dhcpv6Srv::run() { .arg(query->getRemoteAddr().toText()) .arg(e.what()); + // Increase the statistic of dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop", + static_cast(1)); + } catch (const isc::Exception& e) { // Catch-all exception (at least for ones based on the isc Exception @@ -553,6 +583,10 @@ bool Dhcpv6Srv::run() { .arg(query->getName()) .arg(query->getRemoteAddr().toText()) .arg(e.what()); + + // Increase the statistic of dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop", + static_cast(1)); } if (rsp) { @@ -665,6 +699,10 @@ bool Dhcpv6Srv::run() { .arg(static_cast(rsp->getType())).arg(rsp->toText()); sendPacket(rsp); + + // Update statistics accordingly for sent packet. + processStatsSent(rsp); + } catch (const std::exception& e) { LOG_ERROR(packet_logger, DHCP6_PACKET_SEND_FAIL) .arg(e.what()); @@ -2898,5 +2936,77 @@ void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) { } } +void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) { + // Note that we're not bumping pkt4-received statistic as it was + // increased early in the packet reception code. + + string stat_name = "pkt6-unknown-received"; + switch (query->getType()) { + case DHCPV6_SOLICIT: + stat_name = "pkt6-solicit-received"; + break; + case DHCPV6_ADVERTISE: + // Should not happen, but let's keep a counter for it + stat_name = "pkt6-advertise-received"; + break; + case DHCPV6_REQUEST: + stat_name = "pkt6-request-received"; + break; + case DHCPV6_CONFIRM: + stat_name = "pkt6-confirm-received"; + break; + case DHCPV6_RENEW: + stat_name = "pkt6-renew-received"; + break; + case DHCPV6_REBIND: + stat_name = "pkt6-rebind-received"; + break; + case DHCPV6_REPLY: + // Should not happen, but let's keep a counter for it + stat_name = "pkt6-reply-received"; + break; + case DHCPV6_RELEASE: + stat_name = "pkt6-release-received"; + break; + case DHCPV6_DECLINE: + stat_name = "pkt6-decline-received"; + break; + case DHCPV6_RECONFIGURE: + stat_name = "pkt6-reconfigure-received"; + break; + case DHCPV6_INFORMATION_REQUEST: + stat_name = "pkt6-infrequest-received"; + break; + default: + ; // do nothing + } + + isc::stats::StatsMgr::instance().addValue(stat_name, + static_cast(1)); +} + +void Dhcpv6Srv::processStatsSent(const Pkt6Ptr& response) { + // Increase generic counter for sent packets. + isc::stats::StatsMgr::instance().addValue("pkt6-sent", + static_cast(1)); + + // Increase packet type specific counter for packets sent. + string stat_name; + switch (response->getType()) { + case DHCPV6_ADVERTISE: + stat_name = "pkt6-advertise-sent"; + break; + case DHCPV6_REPLY: + stat_name = "pkt6-reply-sent"; + break; + default: + // That should never happen + return; + } + + isc::stats::StatsMgr::instance().addValue(stat_name, + static_cast(1)); +} + }; }; diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index a26ae4db14..3dc32a461f 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -740,6 +740,14 @@ private: Lease6Ptr& new_lease, const std::string& hostname, bool do_fwd, bool do_rev); + /// @brief Updates statistics for received packets + /// @param query packet received + static void processStatsReceived(const Pkt6Ptr& query); + + /// @brief Updates statistics for transmitted packets + /// @param query packet transmitted + static void processStatsSent(const Pkt6Ptr& response); + /// @brief Allocation Engine. /// Pointer to the allocation engine that we are currently using /// It must be a pointer, because we will support changing engines diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index ed16e00a60..099b1571de 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -109,6 +109,8 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la dhcp6_unittests_LDADD += $(top_builddir)/src/bin/cfgrpt/libcfgrpt.la +dhcp6_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la + endif noinst_PROGRAMS = $(TESTS) diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index eb308e711a..e97a6dd3f7 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -58,7 +58,6 @@ using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::util; -using namespace isc::hooks; using namespace std; namespace { @@ -2326,6 +2325,87 @@ TEST_F(Dhcpv6SrvTest, rsooOverride) { ASSERT_EQ(1, opt->getData().size()); } +// Test checks if pkt6-advertise-received is bumped up correctly. +// Note that in properly configured network the server never receives Advertise +// messages. +TEST_F(Dhcpv6SrvTest, receiveAdvertiseStat) { + testReceiveStats(DHCPV6_ADVERTISE, "pkt6-advertise-received"); +} + +// Test checks if pkt6-reply-received is bumped up correctly. +// Note that in properly configured network the server never receives Reply +// messages. +TEST_F(Dhcpv6SrvTest, receiveReplyStat) { + testReceiveStats(DHCPV6_ADVERTISE, "pkt6-advertise-received"); +} + +// Test checks if pkt6-unknown-received is bumped up correctly. +TEST_F(Dhcpv6SrvTest, receiveUnknownStat) { + testReceiveStats(123, "pkt6-unknown-received"); +} + +// Test checks if pkt6-renew-received is bumped up correctly. +TEST_F(Dhcpv6SrvTest, receiveRenewStat) { + testReceiveStats(DHCPV6_RENEW, "pkt6-renew-received"); +} + +// Test checks if pkt6-rebind-received is bumped up correctly. +TEST_F(Dhcpv6SrvTest, receiveRebindStat) { + testReceiveStats(DHCPV6_REBIND, "pkt6-rebind-received"); +} + +// Test checks if pkt6-release-received is bumped up correctly. +TEST_F(Dhcpv6SrvTest, receiveReleaseStat) { + testReceiveStats(DHCPV6_RELEASE, "pkt6-release-received"); +} + +// Test checks if pkt6-decline-received is bumped up correctly. +TEST_F(Dhcpv6SrvTest, receiveDeclineStat) { + testReceiveStats(DHCPV6_DECLINE, "pkt6-decline-received"); +} + +// Test checks if reception of a malformed packet increases pkt-parse-failed +// and pkt6-receive-drop +TEST_F(Dhcpv6SrvTest, receiveParseFailedStat) { + using namespace isc::stats; + StatsMgr& mgr = StatsMgr::instance(); + NakedDhcpv6Srv srv(0); + + // Let's get a simple SOLICIT... + Pkt6Ptr pkt = PktCaptures::captureSimpleSolicit(); + + // And pretend it's packet is only 3 bytes long. + pkt->data_.resize(3); + + // Check that those statistics are not set before the test + ObservationPtr pkt6_rcvd = mgr.getObservation("pkt6-received"); + ObservationPtr parse_fail = mgr.getObservation("pkt6-parse-failed"); + ObservationPtr recv_drop = mgr.getObservation("pkt6-receive-drop"); + EXPECT_FALSE(pkt6_rcvd); + EXPECT_FALSE(parse_fail); + EXPECT_FALSE(recv_drop); + + // Simulate that we have received that traffic + srv.fakeReceive(pkt); + + // Server will now process to run its normal loop, but instead of calling + // IfaceMgr::receive6(), it will read all packets from the list set by + // fakeReceive() + srv.run(); + + // All expected statstics must be present. + pkt6_rcvd = mgr.getObservation("pkt6-received"); + parse_fail = mgr.getObservation("pkt6-parse-failed"); + recv_drop = mgr.getObservation("pkt6-receive-drop"); + ASSERT_TRUE(pkt6_rcvd); + ASSERT_TRUE(parse_fail); + ASSERT_TRUE(recv_drop); + + // They also must have expected values. + EXPECT_EQ(1, pkt6_rcvd->getInteger().first); + EXPECT_EQ(1, parse_fail->getInteger().first); + EXPECT_EQ(1, recv_drop->getInteger().first); +} /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test /// to call processX() methods. diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index 550fb5244c..dd40b049b9 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include #include using namespace isc::data; @@ -28,6 +30,8 @@ using namespace isc::asiolink; namespace isc { namespace test { +const char* NakedDhcpv6SrvTest::DUID_FILE = "server-id-test.txt"; + Dhcpv6SrvTest::Dhcpv6SrvTest() :srv_(0) { subnet_ = isc::dhcp::Subnet6Ptr @@ -741,6 +745,44 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) { EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr)); } +void +Dhcpv6SrvTest::testReceiveStats(uint8_t pkt_type, const std::string& stat_name) { + + using namespace isc::stats; + StatsMgr& mgr = StatsMgr::instance(); + NakedDhcpv6Srv srv(0); + + // Let's get a simple SOLICIT... + Pkt6Ptr pkt = PktCaptures::captureSimpleSolicit(); + + // And pretend it's packet of a different type + pkt->data_[0] = pkt_type; + + // Check that those statistics are not set before the test + ObservationPtr pkt6_rcvd = mgr.getObservation("pkt6-received"); + ObservationPtr tested_stat = mgr.getObservation(stat_name); + EXPECT_FALSE(pkt6_rcvd); + EXPECT_FALSE(tested_stat); + + // Simulate that we have received that traffic + srv.fakeReceive(pkt); + + // Server will now process to run its normal loop, but instead of calling + // IfaceMgr::receive6(), it will read all packets from the list set by + // fakeReceive() + srv.run(); + + // All expected statstics must be present. + pkt6_rcvd = mgr.getObservation("pkt6-received"); + tested_stat = mgr.getObservation(stat_name); + ASSERT_TRUE(pkt6_rcvd); + ASSERT_TRUE(tested_stat); + + // They also must have expected values. + EXPECT_EQ(1, pkt6_rcvd->getInteger().first); + EXPECT_EQ(1, tested_stat->getInteger().first); +} + void Dhcpv6SrvTest::configure(const std::string& config) { configure(config, srv_); @@ -761,6 +803,48 @@ Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) { CfgMgr::instance().commit(); } +NakedDhcpv6SrvTest::NakedDhcpv6SrvTest() +: rcode_(-1) { + // it's ok if that fails. There should not be such a file anyway + unlink(DUID_FILE); + + const isc::dhcp::IfaceMgr::IfaceCollection& ifaces = + isc::dhcp::IfaceMgr::instance().getIfaces(); + + // There must be some interface detected + if (ifaces.empty()) { + // We can't use ASSERT in constructor + ADD_FAILURE() << "No interfaces detected."; + } + + valid_iface_ = (*ifaces.begin())->getName(); + + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); +} + +NakedDhcpv6SrvTest::~NakedDhcpv6SrvTest() { + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); + + // Let's clean up if there is such a file. + unlink(DUID_FILE); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("buffer6_receive"); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("buffer6_send"); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("lease6_renew"); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("lease6_release"); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("pkt6_receive"); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("pkt6_send"); + isc::hooks::HooksManager::preCalloutsLibraryHandle() + .deregisterAllCallouts("subnet6_select"); +} + // Generate IA_NA option with specified parameters boost::shared_ptr NakedDhcpv6SrvTest::generateIA(uint16_t type, uint32_t iaid, uint32_t t1, @@ -855,5 +939,6 @@ NakedDhcpv6SrvTest::checkIA_NAStatusCode( } } + }; // end of isc::test namespace }; // end of isc namespace diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index 7f0b4a5a4b..d4e48319c6 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -127,28 +127,16 @@ public: std::list fake_sent_; }; -static const char* DUID_FILE = "server-id-test.txt"; - -// test fixture for any tests requiring blank/empty configuration -// serves as base class for additional tests +/// @brief Test fixture for any tests requiring blank/empty configuration +/// serves as base class for additional tests class NakedDhcpv6SrvTest : public ::testing::Test { public: - NakedDhcpv6SrvTest() : rcode_(-1) { - // it's ok if that fails. There should not be such a file anyway - unlink(DUID_FILE); - - const isc::dhcp::IfaceMgr::IfaceCollection& ifaces = - isc::dhcp::IfaceMgr::instance().getIfaces(); - - // There must be some interface detected - if (ifaces.empty()) { - // We can't use ASSERT in constructor - ADD_FAILURE() << "No interfaces detected."; - } + /// @brief Constructor + NakedDhcpv6SrvTest(); - valid_iface_ = (*ifaces.begin())->getName(); - } + /// @brief Location of a test DUID file + static const char* DUID_FILE; // Generate IA_NA or IA_PD option with specified parameters boost::shared_ptr generateIA @@ -286,24 +274,7 @@ public: EXPECT_EQ(expected_transid, rsp->getTransid()); } - virtual ~NakedDhcpv6SrvTest() { - // Let's clean up if there is such a file. - unlink(DUID_FILE); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("buffer6_receive"); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("buffer6_send"); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("lease6_renew"); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("lease6_release"); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("pkt6_receive"); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("pkt6_send"); - isc::hooks::HooksManager::preCalloutsLibraryHandle() - .deregisterAllCallouts("subnet6_select"); - }; + virtual ~NakedDhcpv6SrvTest(); // A DUID used in most tests (typically as client-id) isc::dhcp::DuidPtr duid_; @@ -539,6 +510,12 @@ public: testReleaseReject(isc::dhcp::Lease::Type type, const isc::asiolink::IOAddress& addr); + /// @brief simulates reception of a packet of specified type and checks statistic + /// + /// @param pkt_type reception of a packet of this type will be simulated + /// @param stat_name this statistic is expected to be set to 1 + void testReceiveStats(uint8_t pkt_type, const std::string& stat_name); + /// A subnet used in most tests isc::dhcp::Subnet6Ptr subnet_; diff --git a/src/bin/dhcp6/tests/infrequest_unittest.cc b/src/bin/dhcp6/tests/infrequest_unittest.cc index ead363642d..8b15f7fb6e 100644 --- a/src/bin/dhcp6/tests/infrequest_unittest.cc +++ b/src/bin/dhcp6/tests/infrequest_unittest.cc @@ -18,6 +18,7 @@ #include #include #include +#include using namespace isc; using namespace isc::dhcp; @@ -126,6 +127,17 @@ public: InfRequestTest() : Dhcpv6SrvTest(), iface_mgr_test_config_(true) { + + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); + } + + /// @brief Destructor. + /// + /// Removes any statistics that may have been set. + ~InfRequestTest() { + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); } /// @brief Interface Manager's fake configuration control. @@ -301,7 +313,53 @@ TEST_F(InfRequestTest, infRequestNoSubnets) { EXPECT_EQ("2001:db8::2", addrs[1].toText()); } +/// Check that server processes correctly an incoming inf-request in a +/// typical subnet that has also address and prefix pools. +TEST_F(InfRequestTest, infRequestStats) { + Dhcp6Client client; + // Configure client to request IA_PD. + configure(CONFIGS[0], *client.getServer()); + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(1, subnets->size()); + + // Ok, let's check the statistics. None should be present. + using namespace isc::stats; + StatsMgr& mgr = StatsMgr::instance(); + ObservationPtr pkt6_rcvd = mgr.getObservation("pkt6-received"); + ObservationPtr pkt6_infreq_rcvd = mgr.getObservation("pkt6-infrequest-received"); + ObservationPtr pkt6_reply_sent = mgr.getObservation("pkt6-reply-sent"); + ObservationPtr pkt6_sent = mgr.getObservation("pkt6-sent"); + EXPECT_FALSE(pkt6_rcvd); + EXPECT_FALSE(pkt6_infreq_rcvd); + EXPECT_FALSE(pkt6_reply_sent); + EXPECT_FALSE(pkt6_sent); + + // Perform 2-way exchange (Inf-request/reply) + client.requestOption(D6O_NAME_SERVERS); + ASSERT_NO_THROW(client.doInfRequest()); + // Confirm that there's a response + Pkt6Ptr response = client.getContext().response_; + ASSERT_TRUE(response); + + pkt6_rcvd = mgr.getObservation("pkt6-received"); + pkt6_infreq_rcvd = mgr.getObservation("pkt6-infrequest-received"); + pkt6_reply_sent = mgr.getObservation("pkt6-reply-sent"); + pkt6_sent = mgr.getObservation("pkt6-sent"); + + ASSERT_TRUE(pkt6_rcvd); + ASSERT_TRUE(pkt6_infreq_rcvd); + ASSERT_TRUE(pkt6_reply_sent); + ASSERT_TRUE(pkt6_sent); + + // They also must have expected values. + EXPECT_EQ(1, pkt6_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_infreq_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_reply_sent->getInteger().first); + EXPECT_EQ(1, pkt6_sent->getInteger().first); +} } // end of anonymous namespace diff --git a/src/bin/dhcp6/tests/sarr_unittest.cc b/src/bin/dhcp6/tests/sarr_unittest.cc index 2035521383..d83c25a7dc 100644 --- a/src/bin/dhcp6/tests/sarr_unittest.cc +++ b/src/bin/dhcp6/tests/sarr_unittest.cc @@ -19,6 +19,7 @@ #include #include #include +#include using namespace isc; using namespace isc::dhcp; @@ -101,6 +102,8 @@ public: SARRTest() : Dhcpv6SrvTest(), iface_mgr_test_config_(true) { + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); } /// @brief Destructor. @@ -109,6 +112,9 @@ public: virtual ~SARRTest() { D2ClientConfigPtr cfg(new D2ClientConfig()); CfgMgr::instance().setD2ClientConfig(cfg); + + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); } /// @brief Interface Manager's fake configuration control. @@ -276,5 +282,64 @@ TEST_F(SARRTest, rapidCommitDisable) { EXPECT_EQ(0, CfgMgr::instance().getD2ClientMgr().getQueueSize()); } +// This test verifies that regular Solicit/Adv/Request/Reply exchange will +// result in appropriately set statistics. +TEST_F(SARRTest, sarrStats) { + + // Let's use one of the existing configurations and tell the client to + // as for an address. + Dhcp6Client client; + configure(CONFIGS[1], *client.getServer()); + client.setInterface("eth1"); + client.useNA(); + + // Make sure we ended-up having expected number of subnets configured. + const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()-> + getCfgSubnets6()->getAll(); + ASSERT_EQ(2, subnets->size()); + + // Ok, let's check the statistics. None should be present. + using namespace isc::stats; + StatsMgr& mgr = StatsMgr::instance(); + ObservationPtr pkt6_rcvd = mgr.getObservation("pkt6-received"); + ObservationPtr pkt6_solicit_rcvd = mgr.getObservation("pkt6-solicit-received"); + ObservationPtr pkt6_adv_sent = mgr.getObservation("pkt6-advertise-sent"); + ObservationPtr pkt6_request_rcvd = mgr.getObservation("pkt6-request-received"); + ObservationPtr pkt6_reply_sent = mgr.getObservation("pkt6-reply-sent"); + ObservationPtr pkt6_sent = mgr.getObservation("pkt6-sent"); + EXPECT_FALSE(pkt6_rcvd); + EXPECT_FALSE(pkt6_solicit_rcvd); + EXPECT_FALSE(pkt6_adv_sent); + EXPECT_FALSE(pkt6_request_rcvd); + EXPECT_FALSE(pkt6_reply_sent); + EXPECT_FALSE(pkt6_sent); + + // Perform 4-way exchange. + ASSERT_NO_THROW(client.doSARR()); + // Server should have assigned a prefix. + ASSERT_EQ(1, client.getLeaseNum()); + + // All expected statstics must be present now. + pkt6_rcvd = mgr.getObservation("pkt6-received"); + pkt6_solicit_rcvd = mgr.getObservation("pkt6-solicit-received"); + pkt6_adv_sent = mgr.getObservation("pkt6-advertise-sent"); + pkt6_request_rcvd = mgr.getObservation("pkt6-request-received"); + pkt6_reply_sent = mgr.getObservation("pkt6-reply-sent"); + pkt6_sent = mgr.getObservation("pkt6-sent"); + ASSERT_TRUE(pkt6_rcvd); + ASSERT_TRUE(pkt6_solicit_rcvd); + ASSERT_TRUE(pkt6_adv_sent); + ASSERT_TRUE(pkt6_request_rcvd); + ASSERT_TRUE(pkt6_reply_sent); + ASSERT_TRUE(pkt6_sent); + + // They also must have expected values. + EXPECT_EQ(2, pkt6_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_solicit_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_adv_sent->getInteger().first); + EXPECT_EQ(1, pkt6_request_rcvd->getInteger().first); + EXPECT_EQ(1, pkt6_reply_sent->getInteger().first); + EXPECT_EQ(2, pkt6_sent->getInteger().first); +} } // end of anonymous namespace