Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Sebastian Schrader
Kea
Commits
6f994e6f
Commit
6f994e6f
authored
Sep 11, 2017
by
Marcin Siodelski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[5306] Client contexts now hold multiple hosts.
parent
c76416e5
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
249 additions
and
73 deletions
+249
-73
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.cc
+10
-8
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/dhcp6_srv.cc
+8
-8
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.cc
+86
-39
src/lib/dhcpsrv/alloc_engine.h
src/lib/dhcpsrv/alloc_engine.h
+21
-5
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
+122
-11
src/lib/dhcpsrv/tests/alloc_engine_utils.cc
src/lib/dhcpsrv/tests/alloc_engine_utils.cc
+2
-2
No files found.
src/bin/dhcp4/dhcp4_srv.cc
View file @
6f994e6f
...
...
@@ -384,9 +384,9 @@ Dhcpv4Exchange::setHostIdentifiers() {
void
Dhcpv4Exchange
::
setReservedClientClasses
()
{
if
(
context_
->
h
ost
_
&&
query_
)
{
if
(
context_
->
currentH
ost
()
&&
query_
)
{
BOOST_FOREACH
(
const
std
::
string
&
client_class
,
context_
->
h
ost
_
->
getClientClasses4
())
{
context_
->
currentH
ost
()
->
getClientClasses4
())
{
query_
->
addClass
(
client_class
);
}
}
...
...
@@ -394,7 +394,7 @@ Dhcpv4Exchange::setReservedClientClasses() {
void
Dhcpv4Exchange
::
setReservedMessageFields
()
{
ConstHostPtr
host
=
context_
->
h
ost
_
;
ConstHostPtr
host
=
context_
->
currentH
ost
()
;
// Nothing to do if host reservations not specified for this client.
if
(
host
)
{
if
(
!
host
->
getNextServer
().
isV4Zero
())
{
...
...
@@ -1167,7 +1167,7 @@ Dhcpv4Srv::buildCfgOptionList(Dhcpv4Exchange& ex) {
}
// Firstly, host specific options.
const
ConstHostPtr
&
host
=
ex
.
getContext
()
->
h
ost
_
;
const
ConstHostPtr
&
host
=
ex
.
getContext
()
->
currentH
ost
()
;
if
(
host
&&
!
host
->
getCfgOption4
()
->
empty
())
{
co_list
.
push_back
(
host
->
getCfgOption4
());
}
...
...
@@ -1471,9 +1471,10 @@ Dhcpv4Srv::processClientFqdnOption(Dhcpv4Exchange& ex) {
fqdn_resp
->
setFlag
(
Option4ClientFqdn
::
FLAG_E
,
fqdn
->
getFlag
(
Option4ClientFqdn
::
FLAG_E
));
if
(
ex
.
getContext
()
->
host_
&&
!
ex
.
getContext
()
->
host_
->
getHostname
().
empty
())
{
if
(
ex
.
getContext
()
->
currentHost
()
&&
!
ex
.
getContext
()
->
currentHost
()
->
getHostname
().
empty
())
{
D2ClientMgr
&
d2_mgr
=
CfgMgr
::
instance
().
getD2ClientMgr
();
fqdn_resp
->
setDomainName
(
d2_mgr
.
qualifyName
(
ex
.
getContext
()
->
h
ost
_
->
getHostname
(),
fqdn_resp
->
setDomainName
(
d2_mgr
.
qualifyName
(
ex
.
getContext
()
->
currentH
ost
()
->
getHostname
(),
true
),
Option4ClientFqdn
::
FULL
);
}
else
{
...
...
@@ -1519,7 +1520,7 @@ Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) {
// Hostname reservations take precedence over any other configuration,
// i.e. DDNS configuration.
if
(
ctx
->
h
ost
_
&&
!
ctx
->
h
ost
_
->
getHostname
().
empty
())
{
if
(
ctx
->
currentH
ost
()
&&
!
ctx
->
currentH
ost
()
->
getHostname
().
empty
())
{
// In order to send a reserved hostname value we expect that at least
// one of the following is the case: the client has sent us a hostname
// option, or the client has sent Parameter Request List option with
...
...
@@ -1551,7 +1552,8 @@ Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) {
// send back a hostname option, send this option with a reserved
// name for this client.
if
(
should_send_hostname
)
{
const
std
::
string
&
hostname
=
d2_mgr
.
qualifyName
(
ctx
->
host_
->
getHostname
(),
const
std
::
string
&
hostname
=
d2_mgr
.
qualifyName
(
ctx
->
currentHost
()
->
getHostname
(),
false
);
LOG_DEBUG
(
ddns4_logger
,
DBG_DHCP4_DETAIL_DATA
,
DHCP4_RESERVED_HOSTNAME_ASSIGNED
)
...
...
src/bin/dhcp6/dhcp6_srv.cc
View file @
6f994e6f
...
...
@@ -861,8 +861,8 @@ Dhcpv6Srv::buildCfgOptionList(const Pkt6Ptr& question,
AllocEngine
::
ClientContext6
&
ctx
,
CfgOptionList
&
co_list
)
{
// Firstly, host specific options.
if
(
ctx
.
h
ost
_
&&
!
ctx
.
h
ost
_
->
getCfgOption6
()
->
empty
())
{
co_list
.
push_back
(
ctx
.
h
ost
_
->
getCfgOption6
());
if
(
ctx
.
currentH
ost
()
&&
!
ctx
.
currentH
ost
()
->
getCfgOption6
()
->
empty
())
{
co_list
.
push_back
(
ctx
.
currentH
ost
()
->
getCfgOption6
());
}
// Secondly, pool specific options. Pools are defined within a subnet, so
...
...
@@ -1250,8 +1250,8 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
}
else
{
// No FQDN so get the lease hostname from the host reservation if
// there is one.
if
(
ctx
.
h
ost
_
)
{
ctx
.
hostname_
=
ctx
.
h
ost
_
->
getHostname
();
if
(
ctx
.
currentH
ost
()
)
{
ctx
.
hostname_
=
ctx
.
currentH
ost
()
->
getHostname
();
}
return
;
...
...
@@ -1271,10 +1271,10 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
d2_mgr
.
adjustFqdnFlags
<
Option6ClientFqdn
>
(
*
fqdn
,
*
fqdn_resp
);
// If there's a reservation and it has a hostname specified, use it!
if
(
ctx
.
h
ost
_
&&
!
ctx
.
h
ost
_
->
getHostname
().
empty
())
{
if
(
ctx
.
currentH
ost
()
&&
!
ctx
.
currentH
ost
()
->
getHostname
().
empty
())
{
// Add the qualifying suffix.
// After #3765, this will only occur if the suffix is not empty.
fqdn_resp
->
setDomainName
(
d2_mgr
.
qualifyName
(
ctx
.
h
ost
_
->
getHostname
(),
fqdn_resp
->
setDomainName
(
d2_mgr
.
qualifyName
(
ctx
.
currentH
ost
()
->
getHostname
(),
true
),
Option6ClientFqdn
::
FULL
);
}
else
{
...
...
@@ -3082,9 +3082,9 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
void
Dhcpv6Srv
::
setReservedClientClasses
(
const
Pkt6Ptr
&
pkt
,
const
AllocEngine
::
ClientContext6
&
ctx
)
{
if
(
ctx
.
h
ost
_
&&
pkt
)
{
if
(
ctx
.
currentH
ost
()
&&
pkt
)
{
BOOST_FOREACH
(
const
std
::
string
&
client_class
,
ctx
.
h
ost
_
->
getClientClasses6
())
{
ctx
.
currentH
ost
()
->
getClientClasses6
())
{
pkt
->
addClass
(
client_class
);
}
}
...
...
src/lib/dhcpsrv/alloc_engine.cc
View file @
6f994e6f
...
...
@@ -309,21 +309,27 @@ template<typename ContextType>
void
AllocEngine
::
findReservationInternal
(
ContextType
&
ctx
,
const
AllocEngine
::
HostGetFunc
&
host_get
)
{
ctx
.
host_
.
reset
();
ctx
.
hosts_
.
clear
();
auto
subnet
=
ctx
.
subnet_
;
// We can only search for the reservation if a subnet has been selected.
if
(
ctx
.
subnet_
)
{
while
(
subnet
)
{
// Iterate over configured identifiers in the order of preference
// and try to use each of them to search for the reservations.
BOOST_FOREACH
(
const
IdentifierPair
&
id_pair
,
ctx
.
host_identifiers_
)
{
// Attempt to find a host using a specified identifier.
ctx
.
host_
=
host_get
(
ctx
.
subnet_
->
getID
(),
id_pair
.
first
,
&
id_pair
.
second
[
0
],
id_pair
.
second
.
size
());
// If we found matching host, return.
if
(
ctx
.
host_
)
{
return
;
ConstHostPtr
host
=
host_get
(
subnet
->
getID
(),
id_pair
.
first
,
&
id_pair
.
second
[
0
],
id_pair
.
second
.
size
());
// If we found matching host for this subnet.
if
(
host
)
{
ctx
.
hosts_
[
subnet
->
getID
()]
=
host
;
break
;
}
}
subnet
=
subnet
->
getNextSubnet
(
ctx
.
subnet_
);
}
}
...
...
@@ -334,7 +340,7 @@ AllocEngine::findReservationInternal(ContextType& ctx,
AllocEngine
::
ClientContext6
::
ClientContext6
()
:
query_
(),
fake_allocation_
(
false
),
subnet_
(),
duid_
(),
hwaddr_
(),
host_identifiers_
(),
host_
(),
fwd_dns_update_
(
false
),
hwaddr_
(),
host_identifiers_
(),
host
s
_
(),
fwd_dns_update_
(
false
),
rev_dns_update_
(
false
),
hostname_
(),
callout_handle_
(),
ias_
()
{
}
...
...
@@ -348,7 +354,7 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet,
const
Pkt6Ptr
&
query
,
const
CalloutHandlePtr
&
callout_handle
)
:
query_
(
query
),
fake_allocation_
(
fake_allocation
),
subnet_
(
subnet
),
duid_
(
duid
),
hwaddr_
(),
host_identifiers_
(),
host_
(),
duid_
(
duid
),
hwaddr_
(),
host_identifiers_
(),
host
s
_
(),
fwd_dns_update_
(
fwd_dns
),
rev_dns_update_
(
rev_dns
),
hostname_
(
hostname
),
callout_handle_
(
callout_handle
),
allocated_resources_
(),
ias_
()
{
...
...
@@ -386,6 +392,16 @@ isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
(
allocated_resources_
.
count
(
std
::
make_pair
(
prefix
,
prefix_len
))));
}
ConstHostPtr
AllocEngine
::
ClientContext6
::
currentHost
()
const
{
if
(
subnet_
)
{
auto
host
=
hosts_
.
find
(
subnet_
->
getID
());
if
(
host
!=
hosts_
.
cend
())
{
return
(
host
->
second
);
}
}
return
(
ConstHostPtr
());
}
void
AllocEngine
::
findReservation
(
ClientContext6
&
ctx
)
{
findReservationInternal
(
ctx
,
boost
::
bind
(
&
HostMgr
::
get6
,
...
...
@@ -432,7 +448,7 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
// Hence independent checks.
// Case 1: There are no leases and there's a reservation for this host.
if
(
leases
.
empty
()
&&
ctx
.
h
ost
_
)
{
if
(
leases
.
empty
()
&&
ctx
.
currentH
ost
()
)
{
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR
)
...
...
@@ -456,7 +472,7 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
// There is at least one lease for this client and there are no reservations.
// We will return these leases for the client, but we may need to update
// FQDN information.
}
else
if
(
!
leases
.
empty
()
&&
!
ctx
.
h
ost
_
)
{
}
else
if
(
!
leases
.
empty
()
&&
!
ctx
.
currentH
ost
()
)
{
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR
)
...
...
@@ -475,7 +491,7 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
// assign something new.
// Case 3: There are leases and there are reservations.
}
else
if
(
!
leases
.
empty
()
&&
ctx
.
h
ost
_
)
{
}
else
if
(
!
leases
.
empty
()
&&
ctx
.
currentH
ost
()
)
{
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V6_ALLOC_LEASES_HR
)
...
...
@@ -754,7 +770,7 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
Lease6Collection
&
existing_leases
)
{
// If there are no reservations or the reservation is v4, there's nothing to do.
if
(
!
ctx
.
h
ost
_
||
!
ctx
.
h
ost
_
->
hasIPv6Reservation
())
{
if
(
!
ctx
.
currentH
ost
()
||
!
ctx
.
currentH
ost
()
->
hasIPv6Reservation
())
{
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V6_ALLOC_NO_V6_HR
)
.
arg
(
ctx
.
query_
->
getLabel
());
...
...
@@ -770,8 +786,8 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
// we already have a lease for a reserved address or prefix.
BOOST_FOREACH
(
const
Lease6Ptr
&
lease
,
existing_leases
)
{
if
((
lease
->
valid_lft_
!=
0
))
{
if
(
ctx
.
h
ost
_
->
hasReservation
(
IPv6Resrv
(
type
,
lease
->
addr_
,
lease
->
prefixlen_
)))
{
if
(
ctx
.
currentH
ost
()
->
hasReservation
(
IPv6Resrv
(
type
,
lease
->
addr_
,
lease
->
prefixlen_
)))
{
// 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
,
...
...
@@ -794,7 +810,7 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
// over reservations specified and try to allocate one of them for the IA.
// Get the IPv6 reservations of specified type.
const
IPv6ResrvRange
&
reservs
=
ctx
.
h
ost
_
->
getIPv6Reservations
(
type
);
const
IPv6ResrvRange
&
reservs
=
ctx
.
currentH
ost
()
->
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
();
...
...
@@ -860,16 +876,16 @@ 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
.
h
ost
_
)
{
if
(
ctx
.
currentH
ost
()
)
{
if
(
candidate
->
type_
==
Lease6
::
TYPE_NA
)
{
if
(
ctx
.
h
ost
_
->
hasReservation
(
IPv6Resrv
(
IPv6Resrv
::
TYPE_NA
,
candidate
->
addr_
)))
{
if
(
ctx
.
currentH
ost
()
->
hasReservation
(
IPv6Resrv
(
IPv6Resrv
::
TYPE_NA
,
candidate
->
addr_
)))
{
continue
;
}
}
else
{
if
(
ctx
.
h
ost
_
->
hasReservation
(
IPv6Resrv
(
IPv6Resrv
::
TYPE_PD
,
candidate
->
addr_
,
candidate
->
prefixlen_
)))
{
if
(
ctx
.
currentH
ost
()
->
hasReservation
(
IPv6Resrv
(
IPv6Resrv
::
TYPE_PD
,
candidate
->
addr_
,
candidate
->
prefixlen_
)))
{
continue
;
}
}
...
...
@@ -956,7 +972,8 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
Lease6Collection
&
existing_leases
)
{
// This method removes leases that are not reserved for this host.
// It will keep at least one lease, though.
if
(
existing_leases
.
empty
()
||
!
ctx
.
host_
||
!
ctx
.
host_
->
hasIPv6Reservation
())
{
if
(
existing_leases
.
empty
()
||
!
ctx
.
currentHost
()
||
!
ctx
.
currentHost
()
->
hasIPv6Reservation
())
{
return
;
}
...
...
@@ -970,7 +987,7 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
IPv6Resrv
resv
(
ctx
.
currentIA
().
type_
==
Lease
::
TYPE_NA
?
IPv6Resrv
::
TYPE_NA
:
IPv6Resrv
::
TYPE_PD
,
(
*
lease
)
->
addr_
,
(
*
lease
)
->
prefixlen_
);
if
(
!
ctx
.
h
ost
_
->
hasReservation
(
resv
))
{
if
(
!
ctx
.
currentH
ost
()
->
hasReservation
(
resv
))
{
// We have reservations, but not for this lease. Release it.
// Remove this lease from LeaseMgr
...
...
@@ -1234,7 +1251,7 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
removeNonmatchingReservedLeases6
(
ctx
,
leases
);
}
if
(
ctx
.
h
ost
_
)
{
if
(
ctx
.
currentH
ost
()
)
{
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V6_RENEW_HR
)
...
...
@@ -2154,13 +2171,31 @@ addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx
/// dynamic pool. The allocation engine uses this function to check if
/// the reservation is made for the IPv4 address.
///
/// @param ctx Client context holding the data extracted from the
/// @param
[out]
ctx Client context holding the data extracted from the
/// client's message.
///
/// @return true if the context contains the reservation for the IPv4 address.
bool
hasAddressReservation
(
const
AllocEngine
::
ClientContext4
&
ctx
)
{
return
(
ctx
.
host_
&&
!
ctx
.
host_
->
getIPv4Reservation
().
isV4Zero
());
hasAddressReservation
(
AllocEngine
::
ClientContext4
&
ctx
)
{
if
(
ctx
.
hosts_
.
empty
())
{
return
(
false
);
}
Subnet4Ptr
subnet
=
ctx
.
subnet_
;
while
(
subnet
)
{
auto
host
=
ctx
.
hosts_
.
find
(
subnet
->
getID
());
if
((
host
!=
ctx
.
hosts_
.
end
())
&&
!
(
host
->
second
->
getIPv4Reservation
().
isV4Zero
()))
{
ctx
.
subnet_
=
subnet
;
return
(
true
);
}
// No address reservation found here, so let's try another subnet
// within the same shared network.
subnet
=
subnet
->
getNextSubnet
(
ctx
.
subnet_
);
}
return
(
false
);
}
/// @brief Finds existing lease in the database.
...
...
@@ -2285,7 +2320,7 @@ AllocEngine::ClientContext4::ClientContext4()
requested_address_
(
IOAddress
::
IPV4_ZERO_ADDRESS
()),
fwd_dns_update_
(
false
),
rev_dns_update_
(
false
),
hostname_
(
""
),
callout_handle_
(),
fake_allocation_
(
false
),
old_lease_
(),
host_
(),
conflicting_lease_
(),
query_
(),
old_lease_
(),
host
s
_
(),
conflicting_lease_
(),
query_
(),
host_identifiers_
()
{
}
...
...
@@ -2301,7 +2336,7 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
requested_address_
(
requested_addr
),
fwd_dns_update_
(
fwd_dns_update
),
rev_dns_update_
(
rev_dns_update
),
hostname_
(
hostname
),
callout_handle_
(),
fake_allocation_
(
fake_allocation
),
old_lease_
(),
host_
(),
fake_allocation_
(
fake_allocation
),
old_lease_
(),
host
s
_
(),
host_identifiers_
()
{
// Initialize host identifiers.
...
...
@@ -2310,6 +2345,17 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
}
}
ConstHostPtr
AllocEngine
::
ClientContext4
::
currentHost
()
const
{
if
(
subnet_
)
{
auto
host
=
hosts_
.
find
(
subnet_
->
getID
());
if
(
host
!=
hosts_
.
cend
())
{
return
(
host
->
second
);
}
}
return
(
ConstHostPtr
());
}
Lease4Ptr
AllocEngine
::
allocateLease4
(
ClientContext4
&
ctx
)
{
// The NULL pointer indicates that the old lease didn't exist. It may
...
...
@@ -2366,24 +2412,24 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V4_DISCOVER_HR
)
.
arg
(
ctx
.
query_
->
getLabel
())
.
arg
(
ctx
.
h
ost
_
->
getIPv4Reservation
().
toText
());
.
arg
(
ctx
.
currentH
ost
()
->
getIPv4Reservation
().
toText
());
// If the client doesn't have a lease or the leased address is different
// than the reserved one then let's try to allocate the reserved address.
// Otherwise the address that the client has is the one for which it
// has a reservation, so just renew it.
if
(
!
client_lease
||
(
client_lease
->
addr_
!=
ctx
.
h
ost
_
->
getIPv4Reservation
()))
{
if
(
!
client_lease
||
(
client_lease
->
addr_
!=
ctx
.
currentH
ost
()
->
getIPv4Reservation
()))
{
// The call below will return a pointer to the lease for the address
// reserved to this client, if the lease is available, i.e. is not
// currently assigned to any other client.
// Note that we don't remove the existing client's lease at this point
// because this is not a real allocation, we just offer what we can
// allocate in the DHCPREQUEST time.
new_lease
=
allocateOrReuseLease4
(
ctx
.
h
ost
_
->
getIPv4Reservation
(),
ctx
);
new_lease
=
allocateOrReuseLease4
(
ctx
.
currentH
ost
()
->
getIPv4Reservation
(),
ctx
);
if
(
!
new_lease
)
{
LOG_WARN
(
alloc_engine_logger
,
ALLOC_ENGINE_V4_DISCOVER_ADDRESS_CONFLICT
)
.
arg
(
ctx
.
query_
->
getLabel
())
.
arg
(
ctx
.
h
ost
_
->
getIPv4Reservation
().
toText
())
.
arg
(
ctx
.
currentH
ost
()
->
getIPv4Reservation
().
toText
())
.
arg
(
ctx
.
conflicting_lease_
?
ctx
.
conflicting_lease_
->
toText
()
:
"(no lease info)"
);
}
...
...
@@ -2489,7 +2535,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
// allocation engine needs to find an appropriate address.
// If there is a reservation for the client, let's try to
// allocate the reserved address.
ctx
.
requested_address_
=
ctx
.
h
ost
_
->
getIPv4Reservation
();
ctx
.
requested_address_
=
ctx
.
currentH
ost
()
->
getIPv4Reservation
();
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V4_REQUEST_USE_HR
)
...
...
@@ -2521,8 +2567,9 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
// address because the reserved address is in use. We will have to
// check if the address is in use.
if
(
hasAddressReservation
(
ctx
)
&&
(
ctx
.
host_
->
getIPv4Reservation
()
!=
ctx
.
requested_address_
))
{
existing
=
LeaseMgrFactory
::
instance
().
getLease4
(
ctx
.
host_
->
getIPv4Reservation
());
(
ctx
.
currentHost
()
->
getIPv4Reservation
()
!=
ctx
.
requested_address_
))
{
existing
=
LeaseMgrFactory
::
instance
().
getLease4
(
ctx
.
currentHost
()
->
getIPv4Reservation
());
// If the reserved address is not in use, i.e. the lease doesn't
// exist or is expired, and the client is requesting a different
// address, return NULL. The client should go back to the
...
...
@@ -2532,7 +2579,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
ALLOC_ENGINE_V4_REQUEST_INVALID
)
.
arg
(
ctx
.
query_
->
getLabel
())
.
arg
(
ctx
.
h
ost
_
->
getIPv4Reservation
().
toText
())
.
arg
(
ctx
.
currentH
ost
()
->
getIPv4Reservation
().
toText
())
.
arg
(
ctx
.
requested_address_
.
toText
());
return
(
Lease4Ptr
());
...
...
@@ -2543,7 +2590,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
// address is reserved for the client. If the address is not reserved one
// and it doesn't belong to the dynamic pool, do not allocate it.
if
((
!
hasAddressReservation
(
ctx
)
||
(
ctx
.
h
ost
_
->
getIPv4Reservation
()
!=
ctx
.
requested_address_
))
&&
(
ctx
.
currentH
ost
()
->
getIPv4Reservation
()
!=
ctx
.
requested_address_
))
&&
!
inAllowedPool
(
ctx
,
ctx
.
requested_address_
))
{
LOG_DEBUG
(
alloc_engine_logger
,
ALLOC_ENGINE_DBG_TRACE
,
...
...
src/lib/dhcpsrv/alloc_engine.h
View file @
6f994e6f
...
...
@@ -315,10 +315,12 @@ public:
/// received by the server.
IdentifierList
host_identifiers_
;
/// @brief A pointer to the object identifying host reservations.
/// @brief Holds a map of hosts belonging to the client within different
/// subnets.
///
/// May be NULL if there are no reservations.
ConstHostPtr
host_
;
/// Multiple hosts may appear when the client belongs to a shared
/// network.
std
::
map
<
SubnetID
,
ConstHostPtr
>
hosts_
;
/// @brief A boolean value which indicates that server takes
/// responsibility for the forward DNS Update for this lease
...
...
@@ -441,6 +443,11 @@ public:
ias_
.
push_back
(
IAContext
());
};
/// @brief Returns host for currently selected subnet.
///
/// @return Pointer to the host object.
ConstHostPtr
currentHost
()
const
;
/// @brief Default constructor.
ClientContext6
();
...
...
@@ -1072,8 +1079,12 @@ public:
/// @brief A pointer to an old lease that the client had before update.
Lease4Ptr
old_lease_
;
/// @brief A pointer to the object identifying host reservations.
ConstHostPtr
host_
;
/// @brief Holds a map of hosts belonging to the client within different
/// subnets.
///
/// Multiple hosts may appear when the client belongs to a shared
/// network.
std
::
map
<
SubnetID
,
ConstHostPtr
>
hosts_
;
/// @brief A pointer to the object representing a lease in conflict.
///
...
...
@@ -1102,6 +1113,11 @@ public:
host_identifiers_
.
push_back
(
IdentifierPair
(
id_type
,
identifier
));
}
/// @brief Returns host for currently selected subnet.
///
/// @return Pointer to the host object.
ConstHostPtr
currentHost
()
const
;
/// @brief Default constructor.
ClientContext4
();
...
...
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
View file @
6f994e6f
...
...
@@ -601,6 +601,60 @@ TEST_F(AllocEngine4Test, discoverSharedNetworkClassification) {
EXPECT_TRUE
(
subnet1
->
inPool
(
Lease
::
TYPE_V4
,
lease
->
addr_
));
}
// Test that reservations within shared network take precedence over the
// existing leases regardless in which subnet belonging to a shared network
// reservations belong.
TEST_F
(
AllocEngine4Test
,
discoverSharedNetworkReservations
)
{
AllocEngine
engine
(
AllocEngine
::
ALLOC_ITERATIVE
,
100
,
false
);
// Create two subnets, each with a single address pool. The first subnet
// has only one address in its address pool to make it easier to simulate
// address exhaustion.
Subnet4Ptr
subnet1
(
new
Subnet4
(
IOAddress
(
"192.0.2.0"
),
24
,
1
,
2
,
3
,
SubnetID
(
1
)));
Subnet4Ptr
subnet2
(
new
Subnet4
(
IOAddress
(
"10.1.2.0"
),
24
,
1
,
2
,
3
,
SubnetID
(
2
)));
Pool4Ptr
pool1
(
new
Pool4
(
IOAddress
(
"192.0.2.17"
),
IOAddress
(
"192.0.2.17"
)));
Pool4Ptr
pool2
(
new
Pool4
(
IOAddress
(
"10.1.2.5"
),
IOAddress
(
"10.1.2.100"
)));
subnet1
->
addPool
(
pool1
);
subnet2
->
addPool
(
pool2
);
// Both subnets belong to the same network so they can be used
// interchangeably.
SharedNetwork4Ptr
network
(
new
SharedNetwork4
(
"test_network"
));
network
->
add
(
subnet1
);
network
->
add
(
subnet2
);
// Create reservation for the client.
HostPtr
host
(
new
Host
(
&
hwaddr_
->
hwaddr_
[
0
],
hwaddr_
->
hwaddr_
.
size
(),
Host
::
IDENT_HWADDR
,
subnet2
->
getID
(),
SubnetID
(
0
),
IOAddress
(
"10.2.3.23"
)));
CfgMgr
::
instance
().
getStagingCfg
()
->
getCfgHosts
()
->
add
(
host
);
CfgMgr
::
instance
().
commit
();
// Start allocation from subnet1. The engine should determine that the
// client has reservations in subnet2 and should rather assign reserved
// addresses.
AllocEngine
::
ClientContext4
ctx
(
subnet1
,
ClientIdPtr
(),
hwaddr_
,
IOAddress
::
IPV4_ZERO_ADDRESS
(),
false
,
false
,
"host.example.com."
,
true
);
AllocEngine
::
findReservation
(
ctx
);
ctx
.
query_
.
reset
(
new
Pkt4
(
DHCPDISCOVER
,
1234
));
Lease4Ptr
lease
=
engine
.
allocateLease4
(
ctx
);
ASSERT_TRUE
(
lease
);
EXPECT_EQ
(
"10.2.3.23"
,
lease
->
addr_
.
toText
());
// Let's create a lease for the client to make sure the lease is not
// renewed but a reserved lease is offerred.
Lease4Ptr
lease2
(
new
Lease4
(
IOAddress
(
"192.0.2.17"
),
hwaddr_
,
ClientIdPtr
(),
501
,
502
,
503
,
time
(
NULL
),
subnet1
->
getID
()));
lease
->
cltt_
=
time
(
NULL
)
-
10
;
// Allocated 10 seconds ago
ASSERT_TRUE
(
LeaseMgrFactory
::
instance
().
addLease
(
lease2
));
ctx
.
subnet_
=
subnet1
;
AllocEngine
::
findReservation
(
ctx
);
lease
=
engine
.
allocateLease4
(
ctx
);
ASSERT_TRUE
(
lease
);
EXPECT_EQ
(
"10.2.3.23"
,
lease
->
addr_
.
toText
());
}
// This test verifies that the server can allocate an address from a
// different subnet than orginally selected, when the address pool in
// the first subnet is exhausted.
...
...
@@ -745,6 +799,63 @@ TEST_F(AllocEngine4Test, requestSharedNetworkClassification) {
EXPECT_TRUE
(
subnet2
->
inPool
(
Lease
::
TYPE_V4
,
lease
->
addr_
));
}
// Test that reservations within shared network take precedence over the
// existing leases regardless in which subnet belonging to a shared network
// reservations belong (DHCPREQUEST case).
TEST_F
(
AllocEngine4Test
,
requestSharedNetworkReservations
)
{
AllocEngine
engine
(
AllocEngine
::
ALLOC_ITERATIVE
,
100
,
false
);
// Create two subnets, each with a single address pool. The first subnet
// has only one address in its address pool to make it easier to simulate
// address exhaustion.
Subnet4Ptr
subnet1
(
new
Subnet4
(
IOAddress
(
"192.0.2.0"
),
24
,
1
,
2
,
3
,
SubnetID
(
1
)));
Subnet4Ptr
subnet2
(
new
Subnet4
(
IOAddress
(
"10.1.2.0"
),
24
,
1
,
2
,
3
,
SubnetID
(
2
)));
Pool4Ptr
pool1
(
new
Pool4
(
IOAddress
(
"192.0.2.17"
),
IOAddress
(
"192.0.2.17"
)));
Pool4Ptr
pool2
(
new
Pool4
(
IOAddress
(
"10.1.2.5"
),
IOAddress
(
"10.1.2.100"
)));
subnet1
->
addPool
(
pool1
);
subnet2
->
addPool
(
pool2
);
// Both subnets belong to the same network so they can be used
// interchangeably.
SharedNetwork4Ptr
network
(
new
SharedNetwork4
(
"test_network"
));
network
->
add
(
subnet1
);
network
->
add
(
subnet2
);
// Create reservation for the client.
HostPtr
host
(
new
Host
(
&
hwaddr_
->
hwaddr_
[
0
],
hwaddr_
->
hwaddr_
.
size
(),
Host
::
IDENT_HWADDR
,
subnet2
->
getID
(),
SubnetID
(
0
),
IOAddress
(
"10.2.3.23"
)));
CfgMgr
::
instance
().
getStagingCfg
()
->
getCfgHosts
()
->
add
(
host
);
CfgMgr
::
instance
().
commit
();
// Start allocation from subnet1. The engine should determine that the
// client has reservations in subnet2 and should rather assign reserved
// addresses.
AllocEngine
::
ClientContext4
ctx
(
subnet1
,
ClientIdPtr
(),
hwaddr_
,
IOAddress
::
IPV4_ZERO_ADDRESS
(),
false
,
false
,
"host.example.com."
,
false
);
AllocEngine
::
findReservation
(
ctx
);
ctx
.
query_
.
reset
(
new
Pkt4
(
DHCPREQUEST
,
1234
));
Lease4Ptr
lease
=
engine
.
allocateLease4
(
ctx
);
ASSERT_TRUE
(
lease
);
EXPECT_EQ
(
"10.2.3.23"
,
lease
->
addr_
.
toText
());
// Remove the lease for another test below.
ASSERT_TRUE
(
LeaseMgrFactory
::
instance
().
deleteLease
(
lease
->
addr_
));
// Let's create a lease for the client to make sure the lease is not
// renewed but a reserved lease is allocated again.
Lease4Ptr
lease2
(
new
Lease4
(
IOAddress
(
"192.0.2.17"
),
hwaddr_
,
ClientIdPtr
(),
501
,
502
,
503
,
time
(
NULL
),
subnet1
->
getID
()));
lease
->
cltt_
=
time
(
NULL
)
-
10
;
// Allocated 10 seconds ago
ASSERT_TRUE
(
LeaseMgrFactory
::
instance
().
addLease
(
lease2
));
ctx
.
subnet_
=
subnet1
;
AllocEngine
::
findReservation
(
ctx
);
lease
=
engine
.
allocateLease4
(
ctx
);
ASSERT_TRUE
(
lease
);
EXPECT_EQ
(
"10.2.3.23"
,
lease
->
addr_
.
toText
());
}
// This test checks if an expired lease can be reused in DHCPDISCOVER (fake
// allocation)
TEST_F
(
AllocEngine4Test
,
discoverReuseExpiredLease4
)
{
...
...
@@ -1964,7 +2075,7 @@ TEST_F(AllocEngine4Test, findReservation) {
// There is no reservation in the database so no host should be returned.
ASSERT_NO_THROW
(
engine
.
findReservation
(
ctx
));
EXPECT_FALSE
(
ctx
.
h
ost
_
);
EXPECT_FALSE
(
ctx
.
currentH
ost
()
);
// Create a reservation for the client.
HostPtr
host
(
new
Host
(
&
hwaddr_
->
hwaddr_
[
0
],
hwaddr_
->
hwaddr_
.
size
(),
...
...
@@ -1975,21 +2086,21 @@ TEST_F(AllocEngine4Test, findReservation) {
// This time the reservation should be returned.
ASSERT_NO_THROW
(
engine
.
findReservation
(
ctx
));
EXPECT_TRUE
(
ctx
.
h
ost
_
);