Commit 1c5e0902 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[3987] lease6_decline implemented.

parent 7114a202
......@@ -59,7 +59,6 @@ using namespace isc::asiolink;
using namespace isc::hooks;
using namespace isc::config;
using namespace isc::dhcp::test;
using namespace isc::test;
namespace {
......
......@@ -617,7 +617,7 @@ Dhcpv4SrvTest::pretendReceivingPkt(NakedDhcpv4Srv& srv, const std::string& confi
configure(config);
// Let's just use one of the actual captured packets that we have.
Pkt4Ptr pkt = isc::test::PktCaptures::captureRelayedDiscover();
Pkt4Ptr pkt = PktCaptures::captureRelayedDiscover();
// We just need to tweak it a it, to pretend that it's type is as desired.
// Note that when receiving a packet, its on-wire form is stored in data_
......
......@@ -174,6 +174,26 @@ packet processing. Hook points that are not specific to packet processing
this fact; otherwise the client will think the lease was renewed and continue
to operate under this assumption.
@subsection dhcpv6HooksLease6Decline lease6_decline
- @b Arguments:
- name: @b query6, type: isc::dhcp::PktPtr, direction: <b>in</b>
- name: @b lease6, type: isc::dhcp::Lease6Ptr, direction: <b>in/out</b>
- @b Description: This callout is executed when the server engine is
about to decline an existing lease. The client's request is provided as
the "query6" argument and the existing lease with the appropriate fields
already modified is given in the "lease6" argument. The lease contains
the lease before it is being declined.
- <b>Next step status</b>: If any callout installed on "lease6_decline"
sets the status to SKIP, the server will not decline the lease, but will
continue processing the packet as if it did. It will send the response
that the lease was declined, but the actual database will not be
updated. If any callout installed sets the status to DROP, the packet
processing will be aborted, the lease will not be declined and the
server will not send a response.
@subsection dhcpv6HooksLease6Release lease6_release
- @b Arguments:
......
......@@ -292,6 +292,20 @@ or released leases), but the response will not be send to the client.
The argument includes the client and transaction identification
information.
% DHCP6_HOOK_DECLINE_SKIP During Decline processing (client=%1, interface=%2, addr=%3) hook callout set status to DROP, ignoring packet.
This message indicates that the server received DECLINE message, it was verified
to be correct and matching server's lease information. The server called hooks
for the lease6_decline hook point and one of the callouts set next step status to SKIP.
The server will skip the operation of moving the lease to the declined state and
will continue processing.
% DHCP6_HOOK_DECLINE_DROP During Decline processing (client=%1, interface=%2, addr=%3) hook callout set status to DROP, dropping packet.
This message indicates that the server received DECLINE message, it was verified
to be correct and matching server's lease information. The server called hooks
for the lease6_decline hook point and one of the callouts set next step status to DROP.
The server will now abort processing of the packet as if it was never
received. The lease will continue to be assigned to this client.
% DHCP6_HOOK_LEASE6_RELEASE_NA_SKIP %1: DHCPv6 address lease was not released because a callout set the skip flag
This debug message is printed when a callout installed on the
lease6_release hook point set the skip flag. For this particular hook
......
......@@ -96,6 +96,7 @@ struct Dhcp6Hooks {
int hook_index_lease6_release_; ///< index for "lease6_release" hook point
int hook_index_pkt6_send_; ///< index for "pkt6_send" hook point
int hook_index_buffer6_send_; ///< index for "buffer6_send" hook point
int hook_index_lease6_decline_; ///< index for "lease6_decline" hook point
/// Constructor that registers hook points for DHCPv6 engine
Dhcp6Hooks() {
......@@ -105,6 +106,7 @@ struct Dhcp6Hooks {
hook_index_lease6_release_ = HooksManager::registerHook("lease6_release");
hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
hook_index_buffer6_send_ = HooksManager::registerHook("buffer6_send");
hook_index_lease6_decline_ = HooksManager::registerHook("lease6_decline");
}
};
......@@ -2593,12 +2595,17 @@ Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
// Include server-id
appendDefaultOptions(decline, reply);
declineLeases(decline, reply, ctx);
if (declineLeases(decline, reply, ctx)) {
return (reply);
} else {
return (reply);
// declineLeases returns false only if the hooks set the next step
// status to DROP. We'll just doing as requested.
return (Pkt6Ptr());
}
}
void
bool
Dhcpv6Srv::declineLeases(const Pkt6Ptr& decline, Pkt6Ptr& reply,
AllocEngine::ClientContext6& ctx) {
......@@ -2619,7 +2626,15 @@ Dhcpv6Srv::declineLeases(const Pkt6Ptr& decline, Pkt6Ptr& reply,
OptionPtr answer_opt = declineIA(decline, ctx.duid_, general_status,
boost::dynamic_pointer_cast<Option6IA>(opt->second));
if (answer_opt) {
// We have an answer, let's use it.
reply->addOption(answer_opt);
} else {
// The only case when declineIA could return NULL is if one of the
// hook callouts set next step status to DROP. We just need to drop
// this packet.
return (false);
}
break;
}
......@@ -2628,6 +2643,8 @@ Dhcpv6Srv::declineLeases(const Pkt6Ptr& decline, Pkt6Ptr& reply,
;
}
}
return (true);
}
OptionPtr
......@@ -2734,7 +2751,11 @@ Dhcpv6Srv::declineIA(const Pkt6Ptr& decline, const DuidPtr& duid,
}
// Ok, all is good. Decline this lease.
declineLease(decline, lease, ia_rsp);
if (!declineLease(decline, lease, ia_rsp)) {
// declineLease returns false only when hook callouts set the next
// step status to drop. We just propagate the bad news here.
return (OptionPtr());
}
}
if (total_addrs == 0) {
......@@ -2755,10 +2776,47 @@ Dhcpv6Srv::setStatusCode(boost::shared_ptr<isc::dhcp::Option6IA>& container,
container->addOption(status);
}
void
bool
Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
boost::shared_ptr<Option6IA> ia_rsp) {
// Let's call lease6_decline hooks if necessary.
if (HooksManager::calloutsPresent(Hooks.hook_index_lease6_decline_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(decline);
// Delete previously set arguments
callout_handle->deleteAllArguments();
// Pass incoming packet as argument
callout_handle->setArgument("query6", decline);
callout_handle->setArgument("lease6", lease);
// Call callouts
HooksManager::callCallouts(Hooks.hook_index_lease6_decline_,
*callout_handle);
// Callouts decided to SKIP the next processing step. The next
// processing step would to actually decline the lease, so we'll
// keep the lease as is.
if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_SKIP) {
LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_DECLINE_SKIP)
.arg(decline->getLabel())
.arg(decline->getIface())
.arg(lease->addr_.toText());
return (true);
}
// Callouts decided to DROP the packet. Let's simply log it and
// return false, so upper layers will act accordingly.
if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
LOG_DEBUG(hooks_logger, DBG_DHCP6_DETAIL, DHCP6_HOOK_DECLINE_DROP)
.arg(decline->getLabel())
.arg(decline->getIface())
.arg(lease->addr_.toText());
return (false);
}
}
// Check if a lease has flags indicating that the FQDN update has
// been performed. If so, create NameChangeRequest which removes
// the entries. This method does all necessary checks.
......@@ -2772,8 +2830,6 @@ Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
// Global declined addresses counter.
StatsMgr::instance().addValue("declined-addresses", static_cast<int64_t>(1));
// @todo: Call hooks.
// We need to disassociate the lease from the client. Once we move a lease
// to declined state, it is no longer associated with the client in any
// way.
......@@ -2785,6 +2841,8 @@ Dhcpv6Srv::declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
ia_rsp->addOption(createStatusCode(*decline, *ia_rsp, STATUS_Success,
"Lease declined. Hopefully the next one will be better."));
return (true);
}
Pkt6Ptr
......
......@@ -709,9 +709,10 @@ protected:
/// @param decline Decline messege sent by a client
/// @param reply Server's response (IA_NA with status will be added here)
/// @param client context
void
declineLeases(const Pkt6Ptr& decline, Pkt6Ptr& reply,
AllocEngine::ClientContext6& ctx);
/// @return true when expected to continue, false when hooks told us to drop
/// the packet
bool declineLeases(const Pkt6Ptr& decline, Pkt6Ptr& reply,
AllocEngine::ClientContext6& ctx);
/// @brief Declines leases in a single IA_NA option
///
......@@ -743,9 +744,10 @@ protected:
/// @param decline used for generating removal Name Change Request.
/// @param lease lease to be declined
/// @param ia_rsp response IA_NA.
void
declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
boost::shared_ptr<Option6IA> ia_rsp);
/// @return true when expected to continue, false when hooks told us to drop
/// the packet
bool declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
boost::shared_ptr<Option6IA> ia_rsp);
/// @brief A simple utility method that sets the status code
///
......
......@@ -25,7 +25,6 @@ using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::test;
namespace {
......
......@@ -18,6 +18,7 @@
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_message_test.h>
#include <dhcpsrv/lease.h>
#include <stats/stats_mgr.h>
using namespace isc;
......@@ -25,7 +26,6 @@ using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::test;
using namespace isc::stats;
namespace {
......@@ -56,38 +56,6 @@ const char* DECLINE_CONFIGS[] = {
class DeclineTest : public Dhcpv6MessageTest {
public:
/// @brief Specifies expected outcome
enum ExpectedResult {
SHOULD_PASS, // pass = accept decline, move lease to declined state.
SHOULD_FAIL // fail = reject the decline
};
/// @brief Specifies what address should the client include in its Decline
enum AddressInclusion {
VALID_ADDR, // Client will include its own, valid address
BOGUS_ADDR, // Client will include an address it doesn't own
NO_ADDR, // Client will send empty IA_NA (without address)
NO_IA // Client will not send IA_NA at all
};
/// @brief Tests if the acquired lease is or is not declined.
///
/// @param duid1 DUID used during lease acquisition
/// @param iaid1 IAID used during lease acquisition
/// @param duid2 DUID used during Decline exchange
/// @param iaid2 IAID used during Decline exchange
/// @param addr_type specify what sort of address the client should
/// include (its own, a bogus one or no address at all)
/// @param expected_result SHOULD_PASS if the lease is expected to
/// be successfully declined, or SHOULD_FAIL if the lease is expected
/// to not be declined.
void acquireAndDecline(const std::string& duid1,
const uint32_t iaid1,
const std::string& duid2,
const uint32_t iaid2,
AddressInclusion addr_type,
ExpectedResult expected_result);
/// @brief Constructor.
///
/// Sets up fake interfaces.
......@@ -100,17 +68,23 @@ public:
};
};
namespace isc {
namespace dhcp {
namespace test {
void
DeclineTest::acquireAndDecline(const std::string& duid1,
const uint32_t iaid1,
const std::string& duid2,
const uint32_t iaid2,
AddressInclusion addr_type,
ExpectedResult expected_result) {
Dhcpv6SrvTest::acquireAndDecline(Dhcp6Client& client,
const std::string& duid1,
const uint32_t iaid1,
const std::string& duid2,
const uint32_t iaid2,
AddressInclusion addr_type,
ExpectedResult expected_result) {
// Set this global statistic explicitly to zero.
StatsMgr::instance().setValue("declined-addresses", static_cast<int64_t>(0));
Dhcp6Client client;
client.setDUID(duid1);
client.useNA(iaid1);
......@@ -135,7 +109,7 @@ DeclineTest::acquireAndDecline(const std::string& duid1,
// Make sure that the client has acquired NA lease.
std::vector<Lease6> leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
ASSERT_EQ(1, leases_client_na.size());
EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
EXPECT_EQ(STATUS_Success, client.getStatusCode(iaid1));
// Remember the acquired address.
IOAddress acquired_address = leases_client_na[0].addr_;
......@@ -214,19 +188,25 @@ DeclineTest::acquireAndDecline(const std::string& duid1,
// This test checks that the client can acquire and decline the lease.
TEST_F(DeclineTest, basic) {
acquireAndDecline("01:02:03:04:05:06", 1234,
"01:02:03:04:05:06", 1234,
VALID_ADDR, SHOULD_PASS);
Dhcp6Client client;
acquireAndDecline(client, "01:02:03:04:05:06", 1234, "01:02:03:04:05:06",
1234, VALID_ADDR, SHOULD_PASS);
}
};
};
};
namespace {
// This test verifies the decline is rejected in the following case:
// - Client acquires new lease using duid, iaid
// - Client sends the DECLINE with duid, iaid, but uses wrong address.
// - The server rejects Decline due to address mismatch
TEST_F(DeclineTest, addressMismatch) {
acquireAndDecline("01:02:03:04:05:06", 1234,
"01:02:03:04:05:06", 1234,
BOGUS_ADDR, SHOULD_FAIL);
Dhcp6Client client;
acquireAndDecline(client, "01:02:03:04:05:06", 1234, "01:02:03:04:05:06",
1234, BOGUS_ADDR, SHOULD_FAIL);
}
// This test verifies the decline is rejected in the following case:
......@@ -234,9 +214,9 @@ TEST_F(DeclineTest, addressMismatch) {
// - Client sends the DECLINE with duid, iaid2
// - The server rejects Decline due to IAID mismatch
TEST_F(DeclineTest, iaidMismatch) {
acquireAndDecline("01:02:03:04:05:06", 1234,
"01:02:03:04:05:06", 1235,
VALID_ADDR, SHOULD_FAIL);
Dhcp6Client client;
acquireAndDecline(client, "01:02:03:04:05:06", 1234, "01:02:03:04:05:06",
1235, VALID_ADDR, SHOULD_FAIL);
}
// This test verifies the decline correctness in the following case:
......@@ -244,7 +224,8 @@ TEST_F(DeclineTest, iaidMismatch) {
// - Client sends the DECLINE using duid2, iaid
// - The server rejects the Decline due to DUID mismatch
TEST_F(DeclineTest, duidMismatch) {
acquireAndDecline("01:02:03:04:05:06", 1234,
Dhcp6Client client;
acquireAndDecline(client, "01:02:03:04:05:06", 1234,
"01:02:03:04:05:07", 1234,
VALID_ADDR, SHOULD_FAIL);
}
......@@ -255,7 +236,8 @@ TEST_F(DeclineTest, duidMismatch) {
// include the address in it
// - The server rejects the Decline due to missing address
TEST_F(DeclineTest, noAddrsSent) {
acquireAndDecline("01:02:03:04:05:06", 1234,
Dhcp6Client client;
acquireAndDecline(client, "01:02:03:04:05:06", 1234,
"01:02:03:04:05:06", 1234,
NO_ADDR, SHOULD_FAIL);
}
......@@ -266,7 +248,8 @@ TEST_F(DeclineTest, noAddrsSent) {
// include IA_NA at all
// - The server rejects the Decline due to missing IA_NA
TEST_F(DeclineTest, noIAs) {
acquireAndDecline("01:02:03:04:05:06", 1234,
Dhcp6Client client;
acquireAndDecline(client, "01:02:03:04:05:06", 1234,
"01:02:03:04:05:06", 1234,
NO_IA, SHOULD_FAIL);
}
......
......@@ -30,7 +30,6 @@
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::test;
namespace {
......
......@@ -178,7 +178,7 @@ public:
/// - not relayed
///
/// @param srv Object representing server under test.
Dhcp6Client(boost::shared_ptr<isc::test::NakedDhcpv6Srv>& srv);
Dhcp6Client(boost::shared_ptr<isc::dhcp::test::NakedDhcpv6Srv>& srv);
/// @brief Create lease for the client.
///
......@@ -365,7 +365,7 @@ public:
}
/// @brief Returns the server that the client is communicating with.
boost::shared_ptr<isc::test::NakedDhcpv6Srv> getServer() const {
boost::shared_ptr<isc::dhcp::test::NakedDhcpv6Srv> getServer() const {
return (srv_);
}
......@@ -727,7 +727,7 @@ private:
std::string iface_name_;
/// @brief Pointer to the server that the client is communicating with.
boost::shared_ptr<isc::test::NakedDhcpv6Srv> srv_;
boost::shared_ptr<isc::dhcp::test::NakedDhcpv6Srv> srv_;
bool use_na_; ///< Enable address assignment.
bool use_pd_; ///< Enable prefix delegation.
......
// Copyright (C) 2014, 2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2015 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
......@@ -26,7 +26,7 @@ namespace test {
/// @brief Base class for test fixure classes used to validate the DHCPv6
/// message processing by the server.
class Dhcpv6MessageTest : public isc::test::Dhcpv6SrvTest {
class Dhcpv6MessageTest : public isc::dhcp::test::Dhcpv6SrvTest {
public:
/// @brief Constructor.
///
......
......@@ -53,7 +53,6 @@
using namespace isc;
using namespace isc::data;
using namespace isc::test;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
......
......@@ -29,6 +29,7 @@ using namespace isc::asiolink;
using namespace isc::stats;
namespace isc {
namespace dhcp {
namespace test {
const char* NakedDhcpv6SrvTest::DUID_FILE = "server-id-test.txt";
......@@ -827,6 +828,6 @@ NakedDhcpv6SrvTest::checkIA_NAStatusCode(
}
}
}; // end of isc::test namespace
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
......@@ -39,6 +39,7 @@
#include <list>
namespace isc {
namespace dhcp {
namespace test {
/// @brief "naked" Dhcpv6Srv class that exposes internal members
......@@ -290,11 +291,28 @@ public:
std::string valid_iface_;
};
// We need to pass one reference to the Dhcp6Client, which is defined in
// dhcp6_client.h. That header includes this file. To avoid circular
// dependencies, we use forward declaration here.
class Dhcp6Client;
// Provides suport for tests against a preconfigured subnet6
// extends upon NakedDhcp6SrvTest
class Dhcpv6SrvTest : public NakedDhcpv6SrvTest {
public:
/// Name of the server-id file (used in server-id tests)
/// @brief Specifies expected outcome
enum ExpectedResult {
SHOULD_PASS, // pass = accept decline, move lease to declined state.
SHOULD_FAIL // fail = reject the decline
};
/// @brief Specifies what address should the client include in its Decline
enum AddressInclusion {
VALID_ADDR, // Client will include its own, valid address
BOGUS_ADDR, // Client will include an address it doesn't own
NO_ADDR, // Client will send empty IA_NA (without address)
NO_IA // Client will not send IA_NA at all
};
/// @brief Constructor that initializes a simple default configuration
///
......@@ -437,6 +455,26 @@ public:
bool compareOptions(const isc::dhcp::OptionPtr& option1,
const isc::dhcp::OptionPtr& option2);
/// @brief Tests if the acquired lease is or is not declined.
///
/// @param client Dhcp6Client instance
/// @param duid1 DUID used during lease acquisition
/// @param iaid1 IAID used during lease acquisition
/// @param duid2 DUID used during Decline exchange
/// @param iaid2 IAID used during Decline exchange
/// @param addr_type specify what sort of address the client should
/// include (its own, a bogus one or no address at all)
/// @param expected_result SHOULD_PASS if the lease is expected to
/// be successfully declined, or SHOULD_FAIL if the lease is expected
/// to not be declined.
void acquireAndDecline(Dhcp6Client& client,
const std::string& duid1,
const uint32_t iaid1,
const std::string& duid2,
const uint32_t iaid2,
AddressInclusion addr_type,
ExpectedResult expected_result);
/// @brief Performs basic (positive) RENEW test
///
/// See renewBasic and pdRenewBasic tests for detailed explanation.
......@@ -520,7 +558,8 @@ public:
NakedDhcpv6Srv srv_;
};
}; // end of isc::test namespace
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
#endif // DHCP6_TEST_UTILS_H
......@@ -32,7 +32,7 @@
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::test;
using namespace isc::dhcp::test;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp_ddns;
......
......@@ -28,6 +28,7 @@
#include <hooks/server_hooks.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
#include <dhcp/tests/pkt_captures.h>
#include <cc/command_interpreter.h>
#include <boost/scoped_ptr.hpp>
......@@ -39,7 +40,7 @@
using namespace isc;
using namespace isc::data;
using namespace isc::test;
using namespace isc::dhcp::test;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::util;
......@@ -457,6 +458,43 @@ public:
return (0);
}
/// Lease6_decline test callback
///
/// Stores all parameters in callback_* fields.
///
/// @param callout_handle handle passed by the hooks framework
/// @return always 0
static int
lease6_decline_callout(CalloutHandle& callout_handle) {
callback_name_ = string("lease6_decline");
callout_handle.getArgument("query6", callback_pkt6_);
callout_handle.getArgument("lease6", callback_lease6_);
return (0);
}
/// Lease6_decline callout that sets status to SKIP
///
/// @param callout_handle handle passed by the hooks framework
/// @return always 0
static int
lease6_decline_skip_callout(CalloutHandle& callout_handle) {
callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
return (lease6_decline_callout(callout_handle));
}
/// Lease6_decline callout that sets status to DROP
///