#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>
// 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
}
}
+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_;
}
}
-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));
}
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
.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)
.arg("get exception?");
}
}
-
- if (!classes.empty()) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
- .arg(pkt->getLabel())
- .arg(classes);
- }
}
void
" \"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.
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