Commit 87ce14cd authored by Stephen Morris's avatar Stephen Morris
Browse files

[master] Merge branch 'trac2681'

parents 227f2b1d cf0ce4ba
# Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
# Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
......@@ -94,6 +94,11 @@ server is about to open sockets on the specified port.
The IPv4 DHCP server has received a packet that it is unable to
interpret. The reason why the packet is invalid is included in the message.
% DHCP4_PACKET_PROCESS_FAIL failed to process packet received from %1: %2
This is a general catch-all message indicating that the processing of a
received packet failed. The reason is given in the message. The server
will not send a response but will instead ignore the packet.
% DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3
A debug message noting that the server has received the specified type of
packet on the specified interface. Note that a packet marked as UNKNOWN
......
......@@ -149,32 +149,53 @@ Dhcpv4Srv::run() {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
.arg(query->toText());
switch (query->getType()) {
case DHCPDISCOVER:
rsp = processDiscover(query);
break;
case DHCPREQUEST:
rsp = processRequest(query);
break;
case DHCPRELEASE:
processRelease(query);
break;
case DHCPDECLINE:
processDecline(query);
break;
case DHCPINFORM:
processInform(query);
break;
default:
// Only action is to output a message if debug is enabled,
// and that will be covered by the debug statement before
// the "switch" statement.
;
try {
switch (query->getType()) {
case DHCPDISCOVER:
rsp = processDiscover(query);
break;
case DHCPREQUEST:
rsp = processRequest(query);
break;
case DHCPRELEASE:
processRelease(query);
break;
case DHCPDECLINE:
processDecline(query);
break;
case DHCPINFORM:
processInform(query);
break;
default:
// Only action is to output a message if debug is enabled,
// and that is covered by the debug statement before the
// "switch" statement.
;
}
} catch (const isc::Exception& e) {
// Catch-all exception (at least for ones based on the isc
// Exception class, which covers more or less all that
// are explicitly raised in the BIND 10 code). Just log
// the problem and ignore the packet. (The problem is logged
// as a debug message because debug is disabled by default -
// it prevents a DDOS attack based on the sending of problem
// packets.)
if (dhcp4_logger.isDebugEnabled(DBG_DHCP4_BASIC)) {
std::string source = "unknown";
HWAddrPtr hwptr = query->getHWAddr();
if (hwptr) {
source = hwptr->toText();
}
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
DHCP4_PACKET_PROCESS_FAIL)
.arg(source).arg(e.what());
}
}
if (rsp) {
......
# Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
# Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
......@@ -93,6 +93,11 @@ This message indicates that the server failed to grant (in response to
received REQUEST) a lease for a given client. There may be many reasons for
such failure. Each specific failure is logged in a separate log entry.
% DHCP6_PACKET_PROCESS_FAIL processing of %1 message received from %2 failed: %3
This is a general catch-all message indicating that the processing of the
specified packet type from the indicated address failed. The reason is given in the
message. The server will not send a response but will instead ignore the packet.
% DHCP6_RELEASE address %1 belonging to client duid=%2, iaid=%3 was released properly.
This debug message indicates that an address was released properly. It
is a normal operation during client shutdown.
......
......@@ -182,8 +182,8 @@ bool Dhcpv6Srv::run() {
break;
case DHCPV6_RELEASE:
rsp = processRelease(query);
break;
rsp = processRelease(query);
break;
case DHCPV6_DECLINE:
rsp = processDecline(query);
......@@ -199,11 +199,26 @@ bool Dhcpv6Srv::run() {
// the "switch" statement.
;
}
} catch (const RFCViolation& e) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
.arg(query->getName())
.arg(query->getRemoteAddr())
.arg(e.what());
} catch (const isc::Exception& e) {
// Catch-all exception (at least for ones based on the isc
// Exception class, which covers more or less all that
// are explicitly raised in the BIND 10 code). Just log
// the problem and ignore the packet. (The problem is logged
// as a debug message because debug is disabled by default -
// it prevents a DDOS attack based on the sending of problem
// packets.)
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
.arg(query->getName())
.arg(query->getRemoteAddr())
.arg(e.what());
}
if (rsp) {
......@@ -685,11 +700,10 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// cause of that failure. The only thing left is to insert
// status code to pass the sad news to the client.
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
DHCP6_LEASE_ADVERT_FAIL:DHCP6_LEASE_ALLOC_FAIL)
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation ?
DHCP6_LEASE_ADVERT_FAIL : DHCP6_LEASE_ALLOC_FAIL)
.arg(duid?duid->toText():"(no-duid)")
.arg(ia->getIAID())
.arg(subnet->toText());
.arg(ia->getIAID());
ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
"Sorry, no address could be allocated."));
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <cstring>
......@@ -169,95 +170,104 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
const IOAddress& hint,
bool fake_allocation /* = false */ ) {
// That check is not necessary. We create allocator in AllocEngine
// constructor
if (!allocator_) {
isc_throw(InvalidOperation, "No allocator selected");
}
// check if there's existing lease for that subnet/duid/iaid combination.
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
if (existing) {
// we have a lease already. This is a returning client, probably after
// his reboot.
return (existing);
}
try {
// That check is not necessary. We create allocator in AllocEngine
// constructor
if (!allocator_) {
isc_throw(InvalidOperation, "No allocator selected");
}
// check if the hint is in pool and is available
if (subnet->inPool(hint)) {
existing = LeaseMgrFactory::instance().getLease6(hint);
if (!existing) {
/// @todo: check if the hint is reserved once we have host support
/// implemented
// check if there's existing lease for that subnet/duid/iaid combination.
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
if (existing) {
// we have a lease already. This is a returning client, probably after
// his reboot.
return (existing);
}
// the hint is valid and not currently used, let's create a lease for it
Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, fake_allocation);
// check if the hint is in pool and is available
if (subnet->inPool(hint)) {
existing = LeaseMgrFactory::instance().getLease6(hint);
if (!existing) {
/// @todo: check if the hint is reserved once we have host support
/// implemented
// the hint is valid and not currently used, let's create a lease for it
Lease6Ptr lease = createLease6(subnet, duid, iaid, hint, fake_allocation);
// It can happen that the lease allocation failed (we could have lost
// the race condition. That means that the hint is lo longer usable and
// we need to continue the regular allocation path.
if (lease) {
return (lease);
}
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, duid, iaid,
fake_allocation));
}
// It can happen that the lease allocation failed (we could have lost
// the race condition. That means that the hint is lo longer usable and
// we need to continue the regular allocation path.
if (lease) {
return (lease);
}
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, duid, iaid,
fake_allocation));
}
}
}
// Hint is in the pool but is not available. Search the pool until first of
// the following occurs:
// - we find a free address
// - we find an address for which the lease has expired
// - we exhaust number of tries
//
// @todo: Current code does not handle pool exhaustion well. It will be
// improved. Current problems:
// 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
// 10 addresses), we will iterate over it 100 times before giving up
// 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
// 3. the whole concept of infinite attempts is just asking for infinite loop
// We may consider some form or reference counting (this pool has X addresses
// left), but this has one major problem. We exactly control allocation
// moment, but we currently do not control expiration time at all
unsigned int i = attempts_;
do {
IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
/// @todo: check if the address is reserved once we have host support
/// implemented
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate);
if (!existing) {
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
fake_allocation);
if (lease) {
return (lease);
}
// Hint is in the pool but is not available. Search the pool until first of
// the following occurs:
// - we find a free address
// - we find an address for which the lease has expired
// - we exhaust number of tries
//
// @todo: Current code does not handle pool exhaustion well. It will be
// improved. Current problems:
// 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
// 10 addresses), we will iterate over it 100 times before giving up
// 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
// 3. the whole concept of infinite attempts is just asking for infinite loop
// We may consider some form or reference counting (this pool has X addresses
// left), but this has one major problem. We exactly control allocation
// moment, but we currently do not control expiration time at all
unsigned int i = attempts_;
do {
IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
/// @todo: check if the address is reserved once we have host support
/// implemented
// Although the address was free just microseconds ago, it may have
// been taken just now. If the lease insertion fails, we continue
// allocation attempts.
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, duid, iaid,
fake_allocation));
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate);
if (!existing) {
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
fake_allocation);
if (lease) {
return (lease);
}
// Although the address was free just microseconds ago, it may have
// been taken just now. If the lease insertion fails, we continue
// allocation attempts.
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, duid, iaid,
fake_allocation));
}
}
}
// continue trying allocation until we run out of attempts
// (or attempts are set to 0, which means infinite)
--i;
} while ( i || !attempts_);
// Continue trying allocation until we run out of attempts
// (or attempts are set to 0, which means infinite)
--i;
} while ((i > 0) || !attempts_);
isc_throw(AllocFailed, "Failed to allocate address after " << attempts_
<< " tries");
// Unable to allocate an address, return an empty lease.
LOG_WARN(dhcpsrv_logger, DHCPSRV_ADDRESS6_ALLOC_FAIL).arg(attempts_);
} catch (const isc::Exception& e) {
// Some other error, return an empty lease.
LOG_ERROR(dhcpsrv_logger, DHCPSRV_ADDRESS6_ALLOC_ERROR).arg(e.what());
}
return (Lease6Ptr());
}
Lease4Ptr
......@@ -267,115 +277,123 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
const IOAddress& hint,
bool fake_allocation /* = false */ ) {
// Allocator is always created in AllocEngine constructor and there is
// currently no other way to set it, so that check is not really necessary.
if (!allocator_) {
isc_throw(InvalidOperation, "No allocator selected");
}
// Check if there's existing lease for that subnet/clientid/hwaddr combination.
Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet->getID());
if (existing) {
// We have a lease already. This is a returning client, probably after
// its reboot.
existing = renewLease4(subnet, clientid, hwaddr, existing, fake_allocation);
if (existing) {
return (existing);
try {
// Allocator is always created in AllocEngine constructor and there is
// currently no other way to set it, so that check is not really necessary.
if (!allocator_) {
isc_throw(InvalidOperation, "No allocator selected");
}
// If renewal failed (e.g. the lease no longer matches current configuration)
// let's continue the allocation process
}
if (clientid) {
existing = LeaseMgrFactory::instance().getLease4(*clientid, subnet->getID());
// Check if there's existing lease for that subnet/clientid/hwaddr combination.
Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(hwaddr->hwaddr_, subnet->getID());
if (existing) {
// we have a lease already. This is a returning client, probably after
// We have a lease already. This is a returning client, probably after
// its reboot.
existing = renewLease4(subnet, clientid, hwaddr, existing, fake_allocation);
// @todo: produce a warning. We haven't found him using MAC address, but
// we found him using client-id
if (existing) {
return (existing);
}
// If renewal failed (e.g. the lease no longer matches current configuration)
// let's continue the allocation process
}
}
// check if the hint is in pool and is available
if (subnet->inPool(hint)) {
existing = LeaseMgrFactory::instance().getLease4(hint);
if (!existing) {
/// @todo: Check if the hint is reserved once we have host support
/// implemented
if (clientid) {
existing = LeaseMgrFactory::instance().getLease4(*clientid, subnet->getID());
if (existing) {
// we have a lease already. This is a returning client, probably after
// its reboot.
existing = renewLease4(subnet, clientid, hwaddr, existing, fake_allocation);
// @todo: produce a warning. We haven't found him using MAC address, but
// we found him using client-id
if (existing) {
return (existing);
}
}
}
// The hint is valid and not currently used, let's create a lease for it
Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, hint, fake_allocation);
// check if the hint is in pool and is available
if (subnet->inPool(hint)) {
existing = LeaseMgrFactory::instance().getLease4(hint);
if (!existing) {
/// @todo: Check if the hint is reserved once we have host support
/// implemented
// The hint is valid and not currently used, let's create a lease for it
Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, hint, fake_allocation);
// It can happen that the lease allocation failed (we could have lost
// the race condition. That means that the hint is lo longer usable and
// we need to continue the regular allocation path.
if (lease) {
return (lease);
}
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
fake_allocation));
}
// It can happen that the lease allocation failed (we could have lost
// the race condition. That means that the hint is lo longer usable and
// we need to continue the regular allocation path.
if (lease) {
return (lease);
}
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
fake_allocation));
}
}
}
// Hint is in the pool but is not available. Search the pool until first of
// the following occurs:
// - we find a free address
// - we find an address for which the lease has expired
// - we exhaust the number of tries
//
// @todo: Current code does not handle pool exhaustion well. It will be
// improved. Current problems:
// 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
// 10 addresses), we will iterate over it 100 times before giving up
// 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
// 3. the whole concept of infinite attempts is just asking for infinite loop
// We may consider some form or reference counting (this pool has X addresses
// left), but this has one major problem. We exactly control allocation
// moment, but we currently do not control expiration time at all
unsigned int i = attempts_;
do {
IOAddress candidate = allocator_->pickAddress(subnet, clientid, hint);
/// @todo: check if the address is reserved once we have host support
/// implemented
Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(candidate);
if (!existing) {
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, candidate,
fake_allocation);
if (lease) {
return (lease);
}
// Hint is in the pool but is not available. Search the pool until first of
// the following occurs:
// - we find a free address
// - we find an address for which the lease has expired
// - we exhaust the number of tries
//
// @todo: Current code does not handle pool exhaustion well. It will be
// improved. Current problems:
// 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
// 10 addresses), we will iterate over it 100 times before giving up
// 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
// 3. the whole concept of infinite attempts is just asking for infinite loop
// We may consider some form or reference counting (this pool has X addresses
// left), but this has one major problem. We exactly control allocation
// moment, but we currently do not control expiration time at all
unsigned int i = attempts_;
do {
IOAddress candidate = allocator_->pickAddress(subnet, clientid, hint);
/// @todo: check if the address is reserved once we have host support
/// implemented
// Although the address was free just microseconds ago, it may have
// been taken just now. If the lease insertion fails, we continue
// allocation attempts.
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
fake_allocation));
Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(candidate);
if (!existing) {
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, candidate,
fake_allocation);
if (lease) {
return (lease);
}
// Although the address was free just microseconds ago, it may have
// been taken just now. If the lease insertion fails, we continue
// allocation attempts.
} else {
if (existing->expired()) {
return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
fake_allocation));
}
}
}
// Continue trying allocation until we run out of attempts
// (or attempts are set to 0, which means infinite)
--i;
} while ( i || !attempts_);
// Continue trying allocation until we run out of attempts
// (or attempts are set to 0, which means infinite)
--i;
} while ((i > 0) || !attempts_);
isc_throw(AllocFailed, "Failed to allocate address after " << attempts_
<< " tries");
// Unable to allocate an address, return an empty lease.
LOG_WARN(dhcpsrv_logger, DHCPSRV_ADDRESS4_ALLOC_FAIL).arg(attempts_);
} catch (const isc::Exception& e) {
// Some other error, return an empty lease.
LOG_ERROR(dhcpsrv_logger, DHCPSRV_ADDRESS4_ALLOC_ERROR).arg(e.what());
}
return (Lease4Ptr());
}
Lease4Ptr AllocEngine::renewLease4(const SubnetPtr& subnet,
......
......@@ -14,6 +14,46 @@
$NAMESPACE isc::dhcp
% DHCPSRV_ADDRESS4_ALLOC_ERROR error during attempt to allocate an IPv4 address: %1
An error occurred during an attempt to allocate an IPv4 address, the
reason for the failure being contained in the message. The server will
return a message to the client refusing a lease.
% DHCPSRV_ADDRESS4_ALLOC_FAIL failed to allocate an IPv4 address after %1 attempt(s)
THE DHCP allocation engine gave up trying to allocate an IPv4 address
after the specified number of attempts. This probably means that the
address pool from which the allocation is being attempted is either
empty, or very nearly empty. As a result, the client will have been
refused a lease.
This message may indicate that your address pool is too small for the
number of clients you are trying to service and should be expanded.
Alternatively, if the you know that the number of concurrently active
clients is less than the addresses you have available, you may want to
consider reducing the lease lifetime. In this way, addresses allocated
to clients that are no longer active on the network will become available
available sooner.
% DHCPSRV_ADDRESS6_ALLOC_ERROR error during attempt to allocate an IPv6 address: %1
An error occurred during an attempt to allocate an IPv6 address, the
reason for the failure being contained in the message. The server will
return a message to the client refusing a lease.
% DHCPSRV_ADDRESS6_ALLOC_FAIL failed to allocate an IPv6 address after %1 attempt(s)
The DHCP allocation engine gave up trying to allocate an IPv6 address
after the specified number of attempts. This probably means that the
address pool from which the allocation is being attempted is either
empty, or very nearly empty. As a result, the client will have been
refused a lease.
This message may indicate that your address pool is too small for the
number of clients you are trying to service and should be expanded.
Alternatively, if the you know that the number of concurrently active