Browse Source

[4303] Servers now search for host reservations using any identifier.

Marcin Siodelski 9 years ago
parent
commit
1f0f7c7d21

+ 38 - 0
src/bin/dhcp4/dhcp4_srv.cc

@@ -138,6 +138,44 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
                 .arg(subnet->getID());
                 .arg(subnet->getID());
         }
         }
     }
     }
+
+    // Before we can check for static reservations, we need to prepare a set
+    // of identifiers to be used for this.
+
+    // HW address.
+    if (context_->hwaddr_ && !context_->hwaddr_->hwaddr_.empty()) {
+        context_->host_identifiers_[Host::IDENT_HWADDR] = context_->hwaddr_->hwaddr_;
+    }
+
+    // Client identifier
+    if (context_->clientid_) {
+        const std::vector<uint8_t>& vec = context_->clientid_->getDuid();
+        if (!vec.empty()) {
+            // Client identifier type = DUID? Client identifier holding a DUID
+            // comprises Type (1 byte), IAID (4 bytes), followed by the actual
+            // DUID. Thus, the minimal length is 6.
+            if ((vec[0] == CLIENT_ID_OPTION_TYPE_DUID) && (vec.size() > 5)) {
+                // Extract DUID, skip IAID.
+                context_->host_identifiers_.insert(
+                        AllocEngine::IdentifierPair(Host::IDENT_DUID,
+                                                    std::vector<uint8_t>(vec.begin() + 5,
+                                                                         vec.end())));
+            }
+            /// @todo Add support for other client identifiers (see #4317).
+        }
+    }
+    // Circuit id
+    OptionPtr rai = query_->getOption(DHO_DHCP_AGENT_OPTIONS);
+    if (rai) {
+        OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
+        if (circuit_id_opt) {
+            const OptionBuffer& circuit_id_vec = circuit_id_opt->getData();
+            if (!circuit_id_vec.empty()) {
+                context_->host_identifiers_[Host::IDENT_CIRCUIT_ID] = circuit_id_vec;
+            }
+        }
+    }
+
     // Check for static reservations.
     // Check for static reservations.
     alloc_engine->findReservation(*context_);
     alloc_engine->findReservation(*context_);
 };
 };

+ 14 - 3
src/bin/dhcp4/tests/dhcp4_client.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -49,7 +49,8 @@ Dhcp4Client::Dhcp4Client(const Dhcp4Client::State& state) :
     server_facing_relay_addr_("10.0.0.2"),
     server_facing_relay_addr_("10.0.0.2"),
     srv_(boost::shared_ptr<NakedDhcpv4Srv>(new NakedDhcpv4Srv(0))),
     srv_(boost::shared_ptr<NakedDhcpv4Srv>(new NakedDhcpv4Srv(0))),
     state_(state),
     state_(state),
-    use_relay_(false) {
+    use_relay_(false),
+    circuit_id_() {
 }
 }
 
 
 Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
 Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
@@ -67,7 +68,8 @@ Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
     server_facing_relay_addr_("10.0.0.2"),
     server_facing_relay_addr_("10.0.0.2"),
     srv_(srv),
     srv_(srv),
     state_(state),
     state_(state),
-    use_relay_(false) {
+    use_relay_(false),
+    circuit_id_() {
 }
 }
 
 
 void
 void
@@ -468,6 +470,15 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
         msg->setHops(1);
         msg->setHops(1);
         msg->setGiaddr(relay_addr_);
         msg->setGiaddr(relay_addr_);
         msg->setLocalAddr(server_facing_relay_addr_);
         msg->setLocalAddr(server_facing_relay_addr_);
+        // Insert RAI
+        OptionPtr rai(new Option(Option::V4, DHO_DHCP_AGENT_OPTIONS));
+        // Insert circuit id, if specified.
+        if (!circuit_id_.empty()) {
+            rai->addOption(OptionPtr(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
+                                                OptionBuffer(circuit_id_.begin(),
+                                                             circuit_id_.end()))));
+        }
+        msg->addOption(rai);
     }
     }
     // Repack the message to simulate wire-data parsing.
     // Repack the message to simulate wire-data parsing.
     msg->pack();
     msg->pack();

