]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3094] Checkpoint: added UTs, doc to do
authorFrancis Dupont <fdupont@isc.org>
Sun, 30 Jun 2024 11:32:01 +0000 (13:32 +0200)
committerFrancis Dupont <fdupont@isc.org>
Thu, 18 Jul 2024 12:44:32 +0000 (14:44 +0200)
src/bin/dhcp4/dhcp4_messages.cc
src/bin/dhcp4/dhcp4_messages.h
src/bin/dhcp4/dhcp4_messages.mes
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

index 793b3a745614121d087eb0f8299286ceff56d0b5..72f0731feea50d72aa8a477db99dbdc4a6155607 100644 (file)
@@ -174,6 +174,8 @@ extern const isc::log::MessageID DHCP4_SUBNET_SELECTED = "DHCP4_SUBNET_SELECTED"
 extern const isc::log::MessageID DHCP4_SUBNET_SELECTION_FAILED = "DHCP4_SUBNET_SELECTION_FAILED";
 extern const isc::log::MessageID DHCP4_TESTING_MODE_SEND_TO_SOURCE_ENABLED = "DHCP4_TESTING_MODE_SEND_TO_SOURCE_ENABLED";
 extern const isc::log::MessageID DHCP4_UNKNOWN_ADDRESS_REQUESTED = "DHCP4_UNKNOWN_ADDRESS_REQUESTED";
+extern const isc::log::MessageID DHCP4_V6_ONLY_PREFERRED_MISSING_IN_ACK = "DHCP4_V6_ONLY_PREFERRED_MISSING_IN_ACK";
+extern const isc::log::MessageID DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER = "DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER";
 
 } // namespace dhcp
 } // namespace isc
@@ -348,6 +350,8 @@ const char* values[] = {
     "DHCP4_SUBNET_SELECTION_FAILED", "%1: failed to select subnet for the client",
     "DHCP4_TESTING_MODE_SEND_TO_SOURCE_ENABLED", "All packets will be send to source address of an incoming packet - use only for testing",
     "DHCP4_UNKNOWN_ADDRESS_REQUESTED", "%1: client requested an unknown address, client sent ciaddr %2, requested-ip-address %3",
+    "DHCP4_V6_ONLY_PREFERRED_MISSING_IN_ACK", "v6-only-preferred option missing in 0.0.0.0 offer to query: %1",
+    "DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER", "v6-only-preferred option missing in 0.0.0.0 offer to query: %1",
     NULL
 };
 
index 50d550d757a13a71a0de161d9f81bcdc4d2a0a72..22400d909de9ec325ab2bd1c1d81b2cbcf7b0b0f 100644 (file)
@@ -175,6 +175,8 @@ extern const isc::log::MessageID DHCP4_SUBNET_SELECTED;
 extern const isc::log::MessageID DHCP4_SUBNET_SELECTION_FAILED;
 extern const isc::log::MessageID DHCP4_TESTING_MODE_SEND_TO_SOURCE_ENABLED;
 extern const isc::log::MessageID DHCP4_UNKNOWN_ADDRESS_REQUESTED;
+extern const isc::log::MessageID DHCP4_V6_ONLY_PREFERRED_MISSING_IN_ACK;
+extern const isc::log::MessageID DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER;
 
 } // namespace dhcp
 } // namespace isc
index 5cfc1281c1045c86c6a7081dcf9f4a0f79043caa..5daf669fbceea127864f26186a7cc666b12f6185 100644 (file)
@@ -1154,3 +1154,13 @@ contains the client and the transaction identification information.
 The second argument contains the IPv4 address in the ciaddr field. The
 third argument contains the IPv4 address in the requested-ip-address
 option (if present).
