From: Stephen Morris Date: Thu, 20 Jul 2017 22:44:47 +0000 (+0200) Subject: [client] Define all client state handlers X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=58fe4572b50beb8d40817b255a0311ecf047af77;p=thirdparty%2Fkea.git [client] Define all client state handlers Also he skeletons of some of the state handlers have been written. --- diff --git a/src/bin/client/Makefile.am b/src/bin/client/Makefile.am index 6f2a70391b..638e5f9084 100644 --- a/src/bin/client/Makefile.am +++ b/src/bin/client/Makefile.am @@ -9,11 +9,10 @@ if USE_STATIC_LINK AM_LDFLAGS = -static endif -CLEANFILES = -man_MANS = kea-client.8 +CLEANFILES = *.gcno *.gcda client_messages.h client_messages.cc s-messages +man_MANS = kea-client.8 DISTCLEANFILES = $(man_MANS) - EXTRA_DIST = $(man_MANS) kea-client.xml EXTRA_DIST += client.dox EXTRA_DIST += clnt_lexer.ll clnt_parser.yy @@ -32,6 +31,18 @@ $(man_MANS): endif +# Build the message code from the messages files + +client_messages.h client_messages.cc: s-messages + +s-messages: client_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/client/client_messages.mes + touch $@ + +BUILT_SOURCES = client_messages.h client_messages.cc + +# Convenience archive. + noinst_LTLIBRARIES = libclient.la libclient_la_SOURCES = @@ -41,6 +52,12 @@ libclient_la_SOURCES += executor.cc executor.h libclient_la_SOURCES += clnt_process.cc clnt_process.h libclient_la_SOURCES += clnt_cfg_mgr.cc clnt_cfg_mgr.h libclient_la_SOURCES += clnt_config.cc clnt_config.h +libclient_la_SOURCES += client_log.cc client_log.h + +nodist_libclient_la_SOURCES = clients_messages.h client_messages.cc + +EXTRA_DIST += client_messages.mes + sbin_PROGRAMS = kea-client diff --git a/src/bin/client/client6_interface.cc b/src/bin/client/client6_interface.cc index 9e1375c41a..2e1974589a 100644 --- a/src/bin/client/client6_interface.cc +++ b/src/bin/client/client6_interface.cc @@ -6,7 +6,8 @@ #include #include -#include +#include +#include #include using namespace isc::util; @@ -14,95 +15,144 @@ using namespace isc::util; namespace isc { namespace client { -// Define the constants -const int ClientInterface::ST_BEGIN; -const int ClientInterface::ST_SERVER_DISCOVERY; -const int ClientInterface::ST_AWAITING_RECOGNITION; - -const int ClientInterface::EVT_SEND_SOLICIT; -const int ClientInterface::EVT_SOLICIT_RESPONSE_RECEIVED; -const int ClientInterface::EVT_SOLICIT_TIMEOUT; -const int ClientInterface::EVT_SEND_REQUEST; - - // Constructor. -ClientInterface::ClientInterface(const std::string& name, int ifindex, +Client6Interface::Client6Interface(const std::string& name, int ifindex, isc::asiolink::IOServicePtr& io_service) : - isc::dhcp::Iface(name, ifindex), io_service_(io_service) - { + isc::dhcp::Iface(name, ifindex), isc::util::StateModel(), + io_service_(io_service), solicit_timer_running_(false) { +} + +// Destructor +Client6Interface::~Client6Interface() { } -ClientInterface::~ClientInterface() { +// Convenience function to log that a state has been entered with an unexpected +// event. + +void +Client6Interface::logUnexpectedEvent() { + LOG_ERROR(client_logger, CLIENT6_UNEXPECTED_EVENT) + .arg(getStateLabel(getCurrState())) + .arg(getEventLabel(getNextEvent())); } +// Event methods. + void -ClientInterface::defineEvents() { +Client6Interface::defineEvents() { // Call superclass impl first. StateModel::defineEvents(); - // Define NCT events. + defineEvent(EVT_SERVER_DISCOVERY, "EVT_SERVER_DISCOVERY"); defineEvent(EVT_SEND_SOLICIT, "EVT_SEND_SOLICIT"); - defineEvent(EVT_SOLICIT_RESPONSE_RECEIVED, "EVT_SOLICIT_RESPONSE_RECEIVED"); + defineEvent(EVT_ADVERTISE_RECEIVED, "EVT_ADVERTISE_RECEIVED"); defineEvent(EVT_SOLICIT_TIMEOUT, "EVT_SOLICIT_TIMEOUT"); defineEvent(EVT_SEND_REQUEST, "EVT_SEND_REQUEST"); - defineEvent(EVT_REQUEST_RESPONSE_RECEIVED, "EVT_REQUEST_RESPONSE_RECEIVED"); - defineEvent(EVT_REQUEST_RESPONSE_TIMEOUT, "EVT_REQUEST_RESPONSE_TIMEOUT"); + defineEvent(EVT_REQUEST_TIMEOUT, "EVT_REQUEST_TIMEOUT"); + defineEvent(EVT_REPLY_RECEIVED, "EVT_REPLY_RECEIVED"); + defineEvent(EVT_DUPLICATE_CHECK, "EVT_DUPLICATE_CHECK"); defineEvent(EVT_DUPLICATE_TIMEOUT, "EVT_DUPLICATE_TIMEOUT"); - defineEvent(EVT_T1_TIMEOUT, "EVT_T1_TIMEOUT"); - defineEvent(EVT_RENEW_RESPONSE_RECEIVED, "EVT_RENEW_RESPONSE_RECEIVED"); - defineEvent(EVT_RENEW_RESPONSE_TIMEOUT, "EVT_RENEW_RESPONSE_TIMEOUT"); - defineEvent(EVT_T2_TIMEOUT, "EVT_T2_TIMEOUT"); - defineEvent(EVT_RETRANSMISSION_TIMEOUT, "EVT_RETRANSMISSION_TIMEOUT"); + defineEvent(EVT_CONFIGURED, "EVT_CONFIGURED"); + defineEvent(EVT_START_RENEW, "EVT_START_RENEW"); + defineEvent(EVT_RESEND_RENEW, "EVT_RESEND_RENEW"); + defineEvent(EVT_REBINDING, "EVT_REBINDING"); + defineEvent(EVT_PROCESS_REBINDING, "EVT_PROCESS_REBINDING"); + defineEvent(EVT_REBINDING_TIMEOUT, "EVT_REBINDING_TIMEOUT"); } void -ClientInterface::verifyEvents() { +Client6Interface::verifyEvents() { // Call superclass impl first. StateModel::verifyEvents(); // Verify events by trying to get them. + getEvent(EVT_SERVER_DISCOVERY); getEvent(EVT_SEND_SOLICIT); - getEvent(EVT_SOLICIT_RESPONSE_RECEIVED); + getEvent(EVT_ADVERTISE_RECEIVED); getEvent(EVT_SOLICIT_TIMEOUT); getEvent(EVT_SEND_REQUEST); - getEvent(EVT_REQUEST_RESPONSE_RECEIVED); - getEvent(EVT_REQUEST_RESPONSE_TIMEOUT); + getEvent(EVT_REQUEST_TIMEOUT); + getEvent(EVT_REPLY_RECEIVED); + getEvent(EVT_DUPLICATE_CHECK); getEvent(EVT_DUPLICATE_TIMEOUT); - getEvent(EVT_T1_TIMEOUT); - getEvent(EVT_RENEW_RESPONSE_RECEIVED); - getEvent(EVT_RENEW_RESPONSE_TIMEOUT); - getEvent(EVT_T2_TIMEOUT); - getEvent(EVT_RETRANSMISSION_TIMEOUT); + getEvent(EVT_CONFIGURED); + getEvent(EVT_START_RENEW); + getEvent(EVT_RESEND_RENEW); + getEvent(EVT_REBINDING); + getEvent(EVT_PROCESS_REBINDING); + getEvent(EVT_REBINDING_TIMEOUT); } +// State methods + void -ClientInterface::defineStates() { +Client6Interface::defineStates() { // Call superclass impl first. StateModel::defineStates(); + // This class is "abstract" in that it does not supply handlers for its // states, derivations must do that therefore they must define them. - defineState(ST_BEGIN, "ST_BEGIN", - boost::bind(&ClientInterface::beginHandler, this)); - + boost::bind(&Client6Interface::beginHandler, this)); defineState(ST_SERVER_DISCOVERY, "ST_SERVER_DISCOVERY", - boost::bind(&ClientInterface::serverDiscoveryHandler, this)); - - defineState(ST_AWAITING_RECOGNITION, "ST_AWAITING_RECOGNITION", - boost::bind(&ClientInterface::awaitingRecognitionHandler, - this)); - + boost::bind(&Client6Interface::serverDiscoveryHandler, this)); + defineState(ST_SEND_SOLICIT, "ST_SEND_SOLICIT", + boost::bind(&Client6Interface::sendSolicitHandler, this)); + defineState(ST_SOLICIT_TIMEOUT, "ST_SOLICIT_TIMEOUT", + boost::bind(&Client6Interface::solicitTimeoutHandler, this)); + defineState(ST_ADVERTISE_RECEIVED, "ST_ADVERTISE_RECEIVED", + boost::bind(&Client6Interface::advertiseReceivedHandler, this)); + defineState(ST_SEND_REQUEST, "ST_SEND_REQUEST", + boost::bind(&Client6Interface::sendRequestHandler, this)); + defineState(ST_REQUEST_TIMEOUT, "ST_REQUEST_TIMEOUT", + boost::bind(&Client6Interface::requestTimeoutHandler, this)); + defineState(ST_REPLY_RECEIVED, "ST_REPLY_RECEIVED", + boost::bind(&Client6Interface::replyReceivedHandler, this)); + defineState(ST_DUPLICATE_CHECK, "ST_DUPLICATE_CHECK", + boost::bind(&Client6Interface::duplicateCheckHandler, this)); + defineState(ST_DUPLICATE_TIMEOUT, "ST_DUPLICATE_TIMEOUT", + boost::bind(&Client6Interface::duplicateTimeoutHandler, this)); + defineState(ST_CONFIGURED, "ST_CONFIGURED", + boost::bind(&Client6Interface::configuredHandler, this)); + defineState(ST_START_RENEW, "ST_START_RENEW", + boost::bind(&Client6Interface::startRenewHandler, this)); + defineState(ST_RESEND_RENEW, "ST_RESEND_RENEW", + boost::bind(&Client6Interface::resendRenewHandler, this)); + defineState(ST_RENEW_COMPLETE, "ST_RENEW_COMPLETE", + boost::bind(&Client6Interface::renewCompleteHandler, this)); + defineState(ST_REBINDING, "ST_REBINDING", + boost::bind(&Client6Interface::rebindingHandler, this)); + defineState(ST_PROCESS_REBINDING, "ST_PROCESS_REBINDING", + boost::bind(&Client6Interface::processRebindingHandler, this)); + defineState(ST_REBINDING_TIMEOUT, "ST_REBINDING_TIMEOUT", + boost::bind(&Client6Interface::rebindingTimeoutHandler, this)); + defineState(ST_BEGIN, "ST_BEGIN", + boost::bind(&Client6Interface::beginHandler, this)); } void -ClientInterface::verifyStates() { +Client6Interface::verifyStates() { // Call the superclass implementation first StateModel::verifyStates(); - // Verify ClientInterface states by attempting to fetch them. + // Verify Client6Interface states by attempting to fetch them. getState(ST_BEGIN); getState(ST_SERVER_DISCOVERY); - getState(ST_AWAITING_RECOGNITION); + getState(ST_SEND_SOLICIT); + getState(ST_SOLICIT_TIMEOUT); + getState(ST_ADVERTISE_RECEIVED); + getState(ST_SEND_REQUEST); + getState(ST_REQUEST_TIMEOUT); + getState(ST_REPLY_RECEIVED); + getState(ST_DUPLICATE_CHECK); + getState(ST_DUPLICATE_TIMEOUT); + getState(ST_CONFIGURED); + getState(ST_START_RENEW); + getState(ST_RESEND_RENEW); + getState(ST_RENEW_COMPLETE); + getState(ST_REBINDING); + getState(ST_PROCESS_REBINDING); + getState(ST_REBINDING_TIMEOUT); } @@ -110,47 +160,120 @@ ClientInterface::verifyStates() { // event and enters the given state. void -ClientInterface::asynchCompletion(const boost::system::error_code&, int event) { +Client6Interface::asynchCompletion(const boost::system::error_code&, int event) { // TODO: Check error code runModel(event); } -// Begin handler. Initialise, then transition to SERVER_DISCOVERY +// BEGIN State Handler +// +// The entry point of the state machine, this opens sockets on the interface +// and performs other initialization. void -ClientInterface::beginHandler() { +Client6Interface::beginHandler() { std::cout << "beginHandler()\n"; - // TODO: Initialization - // Move to the server discovery stage. - transition(ST_SERVER_DISCOVERY, EVT_SEND_SOLICIT); + switch (getNextEvent()) { + + case START_EVT: + // TODO: Initialization + + // Move to the server discovery stage. + transition(ST_SERVER_DISCOVERY, EVT_SERVER_DISCOVERY); + break; + + default: + logUnexpectedEvent(); + break; + } + + return; } -// Server discovery handler. Broadcast a SOLICT message, then start a timer -// and issue a read for a response. +// SERVER_DISCOVERY State Handler +// +// Initializes list of available servers (to empty), then moves to the next +// state. The split between BEGIN and this state is BEGIN contains the +// initialization that only needs to be done once for the interface, this +// state holds teh initialization that needs to be performed every time we +// decide to try and find servers. void -ClientInterface::serverDiscoveryHandler() { - if (doOnEntry()) { - std::cout << "serverDiscoveryHandler()\n"; - postNextEvent(NOP_EVT); - - } else { - // All events (the completion of the I/O or the expiry of the timer) - // cause a transition to the AWAITING_RECOGNITION state. - transition(ST_AWAITING_RECOGNITION, getNextEvent()); +Client6Interface::serverDiscoveryHandler() { + std::cout << "serverDiscoveryHandler()\n"; + + switch (getNextEvent()) { + + case EVT_SERVER_DISCOVERY: + // Initialize list of servers + + // TODO: Initialize list of servers + + transition(ST_SEND_SOLICIT, EVT_SEND_SOLICIT); + break; + + default: + logUnexpectedEvent(); + break; } + + return; } -// Awaiting recognition state. Process the response and timer handlers. +// SEND_SOLICIT State Handler +// +// The client sends a SOLICIT message then waits in this state until either +// an ADVERTISE message is received from a server, or the timer associated +// with the sending of the SOLICIT has timed out. void -ClientInterface::awaitingRecognitionHandler() { - std::cout << "awaitingRecognitionHandler()\n"; +Client6Interface::sendSolicitHandler() { + std::cout << "sendSOlicitHandler()\n"; + + switch (getNextEvent()) { + + case EVT_SEND_SOLICIT: + // Entry via an internal transfer from another state. Broadcast + // the SOLICIT message to all listening servers. + + // TODO: Send the SOLICIT message + + // Issue the read for the ADVERTISE message + // TODO: Issue read. + + // Kick off the solicit timer if it is not already running. + if (! solicit_timer_running_) { + // TODO: Start solicit timer + + solicit_timer_running_ = true; + } + + // Now exit the state. The state will be re-entered when either + // the ADVERTISE message is received or when the solicit timer + // times out. + postNextEvent(NOP_EVT); + break; + + case EVT_ADVERTISE_RECEIVED: + // Entered the state because a server responded with an ADVERTISE + // message. + transition(ST_ADVERTISE_RECEIVED, EVT_ADVERTISE_RECEIVED); + break; + + case EVT_SOLICIT_TIMEOUT: + // Entered the state because the timer started when we sent the + // first SOLICIT message has finally expired. + transition(ST_SOLICIT_TIMEOUT, EVT_SOLICIT_TIMEOUT); + break; + + default: + logUnexpectedEvent(); + break; + } - // Stay in the AWAITING_RECOGNITION state. - transition(ST_AWAITING_RECOGNITION, NOP_EVT); + return; } } // namespace isc::client diff --git a/src/bin/client/client6_interface.h b/src/bin/client/client6_interface.h index 068287a58d..2e5b5b036e 100644 --- a/src/bin/client/client6_interface.h +++ b/src/bin/client/client6_interface.h @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -68,8 +69,8 @@ public: /// Client Interface will wait for completion in the state that triggered the /// operation, transitioning to the new state when the operation completes. -class Client6Interface : public isc::util::StateModel, - public isc::dhcp::Iface { +class Client6Interface : public isc::dhcp::Iface, + public isc::util::StateModel { public: /// @brief State list @@ -89,7 +90,7 @@ public: ST_DUPLICATE_TIMEOUT, ST_CONFIGURED, ST_START_RENEW, - ST_RESENT_RENEW, + ST_RESEND_RENEW, ST_RENEW_COMPLETE, ST_REBINDING, ST_PROCESS_REBINDING, @@ -105,7 +106,7 @@ public: EVT_SEND_SOLICIT, EVT_ADVERTISE_RECEIVED, EVT_SOLICIT_TIMEOUT, - EVT_SEND_REQUEST + EVT_SEND_REQUEST, EVT_REQUEST_TIMEOUT, EVT_REPLY_RECEIVED, EVT_DUPLICATE_CHECK, @@ -177,24 +178,38 @@ public: /// BEGIN is the state the model transitions into when the method /// startTransaction is called, It is the entry point into the state /// machine. It performs all the initialization then unconditionally - /// transitions to the SEND_SOLICIT state. + /// transitions to the SERVER_DISCOVERY state. void beginHandler(); + /// @brief State handler for SERVER_DISCOVERY + /// + /// The second part of the initialization, this state initializes the + /// list of available servers before transitioning to the SEND_SOLICIT + /// state. + void serverDiscoveryHandler(); + /// @brief State handler for SEND_SOLICIT /// /// SEND_SOLICIT is the state that initiates the discovery of servers. /// It initializes the list of servers, then broadcasts the SOLICIT message. /// It then kicks off an asynchronous read, as well as a read timer. - void serverDiscoveryHandler(); + void sendSolicitHandler(); /// @brief State handler for ADVERTISE_RECEIVED /// /// The client enters this state when an ADVERTISE is received from the /// server. If the SOLICIT has not timed out, it will - unless the /// server is one with the maximum preference value - loop round and - /// wait for more responses until teh solicit timer times out. + /// wait for more responses until the solicit timer times out. void advertiseReceivedHandler(); + /// @brief State handler for SOLICIT_TIMEOUT + /// + /// This state is entered when the timer associated with the SOLICIT + /// message has timed out. Providing that some servers advertised + /// themselves, the client moves from this state into SEND_REQUEST. + void solicitTimeoutHandler(); + /// @brief State handler for SEND_REQUEST /// /// Starts a timer and sends the REQUEST to the server at the top of the @@ -206,14 +221,14 @@ public: /// Called when the timer associated with waiting for a REPLY from the /// server has timed out, this either sends another request (to the same /// or a different server) or goes back to looking for servers. - void requestTimeoutHandler()l + void requestTimeoutHandler(); /// @brief State handler for REPLY_RECEIVED /// /// When the server responds with a REPLY message, the client enters this /// state. Depending on the response, it will either look for duplicate /// addresses or try to contact another server. - void replyReceivedTimer(); + void replyReceivedHandler(); /// @brief State handler for DUPLICATE_CHECK /// @@ -266,7 +281,7 @@ public: /// Entered when a rebinding response has been received, in this state /// the client decides whether to acept the response of take some other /// action. - void processRebdingingHandler(); + void processRebindingHandler(); /// @brief State handler for REBINDING_TIMEOUT /// @@ -294,8 +309,22 @@ public: } private: + /// @brief Log State Error + /// + /// This is a convenience function to log an error due to the current + /// state not expecting to be entered with the current (next) event. + /// Although it breaks the Kea idea that each LOG_XXX should be with a + /// unique error code, the combination of error code and state (the + /// first parameter) is unique. + void logUnexpectedEvent(); + /// @brief The IOService which should be used to for IO processing. asiolink::IOServicePtr io_service_; + + // Flags to indicate that timers are running. The names are fairly + // self-explanatory. + bool solicit_timer_running_; + }; /// @brief Defines a pointer to a Client6Interface diff --git a/src/bin/client/client_log.cc b/src/bin/client/client_log.cc new file mode 100644 index 0000000000..0461664a2e --- /dev/null +++ b/src/bin/client/client_log.cc @@ -0,0 +1,19 @@ +// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// Defines the logger used by the top-level component of kea-dhcp-ddns. + +#include + +namespace isc { +namespace client { + +/// @brief Defines the logger used within the Kea client. +isc::log::Logger client_logger("client"); + +} // namespace client +} // namespace isc + diff --git a/src/bin/client/client_log.h b/src/bin/client/client_log.h new file mode 100644 index 0000000000..e026c55001 --- /dev/null +++ b/src/bin/client/client_log.h @@ -0,0 +1,23 @@ +// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CLIENT_LOG_H +#define CLIENT_LOG_H + +#include +#include +#include + +namespace isc { +namespace client { + +/// Define the loggers for the Kea client logging +extern isc::log::Logger client_logger; + +} // namespace d2 +} // namespace isc + +#endif // CLIENT_LOG_H diff --git a/src/bin/client/client_messages.mes b/src/bin/client/client_messages.mes new file mode 100644 index 0000000000..2cc7a33a70 --- /dev/null +++ b/src/bin/client/client_messages.mes @@ -0,0 +1,14 @@ +# Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$NAMESPACE isc::client + +% CLIENT6_UNEXPECTED_EVENT state %1 entered with unexpected event %2 +Every state in the DHCPv6 client's state machine expects to be entered due to +one of a limited set of events. This error message is output because the +processing associated with the state did not expect it to be entered with the +given event. The reason that this occurred will depend on the event and the +state being entered. It is most likely a programming error.