- See ControlledDhcpv4Srv class for msgq support.
* @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
* 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
*
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
--- /dev/null
+// 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 <config.h>
+
+//#include <sys/types.h>
+//#include <sys/socket.h>
+//#include <sys/select.h>
+//#include <netdb.h>
+//#include <netinet/in.h>
+//#include <stdlib.h>
+//#include <errno.h>
+
+#include <cassert>
+#include <iostream>
+
+#include <cc/session.h>
+#include <cc/data.h>
+
+#include <exceptions/exceptions.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+
+#include <util/buffer.h>
+#include <log/dummylog.h>
+
+#include <dhcp4/spec_config.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp/iface_mgr.h>
+
+#include <asiolink/asiolink.h>
+#include <log/logger_support.h>
+
+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.
+}
+
+};
+};
--- /dev/null
+// 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 <dhcp4/dhcp4_srv.h>
+#include <asiolink/asiolink.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+
+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
/// 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:
/// critical error.
bool run();
- /// @brief instructs server to shut down.
+ /// @brief Instructs the server to shut down.
void shutdown();
protected:
-// 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
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cassert>
#include <iostream>
-
-#include <cc/session.h>
-#include <cc/data.h>
-
#include <exceptions/exceptions.h>
-#include <cc/session.h>
-#include <config/ccsession.h>
-
-#include <util/buffer.h>
#include <log/dummylog.h>
-
-#include <dhcp4/spec_config.h>
-#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp/iface_mgr.h>
-#include <asiolink/asiolink.h>
-#include <log/logger_support.h>
-
-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 {
+/// 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.
-bool verbose_mode = false;
+namespace {
void
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) {
}
}
- // 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) {
}
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;
--- /dev/null
+// 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 <config.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp/dhcp4.h>
+#include <dhcp4/dhcp4_srv.h>
+#include <dhcp/option.h>
+#include <asiolink/io_address.h>
+
+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<Pkt4> processDiscover(boost::shared_ptr<Pkt4>& discover) {
+ return Dhcpv4Srv::processDiscover(discover);
+ }
+ boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request) {
+ return Dhcpv4Srv::processRequest(request);
+ }
+ void processRelease(boost::shared_ptr<Pkt4>& release) {
+ return Dhcpv4Srv::processRelease(release);
+ }
+ void processDecline(boost::shared_ptr<Pkt4>& decline) {
+ Dhcpv4Srv::processDecline(decline);
+ }
+ boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& 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<Pkt4>& q,
+ const boost::shared_ptr<Pkt4>& 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<uint8_t> mac(6);
+ for (int i = 0; i < 6; i++) {
+ mac[i] = 255 - i;
+ }
+
+ boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
+ boost::shared_ptr<Pkt4> 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<uint8_t> mac(6);
+ for (int i = 0; i < 6; i++) {
+ mac[i] = i*10;
+ }
+
+ boost::shared_ptr<Pkt4> req(new Pkt4(DHCPREQUEST, 1234));
+ boost::shared_ptr<Pkt4> 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<Pkt4> 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<Pkt4> 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<Pkt4> 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