Commit b0daac3d authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

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

parent 24213920
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#endif #endif
#include <dhcpsrv/memfile_lease_mgr.h> #include <dhcpsrv/memfile_lease_mgr.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
...@@ -153,8 +154,19 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, ...@@ -153,8 +154,19 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
// Check for static reservations. // Check for static reservations.
alloc_engine->findReservation(*context_); 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 void
...@@ -333,6 +345,16 @@ Dhcpv4Exchange::setHostIdentifiers() { ...@@ -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 void
Dhcpv4Exchange::setReservedMessageFields() { Dhcpv4Exchange::setReservedMessageFields() {
ConstHostPtr host = context_->host_; ConstHostPtr host = context_->host_;
...@@ -2604,7 +2626,7 @@ Dhcpv4Srv::sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid) { ...@@ -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 // Built-in vendor class processing
boost::shared_ptr<OptionString> vendor_class = boost::shared_ptr<OptionString> vendor_class =
boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER)); boost::dynamic_pointer_cast<OptionString>(pkt->getOption(DHO_VENDOR_CLASS_IDENTIFIER));
...@@ -2614,14 +2636,11 @@ void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) { ...@@ -2614,14 +2636,11 @@ void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) {
} }
pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue()); pkt->addClass(VENDOR_CLASS_PREFIX + vendor_class->getValue());
classes += VENDOR_CLASS_PREFIX + vendor_class->getValue();
} }
void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) { void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
string classes = "";
// First phase: built-in vendor class processing // First phase: built-in vendor class processing
classifyByVendor(pkt, classes); classifyByVendor(pkt);
// Run match expressions // Run match expressions
// Note getClientClassDictionary() cannot be null // Note getClientClassDictionary() cannot be null
...@@ -2645,7 +2664,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) { ...@@ -2645,7 +2664,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
.arg(status); .arg(status);
// Matching: add the class // Matching: add the class
pkt->addClass(it->first); pkt->addClass(it->first);
classes += it->first + " ";
} else { } else {
LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, EVAL_RESULT) LOG_DEBUG(options4_logger, DBG_DHCP4_DETAIL, EVAL_RESULT)
.arg(it->first) .arg(it->first)
...@@ -2661,12 +2679,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) { ...@@ -2661,12 +2679,6 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
.arg("get exception?"); .arg("get exception?");
} }
} }
if (!classes.empty()) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
.arg(pkt->getLabel())
.arg(classes);
}
} }
void void
......
...@@ -154,6 +154,9 @@ private: ...@@ -154,6 +154,9 @@ private:
/// host-reservation-identifiers /// host-reservation-identifiers
void setHostIdentifiers(); void setHostIdentifiers();
/// @brief Assigns classes retrieved from host reservation database.
void setReservedClientClasses();
/// @brief Pointer to the allocation engine used by the server. /// @brief Pointer to the allocation engine used by the server.
AllocEnginePtr alloc_engine_; AllocEnginePtr alloc_engine_;
/// @brief Pointer to the DHCPv4 message sent by the client. /// @brief Pointer to the DHCPv4 message sent by the client.
...@@ -783,8 +786,7 @@ private: ...@@ -783,8 +786,7 @@ private:
/// @note This is the first part of @ref classifyPacket /// @note This is the first part of @ref classifyPacket
/// ///
/// @param pkt packet to be classified /// @param pkt packet to be classified
/// @param classes a reference to added class names for logging void classifyByVendor(const Pkt4Ptr& pkt);
void classifyByVendor(const Pkt4Ptr& pkt, std::string& classes);
/// @private /// @private
/// @brief Constructs netmask option based on subnet4 /// @brief Constructs netmask option based on subnet4
......
...@@ -65,8 +65,50 @@ const char* CONFIGS[] = { ...@@ -65,8 +65,50 @@ const char* CONFIGS[] = {
" \"id\": 1," " \"id\": 1,"
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]" " \"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. /// @brief Test fixture class for testing classification.
...@@ -267,5 +309,62 @@ TEST_F(ClassifyTest, fixedFieldsInformFile2) { ...@@ -267,5 +309,62 @@ TEST_F(ClassifyTest, fixedFieldsInformFile2) {
testFixedFields(CONFIGS[0], DHCPINFORM, pxe, "0.0.0.0", "", "ipxe.efi"); 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 } // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment