Browse Source

[4765] DHCPv4 server uses classes defined in host reservations db.

Marcin Siodelski 8 years ago
parent
commit
b0daac3dfa
3 changed files with 128 additions and 15 deletions
  1. 24 12
      src/bin/dhcp4/dhcp4_srv.cc
  2. 4 2
      src/bin/dhcp4/dhcp4_srv.h
  3. 100 1
      src/bin/dhcp4/tests/classify_unittest.cc

+ 24 - 12
src/bin/dhcp4/dhcp4_srv.cc

@@ -57,6 +57,7 @@
 #endif
 #include <dhcpsrv/memfile_lease_mgr.h>
 
+#include <boost/algorithm/string/join.hpp>
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
 #include <boost/shared_ptr.hpp>
@@ -153,8 +154,19 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
 
             // Check for static reservations.
             alloc_engine->findReservation(*context_);
+
+            // Assign classes.
+            setReservedClientClasses();
         }
     }
+
+    const ClientClasses& classes = query_->getClasses();
+    if (!classes.empty()) {
+        std::string joined_classes = boost::algorithm::join(classes, ", ");
+        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
+            .arg(query_->getLabel())
+            .arg(joined_classes);
+    }
 };
 
 void
@@ -334,6 +346,16 @@ Dhcpv4Exchange::setHostIdentifiers() {
 }
 
 void
+Dhcpv4Exchange::setReservedClientClasses() {
+    if (context_->host_ && query_) {
+        BOOST_FOREACH(const std::string& client_class,
+                      context_->host_->getClientClasses4()) {
+            query_->addClass(client_class);
+        }
+    }
+}
+
+void
 Dhcpv4Exchange::setReservedMessageFields() {
     ConstHostPtr host = context_->host_;
     // Nothing to do if host reservations not specified for this client.
@@ -2604,7 +2626,7 @@ Dhcpv4Srv::sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid) {
     }
 }
 
-void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) {
+void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt) {
     // Built-in vendor class processing
     boost::shared_ptr<OptionString> vendor_class =
         boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
@@ -2614,14 +2636,11 @@ void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) {
     }
 
     pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
-    classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
 }
 
 void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
-    string classes = "";
-
     // First phase: built-in vendor class processing
-    classifyByVendor(pkt, classes);
+    classifyByVendor(pkt);
 
     // Run match expressions
     // Note getClientClassDictionary() cannot be null
@@ -2645,7 +2664,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
                     .arg(status);
                 // Matching: add the class
                 pkt->addClass(it->first);
-                classes += it->first + " ";
             } else {
                 LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, EVAL_RESULT)
                     .arg(it->first)
@@ -2661,12 +2679,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
                 .arg("get exception?");
         }
     }
-
-    if (!classes.empty()) {
-        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
-            .arg(pkt->getLabel())
-            .arg(classes);
-    }
 }
 
 void

+ 4 - 2
src/bin/dhcp4/dhcp4_srv.h

@@ -154,6 +154,9 @@ private:
     /// host-reservation-identifiers
     void setHostIdentifiers();
 
+    /// @brief Assigns classes retrieved from host reservation database.
+    void setReservedClientClasses();
+
     /// @brief Pointer to the allocation engine used by the server.
     AllocEnginePtr alloc_engine_;
     /// @brief Pointer to the DHCPv4 message sent by the client.
@@ -783,8 +786,7 @@ private:
     /// @note This is the first part of @ref classifyPacket
     ///
     /// @param pkt packet to be classified
-    /// @param classes a reference to added class names for logging
-    void classifyByVendor(const Pkt4Ptr& pkt, std::string& classes);
+    void classifyByVendor(const Pkt4Ptr& pkt);
 
     /// @private
     /// @brief Constructs netmask option based on subnet4

+ 100 - 1
src/bin/dhcp4/tests/classify_unittest.cc

@@ -65,8 +65,50 @@ const char* CONFIGS[] = {
         "    \"id\": 1,"
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]"
         " } ]"