+ 13 - 1
src/bin/dhcp4/tests/dhcp4_client.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -284,6 +284,14 @@ public:
                         const uint8_t option2 = 0,
                         const uint8_t option2 = 0,
                         const uint8_t option3 = 0);
                         const uint8_t option3 = 0);
 
 
+    /// @brief Sets circuit-id value to be included in the circuit-id
+    /// sub option of the RAI option.
+    ///
+    /// @param circuit_id New circuit-id value.
+    void setCircuitId(const std::string& circuit_id) {
+        circuit_id_ = circuit_id;
+    }
+
     /// @brief Sets destination address for the messages being sent by the
     /// @brief Sets destination address for the messages being sent by the
     /// client.
     /// client.
     ///
     ///
@@ -468,6 +476,10 @@ private:
     /// @brief Enable relaying messages to the server.
     /// @brief Enable relaying messages to the server.
     bool use_relay_;
     bool use_relay_;
 
 
+    /// @brief Specifies value to be inserted into circuit-id sub option
+    /// of the RAI option.
+    std::string circuit_id_;
+
     /// @brief Extra options the client will send.
     /// @brief Extra options the client will send.
     OptionCollection extra_options_;
     OptionCollection extra_options_;
 };
 };

+ 34 - 1
src/bin/dhcp4/tests/dora_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -125,6 +125,14 @@ const char* DORA_CONFIGS[] = {
         "       {"
         "       {"
         "         \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
         "         \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
         "         \"ip-address\": \"10.0.0.7\""
         "         \"ip-address\": \"10.0.0.7\""
+        "       },"
+        "       {"
+        "         \"duid\": \"01:02:03:04:05\","
+        "         \"ip-address\": \"10.0.0.8\""
+        "       },"
+        "       {"
+        "         \"circuit-id\": \"'charter950'\","
+        "         \"ip-address\": \"10.0.0.9\""
         "       }"
         "       }"
         "    ]"
         "    ]"
         "} ]"
         "} ]"
@@ -688,6 +696,31 @@ TEST_F(DORATest, reservation) {
     ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_));
     ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_));
 }
 }
 
 
+// This test checks that it is possible to make a reservation by
+// circuit-id inserted by the relay agent..
+TEST_F(DORATest, reservationByCircuitId) {
+    // Client A is a one which will have a reservation.
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    // Use relay agent so as the circuit-id can be inserted.
+    client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
+    // Specify circuit-id.
+    client.setCircuitId("charter950");
+
+    // Configure DHCP server.
+    configure(DORA_CONFIGS[2], *client.getServer());
+    // Client A performs 4-way exchange and should obtain a reserved
+    // address.
+    ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+                                  IOAddress>(new IOAddress("0.0.0.0"))));
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Make sure that the client has got the lease for the reserved address.
+    ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
+}
+
 // This test checks that setting the match-client-id value to false causes
 // This test checks that setting the match-client-id value to false causes
 // the server to ignore changing client identifier when the client is
 // the server to ignore changing client identifier when the client is
 // using consistent HW address.
 // using consistent HW address.

+ 14 - 1
src/bin/dhcp6/dhcp6_srv.cc

@@ -276,9 +276,20 @@ AllocEngine::ClientContext6
 Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
 Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
     AllocEngine::ClientContext6 ctx;
     AllocEngine::ClientContext6 ctx;
     ctx.subnet_ = selectSubnet(pkt);
     ctx.subnet_ = selectSubnet(pkt);
+    ctx.query_ = pkt;
+
+    // Collect host identifiers.
+    // DUID
     ctx.duid_ = pkt->getClientId();
     ctx.duid_ = pkt->getClientId();
+    if (ctx.duid_) {
+        ctx.host_identifiers_[Host::IDENT_DUID] = ctx.duid_->getDuid();
+    }
+    // HW Address.
     ctx.hwaddr_ = getMAC(pkt);
     ctx.hwaddr_ = getMAC(pkt);
