BIND 10 master, updated. 9e571cc217d6b1a2fd6fdae1565fcc6fde6d08b1 [master] Merge branch 'trac3242'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Feb 10 10:35:46 UTC 2014
The branch, master has been updated
via 9e571cc217d6b1a2fd6fdae1565fcc6fde6d08b1 (commit)
via b6c929fa55635e2fc6b673b6eb02569c55a16887 (commit)
via 555c87c709c5b8306f469e620cded1ca4c95f2e6 (commit)
via 97cec5c2ecf2f1864b58497cf12ae848a78ee865 (commit)
via 340784189765ee4dd56aaea7a71d50d2edf18684 (commit)
via f22c490bd4a194ad693e29a8e9f65a0480405e5c (commit)
via 1b522d63a842654c680602d3ee89eac324333d48 (commit)
via fbade26f032fdd495abe2effb0a2913b642b0ac6 (commit)
via 594b8277d0199731a3b282345248fea422d9ff86 (commit)
via c3a1bd5a608a7d39ea5e4220693f8c4068455d1b (commit)
via 0b6ff4416ca1c990456d05d4a7d5417c523fdbc6 (commit)
via ce60aa86dcb9aaef23a30ec2ed2a42944245e7a7 (commit)
via a435b27fe184749d643fa63d06be74aebb48aa2e (commit)
via 4f3dc5a36ac27e91e785eb94c9de1b4798477940 (commit)
via abb2b61c859ff5b584a1ec75203e2e4d6914457a (commit)
via f4548b2768162c8748665d190e288bc87327dde1 (commit)
via f1fabbe371b1905707e19e564443327dbb94114f (commit)
via 3d817285c8b1ab5e95eef930ca2e426b41cc3bf6 (commit)
via 52dc56691fc1e5b5b9f9405e02627cc2791ea608 (commit)
via aed723721a6c4a1296dbc51553eacdff8e205d81 (commit)
from 49d0ce1889192c7df7dc94544dfca8e51054015e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 9e571cc217d6b1a2fd6fdae1565fcc6fde6d08b1
Merge: 49d0ce1 b6c929f
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Feb 10 11:32:27 2014 +0100
[master] Merge branch 'trac3242'
Conflicts:
src/bin/dhcp4/tests/fqdn_unittest.cc
-----------------------------------------------------------------------
Summary of changes:
doc/guide/bind10-guide.xml | 34 ++
src/bin/dhcp4/dhcp4_messages.mes | 19 +
src/bin/dhcp4/dhcp4_srv.cc | 153 ++++++--
src/bin/dhcp4/dhcp4_srv.h | 80 +++-
src/bin/dhcp4/tests/Makefile.am | 2 +
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 370 ++++++++++++++----
src/bin/dhcp4/tests/dhcp4_test_utils.cc | 85 ++---
src/bin/dhcp4/tests/dhcp4_test_utils.h | 294 +++++++-------
src/bin/dhcp4/tests/direct_client_unittest.cc | 403 ++++++++++++++++++++
src/bin/dhcp4/tests/fqdn_unittest.cc | 46 ++-
src/lib/dhcp/dhcp4.h | 6 +-
src/lib/dhcp/iface_mgr.cc | 18 +
src/lib/dhcp/iface_mgr.h | 11 +
src/lib/dhcp/pkt4.cc | 2 +
src/lib/dhcp/tests/Makefile.am | 19 +
src/lib/dhcp/tests/iface_mgr_test_config.cc | 137 +++++++
src/lib/dhcp/tests/iface_mgr_test_config.h | 241 ++++++++++++
src/lib/dhcp/tests/iface_mgr_unittest.cc | 53 ++-
.../tests/pkt_filter_test_stub.cc} | 40 +-
..._filter_test_utils.h => pkt_filter_test_stub.h} | 94 +----
src/lib/dhcp/tests/pkt_filter_test_utils.h | 6 +-
src/lib/dhcpsrv/cfgmgr.cc | 40 +-
src/lib/dhcpsrv/cfgmgr.h | 16 +-
src/lib/dhcpsrv/tests/Makefile.am | 1 +
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 43 +++
25 files changed, 1734 insertions(+), 479 deletions(-)
create mode 100644 src/bin/dhcp4/tests/direct_client_unittest.cc
create mode 100644 src/lib/dhcp/tests/iface_mgr_test_config.cc
create mode 100644 src/lib/dhcp/tests/iface_mgr_test_config.h
copy src/lib/{dhcpsrv/tests/test_get_callout_handle.cc => dhcp/tests/pkt_filter_test_stub.cc} (53%)
copy src/lib/dhcp/tests/{pkt_filter_test_utils.h => pkt_filter_test_stub.h} (52%)
-----------------------------------------------------------------------
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index dff8678..0136efa 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -4475,6 +4475,40 @@ Dhcp4/subnet4 [] list (default)
</section>
+ <section id="dhcp4-subnet-selection">
+ <title>How DHCPv4 server selects subnet for a client</title>
+ <para>
+ The DHCPv4 server differentiates between the directly connected clients,
+ clients trying to renew leases and clients sending their messages through
+ relays. For the directly connected clients the server will check the
+ configuration of the interface on which the message has been received, and
+ if the server configuration doesn't match any configured subnet the
+ message is discarded.</para>
+ <para>Assuming that the server's interface is configured with the 192.0.2.3
+ IPv4 address, the server will only process messages received through
+ this interface from the directly connected client, if there is a subnet
+ configured, to which this IPv4 address belongs, e.g. 192.0.2.0/24.
+ The server will use this subnet to assign IPv4 address for the client.
+ </para>
+ <para>
+ The rule above does not apply when the client unicasts its message, i.e.
+ is trying to renew its lease. Such message is accepted through any
+ interface. The renewing client sets ciaddr to the currently used IPv4
+ address. The server uses this address to select the subnet for the client
+ (in particular, to extend the lease using this address).
+ </para>
+ <para>
+ If the message is relayed it is accepted through any interface. The giaddr
+ set by the relay agent is used to select the subnet for the client.
+ </para>
+ <note>
+ <para>The subnet selection mechanism described in this section is based
+ on the assumption that client classification is not used. The classification
+ mechanism alters the way in which subnet is selected for the client,
+ depending on the clasess that the client belongs to.</para>
+ </note>
+ </section>
+
<section id="dhcp4-std">
<title>Supported Standards</title>
<para>The following standards and draft standards are currently
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 297acfd..e8b5812 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -135,6 +135,18 @@ A "libreload" command was issued to reload the hooks libraries but for
some reason the reload failed. Other error messages issued from the
hooks framework will indicate the nature of the problem.
+% DHCP4_UNRECOGNIZED_RCVD_PACKET_TYPE received message (transaction id %1) has unrecognized type %2 in option 53
+This debug message indicates that the message type carried in DHCPv4 option
+53 is unrecognized by the server. The valid message types are listed
+on the IANA website: http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53.
+The message will not be processed by the server.
+
+% DHCP4_UNSUPPORTED_RCVD_PACKET_TYPE received message (transaction id %1), having type %2 is not supported
+This debug message indicates that the message type carried in DHCPv4 option
+53 is valid but the message will not be processed by the server. This includes
+messages being normally sent by the server to the client, such as Offer, ACK,
+NAK etc.
+
% DHCP4_LEASE_ADVERT lease %1 advertised (client client-id %2, hwaddr %3)
This debug message indicates that the server successfully advertised
a lease. It is up to the client to choose one server out of othe advertised
@@ -175,6 +187,13 @@ This warning message is issued when current server configuration specifies
no interfaces that server should listen on, or specified interfaces are not
configured to receive the traffic.
+% DHCP4_NO_SUBNET_FOR_DIRECT_CLIENT no suitable subnet configured for a direct client sending packet with transaction id %1, on interface %2, received message is dropped
+This info messsage is logged when received a message from a directly connected
+client but there is no suitable subnet configured for the interface on
+which this message has been received. The IPv4 address assigned on this
+interface must belong to one of the configured subnets. Otherwise
+received message is dropped.
+
% DHCP4_NOT_RUNNING IPv4 DHCP server is not running
A warning message is issued when an attempt is made to shut down the
IPv4 DHCP server but it is not running.
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 46ad0bb..d3bfe01 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -230,26 +230,15 @@ Dhcpv4Srv::run() {
}
}
- // Check if the DHCPv4 packet has been sent to us or to someone else.
- // If it hasn't been sent to us, drop it!
- if (!acceptServerId(query)) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_NOT_FOR_US)
- .arg(query->getTransid())
- .arg(query->getIface());
- continue;
- }
-
- // When receiving a packet without message type option, getType() will
- // throw. Let's set type to -1 as default error indicator.
- int type = -1;
- try {
- type = query->getType();
- } catch (...) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_NO_TYPE)
- .arg(query->getIface());
+ // Check whether the message should be further processed or discarded.
+ // There is no need to log anything here. This function logs by itself.
+ if (!accept(query)) {
continue;
}
+ // We have sanity checked (in accept() that the Message Type option
+ // exists, so we can safely get it here.
+ int type = query->getType();
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
.arg(serverReceivedPacketName(type))
.arg(type)
@@ -1413,24 +1402,38 @@ Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
}
Subnet4Ptr
-Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
+Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) const {
Subnet4Ptr subnet;
- // Is this relayed message?
- IOAddress relay = question->getGiaddr();
static const IOAddress notset("0.0.0.0");
+ static const IOAddress bcast("255.255.255.255");
- if (relay != notset) {
- // Yes: Use relay address to select subnet
- subnet = CfgMgr::instance().getSubnet4(relay);
+ // If a message is relayed, use the relay (giaddr) address to select subnet
+ // for the client. Note that this may result in exception if the value
+ // of hops does not correspond with the Giaddr. Such message is considered
+ // to be malformed anyway and the message will be dropped by the higher
+ // level functions.
+ if (question->isRelayed()) {
+ subnet = CfgMgr::instance().getSubnet4(question->getGiaddr());
+
+ // The message is not relayed so it is sent directly by a client. But
+ // the client may be renewing its lease and in such case it unicasts
+ // its message to the server. Note that the unicast Request bypasses
+ // relays and the client may be in a different network, so we can't
+ // use IP address on the local interface to get the subnet. Instead,
+ // we rely on the client's address to get the subnet.
+ } else if ((question->getLocalAddr() != bcast) &&
+ (question->getCiaddr() != notset)) {
+ subnet = CfgMgr::instance().getSubnet4(question->getCiaddr());
+
+ // The message has been received from a directly connected client
+ // and this client appears to have no address. The IPv4 address
+ // assigned to the interface on which this message has been received,
+ // will be used to determine the subnet suitable for the client.
} else {
-
- // No: Use client's address to select subnet
- subnet = CfgMgr::instance().getSubnet4(question->getRemoteAddr());
+ subnet = CfgMgr::instance().getSubnet4(question->getIface());
}
- /// @todo Implement getSubnet4(interface-name)
-
// Let's execute all callouts registered for subnet4_select
if (HooksManager::calloutsPresent(hook_index_subnet4_select_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(question);
@@ -1465,7 +1468,93 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
}
bool
-Dhcpv4Srv::acceptServerId(const Pkt4Ptr& pkt) const {
+Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
+ // Check if the message from directly connected client (if directly
+ // connected) should be dropped or processed.
+ if (!acceptDirectRequest(query)) {
+ LOG_INFO(dhcp4_logger, DHCP4_NO_SUBNET_FOR_DIRECT_CLIENT)
+ .arg(query->getTransid())
+ .arg(query->getIface());
+ return (false);
+ }
+
+ // Check if the DHCPv4 packet has been sent to us or to someone else.
+ // If it hasn't been sent to us, drop it!
+ if (!acceptServerId(query)) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_NOT_FOR_US)
+ .arg(query->getTransid())
+ .arg(query->getIface());
+ return (false);
+ }
+
+ // Check that the message type is accepted by the server. We rely on the
+ // function called to log a message if needed.
+ if (!acceptMessageType(query)) {
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const {
+ try {
+ if (pkt->isRelayed()) {
+ return (true);
+ }
+ } catch (const Exception& ex) {
+ return (false);
+ }
+ static const IOAddress bcast("255.255.255.255");
+ return ((pkt->getLocalAddr() != bcast || selectSubnet(pkt)));
+}
+
+bool
+Dhcpv4Srv::acceptMessageType(const Pkt4Ptr& query) const {
+ // When receiving a packet without message type option, getType() will
+ // throw.
+ int type;
+ try {
+ type = query->getType();
+
+ } catch (...) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_NO_TYPE)
+ .arg(query->getIface());
+ return (false);
+ }
+
+ // If we receive a message with a non-existing type, we are logging it.
+ if (type > DHCPLEASEQUERYDONE) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
+ DHCP4_UNRECOGNIZED_RCVD_PACKET_TYPE)
+ .arg(type)
+ .arg(query->getTransid());
+ return (false);
+ }
+
+ // Once we know that the message type is within a range of defined DHCPv4
+ // messages, we do a detailed check to make sure that the received message
+ // is targeted at server. Note that we could have received some Offer
+ // message broadcasted by the other server to a relay. Even though, the
+ // server would rather unicast its response to a relay, let's be on the
+ // safe side. Also, we want to drop other messages which we don't support.
+ // All these valid messages that we are not going to process are dropped
+ // silently.
+ if ((type != DHCPDISCOVER) && (type != DHCPREQUEST) &&
+ (type != DHCPRELEASE) && (type != DHCPDECLINE) &&
+ (type != DHCPINFORM)) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
+ DHCP4_UNSUPPORTED_RCVD_PACKET_TYPE)
+ .arg(type)
+ .arg(query->getTransid());
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const {
// This function is meant to be called internally by the server class, so
// we rely on the caller to sanity check the pointer and we don't check
// it here.
@@ -1475,7 +1564,7 @@ Dhcpv4Srv::acceptServerId(const Pkt4Ptr& pkt) const {
// Note that we don't check cases that server identifier is mandatory
// but not present. This is meant to be sanity checked in other
// functions.
- OptionPtr option = pkt->getOption(DHO_DHCP_SERVER_IDENTIFIER);
+ OptionPtr option = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
if (!option) {
return (true);
}
@@ -1603,8 +1692,8 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
size_t
Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
- const std::string& option_space,
- isc::dhcp::OptionCollection& options) {
+ const std::string& option_space,
+ isc::dhcp::OptionCollection& options) {
size_t offset = 0;
OptionDefContainer option_defs;
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index ec63e7f..087120c 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -167,6 +167,83 @@ public:
protected:
+ /// @name Functions filtering and sanity-checking received messages.
+ ///
+ /// @todo These functions are supposed to be moved to a new class which
+ /// will manage different rules for accepting and rejecting messages.
+ /// Perhaps ticket #3116 is a good opportunity to do it.
+ ///
+ //@{
+ /// @brief Checks whether received message should be processed or discarded.
+ ///
+ /// This function checks whether received message should be processed or
+ /// discarded. It should be called on the beginning of message processing
+ /// (just after the message has been decoded). This message calls a number
+ /// of other functions which check whether message should be processed,
+ /// using different criteria.
+ ///
+ /// This function should be extended when new criteria for accepting
+ /// received message have to be implemented. This function is meant to
+ /// aggregate all early filtering checks on the received message. By having
+ /// a single function like this, we are avoiding bloat of the server's main
+ /// loop.
+ ///
+ /// @warning This function should remain exception safe.
+ ///
+ /// @param query Received message.
+ ///
+ /// @return true if the message should be further processed, or false if
+ /// the message should be discarded.
+ bool accept(const Pkt4Ptr& query) const;
+
+ /// @brief Check if a message sent by directly connected client should be
+ /// accepted or discared.
+ ///
+ /// This function checks if the received message is from directly connected
+ /// client. If it is, it checks that it should be processed or discarded.
+ ///
+ /// Note that this function doesn't validate all addresses being carried in
+ /// the message. The primary purpose of this function is to filter out
+ /// direct messages in the local network for which there is no suitable
+ /// subnet configured. For example, this function accepts unicast messages
+ /// because unicasts may be used by clients located in remote networks to
+ /// to renew existing leases. If their notion of address is wrong, the
+ /// server will have to sent a NAK, instead of dropping the message.
+ /// Detailed validation of such messages is performed at later stage of
+ /// processing.
+ ///
+ /// This function accepts the following messages:
+ /// - all valid relayed messages,
+ /// - all unicast messages,
+ /// - all broadcast messages received on the interface for which the
+ /// suitable subnet exists (is configured).
+ ///
+ /// @param query Message sent by a client.
+ ///
+ /// @return true if message is accepted for further processing, false
+ /// otherwise.
+ bool acceptDirectRequest(const Pkt4Ptr& query) const;
+
+ /// @brief Check if received message type is valid for the server to
+ /// process.
+ ///
+ /// This function checks that the received message type belongs to the range
+ /// of types regonized by the server and that the message of this type
+ /// should be processed by the server.
+ ///
+ /// The messages types accepted for processing are:
+ /// - Discover
+ /// - Request
+ /// - Release
+ /// - Decline
+ /// - Inform
+ ///
+ /// @param query Message sent by a client.
+ ///
+ /// @return true if message is accepted for further processing, false
+ /// otherwise.
+ bool acceptMessageType(const Pkt4Ptr& query) const;
+
/// @brief Verifies if the server id belongs to our server.
///
/// This function checks if the server identifier carried in the specified
@@ -181,6 +258,7 @@ protected:
/// @return true, if the server identifier is absent or matches one of the
/// server identifiers that the server is using; false otherwise.
bool acceptServerId(const Pkt4Ptr& pkt) const;
+ //@}
/// @brief verifies if specified packet meets RFC requirements
///
@@ -529,7 +607,7 @@ protected:
///
/// @param question client's message
/// @return selected subnet (or NULL if no suitable subnet was found)
- isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question);
+ isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question) const;
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index 91b5a3b..f7fb5cc 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -80,6 +80,7 @@ dhcp4_unittests_SOURCES += dhcp4_test_utils.h
dhcp4_unittests_SOURCES += dhcp4_unittests.cc
dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += dhcp4_test_utils.cc dhcp4_test_utils.h
+dhcp4_unittests_SOURCES += direct_client_unittest.cc
dhcp4_unittests_SOURCES += wireshark.cc
dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += config_parser_unittest.cc
@@ -95,6 +96,7 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index e930ba0..b8f6a24 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -29,6 +29,7 @@
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/config_parser.h>
@@ -61,12 +62,15 @@ namespace {
// 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(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create the instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
// 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->setGiaddr(IOAddress("192.0.1.1"));
req->setHops(2);
// Set ciaddr to zero. This simulates the client which applies
// for the new lease.
@@ -75,16 +79,16 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
req->setFlags(0x0000);
// Set local address, port and interface.
- req->setLocalAddr(IOAddress("192.0.3.1"));
+ req->setLocalAddr(IOAddress("192.0.2.1"));
req->setLocalPort(1001);
- req->setIface("eth0");
+ req->setIface("eth1");
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.
boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
- resp->setYiaddr(IOAddress("192.0.2.100"));
+ resp->setYiaddr(IOAddress("192.0.1.100"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
// Set hops value for the response.
@@ -94,40 +98,43 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
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());
+ EXPECT_EQ("192.0.1.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());
+ // Local address should be the address assigned to interface eth1.
+ EXPECT_EQ("192.0.2.3", 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("eth1", 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
// address will take precedence.
- req->setGiaddr(IOAddress("192.0.2.50"));
- req->setCiaddr(IOAddress("192.0.2.11"));
+ req->setGiaddr(IOAddress("192.0.1.50"));
+ req->setCiaddr(IOAddress("192.0.1.11"));
req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
- resp->setYiaddr(IOAddress("192.0.2.100"));
+ resp->setYiaddr(IOAddress("192.0.1.100"));
// Clear remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
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());
+ EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
}
// This test verifies that the destination address of the response message
// 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(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
@@ -136,7 +143,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// Set ciaddr to non-zero address. The response should be sent to this
// address as the client is in renewing or rebinding state (it is fully
// configured).
- req->setCiaddr(IOAddress("192.0.2.15"));
+ req->setCiaddr(IOAddress("192.0.1.15"));
// Let's configure broadcast flag. It should be ignored because
// we are responding directly to the client having an address
// and trying to extend his lease. Broadcast flag is only used
@@ -147,11 +154,11 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// 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"));
+ req->setLocalAddr(IOAddress("192.0.2.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->setIface("eth1");
req->setIndex(1);
// Create a response.
@@ -160,7 +167,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// it will actually get different address. The response
// should not be sent to this address but rather to ciaddr
// as client still have ciaddr configured.
- resp->setYiaddr(IOAddress("192.0.2.13"));
+ resp->setYiaddr(IOAddress("192.0.1.13"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
// Copy hops value from the query.
@@ -169,17 +176,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
// Check that server responds to ciaddr
- EXPECT_EQ("192.0.2.15", resp->getRemoteAddr().toText());
+ EXPECT_EQ("192.0.1.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());
+ EXPECT_EQ("192.0.2.3", 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("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
}
@@ -191,7 +198,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// 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(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
@@ -210,13 +220,13 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// 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->setIface("eth1");
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"));
+ resp->setYiaddr(IOAddress("192.0.1.13"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
// Copy hops count.
@@ -227,7 +237,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// 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;
+ test_config.setDirectResponse(false);
// When running unit tests, the IfaceMgr is using the default Packet
// Filtering class, PktFilterInet. This class does not support direct
@@ -244,14 +254,14 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// 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());
+ EXPECT_EQ("192.0.2.3", 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("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
// We also want to test the case when the server has capability to
@@ -260,13 +270,13 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// 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;
+ test_config.setDirectResponse(true);
// Now we expect that the server will send its response to the
// address assigned for the client.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
- EXPECT_EQ("192.0.2.13", resp->getRemoteAddr().toText());
+ EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText());
}
// This test verifies that the destination address of the response message
@@ -274,7 +284,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// 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(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
@@ -287,7 +300,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
// 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->setIface("eth1");
req->setIndex(1);
// Let's set the broadcast flag.
@@ -296,7 +309,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
// 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"));
+ resp->setYiaddr(IOAddress("192.0.1.13"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
@@ -310,21 +323,21 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
// 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());
+ EXPECT_EQ("192.0.2.3", 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("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
}
// This test verifies that exception is thrown of the invalid combination
// of giaddr and hops is specified in a client's message.
-TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataInvalid) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataInvalid) {
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
// The hops and giaddr values are used to determine if the client's
@@ -435,7 +448,7 @@ TEST_F(Dhcpv4SrvTest, openActiveSockets) {
// are other tests that verify correctness of the allocation
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvFakeIfaceTest, processDiscover) {
+TEST_F(Dhcpv4SrvTest, processDiscover) {
testDiscoverRequest(DHCPDISCOVER);
}
@@ -447,11 +460,11 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, processDiscover) {
// are other tests that verify correctness of the allocation
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvFakeIfaceTest, processRequest) {
+TEST_F(Dhcpv4SrvTest, processRequest) {
testDiscoverRequest(DHCPREQUEST);
}
-TEST_F(Dhcpv4SrvFakeIfaceTest, processRelease) {
+TEST_F(Dhcpv4SrvTest, processRelease) {
NakedDhcpv4Srv srv;
Pkt4Ptr pkt(new Pkt4(DHCPRELEASE, 1234));
@@ -459,7 +472,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, processRelease) {
EXPECT_NO_THROW(srv.processRelease(pkt));
}
-TEST_F(Dhcpv4SrvFakeIfaceTest, processDecline) {
+TEST_F(Dhcpv4SrvTest, processDecline) {
NakedDhcpv4Srv srv;
Pkt4Ptr pkt(new Pkt4(DHCPDECLINE, 1234));
@@ -467,7 +480,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, processDecline) {
EXPECT_NO_THROW(srv.processDecline(pkt));
}
-TEST_F(Dhcpv4SrvFakeIfaceTest, processInform) {
+TEST_F(Dhcpv4SrvTest, processInform) {
NakedDhcpv4Srv srv;
Pkt4Ptr pkt(new Pkt4(DHCPINFORM, 1234));
@@ -524,7 +537,10 @@ TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) {
// - copy of client-id
// - server-id
// - offered address
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
+TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -532,7 +548,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -562,7 +578,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
// - copy of client-id
// - server-id
// - offered address
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
+TEST_F(Dhcpv4SrvTest, DiscoverHint) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("192.0.2.107");
@@ -572,7 +591,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setYiaddr(hint);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -603,7 +622,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
// - copy of client-id
// - server-id
// - offered address
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
+TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("192.0.2.107");
@@ -612,7 +634,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
dis->setRemoteAddr(IOAddress("192.0.2.1"));
dis->setYiaddr(hint);
dis->setHWAddr(generateHWAddr(6));
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -642,7 +664,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
// - copy of client-id
// - server-id
// - offered address (!= hint)
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
+TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("10.1.2.3");
@@ -652,7 +677,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setYiaddr(hint);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -681,7 +706,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
// and this is a correct behavior. It is REQUEST that will fail for the third
// client. OFFER is basically saying "if you send me a request, you will
// probably get an address like this" (there are no guarantees).
-TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
+TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -694,9 +722,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
dis3->setRemoteAddr(IOAddress("192.0.2.3"));
// Assign interfaces
- dis1->setIface("eth0");
- dis2->setIface("eth0");
- dis3->setIface("eth0");
+ dis1->setIface("eth1");
+ dis2->setIface("eth1");
+ dis3->setIface("eth1");
// Different client-id sizes
OptionPtr clientid1 = generateClientId(4); // length 4
@@ -746,14 +774,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
// Checks whether echoing back client-id is controllable, i.e.
// whether the server obeys echo-client-id and sends (or not)
// client-id
-TEST_F(Dhcpv4SrvFakeIfaceTest, discoverEchoClientId) {
+TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
NakedDhcpv4Srv srv(0);
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv.processDiscover(dis);
@@ -785,7 +816,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, discoverEchoClientId) {
// - assigned address
//
// Test verifies that the lease is actually in the database.
-TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
+TEST_F(Dhcpv4SrvTest, RequestBasic) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -795,7 +829,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
OptionPtr clientid = generateClientId();
req->addOption(clientid);
req->setYiaddr(hint);
- req->setIface("eth0");
+ req->setIface("eth1");
// Pass it to the server and get an advertise
Pkt4Ptr ack = srv->processRequest(req);
@@ -831,7 +865,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
// - copy of client-id
// - server-id
// - assigned address (different for each client)
-TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
+TEST_F(Dhcpv4SrvTest, ManyRequests) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -850,9 +886,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
req3->setRemoteAddr(relay);
// Assign interfaces
- req1->setIface("eth0");
- req2->setIface("eth0");
- req3->setIface("eth0");
+ req1->setIface("eth1");
+ req2->setIface("eth1");
+ req3->setIface("eth1");
req1->setYiaddr(req_addr1);
req2->setYiaddr(req_addr2);
@@ -919,14 +955,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
}
// Checks whether echoing back client-id is controllable
-TEST_F(Dhcpv4SrvFakeIfaceTest, requestEchoClientId) {
+TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
NakedDhcpv4Srv srv(0);
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get ACK
Pkt4Ptr ack = srv.processRequest(dis);
@@ -953,7 +992,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, requestEchoClientId) {
// - returned REPLY message has server-id
// - returned REPLY message has IA that includes IAADDR
// - lease is actually renewed in LeaseMgr
-TEST_F(Dhcpv4SrvFakeIfaceTest, RenewBasic) {
+TEST_F(Dhcpv4SrvTest, RenewBasic) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -1038,7 +1080,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RenewBasic) {
// identifiers used by a server is accepted,
// - a message with a server identifier which doesn't match any server
// identifier used by a server, is not accepted.
-TEST_F(Dhcpv4SrvFakeIfaceTest, acceptServerId) {
+TEST_F(Dhcpv4SrvTest, acceptServerId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
NakedDhcpv4Srv srv(0);
Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 1234));
@@ -1065,7 +1110,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, acceptServerId) {
// Add a server id being an IPv4 address configured on eth0 interface.
// A DHCPv4 message holding this server identifier should be accepted.
OptionCustomPtr eth0_serverid(new OptionCustom(def, Option::V6));
- eth0_serverid->writeAddress(IOAddress("192.0.3.1"));
+ eth0_serverid->writeAddress(IOAddress("192.0.2.3"));
ASSERT_NO_THROW(pkt->addOption(eth0_serverid));
EXPECT_TRUE(srv.acceptServerId(pkt));
@@ -1117,7 +1162,7 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
// This test verifies that incoming (positive) RELEASE can be handled properly.
// As there is no REPLY in DHCPv4, the only thing to verify here is that
// the lease is indeed removed from the database.
-TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseBasic) {
+TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -1189,7 +1234,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseBasic) {
// 1. there is no such lease at all
// 2. there is such a lease, but it is assigned to a different IAID
// 3. there is such a lease, but it belongs to a different client
-TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseReject) {
+TEST_F(Dhcpv4SrvTest, ReleaseReject) {
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -1278,7 +1323,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseReject) {
}
// Checks if received relay agent info option is echoed back to the client
-TEST_F(Dhcpv4SrvFakeIfaceTest, relayAgentInfoEcho) {
+TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1321,7 +1368,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, relayAgentInfoEcho) {
// Checks if vendor options are parsed correctly and requested vendor options
// are echoed back.
-TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsis) {
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1515,7 +1564,10 @@ TEST_F(Dhcpv4SrvTest, unpackOptions) {
// Checks whether the server uses default (0.0.0.0) siaddr value, unless
// explicitly specified
-TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
+TEST_F(Dhcpv4SrvTest, siaddrDefault) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("192.0.2.107");
@@ -1525,7 +1577,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setYiaddr(hint);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -1539,14 +1591,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
}
// Checks whether the server uses specified siaddr value
-TEST_F(Dhcpv4SrvFakeIfaceTest, siaddr) {
+TEST_F(Dhcpv4SrvTest, siaddr) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
subnet_->setSiaddr(IOAddress("192.0.2.123"));
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
- dis->setIface("eth0");
+ dis->setIface("eth1");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
@@ -1565,7 +1620,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, siaddr) {
// specific value and returned in server messages. There's also similar test for
// checking parser only configuration, see Dhcp4ParserTest.nextServerOverride in
// config_parser_unittest.cc.
-TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
+TEST_F(Dhcpv4SrvTest, nextServerOverride) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1592,7 +1649,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
- dis->setIface("eth0");
+ dis->setIface("eth1");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
@@ -1608,7 +1665,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
// when there is no specific value defined in subnet and returned to the client
// properly. There's also similar test for checking parser only configuration,
// see Dhcp4ParserTest.nextServerGlobal in config_parser_unittest.cc.
-TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerGlobal) {
+TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1634,7 +1693,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerGlobal) {
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
- dis->setIface("eth0");
+ dis->setIface("eth1");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
@@ -1671,7 +1730,7 @@ const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
/// can't modify non-static members (for obvious reasons), so many
/// fields are declared static. It is still better to keep them as
/// one class rather than unrelated collection of global objects.
-class HooksDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
+class HooksDhcpv4SrvTest : public Dhcpv4SrvTest {
public:
@@ -1766,7 +1825,13 @@ public:
buf.push_back(1); // length (just one byte)
buf.push_back(static_cast<uint8_t>(DHCPDISCOVER));
- return (Pkt4Ptr(new Pkt4(&buf[0], buf.size())));
+ Pkt4Ptr dis(new Pkt4(&buf[0], buf.size()));
+ // Interface must be selected for a Discover. Server will use the interface
+ // name to select a subnet for a client. This test is using fake interfaces
+ // and the fake eth0 interface has IPv4 address matching the subnet
+ // currently configured for this test.
+ dis->setIface("eth1");
+ return (dis);
}
/// Test callback that stores received callout name and pkt4 value
@@ -2126,6 +2191,8 @@ vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
// Note that the test name does not follow test naming convention,
// but the proper hook name is "buffer4_receive".
TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2159,6 +2226,9 @@ TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
// Checks if callouts installed on buffer4_receive is able to change
// the values and the parameters are indeed used by the server.
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(
@@ -2198,6 +2268,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
// the server should eventually drop it, because there won't be mandatory options
// (or rather option objects) in it.
TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2225,6 +2297,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
// Note that the test name does not follow test naming convention,
// but the proper hook name is "pkt4_receive".
TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2258,6 +2332,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
// Checks if callouts installed on pkt4_received is able to change
// the values and the parameters are indeed used by the server.
TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2294,6 +2370,8 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
// existing options and that change impacts server processing (mandatory
// client-id option is deleted, so the packet is expected to be dropped)
TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2318,6 +2396,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
// Checks if callouts installed on pkt4_received is able to set skip flag that
// will cause the server to not process the packet (drop), even though it is valid.
TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2343,6 +2423,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
// Checks if callouts installed on pkt4_send are indeed called and the
// all necessary parameters are passed.
TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2379,6 +2461,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
// Checks if callouts installed on pkt4_send is able to change
// the values and the packet sent contains those changes
TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2416,6 +2500,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
// we are trying to send a packet without server-id. The packet should
// be sent
TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2447,6 +2533,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
// Checks if callouts installed on pkt4_skip is able to set skip flag that
// will cause the server to not process the packet (drop), even though it is valid.
TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2476,6 +2564,8 @@ TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
// Checks if callouts installed on buffer4_send are indeed called and the
// all necessary parameters are passed.
TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2512,6 +2602,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
// Checks if callouts installed on buffer4_send are indeed called and that
// the output buffer can be changed.
TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2541,6 +2633,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
// Checks if callouts installed on buffer4_send can set skip flag and that flag
// causes the packet to not be sent
TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2566,6 +2660,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
// This test checks if subnet4_select callout is triggered and reports
// valid parameters
TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2598,7 +2694,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
// Prepare discover packet. Server should select first subnet for it
Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
sol->setRemoteAddr(IOAddress("192.0.2.1"));
- sol->setIface("eth0");
+ sol->setIface("eth1");
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
@@ -2632,6 +2728,8 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
// This test checks if callout installed on subnet4_select hook point can pick
// a different subnet.
TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install a callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2692,6 +2790,8 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
// properly and that callout installed on lease4_renew is triggered with
// expected parameters.
TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2777,6 +2877,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
// This test verifies that a callout installed on lease4_renew can trigger
// the server to not renew a lease.
TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2842,6 +2944,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
// This test verifies that valid RELEASE triggers lease4_release callouts
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2927,6 +3031,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
// This test verifies that skip flag returned by a callout installed on the
// lease4_release hook point will keep the lease
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2990,7 +3096,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
}
// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
-TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorOptionsParse) {
+TEST_F(Dhcpv4SrvTest, docsisVendorOptionsParse) {
// Let's get a traffic capture from DOCSIS3.0 modem
Pkt4Ptr dis = captureRelayedDiscover();
@@ -3014,7 +3120,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorOptionsParse) {
}
// Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO)
-TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorORO) {
+TEST_F(Dhcpv4SrvTest, docsisVendorORO) {
// Let's get a traffic capture from DOCSIS3.0 modem
Pkt4Ptr dis = captureRelayedDiscover();
@@ -3036,7 +3142,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorORO) {
// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
// vendor options is parsed correctly and the requested options are actually assigned.
-TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsORO) {
+TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -3124,7 +3232,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsORO) {
// Test checks whether it is possible to use option definitions defined in
// src/lib/dhcp/docsis3_option_defs.h.
-TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsisDefinitions) {
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
ConstElementPtr x;
string config_prefix = "{ \"interfaces\": [ \"all\" ],"
"\"rebind-timer\": 2000, "
@@ -3201,4 +3309,98 @@ TEST_F(Dhcpv4SrvTest, clientClassification) {
EXPECT_FALSE(dis2->inClass("docsis3.0"));
}
+// This test verifies that the direct message is dropped when it has been
+// received by the server via an interface for which there is no subnet
+// configured. It also checks that the message is not dropped (is processed)
+// when it is relayed or unicast.
+TEST_F(Dhcpv4SrvTest, acceptDirectRequest) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ NakedDhcpv4Srv srv(0);
+
+ Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1234));
+ // Set Giaddr and local server's unicast address, but don't set hops.
+ // Hops value must be greater than 0, when giaddr is set. Otherwise,
+ // message is considered malformed and the accept() function should
+ // return false.
+ pkt->setGiaddr(IOAddress("192.0.10.1"));
+ pkt->setLocalAddr(IOAddress("192.0.2.3"));
+ pkt->setIface("eth1");
+ EXPECT_FALSE(srv.accept(pkt));
+
+ // Let's set hops and check that the message is now accepted as
+ // a relayed message.
+ pkt->setHops(1);
+ EXPECT_TRUE(srv.accept(pkt));
+
+ // Make it a direct message but keep unicast server's address. The
+ // messages sent to unicast address should be accepted as they are
+ // most likely to renew existing leases. The server should respond
+ // to renews so they have to be accepted and processed.
+ pkt->setHops(0);
+ pkt->setGiaddr(IOAddress("0.0.0.0"));
+ EXPECT_TRUE(srv.accept(pkt));
+
+ // Direct message is now sent to a broadcast address. The server
+ // should accept this message because it has been received via
+ // eth1 for which there is a subnet configured (see test fixture
+ // class constructor).
+ pkt->setLocalAddr(IOAddress("255.255.255.255"));
+ EXPECT_TRUE(srv.accept(pkt));
+
+ // For eth0, there is no subnet configured. Such message is expected
+ // to be silently dropped.
+ pkt->setIface("eth0");
+ EXPECT_FALSE(srv.accept(pkt));
+
+ // But, if the message is unicast it should be accepted, even though
+ // it has been received via eth0.
+ pkt->setLocalAddr(IOAddress("10.0.0.1"));
+ EXPECT_TRUE(srv.accept(pkt));
+
+}
+
+// This test checks that the server rejects a message with invalid type.
+TEST_F(Dhcpv4SrvTest, acceptMessageType) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ NakedDhcpv4Srv srv(0);
+
+ // Specify messages to be accepted by the server.
+ int allowed[] = {
+ DHCPDISCOVER,
+ DHCPREQUEST,
+ DHCPRELEASE,
+ DHCPDECLINE,
+ DHCPINFORM
+ };
+ size_t allowed_size = sizeof(allowed) / sizeof(allowed[0]);
+ // Check that the server actually accepts these message types.
+ for (int i = 0; i < allowed_size; ++i) {
+ EXPECT_TRUE(srv.acceptMessageType(Pkt4Ptr(new Pkt4(allowed[i], 1234))))
+ << "Test failed for message type " << i;
+ }
+ // Specify messages which server is supposed to drop.
+ int not_allowed[] = {
+ DHCPOFFER,
+ DHCPACK,
+ DHCPNAK,
+ DHCPLEASEQUERY,
+ DHCPLEASEUNASSIGNED,
+ DHCPLEASEUNKNOWN,
+ DHCPLEASEACTIVE,
+ DHCPBULKLEASEQUERY,
+ DHCPLEASEQUERYDONE
+ };
+ size_t not_allowed_size = sizeof(not_allowed) / sizeof(not_allowed[0]);
+ // Actually check that the server will drop these messages.
+ for (int i = 0; i < not_allowed_size; ++i) {
+ EXPECT_FALSE(srv.acceptMessageType(Pkt4Ptr(new Pkt4(not_allowed[i],
+ 1234))))
+ << "Test failed for message type " << i;
+ }
+}
+
}; // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
index e983e7b..ab1afc6 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -15,12 +15,15 @@
#include <config.h>
#include <asiolink/io_address.h>
+#include <cc/data.h>
#include <config/ccsession.h>
+#include <dhcp4/config_parser.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_custom.h>
#include <dhcp/iface_mgr.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/lease_mgr.h>
@@ -28,17 +31,14 @@
using namespace std;
using namespace isc::asiolink;
-
+using namespace isc::data;
namespace isc {
namespace dhcp {
namespace test {
-/// dummy server-id file location
-static const char* SRVID_FILE = "server-id-test.txt";
-
Dhcpv4SrvTest::Dhcpv4SrvTest()
-:rcode_(-1) {
+:rcode_(-1), srv_(0) {
subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
2000, 3000));
pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
@@ -52,9 +52,6 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
opt_routers->setAddress(IOAddress("192.0.2.2"));
subnet_->addOption(opt_routers, false, "dhcp4");
-
- // it's ok if that fails. There should not be such a file anyway
- unlink(SRVID_FILE);
}
Dhcpv4SrvTest::~Dhcpv4SrvTest() {
@@ -387,9 +384,6 @@ void Dhcpv4SrvTest::TearDown() {
CfgMgr::instance().deleteSubnets4();
- // Let's clean up if there is such a file.
- unlink(SRVID_FILE);
-
// Close all open sockets.
IfaceMgr::instance().closeSockets();
@@ -413,58 +407,11 @@ void Dhcpv4SrvTest::TearDown() {
}
-Dhcpv4SrvFakeIfaceTest::Dhcpv4SrvFakeIfaceTest()
-: Dhcpv4SrvTest(), current_pkt_filter_() {
- // Remove current interface configuration. Instead we want to add
- // a couple of fake interfaces.
- IfaceMgr& ifacemgr = IfaceMgr::instance();
- ifacemgr.closeSockets();
- ifacemgr.clearIfaces();
-
- // Add fake interfaces.
- ifacemgr.addInterface(createIface("lo", 0, "127.0.0.1"));
- ifacemgr.addInterface(createIface("eth0", 1, "192.0.3.1"));
- ifacemgr.addInterface(createIface("eth1", 2, "10.0.0.1"));
-
- // In order to use fake interfaces we have to supply the custom
- // packet filtering class, which can mimic opening sockets on
- // fake interafaces.
- current_pkt_filter_.reset(new PktFilterTest());
- ifacemgr.setPacketFilter(current_pkt_filter_);
- ifacemgr.openSockets4();
-}
-
void
-Dhcpv4SrvFakeIfaceTest::TearDown() {
- // The base class function restores the original packet filtering class.
- Dhcpv4SrvTest::TearDown();
- // The base class however, doesn't re-detect real interfaces.
- try {
- IfaceMgr::instance().clearIfaces();
- IfaceMgr::instance().detectIfaces();
+Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
- } catch (const Exception& ex) {
- FAIL() << "Failed to restore interface configuration after using"
- " fake interfaces";
- }
-}
-
-Iface
-Dhcpv4SrvFakeIfaceTest::createIface(const std::string& name, const int ifindex,
- const std::string& addr) {
- Iface iface(name, ifindex);
- iface.addAddress(IOAddress(addr));
- if (name == "lo") {
- iface.flag_loopback_ = true;
- }
- iface.flag_up_ = true;
- iface.flag_running_ = true;
- iface.inactive4_ = false;
- return (iface);
-}
-
-void
-Dhcpv4SrvFakeIfaceTest::testDiscoverRequest(const uint8_t msg_type) {
// Create an instance of the tested class.
boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
@@ -608,6 +555,20 @@ Dhcpv4SrvFakeIfaceTest::testDiscoverRequest(const uint8_t msg_type) {
EXPECT_TRUE(noBasicOptions(rsp));
}
+void
+Dhcpv4SrvTest::configure(const std::string& config) {
+ ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ EXPECT_NO_THROW(status = configureDhcp4Server(srv_, json));
+ ASSERT_TRUE(status);
+ int rcode;
+ ConstElementPtr comment = config::parseAnswer(rcode, status);
+ ASSERT_EQ(0, rcode);
+}
+
+
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index 8a22117..3f7c326 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -87,6 +87,120 @@ public:
typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;
+/// @brief "Naked" DHCPv4 server, exposes internal fields
+class NakedDhcpv4Srv: public Dhcpv4Srv {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// This constructor disables default modes of operation used by the
+ /// Dhcpv4Srv class:
+ /// - Send/receive broadcast messages through sockets on interfaces
+ /// which support broadcast traffic.
+ /// - Direct DHCPv4 traffic - communication with clients which do not
+ /// have IP address assigned yet.
+ ///
+ /// Enabling these modes requires root privilges so they must be
+ /// disabled for unit testing.
+ ///
+ /// Note, that disabling broadcast options on sockets does not impact
+ /// the operation of these tests because they use local loopback
+ /// interface which doesn't have broadcast capability anyway. It rather
+ /// prevents setting broadcast options on other (broadcast capable)
+ /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
+ ///
+ /// The Direct DHCPv4 Traffic capability can be disabled here because
+ /// it is tested with PktFilterLPFTest unittest. The tests which belong
+ /// to PktFilterLPFTest can be enabled on demand when root privileges can
+ /// be guaranteed.
+ ///
+ /// @param port port number to listen on; the default value 0 indicates
+ /// that sockets should not be opened.
+ NakedDhcpv4Srv(uint16_t port = 0)
+ : Dhcpv4Srv(port, "type=memfile", false, false) {
+ // Create fixed server id.
+ server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
+ asiolink::IOAddress("192.0.3.1")));
+ }
+
+ /// @brief Returns fixed server identifier assigned to the naked server
+ /// instance.
+ OptionPtr getServerID() const {
+ return (server_id_);
+ }
+
+ /// @brief fakes packet reception
+ /// @param timeout ignored
+ ///
+ /// The method receives all packets queued in receive queue, one after
+ /// another. Once the queue is empty, it initiates the shutdown procedure.
+ ///
+ /// See fake_received_ field for description
+ virtual Pkt4Ptr receivePacket(int /*timeout*/) {
+
+ // If there is anything prepared as fake incoming traffic, use it
+ if (!fake_received_.empty()) {
+ Pkt4Ptr pkt = fake_received_.front();
+ fake_received_.pop_front();
+ return (pkt);
+ }
+
+ // If not, just trigger shutdown and return immediately
+ shutdown();
+ return (Pkt4Ptr());
+ }
+
+ /// @brief fake packet sending
+ ///
+ /// Pretend to send a packet, but instead just store it in fake_send_ list
+ /// where test can later inspect server's response.
+ virtual void sendPacket(const Pkt4Ptr& pkt) {
+ fake_sent_.push_back(pkt);
+ }
+
+ /// @brief adds a packet to fake receive queue
+ ///
+ /// See fake_received_ field for description
+ void fakeReceive(const Pkt4Ptr& pkt) {
+ fake_received_.push_back(pkt);
+ }
+
+ virtual ~NakedDhcpv4Srv() {
+ }
+
+ /// @brief Dummy server identifier option used by various tests.
+ OptionPtr server_id_;
+
+ /// @brief packets we pretend to receive
+ ///
+ /// Instead of setting up sockets on interfaces that change between OSes, it
+ /// is much easier to fake packet reception. This is a list of packets that
+ /// we pretend to have received. You can schedule new packets to be received
+ /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
+ std::list<Pkt4Ptr> fake_received_;
+
+ std::list<Pkt4Ptr> fake_sent_;
+
+ using Dhcpv4Srv::adjustIfaceData;
+ using Dhcpv4Srv::appendServerID;
+ using Dhcpv4Srv::processDiscover;
+ using Dhcpv4Srv::processRequest;
+ using Dhcpv4Srv::processRelease;
+ using Dhcpv4Srv::processDecline;
+ using Dhcpv4Srv::processInform;
+ using Dhcpv4Srv::processClientName;
+ using Dhcpv4Srv::computeDhcid;
+ using Dhcpv4Srv::createNameChangeRequests;
+ using Dhcpv4Srv::acceptServerId;
+ using Dhcpv4Srv::sanityCheck;
+ using Dhcpv4Srv::srvidToString;
+ using Dhcpv4Srv::unpackOptions;
+ using Dhcpv4Srv::name_change_reqs_;
+ using Dhcpv4Srv::classifyPacket;
+ using Dhcpv4Srv::accept;
+ using Dhcpv4Srv::acceptMessageType;
+};
+
class Dhcpv4SrvTest : public ::testing::Test {
public:
@@ -267,61 +381,6 @@ public:
/// @return created packet
Pkt4Ptr packetFromCapture(const std::string& hex_string);
- /// @brief This function cleans up after the test.
- virtual void TearDown();
-
- /// @brief A subnet used in most tests
- Subnet4Ptr subnet_;
-
- /// @brief A pool used in most tests
- Pool4Ptr pool_;
-
- /// @brief A client-id used in most tests
- ClientIdPtr client_id_;
-
- int rcode_;
-
- isc::data::ConstElementPtr comment_;
-
-};
-
-/// @brief Test fixture class to be used for tests which require fake
-/// interfaces.
-///
-/// The DHCPv4 server must always append the server identifier to its response.
-/// The server identifier is typically an IP address assigned to the interface
-/// on which the query has been received. The DHCPv4 server uses IfaceMgr to
-/// check this address. In order to test this functionality, a set of interfaces
-/// must be known to the test. This test fixture class creates a set of well
-/// known (fake) interfaces which can be assigned to the test DHCPv4 messages
-/// so as the response (including server identifier) can be validated.
-/// The real interfaces are removed from the IfaceMgr in the constructor and
-/// they are re-assigned in the destructor.
-class Dhcpv4SrvFakeIfaceTest : public Dhcpv4SrvTest {
-public:
- /// @brief Constructor.
- ///
- /// Creates a set of fake interfaces:
- /// - lo, index: 0, address: 127.0.0.1
- /// - eth0, index: 1, address: 192.0.3.1
- /// - eth1, index: 2, address: 10.0.0.1
- ///
- /// These interfaces replace the real interfaces detected by the IfaceMgr.
- Dhcpv4SrvFakeIfaceTest();
-
- /// @brief Restores the original interface configuration.
- virtual void TearDown();
-
- /// @brief Creates an instance of the interface.
- ///
- /// @param name Name of the interface.
- /// @param ifindex Index of the interface.
- /// @param addr IP address assigned to the interface, represented as string.
- ///
- /// @return Iface Instance of the interface.
- static Iface createIface(const std::string& name, const int ifindex,
- const std::string& addr);
-
/// @brief Tests if Discover or Request message is processed correctly
///
/// This test verifies that the Parameter Request List option is handled
@@ -335,123 +394,30 @@ 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_;
-
-};
-
-/// @brief "Naked" DHCPv4 server, exposes internal fields
-class NakedDhcpv4Srv: public Dhcpv4Srv {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor disables default modes of operation used by the
- /// Dhcpv4Srv class:
- /// - Send/receive broadcast messages through sockets on interfaces
- /// which support broadcast traffic.
- /// - Direct DHCPv4 traffic - communication with clients which do not
- /// have IP address assigned yet.
- ///
- /// Enabling these modes requires root privilges so they must be
- /// disabled for unit testing.
- ///
- /// Note, that disabling broadcast options on sockets does not impact
- /// the operation of these tests because they use local loopback
- /// interface which doesn't have broadcast capability anyway. It rather
- /// prevents setting broadcast options on other (broadcast capable)
- /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
- ///
- /// The Direct DHCPv4 Traffic capability can be disabled here because
- /// it is tested with PktFilterLPFTest unittest. The tests which belong
- /// to PktFilterLPFTest can be enabled on demand when root privileges can
- /// be guaranteed.
+ /// @brief Runs DHCPv4 configuration from the JSON string.
///
- /// @param port port number to listen on; the default value 0 indicates
- /// that sockets should not be opened.
- NakedDhcpv4Srv(uint16_t port = 0)
- : Dhcpv4Srv(port, "type=memfile", false, false) {
- // Create fixed server id.
- server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
- asiolink::IOAddress("192.0.3.1")));
- }
-
- /// @brief Returns fixed server identifier assigned to the naked server
- /// instance.
- OptionPtr getServerID() const {
- return (server_id_);
- }
+ /// @param config String holding server configuration in JSON format.
+ void configure(const std::string& config);
- /// @brief fakes packet reception
- /// @param timeout ignored
- ///
- /// The method receives all packets queued in receive queue, one after
- /// another. Once the queue is empty, it initiates the shutdown procedure.
- ///
- /// See fake_received_ field for description
- virtual Pkt4Ptr receivePacket(int /*timeout*/) {
-
- // If there is anything prepared as fake incoming traffic, use it
- if (!fake_received_.empty()) {
- Pkt4Ptr pkt = fake_received_.front();
- fake_received_.pop_front();
- return (pkt);
- }
-
- // If not, just trigger shutdown and return immediately
- shutdown();
- return (Pkt4Ptr());
- }
+ /// @brief This function cleans up after the test.
+ virtual void TearDown();
- /// @brief fake packet sending
- ///
- /// Pretend to send a packet, but instead just store it in fake_send_ list
- /// where test can later inspect server's response.
- virtual void sendPacket(const Pkt4Ptr& pkt) {
- fake_sent_.push_back(pkt);
- }
+ /// @brief A subnet used in most tests
+ Subnet4Ptr subnet_;
- /// @brief adds a packet to fake receive queue
- ///
- /// See fake_received_ field for description
- void fakeReceive(const Pkt4Ptr& pkt) {
- pkt->setIface("eth0");
- fake_received_.push_back(pkt);
- }
+ /// @brief A pool used in most tests
+ Pool4Ptr pool_;
- virtual ~NakedDhcpv4Srv() {
- }
+ /// @brief A client-id used in most tests
+ ClientIdPtr client_id_;
- /// @brief Dummy server identifier option used by various tests.
- OptionPtr server_id_;
+ int rcode_;
- /// @brief packets we pretend to receive
- ///
- /// Instead of setting up sockets on interfaces that change between OSes, it
- /// is much easier to fake packet reception. This is a list of packets that
- /// we pretend to have received. You can schedule new packets to be received
- /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
- std::list<Pkt4Ptr> fake_received_;
+ isc::data::ConstElementPtr comment_;
- std::list<Pkt4Ptr> fake_sent_;
+ /// @brief Server object under test.
+ NakedDhcpv4Srv srv_;
- using Dhcpv4Srv::adjustIfaceData;
- using Dhcpv4Srv::appendServerID;
- using Dhcpv4Srv::processDiscover;
- using Dhcpv4Srv::processRequest;
- using Dhcpv4Srv::processRelease;
- using Dhcpv4Srv::processDecline;
- using Dhcpv4Srv::processInform;
- using Dhcpv4Srv::processClientName;
- using Dhcpv4Srv::computeDhcid;
- using Dhcpv4Srv::createNameChangeRequests;
- using Dhcpv4Srv::acceptServerId;
- using Dhcpv4Srv::sanityCheck;
- using Dhcpv4Srv::srvidToString;
- using Dhcpv4Srv::unpackOptions;
- using Dhcpv4Srv::name_change_reqs_;
- using Dhcpv4Srv::classifyPacket;
};
}; // end of isc::dhcp::test namespace
diff --git a/src/bin/dhcp4/tests/direct_client_unittest.cc b/src/bin/dhcp4/tests/direct_client_unittest.cc
new file mode 100644
index 0000000..9a9dbae
--- /dev/null
+++ b/src/bin/dhcp4/tests/direct_client_unittest.cc
@@ -0,0 +1,403 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcp4/config_parser.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Test fixture class for testing message processing from directly
+/// connected clients.
+///
+/// This class provides mechanisms for testing processing of DHCPv4 messages
+/// from directly connected clients.
+class DirectClientTest : public Dhcpv4SrvTest {
+public:
+ /// @brief Constructor.
+ ///
+ /// Initializes DHCPv4 server object used by various tests.
+ DirectClientTest();
+
+ /// @brief Configures the server with one subnet.
+ ///
+ /// This creates new configuration for the DHCPv4 with one subnet having
+ /// a specified prefix.
+ ///
+ /// The subnet parameters (such as options, timers etc.) are aribitrarily
+ /// selected. The subnet and pool mask is always /24. The real configuration
+ /// would exclude .0 (network address) and .255 (broadcast address), but we
+ /// ignore that fact for the sake of test simplicity.
+ ///
+ /// @param prefix Prefix for a subnet.
+ void configureSubnet(const std::string& prefix);
+
+ /// @brief Configures the server with two subnets.
+ ///
+ /// This function configures DHCPv4 server with two different subnets.
+ /// The subnet parameters (such as options, timers etc.) are aribitrarily
+ /// selected. The subnet and pool mask is /24. The real configuration
+ /// would exclude .0 (network address) and .255 (broadcast address), but we
+ /// ignore that fact for the sake of test simplicity.
+ ///
+ /// @param prefix1 Prefix of the first subnet to be configured.
+ /// @param prefix2 Prefix of the second subnet to be configured.
+ void configureTwoSubnets(const std::string& prefix1,
+ const std::string& prefix2);
+
+ /// @brief Creates simple message from a client.
+ ///
+ /// This function creates a DHCPv4 message having a specified type
+ /// (e.g. Discover, Request) and sets some properties of this
+ /// message: client identifier, address and interface. The copy of
+ /// this message is then created by parsing wire data of the original
+ /// message. This simulates the case when the message is received and
+ /// parsed by the server.
+ ///
+ /// @param msg_type Type of the message to be created.
+ /// @param iface Name of the interface on which the message has been
+ /// "received" by the server.
+ ///
+ /// @return Generated message.
+ Pkt4Ptr createClientMessage(const uint16_t msg_type,
+ const std::string& iface);
+
+ /// @brief Creates simple message from a client.
+ ///
+ /// This function configures a client's message by adding client identifier,
+ /// setting interface and addresses. The copy of this message is then
+ /// created by parsing wire data of the original message. This simulates the
+ /// case when the message is received and parsed by the server.
+ ///
+ /// @param msg Caller supplied message to be configured. This object must
+ /// not be NULL.
+ /// @param iface Name of the interface on which the message has been
+ /// "received" by the server.
+ ///
+ /// @return Configured and parsed message.
+ Pkt4Ptr createClientMessage(const Pkt4Ptr &msg, const std::string& iface);
+
+};
+
+DirectClientTest::DirectClientTest() : Dhcpv4SrvTest() {
+}
+
+void
+DirectClientTest::configureSubnet(const std::string& prefix) {
+ std::ostringstream config;
+ config << "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"option-data\": [ ],"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"" << prefix << "/24\" ],"
+ " \"subnet\": \"" << prefix << "/24\", "
+ " \"rebind-timer\": 2000, "
+ " \"renew-timer\": 1000, "
+ " \"valid-lifetime\": 4000"
+ "} ],"
+ "\"valid-lifetime\": 4000 }";
+
+ configure(config.str());
+
+}
+
+void
+DirectClientTest::configureTwoSubnets(const std::string& prefix1,
+ const std::string& prefix2) {
+ std::ostringstream config;
+ config << "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"option-data\": [ ],"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"" << prefix1 << "/24\" ],"
+ " \"subnet\": \"" << prefix1 << "/24\", "
+ " \"rebind-timer\": 2000, "
+ " \"renew-timer\": 1000, "
+ " \"valid-lifetime\": 4000"
+ " },"
+ "{ "
+ " \"pool\": [ \"" << prefix2 << "/24\" ],"
+ " \"subnet\": \"" << prefix2 << "/24\", "
+ " \"rebind-timer\": 2000, "
+ " \"renew-timer\": 1000, "
+ " \"valid-lifetime\": 4000"
+ "} ],"
+ "\"valid-lifetime\": 4000 }";
+
+ configure(config.str());
+}
+
+Pkt4Ptr
+DirectClientTest:: createClientMessage(const uint16_t msg_type,
+ const std::string& iface) {
+ // Create a source packet.
+ Pkt4Ptr msg = Pkt4Ptr(new Pkt4(msg_type, 1234));
+ return (createClientMessage(msg, iface));
+
+}
+
+Pkt4Ptr
+DirectClientTest::createClientMessage(const Pkt4Ptr& msg,
+ const std::string& iface) {
+ msg->setRemoteAddr(IOAddress("255.255.255.255"));
+ msg->addOption(generateClientId());
+ msg->setIface(iface);
+
+ // Create copy of this packet by parsing its wire data. Make sure that the
+ // local and remote address are set like it was a message sent from the
+ // directly connected client.
+ Pkt4Ptr received;
+ createPacketFromBuffer(msg, received);
+ received->setIface(iface);
+ received->setLocalAddr(IOAddress("255.255.255.255"));
+ received->setRemoteAddr(IOAddress("0.0.0.0"));
+
+ return (received);
+}
+
+// This test checks that the message from directly connected client
+// is processed and that client is offered IPv4 address from the subnet which
+// is suitable for the local interface on which the client's message is
+// received. This test uses two subnets, with two active interfaces which IP
+// addresses belong to these subnets. The address offered to the client
+// which message has been sent over eth0 should belong to a different
+// subnet than the address offered for the client sending its message
+// via eth1.
+TEST_F(DirectClientTest, twoSubnets) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add two subnets: address on eth0 belongs to the second subnet,
+ // address on eth1 belongs to the first subnet.
+ ASSERT_NO_FATAL_FAILURE(configureTwoSubnets("192.0.2.0", "10.0.0.0"));
+ // Create Discover and simulate reception of this message through eth0.
+ Pkt4Ptr dis = createClientMessage(DHCPDISCOVER, "eth0");
+ srv_.fakeReceive(dis);
+ // Create Request and simulate reception of this message through eth1.
+ Pkt4Ptr req = createClientMessage(DHCPREQUEST, "eth1");
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server did send reposonses.
+ ASSERT_EQ(2, srv_.fake_sent_.size());
+
+ // Make sure that we received a response.
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+ srv_.fake_sent_.pop_front();
+
+ // Client should get an Offer (not a NAK).
+ ASSERT_EQ(DHCPOFFER, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(response->getYiaddr());
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+ // A client that sent Request over the other interface should get Ack.
+ response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+
+ // Client should get an Ack (not a NAK).
+ ASSERT_EQ(DHCPACK, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ subnet = CfgMgr::instance().getSubnet4(response->getYiaddr());
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("192.0.2.0", subnet->get().first.toText());
+
+}
+
+// This test checks that server selects a subnet when receives a message
+// through an interface for which the subnet has been configured. This
+// interface has IPv4 address assigned which belongs to this subnet.
+// This test also verifies that when the message is received through
+// the interface for which there is no suitable subnet, the message
+// is discarded.
+TEST_F(DirectClientTest, oneSubnet) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add a subnet which will be selected when a message from directly
+ // connected client is received through interface eth0.
+ ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
+ // Create Discover and simulate reception of this message through eth0.
+ Pkt4Ptr dis = createClientMessage(DHCPDISCOVER, "eth0");
+ srv_.fakeReceive(dis);
+ // Create Request and simulate reception of this message through eth1.
+ Pkt4Ptr req = createClientMessage(DHCPDISCOVER, "eth1");
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server sent one response for the message received
+ // through eth0. The other client's message should be dicarded.
+ ASSERT_EQ(1, srv_.fake_sent_.size());
+
+ // Check the response. The first Discover was sent via eth0 for which
+ // the subnet has been configured.
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+ srv_.fake_sent_.pop_front();
+
+ // Since Discover has been received through the interface for which
+ // the subnet has been configured, the server should respond with
+ // an Offer message.
+ ASSERT_EQ(DHCPOFFER, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(response->getYiaddr());
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+}
+
+// This test verifies that the server uses ciaddr to select a subnet for a
+// client which renews its lease.
+TEST_F(DirectClientTest, renew) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add a subnet.
+ ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
+ // Make sure that the subnet has been really added. Also, the subnet
+ // will be needed to create a lease for a client.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("10.0.0.10"));
+ // Create a lease for a client that we will later renewed. By explicitly
+ // creating a lease we will get to know the lease parameters, such as
+ // leased address etc.
+ const uint8_t hwaddr[] = { 1, 2, 3, 4, 5, 6 };
+ Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr, sizeof(hwaddr),
+ &generateClientId()->getData()[0],
+ generateClientId()->getData().size(),
+ 100, 50, 75, time(NULL),
+ subnet->getID()));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ // Create a Request to renew client's lease. The renew request is unicast
+ // through eth1. Note, that in case of renewal the client unicasts its
+ // Request and sets the ciaddr. The server is supposed to use ciaddr to
+ // pick the subnet for the client. In order to make sure that the server
+ // uses ciaddr, we simulate reception of the packet through eth1, for which
+ // there is no subnet for directly connected clients.
+ Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+ req->setCiaddr(IOAddress("10.0.0.10"));
+ req = createClientMessage(req, "eth1");
+ req->setLocalAddr(IOAddress("10.0.0.1"));
+ req->setRemoteAddr(req->getCiaddr());
+
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server did send reposonse.
+ ASSERT_EQ(1, srv_.fake_sent_.size());
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+
+ ASSERT_EQ(DHCPACK, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ subnet = CfgMgr::instance().getSubnet4(response->getYiaddr());
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+}
+
+// This test verifies that when a client in the Rebinding state broadcasts
+// a Request message through an interface for which a subnet is configured,
+// the server responds to this Request. It also verifies that when such a
+// Request is sent through the interface for which there is no subnet configured
+// the client's message is discarded.
+TEST_F(DirectClientTest, rebind) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add a subnet.
+ ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
+ // Make sure that the subnet has been really added. Also, the subnet
+ // will be needed to create a lease for a client.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("10.0.0.10"));
+ // Create a lease, which will be later renewed. By explicitly creating a
+ // lease we will know the lease parameters, such as leased address etc.
+ const uint8_t hwaddr[] = { 1, 2, 3, 4, 5, 6 };
+ Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr, sizeof(hwaddr),
+ &generateClientId()->getData()[0],
+ generateClientId()->getData().size(),
+ 100, 50, 75, time(NULL),
+ subnet->getID()));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ // Broadcast Request through an interface for which there is no subnet
+ // configured. This messag should be discarded by the server.
+ Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+ req->setCiaddr(IOAddress("10.0.0.10"));
+ req = createClientMessage(req, "eth1");
+ req->setRemoteAddr(req->getCiaddr());
+
+ srv_.fakeReceive(req);
+
+ // Broadcast another Request through an interface for which there is
+ // a subnet configured. The server should generate a response.
+ req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 5678));
+ req->setCiaddr(IOAddress("10.0.0.10"));
+ req = createClientMessage(req, "eth0");
+ req->setRemoteAddr(req->getCiaddr());
+
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server did send exactly one reposonse.
+ ASSERT_EQ(1, srv_.fake_sent_.size());
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+
+ // Make sure that the server responsed with ACK, not NAK.
+ ASSERT_EQ(DHCPACK, response->getType());
+ // Make sure that the response is generated for the second Request
+ // (transmitted over eth0).
+ EXPECT_EQ(5678, response->getTransid());
+ // Check that the offered address belongs to the suitable subnet.
+ subnet = CfgMgr::instance().getSubnet4(response->getYiaddr());
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+}
+
+}
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
index e17a7e3..f1cb7b0 100644
--- a/src/bin/dhcp4/tests/fqdn_unittest.cc
+++ b/src/bin/dhcp4/tests/fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -16,6 +16,7 @@
#include <asiolink/io_address.h>
#include <dhcp/option4_client_fqdn.h>
#include <dhcp/option_int_array.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp_ddns/ncr_msg.h>
#include <dhcpsrv/cfgmgr.h>
@@ -31,16 +32,15 @@ using namespace isc::dhcp_ddns;
namespace {
-class NameDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
+class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
public:
-
// Bit Constants for turning on and off DDNS configuration options.
static const uint16_t ALWAYS_INCLUDE_FQDN = 1;
static const uint16_t OVERRIDE_NO_UPDATE = 2;
static const uint16_t OVERRIDE_CLIENT_UPDATE = 4;
static const uint16_t REPLACE_CLIENT_NAME = 8;
- NameDhcpv4SrvTest() : Dhcpv4SrvFakeIfaceTest() {
+ NameDhcpv4SrvTest() : Dhcpv4SrvTest() {
srv_ = new NakedDhcpv4Srv(0);
// Config DDNS to be enabled, all controls off
enableD2();
@@ -154,7 +154,7 @@ public:
const bool include_clientid = true) {
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
pkt->setRemoteAddr(IOAddress("192.0.2.3"));
- pkt->setIface("eth0");
+ pkt->setIface("eth1");
// For DISCOVER we don't include server id, because client broadcasts
// the message to all servers.
if (msg_type != DHCPDISCOVER) {
@@ -357,6 +357,10 @@ public:
/// @param response_flags Mask of expected FQDN flags in the response
void flagVsConfigScenario(const uint8_t client_flags,
const uint8_t response_flags) {
+ // Create fake interfaces and open fake sockets.
+ IfaceMgrTestConfig iface_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, client_flags,
"myhost.example.com.",
Option4ClientFqdn::FULL, true);
@@ -726,6 +730,9 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsLeaseMismatch) {
// Test that the OFFER message generated as a result of the DISCOVER message
// processing will not result in generation of the NameChangeRequests.
TEST_F(NameDhcpv4SrvTest, processDiscover) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithFqdn(DHCPDISCOVER, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"myhost.example.com.",
@@ -741,6 +748,9 @@ TEST_F(NameDhcpv4SrvTest, processDiscover) {
// Test that server generates client's hostname from the IP address assigned
// to it when DHCPv4 Client FQDN option specifies an empty domain-name.
TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"", Option4ClientFqdn::PARTIAL, true);
@@ -767,6 +777,10 @@ TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
// to it when DHCPv4 Client FQDN option specifies an empty domain-name AND
// ddns updates are disabled.
TEST_F(NameDhcpv4SrvTest, processRequestEmptyDomainNameDisabled) {
+ // Create fake interfaces and open fake sockets.
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
disableD2();
Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
@@ -790,10 +804,13 @@ TEST_F(NameDhcpv4SrvTest, processRequestEmptyDomainNameDisabled) {
// Test that server generates client's hostname from the IP address assigned
// to it when Hostname option carries the top level domain-name.
TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithHostname(DHCPREQUEST, ".");
// Set interface for the incoming packet. The server requires it to
// generate client id.
- req->setIface("eth0");
+ req->setIface("eth1");
Pkt4Ptr reply;
ASSERT_NO_THROW(reply = srv_->processRequest(req));
@@ -817,6 +834,9 @@ TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
// request but modify the DNS entries for the lease according to the contents
// of the FQDN sent in the second request.
TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req1 = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"myhost.example.com.",
@@ -869,11 +889,14 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
// but modify the DNS entries for the lease according to the contents of the
// Hostname sent in the second request.
TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com.");
// Set interface for the incoming packet. The server requires it to
// generate client id.
- req1->setIface("eth0");
+ req1->setIface("eth1");
Pkt4Ptr reply;
@@ -896,7 +919,7 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
// Set interface for the incoming packet. The server requires it to
// generate client id.
- req2->setIface("eth0");
+ req2->setIface("eth1");
ASSERT_NO_THROW(reply = srv_->processRequest(req2));
@@ -923,6 +946,9 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
// DDNS updates are enabled that the server genenerates a NameChangeRequest
// to remove entries corresponding to the released lease.
TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Verify the updates are enabled.
ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
@@ -965,6 +991,10 @@ TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
// and DDNS updates are disabled that server does NOT generate a
// NameChangeRequest to remove entries corresponding to the released lease.
TEST_F(NameDhcpv4SrvTest, processRequestReleaseUpdatesDisabled) {
+ // Create fake interfaces and open fake sockets.
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Disable DDNS.
disableD2();
ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index 392478a..2b24405 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2011, 2014 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
@@ -154,7 +154,9 @@ enum DHCPMessageType {
DHCPLEASEQUERY = 10,
DHCPLEASEUNASSIGNED = 11,
DHCPLEASEUNKNOWN = 12,
- DHCPLEASEACTIVE = 13
+ DHCPLEASEACTIVE = 13,
+ DHCPBULKLEASEQUERY = 14,
+ DHCPLEASEQUERYDONE = 15
};
static const uint16_t DHCP4_CLIENT_PORT = 68;
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index a531134..e6aea3d 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -199,6 +199,24 @@ void Iface::addUnicast(const isc::asiolink::IOAddress& addr) {
unicasts_.push_back(addr);
}
+bool
+Iface::getAddress4(isc::asiolink::IOAddress& address) const {
+ // Iterate over existing addresses assigned to the interface.
+ // Try to find the one that is IPv4.
+ const AddressCollection& addrs = getAddresses();
+ for (AddressCollection::const_iterator addr = addrs.begin();
+ addr != addrs.end(); ++addr) {
+ // If address is IPv4, we assign it to the function argument
+ // and return true.
+ if (addr->isV4()) {
+ address = *addr;
+ return (true);
+ }
+ }
+ // There is no IPv4 address assigned to this interface.
+ return (false);
+}
+
void IfaceMgr::closeSockets() {
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end(); ++iface) {
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 12212c7..4007604 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -246,6 +246,17 @@ public:
/// @return collection of addresses
const AddressCollection& getAddresses() const { return addrs_; }
+ /// @brief Returns IPv4 address assigned to the interface.
+ ///
+ /// This function looks for an IPv4 address assigned to the interface
+ /// and returns it through the argument.
+ ///
+ /// @param [out] address IPv4 address assigned to the interface.
+ ///
+ /// @return Boolean value which informs whether IPv4 address has been found
+ /// for the interface (if true), or not (false).
+ bool getAddress4(isc::asiolink::IOAddress& address) const;
+
/// @brief Adds an address to an interface.
///
/// This only adds an address to collection, it does not physically
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index b641a03..fa0e0f9 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -396,6 +396,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
case DHCPRELEASE:
case DHCPINFORM:
case DHCPLEASEQUERY:
+ case DHCPBULKLEASEQUERY:
return (BOOTREQUEST);
case DHCPACK:
@@ -404,6 +405,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
case DHCPLEASEUNASSIGNED:
case DHCPLEASEUNKNOWN:
case DHCPLEASEACTIVE:
+ case DHCPLEASEQUERYDONE:
return (BOOTREPLY);
default:
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 6b3683d..d41246a 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -24,11 +24,29 @@ TESTS_ENVIRONMENT = \
TESTS =
if HAVE_GTEST
+
+# Creates a library which is shared by various unit tests which require
+# configuration of fake interfaces.
+# The libdhcp++ does not link with this library because this would cause
+# build failures being a result of concurrency between build of this
+# library and the unit tests when make -j option was used, as they
+# are built out of the same makefile. Instead, the libdhcp++ tests link to
+# files belonging to this library, directly.
+noinst_LTLIBRARIES = libdhcptest.la
+
+libdhcptest_la_SOURCES = iface_mgr_test_config.cc iface_mgr_test_config.h
+libdhcptest_la_SOURCES += pkt_filter_test_stub.cc pkt_filter_test_stub.h
+libdhcptest_la_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcptest_la_CPPFLAGS = $(AM_CPPFLAGS)
+libdhcptest_la_LDFLAGS = $(AM_LDFLAGS)
+libdhcptest_la_LIBADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+
TESTS += libdhcp++_unittests
libdhcp___unittests_SOURCES = run_unittests.cc
libdhcp___unittests_SOURCES += hwaddr_unittest.cc
libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += iface_mgr_test_config.cc iface_mgr_test_config.h
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option4_client_fqdn_unittest.cc
@@ -51,6 +69,7 @@ libdhcp___unittests_SOURCES += pkt6_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc
+libdhcp___unittests_SOURCES += pkt_filter_test_stub.cc pkt_filter_test_stub.h
libdhcp___unittests_SOURCES += pkt_filter_test_utils.h pkt_filter_test_utils.cc
libdhcp___unittests_SOURCES += pkt_filter6_test_utils.h pkt_filter6_test_utils.cc
diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.cc b/src/lib/dhcp/tests/iface_mgr_test_config.cc
new file mode 100644
index 0000000..8da7e6e
--- /dev/null
+++ b/src/lib/dhcp/tests/iface_mgr_test_config.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp/tests/pkt_filter_test_stub.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+IfaceMgrTestConfig::IfaceMgrTestConfig(const bool default_config) {
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().clearIfaces();
+ packet_filter4_ = PktFilterPtr(new PktFilterTestStub());
+ IfaceMgr::instance().setPacketFilter(packet_filter4_);
+
+ // Create default set of fake interfaces: lo, eth0 and eth1.
+ if (default_config) {
+ createIfaces();
+ }
+}
+
+IfaceMgrTestConfig::~IfaceMgrTestConfig() {
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
+
+ IfaceMgr::instance().detectIfaces();
+}
+
+void
+IfaceMgrTestConfig::addAddress(const std::string& iface_name,
+ const IOAddress& address) {
+ Iface* iface = IfaceMgr::instance().getIface(iface_name);
+ if (iface == NULL) {
+ isc_throw(isc::BadValue, "interface '" << iface_name
+ << "' doesn't exist");
+ }
+ iface->addAddress(address);
+}
+
+void
+IfaceMgrTestConfig::addIface(const Iface& iface) {
+ IfaceMgr::instance().addInterface(iface);
+}
+
+void
+IfaceMgrTestConfig::addIface(const std::string& name, const int ifindex) {
+ IfaceMgr::instance().addInterface(createIface(name, ifindex));
+}
+
+Iface
+IfaceMgrTestConfig::createIface(const std::string &name, const int ifindex) {
+ Iface iface(name, ifindex);
+ if (name == "lo") {
+ iface.flag_loopback_ = true;
+ }
+ iface.flag_multicast_ = true;
+ // On BSD systems, the SO_BINDTODEVICE option is not supported.
+ // Therefore the IfaceMgr will throw an exception on attempt to
+ // open sockets on more than one broadcast-capable interface at
+ // the same time. In order to prevent this error, we mark all
+ // interfaces broadcast-incapable for unit testing.
+ iface.flag_broadcast_ = false;
+ iface.flag_up_ = true;
+ iface.flag_running_ = true;
+ iface.inactive4_ = false;
+ iface.inactive6_ = false;
+ return (iface);
+}
+
+void
+IfaceMgrTestConfig::createIfaces() {
+ // local loopback
+ addIface("lo", 0);
+ addAddress("lo", IOAddress("127.0.0.1"));
+ addAddress("lo", IOAddress("::1"));
+ // eth0
+ addIface("eth0", 1);
+ addAddress("eth0", IOAddress("10.0.0.1"));
+ addAddress("eth0", IOAddress("fe80::3a60:77ff:fed5:cdef"));
+ addAddress("eth0", IOAddress("2001:db8:1::1"));
+ // eth1
+ addIface("eth1", 2);
+ addAddress("eth1", IOAddress("192.0.2.3"));
+ addAddress("eth1", IOAddress("fe80::3a60:77ff:fed5:abcd"));
+
+}
+
+void
+IfaceMgrTestConfig::setDirectResponse(const bool direct_resp) {
+ boost::shared_ptr<PktFilterTestStub> stub =
+ boost::dynamic_pointer_cast<PktFilterTestStub>(getPacketFilter4());
+ if (!stub) {
+ isc_throw(isc::Unexpected, "unable to set direct response capability for"
+ " test packet filter - current packet filter is not"
+ " of a PktFilterTestStub");
+ }
+ stub->direct_response_supported_ = direct_resp;
+}
+
+void
+IfaceMgrTestConfig::setIfaceFlags(const std::string& name,
+ const FlagLoopback& loopback,
+ const FlagUp& up,
+ const FlagRunning& running,
+ const FlagInactive4& inactive4,
+ const FlagInactive6& inactive6) {
+ Iface* iface = IfaceMgr::instance().getIface(name);
+ if (iface == NULL) {
+ isc_throw(isc::BadValue, "interface '" << name << "' doesn't exist");
+ }
+ iface->flag_loopback_ = loopback.flag_;
+ iface->flag_up_ = up.flag_;
+ iface->flag_running_ = running.flag_;
+ iface->inactive4_ = inactive4.flag_;
+ iface->inactive6_ = inactive6.flag_;
+}
+
+}
+}
+}
diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.h b/src/lib/dhcp/tests/iface_mgr_test_config.h
new file mode 100644
index 0000000..a2ceba5
--- /dev/null
+++ b/src/lib/dhcp/tests/iface_mgr_test_config.h
@@ -0,0 +1,241 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef IFACE_MGR_TEST_CONFIG_H
+#define IFACE_MGR_TEST_CONFIG_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <boost/noncopyable.hpp>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+///
+/// @name Set of structures describing interface flags.
+///
+/// These flags encapsulate the boolean type to pass the flags values
+/// to @c IfaceMgrTestConfig methods. If the values passed to these methods
+/// were not encapsulated by the types defined here, the API would become
+/// prone to errors like swapping parameters being passed to specific functions.
+/// For example, in the call to @c IfaceMgrTestConfig::setIfaceFlags:
+/// @code
+/// IfaceMgrTestConfig test_config(true);
+/// test_config.setIfaceFlags("eth1", false, false, true, false, false);
+/// @endcode
+///
+/// it is quite likely that the developer by mistake swaps the values and
+/// assigns them to wrong flags. When the flags are encapsulated with dedicated
+/// structs, the compiler will return an error if values are swapped. For
+/// example:
+/// @code
+/// IfaceMgrTestConfig test_config(true);
+/// test_config.setIfaceFlags("eth1", FlagLoopback(false), FlagUp(false),
+/// FlagRunning(true), FlagInactive4(false),
+/// FlagInactive6(false));
+/// @endcode
+/// will succeed, but the following code will result in the compilation error
+/// and thus protect a developer from making an error:
+/// @code
+/// IfaceMgrTestConfig test_config(true);
+/// test_config.setIfaceFlags("eth1", FlagLoopback(false),
+/// FlagRunning(true), FlagUp(false),
+/// FlagInactive4(false), FlagInactive6(false));
+/// @endcode
+///
+//@{
+/// @brief Structure describing the loopback interface flag.
+struct FlagLoopback {
+ explicit FlagLoopback(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the up interface flag.
+struct FlagUp {
+ explicit FlagUp(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the running interface flag.
+struct FlagRunning {
+ explicit FlagRunning(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the inactive4 interface flag.
+struct FlagInactive4 {
+ explicit FlagInactive4(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the inactive6 interface flag.
+struct FlagInactive6 {
+ explicit FlagInactive6(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+//@}
+
+/// @brief Convenience class for configuring @c IfaceMgr for unit testing.
+///
+/// This class is used by various unit tests which test the code relaying
+/// on IfaceMgr. The use of this class is not limited to libdhcp++ validation.
+/// There are other libraries and applications (e.g. DHCP servers) which
+/// depend on @c IfaceMgr.
+///
+/// During the normal operation, the @c IfaceMgr detects interfaces present
+/// on the machine where it is running. It also provides the means for
+/// applications to open sockets on these interfaces and perform other
+/// IO operations. This however creates dependency of the applications
+/// using @c IfaceMgr on the physical properties of the system and effectively
+/// makes it very hard to unit test the dependent code.
+///
+/// Unit tests usually require that @c IfaceMgr holds a list of well known
+/// interfaces with the well known set of IP addresses and other properties
+/// (a.k.a. interface flags). The solution which works for many test scenarios
+/// is to provide a set of well known fake interfaces, by bypassing the
+/// standard interface detection procedure and manually adding @c Iface objects
+/// which encapsulate the fake interfaces. As a consequence, it becomes
+/// impossible to test IO operations (e.g. sending packets) because real sockets
+/// can't be opened on these interfaces. The @c PktFilterTestStub class
+/// is used by this class to mimic behavior of IO operations on fake sockets.
+///
+/// This class provides a set of convenience functions that should be called
+/// by unit tests to configure the @c IfaceMgr with fake interfaces.
+///
+/// The class allows the caller to create custom fake interfaces (with custom
+/// IPv4 and IPv6 addresses, flags etc.), but it also provides a default
+/// test configuration for interfaces as follows:
+/// - lo
+/// - 127.0.0.1
+/// - ::1
+/// - eth0
+/// - 10.0.0.1
+/// - fe80::3a60:77ff:fed5:cdef
+/// - 2001:db8:1::1
+/// - eth1
+/// - 192.0.2.3
+/// - fe80::3a60:77ff:fed5:abcd
+///
+/// For all interfaces the following flags are set:
+/// - multicast
+/// - up
+/// - running
+class IfaceMgrTestConfig : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// It closes all sockets opened by @c IfaceMgr and removes all interfaces
+ /// being used by @c IfaceMgr.
+ IfaceMgrTestConfig(const bool default_config = false);
+
+ /// @brief Destructor.
+ ///
+ /// Closes all currently opened sockets, removes current interfaces and
+ /// sets the default packet filtering classes. The default packet filtering
+ /// classes are used for IO operations on real sockets/interfaces.
+ ///
+ /// Destructor also re-detects real interfaces.
+ ~IfaceMgrTestConfig();
+
+ /// @brief Adds new IPv4 or IPv6 address to the interface.
+ ///
+ /// @param iface_name Name of the interface on which new address should
+ /// be configured.
+ /// @param address IPv4 or IPv6 address to be configured on the interface.
+ void addAddress(const std::string& iface_name,
+ const asiolink::IOAddress& address);
+
+ /// @brief Configures new interface for the @c IfaceMgr.
+ ///
+ /// @param iface Object encapsulating interface to be added.
+ void addIface(const Iface& iface);
+
+ /// @brief Configures new interface for the @c IfaceMgr.
+ ///
+ /// @param name Name of the new interface.
+ /// @param ifindex Index for a new interface.
+ void addIface(const std::string& name, const int ifindex);
+
+ /// @brief Create an object representing interface.
+ ///
+ /// Apart from creating an interface, this function also sets the
+ /// interface flags:
+ /// - loopback flag if interface name is "lo"
+ /// - up always true
+ /// - running always true
+ /// - inactive always to false
+ /// - multicast always to true
+ /// - broadcast always to false
+ ///
+ /// If one needs to modify the default flag settings, the setIfaceFlags
+ /// function should be used.
+ ///
+ /// @param name A name of the interface to be created.
+ /// @param ifindex An index of the interface to be created.
+ ///
+ /// @return An object representing interface.
+ static Iface createIface(const std::string& name, const int ifindex);
+
+ /// @brief Creates a default (example) set of fake interfaces.
+ void createIfaces();
+
+ /// @brief Returns currently used packet filter for DHCPv4.
+ PktFilterPtr getPacketFilter4() const {
+ return (packet_filter4_);
+ }
+
+ /// @brief Sets the direct response capability for current packet filter.
+ ///
+ /// The test uses stub implementation of packet filter object. It is
+ /// possible to configure that object to report having a capability
+ /// to directly repond to clients which don't have an address yet.
+ /// This function sets this property for packet filter object.
+ ///
+ /// @param direct_resp Value to be set.
+ ///
+ /// @throw isc::Unexpected if unable to set the property.
+ void setDirectResponse(const bool direct_resp);
+
+ /// @brief Sets various flags on the specified interface.
+ ///
+ /// This function configures interface with new values for flags.
+ ///
+ /// @param name Interface name.
+ /// @param loopback Specifies if interface is a loopback interface.
+ /// @param up Specifies if the interface is up.
+ /// @param running Specifies if the interface is running.
+ /// @param inactive4 Specifies if the interface is inactive for V4
+ /// traffic, i.e. @c IfaceMgr opens V4 sockets on this interface.
+ /// @param inactive6 Specifies if the interface is inactive for V6
+ /// traffic, i.e. @c IfaceMgr opens V6 sockets on this interface.
+ void setIfaceFlags(const std::string& name,
+ const FlagLoopback& loopback,
+ const FlagUp& up,
+ const FlagRunning& running,
+ const FlagInactive4& inactive4,
+ const FlagInactive6& inactive6);
+
+private:
+ /// @brief Currently used packet filter for DHCPv4.
+ PktFilterPtr packet_filter4_;
+
+};
+
+};
+};
+};
+
+#endif // IFACE_MGR_TEST_CONFIG_H
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index b95b4de..c8cd195 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -19,6 +19,7 @@
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt6.h>
#include <dhcp/pkt_filter.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp/tests/pkt_filter6_test_utils.h>
#include <boost/bind.hpp>
@@ -529,6 +530,32 @@ TEST_F(IfaceMgrTest, ifaceClass) {
EXPECT_STREQ("eth5/7", iface.getFullName().c_str());
}
+// Test that the IPv4 address can be retrieved for the interface.
+TEST_F(IfaceMgrTest, ifaceGetAddress) {
+ Iface iface("eth0", 0);
+
+ IOAddress addr("::1");
+ // Initially, the Iface has no addresses assigned.
+ EXPECT_FALSE(iface.getAddress4(addr));
+ // Add some addresses with IPv4 address in the middle.
+ iface.addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+ iface.addAddress(IOAddress("10.1.2.3"));
+ iface.addAddress(IOAddress("2001:db8:1::2"));
+ // The v4 address should be returned.
+ EXPECT_TRUE(iface.getAddress4(addr));
+ EXPECT_EQ("10.1.2.3", addr.toText());
+ // Delete the IPv4 address and leave only two IPv6 addresses.
+ ASSERT_NO_THROW(iface.delAddress(IOAddress("10.1.2.3")));
+ // The IPv4 address should not be returned.
+ EXPECT_FALSE(iface.getAddress4(addr));
+ // Add a different IPv4 address at the end of the list.
+ iface.addAddress(IOAddress("192.0.2.3"));
+ // This new address should now be returned.
+ EXPECT_TRUE(iface.getAddress4(addr));
+ EXPECT_EQ("192.0.2.3", addr.toText());
+
+}
+
// TODO: Implement getPlainMac() test as soon as interface detection
// is implemented.
TEST_F(IfaceMgrTest, getIface) {
@@ -1381,31 +1408,29 @@ TEST_F(IfaceMgrTest, openSockets4) {
// This test verifies that the socket is not open on the interface which is
// down, but sockets are open on all other non-loopback interfaces.
TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
- NakedIfaceMgr ifacemgr;
-
- // Remove all real interfaces and create a set of dummy interfaces.
- ifacemgr.createIfaces();
-
- boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
- ASSERT_TRUE(custom_packet_filter);
- ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+ IfaceMgrTestConfig config(true);
// Boolean parameters specify that eth0 is:
// - not a loopback
// - is "down" (not up)
// - is not running
// - is active (is not inactive)
- ifacemgr.setIfaceFlags("eth0", false, false, true, false, false);
- ASSERT_FALSE(ifacemgr.getIface("eth0")->flag_up_);
- ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
+ config.setIfaceFlags("eth0", FlagLoopback(false), FlagUp(false),
+ FlagRunning(false), FlagInactive4(false),
+ FlagInactive6(false));
+ ASSERT_FALSE(IfaceMgr::instance().getIface("eth0")->flag_up_);
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4(DHCP4_SERVER_PORT, true,
+ NULL));
// There should be no socket on eth0 open, because interface was down.
- EXPECT_TRUE(ifacemgr.getIface("eth0")->getSockets().empty());
+ EXPECT_TRUE(IfaceMgr::instance().getIface("eth0")->getSockets().empty());
+
// Expecting that the socket is open on eth1 because it was up, running
// and active.
- EXPECT_EQ(1, ifacemgr.getIface("eth1")->getSockets().size());
+ EXPECT_EQ(1, IfaceMgr::instance().getIface("eth1")->getSockets().size());
// Never open socket on loopback interface.
- EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
+ EXPECT_TRUE(IfaceMgr::instance().getIface("lo")->getSockets().empty());
+
}
// This test verifies that the socket is not open on the interface which is
diff --git a/src/lib/dhcp/tests/pkt_filter_test_stub.cc b/src/lib/dhcp/tests/pkt_filter_test_stub.cc
new file mode 100644
index 0000000..2e9ab5e
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_test_stub.cc
@@ -0,0 +1,49 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/tests/pkt_filter_test_stub.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+PktFilterTestStub::PktFilterTestStub()
+ : direct_response_supported_(true) {
+}
+
+bool
+PktFilterTestStub::isDirectResponseSupported() const {
+ return (direct_response_supported_);
+}
+
+SocketInfo
+PktFilterTestStub::openSocket(const Iface&,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port, const bool, const bool) {
+ return (SocketInfo(addr, port, 0));
+}
+
+Pkt4Ptr
+PktFilterTestStub::receive(const Iface&, const SocketInfo&) {
+ return Pkt4Ptr();
+}
+
+int
+PktFilterTestStub::send(const Iface&, uint16_t, const Pkt4Ptr&) {
+ return (0);
+}
+
+}
+}
+}
diff --git a/src/lib/dhcp/tests/pkt_filter_test_stub.h b/src/lib/dhcp/tests/pkt_filter_test_stub.h
new file mode 100644
index 0000000..f8c6130
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_test_stub.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef PKT_FILTER_TEST_STUB_H
+#define PKT_FILTER_TEST_STUB_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter.h>
+#include <dhcp/pkt4.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief A stub implementation of the PktFilter class.
+///
+/// This class implements abstract methods of the @c isc::dhcp::PktFilter
+/// class. It is used by unit tests, which test protected methods of the
+/// @c isc::dhcp::test::PktFilter class. The implemented abstract methods are
+/// no-op.
+class PktFilterTestStub : public PktFilter {
+public:
+
+ /// @brief Constructor.
+ PktFilterTestStub();
+
+ /// @brief Checks if the direct DHCPv4 response is supported.
+ ///
+ /// This function checks if the direct response capability is supported,
+ /// i.e. if the server can respond to the client which doesn't have an
+ /// address yet. For this dummy class, the true is alaways returned.
+ ///
+ /// @return always true.
+ virtual bool isDirectResponseSupported() const;
+
+ /// @brief Simulate opening of the socket.
+ ///
+ /// This function simulates opening a primary socket. In reality, it doesn't
+ /// open a socket but the socket descriptor returned in the SocketInfo
+ /// structure is always set to 0.
+ ///
+ /// @param iface An interface descriptor.
+ /// @param addr Address on the interface to be used to send packets.
+ /// @param port Port number to bind socket to.
+ /// @param receive_bcast A flag which indicates if socket should be
+ /// configured to receive broadcast packets (if true).
+ /// @param send_bcast A flag which indicates if the socket should be
+ /// configured to send broadcast packets (if true).
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return A SocketInfo structure with the socket descriptor set to 0. The
+ /// fallback socket descriptor is set to a negative value.
+ virtual SocketInfo openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast);
+
+ /// @brief Simulate reception of the DHCPv4 message.
+ ///
+ /// @param iface An interface to be used to receive DHCPv4 message.
+ /// @param sock_info A descriptor of the primary and fallback sockets.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return always a NULL object.
+ virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& sock_info);
+
+ /// @brief Simulates sending a DHCPv4 message.
+ ///
+ /// This function does nothing.
+ ///
+ /// @param iface An interface to be used to send DHCPv4 message.
+ /// @param port A port used to send a message.
+ /// @param pkt A DHCPv4 to be sent.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return 0.
+ virtual int send(const Iface& iface, uint16_t port, const Pkt4Ptr& pkt);
+
+ // Change the scope of the protected function so as they can be unit tested.
+ using PktFilter::openFallbackSocket;
+
+ bool direct_response_supported_;
+};
+
+} // namespace isc::dhcp::test
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // PKT_FILTER_TEST_STUB_H
diff --git a/src/lib/dhcp/tests/pkt_filter_test_utils.h b/src/lib/dhcp/tests/pkt_filter_test_utils.h
index b2320cf..2ce9dd5 100644
--- a/src/lib/dhcp/tests/pkt_filter_test_utils.h
+++ b/src/lib/dhcp/tests/pkt_filter_test_utils.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -129,7 +129,9 @@ public:
/// fallback socket descriptor is set to a negative value.
virtual SocketInfo openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
- const uint16_t port, const bool, const bool);
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast);
/// @brief Simulate reception of the DHCPv4 message.
///
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index a22d147..207870e 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
@@ -210,25 +211,10 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
}
Subnet4Ptr
-CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) {
-
- // If there's only one subnet configured, let's just use it
- // The idea is to keep small deployments easy. In a small network - one
- // router that also runs DHCPv6 server. Users specifies a single pool and
- // expects it to just work. Without this, the server would complain that it
- // doesn't have IP address on its interfaces that matches that
- // configuration. Such requirement makes sense in IPv4, but not in IPv6.
- // The server does not need to have a global address (using just link-local
- // is ok for DHCPv6 server) from the pool it serves.
- if (subnets4_.size() == 1) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
- DHCPSRV_CFGMGR_ONLY_SUBNET4)
- .arg(subnets4_[0]->toText()).arg(hint.toText());
- return (subnets4_[0]);
- }
-
- // If there is more than one, we need to choose the proper one
- for (Subnet4Collection::iterator subnet = subnets4_.begin();
+CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) const {
+ // Iterate over existing subnets to find a suitable one for the
+ // given address.
+ for (Subnet4Collection::const_iterator subnet = subnets4_.begin();
subnet != subnets4_.end(); ++subnet) {
if ((*subnet)->inRange(hint)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
@@ -244,6 +230,22 @@ CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) {
return (Subnet4Ptr());
}
+Subnet4Ptr
+CfgMgr::getSubnet4(const std::string& iface_name) const {
+ Iface* iface = IfaceMgr::instance().getIface(iface_name);
+ // This should never happen in the real life. Hence we throw an exception.
+ if (iface == NULL) {
+ isc_throw(isc::BadValue, "interface " << iface_name <<
+ " doesn't exist and therefore it is impossible"
+ " to find a suitable subnet for its IPv4 address");
+ }
+ IOAddress addr("0.0.0.0");
+ // If IPv4 address assigned to the interface exists, find a suitable
+ // subnet for it, else return NULL pointer to indicate that no subnet
+ // could be found.
+ return (iface->getAddress4(addr) ? getSubnet4(addr) : Subnet4Ptr());
+}
+
void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
/// @todo: Check that this new subnet does not cross boundaries of any
/// other already defined subnet.
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 18455d0..63675de 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -219,7 +219,7 @@ public:
/// to choose a different subnet. Server code has to offer a list
/// of possible choices (i.e. all subnets).
/// @return a pointer to const Subnet6 collection
- const Subnet4Collection* getSubnets4() {
+ const Subnet4Collection* getSubnets4() const {
return (&subnets4_);
}
@@ -244,7 +244,19 @@ public:
/// @param hint an address that belongs to a searched subnet
///
/// @return a subnet object
- Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint);
+ Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint) const;
+
+ /// @brief Returns a subnet for the specified local interface.
+ ///
+ /// This function checks that the IP address assigned to the specified
+ /// interface belongs to any IPv4 subnet configured, and returns this
+ /// subnet.
+ ///
+ /// @param iface Short name of the interface which is being checked.
+ ///
+ /// @return Pointer to the subnet matching interface specified or NULL
+ /// pointer if IPv4 address on the interface doesn't match any subnet.
+ Subnet4Ptr getSubnet4(const std::string& iface) const;
/// @brief adds a subnet4
void addSubnet4(const Subnet4Ptr& subnet);
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 28f8049..5945b9b 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -89,6 +89,7 @@ libdhcpsrv_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
endif
libdhcpsrv_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 8e1fe53..5962b31 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -18,6 +18,7 @@
#include <dhcpsrv/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <dhcp/dhcp6.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <gtest/gtest.h>
@@ -29,6 +30,7 @@
using namespace std;
using namespace isc::asiolink;
using namespace isc::dhcp;
+using namespace isc::dhcp::test;
using namespace isc::util;
using namespace isc;
@@ -739,6 +741,47 @@ TEST_F(CfgMgrTest, d2ClientConfig) {
EXPECT_NE(*original_config, *updated_config);
}
+// This test verfies that CfgMgr correctly determines that the address of the
+// interface belongs to existing IPv4 subnet.
+TEST_F(CfgMgrTest, getSubnet4ForInterface) {
+ IfaceMgrTestConfig config(true);
+
+ // Initially, there are no subnets configured, so none of the IPv4
+ // addresses assigned to eth0 and eth1 can match with any subnet.
+ EXPECT_FALSE(CfgMgr::instance().getSubnet4("eth0"));
+ EXPECT_FALSE(CfgMgr::instance().getSubnet4("eth1"));
+
+ // Configure first subnet which address on eth0 corresponds to.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("10.0.0.1"), 24, 1, 2, 3));
+ CfgMgr::instance().addSubnet4(subnet1);
+
+ // The address on eth0 should match the existing subnet.
+ Subnet4Ptr subnet1_ret;
+ subnet1_ret = CfgMgr::instance().getSubnet4("eth0");
+ ASSERT_TRUE(subnet1_ret);
+ EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
+ // There should still be no match for eth1.
+ EXPECT_FALSE(CfgMgr::instance().getSubnet4("eth1"));
+
+ // Configure a second subnet.
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.1"), 24, 1, 2, 3));
+ CfgMgr::instance().addSubnet4(subnet2);
+
+ // There should be match between eth0 and subnet1 and between eth1 and
+ // subnet 2.
+ subnet1_ret = CfgMgr::instance().getSubnet4("eth0");
+ ASSERT_TRUE(subnet1_ret);
+ EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
+ Subnet4Ptr subnet2_ret = CfgMgr::instance().getSubnet4("eth1");
+ ASSERT_TRUE(subnet2_ret);
+ EXPECT_EQ(subnet2->get().first, subnet2_ret->get().first);
+
+ // This function throws an exception if the name of the interface is wrong.
+ EXPECT_THROW(CfgMgr::instance().getSubnet4("bogus-interface"),
+ isc::BadValue);
+
+}
+
/// @todo Add unit-tests for testing:
/// - addActiveIface() with invalid interface name
More information about the bind10-changes
mailing list