Parcourir la source

[master] Merge branch 'trac2949' (inf-request in dhcpv6)

Tomek Mrugalski il y a 10 ans
Parent
commit
3185d229c3

+ 1 - 0
doc/Makefile.am

@@ -10,6 +10,7 @@ nobase_dist_doc_DATA += examples/kea6/simple.json
 nobase_dist_doc_DATA += examples/kea6/several-subnets.json
 nobase_dist_doc_DATA += examples/kea6/multiple-options.json
 nobase_dist_doc_DATA += examples/kea6/advanced.json
+nobase_dist_doc_DATA += examples/kea6/stateless.json
 nobase_dist_doc_DATA += examples/ddns/sample1.json
 nobase_dist_doc_DATA += examples/ddns/template.json
 

+ 23 - 0
doc/examples/kea6/stateless.json

@@ -0,0 +1,23 @@
+# A very simply stateless configuration that provides information about DNS
+# servers to all clients, regardless of their point of attachment.
+#
+# It is also possible to specify options on a per subnet basis
+# in the same way as in stateful mode.
+#
+
+{
+"Dhcp6": {
+    "interfaces": [ "ethX" ],
+
+# This is the list of options that will be granted to all clients that ask.
+    "option-data": [ {
+        "name": "dns-servers",
+        "data": "2001:db8::1, 2001:db8::2"
+    } ],
+
+# Kea 0.9.1 requires lease-database to be specified, even it is not used.
+# In stateless mode, only options are granted, not addresses or prefixes, so
+# there will be no leases (unless stateless and stateful mode is used together).
+    "lease-database": { "type": "memfile" }
+}
+}

+ 44 - 2
doc/guide/dhcp6-srv.xml

@@ -1743,6 +1743,49 @@ should include options from the isc option space:
 
     </section>
 
+    <section id="stateless-dhcp6">
+      <title>Stateless DHCPv6 (Information-Request Message)</title>
+      <para>Typically DHCPv6 is used to assign both addresses and options. These
+      assignments (leases) have state that changes over time, hence
+      their name, stateful. DHCPv6 also supports a stateless mode,
+      where clients request configuration options only. This mode is
+      considered lightweight from the server perspective, as it does not require
+      any state tracking; hence its name.</para>
+      <para>The Kea server supports stateless mode. Clients can send
+      Information-Request messages and the server will send back
+      answers with the requested options (providing the options are
+      available in the server configuration).  The server will attempt to
+      use per-subnet options first. If that fails - for whatever reason - it
+      will then try to provide options defined in the global scope.</para>
+
+      <para>Stateless and stateful mode can be used together. No special
+      configuration directives are required to handle this. Simply use the
+      configuration for stateful clients and the stateless clients will get
+      just options they requested.</para>
+
+      <para>This usage of global options allows for an interesting case.
+      It is possible to run a server that provides just options and no
+      addresses or prefixes. If the options have the same value in each
+      subnet, the configuration can define required options in the global
+      scope and skip subnet definitions altogether. Here's a simple example of
+      such a configuration:
+<screen>
+"Dhcp6": {
+    "interfaces": [ "ethX" ],
+    <userinput>"option-data": [ {
+        "name": "dns-servers",
+        "data": "2001:db8::1, 2001:db8::2"
+    } ]</userinput>,
+    "lease-database": { "type": "memfile" }
+ }
+</screen>
+      This very simple configuration will provide DNS server information
+      to all clients in the network, regardless of their location. Note the
+      specification of the memfile lease database: this is required since,
+      as of version 0.9.1, Kea requires a lease database to be specified
+      even if it is not used.</para>
+    </section>
+
     <section id="dhcp6-relay-override">
       <title>Using specific relay agent for a subnet</title>
       <para>
@@ -2051,8 +2094,7 @@ should include options from the isc option space:
 
         <listitem>
           <simpara>
-            Duplication report (DECLINE), stateless configuration
-            (INFORMATION-REQUEST) and client reconfiguration (RECONFIGURE) are
+            Duplication report (DECLINE) and client reconfiguration (RECONFIGURE) are
             not yet supported.
           </simpara>
         </listitem>

+ 50 - 23
src/bin/dhcp6/dhcp6_srv.cc

@@ -701,7 +701,7 @@ Dhcpv6Srv::generateServerID() {
 }
 
 void
-Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+Dhcpv6Srv::copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     // Add client-id.
     OptionPtr clientid = question->getOption(D6O_CLIENTID);
     if (clientid) {
@@ -724,29 +724,43 @@ Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) {
 
 void
 Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
-    // Get the configured subnet suitable for the incoming packet.
-    Subnet6Ptr subnet = selectSubnet(question);
-    // Leave if there is no subnet matching the incoming packet.
-    // There is no need to log the error message here because
-    // it will be logged in the assignLease() when it fails to
-    // pick the suitable subnet. We don't want to duplicate
-    // error messages in such case.
-    if (!subnet) {
-        return;
-    }
 
     // Client requests some options using ORO option. Try to
     // get this option from client's message.
     boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
-        boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(question->getOption(D6O_ORO));
-    // Option ORO not found. Don't do anything then.
+        boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >
+        (question->getOption(D6O_ORO));
+
+    // Option ORO not found? We're done here then.
     if (!option_oro) {
         return;
     }
+
+    // Get global option definitions (i.e. options defined to apply to all,
+    // unless overwritten on a subnet or host level)
+    ConstCfgOptionPtr global_opts = CfgMgr::instance().getCurrentCfg()->
+        getCfgOption();
+
+    // Get the configured subnet suitable for the incoming packet.
+    // It may be NULL (if server is misconfigured or the client was rejected
+    // using client classes).
+    Subnet6Ptr subnet = selectSubnet(question);
+
     // Get the list of options that client requested.
     const std::vector<uint16_t>& requested_opts = option_oro->getValues();
     BOOST_FOREACH(uint16_t opt, requested_opts) {
-        OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", opt);
+        // If we found a subnet for this client, try subnet first.
+        if (subnet) {
+            OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", opt);
+            if (desc.option_) {
+                // Attempt to assign an option from subnet first.
+                answer->addOption(desc.option_);
+                continue;
+            }
+        }
+
+        // If subnet specific option is not there, try global.
+        OptionDescriptor desc = global_opts->get("dhcp6", opt);
         if (desc.option_) {
             answer->addOption(desc.option_);
         }
@@ -2272,7 +2286,7 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 
     Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
-    copyDefaultOptions(solicit, advertise);
+    copyClientOptions(solicit, advertise);
     appendDefaultOptions(solicit, advertise);
     appendRequestedOptions(solicit, advertise);
     appendRequestedVendorOptions(solicit, advertise);
@@ -2295,7 +2309,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
 
-    copyDefaultOptions(request, reply);
+    copyClientOptions(request, reply);
     appendDefaultOptions(request, reply);
     appendRequestedOptions(request, reply);
     appendRequestedVendorOptions(request, reply);
@@ -2315,7 +2329,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
 
-    copyDefaultOptions(renew, reply);
+    copyClientOptions(renew, reply);
     appendDefaultOptions(renew, reply);
     appendRequestedOptions(renew, reply);
 
@@ -2332,7 +2346,7 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
 
-    copyDefaultOptions(rebind, reply);
+    copyClientOptions(rebind, reply);
     appendDefaultOptions(rebind, reply);
     appendRequestedOptions(rebind, reply);
 
@@ -2356,7 +2370,7 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
     // The server sends Reply message in response to Confirm.
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
     // Make sure that the necessary options are included.
-    copyDefaultOptions(confirm, reply);
+    copyClientOptions(confirm, reply);
     appendDefaultOptions(confirm, reply);
     // Indicates if at least one address has been verified. If no addresses
     // are verified it means that the client has sent no IA_NA options
@@ -2430,7 +2444,7 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
 
-    copyDefaultOptions(release, reply);
+    copyClientOptions(release, reply);
     appendDefaultOptions(release, reply);
 
     releaseLeases(release, reply);
@@ -2445,14 +2459,27 @@ Pkt6Ptr
 Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
     /// @todo: Implement this
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
-    return reply;
+    return (reply);
 }
 
 Pkt6Ptr
 Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
-    /// @todo: Implement this
+
+    // Create a Reply packet, with the same trans-id as the client's.
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
-    return reply;
+
+    // Copy client options (client-id, also relay information if present)
+    copyClientOptions(infRequest, reply);
+
+    // Append default options, i.e. options that the server is supposed
+    // to put in all messages it sends (server-id for now, but possibly other
+    // options once we start supporting authentication)
+    appendDefaultOptions(infRequest, reply);
+
+    // Try to assign options that were requested by the client.
+    appendRequestedOptions(infRequest, reply);
+
+    return (reply);
 }
 
 size_t

