Browse Source

[master] Merge branch 'trac3795' (DHCPv6 statistics)

Conflicts:
	doc/guide/dhcp4-srv.xml
Tomek Mrugalski 9 years ago
parent
commit
7616e3b3b8

+ 29 - 38
doc/guide/dhcp4-srv.xml

@@ -2643,12 +2643,13 @@ temporarily override a list of interface names and listen on all interfaces.
             <entry>pkt4-received</entry>
             <entry>integer</entry>
             <entry>
-            Number of packets received. This includes all packets: valid, bogus, corrupted,
-            rejected etc.  This statistic is expected to grow rapidly.
+            Number of DHCPv4 packets received. This includes all packets: valid,
+            bogus, corrupted, rejected etc.  This statistic is expected to grow
+            rapidly.
             </entry>
             </row>
-            <row>
 
+            <row>
             <entry>pkt4-discover-received</entry>
             <entry>integer</entry>
             <entry>
@@ -2820,46 +2821,36 @@ temporarily override a list of interface names and listen on all interfaces.
             </entry>
             </row>
 
+            <row>
+              <entry>subnet[id].total-addresses</entry>
+              <entry>integer</entry>
+              <entry>The total number of addresses available for the DHCPv4
+              management. In other words, this is the sum of all addresses in
+              all configured pools. This statistic changes only during
+              configuration changes. Note it does not take into account any
+              addresses that may be reserved due to host reservation. The
+              <emphasis>id</emphasis> is the subnet-id of a given subnet. This
+              statistic is exposed for each subnet separately. This statistic is
+              reset during reconfiguration event.</entry>
+            </row>
+            <row>
+              <entry>subnet[id].assigned-addresses</entry>
+              <entry>integer</entry>
+              <entry>This statistic shows the number of assigned addresses in a
+              given subnet.  This statistic increases every time a new lease is
+              allocated (as a result of receiving a DHCPREQUEST message) and is
+              decreased every time a lease is released (a DHCPRELEASE message is
+              received). When lease expiration is implemented (planned for Kea
+              1.0), it will also decrease when a lease is expired. The
+              <emphasis>id</emphasis> is the subnet-id of a given subnet. This
+              statistic is exposed for each subnet separately. This statistic is
+              reset during reconfiguration event.</entry>
+            </row>
         </tbody>
         </tgroup>
         </table>
     </section>
 
-<!-- proper section structure added in ticket 3794, will merge it
-appropiately -->
-
-
-<section id="dummy">
-  <title>MERGE ME</title>
-  <para>
-    <itemizedlist>
-      <listitem>
-        <simpara><emphasis>subnet[id].total-addresses</emphasis> (integer) -
-        this statistic shows the total number of addresses available for the
-        DHCPv4 management. In other words, this is the sum of all addresses in
-        all configured pools. This statistic changes only during configuration
-        changes. Note it does not take into account any addresses that may be
-        reserved due to host reservation. The <emphasis>id</emphasis> is the
-        subnet-id of a given subnet. This statistic is exposed for each subnet
-	separately. This statistic is reset during reconfiguration event.
-	</simpara>
-      </listitem>
-      <listitem>
-        <simpara><emphasis>subnet[id].assigned-addresses</emphasis> (integer) -
-        this statistic shows the number of assigned addresses in a given subnet.
-        This statistic increases every time a new lease is allocated (as a result
-        of receiving a DHCPREQUEST message) and is decreased every time a lease is
-        released (a DHCPRELEASE message is received). When lease expiration
-        is implemented (planned for Kea 1.0), it will also decrease when a lease
-        is expired. The <emphasis>id</emphasis> is the subnet-id of a given
-        subnet. This statistic is exposed for each subnet separately. This
-        statistic is reset during reconfiguration event.
-	</simpara>
-      </listitem>
-    </itemizedlist>
-    </para>
-</section>
-
     <section id="dhcp4-ctrl-channel">
       <title>Management API for the DHCPv4 server</title>
       <para>

+ 237 - 37
doc/guide/dhcp6-srv.xml

@@ -88,9 +88,9 @@ strings <userinput>path</userinput>/kea-dhcp6 | sed -n 's/;;;; //p'
       </para>
 
       <para>
-	    When running in a console, the server can be shut down by
-	    pressing ctrl-c. It detects the key combination and shuts
-	    down gracefully.
+            When running in a console, the server can be shut down by
+            pressing ctrl-c. It detects the key combination and shuts
+            down gracefully.
       </para>
 
       <para>