-    ctx.query_ = pkt;
+    if (ctx.hwaddr_) {
+        ctx.host_identifiers_[Host::IDENT_HWADDR] = ctx.hwaddr_->hwaddr_;
+    }
+    // And find a host reservation using those identifiers.
     alloc_engine_->findReservation(ctx);
     alloc_engine_->findReservation(ctx);
 
 
     return (ctx);
     return (ctx);
@@ -1321,6 +1332,7 @@ Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
     ctx.hwaddr_ = orig_ctx.hwaddr_;
     ctx.hwaddr_ = orig_ctx.hwaddr_;
     ctx.host_ = orig_ctx.host_;
     ctx.host_ = orig_ctx.host_;
     ctx.query_ = orig_ctx.query_;
     ctx.query_ = orig_ctx.query_;
+    ctx.host_identifiers_ = orig_ctx.host_identifiers_;
 
 
     Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
     Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
 
 
@@ -1442,6 +1454,7 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
     ctx.hwaddr_ = orig_ctx.hwaddr_;
     ctx.hwaddr_ = orig_ctx.hwaddr_;
     ctx.host_ = orig_ctx.host_;
     ctx.host_ = orig_ctx.host_;
     ctx.query_ = orig_ctx.query_;
     ctx.query_ = orig_ctx.query_;
+    ctx.host_identifiers_ = orig_ctx.host_identifiers_;
 
 
     Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
     Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
 
 

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

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -412,6 +412,13 @@ public:
         iface_name_ = iface_name;
         iface_name_ = iface_name;
     }
     }
 
 
+    /// @brief Sets link local address used by the client.
+    ///
+    /// @param link_local New link local address.
+    void setLinkLocal(const asiolink::IOAddress& link_local) {
+        link_local_ = link_local;
+    }
+
     /// @brief Set an address hint to be sent to a server.
     /// @brief Set an address hint to be sent to a server.
     ///
     ///
     /// @param pref_lft Preferred lifetime.
     /// @param pref_lft Preferred lifetime.

+ 87 - 1
src/bin/dhcp6/tests/host_unittest.cc

@@ -1,15 +1,17 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 #include <config.h>
 #include <config.h>
+#include <asiolink/io_address.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/tests/dhcp6_client.h>
 #include <dhcp6/tests/dhcp6_client.h>
 
 
 using namespace isc;
 using namespace isc;
+using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 using namespace isc::dhcp::test;
 
 
@@ -19,6 +21,8 @@ namespace {
 ///
 ///
 /// - Configuration 0:
 /// - Configuration 0:
 ///   Single subnet with two reservations, one with a hostname, one without
 ///   Single subnet with two reservations, one with a hostname, one without
+/// - Configuration 1:
+///   Multiple reservations using different host identifiers.
 const char* CONFIGS[] = {
 const char* CONFIGS[] = {
     // Configuration 0:
     // Configuration 0:
     "{ "
     "{ "
@@ -45,7 +49,35 @@ const char* CONFIGS[] = {
         "        \"ip-addresses\": [ \"2001:db8:1:1::babf\" ]"
         "        \"ip-addresses\": [ \"2001:db8:1:1::babf\" ]"
         "    } ]"
         "    } ]"
         " } ]"
         " } ]"
+    "}",
+
+    // Configuration 1:
+    "{ "
+        "\"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 4000, "
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"mac-sources\": [ \"ipv6-link-local\" ], "
+        "\"subnet6\": [ "
+        " { "
+        "    \"subnet\": \"2001:db8:1::/48\", "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+        "    \"interface\" : \"eth0\" , "
+        "    \"reservations\": ["
+        "    {"
+        "        \"hw-address\": \"38:60:77:d5:ff:ee\","
+        "        \"ip-addresses\": [ \"2001:db8:1::1\" ]"
+        "    },"
+        "    {"
+        "        \"duid\": \"01:02:03:05\","
+        "        \"ip-addresses\": [ \"2001:db8:1::2\" ]"
+        "    } ]"
+        " } ]"
     "}"
     "}"
+
 };
 };
 
 
 /// @brief Test fixture class for testing host reservations
 /// @brief Test fixture class for testing host reservations
@@ -59,6 +91,36 @@ public:
           iface_mgr_test_config_(true) {
           iface_mgr_test_config_(true) {
     }
     }
 
 
+    /// @brief Verifies that the reservation is retrieved by the server
+    /// using one of the host identifiers.
+    ///
+    /// @param client Reference to a client to be used in the test.
+    /// The client should be preconfigured to insert a specific identifier
+    /// into the message, e.g. DUID, HW address etc.
+    /// @param exp_ip_address Expected IPv6 address in the returned
+    /// reservation.
+    void testReservationByIdentifier(Dhcp6Client& client,
+                                     const std::string exp_ip_address) {
+        configure(CONFIGS[1], *client.getServer());
+
+        const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
+            getCfgSubnets6()->getAll();
+        ASSERT_EQ(1, subnets->size());
+
+        // Configure client to request IA_NA and append IA_NA option
+        //  to the client's message.
+        client.useNA();
+        ASSERT_NO_THROW(client.useHint(100, 200, 64, "2001:db8:1:1::dead:beef"));
+
+        // Perform 4-way exchange.
+        ASSERT_NO_THROW(client.doSARR());
+
+        // Verify that the client we got the reserved address
+        ASSERT_EQ(1, client.getLeaseNum());
+        Lease6 lease_client = client.getLease(0);
+        EXPECT_EQ(exp_ip_address, lease_client.addr_.toText());
+    }
+
     /// @brief Interface Manager's fake configuration control.
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
     IfaceMgrTestConfig iface_mgr_test_config_;
 };
 };
@@ -246,4 +308,28 @@ TEST_F(HostTest, sarrAndRebind) {
     EXPECT_EQ("alice", lease_server2->hostname_);
     EXPECT_EQ("alice", lease_server2->hostname_);
 }
 }
 
 
+// This test verfies that the host reservation by DUID is found by the
+// server.
+TEST_F(HostTest, reservationByDUID) {
+    Dhcp6Client client;
+    // Set DUID matching the one used to create host reservations.
+    client.setDUID("01:02:03:05");
+    // Run the actual test.
+    testReservationByIdentifier(client, "2001:db8:1::2");
+}
+
+// This test verfies that the host reservation by HW address is found
+// by the server.
+TEST_F(HostTest, reservationByHWAddress) {
+    Dhcp6Client client;
+    // Set link local address for the client which the server will
+    // use to decode the HW address as 38:60:77:d5:ff:ee. This
+    // decoded address will be used to search for host reservations.
+    client.setLinkLocal(IOAddress("fe80::3a60:77ff:fed5:ffee"));
+    // Run the actual test.
+    testReservationByIdentifier(client, "2001:db8:1::1");
+}
+
+
+
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 3 - 0
src/lib/dhcp/dhcp4.h

@@ -283,6 +283,9 @@ static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT_CTRL = 152; //RFC6607
 
 
 #endif
 #endif
 
 
+/* Client identifier types */
+static const uint8_t CLIENT_ID_OPTION_TYPE_DUID = 255;
+
 } // end of isc::dhcp namespace
 } // end of isc::dhcp namespace
 } // end of isc namespace
 } // end of isc namespace
 
 

+ 36 - 13
src/lib/dhcpsrv/alloc_engine.cc

@@ -309,7 +309,7 @@ AllocEngine::ClientContext6::ClientContext6()
     : subnet_(), duid_(), iaid_(0), type_(Lease::TYPE_NA), hwaddr_(),
     : subnet_(), duid_(), iaid_(0), type_(Lease::TYPE_NA), hwaddr_(),
       hints_(), fwd_dns_update_(false), rev_dns_update_(false), hostname_(""),
       hints_(), fwd_dns_update_(false), rev_dns_update_(false), hostname_(""),
       callout_handle_(), fake_allocation_(false), old_leases_(), host_(),
       callout_handle_(), fake_allocation_(false), old_leases_(), host_(),
-      query_(), ia_rsp_() {
+      query_(), ia_rsp_(), host_identifiers_() {
 }
 }
 
 
 AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
 AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
