]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[client] Define all client state handlers
authorStephen Morris <stephen@isc.org>
Thu, 20 Jul 2017 22:44:47 +0000 (00:44 +0200)
committerTomek Mrugalski <tomasz@isc.org>
Mon, 18 Nov 2019 19:26:54 +0000 (03:26 +0800)
Also he skeletons of some of the state handlers have been written.

src/bin/client/Makefile.am
src/bin/client/client6_interface.cc
src/bin/client/client6_interface.h
src/bin/client/client_log.cc [new file with mode: 0644]
src/bin/client/client_log.h [new file with mode: 0644]
src/bin/client/client_messages.mes [new file with mode: 0644]

index 6f2a70391bfbf0980f13a8da2fcb5293edd620d3..638e5f9084a8b120ede92c2ca3be6ef408a4ea13 100644 (file)
@@ -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
 
index 9e1375c41a3189c2da5cb1baf5d01b33ccda1ed2..2e1974589a43092613d649b08b4ef127ab9d4a9e 100644 (file)
@@ -6,7 +6,8 @@
 
 #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;
@@ -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
index 068287a58d020a8dba63a113cc10f8b57df532f5..2e5b5b036efdb4466369cf708fb1ae9627bbfed8 100644 (file)
@@ -11,6 +11,7 @@
 #include <dhcp/iface_mgr.h>
 #include <exceptions/exceptions.h>
 #include <util/state_model.h>
+#include <client/client_log.h>
 
 #include <string>
 
@@ -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 (file)
index 0000000..0461664
--- /dev/null
@@ -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 <client/client_log.h>
+
+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 (file)
index 0000000..e026c55
--- /dev/null
@@ -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 <log/logger_support.h>
+#include <log/macros.h>
+#include <client/client_messages.h>
+
+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 (file)
index 0000000..2cc7a33
--- /dev/null
@@ -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.