Browse Source

[1708] msgq support in DHCPv6 added.

Tomek Mrugalski 13 years ago
parent
commit
f4566355e7

+ 5 - 3
doc/devel/02-dhcp.dox

@@ -72,11 +72,13 @@
  * DHCPv6 server component does not support relayed traffic yet, as
  * support for relay decapsulation is not implemented yet.
  *
- * DHCPv6 server component does not listen to BIND10 message queue.
- *
  * DHCPv6 server component does not use BIND10 logging yet.
  *
- * DHCPv6 server component is not integrated with boss yet.
+ * @section dhcpv6Session BIND10 message queue integration
+ *
+ * DHCPv4 server component is now integrated with BIND10 message queue.
+ * It follows the same principle as DHCPv4. See \ref dhcpv4Session for
+ * details.
  *
  * @page libdhcp libdhcp++
  *

File diff suppressed because it is too large
+ 63 - 75
doc/guide/bind10-guide.html


File diff suppressed because it is too large
+ 290 - 304
doc/guide/bind10-guide.txt


+ 21 - 37
doc/guide/bind10-guide.xml

@@ -2455,7 +2455,7 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
 &gt; <userinput>config commit</userinput></screen></para>
 
       <para>
-        At start, the server will detect available network interfaces
+        During start-up the server will detect available network interfaces
         and will attempt to open UDP sockets on all interfaces that
         are up, running, are not loopback, and have IPv4 address
         assigned.
@@ -2465,17 +2465,8 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
         will respond to them with OFFER and ACK, respectively.
 
         Since the DHCPv4 server opens privileged ports, it requires root
-        access. Make sure you run this daemon as root.</para>
-
-        <note>
-          <para>
-            Integration with <command>bind10</command> is
-            planned. Ultimately, <command>b10-dhcp4</command> will not
-            be started directly, but rather via
-            <command>bind10</command>. Please be aware of this planned
-            change.
-          </para>
-        </note>
+        access. Make sure you run this daemon as root.
+      </para>
 
     </section>
 
@@ -2640,22 +2631,25 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
       </para>
 
       <para>
-        The DHCPv6 server is implemented as <command>b10-dhcp6</command>
-        daemon. As it is not configurable yet, it is fully autonomous,
-        that is it does not interact with <command>b10-cfgmgr</command>.
-        To start DHCPv6 server, simply input:
-
-        <screen>
-#<userinput>cd src/bin/dhcp6</userinput>
-#<userinput>./b10-dhcp6</userinput>
-</screen>
+        <command>b10-dhcp6</command> is a BIND10 component and is being
+        run under BIND10 framework. To add a DHCPv6 process to the set of running
+        BIND10 services, you can use following commands in <command>bindctl</command>:
+        <screen>&gt; <userinput>config add Boss/components b10-dhcp6</userinput>
+&gt; <userinput>config set Boss/components/b10-dhcp6/kind dispensable</userinput>
+&gt; <userinput>config commit</userinput></screen>
+      </para>
 
-        Depending on your installation, <command>b10-dhcp6</command>
-        binary may reside in src/bin/dhcp6 in your source code
-        directory, in /usr/local/bin/b10-dhcp6 or other directory
-        you specified during compilation.
+       <para>
+         To shutdown running <command>b10-dhcp6</command>, please use the
+         following command:
+         <screen>&gt; <userinput>Dhcp6 shutdown</userinput></screen>
+         or
+         <screen>&gt; <userinput>config remove Boss/components b10-dhcp6</userinput>
+&gt; <userinput>config commit</userinput></screen>
+       </para>
 
-        At start, server will detect available network interfaces
+      <para>
+        During start-up the server will detect available network interfaces
         and will attempt to open UDP sockets on all interfaces that
         are up, running, are not loopback, are multicast-capable, and
         have IPv6 address assigned.
@@ -2668,16 +2662,6 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
         access. Make sure you run this daemon as root.
       </para>
 