@@ -506,10 +506,10 @@ temporarily override a list of interface names and listen on all interfaces.
       </para>
 
       <para>
-	The following configuration will assign the specified subnet
-	identifier to the newly configured subnet:
+        The following configuration will assign the specified subnet
+        identifier to the newly configured subnet:
 
-	<screen>
+        <screen>
 "Dhcp6": {
     "subnet6": [
         {
@@ -520,9 +520,9 @@ temporarily override a list of interface names and listen on all interfaces.
     ]
 }
 </screen>
-	This identifier will not change for this subnet unless the "id" parameter is
-	removed or set to 0. The value of 0 forces auto-generation of the subnet
-	identifier.
+        This identifier will not change for this subnet unless the "id" parameter is
+        removed or set to 0. The value of 0 forces auto-generation of the subnet
+        identifier.
       </para>
       <!-- @todo: describe whether database needs to be updated after changing
       id -->
@@ -673,15 +673,15 @@ temporarily override a list of interface names and listen on all interfaces.
       <title>Subnet and Prefix Delegation Pools</title>
       <para>
         Subnets may also be configured to delegate prefixes, as defined in
-	    <ulink url="http://tools.ietf.org/html/rfc3633">RFC 3633</ulink>.
+            <ulink url="http://tools.ietf.org/html/rfc3633">RFC 3633</ulink>.
         A subnet may have one or more prefix delegation pools.  Each pool has
         a prefixed address, which is specified as a prefix and a prefix length,
         as well as a delegated prefix length. <command>delegated-len</command>
-	    must not be shorter (that is it must be numerically greater or equal)
-	    than <command>prefix-len</command>.
-	    If both <command>delegated-len</command>
-	    and <command>prefix-len</command> are equal, the server will be able to
-	    delegate only one prefix. The delegated <command>prefix</command> does
+            must not be shorter (that is it must be numerically greater or equal)
+            than <command>prefix-len</command>.
+            If both <command>delegated-len</command>
+            and <command>prefix-len</command> are equal, the server will be able to
+            delegate only one prefix. The delegated <command>prefix</command> does
         not have to match the <command>subnet</command> prefix.
       </para>
       <para> Below is a sample subnet configuration which enables prefix
@@ -1317,7 +1317,7 @@ should include options from the isc option space:
         to designate that a given subnet is local, i.e. reachable directly over
         the specified interface. For example the server that is intended to serve
         a local subnet over eth0 may be configured as follows:
-	<screen>
+        <screen>
 "Dhcp6": {
     "subnet6": [
         {
@@ -2262,14 +2262,14 @@ should include options from the isc option space:
 
       <para>
         An example configuration that disables reservation looks like follows:
-	<screen>
+        <screen>
 "Dhcp6": {
     "subnet6": [
-	{
+        {
         "subnet": "2001:db8:1::/64",
         <userinput>"reservation-mode": "disabled"</userinput>,
         ...
-	}
+        }
     ]
 }
 </screen>
@@ -2392,9 +2392,9 @@ should include options from the isc option space:
                      "pool": "2001:db8:1::1-2001:db8:1::ffff"
                  }
              ],
-	     <userinput>"relay": {
-	         "ip-address": "3000::1"
-	     }</userinput>
+             <userinput>"relay": {
+                 "ip-address": "3000::1"
+             }</userinput>
         }
     ]
 }
@@ -2425,11 +2425,11 @@ should include options from the isc option space:
 "Dhcp6": {
     "subnet6": [
         {
-	    "subnet": "3000::/64",
-	    "pools": [
-	        { "pool": "3000::2 - 3000::ffff" }
-	    ],
-	    <userinput>"client-class": "VENDOR_CLASS_docsis3.0",
+            "subnet": "3000::/64",
+            "pools": [
+                { "pool": "3000::2 - 3000::ffff" }
+            ],
+            <userinput>"client-class": "VENDOR_CLASS_docsis3.0",
             "relay": {
                 "ip-address": "3000::1"
             }</userinput>
@@ -2442,9 +2442,9 @@ should include options from the isc option space:
                      "pool": "2001:db8:1::1-2001:db8:1::ffff"
                  }
              ],
-	     <userinput>"relay": {
-	         "ip-address": "3000::1"
-	     }</userinput>
+             <userinput>"relay": {
+                 "ip-address": "3000::1"
+             }</userinput>
         }
     ]
 }
@@ -2579,6 +2579,206 @@ should include options from the isc option space:
     </para>
     </section>
 