+
+% DHCP4_V6_ONLY_PREFERRED_MISSING_IN_ACK v6-only-preferred option missing in 0.0.0.0 offer to query: %1
+An DHCPACK for the 0.0.0.0 address was generated for a client requesting
+the v6-only-preferred (108) option but the option is not in the response as
+expected: the erroneous response is dropped, the request query is displayed.
+
+% DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER v6-only-preferred option missing in 0.0.0.0 offer to query: %1
+An DHCPOFFER for the 0.0.0.0 address was generated for a client requesting
+the v6-only-preferred (108) option but the option is not in the response as
+expected: the erroneous response is dropped, the discover query is displayed.
index f3cf1c83050bdfd6c6e6e401a0d6c5f07f3b08cc..b47cc74c01867d3cf0ce6d985e6a74db413ba0fc 100644 (file)
@@ -2843,24 +2843,9 @@ Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
 bool
 Dhcpv4Srv::assignZero(Subnet4Ptr& subnet, const ClientClasses& client_classes) {
     Subnet4Ptr current_subnet = subnet;
+    // Try subnets.
     while (current_subnet) {
-        // Try pools.
-        for (auto const& pool : current_subnet->getPools(Lease::TYPE_V4)) {
-            if (!pool->clientSupported(client_classes)) {
-                continue;
-            }
-            auto const& co = pool->getCfgOption();
-            if (!co->empty()) {
-                OptionDescriptor desc = co->get(DHCP4_OPTION_SPACE,
-                                                DHO_V6_ONLY_PREFERRED);
-                if (desc.option_) {
-                    subnet = current_subnet;
-                    return (true);
-                }
-            }
-        }
-        // Try the subnet.
-        auto const& co = current_subnet->getCfgOption();
+        const ConstCfgOptionPtr& co = current_subnet->getCfgOption();
         if (!co->empty()) {
             OptionDescriptor desc = co->get(DHCP4_OPTION_SPACE,
                                             DHO_V6_ONLY_PREFERRED);
@@ -2875,7 +2860,7 @@ Dhcpv4Srv::assignZero(Subnet4Ptr& subnet, const ClientClasses& client_classes) {
     SharedNetwork4Ptr network;
     subnet->getSharedNetwork(network);
     if (network) {
-        auto const& co = network->getCfgOption();
+        const ConstCfgOptionPtr& co = network->getCfgOption();
         if (!co->empty()) {
             OptionDescriptor desc = co->get(DHCP4_OPTION_SPACE,
                                             DHO_V6_ONLY_PREFERRED);
@@ -3802,6 +3787,8 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover, AllocEngine::ClientContext4Ptr& co
         if (ex.getIPv6OnlyPreferred()) {
             if (!ex.getResponse()->getOption(DHO_V6_ONLY_PREFERRED)) {
                 // Better to drop the packet than to send an insane response.
+                LOG_ERROR(packet4_logger, DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER)
+                    .arg(discover->getLabel());
                 return (Pkt4Ptr());
             }
         }
@@ -3886,6 +3873,8 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& cont
         if (ex.getIPv6OnlyPreferred()) {
             if (!response->getOption(DHO_V6_ONLY_PREFERRED)) {
                 // Better to drop the packet than to send an insane response.
+                LOG_ERROR(packet4_logger, DHCP4_V6_ONLY_PREFERRED_MISSING_IN_OFFER)
+                    .arg(request->getLabel());
                 return (Pkt4Ptr());
             }
         }
index ed9597f190bee07983531d50d0393b88ee6f06cb..8cbac01eba9127c01a9932521a19034c453f1f29 100644 (file)
@@ -737,7 +737,7 @@ protected:
     /// or assigning the zero address as the assigned address if the
     /// Yiaddr field of the DHCPv4 message header...
     ///
-    /// Check if there is a pool, subnet or shared network defining an
+    /// Check if there is asubnet or shared network defining an
     /// IPv6-Only Preferred option which will be included by the response.
     ///
     /// @param subnet Reference to the selected subnet, can be modified if
index a044fadabb1269dfb1bccb65cf296a0d09781dbc..02c8e9d51feba813df8e71c22edbca4e00d9aa69 100644 (file)
@@ -5215,6 +5215,1040 @@ TEST_F(Dhcpv4SrvTest, fixedFieldsInClassOrder) {
     }
 }
 
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is offered
+// only when the option is configured.
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredDiscover) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // An address was offered.
+    EXPECT_FALSE(offer->getYiaddr().isV4Zero());
+
+    // No v6-only-preferred option.
+    EXPECT_FALSE(offer->getOption(DHO_V6_ONLY_PREFERRED));
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is offered
+// only when the option is configured.
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredRequest) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // An address was offered.
+    EXPECT_FALSE(ack->getYiaddr().isV4Zero());
+
+    // No v6-only-preferred option.
+    EXPECT_FALSE(ack->getOption(DHO_V6_ONLY_PREFERRED));
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in the subnet.
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredDiscover) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet_->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(offer->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = offer->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in the subnet.
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredRequest) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet_->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(ack->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = ack->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in another subnet of the shared network.
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredDiscoverAnother) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Add the domain name "foo".
+    OptionStringPtr dn(new OptionString(Option::V4, DHO_DOMAIN_NAME, "foo"));
+    subnet_->getCfgOption()->add(dn, false, false, DHCP4_OPTION_SPACE);
+    // Add another subnet,
+    Subnet4Ptr subnet2 = Subnet4::create(IOAddress("192.0.3.0"), 24,
+                                         unspecified,
+                                         unspecified,
+                                         valid_lft,
+                                         subnet_->getID() + 1);
+    // Add the domain name "bar".
+    OptionStringPtr dn2(new OptionString(Option::V4, DHO_DOMAIN_NAME, "bar"));
+    subnet2->getCfgOption()->add(dn2, false, false, DHCP4_OPTION_SPACE);
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet2->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    network->add(subnet2);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(offer->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = offer->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+
+    // domain-name option "bar" was added.
+    got_opt = offer->getOption(DHO_DOMAIN_NAME);
+    ASSERT_TRUE(got_opt);
+    OptionStringPtr got_dn_opt =
+        boost::dynamic_pointer_cast<OptionString>(got_opt);
+    ASSERT_TRUE(got_dn_opt);
+    EXPECT_EQ("bar", got_dn_opt->getValue());
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in another subnet of the shared network.
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredRequestAnother) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Add the domain name "foo".
+    OptionStringPtr dn(new OptionString(Option::V4, DHO_DOMAIN_NAME, "foo"));
+    subnet_->getCfgOption()->add(dn, false, false, DHCP4_OPTION_SPACE);
+    // Add another subnet,
+    Subnet4Ptr subnet2 = Subnet4::create(IOAddress("192.0.3.0"), 24,
+                                         unspecified,
+                                         unspecified,
+                                         valid_lft,
+                                         subnet_->getID() + 1);
+    // Add the domain name "bar".
+    OptionStringPtr dn2(new OptionString(Option::V4, DHO_DOMAIN_NAME, "bar"));
+    subnet2->getCfgOption()->add(dn2, false, false, DHCP4_OPTION_SPACE);
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet2->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    network->add(subnet2);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(ack->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = ack->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+
+    // domain-name option "bar" was added.
+    got_opt = ack->getOption(DHO_DOMAIN_NAME);
+    ASSERT_TRUE(got_opt);
+    OptionStringPtr got_dn_opt =
+        boost::dynamic_pointer_cast<OptionString>(got_opt);
+    ASSERT_TRUE(got_dn_opt);
+    EXPECT_EQ("bar", got_dn_opt->getValue());
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in another subnet of the shared network
+// with a matching guard (another subnet is used when subnet selection
+// skips not matching guards).
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredDiscoverGuarded) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Add the domain name "foo".
+    OptionStringPtr dn(new OptionString(Option::V4, DHO_DOMAIN_NAME, "foo"));
+    subnet_->getCfgOption()->add(dn, false, false, DHCP4_OPTION_SPACE);
+    // Add another subnet,
+    Subnet4Ptr subnet2 = Subnet4::create(IOAddress("192.0.3.0"), 24,
+                                         unspecified,
+                                         unspecified,
+                                         valid_lft,
+                                         subnet_->getID() + 1);
+    // Add the "guard" guard to subnet2.
+    subnet2->allowClientClass("guard");
+    // Add the domain name "bar".
+    OptionStringPtr dn2(new OptionString(Option::V4, DHO_DOMAIN_NAME, "bar"));
+    subnet2->getCfgOption()->add(dn2, false, false, DHCP4_OPTION_SPACE);
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet2->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    network->add(subnet2);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Put into the "guard" class.
+    srv.classifyPacket(dis);
+    dis->addClass("guard");
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(offer->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = offer->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+
+    // domain-name option "bar" was added.
+    got_opt = offer->getOption(DHO_DOMAIN_NAME);
+    ASSERT_TRUE(got_opt);
+    OptionStringPtr got_dn_opt =
+        boost::dynamic_pointer_cast<OptionString>(got_opt);
+    ASSERT_TRUE(got_dn_opt);
+    EXPECT_EQ("bar", got_dn_opt->getValue());
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in another subnet of the shared network
+// with a matching guard (another subnet is used when subnet selection
+// skips not matching guards).
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredRequestGuarded) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Add the domain name "foo".
+    OptionStringPtr dn(new OptionString(Option::V4, DHO_DOMAIN_NAME, "foo"));
+    subnet_->getCfgOption()->add(dn, false, false, DHCP4_OPTION_SPACE);
+    // Add another subnet,
+    Subnet4Ptr subnet2 = Subnet4::create(IOAddress("192.0.3.0"), 24,
+                                         unspecified,
+                                         unspecified,
+                                         valid_lft,
+                                         subnet_->getID() + 1);
+    // Add the "guard" guard to subnet2.
+    subnet2->allowClientClass("guard");
+    // Add the domain name "bar".
+    OptionStringPtr dn2(new OptionString(Option::V4, DHO_DOMAIN_NAME, "bar"));
+    subnet2->getCfgOption()->add(dn2, false, false, DHCP4_OPTION_SPACE);
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet2->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    network->add(subnet2);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Put into the "guard" class.
+    srv.classifyPacket(req);
+    req->addClass("guard");
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(ack->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = ack->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+
+    // domain-name option "bar" was added.
+    got_opt = ack->getOption(DHO_DOMAIN_NAME);
+    ASSERT_TRUE(got_opt);
+    OptionStringPtr got_dn_opt =
+        boost::dynamic_pointer_cast<OptionString>(got_opt);
+    ASSERT_TRUE(got_dn_opt);
+    EXPECT_EQ("bar", got_dn_opt->getValue());
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is not offered
+// when the option is configured in another subnet of the shared network
+// with a not matching guard.
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredDiscoverGuarded) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet with a pool.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+
+    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
+                               IOAddress("192.0.2.110")));
+    subnet_->addPool(pool_);
+
+    // Add another subnet,
+    Subnet4Ptr subnet2 = Subnet4::create(IOAddress("192.0.3.0"), 24,
+                                         unspecified,
+                                         unspecified,
+                                         valid_lft,
+                                         subnet_->getID() + 1);
+    // Add the "guard" guard to subnet2.
+    subnet2->allowClientClass("guard");
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet2->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    network->add(subnet2);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Not in the "guard" class.
+    srv.classifyPacket(dis);
+    EXPECT_FALSE(dis->inClass("guard"));
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // An address was offered.
+    EXPECT_FALSE(offer->getYiaddr().isV4Zero());
+
+    // No v6-only-preferred option.
+    EXPECT_FALSE(offer->getOption(DHO_V6_ONLY_PREFERRED));
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is not offered
+// when the option is configured in another subnet of the shared network
+// with a not matching guard.
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredRequestGuarded) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet with a pool.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+
+    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
+                               IOAddress("192.0.2.110")));
+    subnet_->addPool(pool_);
+
+    // Add another subnet,
+    Subnet4Ptr subnet2 = Subnet4::create(IOAddress("192.0.3.0"), 24,
+                                         unspecified,
+                                         unspecified,
+                                         valid_lft,
+                                         subnet_->getID() + 1);
+    // Add the "guard" guard to subnet2.
+    subnet2->allowClientClass("guard");
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    subnet2->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    network->add(subnet2);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Not in the "guard" class.
+    srv.classifyPacket(req);
+    EXPECT_FALSE(req->inClass("guard"));
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // An address was offered.
+    EXPECT_FALSE(ack->getYiaddr().isV4Zero());
+
+    // No v6-only-preferred option.
+    EXPECT_FALSE(ack->getOption(DHO_V6_ONLY_PREFERRED));
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in the shared network.
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredDiscoverSharedNetwork) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    network->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(offer->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = offer->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is offered
+// when the option is configured in the shared network.
+TEST_F(Dhcpv4SrvTest, v6OnlyPreferredRequestSharedNetwork) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+    // Create a shared network.
+    SharedNetwork4Ptr network(new SharedNetwork4("one"));
+    network->add(subnet_);
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    network->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->add(network);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // 0.0.0.0 was offered.
+    EXPECT_TRUE(ack->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = ack->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is not offered
+// when the option is configured in a pool (i.e. either the pool is used
+// to assign the address or the pool is not used to add options).
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredDiscoverPool) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet with a pool.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+
+    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
+                               IOAddress("192.0.2.110")));
+    subnet_->addPool(pool_);
+
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    pool_->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // Address 192.0.2.100 was offered.
+    EXPECT_EQ("192.0.2.100", offer->getYiaddr().toText());
+
+    // v6-only-preferred option was added because the address is from the pool.
+    OptionPtr got_opt = offer->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is not offered
+// when the option is configured in a pool (i.e. either the pool is used
+// to assign the address or the pool is not used to add options).
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredRequestPool) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Recreate subnet with a pool.
+    Triplet<uint32_t> unspecified;
+    Triplet<uint32_t> valid_lft(500, 1000, 1500);
+    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24,
+                              unspecified,
+                              unspecified,
+                              valid_lft,
+                              subnet_->getID());
+
+    pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
+                               IOAddress("192.0.2.110")));
+    subnet_->addPool(pool_);
+
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    pool_->getCfgOption()->add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // Address 192.0.2.100 was offered.
+    EXPECT_EQ("192.0.2.100", ack->getYiaddr().toText());
+
+    // v6-only-preferred option was added because the address is from the pool.
+    OptionPtr got_opt = ack->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that discover requesting v6-only-preferred 0.0.0.0 is not offered
+// when the option is configured at the global level.
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredDiscoverGlobal) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgOption()->
+        add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+    dis->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    dis->addOption(clientid);
+    dis->setIface("eth1");
+    dis->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    dis->addOption(prl);
+
+    // Get the DHCPOFFER.
+    Pkt4Ptr offer = srv.processDiscover(dis);
+    ASSERT_TRUE(offer);
+    checkResponse(offer, DHCPOFFER, 1234);
+
+    // An address was offered.
+    EXPECT_FALSE(offer->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = offer->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
+// Verify that request requesting v6-only-preferred 0.0.0.0 is not offered
+// when the option is configured at the global level.
+TEST_F(Dhcpv4SrvTest, noV6OnlyPreferredRequestGlobal) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    NakedDhcpv4Srv srv(0);
+
+    // Add the v6-only-preferred option data.
+    const uint32_t v6only_wait(3600);
+    OptionUint32Ptr v6op_opt(new OptionUint32(Option::V4,
+                                              DHO_V6_ONLY_PREFERRED,
+                                              v6only_wait));
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgOption()->
+        add(v6op_opt, false, false, DHCP4_OPTION_SPACE);
+    CfgMgr::instance().commit();
+
+    Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+    req->setRemoteAddr(IOAddress("192.0.2.1"));
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+    req->setIface("eth1");
+    req->setIndex(ETH1_INDEX);
+
+    // Add a PRL with v6-only-preferred.
+    OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                                 DHO_DHCP_PARAMETER_REQUEST_LIST));
+    ASSERT_TRUE(prl);
+    prl->addValue(DHO_V6_ONLY_PREFERRED);
+    req->addOption(prl);
+
+    // Get the DHCPACK.
+    Pkt4Ptr ack = srv.processRequest(req);
+    ASSERT_TRUE(ack);
+    checkResponse(ack, DHCPACK, 1234);
+
+    // An address was offered.
+    EXPECT_FALSE(ack->getYiaddr().isV4Zero());
+
+    // v6-only-preferred option was added.
+    OptionPtr got_opt = ack->getOption(DHO_V6_ONLY_PREFERRED);
+    ASSERT_TRUE(got_opt);
+    OptionUint32Ptr got_v6op_opt =
+        boost::dynamic_pointer_cast<OptionUint32>(got_opt);
+    ASSERT_TRUE(got_v6op_opt);
+    EXPECT_EQ(v6only_wait, got_v6op_opt->getValue());
+}
+
 /// @brief Test fixture for recoverStashedAgentOption.
 class StashAgentOptionTest : public Dhcpv4SrvTest {
 public: