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 @@
#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
......@@ -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<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
......
......@@ -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
......
......@@ -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
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