Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Kea
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Adam Osuchowski
Kea
Commits
fa95cdf5
Commit
fa95cdf5
authored
Jan 03, 2014
by
Marcin Siodelski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[3231] Dynamically generate server identifier and append to server response
parent
c56b6109
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
286 additions
and
82 deletions
+286
-82
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.cc
+73
-36
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/dhcp4_srv.h
+54
-2
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+138
-40
src/bin/dhcp4/tests/dhcp4_test_utils.cc
src/bin/dhcp4/tests/dhcp4_test_utils.cc
+3
-2
src/bin/dhcp4/tests/dhcp4_test_utils.h
src/bin/dhcp4/tests/dhcp4_test_utils.h
+18
-2
No files found.
src/bin/dhcp4/dhcp4_srv.cc
View file @
fa95cdf5
...
...
@@ -131,18 +131,20 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
LOG_DEBUG
(
dhcp4_logger
,
DBG_DHCP4_START
,
DHCP4_OPEN_SOCKET
).
arg
(
port
);
try
{
// First call to instance() will create IfaceMgr (it's a singleton)
// it may throw something if things go wrong.
// The 'true' value of the call to setMatchingPacketFilter imposes
// that IfaceMgr will try to use the mechanism to respond directly
// to the client which doesn't have address assigned. This capability
// may be lacking on some OSes, so there is no guarantee that server
// will be able to respond directly.
IfaceMgr
::
instance
().
setMatchingPacketFilter
(
direct_response_desired
);
// Open sockets only if port is non-zero. Port 0 is used
// for non-socket related testing.
// Open sockets only if port is non-zero. Port 0 is used for testing
// purposes in two cases:
// - when non-socket related testing is performed
// - when the particular test supplies its own packet filtering class.
if
(
port
)
{
// First call to instance() will create IfaceMgr (it's a singleton)
// it may throw something if things go wrong.
// The 'true' value of the call to setMatchingPacketFilter imposes
// that IfaceMgr will try to use the mechanism to respond directly
// to the client which doesn't have address assigned. This capability
// may be lacking on some OSes, so there is no guarantee that server
// will be able to respond directly.
IfaceMgr
::
instance
().
setMatchingPacketFilter
(
direct_response_desired
);
// Create error handler. This handler will be called every time
// the socket opening operation fails. We use this handler to
// log a warning.
...
...
@@ -389,19 +391,6 @@ Dhcpv4Srv::run() {
continue
;
}
adjustRemoteAddr
(
query
,
rsp
);
if
(
!
rsp
->
getHops
())
{
rsp
->
setRemotePort
(
DHCP4_CLIENT_PORT
);
}
else
{
rsp
->
setRemotePort
(
DHCP4_SERVER_PORT
);
}
rsp
->
setLocalAddr
(
query
->
getLocalAddr
());
rsp
->
setLocalPort
(
DHCP4_SERVER_PORT
);
rsp
->
setIface
(
query
->
getIface
());
rsp
->
setIndex
(
query
->
getIndex
());
// Specifies if server should do the packing
bool
skip_pack
=
false
;
...
...
@@ -688,12 +677,21 @@ Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
// add Message Type Option (type 53)
msg
->
setType
(
msg_type
);
// DHCP Server Identifier (type 54)
msg
->
addOption
(
getServerID
());
// more options will be added here later
}
void
Dhcpv4Srv
::
appendServerID
(
const
Pkt4Ptr
&
response
)
{
// The source address for the outbound message should have been set already.
// This is the address that to the best of the server's knowledge will be
// available from the client.
// @todo: perhaps we should consider some more sophisticated server id
// generation, but for the current use cases, it should be ok.
response
->
addOption
(
OptionPtr
(
new
Option4AddrLst
(
DHO_DHCP_SERVER_IDENTIFIER
,
response
->
getLocalAddr
()))
);
}
void
Dhcpv4Srv
::
appendRequestedOptions
(
const
Pkt4Ptr
&
question
,
Pkt4Ptr
&
msg
)
{
...
...
@@ -1267,7 +1265,34 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
}
void
Dhcpv4Srv
::
adjustRemoteAddr
(
const
Pkt4Ptr
&
question
,
Pkt4Ptr
&
msg
)
{
Dhcpv4Srv
::
adjustIfaceData
(
const
Pkt4Ptr
&
query
,
const
Pkt4Ptr
&
response
)
{
adjustRemoteAddr
(
query
,
response
);
// For the non-relayed message, the destination port is the client's port.
// For the relayed message, the server/relay port is a destination.
if
(
!
response
->
getHops
())
{
response
->
setRemotePort
(
DHCP4_CLIENT_PORT
);
}
else
{
response
->
setRemotePort
(
DHCP4_SERVER_PORT
);
}
// In many cases the query is sent to a broadcast address. This address
// appears as a local address in the query message. Therefore we can't
// simply copy local address from the query and use it as a source
// address for the response. Instead, we have to check what address our
// socket is bound to and use it as a source address. This operation
// may throw if for some reason the socket is closed.
// @todo Consider an optimization that we use local address from
// the query if this address is not broadcast.
SocketInfo
sock_info
=
IfaceMgr
::
instance
().
getSocket
(
*
query
);
// Set local adddress, port and interface.
response
->
setLocalAddr
(
sock_info
.
addr_
);
response
->
setLocalPort
(
DHCP4_SERVER_PORT
);
response
->
setIface
(
query
->
getIface
());
response
->
setIndex
(
query
->
getIndex
());
}
void
Dhcpv4Srv
::
adjustRemoteAddr
(
const
Pkt4Ptr
&
question
,
const
Pkt4Ptr
&
response
)
{
// Let's create static objects representing zeroed and broadcast
// addresses. We will use them further in this function to test
// other addresses against them. Since they are static, they will
...
...
@@ -1277,21 +1302,21 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
// If received relayed message, server responds to the relay address.
if
(
question
->
getGiaddr
()
!=
zero_addr
)
{
msg
->
setRemoteAddr
(
question
->
getGiaddr
());
response
->
setRemoteAddr
(
question
->
getGiaddr
());
// If giaddr is 0 but client set ciaddr, server should unicast the
// response to ciaddr.
}
else
if
(
question
->
getCiaddr
()
!=
zero_addr
)
{
msg
->
setRemoteAddr
(
question
->
getCiaddr
());
response
->
setRemoteAddr
(
question
->
getCiaddr
());
// We can't unicast the response to the client when sending NAK,
// because we haven't allocated address for him. Therefore,
// NAK is broadcast.
}
else
if
(
msg
->
getType
()
==
DHCPNAK
)
{
msg
->
setRemoteAddr
(
bcast_addr
);
}
else
if
(
response
->
getType
()
==
DHCPNAK
)
{
response
->
setRemoteAddr
(
bcast_addr
);
// If yiaddr is set it means that we have created a lease for a client.
}
else
if
(
msg
->
getYiaddr
()
!=
zero_addr
)
{
}
else
if
(
response
->
getYiaddr
()
!=
zero_addr
)
{
// If the broadcast bit is set in the flags field, we have to
// send the response to broadcast address. Client may have requested it
// because it doesn't support reception of messages on the interface
...
...
@@ -1300,13 +1325,13 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
// directly to a client without address assigned.
const
bool
bcast_flag
=
((
question
->
getFlags
()
&
Pkt4
::
FLAG_BROADCAST_MASK
)
!=
0
);
if
(
!
IfaceMgr
::
instance
().
isDirectResponseSupported
()
||
bcast_flag
)
{
msg
->
setRemoteAddr
(
bcast_addr
);
response
->
setRemoteAddr
(
bcast_addr
);
// Client cleared the broadcast bit and we support direct responses
// so we should unicast the response to a newly allocated address -
// yiaddr.
}
else
{
msg
->
setRemoteAddr
(
msg
->
getYiaddr
());
response
->
setRemoteAddr
(
response
->
getYiaddr
());
}
...
...
@@ -1314,7 +1339,7 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
// found ourselves at this point, the rational thing to do is to respond
// to the address we got the query from.
}
else
{
msg
->
setRemoteAddr
(
question
->
getRemoteAddr
());
response
->
setRemoteAddr
(
question
->
getRemoteAddr
());
}
}
...
...
@@ -1359,6 +1384,12 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
appendBasicOptions
(
discover
,
offer
);
}
// Set the src/dest IP address, port and interface for the outgoing
// packet.
adjustIfaceData
(
discover
,
offer
);
appendServerID
(
offer
);
return
(
offer
);
}
...
...
@@ -1395,6 +1426,12 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
appendBasicOptions
(
request
,
ack
);
}
// Set the src/dest IP address, port and interface for the outgoing
// packet.
adjustIfaceData
(
request
,
ack
);
appendServerID
(
ack
);
return
(
ack
);
}
...
...
src/bin/dhcp4/dhcp4_srv.h
View file @
fa95cdf5
...
...
@@ -396,10 +396,62 @@ protected:
/// @brief Appends default options to a message
///
/// Currently it is only a Message Type option. This function does not add
/// the Server Identifier option as this option must be added using
/// @c Dhcpv4Srv::appendServerID.
///
/// @param msg message object (options will be added to it)
/// @param msg_type specifies message type
void
appendDefaultOptions
(
Pkt4Ptr
&
msg
,
uint8_t
msg_type
);
/// @brief Adds server identifier option to the server's response.
///
/// This method adds a server identifier to the DHCPv4 message. It exects
/// that the local (source) address is set for this message. If address is
/// not set, it will throw an exception. This method also expects that the
/// server identifier option is not present in the specified message.
/// Otherwise, it will throw an exception on attempt to add a duplicate
/// server identifier option.
///
/// @note This method doesn't throw exceptions by itself but the underlying
/// classes being used my throw. The reason for this method to not sanity
/// check the specified message is that it is meant to be called internally
/// by the @c Dhcpv4Srv class.
///
/// @param [out] response DHCPv4 message to which the server identifier
/// option should be added.
static
void
appendServerID
(
const
Pkt4Ptr
&
response
);
/// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
///
/// This method sets the following parameters for the DHCPv4 message being
/// sent to a client:
/// - client unicast or a broadcast address,
/// - client or relay port,
/// - server address,
/// - server port,
/// - name and index of the interface which is to be used to send the
/// message.
///
/// Internally it calls the @c Dhcpv4Srv::adjustRemoteAddr to figure
/// out the destination address (client unicast address or broadcast
/// address).
///
/// The destination port is always DHCPv4 client (68) or relay (67) port,
/// depending if the response will be sent directly to a client (hops = 0),
/// or through a relay (hops > 0).
///
/// The source port is always set to DHCPv4 server port (67).
///
/// The interface selected for the response is always the same as the
/// one through which the query has been received.
///
/// The source address for the response is the IPv4 address assigned to
/// the interface being used to send the response. This function uses
/// @c IfaceMgr to get the socket bound to the IPv4 address on the
/// particular interface.
static
void
adjustIfaceData
(
const
Pkt4Ptr
&
query
,
const
Pkt4Ptr
&
response
);
/// @brief Sets remote addresses for outgoing packet.
///
/// This method sets the local and remote addresses on outgoing packet.
...
...
@@ -414,8 +466,8 @@ protected:
/// function.
///
/// @param question instance of a packet received by a server.
/// @param [out]
msg
response packet which addresses are to be adjusted.
void
adjustRemoteAddr
(
const
Pkt4Ptr
&
question
,
Pkt4Ptr
&
msg
);
/// @param [out]
response
response packet which addresses are to be adjusted.
static
void
adjustRemoteAddr
(
const
Pkt4Ptr
&
question
,
const
Pkt4Ptr
&
response
);
/// @brief Returns server-identifier option
///
...
...
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
View file @
fa95cdf5
...
...
@@ -65,19 +65,25 @@ const char* SRVID_FILE = "server-id-test.txt";
// This test verifies that the destination address of the response
// message is set to giaddr, when giaddr is set to non-zero address
// in the received message.
TEST_F
(
Dhcpv4SrvTest
,
adjustRemoteAddressRelay
)
{
boost
::
scoped_ptr
<
NakedDhcpv4Srv
>
srv
(
new
NakedDhcpv4Srv
(
0
));
TEST_F
(
Dhcpv4SrvFakeIfaceTest
,
adjustIfaceDataRelay
)
{
// Create the instance of the incoming packet.
boost
::
shared_ptr
<
Pkt4
>
req
(
new
Pkt4
(
DHCPDISCOVER
,
1234
));
// Set the giaddr to non-zero address as if it was relayed.
// Set the giaddr to non-zero address and hops to non-zero value
// as if it was relayed.
req
->
setGiaddr
(
IOAddress
(
"192.0.2.1"
));
req
->
setHops
(
2
);
// Set ciaddr to zero. This simulates the client which applies
// for the new lease.
req
->
setCiaddr
(
IOAddress
(
"0.0.0.0"
));
// Clear broadcast flag.
req
->
setFlags
(
0x0000
);
// Set local address, port and interface.
req
->
setLocalAddr
(
IOAddress
(
"192.0.3.1"
));
req
->
setLocalPort
(
1001
);
req
->
setIface
(
"eth0"
);
req
->
setIndex
(
1
);
// Create a response packet. Assume that the new lease have
// been created and new address allocated. This address is
// stored in yiaddr field.
...
...
@@ -85,12 +91,24 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
resp
->
setYiaddr
(
IOAddress
(
"192.0.2.100"
));
// Clear the remote address.
resp
->
setRemoteAddr
(
IOAddress
(
"0.0.0.0"
));
// Set hops value for the response.
resp
->
setHops
(
req
->
getHops
());
// This function never throws.
ASSERT_NO_THROW
(
srv
->
adjustRemoteAddr
(
req
,
resp
));
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
adjustIfaceData
(
req
,
resp
));
// Now the destination address should be relay's address.
EXPECT_EQ
(
"192.0.2.1"
,
resp
->
getRemoteAddr
().
toText
());
// The query has been relayed, so the response must be sent to the port 67.
EXPECT_EQ
(
DHCP4_SERVER_PORT
,
resp
->
getRemotePort
());
// Local address should be copied from the query message.
EXPECT_EQ
(
"192.0.3.1"
,
resp
->
getLocalAddr
().
toText
());
// The local port is always DHCPv4 server port 67.
EXPECT_EQ
(
DHCP4_SERVER_PORT
,
resp
->
getLocalPort
());
// We will send response over the same interface which was used to receive
// query.
EXPECT_EQ
(
"eth0"
,
resp
->
getIface
());
EXPECT_EQ
(
1
,
resp
->
getIndex
());
// Let's do another test and set other fields: ciaddr and
// flags. By doing it, we want to make sure that the relay
...
...
@@ -103,7 +121,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
// Clear remote address.
resp
->
setRemoteAddr
(
IOAddress
(
"0.0.0.0"
));
ASSERT_NO_THROW
(
srv
->
adjustRemoteAddr
(
req
,
resp
));
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
adjustIfaceData
(
req
,
resp
));
// Response should be sent back to the relay address.
EXPECT_EQ
(
"192.0.2.50"
,
resp
->
getRemoteAddr
().
toText
());
...
...
@@ -113,9 +131,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRelay) {
// is set to ciaddr when giaddr is set to zero and the ciaddr is set to
// non-zero address in the received message. This is the case when the
// client is in Renew or Rebind state.
TEST_F
(
Dhcpv4SrvTest
,
adjustRemoteAddressRenewRebind
)
{
boost
::
scoped_ptr
<
NakedDhcpv4Srv
>
srv
(
new
NakedDhcpv4Srv
(
0
));
TEST_F
(
Dhcpv4SrvFakeIfaceTest
,
adjustIfaceDataRenew
)
{
// Create instance of the incoming packet.
boost
::
shared_ptr
<
Pkt4
>
req
(
new
Pkt4
(
DHCPDISCOVER
,
1234
));
...
...
@@ -132,6 +148,15 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
// whether to unicast the response to the acquired address or
// broadcast it.
req
->
setFlags
(
Pkt4
::
FLAG_BROADCAST_MASK
);
// This is a direct message, so the hops should be cleared.
req
->
setHops
(
0
);
// Set local unicast address as if we are renewing a lease.
req
->
setLocalAddr
(
IOAddress
(
"192.0.3.1"
));
// Request is received on the DHCPv4 server port.
req
->
setLocalPort
(
DHCP4_SERVER_PORT
);
// Set the interface. The response should be sent over the same interface.
req
->
setIface
(
"eth0"
);
req
->
setIndex
(
1
);
// Create a response.
boost
::
shared_ptr
<
Pkt4
>
resp
(
new
Pkt4
(
DHCPOFFER
,
1234
));
...
...
@@ -142,11 +167,25 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
resp
->
setYiaddr
(
IOAddress
(
"192.0.2.13"
));
// Clear the remote address.
resp
->
setRemoteAddr
(
IOAddress
(
"0.0.0.0"
));
// Copy hops value from the query.
resp
->
setHops
(
req
->
getHops
());
ASSERT_NO_THROW
(
srv
->
adjustRemoteAddr
(
req
,
resp
));
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
adjustIfaceData
(
req
,
resp
));
// Check that server responds to ciaddr
EXPECT_EQ
(
"192.0.2.15"
,
resp
->
getRemoteAddr
().
toText
());
// The query was non-relayed, so the response should be sent to a DHCPv4
// client port 68.
EXPECT_EQ
(
DHCP4_CLIENT_PORT
,
resp
->
getRemotePort
());
// The response should be sent from the unicast address on which the
// query has been received.
EXPECT_EQ
(
"192.0.3.1"
,
resp
->
getLocalAddr
().
toText
());
// The response should be sent from the DHCPv4 server port.
EXPECT_EQ
(
DHCP4_SERVER_PORT
,
resp
->
getLocalPort
());
// The interface data should match the data in the query.
EXPECT_EQ
(
"eth0"
,
resp
->
getIface
());
EXPECT_EQ
(
1
,
resp
->
getIndex
());
}
// This test verifies that the destination address of the response message
...
...
@@ -156,9 +195,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressRenewRebind) {
// of the response should be set to yiaddr if server supports direct responses
// to the client which doesn't have an address yet or broadcast if the server
// doesn't support direct responses.
TEST_F
(
Dhcpv4SrvTest
,
adjustRemoteAddressSelect
)
{
boost
::
scoped_ptr
<
NakedDhcpv4Srv
>
srv
(
new
NakedDhcpv4Srv
(
0
));
TEST_F
(
Dhcpv4SrvFakeIfaceTest
,
adjustIfaceDataSelect
)
{
// Create instance of the incoming packet.
boost
::
shared_ptr
<
Pkt4
>
req
(
new
Pkt4
(
DHCPDISCOVER
,
1234
));
...
...
@@ -170,13 +207,31 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
// Let's clear the broadcast flag.
req
->
setFlags
(
0
);
// This is a non-relayed message, so let's clear hops count.
req
->
setHops
(
0
);
// The query is sent to the broadcast address in the Select state.
req
->
setLocalAddr
(
IOAddress
(
"255.255.255.255"
));
// The query has been received on the DHCPv4 server port 67.
req
->
setLocalPort
(
DHCP4_SERVER_PORT
);
// Set the interface. The response should be sent via the same interface.
req
->
setIface
(
"eth0"
);
req
->
setIndex
(
1
);
// Create a response.
boost
::
shared_ptr
<
Pkt4
>
resp
(
new
Pkt4
(
DHCPOFFER
,
1234
));
// Assign some new address for this client.
resp
->
setYiaddr
(
IOAddress
(
"192.0.2.13"
));
// Clear the remote address.
resp
->
setRemoteAddr
(
IOAddress
(
"0.0.0.0"
));
// Copy hops count.
resp
->
setHops
(
req
->
getHops
());
// We want to test the case, when the server (packet filter) doesn't support
// ddirect responses to the client which doesn't have an address yet. In
// case, the server should send its response to the broadcast address.
// We can control whether the current packet filter returns that its support
// direct responses or not.
current_pkt_filter_
->
direct_resp_supported_
=
false
;
// When running unit tests, the IfaceMgr is using the default Packet
// Filtering class, PktFilterInet. This class does not support direct
...
...
@@ -184,24 +239,36 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
// are zero and client has just got new lease, the assigned address is
// carried in yiaddr. In order to send this address to the client,
// server must broadcast its response.
ASSERT_NO_THROW
(
srv
->
adjustRemoteAddr
(
req
,
resp
));
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
adjustIfaceData
(
req
,
resp
));
// Check that the response is sent to broadcast address as the
// server doesn't have capability to respond directly.
EXPECT_EQ
(
"255.255.255.255"
,
resp
->
getRemoteAddr
().
toText
());
// Although the query has been sent to the broadcast address, the
// server should select a unicast address on the particular interface
// as a source address for the response.
EXPECT_EQ
(
"192.0.3.1"
,
resp
->
getLocalAddr
().
toText
());
// The response should be sent from the DHCPv4 server port.
EXPECT_EQ
(
DHCP4_SERVER_PORT
,
resp
->
getLocalPort
());
// The response should be sent via the same interface through which
// query has been received.
EXPECT_EQ
(
"eth0"
,
resp
->
getIface
());
EXPECT_EQ
(
1
,
resp
->
getIndex
());
// We also want to test the case when the server has capability to
// respond directly to the client which is not configured. Server
// makes decision whether it responds directly or broadcast its
// response based on the capability reported by IfaceMgr. In order
// to set this capability we have to provide a dummy Packet Filter
// class which would report the support for direct responses.
// This class is called PktFilterTest.
IfaceMgr
::
instance
().
setPacketFilter
(
PktFilterPtr
(
new
PktFilterTest
()));
// response based on the capability reported by IfaceMgr. We can
// control whether the current packet filter returns that it supports
// direct responses or not.
current_pkt_filter_
->
direct_resp_supported_
=
true
;
// Now we expect that the server will send its response to the
// address assigned for the client.
ASSERT_NO_THROW
(
srv
->
adjustRemoteAddr
(
req
,
resp
));
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
adjustIfaceData
(
req
,
resp
));
EXPECT_EQ
(
"192.0.2.13"
,
resp
->
getRemoteAddr
().
toText
());
}
...
...
@@ -211,9 +278,7 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressSelect) {
// query. Client sets this flag to indicate that it can't receive direct
// responses from the server when it doesn't have its interface configured.
// Server must respect broadcast flag.
TEST_F
(
Dhcpv4SrvTest
,
adjustRemoteAddressBroadcast
)
{
boost
::
scoped_ptr
<
NakedDhcpv4Srv
>
srv
(
new
NakedDhcpv4Srv
(
0
));
TEST_F
(
Dhcpv4SrvFakeIfaceTest
,
adjustIfaceDataBroadcast
)
{
// Create instance of the incoming packet.
boost
::
shared_ptr
<
Pkt4
>
req
(
new
Pkt4
(
DHCPDISCOVER
,
1234
));
...
...
@@ -221,6 +286,13 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressBroadcast) {
req
->
setGiaddr
(
IOAddress
(
"0.0.0.0"
));
// Clear client address as it hasn't got any address configured yet.
req
->
setCiaddr
(
IOAddress
(
"0.0.0.0"
));
// The query is sent to the broadcast address in the Select state.
req
->
setLocalAddr
(
IOAddress
(
"255.255.255.255"
));
// The query has been received on the DHCPv4 server port 67.
req
->
setLocalPort
(
DHCP4_SERVER_PORT
);
// Set the interface. The response should be sent via the same interface.
req
->
setIface
(
"eth0"
);
req
->
setIndex
(
1
);
// Let's set the broadcast flag.
req
->
setFlags
(
Pkt4
::
FLAG_BROADCAST_MASK
);
...
...
@@ -233,25 +305,51 @@ TEST_F(Dhcpv4SrvTest, adjustRemoteAddressBroadcast) {
// Clear the remote address.
resp
->
setRemoteAddr
(
IOAddress
(
"0.0.0.0"
));
// When running unit tests, the IfaceMgr is using the default Packet
// Filtering class, PktFilterInet. This class does not support direct
// responses to the clients without address assigned. If giaddr and
// ciaddr are zero and client has just got the new lease, the assigned
// address is carried in yiaddr. In order to send this address to the
// client, server must send the response to the broadcast address when
// direct response is not supported. This conflicts with the purpose
// of this test which is supposed to verify that responses are sent
// to broadcast address only, when broadcast flag is set. Therefore,
// in order to simulate that direct responses are supported we have
// to replace the default packet filtering class with a dummy class
// which reports direct response capability.
IfaceMgr
::
instance
().
setPacketFilter
(
PktFilterPtr
(
new
PktFilterTest
()));
ASSERT_NO_THROW
(
srv
->
adjustRemoteAddr
(
req
,
resp
));
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
adjustIfaceData
(
req
,
resp
));
// Server must repond to broadcast address when client desired that
// by setting the broadcast flag in its request.
EXPECT_EQ
(
"255.255.255.255"
,
resp
->
getRemoteAddr
().
toText
());
// Although the query has been sent to the broadcast address, the
// server should select a unicast address on the particular interface
// as a source address for the response.
EXPECT_EQ
(
"192.0.3.1"
,
resp
->
getLocalAddr
().
toText
());
// The response should be sent from the DHCPv4 server port.
EXPECT_EQ
(
DHCP4_SERVER_PORT
,
resp
->
getLocalPort
());
// The response should be sent via the same interface through which
// query has been received.
EXPECT_EQ
(
"eth0"
,
resp
->
getIface
());
EXPECT_EQ
(
1
,
resp
->
getIndex
());
}
// This test verifies that the server identifier option is appended to
// a specified DHCPv4 message and the server identifier is correct.
TEST_F
(
Dhcpv4SrvTest
,
appendServerID
)
{
Pkt4Ptr
response
(
new
Pkt4
(
DHCPDISCOVER
,
1234
));
// Set a local address. It is required by the function under test
// to create the Server Identifier option.
response
->
setLocalAddr
(
IOAddress
(
"192.0.3.1"
));
// Append the Server Identifier.
ASSERT_NO_THROW
(
NakedDhcpv4Srv
::
appendServerID
(
response
));
// Make sure that the option has been added.
OptionPtr
opt
=
response
->
getOption
(
DHO_DHCP_SERVER_IDENTIFIER
);
ASSERT_TRUE
(
opt
);
Option4AddrLstPtr
opt_server_id
=
boost
::
dynamic_pointer_cast
<
Option4AddrLst
>
(
opt
);
ASSERT_TRUE
(
opt_server_id
);
// The option is represented as a list of IPv4 addresses but with
// only one address added.
Option4AddrLst
::
AddressContainer
addrs
=
opt_server_id
->
getAddresses
();
ASSERT_EQ
(
1
,
addrs
.
size
());
// This address should match the local address of the packet.
EXPECT_EQ
(
"192.0.3.1"
,
addrs
[
0
].
toText
());
}
// Sanity check. Verifies that both Dhcpv4Srv and its derived
...
...
@@ -856,7 +954,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RenewBasic) {
// @todo: Implement tests for rejecting renewals
// This test verifies if the sanityCheck() really checks options presence.
TEST_F
(
Dhcpv4Srv
FakeIface
Test
,
sanityCheck
)
{
TEST_F
(
Dhcpv4SrvTest
,
sanityCheck
)
{
boost
::
scoped_ptr
<
NakedDhcpv4Srv
>
srv
;
ASSERT_NO_THROW
(
srv
.
reset
(
new
NakedDhcpv4Srv
(
0
)));
...
...
src/bin/dhcp4/tests/dhcp4_test_utils.cc
View file @
fa95cdf5
...
...
@@ -399,7 +399,7 @@ void Dhcpv4SrvTest::TearDown() {
}
Dhcpv4SrvFakeIfaceTest
::
Dhcpv4SrvFakeIfaceTest
()
:
Dhcpv4SrvTest
()
{
:
Dhcpv4SrvTest
()
,
current_pkt_filter_
()
{
// Remove current interface configuration. Instead we want to add
// a couple of fake interfaces.
IfaceMgr
&
ifacemgr
=
IfaceMgr
::
instance
();
...
...
@@ -414,7 +414,8 @@ Dhcpv4SrvFakeIfaceTest::Dhcpv4SrvFakeIfaceTest()
// In order to use fake interfaces we have to supply the custom
// packet filtering class, which can mimic opening sockets on
// fake interafaces.
ifacemgr
.
setPacketFilter
(
PktFilterPtr
(
new
PktFilterTest
()));
current_pkt_filter_
.
reset
(
new
PktFilterTest
());
ifacemgr
.
setPacketFilter
(
current_pkt_filter_
);
ifacemgr
.
openSockets4
();
}
...
...
src/bin/dhcp4/tests/dhcp4_test_utils.h
View file @
fa95cdf5
...
...
@@ -47,11 +47,18 @@ namespace test {
class
PktFilterTest
:
public
PktFilter
{
public:
/// @brief Constructor.
///
/// Sets the 'direct response' capability to true.
PktFilterTest
()
:
direct_resp_supported_
(
true
)
{
}
/// @brief Reports 'direct response' capability.
///
/// @return always true.
virtual
bool
isDirectResponseSupported
()
const
{
return
(
true
);
return
(
direct_resp_supported_
);
}
/// Does nothing.
...
...
@@ -71,6 +78,10 @@ public:
return
(
0
);
}
/// @brief Holds a boolean value which indicates whether direct response
/// capability is supported (true) or not (false).
bool
direct_resp_supported_
;
};
typedef
boost
::
shared_ptr
<
PktFilterTest
>
PktFilterTestPtr
;
...
...
@@ -311,6 +322,10 @@ public:
/// @param msg_type DHCPDISCOVER or DHCPREQUEST
void
testDiscoverRequest
(
const
uint8_t
msg_type
);
/// @brief Holds a pointer to the packet filter object currently used
/// by the IfaceMgr.
PktFilterTestPtr
current_pkt_filter_
;
};