Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
7b192fe3
Commit
7b192fe3
authored
Dec 22, 2014
by
Marcin Siodelski
Browse files
[master] Merge branch 'trac3564'
parents
35070d0d
b1cd4b11
Changes
20
Expand all
Hide whitespace changes
Inline
Side-by-side
doc/Makefile.am
View file @
7b192fe3
...
...
@@ -5,6 +5,7 @@ EXTRA_DIST = version.ent.in differences.txt Doxyfile Doxyfile-xml
nobase_dist_doc_DATA
=
examples/kea4/single-subnet.json
nobase_dist_doc_DATA
+=
examples/kea4/several-subnets.json
nobase_dist_doc_DATA
+=
examples/kea4/multiple-options.json
nobase_dist_doc_DATA
+=
examples/kea4/reservations.json
nobase_dist_doc_DATA
+=
examples/kea6/simple.json
nobase_dist_doc_DATA
+=
examples/kea6/several-subnets.json
nobase_dist_doc_DATA
+=
examples/kea6/multiple-options.json
...
...
doc/examples/kea4/reservations.json
0 → 100644
View file @
7b192fe3
#
This
is
an
example
configuration
file
for
the
DHCPv
4
server
in
Kea.
#
It
contains
one
subnet
in
which
there
are
two
static
address
reservations
#
for
the
clients
identified
by
the
MAC
addresses.
{
"Dhcp4"
:
{
#
Kea
is
told
to
listen
on
eth
0
interface
only.
"interfaces"
:
[
"eth0"
],
#
We
need
to
specify
lease
type.
As
of
May
2014
,
three
backends
are
supported:
#
memfile
,
mysql
and
pgsql.
We'll
just
use
memfile
,
because
it
doesn't
require
#
any
prior
set
up.
"lease-database"
:
{
"type"
:
"memfile"
},
#
Addresses
will
be
assigned
with
valid
lifetimes
being
4000
.
Client
#
is
told
to
start
renewing
after
1000
seconds.
If
the
server
does
not
respond
#
after
2000
seconds
since
the
lease
was
granted
,
client
is
supposed
#
to
start
REBIND
procedure
(emergency
renewal
that
allows
switching
#
to
a
different
server).
"valid-lifetime"
:
4000
,
#
Renew
and
rebind
timers
are
commented
out.
This
implies
that
options
#
58
and
59
will
not
be
sent
to
the
client.
In
this
case
it
is
up
to
#
the
client
to
pick
the
timer
values
according
to
RFC
2131
.
Uncomment
the
#
timers
to
send
these
options
to
the
client.
#
"renew-timer"
:
1000
,
#
"rebind-timer"
:
2000
,
#
Define
a
subnet
with
two
reservations
for
the
192.0
.
2.202
and
#
192.0
.
2.100
address.
Note
that
the
latter
is
a
reservation
for
the
#
address
which
is
within
the
range
of
the
pool
of
the
dynamically
#
allocated
address.
The
server
will
exclude
this
address
from
this
#
pool
and
only
assign
it
to
the
client
which
has
a
reservation
for
#
it.
"subnet4"
:
[
{
"pools"
:
[
{
"pool"
:
"192.0.2.1 - 192.0.2.200"
}
],
"subnet"
:
"192.0.2.0/24"
,
"interface"
:
"eth0"
,
"reservations"
:
[
{
"hw-address"
:
"1a:1b:1c:1d:1e:1f"
,
"ip-address"
:
"192.0.2.202"
},
{
"hw-address"
:
"0a:0b:0c:0d:0e:0f"
,
"ip-address"
:
"192.0.2.100"
}
]
}
]
},
#
The
following
configures
logging.
It
assumes
that
messages
with
at
least
#
informational
level
(info
,
warn
,
error)
will
will
be
logged
to
stdout.
"Logging"
:
{
"loggers"
:
[
{
"name"
:
"kea-dhcp4"
,
"output_options"
:
[
{
"output"
:
"stdout"
}
],
"severity"
:
"INFO"
}
]
}
}
src/bin/dhcp4/dhcp4_srv.cc
View file @
7b192fe3
...
...
@@ -957,14 +957,17 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
OptionCustomPtr
opt_serverid
=
boost
::
dynamic_pointer_cast
<
OptionCustom
>
(
question
->
getOption
(
DHO_DHCP_SERVER_IDENTIFIER
));
// Try to get the Requested IP Address option and use the address as a hint
// for the allocation engine. If the server doesn't already have a lease
// for this client it will try to allocate the one requested.
// Check if the client has sent a requested IP address option or
// ciaddr.
OptionCustomPtr
opt_requested_address
=
boost
::
dynamic_pointer_cast
<
OptionCustom
>
(
question
->
getOption
(
DHO_DHCP_REQUESTED_ADDRESS
));
IOAddress
hint
(
"0.0.0.0"
);
if
(
opt_requested_address
)
{
hint
=
opt_requested_address
->
readAddress
();
}
else
if
(
question
->
getCiaddr
()
!=
IOAddress
(
"0.0.0.0"
))
{
hint
=
question
->
getCiaddr
();
}
HWAddrPtr
hwaddr
=
question
->
getHWAddr
();
...
...
@@ -1309,6 +1312,11 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
// include in the response. If client did not request
// them we append them for him.
appendBasicOptions
(
discover
,
offer
);
}
else
{
// If the server can't offer an address, it drops the packet.
return
(
Pkt4Ptr
());
}
// Set the src/dest IP address, port and interface for the outgoing
...
...
src/bin/dhcp4/tests/dhcp4_client.cc
View file @
7b192fe3
...
...
@@ -57,7 +57,7 @@ Dhcp4Client::Dhcp4Client(const Dhcp4Client::State& state) :
use_relay_
(
false
)
{
}
Dhcp4Client
::
Dhcp4Client
(
boost
::
shared_ptr
<
NakedDhcpv4Srv
>
&
srv
,
Dhcp4Client
::
Dhcp4Client
(
boost
::
shared_ptr
<
NakedDhcpv4Srv
>
srv
,
const
Dhcp4Client
::
State
&
state
)
:
config_
(),
ciaddr_
(
IOAddress
(
"0.0.0.0"
)),
...
...
@@ -349,6 +349,11 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
srv_
->
run
();
}
void
Dhcp4Client
::
setHWAddress
(
const
std
::
string
&
hwaddr_str
)
{
hwaddr_
.
reset
(
new
HWAddr
(
HWAddr
::
fromText
(
hwaddr_str
)));
}
}
// end of namespace isc::dhcp::test
}
// end of namespace isc::dhcp
}
// end of namespace isc
src/bin/dhcp4/tests/dhcp4_client.h
View file @
7b192fe3
...
...
@@ -103,7 +103,7 @@ public:
///
/// @param srv An instance of the DHCPv4 server to be used.
/// @param state Initial client's state.
Dhcp4Client
(
boost
::
shared_ptr
<
NakedDhcpv4Srv
>
&
srv
,
Dhcp4Client
(
boost
::
shared_ptr
<
NakedDhcpv4Srv
>
srv
,
const
State
&
state
=
SELECTING
);
/// @brief Creates a lease for the client using the specified address
...
...
@@ -203,7 +203,9 @@ public:
HWAddrPtr
generateHWAddr
(
const
uint8_t
htype
=
HTYPE_ETHER
)
const
;
/// @brief Returns HW address used by the client.
HWAddrPtr
getHWAddress
()
const
;
HWAddrPtr
getHWAddress
()
const
{
return
(
hwaddr_
);
}
/// @brief Returns current context.
const
Context
&
getContext
()
const
{
...
...
@@ -266,6 +268,11 @@ public:
dest_addr_
=
dest_addr
;
}
/// @brief Sets the explicit hardware address for the client.
///
/// @param hwaddr_str String representation of the HW address.
void
setHWAddress
(
const
std
::
string
&
hwaddr_str
);
/// @brief Sets client state.
///
/// Depending on the current state the client's behavior is different
...
...
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
View file @
7b192fe3
...
...
@@ -1136,6 +1136,25 @@ TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
NakedDhcpv4Srv
srv
(
0
);
// Use of the captured DHCPDISCOVER packet requires that
// subnet 10.254.226.0/24 is in use, because this packet
// contains the giaddr which belongs to this subnet and
// this giaddr is used to select the subnet
std
::
string
config
=
"{
\"
interfaces
\"
: [
\"
*
\"
],"
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
subnet4
\"
: [ { "
"
\"
pools
\"
: [ {
\"
pool
\"
:
\"
10.254.226.0/25
\"
} ],"
"
\"
subnet
\"
:
\"
10.254.226.0/24
\"
, "
"
\"
rebind-timer
\"
: 2000, "
"
\"
renew-timer
\"
: 1000, "
"
\"
valid-lifetime
\"
: 4000,"
"
\"
interface
\"
:
\"
eth0
\"
"
" } ],"
"
\"
valid-lifetime
\"
: 4000 }"
;
configure
(
config
);
// Let's create a relayed DISCOVER. This particular relayed DISCOVER has
// added option 82 (relay agent info) with 3 suboptions. The server
// is supposed to echo it back in its response.
...
...
@@ -1610,7 +1629,7 @@ public:
192
,
0
,
2
,
1
,
// ciaddr
1
,
2
,
3
,
4
,
// yiaddr
192
,
0
,
2
,
255
,
// siaddr
255
,
255
,
255
,
255
,
// giaddr
192
,
0
,
2
,
50
,
// giaddr
};
// Initialize the vector with the header fields defined above.
...
...
@@ -2041,7 +2060,6 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
IfaceMgrTestConfig
test_config
(
true
);
IfaceMgr
::
instance
().
openSockets4
();
// Install callback that modifies MAC addr of incoming packet
EXPECT_NO_THROW
(
HooksManager
::
preCalloutsLibraryHandle
().
registerCallout
(
"buffer4_receive"
,
buffer4_receive_change_hwaddr
));
...
...
src/bin/dhcp4/tests/dhcp4_test_utils.cc
View file @
7b192fe3
...
...
@@ -283,10 +283,11 @@ void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp,
}
}
void
Dhcpv4SrvTest
::
checkResponse
(
const
Pkt4Ptr
&
rsp
,
u
int
8_t
expected_message_type
,
void
Dhcpv4SrvTest
::
checkResponse
(
const
Pkt4Ptr
&
rsp
,
int
expected_message_type
,
uint32_t
expected_transid
)
{
ASSERT_TRUE
(
rsp
);
EXPECT_EQ
(
expected_message_type
,
rsp
->
getType
());
EXPECT_EQ
(
expected_message_type
,
static_cast
<
int
>
(
rsp
->
getType
()));
EXPECT_EQ
(
expected_transid
,
rsp
->
getTransid
());
}
...
...
@@ -544,31 +545,33 @@ Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
if
(
msg_type
==
DHCPDISCOVER
)
{
ASSERT_NO_THROW
(
rsp
=
srv
->
processDiscover
(
received
));
// Should return non-NULL packet.
ASSERT_TRUE
(
rsp
);
// Should return NULL packet.
ASSERT_FALSE
(
rsp
);
}
else
{
ASSERT_NO_THROW
(
rsp
=
srv
->
processRequest
(
received
));
// Should return non-NULL packet.
ASSERT_TRUE
(
rsp
);
// We should get the NAK packet with yiaddr set to 0.
EXPECT_EQ
(
DHCPNAK
,
rsp
->
getType
());
ASSERT_EQ
(
"0.0.0.0"
,
rsp
->
getYiaddr
().
toText
());
// Make sure that none of the requested options is returned in NAK.
// Also options such as Routers or Subnet Mask should not be there,
// because lease hasn't been acquired.
EXPECT_TRUE
(
noRequestedOptions
(
rsp
));
EXPECT_TRUE
(
noBasicOptions
(
rsp
));
}
// We should get the NAK packet with yiaddr set to 0.
EXPECT_EQ
(
DHCPNAK
,
rsp
->
getType
());
ASSERT_EQ
(
"0.0.0.0"
,
rsp
->
getYiaddr
().
toText
());
// Make sure that none of the requested options is returned in NAK.
// Also options such as Routers or Subnet Mask should not be there,
// because lease hasn't been acquired.
EXPECT_TRUE
(
noRequestedOptions
(
rsp
));
EXPECT_TRUE
(
noBasicOptions
(
rsp
));
}
void
Dhcpv4SrvTest
::
configure
(
const
std
::
string
&
config
)
{
configure
(
config
,
srv_
);
Dhcpv4SrvTest
::
configure
(
const
std
::
string
&
config
,
const
bool
commit
)
{
configure
(
config
,
srv_
,
commit
);
}
void
Dhcpv4SrvTest
::
configure
(
const
std
::
string
&
config
,
NakedDhcpv4Srv
&
srv
)
{
Dhcpv4SrvTest
::
configure
(
const
std
::
string
&
config
,
NakedDhcpv4Srv
&
srv
,
const
bool
commit
)
{
ElementPtr
json
=
Element
::
fromJSON
(
config
);
ConstElementPtr
status
;
...
...
@@ -579,7 +582,9 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv) {
ConstElementPtr
comment
=
config
::
parseAnswer
(
rcode
,
status
);
ASSERT_EQ
(
0
,
rcode
);
CfgMgr
::
instance
().
commit
();
if
(
commit
)
{
CfgMgr
::
instance
().
commit
();
}
}
...
...
src/bin/dhcp4/tests/dhcp4_test_utils.h
View file @
7b192fe3
...
...
@@ -307,7 +307,7 @@ public:
/// @param rsp response packet to be validated
/// @param expected_message_type expected message type
/// @param expected_transid expected transaction-id
void
checkResponse
(
const
Pkt4Ptr
&
rsp
,
u
int
8_t
expected_message_type
,
void
checkResponse
(
const
Pkt4Ptr
&
rsp
,
int
expected_message_type
,
uint32_t
expected_transid
);
/// @brief Checks if the lease sent to client is present in the database
...
...
@@ -405,13 +405,18 @@ public:
/// @brief Runs DHCPv4 configuration from the JSON string.
///
/// @param config String holding server configuration in JSON format.
void
configure
(
const
std
::
string
&
config
);
/// @param commit A boolean flag indicating if the new configuration
/// should be committed (if true), or not (if false).
void
configure
(
const
std
::
string
&
config
,
const
bool
commit
=
true
);
/// @brief Configure specified DHCP server using JSON string.
///
/// @param config String holding server configuration in JSON format.
/// @param srv Instance of the server to be configured.
void
configure
(
const
std
::
string
&
config
,
NakedDhcpv4Srv
&
srv
);
/// @param commit A boolean flag indicating if the new configuration
/// should be committed (if true), or not (if false).
void
configure
(
const
std
::
string
&
config
,
NakedDhcpv4Srv
&
srv
,
const
bool
commit
=
true
);
/// @brief This function cleans up after the test.
virtual
void
TearDown
();
...
...
src/bin/dhcp4/tests/dora_unittest.cc
View file @
7b192fe3
...
...
@@ -17,6 +17,10 @@
#include
<cc/data.h>
#include
<dhcp/dhcp4.h>
#include
<dhcp/tests/iface_mgr_test_config.h>
#include
<dhcpsrv/cfgmgr.h>
#include
<dhcpsrv/host.h>
#include
<dhcpsrv/host_mgr.h>
#include
<dhcpsrv/subnet_id.h>
#include
<dhcp4/tests/dhcp4_test_utils.h>
#include
<dhcp4/tests/dhcp4_client.h>
#include
<boost/shared_ptr.hpp>
...
...
@@ -47,12 +51,19 @@ namespace {
/// - Domain Name Server option present: 192.0.2.202, 192.0.2.203.
/// - Log Servers option present: 192.0.2.200 and 192.0.2.201
/// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
///
/// - Configuration 2:
/// - Use for testing simple scenarios with host reservations
/// - 1 subnet: 10.0.0.0/24
/// - One reservation for the client using MAC address:
/// aa:bb:cc:dd:ee:ff, reserved address 10.0.0.7
const
char
*
DORA_CONFIGS
[]
=
{
// Configuration 0
"{
\"
interfaces
\"
: [
\"
*
\"
],"
"
\"
valid-lifetime
\"
: 600,"
"
\"
subnet4
\"
: [ { "
"
\"
subnet
\"
:
\"
10.0.0.0/24
\"
, "
"
\"
id
\"
: 1,"
"
\"
pools
\"
: [ {
\"
pool
\"
:
\"
10.0.0.10-10.0.0.100
\"
} ],"
"
\"
option-data
\"
: [ {"
"
\"
name
\"
:
\"
routers
\"
,"
...
...
@@ -119,6 +130,21 @@ const char* DORA_CONFIGS[] = {
"
\"
space
\"
:
\"
dhcp4
\"
"
" } ]"
" } ]"
"}"
,
// Configuration 2
"{
\"
interfaces
\"
: [
\"
*
\"
],"
"
\"
valid-lifetime
\"
: 600,"
"
\"
subnet4
\"
: [ { "
"
\"
subnet
\"
:
\"
10.0.0.0/24
\"
, "
"
\"
pools
\"
: [ {
\"
pool
\"
:
\"
10.0.0.10-10.0.0.100
\"
} ],"
"
\"
reservations
\"
: [ "
" {"
"
\"
hw-address
\"
:
\"
aa:bb:cc:dd:ee:ff
\"
,"
"
\"
ip-address
\"
:
\"
10.0.0.7
\"
"
" }"
" ]"
"} ]"
"}"
};
...
...
@@ -402,4 +428,246 @@ TEST_F(DORATest, ciaddr) {
EXPECT_EQ
(
"0.0.0.0"
,
resp
->
getCiaddr
().
toText
());
}
// This is a simple test for the host reservation. It creates a reservation
// for an address for a single client, identified by the HW address. The
// test verifies that the client using this HW address will obtain a
// lease for the reserved address. It also checks that the client using
// a different HW address will obtain an address from the dynamic pool.
TEST_F
(
DORATest
,
reservation
)
{
// Client A is a one which will have a reservation.
Dhcp4Client
clientA
(
Dhcp4Client
::
SELECTING
);
// Set explicit HW address so as it matches the reservation in the
// configuration used below.
clientA
.
setHWAddress
(
"aa:bb:cc:dd:ee:ff"
);
// Configure DHCP server.
configure
(
DORA_CONFIGS
[
2
],
*
clientA
.
getServer
());
// Client A performs 4-way exchange and should obtain a reserved
// address.
ASSERT_NO_THROW
(
clientA
.
doDORA
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
// Make sure that the server responded.
ASSERT_TRUE
(
clientA
.
getContext
().
response_
);
Pkt4Ptr
resp
=
clientA
.
getContext
().
response_
;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ
(
DHCPACK
,
static_cast
<
int
>
(
resp
->
getType
()));
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ
(
"10.0.0.7"
,
clientA
.
config_
.
lease_
.
addr_
.
toText
());
// Client B uses the same server as Client A.
Dhcp4Client
clientB
(
clientA
.
getServer
(),
Dhcp4Client
::
SELECTING
);
// Client B has no reservation so it should get the lease from
// the dynamic pool.
ASSERT_NO_THROW
(
clientB
.
doDORA
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
// Make sure that the server responded.
ASSERT_TRUE
(
clientB
.
getContext
().
response_
);
resp
=
clientB
.
getContext
().
response_
;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ
(
DHCPACK
,
static_cast
<
int
>
(
resp
->
getType
()));
// Obtain the subnet to which the returned address belongs.
Subnet4Ptr
subnet
=
CfgMgr
::
instance
().
getCurrentCfg
()
->
getCfgSubnets4
()
->
selectSubnet
(
clientB
.
config_
.
lease_
.
addr_
);
ASSERT_TRUE
(
subnet
);
// Make sure that the address has been allocated from the dynamic pool.
ASSERT_TRUE
(
subnet
->
inPool
(
Lease
::
TYPE_V4
,
clientB
.
config_
.
lease_
.
addr_
));
}
// This test checks the following scenario:
// 1. Client A performs 4-way exchange and obrains a lease from the dynamic pool.
// 2. Reservation is created for the client A using an address out of the dynamic
// pool.
// 3. Client A renews the lease.
// 4. Server responds with DHCPNAK to indicate that the client should stop using
// an address for which it has a lease. Server doesn't want to renew an
// address for which the client doesn't have a reservation, while it has
// a reservation for a different address.
// 5. Client A receives a DHCPNAK and returns to the DHCP server discovery.
// 6. Client A performs a 4-way exchange with a server and the server allocates
// a reserved address to the Client A.
// 7. Client A renews the allocated address and the server returns a DHCPACK.
// 8. Reservation for the Client A is removed.
// 9. Client A renews the (previously reserved) lease and the server returns
// DHCPNAK because the address in use is neither reserved nor belongs to
// the dynamic pool.
// 10. Client A returns to the DHCP server discovery.
// 11. Client A uses 4-way exchange to obtain a lease from the dynamic pool.
// 12. The new address that the Client A is using is reserved for Client B.
// Client A still holds this address.
// 13. Client B uses 4-way exchange to obtain a new lease.
// 14. The server determines that the Client B has a reservation for the
// address which is in use by Client A. The server drops the client's
// DHCPDISCOVER message.
// 15. Client A renews the lease.
// 16. The server determines that the address that Client A is using is reserved
// for Client B. The server returns DHCPNAK to the Client A.
// 17. Client B uses 4-way echange to obtain the reserved lease but the lease
// for the Client A hasn't been removed yet. Client B's DHCPDISCOVER
// message is dropped again.
// 18. Client A uses 4-way exchange to allocate a new lease.
// 19. The server allocates a new lease from the dynamic pool but it avoids
// allocating the address reserved for the Client B.
// 20. Client B uses 4-way exchange to obtain a new lease.
// 21. The server finally allocates a reserved address to the Client B.
TEST_F
(
DORATest
,
reservationsWithConflicts
)
{
Dhcp4Client
client
(
Dhcp4Client
::
SELECTING
);
// Configure DHCP server.
configure
(
DORA_CONFIGS
[
0
],
*
client
.
getServer
());
// Client A performs 4-way exchange and obrains a lease from the
// dynamic pool.
ASSERT_NO_THROW
(
client
.
doDORA
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"10.0.0.50"
))));
// Make sure that the server responded.
ASSERT_TRUE
(
client
.
getContext
().
response_
);
Pkt4Ptr
resp
=
client
.
getContext
().
response_
;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ
(
DHCPACK
,
static_cast
<
int
>
(
resp
->
getType
()));
// Make sure that the client has got the lease with the requested address.
ASSERT_EQ
(
"10.0.0.50"
,
client
.
config_
.
lease_
.
addr_
.
toText
());
configure
(
DORA_CONFIGS
[
0
],
false
);
// Reservation is created for the client A using an address out of the
// dynamic pool.
HostPtr
host
(
new
Host
(
&
client
.
getHWAddress
()
->
hwaddr_
[
0
],
client
.
getHWAddress
()
->
hwaddr_
.
size
(),
Host
::
IDENT_HWADDR
,
SubnetID
(
1
),
SubnetID
(
0
),
IOAddress
(
"10.0.0.9"
)));
CfgMgr
::
instance
().
getStagingCfg
()
->
getCfgHosts
()
->
add
(
host
);
CfgMgr
::
instance
().
commit
();
// Let's transition the client to Renewing state.
client
.
setState
(
Dhcp4Client
::
RENEWING
);
// Set the unicast destination address to indicate that it is a renewal.
client
.
setDestAddress
(
IOAddress
(
"10.0.0.1"
));
ASSERT_NO_THROW
(
client
.
doRequest
());
// Client should get the DHCPNAK from the server because the client has
// a reservation for a different address that it is trying to renew.
resp
=
client
.
getContext
().
response_
;
ASSERT_EQ
(
DHCPNAK
,
static_cast
<
int
>
(
resp
->
getType
()));
// A conforming client would go back to the server discovery.
client
.
setState
(
Dhcp4Client
::
SELECTING
);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW
(
client
.
doDORA
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
// Make sure that the server responded.
ASSERT_TRUE
(
client
.
getContext
().
response_
);
resp
=
client
.
getContext
().
response_
;
// Make sure that the server has responded with DHCPACK with a reserved
// address
ASSERT_EQ
(
DHCPACK
,
static_cast
<
int
>
(
resp
->
getType
()));
ASSERT_EQ
(
"10.0.0.9"
,
client
.
config_
.
lease_
.
addr_
.
toText
());
// Client A renews the allocated address.
client
.
setState
(
Dhcp4Client
::
RENEWING
);
// Set the unicast destination address to indicate that it is a renewal.
client
.
setDestAddress
(
IOAddress
(
"10.0.0.1"
));
ASSERT_NO_THROW
(
client
.
doRequest
());
// Make sure the server responded and renewed the client's address.
resp
=
client
.
getContext
().
response_
;
ASSERT_EQ
(
"10.0.0.9"
,
client
.
config_
.
lease_
.
addr_
.
toText
());
// By reconfiguring the server, we remove the existing reservations.
configure
(
DORA_CONFIGS
[
0
]);
// Try to renew the existing lease again.
ASSERT_NO_THROW
(
client
.
doRequest
());
// The reservation has been removed, so the server should respond with
// a DHCPNAK because the address that the client is using doesn't belong
// to a dynamic pool.
resp
=
client
.
getContext
().
response_
;
ASSERT_EQ
(
DHCPNAK
,
static_cast
<
int
>
(
resp
->
getType
()));
// A conforming client would go back to the server discovery.
client
.
setState
(
Dhcp4Client
::
SELECTING
);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW
(
client
.
doDORA
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
// Make sure that the server responded.
ASSERT_TRUE
(
client
.
getContext
().
response_
);
resp
=
client
.
getContext
().
response_
;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ
(
DHCPACK
,
static_cast
<
int
>
(
resp
->
getType
()));
// Obtain the subnet to which the returned address belongs.
Subnet4Ptr
subnet
=
CfgMgr
::
instance
().
getCurrentCfg
()
->
getCfgSubnets4
()
->
selectSubnet
(
client
.
config_
.
lease_
.
addr_
);
ASSERT_TRUE
(
subnet
);
// Make sure that the address has been allocated from the dynamic pool.
ASSERT_TRUE
(
subnet
->
inPool
(
Lease
::
TYPE_V4
,
client
.
config_
.
lease_
.
addr_
));
// Remember the address allocated in the dynamic pool.
IOAddress
in_pool_addr
=
client
.
config_
.
lease_
.
addr_
;
// Create Client B.
Dhcp4Client
clientB
(
client
.
getServer
());
clientB
.
modifyHWAddr
();
// Create reservation for the Client B, for the address that the
// Client A is using.
configure
(
DORA_CONFIGS
[
0
],
false
);
host
.
reset
(
new
Host
(
&
clientB
.
getHWAddress
()
->
hwaddr_
[
0
],
clientB
.
getHWAddress
()
->
hwaddr_
.
size
(),
Host
::
IDENT_HWADDR
,
SubnetID
(
1
),
SubnetID
(
0
),
in_pool_addr
));
CfgMgr
::
instance
().
getStagingCfg
()
->
getCfgHosts
()
->
add
(
host
);
CfgMgr
::
instance
().
commit
();
// Client B performs a DHCPDISCOVER.
clientB
.
setState
(
Dhcp4Client
::
SELECTING
);
// The server determines that the address reserved for Client B is
// in use by Client A so it drops the message from the Client B.
ASSERT_NO_THROW
(
clientB
.
doDiscover
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
ASSERT_FALSE
(
clientB
.
getContext
().
response_
);
// Client A renews the lease.
client
.
setState
(
Dhcp4Client
::
RENEWING
);
// Set the unicast destination address to indicate that it is a renewal.
client
.
setDestAddress
(
IOAddress
(
in_pool_addr
));
ASSERT_NO_THROW
(
client
.
doRequest
());
// Client A should get a DHCPNAK because it is using an address reserved
// for Client B.
resp
=
client
.
getContext
().
response_
;
ASSERT_EQ
(
DHCPNAK
,
static_cast
<
int
>
(
resp
->
getType
()));
// Client B performs 4-way exchange but still doesn't get an address
// because Client A hasn't obtained a new lease, so it is still using
// an address reserved for Client B.
clientB
.
setState
(
Dhcp4Client
::
SELECTING
);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW
(
clientB
.
doDiscover
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
// Make sure that the server responded.
ASSERT_FALSE
(
clientB
.
getContext
().
response_
);
// Client A performs 4-way exchange.
client
.
setState
(
Dhcp4Client
::
SELECTING
);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW
(
client
.
doDORA
(
boost
::
shared_ptr
<
IOAddress
>
(
new
IOAddress
(
"0.0.0.0"
))));
// Make sure that the server responded.
ASSERT_TRUE
(
client
.
getContext
().
response_
);
resp
=
client
.
getContext
().
response_
;
ASSERT_EQ
(
DHCPACK
,
static_cast
<
int
>
(
resp
->
getType
()));
// The server should have assigned a different address than the one
// reserved for the Client B.
ASSERT_NE
(
client
.
config_
.
lease_
.
addr_
.
toText
(),
in_pool_addr
.
toText
());
subnet
=
CfgMgr
::
instance
().
getCurrentCfg
()
->
getCfgSubnets4
()
->