+ 2 - 1
src/bin/dhcp6/dhcp6_srv.h

@@ -394,10 +394,11 @@ protected:
     /// Copies options that must appear in any server response (ADVERTISE, REPLY)
     /// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
     /// One notable example is client-id. Other options may be copied as required.
+    /// Relay information details are also copied here.
     ///
     /// @param question client's message (options will be copied from here)
     /// @param answer server's message (options will be copied here)
-    void copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
+    void copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
     /// @brief Appends default options to server's answer.
     ///

+ 2 - 1
src/bin/dhcp6/tests/Makefile.am

@@ -25,7 +25,7 @@ AM_CPPFLAGS += -DTOP_BUILDDIR="\"$(top_builddir)\""
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
 
-CLEANFILES  = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
+CLEANFILES  = $(builddir)/logger_lockfile
 CLEANFILES += $(builddir)/load_marker.txt $(builddir)/unload_marker.txt
 CLEANFILES += *.json *.log
 
@@ -84,6 +84,7 @@ dhcp6_unittests_SOURCES += rebind_unittest.cc
 dhcp6_unittests_SOURCES += sarr_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
 dhcp6_unittests_SOURCES += confirm_unittest.cc
+dhcp6_unittests_SOURCES += infrequest_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
 
 if CONFIG_BACKEND_BUNDY

+ 45 - 3
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -16,6 +16,7 @@
 #include <dhcp/option_custom.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/lease.h>
 #include <dhcp6/tests/dhcp6_client.h>
@@ -40,6 +41,8 @@ Dhcp6Client::Dhcp6Client() :
     use_na_(false),
     use_pd_(false),
     use_relay_(false),
+    use_oro_(false),
+    use_client_id_(true),
     prefix_hint_() {
 }
 
@@ -52,7 +55,10 @@ Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
     srv_(srv),
     use_na_(false),
     use_pd_(false),
-    use_relay_(false) {
+    use_relay_(false),
+    use_oro_(false),
+    use_client_id_(true),
+    prefix_hint_() {
 }
 
 void
@@ -245,7 +251,17 @@ Dhcp6Client::createLease(const Lease6& lease) {
 Pkt6Ptr
 Dhcp6Client::createMsg(const uint8_t msg_type) {
     Pkt6Ptr msg(new Pkt6(msg_type, curr_transid_++));
-    msg->addOption(getClientId());
+
+    if (use_client_id_) {
+        msg->addOption(getClientId());
+    }
+    if (use_oro_) {
+        OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
+        oro->setValues(oro_);
+
+        msg->addOption(oro);
+    };
+
     return (msg);
 }
 
@@ -296,6 +312,32 @@ Dhcp6Client::doRequest() {
 }
 
 void
+Dhcp6Client::doInfRequest() {
+    context_.query_ = createMsg(DHCPV6_INFORMATION_REQUEST);
+
+    // IA_NA, IA_TA and IA_PD options are not allowed in INF-REQUEST,
+    // but hey! Let's test it.
+    if (use_na_) {
+        // Insert IA_NA option with iaid=1234.
+        context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
+                                                              1234)));
+    }
+
+    // IA-PD is also not allowed. So it may be useful in testing, too.
+    if (use_pd_) {
+        // Insert IA_PD option with iaid=5678
+        Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678));
+        if (prefix_hint_) {
+            ia->addOption(prefix_hint_);
+        }
+        context_.query_->addOption(ia);
+    }
+
+    sendMsg(context_.query_);
+    context_.response_ = receiveOneMsg();
+}
+
+void
 Dhcp6Client::doRebind() {
     Pkt6Ptr query = createMsg(DHCPV6_REBIND);
     copyIAsFromLeases(query);

+ 45 - 1
src/bin/dhcp6/tests/dhcp6_client.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -22,6 +22,7 @@
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
 #include <set>
+#include <vector>
 
 namespace isc {
 namespace dhcp {
@@ -217,6 +218,16 @@ public:
     /// receiving server's response (if any).
     void doConfirm();
 
+
+    /// @brief Performs stateless (inf-request / reply) exchange.
+    ///
+    /// This function generates Information-request message, sends it
+    /// to the server and then receives the reply. Contents of the Inf-Request
+    /// are controlled by use_na_, use_pd_, use_client_id_ and use_oro_
+    /// fields. This method does not process the response in any specific
+    /// way, just stores it.
+    void doInfRequest();
+
     /// @brief Removes the stateful configuration obtained from the server.
     ///
     /// It removes all leases held by the client.
@@ -365,12 +376,36 @@ public:
         relay_link_addr_ = link_addr;
     }
 
+    /// @brief Controls whether the client should send a client-id or not
+    /// @param send should the client-id be sent?
+    void useClientId(bool send) {
+        use_client_id_ = send;
+    }
+
     /// @brief Lease configuration obtained by the client.
     Configuration config_;
 
     /// @brief Link address of the relay to be used for relayed messages.
     asiolink::IOAddress relay_link_addr_;
 
+    /// @brief Controls whether the client will send ORO
+    ///
+    /// The actual content of the ORO is specified in oro_.
+    /// It is useful to split the actual content and the ORO sending
+    /// decision, so we could test cases of sending empty ORO.
+    /// @param send controls whether ORO will be sent or not.
+    void useORO(bool send) {
+        use_oro_ = send;
+    }
+
+    /// @brief Instructs client to request specified option in ORO
+    ///
+    /// @param option_code client will request this option code
+    void requestOption(uint16_t option_code) {
+        use_oro_ = true;
+        oro_.push_back(option_code);
+    }
+
 private:
 
     /// @brief Applies the new leases for the client.
@@ -470,8 +505,17 @@ private:
     bool use_pd_;    ///< Enable prefix delegation.
     bool use_relay_; ///< Enable relaying messages to the server.
 
+    bool use_oro_;  ///< Conth
+    bool use_client_id_;
+
     /// @brief Pointer to the option holding a prefix hint.
     Option6IAPrefixPtr prefix_hint_;
+
+    /// @brief List of options to be requested
+    ///
+    /// Content of this vector will be sent as ORO if use_oro_ is set
+    /// to true. See @ref sendORO for details.
+    std::vector<uint16_t> oro_;
 };
 
 } // end of namespace isc::dhcp::test