-        <note>
-          <para>
-            Integration with <command>bind10</command> is
-            planned. Ultimately, <command>b10-dhcp6</command> will not
-            be started directly, but rather via
-            <command>bind10</command>. Please be aware of this planned
-            change.
-          </para>
-        </note>
-
     </section>
 
     <section id="dhcp6-config">
@@ -2691,7 +2675,7 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
       <para>
         At this stage of development, the only way to alter server
         configuration is to tweak its source code. To do so, please
-        edit src/bin/dhcp6/dhcp6_srv.cc file and modify following
+        edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following
         parameters and recompile:
         <screen>
 const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";

File diff suppressed because it is too large
+ 1 - 1
doc/guide/bind10-messages.html


+ 3 - 0
src/bin/dhcp6/Makefile.am

@@ -33,6 +33,7 @@ BUILT_SOURCES = spec_config.h
 pkglibexec_PROGRAMS = b10-dhcp6
 
 b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
+b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
@@ -44,6 +45,8 @@ b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 
 b10_dhcp6dir = $(pkgdatadir)
 b10_dhcp6_DATA = dhcp6.spec

+ 159 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -0,0 +1,159 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <cassert>
+#include <iostream>
+
+#include <cc/session.h>
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+#include <util/buffer.h>
+#include <dhcp6/spec_config.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcp/iface_mgr.h>
+#include <asiolink/asiolink.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc::data;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
+
+ConstElementPtr
+ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
+    cout << "b10-dhcp6: Received new config:" << new_config->str() << endl;
+    ConstElementPtr answer = isc::config::createAnswer(0,
+                             "Thank you for sending config.");
+    return (answer);
+}
+
+ConstElementPtr
+ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr args) {
+    cout << "b10-dhcp6: Received new command: [" << command << "], args="
+         << args->str() << endl;
+    if (command == "shutdown") {
+        if (ControlledDhcpv6Srv::server_) {
+            ControlledDhcpv6Srv::server_->shutdown();
+        } else {
+            cout << "Server not initialized yet or already shut down." << endl;
+            ConstElementPtr answer = isc::config::createAnswer(1,
+                                     "Shutdown failure.");
+            return (answer);
+        }
+        ConstElementPtr answer = isc::config::createAnswer(0,
+                                 "Shutting down.");
+        return (answer);
+    }
+
+    ConstElementPtr answer = isc::config::createAnswer(1,
+                             "Unrecognized command.");
+
+    return (answer);
+}
+
+void ControlledDhcpv6Srv::sessionReader(void) {
+    // Process one asio event. If there are more events, iface_mgr will call
+    // this callback more than once.
+    if (server_) {
+        server_->io_service_.run_one();
+    }
+}
+
+void ControlledDhcpv6Srv::establishSession() {
+    
+    string specfile;
+    if (getenv("B10_FROM_BUILD")) {
+        specfile = string(getenv("B10_FROM_BUILD")) +
+            "/src/bin/dhcp6/dhcp6.spec";
+    } else {
+        specfile = string(DHCP6_SPECFILE_LOCATION);
+    }
+
+    /// @todo: Check if session is not established already. Throw, if it is.
+    
+    cout << "b10-dhcp6: my specfile is " << specfile << endl;
+    
+    cc_session_ = new Session(io_service_.get_io_service());
+
+    config_session_ = new ModuleCCSession(specfile, *cc_session_,
+                                          dhcp6ConfigHandler,
+                                          dhcp6CommandHandler, false);
+    config_session_->start();
+
+    /// Integrate the asynchronous I/O model of BIND 10 configuration
+    /// 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;
+    IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
+}
+
+void ControlledDhcpv6Srv::disconnectSession() {
+    if (config_session_) {
+        delete config_session_;
+        config_session_ = NULL;
+    }
+    if (cc_session_) {
+        cc_session_->disconnect();
+        delete cc_session_;
+        cc_session_ = NULL;
+    }
+
+    // deregister session socket
+    IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
+}
+
+ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port /*= DHCP6_SERVER_PORT*/)
+    :Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) {
+    server_ = this; // remember this instance for use in callback
+}
+
+void ControlledDhcpv6Srv::shutdown() {
+    io_service_.stop(); // Stop ASIO transmissions
+    Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure.
+}
+
+ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
+    disconnectSession();
+
+    server_ = NULL; // forget this instance. There should be no callback anymore
+                    // at this stage anyway.
+}
+
+isc::data::ConstElementPtr
+ControlledDhcpv6Srv::execDhcpv6ServerCommand(const std::string& command_id,
+                                             isc::data::ConstElementPtr args) {
+    try {
+        return (dhcp6CommandHandler(command_id, args));
+    } catch (const Exception& ex) {
+        ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
+        return (answer);
+    }
+}
+
+
+};
+};

