Commit aed72372 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3242] Created a test library to create fake interfaces in IfaceMgr.

parent 15884428
......@@ -24,6 +24,16 @@ TESTS_ENVIRONMENT = \
TESTS =
if HAVE_GTEST
lib_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
......@@ -74,6 +84,7 @@ libdhcp___unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
endif
libdhcp___unittests_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
......
// 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();
IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterTestStub()));
// 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:cdef"));
}
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_;
}
}
}
}
// 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.
///
//@{
/// @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.
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 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 Sets various flags on the specified interface.
///
/// This function configures interface with new values for flags.
///
/// @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);
};
};
};
};
#endif // IFACE_MGR_TEST_CONFIG_H
// 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 {
bool
PktFilterTestStub::isDirectResponseSupported() const {
return (true);
}
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);
}
}
}
}
// 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 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, const bool);
/// @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;
};
} // namespace isc::dhcp::test
} // namespace isc::dhcp
} // namespace isc
#endif // PKT_FILTER_TEST_STUB_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment