diff --git a/ChangeLog b/ChangeLog index 46faabf003..7f8ae861b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,21 @@ 4XX. [func] tomek - A new library (libb10-dhcpsrv) has been create. Currently its - functionality is limited to a Configuration Manager. CfgMgr - currently only supports basic configuration storage for DHCPv6 - server, but that capability is expected to be expanded in a near - future. + A new library (libb10-dhcpsrv) has been created. At present, it + only holds the code for the DHCP Configuration Manager. Currently + this object only supports basic configuration storage for the DHCPv6 + server, but that capability will be expanded. (Trac #2238, git TBD) +475. [func] naokikambe + Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and + xfrreqdone. These are per-zone type counters. The value of these + counters can be seen with zone name by invoking "Stats show Xfrout" via + bindctl. + (Trac #2158, git e68c127fed52e6034ab5309ddd506da03c37a08a) + +474. [func] stephen + DHCP servers now use the BIND 10 logging system for messages. + (Trac #1545, git de69a92613b36bd3944cb061e1b7c611c3c85506) + 473. [bug] jelte TCP connections now time out in b10-auth if no (or not all) query data is sent by the client. The timeout value defaults to 5000 diff --git a/Makefile.am b/Makefile.am index 0fbb78244f..1ed0d6325f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,7 +30,7 @@ endif check-valgrind-suppress: if HAVE_VALGRIND - @VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \ + @VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --track-origins=yes --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \ make -C $(abs_top_builddir) check else @echo "*** Valgrind is required for check-valgrind-suppress ***"; exit 1; diff --git a/configure.ac b/configure.ac index 2ead4b7de2..9d7b7ae713 100644 --- a/configure.ac +++ b/configure.ac @@ -1188,6 +1188,7 @@ AC_CONFIG_FILES([Makefile src/lib/datasrc/Makefile src/lib/datasrc/memory/Makefile src/lib/datasrc/memory/tests/Makefile + src/lib/datasrc/memory/tests/testdata/Makefile src/lib/datasrc/memory/benchmarks/Makefile src/lib/datasrc/tests/Makefile src/lib/datasrc/tests/testdata/Makefile diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox index a7fd094152..ca9d8815fa 100644 --- a/doc/devel/mainpage.dox +++ b/doc/devel/mainpage.dox @@ -24,6 +24,7 @@ * - @subpage libdhcp * - @subpage libdhcpIntro * - @subpage libdhcpIfaceMgr + * - @subpage perfdhcpInternals * * @section misc Miscellaneous topics * - @subpage LoggingApi @@ -36,4 +37,4 @@ * @todo: Move this logo to the right (and possibly up). Not sure what * is the best way to do it in Doxygen, without using CSS hacks. * @image html isc-logo.png - */ \ No newline at end of file + */ diff --git a/src/bin/dhcp4/.gitignore b/src/bin/dhcp4/.gitignore index 952db06e0e..86965b9f56 100644 --- a/src/bin/dhcp4/.gitignore +++ b/src/bin/dhcp4/.gitignore @@ -1,4 +1,6 @@ /b10-dhcp4 +/b10-dhcp4.8 +/dhcp4_messages.cc +/dhcp4_messages.h /spec_config.h /spec_config.h.pre -/b10-dhcp4.8 diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am index 295ab41de7..e0c97d17b8 100644 --- a/src/bin/dhcp4/Makefile.am +++ b/src/bin/dhcp4/Makefile.am @@ -12,7 +12,7 @@ endif pkglibexecdir = $(libexecdir)/@PACKAGE@ -CLEANFILES = spec_config.h +CLEANFILES = *.gcno *.gcda spec_config.h dhcp4_messages.h dhcp4_messages.cc man_MANS = b10-dhcp4.8 DISTCLEANFILES = $(man_MANS) @@ -35,11 +35,20 @@ endif spec_config.h: spec_config.h.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@ -BUILT_SOURCES = spec_config.h +dhcp4_messages.h dhcp4_messages.cc: dhcp4_messages.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes + +BUILT_SOURCES = spec_config.h dhcp4_messages.h dhcp4_messages.cc + pkglibexec_PROGRAMS = b10-dhcp4 -b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h +b10_dhcp4_SOURCES = main.cc b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h +b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h +b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h + +nodist_b10_dhcp4_SOURCES = dhcp4_messages.h dhcp4_messages.cc +EXTRA_DIST += dhcp4_messages.mes if USE_CLANGPP # Disable unused parameter warning caused by some of the @@ -47,7 +56,7 @@ if USE_CLANGPP b10_dhcp4_CXXFLAGS = -Wno-unused-parameter endif -b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 724ddf2de5..4bd3103b6b 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -13,28 +13,30 @@ // PERFORMANCE OF THIS SOFTWARE. #include + #include #include -#include +#include #include -#include +#include #include #include -#include -#include #include +#include +#include #include -#include +#include +#include -using namespace std; -using namespace isc::util; -using namespace isc::dhcp; -using namespace isc::util; -using namespace isc::data; +using namespace isc::asiolink; using namespace isc::cc; using namespace isc::config; -using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::log; +using namespace isc::util; +using namespace std; namespace isc { namespace dhcp { @@ -43,7 +45,8 @@ ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL; ConstElementPtr ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) { - cout << "b10-dhcp4: Received new config:" << new_config->str() << endl; + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE) + .arg(new_config->str()); ConstElementPtr answer = isc::config::createAnswer(0, "Thank you for sending config."); return (answer); @@ -51,13 +54,14 @@ ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) { ConstElementPtr ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr args) { - cout << "b10-dhcp4: Received new command: [" << command << "], args=" - << args->str() << endl; + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED) + .arg(command).arg(args->str()); + if (command == "shutdown") { if (ControlledDhcpv4Srv::server_) { ControlledDhcpv4Srv::server_->shutdown(); } else { - cout << "Server not initialized yet or already shut down." << endl; + LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING); ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure."); return (answer); @@ -93,10 +97,9 @@ void ControlledDhcpv4Srv::establishSession() { /// @todo: Check if session is not established already. Throw, if it is. - cout << "b10-dhcp4: my specfile is " << specfile << endl; - + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING) + .arg(specfile); cc_session_ = new Session(io_service_.get_io_service()); - config_session_ = new ModuleCCSession(specfile, *cc_session_, dhcp4ConfigHandler, dhcp4CommandHandler, false); @@ -106,8 +109,8 @@ void ControlledDhcpv4Srv::establishSession() { /// control with the "select" model of the DHCP server. This is /// fully explained in \ref dhcpv4Session. int ctrl_socket = cc_session_->getSocketDesc(); - cout << "b10-dhcp4: Control session started, socket=" - << ctrl_socket << endl; + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED) + .arg(ctrl_socket); IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader); } diff --git a/src/bin/dhcp4/dhcp4_log.cc b/src/bin/dhcp4/dhcp4_log.cc new file mode 100644 index 0000000000..678223b134 --- /dev/null +++ b/src/bin/dhcp4/dhcp4_log.cc @@ -0,0 +1,26 @@ +// 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. + +/// Defines the logger used by the top-level component of b10-dhcp4. + +#include "dhcp4_log.h" + +namespace isc { +namespace dhcp { + +isc::log::Logger dhcp4_logger("dhcp4"); + +} // namespace dhcp +} // namespace isc + diff --git a/src/bin/dhcp4/dhcp4_log.h b/src/bin/dhcp4/dhcp4_log.h new file mode 100644 index 0000000000..3717b62d92 --- /dev/null +++ b/src/bin/dhcp4/dhcp4_log.h @@ -0,0 +1,59 @@ +// 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 __DHCP4_LOG__H +#define __DHCP4_LOG__H + +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// \brief DHCP4 Logging +/// +/// Defines the levels used to output debug messages in the non-library part of +/// the b10-dhcp4 program. Higher numbers equate to more verbose (and detailed) +/// output. + +// Debug levels used to log information during startup and shutdown. +const int DBG_DHCP4_START = DBGLVL_START_SHUT; +const int DBG_DHCP4_SHUT = DBGLVL_START_SHUT; + +// Debug level used to log setting information (such as configuration changes). +const int DBG_DHCP4_COMMAND = DBGLVL_COMMAND; + +// Trace basic operations within the code. +const int DBG_DHCP4_BASIC = DBGLVL_TRACE_BASIC; + +// Trace detailed operations, including errors raised when processing invalid +// packets. (These are not logged at severities of WARN or higher for fear +// that a set of deliberately invalid packets set to the server could overwhelm +// the logging.) +const int DBG_DHCP4_DETAIL = DBGLVL_TRACE_DETAIL; + +// This level is used to log the contents of packets received and sent. +const int DBG_DHCP4_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA; + +/// Define the logger for the "dhcp4" module part of b10-dhcp4. We could define +/// a logger in each file, but we would want to define a common name to avoid +/// spelling mistakes, so it is just one small step from there to define a +/// module-common logger. +extern isc::log::Logger dhcp4_logger; + +} // namespace dhcp4 +} // namespace isc + +#endif // __DHCP4_LOG__H diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes new file mode 100644 index 0000000000..98e402da84 --- /dev/null +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -0,0 +1,98 @@ +# 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. + +$NAMESPACE isc::dhcp + +% DHCP4_CCSESSION_STARTED control channel session started on socket %1 +A debug message issued during startup after the IPv4 DHCP server has +successfully established a session with the BIND 10 control channel. + +% DHCP4_CCSESSION_STARTING starting control channel session, specfile: %1 +This debug message is issued just before the IPv4 DHCP server attempts +to establish a session with the BIND 10 control channel. + +% DHCP4_COMMAND_RECEIVED received command %1, arguments: %2 +A debug message listing the command (and possible arguments) received +from the BIND 10 control system by the IPv4 DHCP server. + +% DHCP4_CONFIG_UPDATE updated configuration received: %1 +A debug message indicating that the IPv4 DHCP server has received an +updated configuration from the BIND 10 configuration system. + +% DHCP4_NOT_RUNNING IPv4 DHCP server is not running +A warning message is issued when an attempt is made to shut down the +IPv4 DHCP server but it is not running. + +% DHCP4_OPEN_SOCKET opening sockets on port %1 +A debug message issued during startup, this indicates that the IPv4 DHCP +server is about to open sockets on the specified port. + +% DHCP4_PACKET_PARSE_FAIL failed to parse incoming packet: %1 +The IPv4 DHCP server has received a packet that it is unable to +interpret. The reason why the packet is invalid is included in the message. + +% DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3 +A debug message noting that the server has received the specified type of +packet on the specified interface. Note that a packet marked as UNKNOWN +may well be a valid DHCP packet, just a type not expected by the server +(e.g. it will report a received OFFER packet as UNKNOWN). + +% DHCP4_PACK_FAIL failed to assemble response correctly +This error is output if the server failed to assemble the data to be +returned to the client into a valid packet. The cause is most likely +to be a programming error: please raise a bug report. + +% DHCP4_QUERY_DATA received packet type %1, data is <%2> +A debug message listing the data received from the client. + +% DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2> +A debug message listing the data returned to the client. + +% DHCP4_SERVER_FAILED server failed: %1 +The IPv4 DHCP server has encountered a fatal error and is terminating. +The reason for the failure is included in the message. + +% DHCP4_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone +The server has failed to establish communication with the rest of BIND +10 and is running in stand-alone mode. (This behavior will change once +the IPv4 DHCP server is properly integrated with the rest of BIND 10.) + +% DHCP4_SHUTDOWN server shutdown +The IPv4 DHCP server has terminated normally. + +% DHCP4_SHUTDOWN_REQUEST shutdown of server requested +This debug message indicates that a shutdown of the IPv4 server has +been requested via a call to the 'shutdown' method of the core Dhcpv4Srv +object. + +% DHCP4_SRV_CONSTRUCT_ERROR error creating Dhcpv4Srv object, reason: %1 +This error message indicates that during startup, the construction of a +core component within the IPv4 DHCP server (the Dhcpv4 server object) +has failed. As a result, the server will exit. The reason for the +failure is given within the message. + +% DHCP4_STANDALONE skipping message queue, running standalone +This is a debug message indicating that the IPv4 server is running in +standalone mode, not connected to the message queue. Standalone mode +is only useful during program development, and should not be used in a +production environment. + +% DHCP4_STARTING server starting +This informational message indicates that the IPv4 DHCP server has +processed any command-line switches and is starting. + +% DHCP4_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4 +This is a debug message issued during the IPv4 DHCP server startup. +It lists some information about the parameters with which the server +is running. diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index a86b010792..2bbc0758ef 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -16,13 +16,15 @@ #include #include #include +#include #include #include -using namespace std; using namespace isc; -using namespace isc::dhcp; using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::log; +using namespace std; // These are hardcoded parameters. Currently this is a skeleton server that only // grants those options and a single, fixed, hardcoded lease. @@ -35,20 +37,19 @@ const std::string HARDCODED_DOMAIN_NAME = "isc.example.com"; const std::string HARDCODED_SERVER_ID = "192.0.2.1"; Dhcpv4Srv::Dhcpv4Srv(uint16_t port) { - cout << "Initialization: opening sockets on port " << port << endl; - + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port); try { - // first call to instance() will create IfaceMgr (it's a singleton) + // First call to instance() will create IfaceMgr (it's a singleton) // it may throw something if things go wrong IfaceMgr::instance(); /// @todo: instantiate LeaseMgr here once it is imlpemented. - IfaceMgr::instance().openSockets4(port); setServerID(); + } catch (const std::exception &e) { - cerr << "Error during DHCPv4 server startup: " << e.what() << endl; + LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what()); shutdown_ = true; return; } @@ -57,12 +58,11 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port) { } Dhcpv4Srv::~Dhcpv4Srv() { - cout << "b10-dhcp4: DHCPv4 server terminating." << endl; IfaceMgr::instance().closeSockets(); } void Dhcpv4Srv::shutdown() { - cout << "b10-dhcp4: DHCPv4 server shutdown." << endl; + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST); shutdown_ = true; } @@ -79,39 +79,48 @@ Dhcpv4Srv::run() { if (query) { try { query->unpack(); + } catch (const std::exception& e) { - /// TODO: Printout reasons of failed parsing - cout << "Failed to parse incoming packet " << endl; + // Failed to parse the packet. + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, + DHCP4_PACKET_PARSE_FAIL).arg(e.what()); continue; } + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED) + .arg(serverReceivedPacketName(query->getType())) + .arg(query->getType()) + .arg(query->getIface()); + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA) + .arg(query->toText()); switch (query->getType()) { case DHCPDISCOVER: rsp = processDiscover(query); break; + case DHCPREQUEST: rsp = processRequest(query); break; + case DHCPRELEASE: processRelease(query); break; + case DHCPDECLINE: processDecline(query); break; + case DHCPINFORM: processInform(query); break; + default: - cout << "Unknown pkt type received:" - << query->getType() << endl; + // Only action is to output a message if debug is enabled, + // and that will be covered by the debug statement before + // the "switch" statement. + ; } - cout << "Received message type " << int(query->getType()) << endl; - - // TODO: print out received packets only if verbose (or debug) - // mode is enabled - cout << query->toText(); - if (rsp) { if (rsp->getRemoteAddr().toText() == "0.0.0.0") { rsp->setRemoteAddr(query->getRemoteAddr()); @@ -127,14 +136,15 @@ Dhcpv4Srv::run() { rsp->setIface(query->getIface()); rsp->setIndex(query->getIndex()); - cout << "Replying with message type " - << static_cast(rsp->getType()) << ":" << endl; - cout << rsp->toText(); - cout << "----" << endl; + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, + DHCP4_RESPONSE_DATA) + .arg(rsp->getType()).arg(rsp->toText()); + if (rsp->pack()) { - cout << "Packet assembled correctly." << endl; + IfaceMgr::instance().send(rsp); + } else { + LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL); } - IfaceMgr::instance().send(rsp); } } @@ -266,15 +276,44 @@ Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) { void Dhcpv4Srv::processRelease(Pkt4Ptr& release) { /// TODO: Implement this. - cout << "Received RELEASE on " << release->getIface() << " interface." << endl; } void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) { /// TODO: Implement this. - cout << "Received DECLINE on " << decline->getIface() << " interface." << endl; } Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) { /// TODO: Currently implemented echo mode. Implement this for real return (inform); } + +const char* +Dhcpv4Srv::serverReceivedPacketName(uint8_t type) { + static const char* DISCOVER = "DISCOVER"; + static const char* REQUEST = "REQUEST"; + static const char* RELEASE = "RELEASE"; + static const char* DECLINE = "DECLINE"; + static const char* INFORM = "INFORM"; + static const char* UNKNOWN = "UNKNOWN"; + + switch (type) { + case DHCPDISCOVER: + return (DISCOVER); + + case DHCPREQUEST: + return (REQUEST); + + case DHCPRELEASE: + return (RELEASE); + + case DHCPDECLINE: + return (DECLINE); + + case DHCPINFORM: + return (INFORM); + + default: + ; + } + return (UNKNOWN); +} diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index dc087ff072..f677259947 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -44,7 +44,7 @@ class Dhcpv4Srv : public boost::noncopyable { public: /// @brief Default constructor. /// - /// Instantiates necessary services, required to run DHCPv6 server. + /// Instantiates necessary services, required to run DHCPv4 server. /// In particular, creates IfaceMgr that will be responsible for /// network interaction. Will instantiate lease manager, and load /// old or create new DUID. It is possible to specify alternate @@ -54,7 +54,7 @@ class Dhcpv4Srv : public boost::noncopyable { /// @param port specifies port number to listen on Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT); - /// @brief Destructor. Used during DHCPv6 service shutdown. + /// @brief Destructor. Used during DHCPv4 service shutdown. ~Dhcpv4Srv(); /// @brief Main server processing loop. @@ -70,6 +70,23 @@ class Dhcpv4Srv : public boost::noncopyable { /// @brief Instructs the server to shut down. void shutdown(); + /// @brief Return textual type of packet received by server + /// + /// Returns the name of valid packet received by the server (e.g. DISCOVER). + /// If the packet is unknown - or if it is a valid DHCP packet but not one + /// expected to be received by the server (such as an OFFER), the string + /// "UNKNOWN" is returned. This method is used in debug messages. + /// + /// As the operation of the method does not depend on any server state, it + /// is declared static. + /// + /// @param type DHCPv4 packet type + /// + /// @return Pointer to "const" string containing the packet name. + /// Note that this string is statically allocated and MUST NOT + /// be freed by the caller. + static const char* serverReceivedPacketName(uint8_t type); + protected: /// @brief Processes incoming DISCOVER and returns response. /// @@ -89,11 +106,11 @@ protected: /// is valid, not expired, not reserved, not used by other client and /// that requesting client is allowed to use it. /// - /// Returns ACK message, NACK message, or NULL + /// Returns ACK message, NAK message, or NULL /// /// @param request a message received from client /// - /// @return ACK or NACK message + /// @return ACK or NAK message Pkt4Ptr processRequest(Pkt4Ptr& request); /// @brief Stub function that will handle incoming RELEASE messages. diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc index 1087cc7fb6..31f0b0251b 100644 --- a/src/bin/dhcp4/main.cc +++ b/src/bin/dhcp4/main.cc @@ -14,13 +14,15 @@ #include #include -#include -#include -#include + #include -using namespace std; +#include +#include +#include + using namespace isc::dhcp; +using namespace std; /// This file contains entry point (main() function) for standard DHCPv4 server /// component for BIND10 framework. It parses command-line arguments and @@ -37,11 +39,10 @@ const char* const DHCP4_NAME = "b10-dhcp4"; void usage() { - cerr << "Usage: b10-dhcp4 [-v]" - << endl; - cerr << "\t-v: verbose output" << endl; - cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl; - cerr << "\t-p number: specify non-standard port number 1-65535 " + cerr << "Usage: " << DHCP4_NAME << " [-v] [-s] [-p number]" << endl; + cerr << " -v: verbose output" << endl; + cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl; + cerr << " -p number: specify non-standard port number 1-65535 " << "(useful for testing only)" << endl; exit(EXIT_FAILURE); } @@ -50,20 +51,21 @@ usage() { int main(int argc, char* argv[]) { int ch; - bool verbose_mode = false; // should server be verbose? int port_number = DHCP4_SERVER_PORT; // The default. any other values are // useful for testing only. - bool stand_alone = false; // should be connect to BIND10 msgq? + bool stand_alone = false; // Should be connect to BIND10 msgq? + bool verbose_mode = false; // Should server be verbose? while ((ch = getopt(argc, argv, "vsp:")) != -1) { switch (ch) { case 'v': verbose_mode = true; - isc::log::denabled = true; break; + case 's': stand_alone = true; break; + case 'p': try { port_number = boost::lexical_cast(optarg); @@ -78,50 +80,46 @@ main(int argc, char* argv[]) { usage(); } break; - case ':': + default: usage(); } } + // Check for extraneous parameters. + if (argc > optind) { + usage(); + } + // 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); + LOG_INFO(dhcp4_logger, DHCP4_STARTING); + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO) + .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no") + .arg(stand_alone ? "yes" : "no" ); - cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port " - << port_number << ", verbose " << (verbose_mode?"yes":"no") - << ", stand-alone=" << (stand_alone?"yes":"no") << endl; - - if (argc - optind > 0) { - usage(); - } int ret = EXIT_SUCCESS; - try { - - cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl; - - /// @todo: pass verbose to the actul server once logging is implemented ControlledDhcpv4Srv server(port_number); - if (!stand_alone) { try { server.establishSession(); } catch (const std::exception& ex) { - cerr << "Failed to establish BIND10 session. " - "Running in stand-alone mode:" << ex.what() << endl; + LOG_ERROR(dhcp4_logger, DHCP4_SESSION_FAIL).arg(ex.what()); // Let's continue. It is useful to have the ability to run // DHCP server in stand-alone mode, e.g. for testing } } else { - cout << "Skipping connection to the BIND10 msgq." << endl; + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_STANDALONE); } - server.run(); + LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN); + } catch (const std::exception& ex) { - cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl; + LOG_FATAL(dhcp4_logger, DHCP4_SERVER_FAILED).arg(ex.what()); ret = EXIT_FAILURE; } diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index 402af604f6..906b5d1e59 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -30,7 +30,7 @@ AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\" AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\" AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" -CLEANFILES = $(builddir)/interfaces.txt +CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile AM_CXXFLAGS = $(B10_CXXFLAGS) @@ -47,9 +47,11 @@ if HAVE_GTEST TESTS += dhcp4_unittests dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc +dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc dhcp4_unittests_SOURCES += dhcp4_unittests.cc dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc +nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc if USE_CLANGPP # Disable unused parameter warning caused by some of the diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index cfc12fb844..fca06ad988 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 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 @@ -271,4 +271,36 @@ TEST_F(Dhcpv4SrvTest, processInform) { delete srv; } +TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) { + // Check all possible packet types + for (int itype = 0; itype < 256; ++itype) { + uint8_t type = itype; + + switch (type) { + case DHCPDECLINE: + EXPECT_STREQ("DECLINE", Dhcpv4Srv::serverReceivedPacketName(type)); + break; + + case DHCPDISCOVER: + EXPECT_STREQ("DISCOVER", Dhcpv4Srv::serverReceivedPacketName(type)); + break; + + case DHCPINFORM: + EXPECT_STREQ("INFORM", Dhcpv4Srv::serverReceivedPacketName(type)); + break; + + case DHCPRELEASE: + EXPECT_STREQ("RELEASE", Dhcpv4Srv::serverReceivedPacketName(type)); + break; + + case DHCPREQUEST: + EXPECT_STREQ("REQUEST", Dhcpv4Srv::serverReceivedPacketName(type)); + break; + + default: + EXPECT_STREQ("UNKNOWN", Dhcpv4Srv::serverReceivedPacketName(type)); + } + } +} + } // end of anonymous namespace diff --git a/src/bin/dhcp4/tests/dhcp4_test.py b/src/bin/dhcp4/tests/dhcp4_test.py index 935bba645a..444dfcfd2a 100644 --- a/src/bin/dhcp4/tests/dhcp4_test.py +++ b/src/bin/dhcp4/tests/dhcp4_test.py @@ -27,16 +27,27 @@ import fcntl class TestDhcpv4Daemon(unittest.TestCase): def setUp(self): - # don't redirect stdout/stderr here as we want to print out things + # Don't redirect stdout/stderr here as we want to print out things # during the test - pass + # + # However, we do want to set the logging lock directory to somewhere + # to which we can write - use the current working directory. We then + # set the appropriate environment variable. os.putenv() may be not + # supported on some platforms as suggested in + # http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ: + # "If the platform supports the putenv() function...". It was checked + # that it does not work on Ubuntu. To overcome this problem we access + # os.environ directly. + lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD" + if lockdir_envvar not in os.environ: + os.environ[lockdir_envvar] = os.getcwd() def tearDown(self): pass def runCommand(self, params, wait=1): """ - This method runs dhcp4 and returns a touple: (returncode, stdout, stderr) + This method runs dhcp4 and returns a tuple: (returncode, stdout, stderr) """ ## @todo: Convert this into generic method and reuse it in dhcp6 @@ -79,9 +90,9 @@ class TestDhcpv4Daemon(unittest.TestCase): fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) # There's potential problem if b10-dhcp4 prints out more - # than 4k of text + # than 16kB of text try: - output = os.read(self.stdout_pipes[0], 4096) + output = os.read(self.stdout_pipes[0], 16384) except OSError: print("No data available from stdout") output = "" @@ -91,7 +102,7 @@ class TestDhcpv4Daemon(unittest.TestCase): output = "" try: - error = os.read(self.stderr_pipes[0], 4096) + error = os.read(self.stderr_pipes[0], 16384) except OSError: print("No data available on stderr") error = "" @@ -128,13 +139,13 @@ class TestDhcpv4Daemon(unittest.TestCase): print(" not that is can bind sockets correctly. Please ignore binding errors.") (returncode, output, error) = self.runCommand(["../b10-dhcp4", "-v"]) - - self.assertEqual( str(output).count("[b10-dhcp4] Initiating DHCPv4 server operation."), 1) + output_text = str(output) + str(error) + self.assertEqual(output_text.count("DHCP4_STARTING"), 1) def test_portnumber_0(self): print("Check that specifying port number 0 is not allowed.") - (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p', '0']) + (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-p', '0']) # When invalid port number is specified, return code must not be success self.assertTrue(returncode != 0) @@ -178,28 +189,19 @@ class TestDhcpv4Daemon(unittest.TestCase): def test_portnumber_nonroot(self): print("Check that specifying unprivileged port number will work.") - (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-s', '-p', '10057']) - - # When invalid port number is specified, return code must not be success - # TODO: Temporarily commented out as socket binding on systems that do not have - # interface detection implemented currently fails. - # self.assertTrue(returncode == 0) - - # Check that there is an error message about invalid port number printed on stderr - self.assertEqual( str(output).count("opening sockets on port 10057"), 1) + # Check that there is a message about running with an unprivileged port + (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-s', '-p', '10057']) + output_text = str(output) + str(error) + self.assertEqual(output_text.count("DHCP4_OPEN_SOCKET opening sockets on port 10057"), 1) def test_skip_msgq(self): print("Check that connection to BIND10 msgq can be disabled.") - (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-s', '-p', '10057']) - - # When invalid port number is specified, return code must not be success - # TODO: Temporarily commented out as socket binding on systems that do not have - # interface detection implemented currently fails. - # self.assertTrue(returncode == 0) - - # Check that there is an error message about invalid port number printed on stderr - self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1) + # Check that the system outputs a message on one of its streams about running + # standalone. + (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-s', '-p', '10057']) + output_text = str(output) + str(error) + self.assertEqual(output_text.count("DHCP4_STANDALONE"), 1) if __name__ == '__main__': unittest.main() diff --git a/src/bin/dhcp6/.gitignore b/src/bin/dhcp6/.gitignore index eedbb846cb..58781893aa 100644 --- a/src/bin/dhcp6/.gitignore +++ b/src/bin/dhcp6/.gitignore @@ -1,11 +1,6 @@ -*~ -Makefile -Makefile.in -*.o -.deps -.libs -b10-dhcp6 -spec_config.h -spec_config.h.pre -tests/dhcp6_unittests +/b10-dhcp6 /b10-dhcp6.8 +/dhcp6_messages.cc +/dhcp6_messages.h +/spec_config.h +/spec_config.h.pre diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index f711c973cf..4dec4e7da6 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -13,7 +13,7 @@ endif pkglibexecdir = $(libexecdir)/@PACKAGE@ -CLEANFILES = spec_config.h +CLEANFILES = spec_config.h dhcp6_messages.h dhcp6_messages.cc man_MANS = b10-dhcp6.8 DISTCLEANFILES = $(man_MANS) @@ -37,11 +37,20 @@ endif spec_config.h: spec_config.h.pre $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@ -BUILT_SOURCES = spec_config.h +dhcp6_messages.h dhcp6_messages.cc: dhcp6_messages.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes + +BUILT_SOURCES = spec_config.h dhcp6_messages.h dhcp6_messages.cc + pkglibexec_PROGRAMS = b10-dhcp6 -b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h +b10_dhcp6_SOURCES = main.cc b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h +b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h +b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h + +nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc +EXTRA_DIST += dhcp6_messages.mes if USE_CLANGPP # Disable unused parameter warning caused by some of the @@ -49,7 +58,7 @@ if USE_CLANGPP b10_dhcp6_CXXFLAGS = -Wno-unused-parameter endif -b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la +b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 461d5f1d64..4afb2039fd 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -13,28 +13,30 @@ // PERFORMANCE OF THIS SOFTWARE. #include + #include #include -#include +#include #include -#include +#include #include #include -#include -#include #include +#include +#include #include -#include +#include +#include -using namespace std; -using namespace isc::util; -using namespace isc::dhcp; -using namespace isc::util; -using namespace isc::data; +using namespace isc::asiolink; using namespace isc::cc; using namespace isc::config; -using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::log; +using namespace isc::util; +using namespace std; namespace isc { namespace dhcp { @@ -43,7 +45,8 @@ ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL; ConstElementPtr ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { - cout << "b10-dhcp6: Received new config:" << new_config->str() << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE) + .arg(new_config->str()); ConstElementPtr answer = isc::config::createAnswer(0, "Thank you for sending config."); return (answer); @@ -51,13 +54,14 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { ConstElementPtr ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr args) { - cout << "b10-dhcp6: Received new command: [" << command << "], args=" - << args->str() << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED) + .arg(command).arg(args->str()); + if (command == "shutdown") { if (ControlledDhcpv6Srv::server_) { ControlledDhcpv6Srv::server_->shutdown(); } else { - cout << "Server not initialized yet or already shut down." << endl; + LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING); ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure."); return (answer); @@ -93,10 +97,9 @@ void ControlledDhcpv6Srv::establishSession() { /// @todo: Check if session is not established already. Throw, if it is. - cout << "b10-dhcp6: my specfile is " << specfile << endl; - + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING) + .arg(specfile); cc_session_ = new Session(io_service_.get_io_service()); - config_session_ = new ModuleCCSession(specfile, *cc_session_, dhcp6ConfigHandler, dhcp6CommandHandler, false); @@ -106,8 +109,8 @@ void ControlledDhcpv6Srv::establishSession() { /// control with the "select" model of the DHCP server. This is /// fully explained in \ref dhcpv6Session. int ctrl_socket = cc_session_->getSocketDesc(); - cout << "b10-dhcp6: Control session started, socket=" - << ctrl_socket << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED) + .arg(ctrl_socket); IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader); } diff --git a/src/bin/dhcp6/dhcp6_log.cc b/src/bin/dhcp6/dhcp6_log.cc new file mode 100644 index 0000000000..d89383492c --- /dev/null +++ b/src/bin/dhcp6/dhcp6_log.cc @@ -0,0 +1,26 @@ +// 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. + +/// Defines the logger used by the top-level component of b10-dhcp6. + +#include "dhcp6_log.h" + +namespace isc { +namespace dhcp { + +isc::log::Logger dhcp6_logger("dhcp6"); + +} // namespace dhcp +} // namespace isc + diff --git a/src/bin/dhcp6/dhcp6_log.h b/src/bin/dhcp6/dhcp6_log.h new file mode 100644 index 0000000000..6d7f4e33b0 --- /dev/null +++ b/src/bin/dhcp6/dhcp6_log.h @@ -0,0 +1,59 @@ +// 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 __DHCP6_LOG__H +#define __DHCP6_LOG__H + +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// \brief DHCP6 Logging +/// +/// Defines the levels used to output debug messages in the non-library part of +/// the b10-dhcp6 program. Higher numbers equate to more verbose (and detailed) +/// output. + +// Debug levels used to log information during startup and shutdown. +const int DBG_DHCP6_START = DBGLVL_START_SHUT; +const int DBG_DHCP6_SHUT = DBGLVL_START_SHUT; + +// Debug level used to log setting information (such as configuration changes). +const int DBG_DHCP6_COMMAND = DBGLVL_COMMAND; + +// Trace basic operations within the code. +const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC; + +// Trace detailed operations, including errors raised when processing invalid +// packets. (These are not logged at severities of WARN or higher for fear +// that a set of deliberately invalid packets set to the server could overwhelm +// the logging.) +const int DBG_DHCP6_DETAIL = DBGLVL_TRACE_DETAIL; + +// This level is used to log the contents of packets received and sent. +const int DBG_DHCP6_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA; + +/// Define the logger for the "dhcp6" module part of b10-dhcp6. We could define +/// a logger in each file, but we would want to define a common name to avoid +/// spelling mistakes, so it is just one small step from there to define a +/// module-common logger. +extern isc::log::Logger dhcp6_logger; + +} // namespace dhcp6 +} // namespace isc + +#endif // __DHCP6_LOG__H diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes new file mode 100644 index 0000000000..a3615319a7 --- /dev/null +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -0,0 +1,101 @@ +# 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. + +$NAMESPACE isc::dhcp + +% DHCP6_CCSESSION_STARTED control channel session started on socket %1 +A debug message issued during startup after the IPv6 DHCP server has +successfully established a session with the BIND 10 control channel. + +% DHCP6_CCSESSION_STARTING starting control channel session, specfile: %1 +This debug message is issued just before the IPv6 DHCP server attempts +to establish a session with the BIND 10 control channel. + +% DHCP6_COMMAND_RECEIVED received command %1, arguments: %2 +A debug message listing the command (and possible arguments) received +from the BIND 10 control system by the IPv6 DHCP server. + +% DHCP6_CONFIG_UPDATE updated configuration received: %1 +A debug message indicating that the IPv6 DHCP server has received an +updated configuration from the BIND 10 configuration system. + +% DHCP6_NOT_RUNNING IPv6 DHCP server is not running +A warning message is issued when an attempt is made to shut down the +IPv6 DHCP server but it is not running. + +% DHCP6_NO_INTERFACES failed to detect any network interfaces +During startup the IPv6 DHCP server failed to detect any network +interfaces and is therefore shutting down. + +% DHCP6_OPEN_SOCKET opening sockets on port %1 +A debug message issued during startup, this indicates that the IPv6 DHCP +server is about to open sockets on the specified port. + +% DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet +The IPv6 DHCP server has received a packet that it is unable to interpret. + +% DHCP6_PACKET_RECEIVED %1 (type %2) packet received +A debug message noting that the server has received the specified type +of packet. Note that a packet marked as UNKNOWN may well be a valid +DHCP packet, just a type not expected by the server (e.g. it will report +a received OFFER packet as UNKNOWN). + +% DHCP6_PACK_FAIL failed to assemble response correctly +This error is output if the server failed to assemble the data to be +returned to the client into a valid packet. The reason is most likely +to be to a programming error: please raise a bug report. + +% DHCP6_QUERY_DATA received packet length %1, data length %2, data is <%3> +A debug message listing the data received from the client or relay. + +% DHCP6_RESPONSE_DATA responding with packet type %1 data is <%2> +A debug message listing the data returned to the client. + +% DHCP6_SERVER_FAILED server failed: %1 +The IPv6 DHCP server has encountered a fatal error and is terminating. +The reason for the failure is included in the message. + +% DHCP6_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone +The server has failed to establish communication with the rest of BIND +10 and is running in stand-alone mode. (This behavior will change once +the IPv6 DHCP server is properly integrated with the rest of BIND 10.) + +% DHCP6_SHUTDOWN server shutdown +The IPv6 DHCP server has terminated normally. + +% DHCP6_SHUTDOWN_REQUEST shutdown of server requested +This debug message indicates that a shutdown of the IPv6 server has +been requested via a call to the 'shutdown' method of the core Dhcpv6Srv +object. + +% DHCP6_SRV_CONSTRUCT_ERROR error creating Dhcpv6Srv object, reason: %1 +This error message indicates that during startup, the construction of a +core component within the IPv6 DHCP server (the Dhcpv6 server object) +has failed. As a result, the server will exit. The reason for the +failure is given within the message. + +% DHCP6_STANDALONE skipping message queue, running standalone +This is a debug message indicating that the IPv6 server is running in +standalone mode, not connected to the message queue. Standalone mode +is only useful during program development, and should not be used in a +production environment. + +% DHCP6_STARTING server starting +This informational message indicates that the IPv6 DHCP server has +processed any command-line switches and is starting. + +% DHCP6_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4 +This is a debug message issued during the IPv6 DHCP server startup. +It lists some information about the parameters with which the server +is running. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 9b43f5374f..7c21941d4d 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -14,23 +14,25 @@ #include #include -#include -#include -#include -#include -#include -#include -#include + #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -using namespace std; using namespace isc; -using namespace isc::dhcp; using namespace isc::asiolink; +using namespace isc::dhcp; using namespace isc::util; +using namespace std; const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd"; const uint32_t HARDCODED_T1 = 1500; // in seconds @@ -40,14 +42,14 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1"; Dhcpv6Srv::Dhcpv6Srv(uint16_t port) { - cout << "Initialization: opening sockets on port " << port << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port); - // first call to instance() will create IfaceMgr (it's a singleton) + // First call to instance() will create IfaceMgr (it's a singleton) // it may throw something if things go wrong try { if (IfaceMgr::instance().countIfaces() == 0) { - cout << "Failed to detect any network interfaces. Aborting." << endl; + LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES); shutdown_ = true; return; } @@ -59,7 +61,7 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) { /// @todo: instantiate LeaseMgr here once it is imlpemented. } catch (const std::exception &e) { - cerr << "Error during DHCPv4 server startup: " << e.what() << endl; + LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what()); shutdown_ = true; return; } @@ -68,13 +70,11 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) { } Dhcpv6Srv::~Dhcpv6Srv() { - cout << "DHCPv6 Srv shutdown." << endl; - IfaceMgr::instance().closeSockets(); } void Dhcpv6Srv::shutdown() { - cout << "b10-dhcp6: DHCPv6 server shutdown." << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SHUTDOWN_REQUEST); shutdown_ = true; } @@ -89,42 +89,58 @@ bool Dhcpv6Srv::run() { if (query) { if (!query->unpack()) { - cout << "Failed to parse incoming packet" << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, + DHCP6_PACKET_PARSE_FAIL); continue; } + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED) + .arg(serverReceivedPacketName(query->getType())) + .arg(query->getType()); + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA) + .arg(query->getType()) + .arg(query->getBuffer().getLength()) + .arg(query->toText()); + switch (query->getType()) { case DHCPV6_SOLICIT: rsp = processSolicit(query); break; + case DHCPV6_REQUEST: rsp = processRequest(query); break; + case DHCPV6_RENEW: rsp = processRenew(query); break; + case DHCPV6_REBIND: rsp = processRebind(query); break; + case DHCPV6_CONFIRM: rsp = processConfirm(query); break; + case DHCPV6_RELEASE: rsp = processRelease(query); break; + case DHCPV6_DECLINE: rsp = processDecline(query); break; + case DHCPV6_INFORMATION_REQUEST: rsp = processInfRequest(query); break; + default: - cout << "Unknown pkt type received:" - << query->getType() << endl; + // Only action is to output a message if debug is enabled, + // and that will be covered by the debug statement before + // the "switch" statement. + ; } - cout << "Received " << query->getBuffer().getLength() << " bytes packet type=" - << query->getType() << endl; - cout << query->toText(); if (rsp) { rsp->setRemoteAddr(query->getRemoteAddr()); rsp->setLocalAddr(query->getLocalAddr()); @@ -132,14 +148,16 @@ bool Dhcpv6Srv::run() { rsp->setLocalPort(DHCP6_SERVER_PORT); rsp->setIndex(query->getIndex()); rsp->setIface(query->getIface()); - cout << "Replying with:" << rsp->getType() << endl; - cout << rsp->toText(); - cout << "----" << endl; - if (!rsp->pack()) { - cout << "Failed to assemble response packet." << endl; - continue; + + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, + DHCP6_RESPONSE_DATA) + .arg(rsp->getType()).arg(rsp->toText()); + + if (rsp->pack()) { + IfaceMgr::instance().send(rsp); + } else { + LOG_ERROR(dhcp6_logger, DHCP6_PACK_FAIL); } - IfaceMgr::instance().send(rsp); } } @@ -350,3 +368,46 @@ Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) { Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid())); return reply; } + +const char* +Dhcpv6Srv::serverReceivedPacketName(uint8_t type) { + static const char* CONFIRM = "CONFIRM"; + static const char* DECLINE = "DECLINE"; + static const char* INFORMATION_REQUEST = "INFORMATION_REQUEST"; + static const char* REBIND = "REBIND"; + static const char* RELEASE = "RELEASE"; + static const char* RENEW = "RENEW"; + static const char* REQUEST = "REQUEST"; + static const char* SOLICIT = "SOLICIT"; + static const char* UNKNOWN = "UNKNOWN"; + + switch (type) { + case DHCPV6_CONFIRM: + return (CONFIRM); + + case DHCPV6_DECLINE: + return (DECLINE); + + case DHCPV6_INFORMATION_REQUEST: + return (INFORMATION_REQUEST); + + case DHCPV6_REBIND: + return (REBIND); + + case DHCPV6_RELEASE: + return (RELEASE); + + case DHCPV6_RENEW: + return (RENEW); + + case DHCPV6_REQUEST: + return (REQUEST); + + case DHCPV6_SOLICIT: + return (SOLICIT); + + default: + ; + } + return (UNKNOWN); +} diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 9d1bf19497..1cb323657a 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -69,6 +69,24 @@ public: /// @brief Instructs the server to shut down. void shutdown(); + + /// @brief Return textual type of packet received by server + /// + /// Returns the name of valid packet received by the server (e.g. SOLICIT). + /// If the packet is unknown - or if it is a valid DHCP packet but not one + /// expected to be received by the server (such as an ADVERTISE), the string + /// "UNKNOWN" is returned. This method is used in debug messages. + /// + /// As the operation of the method does not depend on any server state, it + /// is declared static. + /// + /// @param type DHCPv4 packet type + /// + /// @return Pointer to "const" string containing the packet name. + /// Note that this string is statically allocated and MUST NOT + /// be freed by the caller. + static const char* serverReceivedPacketName(uint8_t type); + protected: /// @brief Processes incoming SOLICIT and returns response. /// diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc index aebee90699..8eaf60cb97 100644 --- a/src/bin/dhcp6/main.cc +++ b/src/bin/dhcp6/main.cc @@ -14,13 +14,15 @@ #include #include -#include -#include -#include + #include -using namespace std; +#include +#include +#include + using namespace isc::dhcp; +using namespace std; /// This file contains entry point (main() function) for standard DHCPv6 server /// component for BIND10 framework. It parses command-line arguments and @@ -37,11 +39,10 @@ const char* const DHCP6_NAME = "b10-dhcp6"; void usage() { - cerr << "Usage: b10-dhcp6 [-v]" - << endl; - cerr << "\t-v: verbose output" << endl; - cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl; - cerr << "\t-p number: specify non-standard port number 1-65535 " + cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p number]" << endl; + cerr << " -v: verbose output" << endl; + cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl; + cerr << " -p number: specify non-standard port number 1-65535 " << "(useful for testing only)" << endl; exit(EXIT_FAILURE); } @@ -52,18 +53,19 @@ main(int argc, char* argv[]) { int ch; int port_number = DHCP6_SERVER_PORT; // The default. Any other values are // useful for testing only. + bool stand_alone = false; // Should be connect to BIND10 msgq? bool verbose_mode = false; // Should server be verbose? - bool stand_alone = false; // should be connect to BIND10 msgq? while ((ch = getopt(argc, argv, "vsp:")) != -1) { switch (ch) { case 'v': verbose_mode = true; - isc::log::denabled = true; break; + case 's': stand_alone = true; break; + case 'p': try { port_number = boost::lexical_cast(optarg); @@ -78,51 +80,45 @@ main(int argc, char* argv[]) { usage(); } break; - case ':': + default: usage(); } } + // Check for extraneous parameters. + if (argc > optind) { + usage(); + } + // Initialize logging. If verbose, we'll use maximum verbosity. isc::log::initLogger(DHCP6_NAME, (verbose_mode ? isc::log::DEBUG : isc::log::INFO), isc::log::MAX_DEBUG_LEVEL, NULL); - - cout << "b10-dhcp6: My pid=" << getpid() << ", binding to port " - << port_number << ", verbose " << (verbose_mode?"yes":"no") - << ", stand-alone=" << (stand_alone?"yes":"no") << endl; - - if (argc - optind > 0) { - usage(); - } + LOG_INFO(dhcp6_logger, DHCP6_STARTING); + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) + .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no") + .arg(stand_alone ? "yes" : "no" ); int ret = EXIT_SUCCESS; - try { - - cout << "b10-dhcp6: Initiating DHCPv6 server operation." << endl; - - /// @todo: pass verbose to the actual server once logging is implemented ControlledDhcpv6Srv server(port_number); - if (!stand_alone) { try { server.establishSession(); } catch (const std::exception& ex) { - cerr << "Failed to establish BIND10 session. " - "Running in stand-alone mode:" << ex.what() << endl; + LOG_ERROR(dhcp6_logger, DHCP6_SESSION_FAIL).arg(ex.what()); // Let's continue. It is useful to have the ability to run // DHCP server in stand-alone mode, e.g. for testing } } else { - cout << "Skipping connection to the BIND10 msgq." << endl; + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE); } - server.run(); + LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN); } catch (const std::exception& ex) { - cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl; + LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what()); ret = EXIT_FAILURE; } diff --git a/src/bin/dhcp6/tests/.gitignore b/src/bin/dhcp6/tests/.gitignore new file mode 100644 index 0000000000..e170d18915 --- /dev/null +++ b/src/bin/dhcp6/tests/.gitignore @@ -0,0 +1 @@ +/dhcp6_unittests diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 66dc252830..1d9308fcaa 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -26,7 +26,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/bin AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" -CLEANFILES = $(builddir)/interfaces.txt +CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile AM_CXXFLAGS = $(B10_CXXFLAGS) @@ -42,10 +42,13 @@ if HAVE_GTEST TESTS += dhcp6_unittests -dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc ../ctrl_dhcp6_srv.cc -dhcp6_unittests_SOURCES += dhcp6_unittests.cc +dhcp6_unittests_SOURCES = dhcp6_unittests.cc dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc +dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc +dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc +dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc +nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc if USE_CLANGPP # Disable unused parameter warning caused by some of the diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 5e98e18ed2..20287066da 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 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 @@ -223,4 +223,49 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) { // more checks to be implemented } +TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) { + // Check all possible packet types + for (int itype = 0; itype < 256; ++itype) { + uint8_t type = itype; + + switch (type) { + case DHCPV6_CONFIRM: + EXPECT_STREQ("CONFIRM", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_DECLINE: + EXPECT_STREQ("DECLINE", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_INFORMATION_REQUEST: + EXPECT_STREQ("INFORMATION_REQUEST", + Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_REBIND: + EXPECT_STREQ("REBIND", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_RELEASE: + EXPECT_STREQ("RELEASE", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_RENEW: + EXPECT_STREQ("RENEW", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_REQUEST: + EXPECT_STREQ("REQUEST", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + case DHCPV6_SOLICIT: + EXPECT_STREQ("SOLICIT", Dhcpv6Srv::serverReceivedPacketName(type)); + break; + + default: + EXPECT_STREQ("UNKNOWN", Dhcpv6Srv::serverReceivedPacketName(type)); + } + } } + +} // end of anonymous namespace diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py index 399c370742..f3099b9a83 100644 --- a/src/bin/dhcp6/tests/dhcp6_test.py +++ b/src/bin/dhcp6/tests/dhcp6_test.py @@ -27,16 +27,27 @@ import fcntl class TestDhcpv6Daemon(unittest.TestCase): def setUp(self): - # don't redirect stdout/stderr here as we want to print out things + # Don't redirect stdout/stderr here as we want to print out things # during the test - pass + # + # However, we do want to set the logging lock directory to somewhere + # to which we can write - use the current working directory. We then + # set the appropriate environment variable. os.putenv() may be not + # supported on some platforms as suggested in + # http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ: + # "If the platform supports the putenv() function...". It was checked + # that it does not work on Ubuntu. To overcome this problem we access + # os.environ directly. + lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD" + if lockdir_envvar not in os.environ: + os.environ[lockdir_envvar] = os.getcwd() def tearDown(self): pass def runCommand(self, params, wait=1): """ - This method runs a command and returns a touple: (returncode, stdout, stderr) + This method runs a command and returns a tuple: (returncode, stdout, stderr) """ ## @todo: Convert this into generic method and reuse it in dhcp4 and dhcp6 @@ -79,9 +90,9 @@ class TestDhcpv6Daemon(unittest.TestCase): fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) # There's potential problem if b10-dhcp4 prints out more - # than 4k of text + # than 16k of text try: - output = os.read(self.stdout_pipes[0], 4096) + output = os.read(self.stdout_pipes[0], 16384) except OSError: print("No data available from stdout") output = "" @@ -91,7 +102,7 @@ class TestDhcpv6Daemon(unittest.TestCase): output = "" try: - error = os.read(self.stderr_pipes[0], 4096) + error = os.read(self.stderr_pipes[0], 16384) except OSError: print("No data available on stderr") error = "" @@ -130,8 +141,8 @@ class TestDhcpv6Daemon(unittest.TestCase): print("Note: Purpose of some of the tests is to check if DHCPv6 server can be started,") print(" not that is can bind sockets correctly. Please ignore binding errors.") (returncode, output, error) = self.runCommand(["../b10-dhcp6", "-v"]) - - self.assertEqual( str(output).count("b10-dhcp6: Initiating DHCPv6 server operation."), 1) + output_text = str(output) + str(error) + self.assertEqual(output_text.count("DHCP6_STARTING"), 1) def test_portnumber_0(self): print("Check that specifying port number 0 is not allowed.") @@ -180,27 +191,19 @@ class TestDhcpv6Daemon(unittest.TestCase): def test_portnumber_nonroot(self): print("Check that specifying unprivileged port number will work.") - (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547']) - - # When invalid port number is specified, return code must not be success - # TODO: Temporarily commented out as socket binding on systems that do not have - # interface detection implemented currently fails. - # self.assertTrue(returncode == 0) - - self.assertEqual( str(output).count("opening sockets on port 10547"), 1) + # Check that there is a message about running with an unprivileged port + (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-v', '-s', '-p', '10547']) + output_text = str(output) + str(error) + self.assertEqual(output_text.count("DHCP6_OPEN_SOCKET opening sockets on port 10547"), 1) def test_skip_msgq(self): print("Check that connection to BIND10 msgq can be disabled.") - (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547']) - - # When invalid port number is specified, return code must not be success - # TODO: Temporarily commented out as socket binding on systems that do not have - # interface detection implemented currently fails. - # self.assertTrue(returncode == 0) - - self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1) - + # Check that the system outputs a message on one of its streams about running + # standalone. + (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-v', '-s', '-p', '10547']) + output_text = str(output) + str(error) + self.assertEqual(output_text.count("DHCP6_STANDALONE"), 1) if __name__ == '__main__': unittest.main() diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml index f79a42d08f..4415cffb78 100644 --- a/src/bin/xfrout/b10-xfrout.xml +++ b/src/bin/xfrout/b10-xfrout.xml @@ -153,6 +153,54 @@ + + STATISTICS DATA + + + The statistics data collected by the b10-xfrout + daemon for Xfrout include: + + + + + + notifyoutv4 + + Number of IPv4 notifies per zone name sent out from Xfrout + + + + + notifyoutv6 + + Number of IPv6 notifies per zone name sent out from Xfrout + + + + + xfrrej + + Number of XFR requests per zone name rejected by Xfrout + + + + + xfrreqdone + + Number of requested zone transfers per zone name completed + + + + + + + In per-zone counters the special zone name '_SERVER_' exists. It doesn't + mean a specific zone. It represents an entire server and its value means + a total count of all zones. + + + + - - + + DHCP Performance Guide @@ -37,7 +37,10 @@ Tomasz Mrugalski - + + Marcin + Siodelski + BIND 10 is a framework that features Domain Name System (DNS) and Dynamic Host Configuration Protocol (DHCP) @@ -943,9 +946,507 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7 perfdhcp - - TODO: Write something about perfdhcp here. - - +
+ Purpose + + Evaluation of the performance of a DHCP server requires that it + be tested under varying traffic loads. perfdhcp is a testing + tool with the capability to create traffic loads + and generate statistics from the results. Additional features, + such as the ability to send customised DHCP packets, allow it to + be used in a wide range of functional testing. + +
+
+ Key features + + perfdhcp has a number of command line switches to + control DHCP message exchanges. Currently they fall into + the following categories: + + + + Rate control - control how many DHCP exchanges + are initiated within a period of time. The tool can also simulate + best effort conditions by attempting to initiate as many DHCP + packet exchanges as possible within a unit of time. + + + + + Test exit specifiers - control the conditions for test + completion, including the number of initiated exchanges, + the test period orthe maximum number of dropped packets. + + + + + Packet templates - specify files containing packet templates that + are used by perfdhcp to create custom DHCP messages. The tool + allows the specification of a number of values indicating + offsets of values within a packet that are set by the tool. + + + + + Reporting - for each test produce a set of performance data + including the achieved packet exchange rate (server performance). + There are a number of diagnostic selectors available that + enable periodic (intermediate) reporting, printing of packet timestamps, + and the listing of detailed information about internal perfdhcp + states (for debugging). + + + + + Different mode of operations - specify the DHCP protocol used + (v4 or v6), two-way or four-way exchanges, use of the + Rapid Commit option for DHCPv6. + + + + + IP layer options - specify the local/remote address, local interface + and local port to be used for communication with DHCP server. + + + + +
+
+ Command line options + + The following "help" output from the tool describes the + command line switches. This summary also lists the tool's + possible exit codes as well as describing the + error counters printed when the test is complete: + $ ./perfdhcp -h +] [-t] [-R] [-b] + [-n] [-p] [-d] [-D] + [-l] [-P] [-a] + [-L] [-s] [-i] [-B] [-c] [-1] + [-T] [-X] [-O] [-S] [-I] + [-x] [-w] [server] +The [server] argument is the name/address of the DHCP server to +contact. For DHCPv4 operation, exchanges are initiated by +transmitting a DHCP DISCOVER to this address. + +For DHCPv6 operation, exchanges are initiated by transmitting a DHCP +SOLICIT to this address. In the DHCPv6 case, the special name 'all' +can be used to refer to All_DHCP_Relay_Agents_and_Servers (the +multicast address FF02::1:2), or the special name 'servers' to refer +to All_DHCP_Servers (the multicast address FF05::1:3). The [server] +argument is optional only in the case that -l is used to specify an +interface, in which case [server] defaults to 'all'. + +The default is to perform a single 4-way exchange, effectively pinging +the server. +The -r option is used to set up a performance test, without +it exchanges are initiated as fast as possible. + +Options: +-1: Take the server-ID option from the first received message. +-4: DHCPv4 operation (default). This is incompatible with the -6 option. +-6: DHCPv6 operation. This is incompatible with the -4 option. +-a: When the target sending rate is not yet reached, + control how many exchanges are initiated before the next pause. +-b: The base mac, duid, IP, etc, used to simulate different + clients. This can be specified multiple times, each instance is + in the = form, for instance: + (and default) mac=00:0c:01:02:03:04. +-d: Specify the time after which a request is treated as + having been lost. The value is given in seconds and may contain a + fractional component. The default is 1 second. +-E: Offset of the (DHCPv4) secs field / (DHCPv6) + elapsed-time option in the (second/request) template. + The value 0 disables it. +-h: Print this help. +-i: Do only the initial part of an exchange: DO or SA, depending on + whether -6 is given. +-I: Offset of the (DHCPv4) IP address in the requested-IP + option / (DHCPv6) IA_NA option in the (second/request) template. +-l: For DHCPv4 operation, specify the local + hostname/address to use when communicating with the server. By + default, the interface address through which traffic would + normally be routed to the server is used. + For DHCPv6 operation, specify the name of the network interface + via which exchanges are initiated. +-L: Specify the local port to use + (the value 0 means to use the default). +-O: Offset of the last octet to randomize in the template. +-P: Initiate first exchanges back to back at startup. +-r: Initiate DORA/SARR (or if -i is given, DO/SA) + exchanges per second. A periodic report is generated showing the + number of exchanges which were not completed, as well as the + average response latency. The program continues until + interrupted, at which point a final report is generated. +-R: Specify how many different clients are used. With 1 + (the default), all requests seem to come from the same client. +-s: Specify the seed for randomization, making it repeatable. +-S: Offset of the server-ID option in the + (second/request) template. +-T: The name of a file containing the template to use + as a stream of hexadecimal digits. +-v: Report the version number of this program. +-w: Command to call with start/stop at the beginning/end of + the program. +-x: Include extended diagnostics in the output. + is a string of single-keywords specifying + the operations for which verbose output is desired. The selector + keyletters are: + * 'a': print the decoded command line arguments + * 'e': print the exit reason + * 'i': print rate processing details + * 'r': print randomization details + * 's': print first server-id + * 't': when finished, print timers of all successful exchanges + * 'T': when finished, print templates +-X: Transaction ID (aka. xid) offset in the template. + +DHCPv4 only options: +-B: Force broadcast handling. + +DHCPv6 only options: +-c: Add a rapid commit option (exchanges will be SA). + +The remaining options are used only in conjunction with -r: + +-D: Abort the test if more than requests have + been dropped. Use -D0 to abort if even a single request has been + dropped. If includes the suffix '%', it specifies a + maximum percentage of requests that may be dropped before abort. + In this case, testing of the threshold begins after 10 requests + have been expected to be received. +-n: Initiate transactions. No report is + generated until all transactions have been initiated/waited-for, + after which a report is generated and the program terminates. +-p: Send requests for the given test period, which is + specified in the same manner as -d. This can be used as an + alternative to -n, or both options can be given, in which case the + testing is completed when either limit is reached. +-t: Delay in seconds between two periodic reports. + +Errors: +- tooshort: received a too short message +- orphans: received a message which doesn't match an exchange + (duplicate, late or not related) +- locallimit: reached to local system limits when sending a message. + +Exit status: +The exit status is: +0 on complete success. +1 for a general error. +2 if an error is found in the command line arguments. +3 if there are no general failures in operation, but one or more + exchanges are not successfully completed. +]]> + + +
+
+ Starting perfdhcp + + In order to run a performance test, at least two separate systems + have to be installed: client and server. The first one must have + perfdhcp installed, and the latter must be running the DHCP server + (either v4 or v6). If only single system is available the client + and server can be run on virtual machines (running on the same + physical system) but in this case performance data may be heavily + impacted by the overhead involved in running such the virtual + machines. + + + Currently, perfdhcp is seen from the server perspective as relay agent. + This simplifies its implementation: specifically there is no need to + receive traffic sent to braodcast addresses. However, it does impose + a requirement that the IPv4 + address has to be set manually on the interface that will be used to + communicate with the server. For example, if the DHCPv4 server is listening + on the interface connected to the 172.16.1.0 subnet, the interface on client + machine has to have network address assigned from the same subnet, e.g. + #ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up + + + As DHCP uses low port numbers (67 for DHCPv4 relays and + 547 for DHCPv6), running perfdhcp with non-root privileges will + usually result in the error message similar to this: + $./perfdhcp -4 -l eth3 -r 100 all +Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 + + The '-L' command line switch allows the use of a custom local port. + However, although the following command line will work: + $./perfdhcp -4 -l eth3 -r 100 -L 10067 all + in the standard configuration no responses will be received + from the DHCP server because the server responds to default relay + port 67. A way to overcome this issue is to run + perfdhcp as root. + + +
+
+ perfdhcp command line examples + + In this section, a number of perfdhcp command line examples + are presented as a quick start guide for new users. For the + detailed list of command line options refer to + . + +
+ Example: basic usage + + If server is listening on interface with IPv4 address 172.16.1.1, + the simplest perfdhcp command line will look like: + #./perfdhcp 172.16.1.1 +***Rate statistics*** +Rate: 206.345 + +***Statistics for: DISCOVER-OFFER*** +sent packets: 21641 +received packets: 350 +drops: 21291 +orphans: 0 + +min delay: 9.022 ms +avg delay: 143.100 ms +max delay: 259.303 ms +std deviation: 56.074 ms +collected packets: 30 + +***Statistics for: REQUEST-ACK*** +sent packets: 350 +received packets: 268 +drops: 82 +orphans: 0 + +min delay: 3.010 ms +avg delay: 152.470 ms +max delay: 258.634 ms +std deviation: 56.936 ms +collected packets: 0 + + Here, perfdhcp uses remote address 172.16.1.1 as a + destination address and will use a suitable local interface for + communication. Since, no rate control parameters have been specified, + it will initiate DHCP exchanges at the maximum possible rate. Due to the server's + performance limitation, it is likely that many of the packets will be dropped. + The performance test will continue running until it is + interrupted by the user (with ^C). + + + The default performance statistics reported by perfdhcp have the + following meaning: + + Rate - number of packet exchanges (packet sent + to the server and matching response received from the server) + completed within a second. + sent packets - total number of DHCP packets of + a specific type sent to the server. + received packets - total number of DHCP packets + of specific type received from the server. + drops - number of dropped packets for the + particular exchange. The number of dropped packets is calculated as + the difference between the number of sent packets and number of + response packets received from the server. In some cases, the + server will have sent a reponse but perfdhcp execution ended before + the reponse arrived. In such case this packet will be counted + as dropped. + orphans - number of packets that have been + received from the server and did not match any packet sent by + perfdhcp. This may occur if received packet has been sent + to some other host or if then exchange timed out and + the sent packet was removed from perfdhcp's list of packets + awaiting a response. + min delay - minimum delay that occured between + sending the packet to the server and receiving a reponse from + it. + avg delay - average delay between sending the + packet of the specific type the server and receiving a response + from it. + max delay - maximum delay that occured between + sending the packet to the server and receiving a response from + it. + std deviation - standard deviation of the delay + between sending the packet of a specific type to the server and + receiving response from it. + collected packets - number of sent packets that + were garbage collected. Packets may get garbage collected when + the waiting time for server a response exceeds value set with the + '-d' (drop time) switch. + ]]>. + + + + Note: should multiple interfaces on the system running perfdhcp be + connected to the same subnet, the interface to be used for the test + can be specified using either the interface name: + #./perfdhcp -l eth3 + or a local address assigned to it: + #./perfdhcp -l 172.16.1.2 + +
+
+ Example: rate control + + In the examples above perfdhcp initiates new exchanges with a best + effort rate. With this setting, many packets are expected to be dropped + by the server due to performance limitations. In many cases though, it is + desired to verify that the server can handle an expected (reasonable) rate + without dropping any packets. The following command is an example of such + a test: it causes perfdhcp to initiate 300 four-way exchanges + per second, and runs the test for 60 seconds: + #./perfdhcp -l eth3 -p 60 -r 300 +***Rate statistics*** +Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second + +***Statistics for: DISCOVER-OFFER*** +sent packets: 17783 +received packets: 15401 +drops: 2382 +orphans: 0 + +min delay: 0.109 ms +avg delay: 75.756 ms +max delay: 575.614 ms +std deviation: 60.513 ms +collected packets: 11 + +***Statistics for: REQUEST-ACK*** +sent packets: 15401 +received packets: 15317 +drops: 84 +orphans: 0 + +min delay: 0.536 ms +avg delay: 72.072 ms +max delay: 576.749 ms +std deviation: 58.189 ms +collected packets: 0 + + Note that here, the packet drops for the DISCOVER-OFFER + exchange have been significantly reduced (when compared with the + output from the previous example) thanks to the setting of a + reasonable rate. The non-zero number of packet drops and achieved + rate (256/s) indicate that server's measured performance is lower than 300 leases + per second. A further rate decrease should eliminate most of the packet + drops and bring the achieved rate close to expected rate: + #./perfdhcp -l eth3 -p 60 -r 100 -R 30 +***Rate statistics*** +Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second + +***Statistics for: DISCOVER-OFFER*** +sent packets: 5989 +received packets: 5989 +drops: 0 +orphans: 0 + +min delay: 0.023 ms +avg delay: 2.198 ms +max delay: 181.760 ms +std deviation: 9.429 ms +collected packets: 0 + +***Statistics for: REQUEST-ACK*** +sent packets: 5989 +received packets: 5989 +drops: 0 +orphans: 0 + +min delay: 0.473 ms +avg delay: 2.355 ms +max delay: 189.658 ms +std deviation: 5.876 ms +collected packets: 0 + + There are now no packet drops, confirming that the server is able to + handle a load of 100 leases/second. + Note that the last parameter (-R 30) configures perfdhcp to simulate + traffic from 30 distinct clients. + +
+
+ Example: templates + + By default the DHCP messages are formed with default options. With + template files, it is possible to define a custom packet format. + + + The template file names are specified with ]]> + command line option. This option can be specified zero, one or two times. + The first occurence of this option refers to DISCOVER or SOLICIT message + and the second occurence refers to REQUEST (DHCPv4 or DHCPv6) message. + If -T option occurs only once the DISCOVER or SOLICIT message will be + created from the template and the REQUEST message will be created + dynamically (without the template). Note that each template file + holds data for exactly one DHCP message type. Templates for multiple + message types must not be combined in the single file. + The content in template files is encoded as series of ASCII hexadecimal + digits (each byte represented by two ASCII chars 00..FF). Data in a + template file is laid in network byte order and it can be used on the + systems with different endianess. + perfdhcp forms the packet by replacing parts of the message buffer read + from the file with variable data such as elapsed time, hardware address, DUID + etc. The offsets where such variable data is placed is specific to the + template file and have to be specified from the command line. Refer to + to find out how to + specify offsets for particular options and fields. With the following + command line the DHCPv6 SOLICIT and REQUEST packets will be formed from + solicit.hex and request6.hex packets: + #./perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers +***Rate statistics*** +Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second + +***Statistics for: SOLICIT-ADVERTISE*** +sent packets: 570 +received packets: 569 +drops: 1 +orphans: 0 + +min delay: 0.259 ms +avg delay: 0.912 ms +max delay: 6.979 ms +std deviation: 0.709 ms +collected packets: 0 + +***Statistics for: REQUEST-REPLY*** +sent packets: 569 +received packets: 569 +drops: 0 +orphans: 0 + +min delay: 0.084 ms +avg delay: 0.607 ms +max delay: 6.490 ms +std deviation: 0.518 ms +collected packets: 0 + + where the switches have the following meaning: + + two occurences of -O 21 - DUID's last octet + positions in SOLICIT and REQUEST respectively. + -E 84 - elapsed time option position in the + REQUEST template + -S 22 - server id position in the REQUEST + template + -I 40 - IA_NA option position in the REQUEST + template + + + + The offsets of options indicate where they begin in the packet. + The only exception from this rule is ]]> + option that specifies the end of the DUID (DHCPv6) or MAC address + (DHCPv4). Depending on the number of simulated clients + (see ]]> command line option) perfdhcp + will be randomizing bytes in packet buffer starting from this + position backwards. For the number of simulated clients + 256 only one octet (at random-offset position) + will be ranomized, for the number of clients 65536 + two octets (at random-offset and random-offset-1) + will be randmized etc. + +
+
+
diff --git a/tests/tools/perfdhcp/.gitignore b/tests/tools/perfdhcp/.gitignore index 1a8375a158..366076699c 100644 --- a/tests/tools/perfdhcp/.gitignore +++ b/tests/tools/perfdhcp/.gitignore @@ -1 +1,2 @@ /perfdhcp +/perfdhcp2 diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am index 0532f27dcc..816eeeb270 100644 --- a/tests/tools/perfdhcp/Makefile.am +++ b/tests/tools/perfdhcp/Makefile.am @@ -42,3 +42,6 @@ perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la #pkglibexec_PROGRAMS = perfdhcp #perfdhcp_SOURCES = perfdhcp.c + +# ... and the documentation +EXTRA_DIST = perfdhcp_internals.dox diff --git a/tests/tools/perfdhcp/perfdhcp_internals.dox b/tests/tools/perfdhcp/perfdhcp_internals.dox new file mode 100644 index 0000000000..29bc75603f --- /dev/null +++ b/tests/tools/perfdhcp/perfdhcp_internals.dox @@ -0,0 +1,172 @@ +// 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. + +/// @namespace perfdhcp +/// @page perfdhcpInternals perfdhcp Internals +/// +/// The perfdhcp utility provides a way of measuring the performance of +/// DHCP servers by generating large amounts of traffic. Written in C++, +/// its use is described in detail in the DHCP Performance Guide. +/// +/// This document is aimed at people wishing to understand the internals +/// of the perfdhcp program. It describes the major components in the +/// utility and their interaction. +/// +/// @section perfdhcpClasses perfdhcp Classes +/// +/// @subsection perfdhcpCommandOptions CommandOptions (Command Options) +/// +/// isc::perfdhcp::CommandOptions is a singleton class that parses +/// the perfdhcp command line parameters and initializes its members +/// accordingly. If the parameters are invalid, the parser method throws +/// an exception. Usage is simple: +/// +/// @code int main(int argc, char* argv[]) { +/// try { +/// CommandOptions& command_options = CommandOptions::instance(); +/// command_options.parse(argc, argv); +/// } catch(const Exception& e) { +/// ... +/// } +/// @endcode +/// +/// If the argument parsing is successful, the parsed values can be read +/// from the isc::perfdhcp::CommandOptions singleton from any class or +/// function in the program, e.g. +/// +/// @code +/// int rate = CommandOptions::instance().getRate(); +/// @endcode +/// +/// @subsection perfdhcpTestControl TestControl (Test Control) +/// +/// The isc::perfdhcp::TestControl singleton is responsible +/// for test execution coordination. It relies on the +/// isc::perfdhcp::CommandOptions object to get all required test +/// parameters and for this reason, isc::perfdhcp::CommandOptions has +/// to be initialized and isc::perfdhcp::CommandOptions::parse() called +/// prior to calling isc::perfdhcp::TestControl::run(). +/// +/// isc::perfdhcp::TestControl::run() performs initialization of +/// isc::perfdhcp::TestControl then executes the main program loop. In +/// detail, isc::perfdhcp::TestControl::run() performs the following +/// major operations: +/// +/// -# check if the command line has been parsed, +/// -# print diagnostics if specified from command line, +/// -# register DHCP options factory functions, +/// -# read packet templates from files, +/// -# initialize the isc::perfdhcp::StatisticsManager object, +/// -# set interrupt signal handler (handle ^C), +/// -# open and close socket for communication with server, +/// -# coordinate sending and receiving packets, +/// -# coordinate intermediate reporting, +/// -# prints test statistics. +/// +/// isc::perfdhcp::TestControl is a singleton object, so there is one sole +/// instance of it throughout the program. In order to allow the running +/// of unit tests, where a single instance of isc::perfdhcp::TestControl +/// is used multiple times with different command line options, a +/// isc::perfdhcp::TestControl::reset() function is provided to reset +/// the state of the class members. Also, functions that initialize +/// various class members (such as Statistics Manager) will release +/// any objects from previous test runs. +/// +/// @subsection perfStatsMgr StatsMgr (Statistics Manager) +/// +/// isc::perfdhcp::StatsMgr is a class that holds all performance +/// statistics gathered throughout the test execution and is created +/// in isc::perfdhcp::TestControl. isc::perfdhcp::TestControl posts all +/// sent and received packets to isc::perfdhcp::StatsMgr: outgoing packets +/// are recorded and incoming packets are matched with the corresponding +/// outgoing packer to calculate calculate round trip time, number of +/// packet drops etc. Apart from the standard counters implemented in +/// isc::perfdhcp::StatsMgr, custom (named) counters can be specified and +/// incremented by the calling class. isc::perfdhcp::StatsMgr also exposes +/// multiple functions that print gathered statistics into the console. +/// +/// isc::perfdhcp::StatsMgr is a template class that takes an +/// isc::dhcp::Pkt4, isc::dhcp::Pkt6, isc::perfdhcp::PerfPkt4 +/// or isc::perfdhcp::PerfPkt6 as a typename. An instance of +/// isc::perfdhcp::StatsMgr can be created by: +/// +/// @code +/// typedef StatsMgr StatsMgr4; StatsMgr4 stats_mgr4 = +/// boost::shared_ptr(new StatsMgr4()); try { +/// stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO); +/// } catch(const Exception& e) { +/// std::cout << e.what() << std::endl; +/// } +/// @endcode +/// +/// The isc::perfdhcp::StatsMgr instance created in the example above will be used +/// for DHCPv4 testing (i.e. to collect DHCPv4 packets) and will be +/// configured to monitor statistics for DISCOVER-OFFER packet exchanges. +/// +/// @subsection perfdhcpPkt PerfPkt4 and PerfPkt6 +/// +/// The isc::perfdhcp::PerfPkt4 and isc::perfdhcp::PerfPkt6 classes +/// are derived from isc::dhcp::Pkt4 and isc::dhcp::Pkt6. They extend +/// the parent class functionality by adding support for template +/// files. Instances of these classes can be created using a raw buffer +/// (read from a packet template file). Once the packet object is +/// initialized, it is possible to replace parts of the on-wire data by +/// using the isc::perfdhcp::LocalizedOption mechanism. +/// +/// @subsection perfdhcpLocalizedOption LocalizedOption (Localized Option) +/// +/// isc::perfdhcp::LocalizedOption derives from the isc::dhcp::Option +/// class. It represents the DHCP option (v4 or v6) to be +/// placed at specified position in the packet buffer (see @ref +/// perfdhcpPkt). Such an option is added to the option collection in +/// a isc::perfdhcp::PerfPkt4 or isc::perfdhcp::PerfPkt6 object; when +/// any of PerfPktX::rawPack() functions are called their content is +/// stored in the packet output buffer at the position pointed to by +/// the isc::perfdhcp::LocalizedOption object. +/// +/// isc::perfdhcp::LocalizedOption also allows the reading of the +/// on wire data in received packet at the specified position. In +/// this case, isc::perfdhcp::LocalizedOption has to be created and +/// added to the received packet. When PerfPktX::rawUnpack() is +/// called, the contents of the buffer will be read and stored in a +/// isc::perfdhcp::LocalizedOption object for further processing. +/// +/// The following code shows how to create a packet from a +/// (template) buffer and replace option data in the buffer with +/// isc::perfdhcp::LocalizedOption. +/// +/// @code +/// OptionBuffer buf; // fill buf with data here. ... +/// boost::scoped_ptr pkt(new PerfPkt4(&buf[0], buf.size()); +/// const size_t offset_hostname = 240; +/// OptionBuffer vec_hostname; +/// // fill the hostname vector with data ... +/// LocalizedOptionPtr opt_hostname(new LocalizedOption(Option::V4, +/// DHO_HOST_NAME, +/// vec_hostname, +/// offset_hostname)); +/// pkt->addOption(opt_hostname); +/// // by calling rawPack() we replace the packet contents with option +/// // contents at buffer position 240. +/// pkt->rawPack(); +/// @endcode +/// +/// @subsection perfdhcpPktTransform PktTransform (Packet Transform) +/// +/// The isc::perfdhcp::PktTransform helper class contains the +/// static functions to pack and unpack DHCP options (specifically +/// isc::perfdhcp::LocalizedOption) to and from the packet buffer. This +/// logic has been moved away from isc::perfdhcp::PerfPkt4 and +/// isc::perfdhcp::PerfPkt6 classes to isc::perfdhcp::PktTransform +/// because PerfPktX classes share the logic here. diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am index 73ec6babc5..54602aff4b 100644 --- a/tests/tools/perfdhcp/tests/Makefile.am +++ b/tests/tools/perfdhcp/tests/Makefile.am @@ -39,12 +39,12 @@ if USE_CLANGPP run_unittests_CXXFLAGS = -Wno-unused-parameter endif -run_unittests_LDADD = $(GTEST_LDADD) -run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la +run_unittests_LDADD = $(top_builddir)/src/lib/util/libb10-util.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la +run_unittests_LDADD += $(GTEST_LDADD) endif noinst_PROGRAMS = $(TESTS) diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 0481d28da3..801b02dd29 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -21,7 +21,7 @@ #include #include -#include "../command_options.h" +#include "command_options_helper.h" using namespace std; using namespace isc; @@ -48,16 +48,7 @@ protected: /// \param cmdline Command line to parse /// \throws std::bad allocation if tokenization failed void process(const std::string& cmdline) { - CommandOptions& opt = CommandOptions::instance(); - int argc = 0; - char** argv = tokenizeString(cmdline, &argc); - opt.reset(); - opt.parse(argc, argv); - for(int i = 0; i < argc; ++i) { - free(argv[i]); - argv[i] = NULL; - } - free(argv); + CommandOptionsHelper::process(cmdline); } /// \brief Check default initialized values @@ -143,42 +134,6 @@ protected: EXPECT_EQ("", opt.getWrapped()); EXPECT_EQ("192.168.0.1", opt.getServerName()); } - - /// \brief Split string to array of C-strings - /// - /// \param s String to split (tokenize) - /// \param num Number of tokens returned - /// \return array of C-strings (tokens) - char** tokenizeString(const std::string& text_to_split, int* num) const { - char** results = NULL; - // Tokenization with std streams - std::stringstream text_stream(text_to_split); - // Iterators to be used for tokenization - std::istream_iterator text_iterator(text_stream); - std::istream_iterator text_end; - // Tokenize string (space is a separator) using begin and end iteratos - std::vector tokens(text_iterator, text_end); - - if (tokens.size() > 0) { - // Allocate array of C-strings where we will store tokens - results = static_cast(malloc(tokens.size() * sizeof(char*))); - if (results == NULL) { - throw std::bad_alloc(); - } - // Store tokens in C-strings array - for (int i = 0; i < tokens.size(); ++i) { - char* cs = static_cast(malloc(tokens[i].length() + 1)); - strcpy(cs, tokens[i].c_str()); - results[i] = cs; - } - // Return number of tokens to calling function - if (num != NULL) { - *num = tokens.size(); - } - } - return results; - } - }; TEST_F(CommandOptionsTest, Defaults) {