+ 123 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.h

@@ -0,0 +1,123 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CTRL_DHCPV6_SRV_H
+#define CTRL_DHCPV6_SRV_H
+
+#include <dhcp6/dhcp6_srv.h>
+#include <asiolink/asiolink.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+#include <cc/data.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Controlled version of the DHCPv6 server
+///
+/// This is a class that is responsible for establishing connection
+/// with msqg (receving commands and configuration). This is an extended
+/// version of Dhcpv6Srv class that is purely a DHCPv6 server, without
+/// external control. ControlledDhcpv6Srv should be used in typical BIND10
+/// (i.e. featuring msgq) environment, while Dhcpv6Srv should be used in
+/// embedded environments.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
+/// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
+class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param port UDP port to be opened for DHCP traffic
+    ControlledDhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
+
+    /// @brief Destructor.
+    ~ControlledDhcpv6Srv();
+
+    /// @brief Establishes msgq session.
+    ///
+    /// Creates session that will be used to receive commands and updated
+    /// configuration from boss (or indirectly from user via bindctl).
+    void establishSession();
+
+    /// @brief Terminates existing msgq session.
+    ///
+    /// This method terminates existing session with msgq. After calling
+    /// it, no further messages over msgq (commands or configuration updates)
+    /// may be received.
+    ///
+    /// It is ok to call this method when session is disconnected already.
+    void disconnectSession();
+
+    /// @brief Initiates shutdown procedure for the whole DHCPv6 server.
+    void shutdown();
+
+    /// @brief Session callback, processes received commands.
+    ///
+    /// @param command_id text represenation of the command (e.g. "shutdown")
+    /// @param args optional parameters
+    ///
+    /// @return status of the command
+    static isc::data::ConstElementPtr
+    execDhcpv6ServerCommand(const std::string& command,
+                            isc::data::ConstElementPtr args);
+
+protected:
+    /// @brief Static pointer to the sole instance of the DHCP server.
+    ///
+    /// This is required for config and command handlers to gain access to
+    /// the server
+    static ControlledDhcpv6Srv* server_;
+
+    /// @brief A callback for handling incoming configuration updates.
+    ///
+    /// As pointer to this method is used a callback in ASIO used in
+    /// ModuleCCSession, it has to be static.
+    ///
+    /// @param new_config textual representation of the new configuration
+    ///
+    /// @return status of the config update
+    static isc::data::ConstElementPtr
+    dhcp6ConfigHandler(isc::data::ConstElementPtr new_config);
+
+    /// @brief A callback for handling incoming commands.
+    ///
+    /// @param command textual representation of the command
+    /// @param args parameters of the command
+    ///
+    /// @return status of the processed command
+    static isc::data::ConstElementPtr
+    dhcp6CommandHandler(const std::string& command, isc::data::ConstElementPtr args);
+
+    /// @brief Callback that will be called from iface_mgr when command/config arrives.
+    ///
+    /// This static callback method is called from IfaceMgr::receive6() method,
+    /// when there is a new command or configuration sent over msgq.
+    static void sessionReader(void);
+
+    /// @brief IOService object, used for all ASIO operations.
+    isc::asiolink::IOService io_service_;
+
+    /// @brief Helper session object that represents raw connection to msgq.
+    isc::cc::Session* cc_session_;
+
+    /// @brief Session that receives configuation and commands
+    isc::config::ModuleCCSession* config_session_;
+};
+
+}; // namespace isc::dhcp
+}; // namespace isc
+
+#endif