+    <section id="dhcp6-stats">
+      <title>Statistics in DHCPv6 server</title>
+      <note>
+        <para>This section describes DHCPv6-specific statistics. For a general
+        overview and usage of statistics, see <xref linkend="stats" />.</para>
+      </note>
+
+      <para>
+        The DHCPv6 server supports the following statistics:
+      </para>
+        <table frame="all" id="dhcp6-statistics">
+          <title>DHCPv4 Statistics</title>
+          <tgroup cols='3'>
+          <colspec colname='statistic' align='center'/>
+          <colspec colname='type' align='center'/>
+          <colspec colname='description' align='left'/>
+          <thead>
+            <row>
+              <entry>Statistic</entry>
+              <entry>Data Type</entry>
+              <entry>Description</entry>
+            </row>
+          </thead>
+          <tbody>
+
+            <row>
+              <entry>pkt6-received</entry>
+              <entry>integer</entry>
+              <entry>Number of DHCPv6 packets received. This includes all packets:
+              valid, bogus, corrupted, rejected etc. This statistic is expected
+              to grow rapidly.</entry>
+            </row>
+
+            <row>
+              <entry>pkt6-receive-drop</entry>
+              <entry>integer</entry>
+              <entry>Number of incoming packets that were dropped. Exact reason
+              for dropping packets is logged, but the most common reasons may
+              be: an unacceptable or not supported packet type, direct responses
+              are forbidden, the server-id sent by the client does not match the
+              server's server-id or the packet is malformed.</entry>
+            </row>
+
+            <row>
+              <entry>pkt6-parse-failed</entry>
+              <entry>integer</entry>
+              <entry>Number of incoming packets that could not be parsed.
+              A non-zero value of this statistic indicates that the server
+              received a malformed or truncated packet. This may indicate problems
+              in your network, faulty clients, faulty relay agents or server
+              code bug.</entry>
+            </row>
+
+            <row>
+              <entry>pkt6-solicit-received</entry>
+              <entry>integer</entry>
+              <entry>
+                Number of SOLICIT packets received. This statistic is expected
+                to grow.  Its increase means that clients that just booted
+                started their configuration process and their initial packets
+                reached your server.
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-advertise-received</entry>
+              <entry>integer</entry>
+              <entry>
+                Number of ADVERTISE packets received. Advertise packets are sent
+                by the server and the server is never expected to receive them. A non-zero
+                value of this statistic indicates an error ocurring in the network.
+                One likely cause would be a misbehaving relay agent that incorrectly
+                forwards ADVERTISE messages towards the server, rather back to the
+                clients.
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-request-received</entry>
+              <entry>integer</entry>
+              <entry>Number of REQUEST packets received. This statistic
+                is expected to grow. Its increase means that clients that just booted
+                received the server's response (ADVERTISE), accepted it and are now
+                requesting an address (REQUEST).
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-reply-received</entry>
+              <entry>integer</entry>
+              <entry>Number of REPLY packets received. This statistic is
+              expected to remain zero at all times, as REPLY packets are sent by
+              the server and the server is never expected to receive
+              them. A non-zero value indicates an error. One likely cause would be
+              a misbehaving relay agent that incorrectly forwards REPLY messages
+              towards the server, rather back to the clients.
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-renew-received</entry>
+              <entry>integer</entry>
+              <entry>Number of RENEW packets received. This statistic
+                is expected to grow. Its increase means that clients received their
+                addresses and prefixes and are trying to renew them.
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-rebind-received</entry>
+              <entry>integer</entry>
+              <entry>Number of REBIND packets received. A non-zero value
+              indicates that clients didn't receive responses to their RENEW messages
+              (regular lease renewal mechanism) and are attempting to find any server
+              that is able to take over their leases. It may mean that some server's
+              REPLY messages never reached the clients.
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-release-received</entry>
+              <entry>integer</entry>
+              <entry>Number of RELEASE packets received. This statistic is expected
+              to grow when a device is being shut down in the network. It
+              indicates that the address or prefix assigned is reported as no longer
+              needed. Note that many devices, especially wireless, do not send RELEASE,
+              because of design choice or due to moving out of range.
+              </entry>
+            </row>
+
+            <row>
+            <entry>pkt6-decline-received</entry>
+            <entry>integer</entry>
+            <entry>
+              Number of DECLINE packets received. This statistic is expected to
+              remain close to zero. Its increase means that a client leased an
+              address, but discovered that the address is currently used by an
+              unknown device in your network. If this statistic is growing, it
+              may indicate misconfigured server or devices that have statically
+              assigned conflicting addresses.
+            </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-infrequest-received</entry>
+              <entry>integer</entry>
+              <entry>
+                Number of INFORMATION-REQUEST packets received. This statistic
+                is expected to grow if there are devices that are using
+                stateless DHCPv6. INFORMATION-REQUEST messages are used by
+                clients that request stateless configuration, i.e. options
+                and parameters other than addresses or prefixes.
+              </entry>
+            </row>
+
+            <row>
+              <entry>pkt6-unknown-received</entry>
+              <entry>integer</entry>
+              <entry>Number of packets received of an unknown type. Non-zero
+              value of this statistic indicates that the server received a
+              packet that it wasn't able to recognize: either with unsupported
+              type or possibly malformed.</entry>
+            </row>
+
+            <row>
+              <entry>pkt6-sent</entry>
+              <entry>integer</entry>
+              <entry>Number of DHCPv6 packets sent. This statistic is expected
+              to grow every time the server transmits a packet. In general, it
+              should roughly match pkt6-received, as most incoming packets cause
+              the server to respond. There are exceptions (e.g. server receiving a
+              REQUEST with server-id matching other server), so do not worry, if
+              it is lesser than pkt6-received.</entry>
+            </row>
+
+            <row>
+              <entry>pkt6-advertise-sent</entry>
+              <entry>integer</entry>
+              <entry>Number of ADVERTISE packets sent. This statistic is
+              expected to grow in most cases after a SOLICIT is processed. There
+              are certain uncommon, but valid cases where incoming SOLICIT is
+              dropped, but in general this statistic is expected to be close to
+              pkt6-solicit-received.</entry>
+            </row>
+
+            <row>
+              <entry>pkt6-reply-sent</entry>
+              <entry>integer</entry>
+              <entry>Number of REPLY packets sent. This statistic is expected to
+              grow in most cases after a SOLICIT (with rapid-commit), REQUEST,
+              RENEW, REBIND, RELEASE, DECLINE or INFORMATION-REQUEST is
+              processed. There are certain cases where there is no response.
+              </entry>
+            </row>
+
+          </tbody>
+          </tgroup>
+        </table>
+    </section>
+
     <section id="dhcp6-std">
       <title>Supported DHCPv6 Standards</title>
       <para>The following standards are currently