@@ -322,7 +322,7 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const Duid
     subnet_(subnet), duid_(duid), iaid_(iaid), type_(type), hwaddr_(),
     subnet_(subnet), duid_(duid), iaid_(iaid), type_(type), hwaddr_(),
     hints_(), fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
     hints_(), fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
     hostname_(hostname), fake_allocation_(fake_allocation),
     hostname_(hostname), fake_allocation_(fake_allocation),
-    old_leases_(), host_(), query_(), ia_rsp_() {
+    old_leases_(), host_(), query_(), ia_rsp_(), host_identifiers_() {
 
 
     static asiolink::IOAddress any("::");
     static asiolink::IOAddress any("::");
 
 
@@ -331,6 +331,11 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const Duid
     }
     }
     // callout_handle, host pointers initiated to NULL by their
     // callout_handle, host pointers initiated to NULL by their
     // respective constructors.
     // respective constructors.
+
+    // Initialize host identifiers.
+    if (duid) {
+        host_identifiers_[Host::IDENT_DUID] = duid->getDuid();
+    }
 }
 }
 
 
 
 
@@ -339,6 +344,8 @@ void AllocEngine::findReservation(ClientContext6& ctx) const {
         return;
         return;
     }
     }
 
 
+    ctx.host_.reset();
+
     // Check which host reservation mode is supported in this subnet.
     // Check which host reservation mode is supported in this subnet.
     Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
     Subnet::HRMode hr_mode = ctx.subnet_->getHostReservationMode();
 
 
@@ -346,11 +353,15 @@ void AllocEngine::findReservation(ClientContext6& ctx) const {
     // host info only if reservations are not disabled.
     // host info only if reservations are not disabled.
     if (hr_mode != Subnet::HR_DISABLED) {
     if (hr_mode != Subnet::HR_DISABLED) {
 
 
-        ctx.host_ = HostMgr::instance().get6(ctx.subnet_->getID(), ctx.duid_,
-                                             ctx.hwaddr_);
-        } else {
-        // Let's explicitly set it to NULL if reservations are disabled.
-        ctx.host_.reset();
+        BOOST_FOREACH(const IdentifierPair& id, ctx.host_identifiers_) {
+            ctx.host_ = HostMgr::instance().get6(ctx.subnet_->getID(),
+                                                 id.first, &id.second[0],
+                                                 id.second.size());
+            // If we found matching host, return.
+            if (ctx.host_) {
+                return;
+            }
+        }
     }
     }
 }
 }
 
 
@@ -2049,7 +2060,8 @@ AllocEngine::ClientContext4::ClientContext4()
       requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
       requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
       fwd_dns_update_(false), rev_dns_update_(false),
       fwd_dns_update_(false), rev_dns_update_(false),
       hostname_(""), callout_handle_(), fake_allocation_(false),
       hostname_(""), callout_handle_(), fake_allocation_(false),
-      old_lease_(), host_(), conflicting_lease_(), query_() {
+      old_lease_(), host_(), conflicting_lease_(), query_(),
+      host_identifiers_() {
 }
 }
 
 
 AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
 AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
@@ -2064,7 +2076,13 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
       requested_address_(requested_addr),
       requested_address_(requested_addr),
       fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
       fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
       hostname_(hostname), callout_handle_(),
       hostname_(hostname), callout_handle_(),
-      fake_allocation_(fake_allocation), old_lease_(), host_() {
+      fake_allocation_(fake_allocation), old_lease_(), host_(),
+      host_identifiers_() {
+
+    // Initialize host identifiers.
+    if (hwaddr) {
+        host_identifiers_[Host::IDENT_HWADDR] = hwaddr->hwaddr_;
+    }
 }
 }
 
 
 Lease4Ptr
 Lease4Ptr