+ 9 - 4
src/bin/dhcp6/dhcp6_srv.cc

@@ -48,12 +48,12 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
         IfaceMgr::instance();
     } catch (const std::exception &e) {
         cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
-        shutdown = true;
+        shutdown_ = true;
     }
 
     if (IfaceMgr::instance().countIfaces() == 0) {
         cout << "Failed to detect any network interfaces. Aborting." << endl;
-        shutdown = true;
+        shutdown_ = true;
     }
 
     // Now try to open IPv6 sockets on detected interfaces.
@@ -63,7 +63,7 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
 
     setServerID();
 
-    shutdown = false;
+    shutdown_ = false;
 }
 
 Dhcpv6Srv::~Dhcpv6Srv() {
@@ -72,8 +72,13 @@ Dhcpv6Srv::~Dhcpv6Srv() {
     IfaceMgr::instance().closeSockets();
 }
 
+void Dhcpv6Srv::shutdown() {
+    cout << "b10-dhcp6: DHCPv6 server shutdown." << endl;
+    shutdown_ = true;
+}
+
 bool Dhcpv6Srv::run() {
-    while (!shutdown) {
+    while (!shutdown_) {
 
         // client's message and server's response
         Pkt6Ptr query = IfaceMgr::instance().receive6();

+ 3 - 1
src/bin/dhcp6/dhcp6_srv.h

@@ -67,6 +67,8 @@ public:
     ///         critical error.
     bool run();
 
+    /// @brief Instructs the server to shut down.
+    void shutdown();
 protected:
     /// @brief Processes incoming SOLICIT and returns response.
     ///
@@ -184,7 +186,7 @@ protected:
 
     /// indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
-    volatile bool shutdown;
+    volatile bool shutdown_;
 };
 
 }; // namespace isc::dhcp

+ 31 - 43
src/bin/dhcp6/main.cc

@@ -13,47 +13,34 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cassert>
 #include <iostream>
-
-#include <exceptions/exceptions.h>
-#if 0
-// TODO cc is not used yet. It should be eventually
-#include <cc/session.h>
-#include <config/ccsession.h>
-#endif
-
-#include <util/buffer.h>
 #include <log/dummylog.h>
-
-#include <dhcp6/spec_config.h>
-#include "dhcp6/dhcp6_srv.h"
+#include <log/logger_support.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
 
 using namespace std;
-using namespace isc::util;
-
-using namespace isc;
 using namespace isc::dhcp;
 
+/// This file contains entry point (main() function) for standard DHCPv6 server
+/// component for BIND10 framework. It parses command-line arguments and
+/// instantiates ControlledDhcpv6Srv class that is responsible for establishing
+/// connection with msgq (receiving commands and configuration) and also
+/// creating Dhcpv6 server object as well.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
+/// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
+
 namespace {
 
-bool verbose_mode = false;
+const char* const DHCP6_NAME = "b10-dhcp6";
 
 void
 usage() {
-    cerr << "Usage:  b10-dhcp6 [-v]"
+    cerr << "Usage: b10-dhcp6 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
-    cerr << "\t-p number: specify non-standard port number 1-65535 (useful for testing only)" << endl;
+    cerr << "\t-p number: specify non-standard port number 1-65535 "
+         << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
@@ -63,6 +50,7 @@ main(int argc, char* argv[]) {
     int ch;
     int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
                                          // useful for testing only.
+    bool verbose_mode = false; // Should server be verbose?
 
     while ((ch = getopt(argc, argv, "vp:")) != -1) {
         switch (ch) {
@@ -84,7 +72,13 @@ main(int argc, char* argv[]) {
         }
     }
 
-    cout << "My pid=" << getpid() << endl;
+    // 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") << endl;
 
     if (argc - optind > 0) {
         usage();
@@ -92,24 +86,18 @@ main(int argc, char* argv[]) {
 
     int ret = EXIT_SUCCESS;
 
-    // TODO remainder of auth to dhcp6 code copy. We need to enable this in
-    //      dhcp6 eventually
-#if 0
-    Session* cc_session = NULL;
-    Session* statistics_session = NULL;
-    ModuleCCSession* config_session = NULL;
-#endif
     try {
-        string specfile;
-        if (getenv("B10_FROM_BUILD")) {
-            specfile = string(getenv("B10_FROM_BUILD")) +
-                "/src/bin/auth/dhcp6.spec";
-        } else {
-            specfile = string(DHCP6_SPECFILE_LOCATION);
-        }
+
+        cout << "b10-dhcp6: Initiating DHCPv6 server operation." << endl;
+
+        ControlledDhcpv6Srv* server = new ControlledDhcpv6Srv(port_number);
+        server->run();
+        delete server;
+        server = NULL;
 
         cout << "[b10-dhcp6] Initiating DHCPv6 operation." << endl;
 
+        /// @todo: pass verbose to the actual server once logging is implemented
         Dhcpv6Srv* srv = new Dhcpv6Srv(port_number);
 
         srv->run();

+ 4 - 1
src/bin/dhcp6/tests/Makefile.am

@@ -42,9 +42,10 @@ if HAVE_GTEST
 
 TESTS += dhcp6_unittests
 
-dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc
+dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
+dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
@@ -59,6 +60,8 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 
 endif
 

+ 85 - 0
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -0,0 +1,85 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <config/ccsession.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
+    // "naked" DHCPv6 server, exposes internal fields
+public:
+    NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
+};
+
+class CtrlDhcpv6SrvTest : public ::testing::Test {
+public:
+    CtrlDhcpv6SrvTest() {
+    }
+
+    ~CtrlDhcpv6SrvTest() {
+    };
+};
+
+TEST_F(CtrlDhcpv6SrvTest, commands) {
+
+    ControlledDhcpv6Srv* srv = NULL;
+    ASSERT_NO_THROW({
+        srv = new ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+    });
+
+    // use empty parameters list
+    ElementPtr params(new isc::data::MapElement());
+    int rcode = -1;
+
+    // case 1: send bogus command
+    ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params);
+    ConstElementPtr comment = parseAnswer(rcode, result);
+    EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
+
+    // case 2: send shutdown command without any parameters
+    result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
+    comment = parseAnswer(rcode, result);
+    EXPECT_EQ(0, rcode); // expect success
+
+    const pid_t pid(getpid());
+    ConstElementPtr x(new isc::data::IntElement(pid));
+    params->set("pid", x);
+
+    // case 3: send shutdown command with 1 parameter: pid
+    result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
+    comment = parseAnswer(rcode, result);
+    EXPECT_EQ(0, rcode); // expect success
+
+
+    delete srv;
+}
+
+} // end of anonymous namespace

+ 2 - 2
src/bin/dhcp6/tests/dhcp6_test.py

@@ -1,4 +1,4 @@
-# Copyright (C) 2011,2012 Internet Systems Consortium.
+# copyright (C) 2011,2012 Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -131,7 +131,7 @@ class TestDhcpv6Daemon(unittest.TestCase):
         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 operation."), 1)
+        self.assertEqual( str(output).count("b10-dhcp6: Initiating DHCPv6 server operation."), 1)
 
     def test_portnumber_0(self):
         print("Check that specifying port number 0 is not allowed.")