+ 0 - 5
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -186,15 +186,10 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
     checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
 }
 
-
 // Test verifies that the Dhcpv6_srv class can be instantiated. It checks a mode
 // without open sockets and with sockets opened on a high port (to not require
 // root privileges).
 TEST_F(Dhcpv6SrvTest, basic) {
-    // srv has stubbed interface detection. It will read
-    // interfaces.txt instead. It will pretend to have detected
-    // fe80::1234 link-local address on eth0 interface. Obviously
-    // an attempt to bind this socket will fail.
     boost::scoped_ptr<Dhcpv6Srv> srv;
 
     ASSERT_NO_THROW( {

+ 32 - 1
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -16,6 +16,7 @@
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/json_config_parser.h>
 #include <config/ccsession.h>
+#include <string.h>
 
 using namespace isc::data;
 using namespace isc::dhcp;
@@ -621,5 +622,35 @@ NakedDhcpv6SrvTest::generateIA(uint16_t type, uint32_t iaid, uint32_t t1,
     return (ia);
 }
 
+bool
+Dhcpv6SrvTest::compareOptions(const isc::dhcp::OptionPtr& option1,
+                              const isc::dhcp::OptionPtr& option2) {
+    if ((!option1 && option2) || (option1 && !option2)) {
+        return (false);
+    }
+    if (!option1 && !option2) {
+        return (true);
+    }
+
+    // We could start by comparing option codes and option lengths
+    // here, but it's just a waste of time. These are tests, so they
+    // don't have to be super performant. The pack+memcmp approach
+    // verifies all in one go.
+
+    isc::util::OutputBuffer buf1(0);
+    isc::util::OutputBuffer buf2(0);
+
+    option1->pack(buf1);
+    option2->pack(buf2);
+
+    if (buf1.getLength() != buf2.getLength()) {
+        return (false);
+    }
+
+    // memcmp returns 0 when equal.
+    return (!memcmp(buf1.getData(), buf2.getData(), buf1.getLength()));
+}
+
+
 }; // end of isc::test namespace
 }; // end of isc namespace

+ 17 - 1
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2014  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2015  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -462,6 +462,22 @@ public:
              const isc::asiolink::IOAddress& addr,
              const uint8_t prefix_len, const uint32_t iaid);
 
