using namespace std;
/// Structure that holds registered hook indexes
-struct Dhcp6Hooks {
+struct Dhcp4Hooks {
+ int hook_index_buffer4_receive_;///< index for "buffer4_receive" hook point
int hook_index_pkt4_receive_; ///< index for "pkt4_receive" hook point
int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
+ int hook_index_lease4_release_; ///< index for "lease4_release" hook point
int hook_index_pkt4_send_; ///< index for "pkt4_send" hook point
+ int hook_index_buffer4_send_; ///< index for "buffer4_send" hook point
- /// Constructor that registers hook points for DHCPv6 engine
- Dhcp6Hooks() {
+ /// Constructor that registers hook points for DHCPv4 engine
+ Dhcp4Hooks() {
+ hook_index_buffer4_receive_= HooksManager::registerHook("buffer4_receive");
hook_index_pkt4_receive_ = HooksManager::registerHook("pkt4_receive");
hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
hook_index_pkt4_send_ = HooksManager::registerHook("pkt4_send");
+ hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
+ hook_index_buffer4_send_ = HooksManager::registerHook("buffer4_send");
}
};
// will be instantiated (and the constructor run) when the module is loaded.
// As a result, the hook indexes will be defined before any method in this
// module is called.
-Dhcp6Hooks Hooks;
+Dhcp4Hooks Hooks;
namespace isc {
namespace dhcp {
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
const bool direct_response_desired)
-: serverid_(), shutdown_(true), alloc_engine_(), port_(port),
- use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
+: serverid_(), shutdown_(true), alloc_engine_(), port_(port),
+ use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
}
- if (query) {
+ // Timeout may be reached or signal received, which breaks select()
+ // with no reception ocurred
+ if (!query) {
+ continue;
+ }
+
+ bool skip_unpack = false;
+
+ // The packet has just been received so contains the uninterpreted wire
+ // data; execute callouts registered for buffer6_receive.
+ if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+ // Delete previously set arguments
+ callout_handle->deleteAllArguments();
+
+ // Pass incoming packet as argument
+ callout_handle->setArgument("query4", query);
+
+ // Call callouts
+ HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_, *callout_handle);
+
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to parse the packet, so skip at this
+ // stage means that callouts did the parsing already, so server
+ // should skip parsing.
+ if (callout_handle->getSkip()) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_BUFFER_RCVD_SKIP);
+ skip_unpack = true;
+ }
+
+ callout_handle->getArgument("query4", query);
+ }
+
+ // Unpack the packet information unless the buffer4_receive callouts
+ // indicated they did it
+ if (!skip_unpack) {
try {
query->unpack();
-
} catch (const std::exception& e) {
// Failed to parse the packet.
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
DHCP4_PACKET_PARSE_FAIL).arg(e.what());
continue;
}
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
- .arg(serverReceivedPacketName(query->getType()))
- .arg(query->getType())
- .arg(query->getIface());
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
- .arg(static_cast<int>(query->getType()))
- .arg(query->toText());
-
- // Let's execute all callouts registered for packet_received
- if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
- CalloutHandlePtr callout_handle = getCalloutHandle(query);
-
- // Delete previously set arguments
- callout_handle->deleteAllArguments();
-
- // Pass incoming packet as argument
- callout_handle->setArgument("query4", query);
+ }
- // Call callouts
- HooksManager::callCallouts(hook_index_pkt4_receive_,
- *callout_handle);
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
+ .arg(serverReceivedPacketName(query->getType()))
+ .arg(query->getType())
+ .arg(query->getIface());
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
+ .arg(static_cast<int>(query->getType()))
+ .arg(query->toText());
+
+ // Let's execute all callouts registered for packet_received
+ if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+ // Delete previously set arguments
+ callout_handle->deleteAllArguments();
+
+ // Pass incoming packet as argument
+ callout_handle->setArgument("query4", query);
+
+ // Call callouts
+ HooksManager::callCallouts(hook_index_pkt4_receive_,
+ *callout_handle);
+
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to process the packet, so skip at this
+ // stage means drop.
+ if (callout_handle->getSkip()) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
+ continue;
+ }
- // Callouts decided to skip the next processing step. The next
- // processing step would to process the packet, so skip at this
- // stage means drop.
- if (callout_handle->getSkip()) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
- continue;
- }
+ callout_handle->getArgument("query4", query);
+ }
- callout_handle->getArgument("query4", query);
+ try {
+ switch (query->getType()) {
+ case DHCPDISCOVER:
+ rsp = processDiscover(query);
+ break;
+
+ case DHCPREQUEST:
+ // Note that REQUEST is used for many things in DHCPv4: for
+ // requesting new leases, renewing existing ones and even
+ // for rebinding.
+ 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.
+ ;
}
-
- 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());
+ } 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) {
+ if (!rsp) {
+ continue;
+ }
- adjustRemoteAddr(query, rsp);
+ adjustRemoteAddr(query, rsp);
- if (!rsp->getHops()) {
- rsp->setRemotePort(DHCP4_CLIENT_PORT);
- } else {
- rsp->setRemotePort(DHCP4_SERVER_PORT);
- }
+ if (!rsp->getHops()) {
+ rsp->setRemotePort(DHCP4_CLIENT_PORT);
+ } else {
+ rsp->setRemotePort(DHCP4_SERVER_PORT);
+ }
- rsp->setLocalAddr(query->getLocalAddr());
- rsp->setLocalPort(DHCP4_SERVER_PORT);
- rsp->setIface(query->getIface());
- rsp->setIndex(query->getIndex());
+ rsp->setLocalAddr(query->getLocalAddr());
+ rsp->setLocalPort(DHCP4_SERVER_PORT);
+ rsp->setIface(query->getIface());
+ rsp->setIndex(query->getIndex());
- // Execute all callouts registered for packet6_send
- if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
- CalloutHandlePtr callout_handle = getCalloutHandle(query);
+ // Specifies if server should do the packing
+ bool skip_pack = false;
- // Delete all previous arguments
- callout_handle->deleteAllArguments();
+ // Execute all callouts registered for packet6_send
+ if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
- // Clear skip flag if it was set in previous callouts
- callout_handle->setSkip(false);
+ // Delete all previous arguments
+ callout_handle->deleteAllArguments();
- // Set our response
- callout_handle->setArgument("response4", rsp);
+ // Clear skip flag if it was set in previous callouts
+ callout_handle->setSkip(false);
- // Call all installed callouts
- HooksManager::callCallouts(hook_index_pkt4_send_,
- *callout_handle);
+ // Set our response
+ callout_handle->setArgument("response4", rsp);
- // Callouts decided to skip the next processing step. The next
- // processing step would to send the packet, so skip at this
- // stage means "drop response".
- if (callout_handle->getSkip()) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
- continue;
- }
- }
+ // Call all installed callouts
+ HooksManager::callCallouts(hook_index_pkt4_send_,
+ *callout_handle);
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
- DHCP4_RESPONSE_DATA)
- .arg(rsp->getType()).arg(rsp->toText());
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to send the packet, so skip at this
+ // stage means "drop response".
+ if (callout_handle->getSkip()) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
+ skip_pack = true;
+ }
+ }
+
+ if (!skip_pack) {
+ try {
+ rsp->pack();
+ } catch (const std::exception& e) {
+ LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
+ .arg(e.what());
+ }
+ }
+
+ try {
+ // Now all fields and options are constructed into output wire buffer.
+ // Option objects modification does not make sense anymore. Hooks
+ // can only manipulate wire buffer at this stage.
+ // Let's execute all callouts registered for buffer6_send
+ if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_buffer4_send_)) {
+ CalloutHandlePtr callout_handle = getCalloutHandle(query);
- try {
- rsp->pack();
- sendPacket(rsp);
- } catch (const std::exception& e) {
- LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
- .arg(e.what());
+ // Delete previously set arguments
+ callout_handle->deleteAllArguments();
+
+ // Pass incoming packet as argument
+ callout_handle->setArgument("response4", rsp);
+
+ // Call callouts
+ HooksManager::callCallouts(Hooks.hook_index_buffer4_send_, *callout_handle);
+
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to parse the packet, so skip at this
+ // stage means drop.
+ if (callout_handle->getSkip()) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_BUFFER_SEND_SKIP);
+ continue;
}
+
+ callout_handle->getArgument("response4", rsp);
}
+
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
+ DHCP4_RESPONSE_DATA)
+ .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
+
+ sendPacket(rsp);
+ } catch (const std::exception& e) {
+ LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
+ .arg(e.what());
}
}
appendDefaultOptions(ack, DHCPACK);
appendRequestedOptions(request, ack);
+ // Note that we treat REQUEST message uniformly, regardless if this is a
+ // first request (requesting for new address), renewing existing address
+ // or even rebinding.
assignLease(request, ack);
// There are a few basic options that we always want to
return;
}
- // Ok, hw and client-id match - let's release the lease.
- if (LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
+ bool skip = false;
- // Release successful - we're done here
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
- .arg(lease->addr_.toText())
- .arg(client_id ? client_id->toText() : "(no client-id)")
- .arg(release->getHWAddr()->toText());
- } else {
+ // Execute all callouts registered for packet6_send
+ if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_release_)) {
+ CalloutHandlePtr callout_handle = getCalloutHandle(release);
+
+ // Delete all previous arguments
+ callout_handle->deleteAllArguments();
- // Release failed -
- LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
- .arg(lease->addr_.toText())
+ // Pass the original packet
+ callout_handle->setArgument("query4", release);
+
+ // Pass the lease to be updated
+ callout_handle->setArgument("lease4", lease);
+
+ // Call all installed callouts
+ HooksManager::callCallouts(Hooks.hook_index_lease4_release_, *callout_handle);
+
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to send the packet, so skip at this
+ // stage means "drop response".
+ if (callout_handle->getSkip()) {
+ skip = true;
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_LEASE4_RELEASE_SKIP);
+ }
+ }
+
+ // Ok, we've passed all checks. Let's release this address.
+ bool success = false; // was the removal operation succeessful?
+
+ // Ok, hw and client-id match - let's release the lease.
+ if (!skip) {
+ success = LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+ if (success) {
+ // Release successful - we're done here
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE)
+ .arg(lease->addr_.toText())
+ .arg(client_id ? client_id->toText() : "(no client-id)")
+ .arg(release->getHWAddr()->toText());
+ } else {
+ // Release failed -
+ LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
+ .arg(lease->addr_.toText())
.arg(client_id ? client_id->toText() : "(no client-id)")
- .arg(release->getHWAddr()->toText());
+ .arg(release->getHWAddr()->toText());
+ }
}
} catch (const isc::Exception& ex) {
// Rethrow the exception with a bit more data.
/// Structure that holds registered hook indexes
struct AllocEngineHooks {
int hook_index_lease4_select_; ///< index for "lease4_receive" hook point
+ int hook_index_lease4_renew_; ///< index for "lease4_renew" hook point
int hook_index_lease6_select_; ///< index for "lease6_receive" hook point
/// Constructor that registers hook points for AllocationEngine
AllocEngineHooks() {
hook_index_lease4_select_ = HooksManager::registerHook("lease4_select");
+ hook_index_lease4_renew_ = HooksManager::registerHook("lease4_renew");
hook_index_lease6_select_ = HooksManager::registerHook("lease6_select");
}
};
if (existing) {
// We have a lease already. This is a returning client, probably after
// its reboot.
- existing = renewLease4(subnet, clientid, hwaddr, existing, fake_allocation);
+ existing = renewLease4(subnet, clientid, hwaddr, existing, callout_handle,
+ fake_allocation);
if (existing) {
return (existing);
}
if (existing) {
// we have a lease already. This is a returning client, probably after
// its reboot.
- existing = renewLease4(subnet, clientid, hwaddr, existing, fake_allocation);
+ existing = renewLease4(subnet, clientid, hwaddr, existing, callout_handle,
+ fake_allocation);
// @todo: produce a warning. We haven't found him using MAC address, but
// we found him using client-id
if (existing) {
const ClientIdPtr& clientid,
const HWAddrPtr& hwaddr,
const Lease4Ptr& lease,
+ const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation /* = false */) {
+ if (!lease) {
+ isc_throw(InvalidOperation, "Lease4 must be specified");
+ }
+
+ // Let's keep the old data. This is essential if we are using memfile
+ // (the lease returned points directly to the lease4 object in the database)
+ // We'll need it if we want to skip update (i.e. roll back renewal)
+ Lease4 old_values = *lease;
+
lease->subnet_id_ = subnet->getID();
lease->hwaddr_ = hwaddr->hwaddr_;
lease->client_id_ = clientid;
lease->t2_ = subnet->getT2();
lease->valid_lft_ = subnet->getValid();
- if (!fake_allocation) {
+ bool skip = false;
+ // Execute all callouts registered for packet6_send
+ if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_renew_)) {
+
+ // Delete all previous arguments
+ callout_handle->deleteAllArguments();
+
+ // Pass the parameters
+ callout_handle->setArgument("subnet4", subnet);
+ callout_handle->setArgument("clientid", clientid);
+ callout_handle->setArgument("hwaddr", hwaddr);
+
+ // Pass the lease to be updated
+ callout_handle->setArgument("lease4", lease);
+
+ // Call all installed callouts
+ HooksManager::callCallouts(Hooks.hook_index_lease4_renew_, *callout_handle);
+
+ // Callouts decided to skip the next processing step. The next
+ // processing step would to actually renew the lease, so skip at this
+ // stage means "keep the old lease as it is".
+ if (callout_handle->getSkip()) {
+ skip = true;
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_HOOKS, DHCPSRV_HOOK_LEASE4_RENEW_SKIP);
+ }
+ }
+
+ if (!fake_allocation && !skip) {
// for REQUEST we do update the lease
LeaseMgrFactory::instance().updateLease4(lease);
}
+ if (skip) {
+ // Rollback changes (really useful only for memfile)
+ *lease = old_values;
+ }
return (lease);
}