mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
[master] kea-dhcp6 now supports global host reservations
Merge branch '13-global-host-reservations-task-3-add-v6-support-for-new-hr_global-mode'
This commit is contained in:
commit
a5484c4d88
2
AUTHORS
2
AUTHORS
@ -11,7 +11,7 @@ Primary developers:
|
||||
- Marcin Siodelski (DHCPv4, DHCPv6 components, options handling, perfdhcp,
|
||||
host reservation, lease file cleanup, lease expiration,
|
||||
control agent, shared networks, high availability)
|
||||
- Thomas Markwalder (DDNS, user_chk)
|
||||
- Thomas Markwalder (DDNS, user_chk, global host reservations)
|
||||
- Jeremy C. Reed (documentation, build system, testing, release engineering)
|
||||
- Wlodek Wencel (testing, release engineering)
|
||||
- Francis Dupont (crypto, perfdhcp, control agent)
|
||||
|
@ -3103,7 +3103,7 @@ should include options from the isc option space:
|
||||
</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
<para>This feature is currently implemented for memfile backend.</para>
|
||||
|
||||
<para>
|
||||
@ -3268,6 +3268,24 @@ should include options from the isc option space:
|
||||
reservation checks when dealing with existing leases. Therefore, system
|
||||
administrators are encouraged to use out-of-pool reservations if
|
||||
possible.</para>
|
||||
|
||||
<para>Beginning with Kea 1.5.0, there is now support for global
|
||||
host reservations. These are reservations that are specified at the
|
||||
global level within the configuration and that do not belong to any
|
||||
specific subnet. Kea will still match inbound client packets to a
|
||||
subnet as before, but when the subnet's reservation mode is set to
|
||||
<command>"global"</command>, Kea will look for host reservations only
|
||||
among the global reservations defined. Typcially, such reservations would
|
||||
be used to reserve hostnames for clients which may move from one subnet
|
||||
to another.
|
||||
</para>
|
||||
|
||||
<note>You can reserve any ip-address or prefix in a global reservation.
|
||||
Just keep in mind that Kea will not do any sanity checking on the address
|
||||
or prefix and that for Kea 1.5.0, support for global reservations should
|
||||
be considered experimental.
|
||||
</note>
|
||||
|
||||
</section>
|
||||
|
||||
<section xml:id="reservation6-conflict">
|
||||
@ -3317,6 +3335,15 @@ should include options from the isc option space:
|
||||
out-of-pool reservations. If the reserved address does not belong to a
|
||||
pool, there is no way that other clients could get this address.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>The conflict resolution mechanism does not work for global
|
||||
reservations. As of Kea 1.5.0, it is generally recommended to not use
|
||||
global reservations for addresses or prefixes. If you want to use it
|
||||
anyway, you have to manually ensure that the reserved values are not
|
||||
in the dynamic pools.</para>
|
||||
</note>
|
||||
|
||||
</section>
|
||||
|
||||
<section xml:id="reservation6-hostname">
|
||||
@ -3538,10 +3565,10 @@ should include options from the isc option space:
|
||||
Allowed values are:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><simpara> <command>all</command> - enables all host reservation
|
||||
types. This is the default value. This setting is the safest and the most
|
||||
flexible. It allows in-pool and out-of-pool reservations. As all checks
|
||||
are conducted, it is also the slowest.
|
||||
<listitem><simpara> <command>all</command> - enables both in-pool
|
||||
and out-of-pool host reservation types. This is the default value. This
|
||||
setting is the safest and the most flexible. As all checks are conducted,
|
||||
it is also the slowest. This does not check against global reservations.
|
||||
</simpara></listitem>
|
||||
|
||||
<listitem><simpara> <command>out-of-pool</command> - allows only out of
|
||||
@ -3551,7 +3578,18 @@ should include options from the isc option space:
|
||||
with in-pool addresses, thus improving performance. Do not use this mode
|
||||
if any of your reservations use in-pool address. Caution is advised when
|
||||
using this setting. Kea does not sanity check the reservations against
|
||||
<command>reservation-mode</command> and misconfiguration may cause problems.
|
||||
<command>reservation-mode</command> and misconfiguration may cause
|
||||
problems.
|
||||
</simpara></listitem>
|
||||
|
||||
<listitem><simpara> <command>global</command> - allows only global
|
||||
host reservations. With this setting in place, the server searches for
|
||||
reservations for a client only among the defined global reservations.
|
||||
If an address is specified, the server will skip the reservation checks
|
||||
done when dealing in other modes, thus improving performance.
|
||||
Caution is advised when using this setting: Kea does not sanity check
|
||||
the reservations when <command>global</command> and
|
||||
misconfiguration may cause problems.
|
||||
</simpara></listitem>
|
||||
|
||||
<listitem><simpara>
|
||||
@ -3576,9 +3614,44 @@ should include options from the isc option space:
|
||||
}
|
||||
]
|
||||
}
|
||||
</screen>
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An example configuration using global reservations is shown below:
|
||||
<screen>
|
||||
"Dhcp6": {
|
||||
|
||||
<userinput>
|
||||
"reservations": [
|
||||
{
|
||||
"duid": "00:03:00:01:11:22:33:44:55:66",
|
||||
"hostname": "host-one"
|
||||
},
|
||||
{
|
||||
"duid": "00:03:00:01:99:88:77:66:55:44",
|
||||
"hostname": "host-two"
|
||||
}
|
||||
],
|
||||
</userinput>
|
||||
"subnet6": [
|
||||
{
|
||||
"subnet": "2001:db8:1::/64",
|
||||
<userinput>"reservation-mode": "global"</userinput>,
|
||||
...
|
||||
},
|
||||
{
|
||||
"subnet": "2001:db8:2::/64",
|
||||
<userinput>"reservation-mode": "global"</userinput>,
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
</screen>
|
||||
For more details regarding global reservations, see
|
||||
<xref linkend="global-reservations6"/>.
|
||||
</para>
|
||||
|
||||
<para>Another aspect of the host reservations are different types of
|
||||
identifiers. Kea 1.1.0 supports two types of identifiers
|
||||
in DHCPv6: hw-address and duid, but more identifier types
|
||||
@ -3620,6 +3693,76 @@ If not specified, the default value is:
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
<section id="global-reservations6">
|
||||
<title>Global reservations in DHCPv6</title>
|
||||
|
||||
<para>In some deployments, such as mobile, clients can roam within the
|
||||
network and there is a desire to specify certain parameters regardless of
|
||||
the client's current location. To facilitate such a need, a global
|
||||
reservation mechanism has been implemented. The idea behind it is that
|
||||
regular host reservations are tied to specific subnets, by using specific
|
||||
subnet-id. Kea 1.5.0 introduced a new capability to specify global
|
||||
reservation that can be used in every subnet that has global reservations
|
||||
enabled.</para>
|
||||
|
||||
<para>This feature can be used to assign certain parameters, such as
|
||||
hostname or other dedicated, host-specific options. It can also be used to
|
||||
assign addresses or prefixes. However, global reservations that assign
|
||||
either of these bypass the whole topology determination provided by DHCP
|
||||
logic implemented in Kea. It is very easy to misuse this feature and get
|
||||
configuration that is inconsistent. To give a specific example, imagine a
|
||||
global reservation for an address 2001:db8:1111::1 and two subnets
|
||||
2001:db8:1111::/48 and 2001:db8:ffff::/48. If global reservations are used
|
||||
in both subnets and a device matching global host reservations visits part
|
||||
of the network that is covered by 2001:db8:ffff::/48, it will get an IP
|
||||
address 2001:db8:ffff::1, which will be outside of the prefix announced
|
||||
by its local router using Router Advertisements. Such a configuration
|
||||
would be unsuable or at the very least ridden with issues, such as the
|
||||
downlink traffic not reaching the device.</para>
|
||||
|
||||
<para>
|
||||
To use global host reservations a configuration similar to the following
|
||||
can be used:
|
||||
|
||||
<screen>
|
||||
"Dhcp6:" {
|
||||
// This specifies global reservations. They will apply to all subnets that
|
||||
// have global reservations enabled.
|
||||
<userinput>
|
||||
"reservations": [
|
||||
{
|
||||
"hw-address": "aa:bb:cc:dd:ee:ff",
|
||||
"hostname": "hw-host-dynamic"
|
||||
},
|
||||
{
|
||||
"hw-address": "01:02:03:04:05:06",
|
||||
"hostname": "hw-host-fixed",
|
||||
|
||||
// Use of IP address is global reservation is risky. If used outside of
|
||||
// matching subnet, such as 3001::/64, it will result in a broken
|
||||
// configuration being handled to the client.
|
||||
"ip-address": "2001:db8:ff::77"
|
||||
},
|
||||
{
|
||||
"duid": "01:02:03:04:05",
|
||||
"hostname": "duid-host"
|
||||
}
|
||||
]</userinput>,
|
||||
"valid-lifetime": 600,
|
||||
"subnet4": [ {
|
||||
"subnet": "2001:db8:1::/64",
|
||||
<userinput>"reservation-mode": "global",</userinput>
|
||||
"pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ]
|
||||
} ]
|
||||
}
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>When using database backends, the global host reservations are
|
||||
distinguished from regular reservations by using subnet-id value of
|
||||
zero.</para>
|
||||
<!-- see CfgHostOperations::createConfig6() in
|
||||
src/lib/dhcpsrv/cfg_host_operations.cc -->
|
||||
|
||||
|
@ -699,7 +699,13 @@ Dhcpv6SrvTest::configure(const std::string& config) {
|
||||
void
|
||||
Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) {
|
||||
ConstElementPtr json;
|
||||
ASSERT_NO_THROW(json = parseJSON(config));
|
||||
try {
|
||||
json = parseJSON(config);
|
||||
} catch (const std::exception& ex) {
|
||||
// Fatal failure on parsing error
|
||||
FAIL() << "config parsing failed, test is broken: " << ex.what();
|
||||
}
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
// Disable the re-detect flag
|
||||
@ -710,7 +716,8 @@ Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) {
|
||||
ASSERT_TRUE(status);
|
||||
int rcode;
|
||||
ConstElementPtr comment = isc::config::parseAnswer(rcode, status);
|
||||
ASSERT_EQ(0, rcode);
|
||||
ASSERT_EQ(0, rcode) << "configuration failed, test is broken: "
|
||||
<< comment->str();
|
||||
|
||||
CfgMgr::instance().commit();
|
||||
}
|
||||
|
@ -321,7 +321,105 @@ const char* CONFIGS[] = {
|
||||
" }"
|
||||
" ]"
|
||||
"} ]"
|
||||
"}"
|
||||
"}",
|
||||
|
||||
// Configuration 8: Global HRs TYPE_NAs
|
||||
"{ "
|
||||
"\"interfaces-config\": { \n"
|
||||
" \"interfaces\": [ \"*\" ] \n"
|
||||
"},\n "
|
||||
"\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ], \n"
|
||||
"\"reservations\": [ \n"
|
||||
"{ \n"
|
||||
" \"duid\": \"01:02:03:04\", \n"
|
||||
" \"hostname\": \"duid-host-fixed\", \n"
|
||||
" \"ip-addresses\": [ \"3001::1\" ] \n"
|
||||
"}, \n"
|
||||
"{ \n"
|
||||
" \"duid\": \"01:02:03:05\", \n"
|
||||
" \"hostname\": \"duid-host-dynamic\" \n"
|
||||
"}, \n"
|
||||
"{ \n"
|
||||
" \"hw-address\": \"38:60:77:d5:ff:ee\", \n"
|
||||
" \"hostname\": \"hw-host\" \n"
|
||||
"} \n"
|
||||
"], \n"
|
||||
"\"valid-lifetime\": 4000, \n"
|
||||
"\"preferred-lifetime\": 3000, \n"
|
||||
"\"rebind-timer\": 2000, \n"
|
||||
"\"renew-timer\": 1000, \n"
|
||||
"\"mac-sources\": [ \"ipv6-link-local\" ], \n"
|
||||
"\"subnet6\": [ \n"
|
||||
" { \n"
|
||||
" \"subnet\": \"2001:db8:1::/48\", \n"
|
||||
" \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], \n"
|
||||
" \"interface\" : \"eth0\", \n"
|
||||
" \"reservation-mode\": \"global\" \n"
|
||||
" },"
|
||||
" { \n"
|
||||
" \"subnet\": \"2001:db8:2::/48\", \n"
|
||||
" \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ], \n"
|
||||
" \"interface\" : \"eth1\", \n"
|
||||
" \"reservations\": [ \n"
|
||||
" { \n"
|
||||
" \"duid\": \"01:02:03:05\", \n"
|
||||
" \"hostname\": \"subnet-duid-host\" \n"
|
||||
" }] \n"
|
||||
" }"
|
||||
" ] \n"
|
||||
"} \n"
|
||||
,
|
||||
// Configuration 9: Global HRs TYPE_PDs
|
||||
"{ "
|
||||
"\"interfaces-config\": { \n"
|
||||
" \"interfaces\": [ \"*\" ] \n"
|
||||
"},\n "
|
||||
"\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ], \n"
|
||||
"\"reservations\": [ \n"
|
||||
"{ \n"
|
||||
" \"duid\": \"01:02:03:04\", \n"
|
||||
" \"hostname\": \"duid-host-fixed\", \n"
|
||||
" \"prefixes\": [ \"4000::100/120\" ]"
|
||||
"}, \n"
|
||||
"{ \n"
|
||||
" \"duid\": \"01:02:03:05\", \n"
|
||||
" \"hostname\": \"duid-host-dynamic\" \n"
|
||||
"} \n"
|
||||
"], \n"
|
||||
"\"valid-lifetime\": 4000, \n"
|
||||
"\"preferred-lifetime\": 3000, \n"
|
||||
"\"rebind-timer\": 2000, \n"
|
||||
"\"renew-timer\": 1000, \n"
|
||||
"\"mac-sources\": [ \"ipv6-link-local\" ], \n"
|
||||
"\"subnet6\": [ \n"
|
||||
" { \n"
|
||||
" \"subnet\": \"2001:db8:1::/48\", \n"
|
||||
" \"interface\" : \"eth0\", \n"
|
||||
" \"reservation-mode\": \"global\", \n"
|
||||
" \"pd-pools\": [ \n"
|
||||
" { \n"
|
||||
" \"prefix\": \"3000::\", \n"
|
||||
" \"prefix-len\": 119, \n"
|
||||
" \"delegated-len\": 120 \n"
|
||||
" }] \n"
|
||||
" },"
|
||||
" { \n"
|
||||
" \"subnet\": \"2001:db8:2::/48\", \n"
|
||||
" \"interface\" : \"eth1\", \n"
|
||||
" \"pd-pools\": [ \n"
|
||||
" { \n"
|
||||
" \"prefix\": \"3001::\", \n"
|
||||
" \"prefix-len\": 119, \n"
|
||||
" \"delegated-len\": 120 \n"
|
||||
" }], \n"
|
||||
" \"reservations\": [ \n"
|
||||
" { \n"
|
||||
" \"duid\": \"01:02:03:05\", \n"
|
||||
" \"hostname\": \"subnet-duid-host\" \n"
|
||||
" }] \n"
|
||||
" }"
|
||||
" ] \n"
|
||||
"} \n"
|
||||
};
|
||||
|
||||
/// @brief Base class representing leases and hints conveyed within IAs.
|
||||
@ -701,6 +799,14 @@ public:
|
||||
const Reservation& r5 = Reservation::UNSPEC(),
|
||||
const Reservation& r6 = Reservation::UNSPEC()) const;
|
||||
|
||||
/// @brief Verifies that an SARR exchange results in the expected lease
|
||||
///
|
||||
/// @param client Client configured to request a single lease
|
||||
/// @param exp_address expected address/prefix of the lease
|
||||
/// @param exp_hostname expected hostname on the lease
|
||||
void sarrTest(Dhcp6Client& client, const std::string& exp_address,
|
||||
const std::string& exp_hostname);
|
||||
|
||||
/// @brief Configures client to include hint.
|
||||
///
|
||||
/// @param client Reference to a client.
|
||||
@ -1063,6 +1169,25 @@ HostTest::requestEmptyIAs(Dhcp6Client& client) {
|
||||
client.requestPrefix(6);
|
||||
}
|
||||
|
||||
void
|
||||
HostTest::sarrTest(Dhcp6Client& client, const std::string& exp_address,
|
||||
const std::string& exp_hostname) {
|
||||
// Perform 4-way exchange.
|
||||
ASSERT_NO_THROW(client.doSARR());
|
||||
|
||||
// Verify that the client got a dynamic address
|
||||
ASSERT_EQ(1, client.getLeaseNum());
|
||||
Lease6 lease_client = client.getLease(0);
|
||||
EXPECT_EQ(exp_address, lease_client.addr_.toText());
|
||||
|
||||
// Check that the server recorded the lease
|
||||
// and that the server lease has expected hostname.
|
||||
Lease6Ptr lease_server = checkLease(lease_client);
|
||||
ASSERT_TRUE(lease_server);
|
||||
EXPECT_EQ(exp_hostname, lease_server->hostname_);
|
||||
}
|
||||
|
||||
|
||||
// Test basic SARR scenarios against a server configured with one subnet
|
||||
// containing two reservations. One reservation with a hostname, one
|
||||
// without a hostname. Scenarios:
|
||||
@ -1918,5 +2043,108 @@ TEST_F(HostTest, conflictResolutionReuseExpired) {
|
||||
EXPECT_FALSE(client2.hasLeaseWithZeroLifetimeForPrefix(IOAddress("3000::"), 120));
|
||||
}
|
||||
|
||||
// Verifies fundamental Global vs Subnet host reservations for NA leases
|
||||
TEST_F(HostTest, globalReservationsNA) {
|
||||
Dhcp6Client client;
|
||||
ASSERT_NO_FATAL_FAILURE(configure(CONFIGS[8], *client.getServer()));
|
||||
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(2, subnets->size());
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Global HR by DUID with reserved address");
|
||||
client.setDUID("01:02:03:04");
|
||||
client.requestAddress(1234, IOAddress("::"));
|
||||
// Should get global reserved address and reserved host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::1", "duid-host-fixed"));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Global HR by DUID with dynamic address");
|
||||
client.clearConfig();
|
||||
client.setDUID("01:02:03:05");
|
||||
client.requestAddress(1234, IOAddress("::"));
|
||||
// Should get dynamic address and reserved host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:1::", "duid-host-dynamic"));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Global HR by HW Address with dynamic address");
|
||||
client.clearConfig();
|
||||
client.setDUID("33:44:55:66");
|
||||
client.setLinkLocal(IOAddress("fe80::3a60:77ff:fed5:ffee"));
|
||||
client.requestAddress(1234, IOAddress("::"));
|
||||
// Should get dynamic address and hardware host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:1::1", "hw-host"));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Default subnet mode excludes Global HR");
|
||||
client.clearConfig();
|
||||
client.setInterface("eth1");
|
||||
client.setDUID("01:02:03:04");
|
||||
client.requestAddress(1234, IOAddress("::"));
|
||||
// Should get dynamic address and no host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:2::", ""));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Subnet reservation over global");
|
||||
client.clearConfig();
|
||||
client.setInterface("eth1");
|
||||
client.setDUID("01:02:03:05");
|
||||
client.requestAddress(1234, IOAddress("::"));
|
||||
// Should get dynamic address and host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:2::1", "subnet-duid-host"));
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies fundamental Global vs Subnet host reservations for PD leases
|
||||
TEST_F(HostTest, globalReservationsPD) {
|
||||
Dhcp6Client client;
|
||||
ASSERT_NO_FATAL_FAILURE(configure(CONFIGS[9], *client.getServer()));
|
||||
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(2, subnets->size());
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Global HR by DUID with reserved prefix");
|
||||
client.setDUID("01:02:03:04");
|
||||
client.requestPrefix(1);
|
||||
// Should get global reserved prefix and reserved host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "4000::100", "duid-host-fixed"));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Global HR by DUID with dynamic prefix");
|
||||
client.clearConfig();
|
||||
client.setDUID("01:02:03:05");
|
||||
client.requestPrefix(1);
|
||||
// Should get dynamic prefix and reserved host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3000::", "duid-host-dynamic"));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Default subnet mode excludes Global HR");
|
||||
client.clearConfig();
|
||||
client.setInterface("eth1");
|
||||
client.setDUID("01:02:03:04");
|
||||
client.requestPrefix(1);
|
||||
// Should get dynamic prefix and no host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::", ""));
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("Subnet reservation over global");
|
||||
client.clearConfig();
|
||||
client.setInterface("eth1");
|
||||
client.setDUID("01:02:03:05");
|
||||
client.requestPrefix(1);
|
||||
// Should get dynamic prefix and subnet reserved host name
|
||||
ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::100", "subnet-duid-host"));
|
||||
}
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
@ -483,14 +483,37 @@ ConstHostPtr
|
||||
AllocEngine::ClientContext6::currentHost() const {
|
||||
Subnet6Ptr subnet = host_subnet_ ? host_subnet_ : subnet_;
|
||||
if (subnet) {
|
||||
auto host = hosts_.find(subnet->getID());
|
||||
SubnetID id = (subnet_->getHostReservationMode() == Network::HR_GLOBAL ?
|
||||
SUBNET_ID_GLOBAL : subnet->getID());
|
||||
|
||||
auto host = hosts_.find(id);
|
||||
if (host != hosts_.cend()) {
|
||||
return (host->second);
|
||||
}
|
||||
}
|
||||
|
||||
return (ConstHostPtr());
|
||||
}
|
||||
|
||||
ConstHostPtr
|
||||
AllocEngine::ClientContext6::globalHost() const {
|
||||
Subnet6Ptr subnet = host_subnet_ ? host_subnet_ : subnet_;
|
||||
if (subnet && subnet_->getHostReservationMode() == Network::HR_GLOBAL) {
|
||||
auto host = hosts_.find(SUBNET_ID_GLOBAL);
|
||||
if (host != hosts_.cend()) {
|
||||
return (host->second);
|
||||
}
|
||||
}
|
||||
|
||||
return (ConstHostPtr());
|
||||
}
|
||||
|
||||
bool
|
||||
AllocEngine::ClientContext6::hasGlobalReservation(const IPv6Resrv& resv) const {
|
||||
ConstHostPtr ghost = globalHost();
|
||||
return (ghost && ghost->hasReservation(resv));
|
||||
}
|
||||
|
||||
void AllocEngine::findReservation(ClientContext6& ctx) {
|
||||
ctx.hosts_.clear();
|
||||
|
||||
@ -505,6 +528,17 @@ void AllocEngine::findReservation(ClientContext6& ctx) {
|
||||
SharedNetwork6Ptr network;
|
||||
subnet->getSharedNetwork(network);
|
||||
|
||||
if (subnet->getHostReservationMode() == Network::HR_GLOBAL) {
|
||||
ConstHostPtr ghost = findGlobalReservation(ctx);
|
||||
if (ghost) {
|
||||
ctx.hosts_[SUBNET_ID_GLOBAL] = ghost;
|
||||
|
||||
// @todo In theory, to support global as part of HR_ALL,
|
||||
// we would just keep going, instead of returning.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the subnet belongs to a shared network it is usually going to be
|
||||
// more efficient to make a query for all reservations for a particular
|
||||
// client rather than a query for each subnet within this shared network.
|
||||
@ -567,6 +601,24 @@ void AllocEngine::findReservation(ClientContext6& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
ConstHostPtr
|
||||
AllocEngine::findGlobalReservation(ClientContext6& ctx) {
|
||||
ConstHostPtr host;
|
||||
BOOST_FOREACH(const IdentifierPair& id_pair, ctx.host_identifiers_) {
|
||||
// Attempt to find a host using a specified identifier.
|
||||
host = HostMgr::instance().get6(SUBNET_ID_GLOBAL, id_pair.first,
|
||||
&id_pair.second[0], id_pair.second.size());
|
||||
|
||||
// If we found matching global host we're done.
|
||||
if (host) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (host);
|
||||
}
|
||||
|
||||
|
||||
Lease6Collection
|
||||
AllocEngine::allocateLeases6(ClientContext6& ctx) {
|
||||
|
||||
@ -1020,7 +1072,6 @@ void
|
||||
AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
||||
Lease6Collection& existing_leases) {
|
||||
|
||||
|
||||
// If there are no reservations or the reservation is v4, there's nothing to do.
|
||||
if (ctx.hosts_.empty()) {
|
||||
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
|
||||
@ -1029,6 +1080,11 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
||||
return;
|
||||
}
|
||||
|
||||
if (allocateGlobalReservedLeases6(ctx, existing_leases)) {
|
||||
// global reservation provided the lease, we're done
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's convert this from Lease::Type to IPv6Reserv::Type
|
||||
IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
|
||||
IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
|
||||
@ -1039,8 +1095,7 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
||||
BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
|
||||
if ((lease->valid_lft_ != 0)) {
|
||||
if ((ctx.hosts_.count(lease->subnet_id_) > 0) &&
|
||||
ctx.hosts_[lease->subnet_id_]->hasReservation(IPv6Resrv(type, lease->addr_,
|
||||
lease->prefixlen_))) {
|
||||
ctx.hosts_[lease->subnet_id_]->hasReservation(makeIPv6Resrv(*lease))) {
|
||||
// We found existing lease for a reserved address or prefix.
|
||||
// We'll simply extend the lifetime of the lease.
|
||||
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
|
||||
@ -1188,6 +1243,127 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
|
||||
Lease6Collection& existing_leases) {
|
||||
// Get the global host
|
||||
ConstHostPtr ghost = ctx.globalHost();
|
||||
if (!ghost) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
// We want to avoid allocating a new lease for an IA if there is already
|
||||
// a valid lease for which client has reservation. So, we first check if
|
||||
// we already have a lease for a reserved address or prefix.
|
||||
BOOST_FOREACH(const Lease6Ptr& lease, existing_leases) {
|
||||
if ((lease->valid_lft_ != 0) &&
|
||||
(ghost->hasReservation(makeIPv6Resrv(*lease)))) {
|
||||
// We found existing lease for a reserved address or prefix.
|
||||
// We'll simply extend the lifetime of the lease.
|
||||
LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
|
||||
ALLOC_ENGINE_V6_ALLOC_HR_LEASE_EXISTS)
|
||||
.arg(ctx.query_->getLabel())
|
||||
.arg(lease->typeToText(lease->type_))
|
||||
.arg(lease->addr_.toText());
|
||||
|
||||
// Besides IP reservations we're also going to return other reserved
|
||||
// parameters, such as hostname. We want to hand out the hostname value
|
||||
// from the same reservation entry as IP addresses. Thus, let's see if
|
||||
// there is any hostname reservation.
|
||||
if (!ghost->getHostname().empty()) {
|
||||
// We have to determine whether the hostname is generated
|
||||
// in response to client's FQDN or not. If yes, we will
|
||||
// need to qualify the hostname. Otherwise, we just use
|
||||
// the hostname as it is specified for the reservation.
|
||||
OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
|
||||
ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
|
||||
qualifyName(ghost->getHostname(), static_cast<bool>(fqdn));
|
||||
}
|
||||
|
||||
// If this is a real allocation, we may need to extend the lease
|
||||
// lifetime.
|
||||
if (!ctx.fake_allocation_ && conditionalExtendLifetime(*lease)) {
|
||||
LeaseMgrFactory::instance().updateLease6(lease);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
// There is no lease for a reservation in this IA. So, let's now iterate
|
||||
// over reservations specified and try to allocate one of them for the IA.
|
||||
|
||||
// Let's convert this from Lease::Type to IPv6Reserv::Type
|
||||
IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
|
||||
IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
|
||||
|
||||
const IPv6ResrvRange& reservs = ghost->getIPv6Reservations(type);
|
||||
BOOST_FOREACH(IPv6ResrvTuple type_lease_tuple, reservs) {
|
||||
// We do have a reservation for address or prefix.
|
||||
const IOAddress& addr = type_lease_tuple.second.getPrefix();
|
||||
uint8_t prefix_len = type_lease_tuple.second.getPrefixLen();
|
||||
|
||||
// We have allocated this address/prefix while processing one of the
|
||||
// previous IAs, so let's try another reservation.
|
||||
if (ctx.isAllocated(addr, prefix_len)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there's a lease for this address, let's not create it.
|
||||
// It doesn't matter whether it is for this client or for someone else.
|
||||
if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
|
||||
|
||||
if (!ghost->getHostname().empty()) {
|
||||
// If there is a hostname reservation here we should stick
|
||||
// to this reservation. By updating the hostname in the
|
||||
// context we make sure that the database is updated with
|
||||
// this new value and the server doesn't need to do it and
|
||||
// its processing performance is not impacted by the hostname
|
||||
// updates.
|
||||
|
||||
// We have to determine whether the hostname is generated
|
||||
// in response to client's FQDN or not. If yes, we will
|
||||
// need to qualify the hostname. Otherwise, we just use
|
||||
// the hostname as it is specified for the reservation.
|
||||
OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
|
||||
ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
|
||||
qualifyName(ghost->getHostname(), static_cast<bool>(fqdn));
|
||||
}
|
||||
|
||||
// Ok, let's create a new lease...
|
||||
CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
|
||||
Lease6Ptr lease = createLease6(ctx, addr, prefix_len, callout_status);
|
||||
|
||||
// ... and add it to the existing leases list.
|
||||
existing_leases.push_back(lease);
|
||||
|
||||
if (ctx.currentIA().type_ == Lease::TYPE_NA) {
|
||||
LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED)
|
||||
.arg(addr.toText())
|
||||
.arg(ctx.query_->getLabel());
|
||||
} else {
|
||||
LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_PREFIX_GRANTED)
|
||||
.arg(addr.toText())
|
||||
.arg(static_cast<int>(prefix_len))
|
||||
.arg(ctx.query_->getLabel());
|
||||
}
|
||||
|
||||
// We found a lease for this client and this IA. Let's return.
|
||||
// Returning after the first lease was assigned is useful if we
|
||||
// have multiple reservations for the same client. If the client
|
||||
// sends 2 IAs, the first time we call allocateReservedLeases6 will
|
||||
// use the first reservation and return. The second time, we'll
|
||||
// go over the first reservation, but will discover that there's
|
||||
// a lease corresponding to it and will skip it and then pick
|
||||
// the second reservation and turn it into the lease. This approach
|
||||
// would work for any number of reservations.
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
void
|
||||
AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
|
||||
Lease6Collection& existing_leases) {
|
||||
@ -1210,19 +1386,12 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
|
||||
BOOST_FOREACH(const Lease6Ptr& candidate, copy) {
|
||||
// If we have reservation we should check if the reservation is for
|
||||
// the candidate lease. If so, we simply accept the lease.
|
||||
if (ctx.hosts_.count(candidate->subnet_id_) > 0) {
|
||||
if (candidate->type_ == Lease6::TYPE_NA) {
|
||||
if (ctx.hosts_[candidate->subnet_id_]->hasReservation(
|
||||
IPv6Resrv(IPv6Resrv::TYPE_NA, candidate->addr_))) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (ctx.hosts_[candidate->subnet_id_]->hasReservation(
|
||||
IPv6Resrv(IPv6Resrv::TYPE_PD,candidate->addr_,
|
||||
candidate->prefixlen_))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
IPv6Resrv resv = makeIPv6Resrv(*candidate);
|
||||
if ((ctx.hasGlobalReservation(resv)) ||
|
||||
((ctx.hosts_.count(candidate->subnet_id_) > 0) &&
|
||||
(ctx.hosts_[candidate->subnet_id_]->hasReservation(resv)))) {
|
||||
// We have a subnet reservation
|
||||
continue;
|
||||
}
|
||||
|
||||
// The candidate address doesn't appear to be reserved for us.
|
||||
@ -1357,46 +1526,41 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
|
||||
for (Lease6Collection::iterator lease = existing_leases.begin();
|
||||
lease != existing_leases.end(); ++lease) {
|
||||
|
||||
IPv6Resrv resv(ctx.currentIA().type_ == Lease::TYPE_NA ?
|
||||
IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD,
|
||||
(*lease)->addr_, (*lease)->prefixlen_);
|
||||
|
||||
// If there is no reservation for this subnet.
|
||||
if ((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
|
||||
(ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv))) {
|
||||
// If there is reservation for this keep it.
|
||||
IPv6Resrv resv = makeIPv6Resrv(*(*lease));
|
||||
if (ctx.hasGlobalReservation(resv) ||
|
||||
((ctx.hosts_.count((*lease)->subnet_id_) > 0) &&
|
||||
(ctx.hosts_[(*lease)->subnet_id_]->hasReservation(resv)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
// We have reservations, but not for this lease. Release it.
|
||||
// Remove this lease from LeaseMgr
|
||||
LeaseMgrFactory::instance().deleteLease((*lease)->addr_);
|
||||
|
||||
// We have reservations, but not for this lease. Release it.
|
||||
// Update DNS if required.
|
||||
queueNCR(CHG_REMOVE, *lease);
|
||||
|
||||
// Remove this lease from LeaseMgr
|
||||
LeaseMgrFactory::instance().deleteLease((*lease)->addr_);
|
||||
// Need to decrease statistic for assigned addresses.
|
||||
StatsMgr::instance().addValue(
|
||||
StatsMgr::generateName("subnet", (*lease)->subnet_id_,
|
||||
ctx.currentIA().type_ == Lease::TYPE_NA ?
|
||||
"assigned-nas" : "assigned-pds"),
|
||||
static_cast<int64_t>(-1));
|
||||
|
||||
// Update DNS if required.
|
||||
queueNCR(CHG_REMOVE, *lease);
|
||||
/// @todo: Probably trigger a hook here
|
||||
|
||||
// Need to decrease statistic for assigned addresses.
|
||||
StatsMgr::instance().addValue(
|
||||
StatsMgr::generateName("subnet", (*lease)->subnet_id_,
|
||||
ctx.currentIA().type_ == Lease::TYPE_NA ?
|
||||
"assigned-nas" : "assigned-pds"),
|
||||
static_cast<int64_t>(-1));
|
||||
// Add this to the list of removed leases.
|
||||
ctx.currentIA().old_leases_.push_back(*lease);
|
||||
|
||||
/// @todo: Probably trigger a hook here
|
||||
// Set this pointer to NULL. The pointer is still valid. We're just
|
||||
// setting the Lease6Ptr to NULL value. We'll remove all NULL
|
||||
// pointers once the loop is finished.
|
||||
lease->reset();
|
||||
|
||||
// Add this to the list of removed leases.
|
||||
ctx.currentIA().old_leases_.push_back(*lease);
|
||||
|
||||
// Set this pointer to NULL. The pointer is still valid. We're just
|
||||
// setting the Lease6Ptr to NULL value. We'll remove all NULL
|
||||
// pointers once the loop is finished.
|
||||
lease->reset();
|
||||
|
||||
if (--total == 1) {
|
||||
// If there's only one lease left, break the loop.
|
||||
break;
|
||||
}
|
||||
if (--total == 1) {
|
||||
// If there's only one lease left, break the loop.
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1740,10 +1904,11 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the lease still belongs to the subnet and that the use of this subnet
|
||||
// is allowed per client classification. If not, remove this lease.
|
||||
if (((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
|
||||
!ctx.subnet_->clientSupported(ctx.query_->getClasses())) {
|
||||
// If the lease is not global and it is either out of range (NAs only) or it
|
||||
// is not permitted by subnet client classification, delete it.
|
||||
if (!(ctx.hasGlobalReservation(makeIPv6Resrv(*lease))) &&
|
||||
(((lease->type_ != Lease::TYPE_PD) && !ctx.subnet_->inRange(lease->addr_)) ||
|
||||
!ctx.subnet_->clientSupported(ctx.query_->getClasses()))) {
|
||||
// Oh dear, the lease is no longer valid. We need to get rid of it.
|
||||
|
||||
// Remove this lease from LeaseMgr
|
||||
|
@ -484,6 +484,23 @@ public:
|
||||
/// @return Pointer to the host object.
|
||||
ConstHostPtr currentHost() const;
|
||||
|
||||
/// @brief Returns global host reservation if there is one
|
||||
///
|
||||
/// If the current subnet's reservation mode is global and
|
||||
/// there is a global host (i.e. reservation belonging to
|
||||
/// the global subnet), return it. Otherwise return an
|
||||
/// empty pointer.
|
||||
///
|
||||
/// @return Pointer to the host object.
|
||||
ConstHostPtr globalHost() const;
|
||||
|
||||
/// @brief Determines if a global reservation exists
|
||||
///
|
||||
/// @return true if there current subnet's reservation mode is
|
||||
/// global and there is global host containing the given
|
||||
/// lease reservation, false otherwise
|
||||
bool hasGlobalReservation(const IPv6Resrv& resv) const;
|
||||
|
||||
/// @brief Default constructor.
|
||||
ClientContext6();
|
||||
|
||||
@ -747,6 +764,30 @@ public:
|
||||
/// @param ctx Client context that contains all necessary information.
|
||||
static void findReservation(ClientContext6& ctx);
|
||||
|
||||
/// @brief Attempts to find the host reservation for the client.
|
||||
///
|
||||
/// This method attempts to find a "global" host reservation matching the
|
||||
/// client identifier. It will return the first global reservation that
|
||||
/// matches per the configured list of host identifiers, or an empty
|
||||
/// pointer if no matches are found.
|
||||
///
|
||||
/// @param ctx Client context holding various information about the client.
|
||||
/// @return Pointer to the reservation found, or an empty pointer.
|
||||
static ConstHostPtr findGlobalReservation(ClientContext6& ctx);
|
||||
|
||||
/// @brief Creates an IPv6Resrv instance from a Lease6
|
||||
///
|
||||
/// @param lease Reference to the Lease6
|
||||
/// @return The newly formed IPv6Resrv instance
|
||||
static IPv6Resrv makeIPv6Resrv(const Lease6& lease) {
|
||||
if (lease.type_ == Lease::TYPE_NA) {
|
||||
return (IPv6Resrv(IPv6Resrv::TYPE_NA, lease.addr_,
|
||||
(lease.prefixlen_ ? lease.prefixlen_ : 128)));
|
||||
}
|
||||
|
||||
return (IPv6Resrv(IPv6Resrv::TYPE_PD, lease.addr_, lease.prefixlen_));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// @brief creates a lease and inserts it in LeaseMgr if necessary
|
||||
@ -801,16 +842,36 @@ private:
|
||||
|
||||
/// @brief Creates new leases based on reservations.
|
||||
///
|
||||
/// This method allocates new leases, based on host reservation. Existing
|
||||
/// leases are specified in existing_leases parameter. A new lease is not created,
|
||||
/// if there is a lease for specified address on existing_leases list or there is
|
||||
/// a lease used by someone else.
|
||||
/// This method allcoates new leases, based on host reservations.
|
||||
/// Existing leases are specified in the existing_leases parameter.
|
||||
/// It first calls @c allocateGlobalReservedLeases6 to accomodate
|
||||
/// subnets using global reservations. If that method allocates
|
||||
/// addresses, we return, otherwise we continue and check for non-global
|
||||
/// reservations. A new lease is not created, if there is a lease for
|
||||
/// specified address on existing_leases list or there is a lease used by
|
||||
/// someone else.
|
||||
///
|
||||
/// @param ctx client context that contains all details (subnet, client-id, etc.)
|
||||
/// @param existing_leases leases that are already associated with the client
|
||||
void
|
||||
allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases);
|
||||
|
||||
/// @brief Creates new leases based on global reservations.
|
||||
///
|
||||
/// This method is used by @allocateReservedLeases6, to allocate new leases based
|
||||
/// on global reservation if one exists and global reservations are enabled for
|
||||
/// the selected subnet. It differs from it's caller by looking only at the global
|
||||
/// reservation and therefore has no need to iterate over the selected subnet or it's
|
||||
/// siblings looking for host reservations. Like it's caller, existing leases are
|
||||
/// specified in existing_leases parameter. A new lease is not created, if there is
|
||||
/// a lease for specified address on existing_leases list or there is a lease used by
|
||||
/// someone else.
|
||||
///
|
||||
/// @param ctx client context that contains all details (subnet, client-id, etc.)
|
||||
/// @param existing_leases leases that are already associated with the client
|
||||
bool
|
||||
allocateGlobalReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases);
|
||||
|
||||
/// @brief Removes leases that are reserved for someone else.
|
||||
///
|
||||
/// Goes through the list specified in existing_leases and removes those that
|
||||
|
@ -753,7 +753,7 @@ TEST_F(AllocEngine6Test, smallPool6) {
|
||||
|
||||
// Create a subnet with a pool that has one address.
|
||||
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
|
||||
|
||||
|
||||
// Initialize FQDN for a lease.
|
||||
initFqdn("myhost.example.com", true, true);
|
||||
|
||||
@ -2717,6 +2717,197 @@ TEST_F(SharedNetworkAlloc6Test, requestRunningOut) {
|
||||
EXPECT_TRUE(leases.empty());
|
||||
}
|
||||
|
||||
// Verifies that client with a global hostname reservation can get and
|
||||
// renew a dynamic lease from their selected subnet.
|
||||
TEST_F(AllocEngine6Test, globalHostDynamicAddress) {
|
||||
boost::scoped_ptr<AllocEngine> engine;
|
||||
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
|
||||
ASSERT_TRUE(engine);
|
||||
|
||||
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
|
||||
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
|
||||
asiolink::IOAddress("0.0.0.0")));
|
||||
host->setHostname("ghost1");
|
||||
|
||||
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
subnet_->setHostReservationMode(Network::HR_GLOBAL);
|
||||
|
||||
// Create context which will be used to try to allocate leases
|
||||
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
|
||||
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
|
||||
ctx.currentIA().iaid_ = iaid_;
|
||||
|
||||
// Look up the reservation.
|
||||
findReservation(*engine, ctx);
|
||||
// Make sure we found our host.
|
||||
ConstHostPtr current = ctx.currentHost();
|
||||
ASSERT_TRUE(current);
|
||||
ASSERT_EQ("ghost1", current->getHostname());
|
||||
|
||||
// Check that we have been allocated a dynamic address.
|
||||
Lease6Ptr lease;
|
||||
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
|
||||
ASSERT_TRUE(lease);
|
||||
EXPECT_EQ("2001:db8:1::10", lease->addr_.toText());
|
||||
|
||||
// We're going to rollback the clock a little so we can verify a renewal.
|
||||
// We verify the "time" change in case the lease returned to us
|
||||
// by expectOneLease ever becomes a copy and not what is in the lease mgr.
|
||||
--lease->cltt_;
|
||||
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
|
||||
lease->addr_);
|
||||
ASSERT_TRUE(from_mgr);
|
||||
EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
|
||||
|
||||
// This is what the client will send in his renew message.
|
||||
AllocEngine::HintContainer hints;
|
||||
hints.push_back(make_pair(IOAddress("2001:db8:1::10"), 128));
|
||||
|
||||
// Set test fixture hostname_ to the expected value. This gets checked in
|
||||
// renewTest.
|
||||
hostname_ = "ghost1";
|
||||
|
||||
// Client should receive a lease.
|
||||
Lease6Collection renewed = renewTest(*engine, pool_, hints, true);
|
||||
ASSERT_EQ(1, renewed.size());
|
||||
|
||||
// And the lease lifetime should be extended.
|
||||
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
|
||||
<< "Lease lifetime was not extended, but it should";
|
||||
}
|
||||
|
||||
// Verifies that client with a global address reservation can get and
|
||||
// renew a lease for an arbitrary address.
|
||||
TEST_F(AllocEngine6Test, globalHostReservedAddress) {
|
||||
boost::scoped_ptr<AllocEngine> engine;
|
||||
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
|
||||
ASSERT_TRUE(engine);
|
||||
|
||||
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
|
||||
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
|
||||
asiolink::IOAddress("0.0.0.0")));
|
||||
host->setHostname("ghost1");
|
||||
IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("3001::1"), 128);
|
||||
host->addReservation(resv);
|
||||
|
||||
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
subnet_->setHostReservationMode(Network::HR_GLOBAL);
|
||||
|
||||
// Create context which will be used to try to allocate leases
|
||||
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
|
||||
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
|
||||
ctx.currentIA().iaid_ = iaid_;
|
||||
|
||||
// Look up the reservation.
|
||||
findReservation(*engine, ctx);
|
||||
// Make sure we found our host.
|
||||
ConstHostPtr current = ctx.currentHost();
|
||||
ASSERT_TRUE(current);
|
||||
ASSERT_EQ("ghost1", current->getHostname());
|
||||
|
||||
// Check that we have been allocated the fixed address.
|
||||
Lease6Ptr lease;
|
||||
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
|
||||
ASSERT_TRUE(lease);
|
||||
EXPECT_EQ("3001::1", lease->addr_.toText());
|
||||
|
||||
// We're going to rollback the clock a little so we can verify a renewal.
|
||||
// We verify the "time" change in case the lease returned to us
|
||||
// by expectOneLease ever becomes a copy and not what is in the lease mgr.
|
||||
--lease->cltt_;
|
||||
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
|
||||
lease->addr_);
|
||||
ASSERT_TRUE(from_mgr);
|
||||
EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
|
||||
|
||||
// This is what the client will send in his renew message.
|
||||
AllocEngine::HintContainer hints;
|
||||
hints.push_back(make_pair(IOAddress("3001::1"), 128));
|
||||
|
||||
// Set test fixture hostname_ to the expected value. This gets checked in
|
||||
// renewTest.
|
||||
hostname_ = "ghost1";
|
||||
|
||||
// Client should receive a lease.
|
||||
Lease6Collection renewed = renewTest(*engine, pool_, hints, false);
|
||||
ASSERT_EQ(1, renewed.size());
|
||||
|
||||
// And the lease lifetime should be extended.
|
||||
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
|
||||
<< "Lease lifetime was not extended, but it should";
|
||||
}
|
||||
|
||||
// Verifies that client with a global prefix reservation can get and
|
||||
// renew a lease for an arbitrary prefix.
|
||||
TEST_F(AllocEngine6Test, globalHostReservedPrefix) {
|
||||
boost::scoped_ptr<AllocEngine> engine;
|
||||
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
|
||||
ASSERT_TRUE(engine);
|
||||
|
||||
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
|
||||
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
|
||||
asiolink::IOAddress("0.0.0.0")));
|
||||
host->setHostname("ghost1");
|
||||
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("3001::"), 64);
|
||||
host->addReservation(resv);
|
||||
|
||||
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
subnet_->setHostReservationMode(Network::HR_GLOBAL);
|
||||
|
||||
// Create context which will be used to try to allocate leases
|
||||
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
|
||||
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
|
||||
ctx.currentIA().type_ = Lease::TYPE_PD;
|
||||
ctx.currentIA().iaid_ = iaid_;
|
||||
|
||||
// Look up the reservation.
|
||||
findReservation(*engine, ctx);
|
||||
// Make sure we found our host.
|
||||
ConstHostPtr current = ctx.currentHost();
|
||||
ASSERT_TRUE(current);
|
||||
ASSERT_EQ("ghost1", current->getHostname());
|
||||
|
||||
// Check that we have been allocated the fixed address.
|
||||
Lease6Ptr lease;
|
||||
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
|
||||
ASSERT_TRUE(lease);
|
||||
EXPECT_EQ("3001::", lease->addr_.toText());
|
||||
|
||||
// We're going to rollback the clock a little so we can verify a renewal.
|
||||
// We verify the "time" change in case the lease returned to us
|
||||
// by expectOneLease ever becomes a copy and not what is in the lease mgr.
|
||||
--lease->cltt_;
|
||||
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
|
||||
lease->addr_);
|
||||
ASSERT_TRUE(from_mgr);
|
||||
EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
|
||||
|
||||
// This is what the client will send in his renew message.
|
||||
AllocEngine::HintContainer hints;
|
||||
hints.push_back(make_pair(IOAddress("3001::"), 64));
|
||||
|
||||
// Set test fixture hostname_ to the expected value. This gets checked via
|
||||
// renewTest.
|
||||
hostname_ = "ghost1";
|
||||
|
||||
// We need a PD pool to fake renew_test
|
||||
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
|
||||
|
||||
// Client should receive a lease.
|
||||
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, false);
|
||||
ASSERT_EQ(1, renewed.size());
|
||||
|
||||
// And the lease lifetime should be extended.
|
||||
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
|
||||
<< "Lease lifetime was not extended, but it should";
|
||||
}
|
||||
|
||||
}; // namespace test
|
||||
}; // namespace dhcp
|
||||
}; // namespace isc
|
||||
|
@ -44,7 +44,7 @@ namespace test {
|
||||
bool testStatistics(const std::string& stat_name, const int64_t exp_value,
|
||||
const SubnetID subnet_id) {
|
||||
try {
|
||||
std::string name = (!subnet_id ? stat_name :
|
||||
std::string name = (subnet_id == SUBNET_ID_UNUSED ? stat_name :
|
||||
StatsMgr::generateName("subnet", subnet_id, stat_name));
|
||||
ObservationPtr observation = StatsMgr::instance().getObservation(name);
|
||||
if (observation) {
|
||||
|
@ -44,7 +44,7 @@ namespace test {
|
||||
/// @return true if the statistic manager holds a particular value,
|
||||
/// false otherwise.
|
||||
bool testStatistics(const std::string& stat_name, const int64_t exp_value,
|
||||
const SubnetID subnet_id = 0);
|
||||
const SubnetID subnet_id = SUBNET_ID_UNUSED);
|
||||
|
||||
/// @brief Allocation engine with some internal methods exposed
|
||||
class NakedAllocEngine : public AllocEngine {
|
||||
@ -178,9 +178,11 @@ public:
|
||||
EXPECT_EQ(lease->subnet_id_, subnet_->getID());
|
||||
|
||||
if (expected_in_subnet) {
|
||||
EXPECT_TRUE(subnet_->inRange(lease->addr_));
|
||||
EXPECT_TRUE(subnet_->inRange(lease->addr_))
|
||||
<< " address: " << lease->addr_.toText();
|
||||
} else {
|
||||
EXPECT_FALSE(subnet_->inRange(lease->addr_));
|
||||
EXPECT_FALSE(subnet_->inRange(lease->addr_))
|
||||
<< " address: " << lease->addr_.toText();
|
||||
}
|
||||
|
||||
if (expected_in_pool) {
|
||||
@ -374,7 +376,7 @@ public:
|
||||
createHost6(bool add_to_host_mgr, IPv6Resrv::Type type,
|
||||
const asiolink::IOAddress& addr, uint8_t prefix_len) {
|
||||
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
|
||||
Host::IDENT_DUID, SubnetID(0), subnet_->getID(),
|
||||
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
|
||||
asiolink::IOAddress("0.0.0.0")));
|
||||
IPv6Resrv resv(type, addr, prefix_len);
|
||||
host->addReservation(resv);
|
||||
|
Loading…
x
Reference in New Issue
Block a user