From: Marcin Siodelski Date: Fri, 2 Sep 2016 13:38:50 +0000 (+0200) Subject: [4765] DHCPv4 server uses classes defined in host reservations db. X-Git-Tag: trac5006_base~14^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b0daac3dfadc389e1f5e7e7735dc70eb7b5a8f1b;p=thirdparty%2Fkea.git [4765] DHCPv4 server uses classes defined in host reservations db. --- diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 33a2e38058..c1f36ea1da 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -57,6 +57,7 @@ #endif #include +#include #include #include #include @@ -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 @@ -333,6 +345,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_; @@ -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 vendor_class = boost::dynamic_pointer_cast(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 diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index 859a5ef2a8..6c0c51afba 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/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 diff --git a/src/bin/dhcp4/tests/classify_unittest.cc b/src/bin/dhcp4/tests/classify_unittest.cc index aaed262f83..220d4df87b 100644 --- a/src/bin/dhcp4/tests/classify_unittest.cc +++ b/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(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