client identifier to lookup existing leases for the client and will not
record client identifier in the lease database. This mode of operation
is useful when clients don't use stable client identifiers, e.g. multi
-stage booting. Note that the client identifier may be used for other
-operations than lease allocation, e.g. identifying host reservations
-for the client using client identifier. The first argument includes the
-client and transaction identification information. The second argument
-specifies the identifier of the subnet where the client is connected
-and for which this mode of operation is configured on the server.
+stage booting. The first argument includes the client and transaction
+identification information. The second argument specifies the identifier
+of the subnet where the client is connected and for which this mode of
+operation is configured on the server.
% DHCP4_CLIENT_FQDN_DATA %1: Client sent FQDN option: %2
This debug message includes the detailed information extracted from the
// Pointer to client's query.
context_->query_ = query;
- // Set client identifier if the match-client-id flag is enabled (default).
- // If the subnet wasn't found it doesn't matter because we will not be
- // able to allocate a lease anyway so this context will not be used.
+ // If subnet found, retrieve client identifier which will be needed
+ // for allocations and search for reservations associated with a
+ // subnet/shared network.
if (subnet) {
- if (subnet->getMatchClientId()) {
- OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
- if (opt_clientid) {
- context_->clientid_.reset(new ClientId(opt_clientid->getData()));
- }
- } else {
- /// @todo When merging with #3806 use different logger.
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENTID_IGNORED_FOR_LEASES)
- .arg(query->getLabel())
- .arg(subnet->getID());
+ OptionPtr opt_clientid = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
+ if (opt_clientid) {
+ context_->clientid_.reset(new ClientId(opt_clientid->getData()));
}
// Find static reservations if not disabled for our subnet.
.arg(query->getLabel())
.arg(lease->addr_.toText());
+ // We're logging this here, because this is the place where we know
+ // which subnet has been actually used for allocation. If the
+ // client identifier matching is disabled, we want to make sure that
+ // the user is notified.
+ if (!ctx->subnet_->getMatchClientId()) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENTID_IGNORED_FOR_LEASES)
+ .arg(ctx->query_->getLabel())
+ .arg(ctx->subnet_->getID());
+ }
+
resp->setYiaddr(lease->addr_);
/// @todo The server should check what ciaddr the client has supplied
" ]"
" }"
" ]"
+ "}",
+
+ // Configuration #11.
+ "{"
+ " \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ " },"
+ " \"valid-lifetime\": 600,"
+ " \"match-client-id\": false,"
+ " \"shared-networks\": ["
+ " {"
+ " \"name\": \"frog\","
+ " \"interface\": \"eth1\","
+ " \"match-client-id\": true,"
+ " \"subnet4\": ["
+ " {"
+ " \"subnet\": \"192.0.2.0/26\","
+ " \"id\": 10,"
+ " \"match-client-id\": false"
+ " },"
+ " {"
+ " \"subnet\": \"192.0.2.64/26\","
+ " \"id\": 100,"
+ " \"pools\": ["
+ " {"
+ " \"pool\": \"192.0.2.65 - 192.0.2.127\""
+ " }"
+ " ]"
+ " }"
+ " ]"
+ " }"
+ " ]"
+ "}",
+
+ // Configuration #12.
+ "{"
+ " \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ " },"
+ " \"valid-lifetime\": 600,"
+ " \"match-client-id\": false,"
+ " \"shared-networks\": ["
+ " {"
+ " \"name\": \"frog\","
+ " \"interface\": \"eth1\","
+ " \"match-client-id\": false,"
+ " \"subnet4\": ["
+ " {"
+ " \"subnet\": \"192.0.2.0/26\","
+ " \"id\": 10,"
+ " \"match-client-id\": false"
+ " },"
+ " {"
+ " \"subnet\": \"192.0.2.64/26\","
+ " \"id\": 100,"
+ " \"pools\": ["
+ " {"
+ " \"pool\": \"192.0.2.65 - 192.0.2.127\""
+ " }"
+ " ]"
+ " }"
+ " ]"
+ " }"
+ " ]"
"}"
};
EXPECT_EQ("10.0.0", resp2->getYiaddr().toText().substr(0, 6));
}
+// Client id matching gets disabled on the shared network level.
+TEST_F(Dhcpv4SharedNetworkTest, matchClientId) {
+ // Create client using client identifier besides MAC address.
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ client.includeClientId("01:02:03:04");
+ client.setIfaceName("eth1");
+
+ // Create server configuration with match-client-id value initially
+ // set to true. The client should be allocated a lease and the
+ // client identifier should be included in this lease.
+ configure(NETWORKS_CONFIG[11], *client.getServer());
+
+ // Perform 4-way exchange.
+ ASSERT_NO_THROW(client.doDORA());
+ Pkt4Ptr resp1 = client.getContext().response_;
+ ASSERT_TRUE(resp1);
+ ASSERT_EQ(DHCPACK, resp1->getType());
+
+ // Reconfigure the server and turn off client identifier matching
+ // on the shared network level. The subnet from which the client
+ // is allocated an address should derive the match-client-id value
+ // and ignore the fact that the client identifier is not matching.
+ configure(NETWORKS_CONFIG[12], *client.getServer());
+
+ client.includeClientId("01:01:01:01");
+ client.setState(Dhcp4Client::RENEWING);
+
+ // Try to renew the lease with modified MAC address.
+ ASSERT_NO_THROW(client.doRequest());
+ Pkt4Ptr resp2 = client.getContext().response_;
+ ASSERT_TRUE(resp2);
+ ASSERT_EQ(DHCPACK, resp2->getType());
+
+ // The lease should get rewnewed.
+ EXPECT_EQ(resp2->getYiaddr().toText(), resp1->getYiaddr().toText());
+}
+
} // end of anonymous namespace
while (subnet) {
+ ClientIdPtr client_id;
+ if (subnet->getMatchClientId()) {
+ client_id = ctx.clientid_;
+ }
+
// If client identifier has been supplied, use it to lookup the lease. This
// search will return no lease if the client doesn't have any lease in the
// database or if the client didn't use client identifier to allocate the
// existing lease (this include cases when the server was explicitly
// configured to ignore client identifier).
- if (ctx.clientid_) {
- client_lease = lease_mgr.getLease4(*ctx.clientid_, subnet->getID());
+ if (client_id) {
+ client_lease = lease_mgr.getLease4(*client_id, subnet->getID());
}
// If no lease found using the client identifier, try the lookup using
client_lease_it != client_leases.end(); ++client_lease_it) {
Lease4Ptr existing_lease = *client_lease_it;
if ((existing_lease->subnet_id_ == subnet->getID()) &&
- existing_lease->belongsToClient(ctx.hwaddr_, ctx.clientid_)) {
+ existing_lease->belongsToClient(ctx.hwaddr_, client_id)) {
// Found the lease of this client, so return it.
client_lease = existing_lease;
// We got a lease but the subnet it belongs to may differ from
// If it is in use by another client, the address can't be
// allocated.
if (existing && !existing->expired() &&
- !existing->belongsToClient(ctx.hwaddr_, ctx.clientid_)) {
+ !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
+ ctx.clientid_ : ClientIdPtr())) {
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
ALLOC_ENGINE_V4_REQUEST_IN_USE)
// @todo: remove this kludge after ticket #2590 is implemented
std::vector<uint8_t> local_copy;
- if (ctx.clientid_) {
+ if (ctx.clientid_ && ctx.subnet_->getMatchClientId()) {
local_copy = ctx.clientid_->getDuid();
}
const uint8_t* local_copy0 = local_copy.empty() ? 0 : &local_copy[0];
// Pass the parameters
ctx.callout_handle_->setArgument("query4", ctx.query_);
ctx.callout_handle_->setArgument("subnet4", subnet4);
- ctx.callout_handle_->setArgument("clientid", ctx.clientid_);
+ ctx.callout_handle_->setArgument("clientid", subnet4->getMatchClientId() ?
+ ctx.clientid_ : ClientIdPtr());
ctx.callout_handle_->setArgument("hwaddr", ctx.hwaddr_);
// Pass the lease to be updated
uint64_t total_attempts = 0;
while (subnet) {
+ ClientIdPtr client_id;
+ if (subnet->getMatchClientId()) {
+ client_id = ctx.clientid_;
+ }
+
const uint64_t max_attempts = (attempts_ > 0 ? attempts_ :
subnet->getPoolCapacity(Lease::TYPE_V4));
for (uint64_t i = 0; i < max_attempts; ++i) {
- IOAddress candidate = allocator->pickAddress(subnet, ctx.clientid_,
+ IOAddress candidate = allocator->pickAddress(subnet, client_id,
ctx.requested_address_);
// If address is not reserved for another client, try to allocate it.
if (!addressReserved(candidate, ctx)) {
AllocEngine::ClientContext4& ctx) const {
lease->subnet_id_ = ctx.subnet_->getID();
lease->hwaddr_ = ctx.hwaddr_;
- lease->client_id_ = ctx.clientid_;
+ lease->client_id_ = ctx.subnet_->getMatchClientId() ? ctx.clientid_ : ClientIdPtr();
lease->cltt_ = time(NULL);
lease->t1_ = ctx.subnet_->getT1();
lease->t2_ = ctx.subnet_->getT2();