#include <config.h>
#include <boost/bind.hpp>
-#include <client/client_interface.h>
+#include <client/client6_interface.h>
+#include <client/client_log.h>
#include <iostream>
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);
}
// 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
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
#include <util/state_model.h>
+#include <client/client_log.h>
#include <string>
/// 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
ST_DUPLICATE_TIMEOUT,
ST_CONFIGURED,
ST_START_RENEW,
- ST_RESENT_RENEW,
+ ST_RESEND_RENEW,
ST_RENEW_COMPLETE,
ST_REBINDING,
ST_PROCESS_REBINDING,
EVT_SEND_SOLICIT,
EVT_ADVERTISE_RECEIVED,
EVT_SOLICIT_TIMEOUT,
- EVT_SEND_REQUEST
+ EVT_SEND_REQUEST,
EVT_REQUEST_TIMEOUT,
EVT_REPLY_RECEIVED,
EVT_DUPLICATE_CHECK,
/// 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
/// 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
///
/// 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
///
}
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