@@ -2109,10 +2127,15 @@ AllocEngine::findReservation(ClientContext4& ctx) {
         // Check if there is a host reseravtion for this client. Attempt to
         // Check if there is a host reseravtion for this client. Attempt to
         // get host information
         // get host information
         if (hr_mode != Subnet::HR_DISABLED) {
         if (hr_mode != Subnet::HR_DISABLED) {
-            // This method should handle the case when there is neither hwaddr
-            // nor clientid_ available and simply return NULL.
-            ctx.host_ = HostMgr::instance().get4(ctx.subnet_->getID(), ctx.hwaddr_,
-                                                 ctx.clientid_);
+            BOOST_FOREACH(const IdentifierPair& id, ctx.host_identifiers_) {
+                ctx.host_ = HostMgr::instance().get4(ctx.subnet_->getID(),
+                                                     id.first, &id.second[0],
+                                                     id.second.size());
+                // If we found matching host, return.
+                if (ctx.host_) {
+                    return;
+                }
+            }
         }
         }
     }
     }
 }
 }

+ 16 - 1
src/lib/dhcpsrv/alloc_engine.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -23,6 +23,7 @@
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
 
 
 #include <map>
 #include <map>
+#include <utility>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -251,6 +252,12 @@ public:
     /// @brief Container for client's hints.
     /// @brief Container for client's hints.
     typedef std::vector<HintType> HintContainer;
     typedef std::vector<HintType> HintContainer;
 
 
+    /// @brief A tuple holding host identifier type and value.
+    typedef std::pair<Host::IdentifierType, std::vector<uint8_t> > IdentifierPair;
+
+    /// @brief Map holding values to be used as host identifiers.
+    typedef std::map<Host::IdentifierType, std::vector<uint8_t> > IdentifierMap;
+
     /// @brief Context information for the DHCPv6 leases allocation.
     /// @brief Context information for the DHCPv6 leases allocation.
     ///
     ///
     /// This structure holds a set of information provided by the DHCPv6
     /// This structure holds a set of information provided by the DHCPv6
@@ -350,6 +357,10 @@ public:
         /// @brief A pointer to the IA_NA/IA_PD option to be sent in response
         /// @brief A pointer to the IA_NA/IA_PD option to be sent in response
         Option6IAPtr ia_rsp_;
         Option6IAPtr ia_rsp_;
 
 
+        /// @brief A map holding host identifiers extracted from a message
+        /// received by the server.
+        IdentifierMap host_identifiers_;
+
         /// @brief Default constructor.
         /// @brief Default constructor.
         ClientContext6();
         ClientContext6();
 
 
@@ -971,6 +982,10 @@ public:
         /// transaction identification information.
         /// transaction identification information.
         Pkt4Ptr query_;
         Pkt4Ptr query_;
 
 
+        /// @brief A map holding host identifiers extracted from a message
+        /// received by the server.
+        IdentifierMap host_identifiers_;
+
         /// @brief Default constructor.
         /// @brief Default constructor.
         ClientContext4();
         ClientContext4();
 
 

+ 2 - 0
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc

@@ -1649,6 +1649,8 @@ TEST_F(AllocEngine4Test, findReservation) {
     AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
     AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
                                     IOAddress("0.0.0.0"), false, false,
                                     IOAddress("0.0.0.0"), false, false,
                                     "", false);
                                     "", false);
+    ctx.host_identifiers_[Host::IDENT_HWADDR] = hwaddr_->hwaddr_;
+    ctx.host_identifiers_[Host::IDENT_DUID] = clientid_->getDuid();
 
 
     // There is no reservation in the database so no host should be
     // There is no reservation in the database so no host should be
     // retruned.
     // retruned.

+ 2 - 1
src/lib/dhcpsrv/tests/alloc_engine_utils.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -270,6 +270,7 @@ AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
     AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, hint, type,
     AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, hint, type,
                                     false, false, "", fake);
                                     false, false, "", fake);
     ctx.hwaddr_ = hwaddr_;
     ctx.hwaddr_ = hwaddr_;
+    ctx.host_identifiers_[Host::IDENT_HWADDR] = hwaddr_->hwaddr_;
     ctx.query_.reset(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
     ctx.query_.reset(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
 
 
     findReservation(*engine, ctx);
     findReservation(*engine, ctx);