mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
[1651] Control interface in DHCPv4 refactored into separate class
- See ControlledDhcpv4Srv class for msgq support.
This commit is contained in:
parent
d1accb3e7b
commit
016c9ba563
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
162
src/bin/dhcp4/ctrl_dhcp4_srv.cc
Normal file
162
src/bin/dhcp4/ctrl_dhcp4_srv.cc
Normal file
@ -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 <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.
|
||||
}
|
||||
|
||||
};
|
||||
};
|
85
src/bin/dhcp4/ctrl_dhcp4_srv.h
Normal file
85
src/bin/dhcp4/ctrl_dhcp4_srv.h
Normal file
@ -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 <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
|
@ -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:
|
||||
|
@ -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 <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;
|
||||
|
||||
/// 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;
|
||||
|
274
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
Normal file
274
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
Normal file
@ -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 <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
|
Loading…
x
Reference in New Issue
Block a user