+    /// @brief Compare options
+    ///
+    /// This method compares whether options content is identical. It writes
+    /// both options to a buffer and then compares the buffers. Comparing
+    /// two different instances of an option that has identical content
+    /// will return true.
+    ///
+    /// It is safe to pass NULL pointers. Two NULL pointers are equal.
+    /// NULL pointer and non-NULL pointers are obviously non-equal.
+    ///
+    /// @param option1 pointer to the first option
+    /// @param option2
+    /// @return true, if content is identical
+    bool compareOptions(const isc::dhcp::OptionPtr& option1,
+                        const isc::dhcp::OptionPtr& option2);
+
     /// @brief Performs basic (positive) RENEW test
     ///
     /// See renewBasic and pdRenewBasic tests for detailed explanation.

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

@@ -0,0 +1,299 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
+#include <dhcp6/tests/dhcp6_client.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option6_client_fqdn.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used by the Information-Request unit tests.
+///
+/// - Configuration 0:
+///   - one subnet used on eth0 interface
+///     - with address and prefix pools
+///     - dns-servers option
+/// - Configuation 1:
+///   - one subnet used on eth0 interface
+///     - no addresses or prefixes
+///     - domain-search option
+/// - Configuration 2:
+///   - one subnet used on eth0 interface
+///     - dns-servers option for subnet
+///   - sip-servers defined in global scope
+/// - Configuration 3:
+///   - nis-server, nis-domain specified in global scope
+///   - no subnets defined
+const char* CONFIGS[] = {
+    // Configuration 0
+    "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:3::\", "
+        "          \"prefix-len\": 48, "
+        "          \"delegated-len\": 64"
+        "        } ],"
+        "    \"option-data\": [ {"
+        "        \"name\": \"dns-servers\","
+        "        \"data\": \"2001:db8::1, 2001:db8::2\""
+        "    } ],"
+        "    \"subnet\": \"2001:db8::/32\", "
+        "    \"interface\": \"eth0\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+    // Configuration 1
+    "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"option-data\": [ {"
+        "    \"name\": \"sip-server-addr\","
+        "    \"data\": \"2001:db8::abcd\""
+        "    } ],"
+        "    \"subnet\": \"2001:db8::/32\", "
+        "    \"interface\": \"eth0\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+    // Configuration 2
+    "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "    \"option-data\": [ {"
+        "        \"name\": \"sip-server-addr\","
+        "        \"data\": \"2001:db8::1\""
+        "    } ],"
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8::/32\", "
+        "    \"interface\": \"eth0\","
+        "    \"option-data\": [ {"
+        "        \"name\": \"dns-servers\","
+        "        \"data\": \"2001:db8::2\""
+        "    } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+    // Configuration 3
+    "{ \"interfaces\": [ \"*\" ],"
+        "\"option-data\": [ {"
+        "    \"name\": \"nis-servers\","
+        "    \"data\": \"2001:db8::1, 2001:db8::2\""
+        " } ]"
+        "}"
+};
+
+/// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise,
+/// Request-Reply.
+class InfRequestTest : public Dhcpv6SrvTest {
+public:
+    /// @brief Constructor.
+    ///
+    /// Sets up fake interfaces.
+    InfRequestTest()
+        : Dhcpv6SrvTest(),
+          iface_mgr_test_config_(true) {
+    }
+
+    /// @brief Interface Manager's fake configuration control.
+    IfaceMgrTestConfig iface_mgr_test_config_;
+};
+
+/// Check that server processes correctly an incoming inf-request in a
+/// typical subnet that has also address and prefix pools.
+TEST_F(InfRequestTest, infRequestBasic) {
+    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());
+
+    // 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);
+
+    // Check that it contains our client-id
+    OptionPtr client_id = response->getOption(D6O_CLIENTID);
+    ASSERT_TRUE(client_id);
+    EXPECT_TRUE(compareOptions(client_id, client.getClientId()));
+
+    // Check that it contains proper server-id
+    OptionPtr server_id = response->getOption(D6O_SERVERID);
+    ASSERT_TRUE(server_id);
+    EXPECT_TRUE(compareOptions(server_id, client.getServer()->getServerID()));
+
+    // Check that we received requested DNS servers option
+    Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst>
+                            (response->getOption(D6O_NAME_SERVERS));
+    ASSERT_TRUE(dns);
+    Option6AddrLst::AddressContainer addrs = dns->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_EQ("2001:db8::1", addrs[0].toText());
+    EXPECT_EQ("2001:db8::2", addrs[1].toText());
+}
+
+/// Check that server processes correctly an incoming inf-request
+/// that does not hold client-id. It's so called anonymous inf-request.
+/// Uncommon, but certainly valid behavior.
+TEST_F(InfRequestTest, infRequestAnonymous) {
+    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());
+
+    // Perform 2-way exchange (Inf-request/reply)
+    client.requestOption(D6O_NAME_SERVERS);
+    client.useClientId(false);
+    ASSERT_NO_THROW(client.doInfRequest());
+
+    // Confirm that there's a response
+    Pkt6Ptr response = client.getContext().response_;
+    ASSERT_TRUE(response);
+
+    // Check that we received the requested DNS servers option
+    Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst>
+                            (response->getOption(D6O_NAME_SERVERS));
+    ASSERT_TRUE(dns);
+    Option6AddrLst::AddressContainer addrs = dns->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_EQ("2001:db8::1", addrs[0].toText());
+    EXPECT_EQ("2001:db8::2", addrs[1].toText());
+}
+
+/// Check that server processes correctly an incoming inf-request
+/// if there is a subnet without any addresses or prefixes configured.
+TEST_F(InfRequestTest, infRequestStateless) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_PD.
+    configure(CONFIGS[1], *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());
+
+    // Perform 2-way exchange (Inf-request/reply)
+    client.requestOption(D6O_SIP_SERVERS_ADDR);
+    ASSERT_NO_THROW(client.doInfRequest());
+
+    // Confirm that there's a response
+    Pkt6Ptr response = client.getContext().response_;
+    ASSERT_TRUE(response);
+
+    // Check that we received the requested SIP servers option
+    Option6AddrLstPtr sip = boost::dynamic_pointer_cast<Option6AddrLst>
+                            (response->getOption(D6O_SIP_SERVERS_ADDR));
+    ASSERT_TRUE(sip);
+    Option6AddrLst::AddressContainer addrs = sip->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("2001:db8::abcd", addrs[0].toText());
+}
+
+/// Check that server processes correctly an incoming inf-request
+/// if there are options defined at both global and subnet scope.
+TEST_F(InfRequestTest, infRequestSubnetAndGlobal) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_PD.
+    configure(CONFIGS[2], *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());
+
+    // Perform 2-way exchange (Inf-request/reply)
+    client.requestOption(D6O_SIP_SERVERS_ADDR);
+    client.requestOption(D6O_NAME_SERVERS);
+    ASSERT_NO_THROW(client.doInfRequest());
+
+    // Confirm that there's a response
+    Pkt6Ptr response = client.getContext().response_;
+    ASSERT_TRUE(response);
+
+    // Check that we received the requested sip servers option
+    Option6AddrLstPtr sip = boost::dynamic_pointer_cast<Option6AddrLst>
+                            (response->getOption(D6O_SIP_SERVERS_ADDR));
+    ASSERT_TRUE(sip);
+    Option6AddrLst::AddressContainer addrs = sip->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("2001:db8::1", addrs[0].toText());
+
+    // Check that we received the requested dns servers option
+    Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst>
+                            (response->getOption(D6O_NAME_SERVERS));
+    ASSERT_TRUE(dns);
+    addrs = dns->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    EXPECT_EQ("2001:db8::2", addrs[0].toText());
+}
+
+/// Check that server processes correctly an incoming inf-request
+/// if there are options defined at global scope only (no subnets).
+TEST_F(InfRequestTest, infRequestNoSubnets) {
+    Dhcp6Client client;
+
+    // Configure client to request IA_PD.
+    configure(CONFIGS[3], *client.getServer());
+    // Make sure we ended-up having expected number of subnets configured.
+    const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
+        getCfgSubnets6()->getAll();
+    ASSERT_EQ(0, subnets->size());
+
+    // Perform 2-way exchange (Inf-request/reply)
+    client.requestOption(D6O_NIS_SERVERS);
+    ASSERT_NO_THROW(client.doInfRequest());
+
+    // Confirm that there's a response
+    Pkt6Ptr response = client.getContext().response_;
+    ASSERT_TRUE(response);
+
+    // Check that we received the requested sip servers option
+    Option6AddrLstPtr nis = boost::dynamic_pointer_cast<Option6AddrLst>
+                            (response->getOption(D6O_NIS_SERVERS));
+    ASSERT_TRUE(nis);
+    Option6AddrLst::AddressContainer addrs = nis->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_EQ("2001:db8::1", addrs[0].toText());
+    EXPECT_EQ("2001:db8::2", addrs[1].toText());
+}
+
+
+
+
+} // end of anonymous namespace