-    "}"
+    "}",
 
+    // Configuration 1
+    "{ \"interfaces-config\": {"
+        "   \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"client-classes\": ["
+        "{"
+        "   \"name\": \"pxe\","
+        "   \"test\": \"option[93].hex == 0x0009\","
+        "   \"next-server\": \"1.2.3.4\""
+        "},"
+        "{"
+        "   \"name\": \"reserved-class1\","
+        "   \"option-data\": ["
+        "   {"
+        "       \"name\": \"routers\","
+        "       \"data\": \"10.0.0.200\""
+        "   }"
+        "   ]"
+        "},"
+        "{"
+        "   \"name\": \"reserved-class2\","
+        "   \"option-data\": ["
+        "   {"
+        "       \"name\": \"domain-name-servers\","
+        "       \"data\": \"10.0.0.201\""
+        "   }"
+        "   ]"
+        "}"
+        "],"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"id\": 1,"
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
+        "    \"reservations\": [ "
+        "    {"
+        "        \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
+        "        \"client-classes\": [ \"reserved-class1\", \"reserved-class2\" ]"
+        "    }"
+        "    ]"
+        " } ]"
+    "}"
 };
 
 /// @brief Test fixture class for testing classification.
@@ -267,5 +309,62 @@ TEST_F(ClassifyTest, fixedFieldsInformFile2) {
     testFixedFields(CONFIGS[0], DHCPINFORM, pxe, "0.0.0.0", "", "ipxe.efi");
 }
 
+// This test checks that it is possible to specify static reservations for
+// client classes.
+TEST_F(ClassifyTest, clientClassesInHostReservations) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    // Initially, the client uses hardware address for which there are
+    // no reservations.
+    client.setHWAddress("aa:bb:cc:dd:ee:fe");
+    // DNS servers have to be requested to be returned.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS);
+
+    // Add option 93 that matches 'pxe' class in the configuration.
+    OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
+    client.addExtraOption(pxe);
+
+    // Configure DHCP server.
+    configure(CONFIGS[1], *client.getServer());
+
+    // Perform 4-way exchange. The client's HW address doesn't match the
+    // reservations, so we expect that only 'pxe' class will be matched.
+    ASSERT_NO_THROW(client.doDORA());
+
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+
+    // 'pxe' class matches so the siaddr should be set appropriately.
+    EXPECT_EQ("1.2.3.4", resp->getSiaddr().toText());
+    // This client has no reservations for the classes associated with
+    // DNS servers and Routers options.
+    EXPECT_EQ(0, client.config_.routers_.size());
+    EXPECT_EQ(0, client.config_.dns_servers_.size());
+
+    // Modify HW address to match the reservations.
+    client.setHWAddress("aa:bb:cc:dd:ee:ff");
+    ASSERT_NO_THROW(client.doDORA());
+
+    ASSERT_TRUE(client.getContext().response_);
+    resp = client.getContext().response_;
+
+    // This time, the client matches 3 classes (for two it has reservations).
+    EXPECT_EQ("1.2.3.4", resp->getSiaddr().toText());
+    EXPECT_EQ(1, client.config_.routers_.size());
+    EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
+    EXPECT_EQ(1, client.config_.dns_servers_.size());
+    EXPECT_EQ("10.0.0.201", client.config_.dns_servers_[0].toText());
+
+    // This should also work for DHCPINFORM case.
+    ASSERT_NO_THROW(client.doInform());
+    ASSERT_TRUE(client.getContext().response_);
+    resp = client.getContext().response_;
+
+    EXPECT_EQ("1.2.3.4", resp->getSiaddr().toText());
+    EXPECT_EQ(1, client.config_.routers_.size());
+    EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
+    EXPECT_EQ(1, client.config_.dns_servers_.size());
+    EXPECT_EQ("10.0.0.201", client.config_.dns_servers_[0].toText());
+}
+
 
 } // end of anonymous namespace