bundy_controller.cc 8.5 KB
Newer Older
1
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15
//
// 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>
16 17

#include <asiolink/asiolink.h>
18
#include <cc/data.h>
19
#include <cc/session.h>
20
#include <config/ccsession.h>
21
#include <dhcp/iface_mgr.h>
22
#include <dhcp4/json_config_parser.h>
23
#include <dhcp4/ctrl_dhcp4_srv.h>
24
#include <dhcp4/dhcp4_log.h>
25
#include <dhcp4/spec_config.h>
26 27
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcp_config_parser.h>
28
#include <exceptions/exceptions.h>
29
#include <hooks/hooks_manager.h>
30
#include <util/buffer.h>
31

32 33
#include <cassert>
#include <iostream>
34
#include <sstream>
35 36
#include <string>
#include <vector>
37

38
using namespace isc::asiolink;
39 40
using namespace isc::cc;
using namespace isc::config;
41 42
using namespace isc::data;
using namespace isc::dhcp;
43
using namespace isc::hooks;
44 45 46
using namespace isc::log;
using namespace isc::util;
using namespace std;
47 48 49 50

namespace isc {
namespace dhcp {

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
/// @brief Helper session object that represents raw connection to msgq.
isc::cc::Session* cc_session_ = NULL;

/// @brief Session that receives configuration and commands
isc::config::ModuleCCSession* config_session_ = NULL;

/// @brief A dummy configuration handler that always returns success.
///
/// This configuration handler does not perform configuration
/// parsing and always returns success. A dummy handler should
/// be installed using \ref isc::config::ModuleCCSession ctor
/// to get the initial configuration. This initial configuration
/// comprises values for only those elements that were modified
/// the previous session. The \ref dhcp4ConfigHandler can't be
/// used to parse the initial configuration because it needs the
/// full configuration to satisfy dependencies between the
/// various configuration values. Installing the dummy handler
/// that guarantees to return success causes initial configuration
/// to be stored for the session being created and that it can
/// be later accessed with
/// \ref isc::config::ConfigData::getFullConfig().
///
/// @param new_config new configuration.
///
/// @return success configuration status.
76
ConstElementPtr
77
dhcp4StubConfigHandler(ConstElementPtr) {
78 79 80 81 82 83 84 85 86 87 88 89 90 91
    // This configuration handler is intended to be used only
    // when the initial configuration comes in. To receive this
    // configuration a pointer to this handler must be passed
    // using ModuleCCSession constructor. This constructor will
    // invoke the handler and will store the configuration for
    // the configuration session when the handler returns success.
    // Since this configuration is partial we just pretend to
    // parse it and always return success. The function that
    // initiates the session must get the configuration on its
    // own using getFullConfig.
    return (isc::config::createAnswer(0, "Configuration accepted."));
}

ConstElementPtr
92 93
bundyConfigHandler(ConstElementPtr new_config) {
    if (!ControlledDhcpv4Srv::getInstance() || !config_session_) {
94 95 96 97 98 99
        // That should never happen as we install config_handler
        // after we instantiate the server.
        ConstElementPtr answer =
            isc::config::createAnswer(1, "Configuration rejected,"
                                      " server is during startup/shutdown phase.");
        return (answer);
100
    }
101

102 103 104 105 106 107 108 109 110
    // The configuration passed to this handler function is partial.
    // In other words, it just includes the values being modified.
    // In the same time, there are dependencies between various
    // DHCP configuration parsers. For example: the option value can
    // be set if the definition of this option is set. If someone removes
    // an existing option definition then the partial configuration that
    // removes that definition is triggered while a relevant option value
    // may remain configured. This eventually results in the DHCP server
    // configuration being in the inconsistent state.
111 112 113 114 115 116
    // In order to work around this problem we need to merge the new
    // configuration with the existing (full) configuration.

    // Let's create a new object that will hold the merged configuration.
    boost::shared_ptr<MapElement> merged_config(new MapElement());
    // Let's get the existing configuration.
117
    ConstElementPtr full_config = config_session_->getFullConfig();
118 119 120 121 122 123 124 125 126 127 128
    // The full_config and merged_config should be always non-NULL
    // but to provide some level of exception safety we check that they
    // really are (in case we go out of memory).
    if (full_config && merged_config) {
        merged_config->setValue(full_config->mapValue());

        // Merge an existing and new configuration.
        isc::data::merge(merged_config, new_config);
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
            .arg(full_config->str());
    }
129

130
    // Configure the server.
131
    return (ControlledDhcpv4Srv::processConfig(merged_config));
132 133 134 135 136
}




137 138 139
void ControlledDhcpv4Srv::init(const std::string& config_file) {
    // Call base class's init.
    Daemon::init(config_file);
140

141
    string specfile;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
142 143
    if (getenv("KEA_FROM_BUILD")) {
        specfile = string(getenv("KEA_FROM_BUILD")) +
Tomek Mrugalski's avatar
Tomek Mrugalski committed
144
            "/src/bin/dhcp4/dhcp4.spec";
145 146 147 148 149
    } else {
        specfile = string(DHCP4_SPECFILE_LOCATION);
    }

    /// @todo: Check if session is not established already. Throw, if it is.
150

151 152
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING)
              .arg(specfile);
153
    cc_session_ = new Session(io_service_.get_io_service());
154 155 156 157
    // Create a session with the dummy configuration handler.
    // Dumy configuration handler is internally invoked by the
    // constructor and on success the constructor updates
    // the current session with the configuration that had been
158
    // committed in the previous session. If we did not install
159 160
    // the dummy handler, the previous configuration would have
    // been lost.
161
    config_session_ = new ModuleCCSession(specfile, *cc_session_,
162
                                          dhcp4StubConfigHandler,
163
                                          processCommand, false);
164 165
    config_session_->start();

166 167 168 169
    // We initially create ModuleCCSession() without configHandler, as
    // the session module is too eager to send partial configuration.
    // We want to get the full configuration, so we explicitly call
    // getFullConfig() and then pass it to our configHandler.
170
    config_session_->setConfigHandler(bundyConfigHandler);
171 172 173