@@ -2623,13 +2823,13 @@ should include options from the isc option space:
             to echo back the options, checks whether an option is RSOO-enabled,
             ability to mark additional options as RSOO-enabled.</simpara>
           </listitem>
-	  <listitem>
-	    <simpara><emphasis>Client Link-Layer Address Option in
-	    DHCPv6</emphasis>,
-	    <ulink url="http://tools.ietf.org/html/rfc6939">RFC
-	    6939</ulink>: Supported option is client link-layer
-	    address option.</simpara>
-	  </listitem>
+          <listitem>
+            <simpara><emphasis>Client Link-Layer Address Option in
+            DHCPv6</emphasis>,
+            <ulink url="http://tools.ietf.org/html/rfc6939">RFC
+            6939</ulink>: Supported option is client link-layer
+            address option.</simpara>
+          </listitem>
       </itemizedlist>
     </section>
 

+ 103 - 0
src/bin/dhcp6/dhcp6_srv.cc

@@ -45,6 +45,7 @@
 #include <hooks/callout_handle.h>
 #include <hooks/hooks_log.h>
 #include <hooks/hooks_manager.h>
+#include <stats/stats_mgr.h>
 
 #include <util/encode/hex.h>
 #include <util/io_utilities.h>
@@ -73,6 +74,7 @@ using namespace isc::dhcp;
 using namespace isc::dhcp_ddns;
 using namespace isc::hooks;
 using namespace isc::log;
+using namespace isc::stats;
 using namespace isc::util;
 using namespace std;
 
