ctrl_dhcp6_srv.cc 10.5 KB
Newer Older
1
// Copyright (C) 2012-2013 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 <dhcpsrv/dhcp_config_parser.h>
23
#include <dhcpsrv/cfgmgr.h>
24
#include <dhcp6/config_parser.h>
25
#include <dhcp6/ctrl_dhcp6_srv.h>
26
27
28
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/spec_config.h>
#include <exceptions/exceptions.h>
29
#include <hooks/hooks_manager.h>
30
#include <util/buffer.h>
31

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

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

namespace isc {
namespace dhcp {

ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;

ConstElementPtr
53
54
55
56
57
58
59
60
61
62
63
64
65
66
ControlledDhcpv6Srv::dhcp6StubConfigHandler(ConstElementPtr) {
    // 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."));
}

67
68
ConstElementPtr
ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
69

70
71
72
73
74
75
76
    if (!server_ || !server_->config_session_) {
        // 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);
77
78
    }

79
80
81
82
83
84
85
86
87
    // 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.
88
89
90
91
92
93
    // 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.
94
    ConstElementPtr full_config = server_->config_session_->getFullConfig();
95
96
97
98
99
100
101
102
103
104
105
    // 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(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE)
            .arg(merged_config->str());
    }
106

107
    // Configure the server.
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    ConstElementPtr answer = configureDhcp6Server(*server_, merged_config);

    // Check that configuration was successful. If not, do not reopen sockets.
    int rcode = 0;
    parseAnswer(rcode, answer);
    if (rcode != 0) {
        return (answer);
    }

    // Configuration may change active interfaces. Therefore, we have to reopen
    // sockets according to new configuration. This operation is not exception
    // safe and we really don't want to emit exceptions to the callback caller.
    // Instead, catch an exception and create appropriate answer.
    try {
        server_->openActiveSockets(server_->getPort());
    } catch (const std::exception& ex) {
        std::ostringstream err;
        err << "failed to open sockets after server reconfiguration: " << ex.what();
        answer = isc::config::createAnswer(1, err.str());
    }
    return (answer);
129
130
131
132
}

ConstElementPtr
ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr args) {
133
134
135
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED)
              .arg(command).arg(args->str());

136
137
138
139
    if (command == "shutdown") {
        if (ControlledDhcpv6Srv::server_) {
            ControlledDhcpv6Srv::server_->shutdown();
        } else {
140
            LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING);
141
142
143
144
145
146
147
            ConstElementPtr answer = isc::config::createAnswer(1,
                                     "Shutdown failure.");
            return (answer);
        }
        ConstElementPtr answer = isc::config::createAnswer(0,
                                 "Shutting down.");
        return (answer);
148

149
    } else if (command == "libreload") {
150
151
152
153
154
        // TODO delete any stored CalloutHandles referring to the old libraries
        // Get list of currently loaded libraries and reload them.
        vector<string> loaded = HooksManager::getLibraryNames();
        bool status = HooksManager::loadLibraries(loaded);
        if (!status) {
155
            LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL);
156
157
158
159
160
161
162
            ConstElementPtr answer = isc::config::createAnswer(1,
                                     "Failed to reload hooks libraries.");
            return (answer);
        }
        ConstElementPtr answer = isc::config::createAnswer(0,
                                 "Hooks libraries successfully reloaded.");
        return (answer);
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    }

    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() {
180

181
182
183
184
185
186
187
188
189
    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.
190

191
192
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING)
              .arg(specfile);
193
    cc_session_ = new Session(io_service_.get_io_service());
194
195
196
197
    // 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
198
    // committed in the previous session. If we did not install
199
200
    // the dummy handler, the previous configuration would have
    // been lost.
201
    config_session_ = new ModuleCCSession(specfile, *cc_session_,
202
                                          dhcp6StubConfigHandler,
203
204
205
                                          dhcp6CommandHandler, false);
    config_session_->start();

206
207
208
209
    // The constructor already pulled the configuration that had
    // been created in the previous session thanks to the dummy
    // handler. We can switch to the handler that will be
    // parsing future changes to the configuration.
210
211
    config_session_->setConfigHandler(dhcp6ConfigHandler);

212
    try {
213
        // Pull the full configuration out from the session.
214
        configureDhcp6Server(*this, config_session_->getFullConfig());
215
216
217
218
        // Configuration may disable or enable interfaces so we have to
        // reopen sockets according to new configuration.
        openActiveSockets(getPort());

219
    } catch (const std::exception& ex) {
220
        LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
221

222
223
    }

224
225
226
227
    /// 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();
228
229
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED)
              .arg(ctrl_socket);
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
    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);
}

248
249
ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
    : Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) {
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
    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);
    }
}

};
};