From 016c9ba5634e17aa9b0d0aeec7b07efbd73a6bf4 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 8 Jun 2012 15:27:45 +0200 Subject: [PATCH] [1651] Control interface in DHCPv4 refactored into separate class - See ControlledDhcpv4Srv class for msgq support. --- doc/devel/02-dhcp.dox | 22 +- src/bin/dhcp4/Makefile.am | 1 + src/bin/dhcp4/ctrl_dhcp4_srv.cc | 162 +++++++++++ src/bin/dhcp4/ctrl_dhcp4_srv.h | 85 ++++++ src/bin/dhcp4/dhcp4_srv.h | 9 +- src/bin/dhcp4/main.cc | 157 ++-------- .../dhcp4/tests/ctrl_dhcp4_srv_unittest.cc | 274 ++++++++++++++++++ 7 files changed, 564 insertions(+), 146 deletions(-) create mode 100644 src/bin/dhcp4/ctrl_dhcp4_srv.cc create mode 100644 src/bin/dhcp4/ctrl_dhcp4_srv.h create mode 100644 src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox index 782301d5a6..b6c3093e92 100644 --- a/doc/devel/02-dhcp.dox +++ b/doc/devel/02-dhcp.dox @@ -20,11 +20,20 @@ * @section dhcpv4Session BIND10 message queue integration * * DHCPv4 server component is now integrated with BIND10 message queue. - * The integration is performed by establish_session() and disconnect_session() - * functions in src/bin/dhcp4/main.cc file. isc::cc::Session cc_session + * The integration is performed by establishSession() and disconnectSession() + * functions in isc::dhcp::ControlledDhcpv4Srv class. main() method deifined + * in the src/bin/dhcp4/main.cc file instantiates isc::dhcp::ControlledDhcpv4Srv + * class that establishes connection with msgq and install necessary handlers + * for receiving commands and configuration updates. It is derived from + * a base isc::dhcp::Dhcpv4Srv class that implements DHCPv4 server functionality, + * without any controlling mechanisms. + * + * ControlledDhcpv4Srv instantiates several components to make management + * session possible. In particular, isc::cc::Session cc_session * object uses ASIO for establishing connection. It registers its socket - * in isc::asiolink::IOService io_service object. Typically, other components that - * use ASIO for their communication, register their other sockets in the + * in isc::asiolink::IOService io_service object. Typically, other components + * (e.g. auth or resolver) that use ASIO for their communication, register their + * other sockets in the * same io_service and then just call io_service.run() method that does * not return, until one of the callback decides that it is time to shut down * the whole component cal calls io_service.stop(). DHCPv4 works in a @@ -43,7 +52,10 @@ * be used with and without message queue. Second benefit is related to the * first one: \ref libdhcp is supposed to be simple and robust and not require * many dependencies. One notable example of a use case that benefits from - * this approach is a perfdhcp tool. + * this approach is a perfdhcp tool. Finally, the idea is that it should be + * possible to instantiate Dhcpv4Srv object directly, thus getting a server + * that does not support msgq. That is useful for embedded environments. + * It may also be useful in validation. * * @page dhcpv6 DHCPv6 Server Component * diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am index e0a8e20d25..80343606c3 100644 --- a/src/bin/dhcp4/Makefile.am +++ b/src/bin/dhcp4/Makefile.am @@ -31,6 +31,7 @@ BUILT_SOURCES = spec_config.h pkglibexec_PROGRAMS = b10-dhcp4 b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h +b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc new file mode 100644 index 0000000000..7a6e9a20ca --- /dev/null +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -0,0 +1,162 @@ +// Copyright (C) 2012 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 + +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +const char* const DHCP4_NAME = "b10-dhcp4"; + +using namespace std; +using namespace isc::util; +using namespace isc::dhcp; +using namespace isc::util; +using namespace isc::data; +using namespace isc::cc; +using namespace isc::config; +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL; + +ConstElementPtr +dhcp4_config_handler(ConstElementPtr new_config) { + cout << "b10-dhcp4: Received new config:" << new_config->str() << endl; + ConstElementPtr answer = isc::config::createAnswer(0, + "Thank you for sending config."); + return (answer); +} + +ConstElementPtr +dhcp4_command_handler(const string& command, ConstElementPtr args) { + cout << "b10-dhcp4: Received new command: [" << command << "], args=" + << args->str() << endl; + if (command == "shutdown") { + if (ControlledDhcpv4Srv::server_) { + ControlledDhcpv4Srv::server_->shutdown(); + } + ConstElementPtr answer = isc::config::createAnswer(0, + "Shutting down."); + return (answer); + } + + ConstElementPtr answer = isc::config::createAnswer(1, + "Unrecognized command."); + + return (answer); +} + +void ControlledDhcpv4Srv::sessionReader(void) { + // Process one asio event. If there are more events, iface_mgr will call + // this callback more than once. + if (server_) { + server_->io_service_.run_one(); + } +} + +void ControlledDhcpv4Srv::establishSession() { + + string specfile; + if (getenv("B10_FROM_BUILD")) { + specfile = string(getenv("B10_FROM_BUILD")) + + "/src/bin/auth/dhcp4.spec"; + } else { + specfile = string(DHCP4_SPECFILE_LOCATION); + } + + /// @todo: Check if session is not established already. Throw, if it is. + + cout << "b10-dhcp4: my specfile is " << specfile << endl; + + cc_session_ = new Session(io_service_.get_io_service()); + + config_session_ = new ModuleCCSession(specfile, *cc_session_, + dhcp4_config_handler, + dhcp4_command_handler, false); + config_session_->start(); + + int ctrl_socket = cc_session_->getSocketDesc(); + cout << "b10-dhcp4: Control session started, socket=" + << ctrl_socket << endl; + + IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader); +} + +void ControlledDhcpv4Srv::disconnectSession() { + if (config_session_) { + delete config_session_; + config_session_ = NULL; + } + if (cc_session_) { + cc_session_->disconnect(); + delete cc_session_; + cc_session_ = NULL; + } +} + +ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/, + bool verbose /* false */) + :Dhcpv4Srv(port), cc_session_(NULL), config_session_(NULL) { + + // Initialize logging. If verbose, we'll use maximum verbosity. + isc::log::initLogger(DHCP4_NAME, + (verbose ? isc::log::DEBUG : isc::log::INFO), + isc::log::MAX_DEBUG_LEVEL, NULL); + server_ = this; // remember this instance for use in callback + + establishSession(); +} + +void ControlledDhcpv4Srv::shutdown() { + io_service_.stop(); // Stop ASIO transmissions + Dhcpv4Srv::shutdown(); // Initiate DHCPv4 shutdown procedure. +} + +ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { + disconnectSession(); + + server_ = NULL; // forget this instance. There should be no callback anymore + // at this stage anyway. +} + +}; +}; diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h new file mode 100644 index 0000000000..d3af173af0 --- /dev/null +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -0,0 +1,85 @@ +// Copyright (C) 2012 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 CTRL_DHCPV4_SRV_H +#define CTRL_DHCPV4_SRV_H + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Controlled version of the DHCPv4 server +/// +/// This is a class that is responsible for establishing connection +/// with msqg (receving commands and configuration). This is an extended +/// version of Dhcpv4Srv class that is purely a DHCPv4 server, without +/// external control. ControlledDhcpv4Srv should be used in typical BIND10 +/// (i.e. featuring msgq) environment, while Dhcpv4Srv should be used in +/// embedded environments. +/// +/// For detailed explanation or relations between main(), ControlledDhcpv4Srv, +/// Dhcpv4Srv and other classes, see \ref dhcpv4Session. +class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { +public: + ControlledDhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT, + bool verbose = false); + + /// @brief Establishes msgq session. + /// + /// Creates session that will be used to receive commands and updated + /// configuration from boss (or indirectly from user via bindctl). + void establishSession(); + + /// @brief Terminates existing session. + /// + /// This method terminates existing session with msgq. After calling + /// it, not further messages over msgq (commands or configuration updates) + /// may be received. + /// + /// It is ok to call this method when session is disconnected already. + void disconnectSession(); + + /// @brief Initiates shutdown procedure for the whole DHCPv4 server. + void shutdown(); + + ~ControlledDhcpv4Srv(); + + static ControlledDhcpv4Srv* server_; +protected: + + /// @brief callback that will be called from iface_mgr when command/config arrives + /// + /// This static callback method is called from IfaceMgr::receive4() method, + /// when there is a new command or configuration sent over msgq. + static void sessionReader(void); + + /// @brief IOService object, used for all ASIO operations + isc::asiolink::IOService io_service_; + + /// @brief Helper session object that represents raw connection to msgq + isc::cc::Session* cc_session_; + + /// @brief Session that receives configuation and commands + isc::config::ModuleCCSession* config_session_; + +}; + +}; // namespace isc::dhcp +}; // namespace isc + +#endif diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index 28a66b27a5..dc087ff072 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -32,6 +32,13 @@ namespace dhcp { /// that is going to be used as server-identifier, receives incoming /// packets, processes them, manages leases assignment and generates /// appropriate responses. +/// +/// This class does not support any controlling mechanisms directly. +/// See derived \ref ControlledDhcv4Srv class for support for +/// command and configuration updates over msgq. +/// +/// For detailed explanation or relations between main(), ControlledDhcpv4Srv, +/// Dhcpv4Srv and other classes, see \ref dhcpv4Session. class Dhcpv4Srv : public boost::noncopyable { public: @@ -60,7 +67,7 @@ class Dhcpv4Srv : public boost::noncopyable { /// critical error. bool run(); - /// @brief instructs server to shut down. + /// @brief Instructs the server to shut down. void shutdown(); protected: diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc index 8d1944765d..5a641fcaae 100644 --- a/src/bin/dhcp4/main.cc +++ b/src/bin/dhcp4/main.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2012 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 @@ -13,50 +13,26 @@ // PERFORMANCE OF THIS SOFTWARE. #include - -#include -#include -#include -#include -#include -#include -#include - -#include #include - -#include -#include - #include -#include -#include - -#include #include - -#include -#include +#include #include -#include -#include - -const char* const DHCP4_NAME = "b10-dhcp4"; - using namespace std; -using namespace isc::util; using namespace isc::dhcp; -using namespace isc::util; -using namespace isc::data; -using namespace isc::cc; -using namespace isc::config; -using namespace isc::asiolink; + +/// This file contains entry point (main() function) for standard DHCPv4 server +/// component for BIND10 framework. It parses command-line arguments and +/// instantiates ControlledDhcpv4Srv class that is responsible for establishing +/// connection with msgq (receiving commands and configuration) and also +/// creating Dhcpv4 server object as well. +/// +/// For detailed explanation or relations between main(), ControlledDhcpv4Srv, +/// Dhcpv4Srv and other classes, see \ref dhcpv4Session. namespace { -bool verbose_mode = false; - void usage() { cerr << "Usage: b10-dhcp4 [-v]" @@ -66,102 +42,10 @@ usage() { } } // end of anonymous namespace -/// @brief DHCPv4 context (provides access to essential objects) -/// -/// This is a structure that provides access to essential objects -/// used during DHCPv4 operation: Dhcpv4Srv object itself and -/// also objects required for msgq session management. -struct DHCPv4Context { - IOService io_service; - Session* cc_session; - ModuleCCSession* config_session; - Dhcpv4Srv* server; - DHCPv4Context(): cc_session(NULL), config_session(NULL), server(NULL) { }; -}; - -DHCPv4Context dhcp4; - -// Global objects are ugly, but that is the most convenient way of -// having it accessible from handlers. - -// The same applies to global pointers. Ugly, but useful. - -ConstElementPtr -dhcp4_config_handler(ConstElementPtr new_config) { - cout << "b10-dhcp4: Received new config:" << new_config->str() << endl; - ConstElementPtr answer = isc::config::createAnswer(0, - "Thank you for sending config."); - return (answer); -} - -ConstElementPtr -dhcp4_command_handler(const string& command, ConstElementPtr args) { - cout << "b10-dhcp4: Received new command: [" << command << "], args=" - << args->str() << endl; - if (command == "shutdown") { - if (dhcp4.server) { - dhcp4.server->shutdown(); - } - dhcp4.io_service.stop(); - ConstElementPtr answer = isc::config::createAnswer(0, - "Shutting down."); - return (answer); - } - - ConstElementPtr answer = isc::config::createAnswer(1, - "Unrecognized command."); - - return (answer); -} - -/// @brief callback that will be called from iface_mgr when command/config arrives -void session_reader(void) { - // Process one asio event. If there are more events, iface_mgr will call - // this callback more than once. - dhcp4.io_service.run_one(); -} - -/// @brief Establishes msgq session. -/// -/// Creates session that will be used to receive commands and updated -/// configuration from boss (or indirectly from user via bindctl). -void establish_session() { - - string specfile; - if (getenv("B10_FROM_BUILD")) { - specfile = string(getenv("B10_FROM_BUILD")) + - "/src/bin/auth/dhcp4.spec"; - } else { - specfile = string(DHCP4_SPECFILE_LOCATION); - } - - cout << "b10-dhcp4: my specfile is " << specfile << endl; - - dhcp4.cc_session = new Session(dhcp4.io_service.get_io_service()); - - dhcp4.config_session = new ModuleCCSession(specfile, *dhcp4.cc_session, - dhcp4_config_handler, - dhcp4_command_handler, false); - dhcp4.config_session->start(); - - int ctrl_socket = dhcp4.cc_session->getSocketDesc(); - cout << "b10-dhcp4: Control session started, socket=" - << ctrl_socket << endl; - - IfaceMgr::instance().set_session_socket(ctrl_socket, session_reader); -} - -void disconnect_session() { - dhcp4.cc_session->disconnect(); - delete dhcp4.config_session; - dhcp4.config_session = NULL; - delete dhcp4.cc_session; - dhcp4.cc_session = NULL; -} - int main(int argc, char* argv[]) { int ch; + bool verbose_mode = false; // should server be verbose? while ((ch = getopt(argc, argv, ":v")) != -1) { switch (ch) { @@ -175,11 +59,6 @@ main(int argc, char* argv[]) { } } - // Initialize logging. If verbose, we'll use maximum verbosity. - isc::log::initLogger(DHCP4_NAME, - (verbose_mode ? isc::log::DEBUG : isc::log::INFO), - isc::log::MAX_DEBUG_LEVEL, NULL); - cout << "b10-dhcp4: My pid is " << getpid() << endl; if (argc - optind > 0) { @@ -187,19 +66,17 @@ main(int argc, char* argv[]) { } int ret = 0; + ControlledDhcpv4Srv* server = NULL; try { - establish_session(); - cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl; - dhcp4.server = new Dhcpv4Srv(); - dhcp4.server->run(); - disconnect_session(); + server = new ControlledDhcpv4Srv(DHCP4_SERVER_PORT, verbose_mode); + server->run(); + delete server; - delete dhcp4.server; - dhcp4.server = NULL; + server = NULL; } catch (const std::exception& ex) { cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl; diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc new file mode 100644 index 0000000000..cfc12fb844 --- /dev/null +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -0,0 +1,274 @@ +// Copyright (C) 2011 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; + +namespace { +const char* const INTERFACE_FILE = "interfaces.txt"; + +class NakedDhcpv4Srv: public Dhcpv4Srv { + // "naked" DHCPv4 server, exposes internal fields +public: + NakedDhcpv4Srv():Dhcpv4Srv(DHCP4_SERVER_PORT + 10000) { } + + boost::shared_ptr processDiscover(boost::shared_ptr& discover) { + return Dhcpv4Srv::processDiscover(discover); + } + boost::shared_ptr processRequest(boost::shared_ptr& request) { + return Dhcpv4Srv::processRequest(request); + } + void processRelease(boost::shared_ptr& release) { + return Dhcpv4Srv::processRelease(release); + } + void processDecline(boost::shared_ptr& decline) { + Dhcpv4Srv::processDecline(decline); + } + boost::shared_ptr processInform(boost::shared_ptr& inform) { + return Dhcpv4Srv::processInform(inform); + } +}; + +class Dhcpv4SrvTest : public ::testing::Test { +public: + Dhcpv4SrvTest() { + unlink(INTERFACE_FILE); + fstream fakeifaces(INTERFACE_FILE, ios::out | ios::trunc); + if (if_nametoindex("lo") > 0) { + fakeifaces << "lo 127.0.0.1"; + } else if (if_nametoindex("lo0") > 0) { + fakeifaces << "lo0 127.0.0.1"; + } + fakeifaces.close(); + } + + void MessageCheck(const boost::shared_ptr& q, + const boost::shared_ptr& a) { + ASSERT_TRUE(q); + ASSERT_TRUE(a); + + EXPECT_EQ(q->getHops(), a->getHops()); + EXPECT_EQ(q->getIface(), a->getIface()); + EXPECT_EQ(q->getIndex(), a->getIndex()); + EXPECT_EQ(q->getGiaddr(), a->getGiaddr()); + + // check that bare minimum of required options are there + EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK)); + EXPECT_TRUE(a->getOption(DHO_ROUTERS)); + EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER)); + EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME)); + EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK)); + EXPECT_TRUE(a->getOption(DHO_ROUTERS)); + EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME)); + EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS)); + + // check that something is offered + EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0"); + } + + ~Dhcpv4SrvTest() { + unlink(INTERFACE_FILE); + }; +}; + +TEST_F(Dhcpv4SrvTest, basic) { + // nothing to test. DHCPv4_srv instance is created + // in test fixture. It is destroyed in destructor + + Dhcpv4Srv* srv = NULL; + ASSERT_NO_THROW({ + srv = new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000); + }); + + delete srv; +} + +TEST_F(Dhcpv4SrvTest, processDiscover) { + NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(); + vector mac(6); + for (int i = 0; i < 6; i++) { + mac[i] = 255 - i; + } + + boost::shared_ptr pkt(new Pkt4(DHCPDISCOVER, 1234)); + boost::shared_ptr offer; + + pkt->setIface("eth0"); + pkt->setIndex(17); + pkt->setHWAddr(1, 6, mac); + pkt->setRemoteAddr(IOAddress("192.0.2.56")); + pkt->setGiaddr(IOAddress("192.0.2.67")); + + // let's make it a relayed message + pkt->setHops(3); + pkt->setRemotePort(DHCP4_SERVER_PORT); + + // should not throw + EXPECT_NO_THROW( + offer = srv->processDiscover(pkt); + ); + + // should return something + ASSERT_TRUE(offer); + + EXPECT_EQ(DHCPOFFER, offer->getType()); + + // this is relayed message. It should be sent back to relay address. + EXPECT_EQ(pkt->getGiaddr(), offer->getRemoteAddr()); + + MessageCheck(pkt, offer); + + // now repeat the test for directly sent message + pkt->setHops(0); + pkt->setGiaddr(IOAddress("0.0.0.0")); + pkt->setRemotePort(DHCP4_CLIENT_PORT); + + EXPECT_NO_THROW( + offer = srv->processDiscover(pkt); + ); + + // should return something + ASSERT_TRUE(offer); + + EXPECT_EQ(DHCPOFFER, offer->getType()); + + // this is direct message. It should be sent back to origin, not + // to relay. + EXPECT_EQ(pkt->getRemoteAddr(), offer->getRemoteAddr()); + + MessageCheck(pkt, offer); + + delete srv; +} + +TEST_F(Dhcpv4SrvTest, processRequest) { + NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(); + vector mac(6); + for (int i = 0; i < 6; i++) { + mac[i] = i*10; + } + + boost::shared_ptr req(new Pkt4(DHCPREQUEST, 1234)); + boost::shared_ptr ack; + + req->setIface("eth0"); + req->setIndex(17); + req->setHWAddr(1, 6, mac); + req->setRemoteAddr(IOAddress("192.0.2.56")); + req->setGiaddr(IOAddress("192.0.2.67")); + + // should not throw + ASSERT_NO_THROW( + ack = srv->processRequest(req); + ); + + // should return something + ASSERT_TRUE(ack); + + EXPECT_EQ(DHCPACK, ack->getType()); + + // this is relayed message. It should be sent back to relay address. + EXPECT_EQ(req->getGiaddr(), ack->getRemoteAddr()); + + MessageCheck(req, ack); + + // now repeat the test for directly sent message + req->setHops(0); + req->setGiaddr(IOAddress("0.0.0.0")); + req->setRemotePort(DHCP4_CLIENT_PORT); + + EXPECT_NO_THROW( + ack = srv->processDiscover(req); + ); + + // should return something + ASSERT_TRUE(ack); + + EXPECT_EQ(DHCPOFFER, ack->getType()); + + // this is direct message. It should be sent back to origin, not + // to relay. + EXPECT_EQ(ack->getRemoteAddr(), req->getRemoteAddr()); + + MessageCheck(req, ack); + + delete srv; +} + +TEST_F(Dhcpv4SrvTest, processRelease) { + NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(); + + boost::shared_ptr pkt(new Pkt4(DHCPRELEASE, 1234)); + + // should not throw + EXPECT_NO_THROW( + srv->processRelease(pkt); + ); + + // TODO: Implement more reasonable tests before starting + // work on processSomething() method. + + delete srv; +} + +TEST_F(Dhcpv4SrvTest, processDecline) { + NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(); + + boost::shared_ptr pkt(new Pkt4(DHCPDECLINE, 1234)); + + // should not throw + EXPECT_NO_THROW( + srv->processDecline(pkt); + ); + + // TODO: Implement more reasonable tests before starting + // work on processSomething() method. + delete srv; +} + +TEST_F(Dhcpv4SrvTest, processInform) { + NakedDhcpv4Srv* srv = new NakedDhcpv4Srv(); + + boost::shared_ptr pkt(new Pkt4(DHCPINFORM, 1234)); + + // should not throw + EXPECT_NO_THROW( + srv->processInform(pkt); + ); + + // should return something + EXPECT_TRUE(srv->processInform(pkt)); + + // TODO: Implement more reasonable tests before starting + // work on processSomething() method. + + delete srv; +} + +} // end of anonymous namespace