@@ -330,6 +332,12 @@ 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().
+                StatsMgr::instance().addValue("pkt6-received", static_cast<int64_t>(1));
+
             } else {
                 LOG_DEBUG(packet_logger, DBG_DHCP6_DETAIL, DHCP6_BUFFER_WAIT_INTERRUPTED)
                     .arg(timeout);
@@ -431,12 +439,25 @@ bool Dhcpv6Srv::run() {
                     .arg(query->getLocalAddr().toText())
                     .arg(query->getIface())
                     .arg(e.what());
+
+                // Increase the statistics of parse failures and dropped packets.
+                StatsMgr::instance().addValue("pkt6-parse-failed",
+                                              static_cast<int64_t>(1));
+                StatsMgr::instance().addValue("pkt6-receive-drop",
+                                              static_cast<int64_t>(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.
+            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
             continue;
         }
 
@@ -444,6 +465,9 @@ 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.
+            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
             continue;
         }
 
@@ -541,6 +565,9 @@ bool Dhcpv6Srv::run() {
                 .arg(query->getRemoteAddr().toText())
                 .arg(e.what());
 
+            // Increase the statistic of dropped packets.
+            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
+
         } catch (const isc::Exception& e) {
 
             // Catch-all exception (at least for ones based on the isc Exception
@@ -553,6 +580,9 @@ bool Dhcpv6Srv::run() {
                 .arg(query->getName())
                 .arg(query->getRemoteAddr().toText())
                 .arg(e.what());
+
+            // Increase the statistic of dropped packets.
+            StatsMgr::instance().addValue("pkt6-receive-drop", static_cast<int64_t>(1));
         }
 
         if (rsp) {
@@ -665,6 +695,10 @@ bool Dhcpv6Srv::run() {
                     .arg(static_cast<int>(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 +2932,74 @@ void Dhcpv6Srv::processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp) {
     }
 }
 
+void Dhcpv6Srv::processStatsReceived(const Pkt6Ptr& query) {
+    // Note that we're not bumping pkt6-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
+    }
+
+    StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
+}
+
+void Dhcpv6Srv::processStatsSent(const Pkt6Ptr& response) {
+    // Increase generic counter for sent packets.
+    StatsMgr::instance().addValue("pkt6-sent", static_cast<int64_t>(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;
+    }
+
+    StatsMgr::instance().addValue(stat_name, static_cast<int64_t>(1));
+}
+
 };
 };

+ 8 - 0
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

+ 8 - 1
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -295,6 +295,9 @@ Dhcp6Client::doSARR() {
 void
 Dhcp6Client::doSolicit() {
     context_.query_ = createMsg(DHCPV6_SOLICIT);
+    if (forced_server_id_) {
+        context_.query_->addOption(forced_server_id_);
+    }
     if (use_na_) {
         context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
                                                               1234)));
@@ -327,7 +330,11 @@ Dhcp6Client::doSolicit() {
 void
 Dhcp6Client::doRequest() {
     Pkt6Ptr query = createMsg(DHCPV6_REQUEST);
-    query->addOption(context_.response_->getOption(D6O_SERVERID));
+    if (!forced_server_id_) {
+        query->addOption(context_.response_->getOption(D6O_SERVERID));
+    } else {
+        query->addOption(forced_server_id_);
+    }
     copyIAs(context_.response_, query);
 
     // Add Client FQDN if configured.

+ 14 - 0
src/bin/dhcp6/tests/dhcp6_client.h

@@ -448,6 +448,17 @@ public:
         use_rapid_commit_ = rapid_commit;
     }
 
+    /// @brief Specifies server-id to be used in send messages
+    ///
+    /// Overrides the server-id to be sent when server-id is expected to be
+    /// sent. May be NULL, which means use proper server-id sent in Advertise
+    /// (which is a normal client behavior).
+    ///
+    /// @param server_id server-id to be sent
+    void useServerId(const OptionPtr& server_id) {
+        forced_server_id_ = server_id;
+    }
+
     /// @brief Creates an instance of the Client FQDN option to be included
     /// in the client's message.
     ///
@@ -614,6 +625,9 @@ private:
     /// to true. See @ref sendORO for details.
     std::vector<uint16_t> oro_;
 
+    /// @brief forced (Overridden) value of the server-id option (may be NULL)
+    OptionPtr forced_server_id_;
+
     /// @brief FQDN requested by the client.
     Option6ClientFqdnPtr fqdn_;
 };

+ 83 - 2
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -37,7 +37,7 @@
 #include <dhcpsrv/utils.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
-#include <hooks/server_hooks.h>
+#include <stats/stats_mgr.h>
 
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_client.h>
@@ -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,88 @@ 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_REPLY, "pkt6-reply-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.

+ 85 - 0
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -17,8 +17,10 @@
 #include <dhcp/option6_status_code.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/json_config_parser.h>
+#include <dhcp/tests/pkt_captures.h>
 #include <util/pointer_util.h>
 #include <cc/command_interpreter.h>
+#include <stats/stats_mgr.h>
 #include <string.h>
 
 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
@@ -742,6 +746,44 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& 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<Option6IA>
 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

+ 13 - 36
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -127,28 +127,16 @@ public:
     std::list<isc::dhcp::Pkt6Ptr> 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<isc::dhcp::Option6IA> 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_;
 

+ 58 - 0
src/bin/dhcp6/tests/infrequest_unittest.cc

@@ -18,6 +18,7 @@
 #include <dhcp6/tests/dhcp6_client.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_client_fqdn.h>
+#include <stats/stats_mgr.h>
 
 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

+ 148 - 0
src/bin/dhcp6/tests/sarr_unittest.cc

@@ -19,6 +19,8 @@
 #include <dhcp6/tests/dhcp6_client.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/d2_client_mgr.h>
+#include <asiolink/io_address.h>
+#include <stats/stats_mgr.h>
 
 using namespace isc;
 using namespace isc::dhcp;
@@ -101,6 +103,8 @@ public:
     SARRTest()
         : Dhcpv6SrvTest(),
           iface_mgr_test_config_(true) {
+        // Let's wipe all existing statistics.
+        isc::stats::StatsMgr::instance().removeAll();
     }
 
     /// @brief Destructor.
@@ -109,6 +113,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 +283,146 @@ 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
+    // ask 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);
+}
+
+// This test verifies that pkt6-receive-drop is increased properly when the
+// client's packet is rejected due to mismatched server-id value.
+TEST_F(SARRTest, pkt6ReceiveDropStat1) {
+
+    // Dummy server-id (0xff repeated 10 times)
+    std::vector<uint8_t> data(10, 0xff);
+    OptionPtr bogus_srv_id(new Option(Option::V6, D6O_SERVERID, data));
+
+    // Let's use one of the existing configurations and tell the client to
+    // ask for an address.
+    Dhcp6Client client;
+    configure(CONFIGS[1], *client.getServer());
+    client.setInterface("eth1");
+    client.useNA();
+
+    client.doSolicit();
+    client.useServerId(bogus_srv_id);
+    client.doRequest();
+
+    // Ok, let's check the statistic. pkt6-receive-drop should be set to 1.
+    using namespace isc::stats;
+    StatsMgr& mgr = StatsMgr::instance();
+
+    ObservationPtr pkt6_recv_drop = mgr.getObservation("pkt6-receive-drop");
+    ASSERT_TRUE(pkt6_recv_drop);
+
+    EXPECT_EQ(1, pkt6_recv_drop->getInteger().first);
+}
+
+// This test verifies that pkt6-receive-drop is increased properly when the
+// client's packet is rejected due to being unicast communication.
+TEST_F(SARRTest, pkt6ReceiveDropStat2) {
+
+    // Let's use one of the existing configurations and tell the client to
+    // ask for an address.
+    Dhcp6Client client;
+    configure(CONFIGS[1], *client.getServer());
+    client.setInterface("eth1");
+    client.useNA();
+
+    client.setDestAddress(asiolink::IOAddress("2001:db8::1")); // Pretend it's unicast
+    client.doSolicit();
+
+    // Ok, let's check the statistic. pkt6-receive-drop should be set to 1.
+    using namespace isc::stats;
+    StatsMgr& mgr = StatsMgr::instance();
+
+    ObservationPtr pkt6_recv_drop = mgr.getObservation("pkt6-receive-drop");
+    ASSERT_TRUE(pkt6_recv_drop);
+
+    EXPECT_EQ(1, pkt6_recv_drop->getInteger().first);
+}
+
+// This test verifies that pkt6-receive-drop is increased properly when the
+// client's packet is rejected due to having too many client-id options
+// (exactly one is expected).
+TEST_F(SARRTest, pkt6ReceiveDropStat3) {
+
+    // Let's use one of the existing configurations and tell the client to
+    // ask for an address.
+    Dhcp6Client client;
+    configure(CONFIGS[1], *client.getServer());
+    client.setInterface("eth1");
+    client.useNA();
+
+    // Let's send our client-id as server-id. That will result in the
+    // packet containing the client-id twice. That should cause RFCViolation
+    // exception.
+    client.useServerId(client.getClientId());
+    client.doSolicit();
+
+    // Ok, let's check the statistic. pkt6-receive-drop should be set to 1.
+    using namespace isc::stats;
+    StatsMgr& mgr = StatsMgr::instance();
+
+    ObservationPtr pkt6_recv_drop = mgr.getObservation("pkt6-receive-drop");
+    ASSERT_TRUE(pkt6_recv_drop);
+
+    EXPECT_EQ(1, pkt6_recv_drop->getInteger().first);
+}
+
 
 } // end of anonymous namespace