    try {
        configureDhcp4Server(*this, config_session_->getFullConfig());
174 175 176 177

        // Server will start DDNS communications if its enabled.
        server_->startD2();

178 179
        // Configuration may disable or enable interfaces so we have to
        // reopen sockets according to new configuration.
180
        CfgMgr::instance().getConfiguration()->cfg_iface_
181
            .openSockets(getPort(), useBroadcast());
182

183
    } catch (const std::exception& ex) {
184
        LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
185

186 187
    }

188 189 190
    /// Integrate the asynchronous I/O model of former BIND 10/Bundy
    /// configuration control with the "select" model of the DHCP server.
    /// This is fully explained in \ref dhcpv4Session.
191
    int ctrl_socket = cc_session_->getSocketDesc();
192 193
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
              .arg(ctrl_socket);
194
    IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
195 196
}

197 198

void ControlledDhcpv4Srv::cleanup() {
199 200 201 202 203
    if (config_session_) {
        delete config_session_;
        config_session_ = NULL;
    }
    if (cc_session_) {
204 205

        int ctrl_socket = cc_session_->getSocketDesc();
206
        cc_session_->disconnect();
207 208

        IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
209 210 211 212 213
        delete cc_session_;
        cc_session_ = NULL;
    }
}

214
void
Tomek Mrugalski's avatar
Tomek Mrugalski committed
215
Daemon::loggerInit(const char* log_name, bool verbose) {
216 217
    isc::log::initLogger(log_name,
                         (verbose ? isc::log::DEBUG : isc::log::INFO),
Tomek Mrugalski's avatar
Tomek Mrugalski committed
218
                         isc::log::MAX_DEBUG_LEVEL, NULL, true);
219 220
}

221 222
};
};