srv_config.cc 15.2 KB
Newer Older
1
// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6

7
#include <config.h>
8
#include <exceptions/exceptions.h>
9
#include <dhcpsrv/cfgmgr.h>
10
#include <dhcpsrv/srv_config.h>
11
#include <dhcpsrv/lease_mgr_factory.h>
12
#include <dhcpsrv/cfg_hosts_util.h>
13
#include <process/logging_info.h>
14 15
#include <log/logger_manager.h>
#include <log/logger_specification.h>
16
#include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
17
#include <list>
18 19
#include <sstream>

20
using namespace isc::log;
21
using namespace isc::data;
22
using namespace isc::process;
23

24 25 26
namespace isc {
namespace dhcp {

27
SrvConfig::SrvConfig()
28 29 30
    : sequence_(0), cfg_iface_(new CfgIface()),
      cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()),
      cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
31 32
      cfg_shared_networks4_(new CfgSharedNetworks4()),
      cfg_shared_networks6_(new CfgSharedNetworks6()),
33
      cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
34
      cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
35
      cfg_db_access_(new CfgDbAccess()),
36 37
      cfg_host_operations4_(CfgHostOperations::createConfig4()),
      cfg_host_operations6_(CfgHostOperations::createConfig6()),
38
      class_dictionary_(new ClientClassDictionary()),
39
      decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0),
40
      d2_client_config_(new D2ClientConfig()),
41
      configured_globals_(Element::createMap()),
42
      cfg_consist_(new CfgConsistency()),
43
      server_tag_("") {
44 45
}

46
SrvConfig::SrvConfig(const uint32_t sequence)
47 48 49
    : sequence_(sequence), cfg_iface_(new CfgIface()),
      cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()),
      cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
50 51
      cfg_shared_networks4_(new CfgSharedNetworks4()),
      cfg_shared_networks6_(new CfgSharedNetworks6()),
52
      cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
53
      cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
54
      cfg_db_access_(new CfgDbAccess()),
55 56
      cfg_host_operations4_(CfgHostOperations::createConfig4()),
      cfg_host_operations6_(CfgHostOperations::createConfig6()),
57
      class_dictionary_(new ClientClassDictionary()),
58
      decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0),
59
      d2_client_config_(new D2ClientConfig()),
60
      configured_globals_(Element::createMap()),
61 62
      cfg_consist_(new CfgConsistency()),
      server_tag_("") {
63 64
}

65
std::string
66
SrvConfig::getConfigSummary(const uint32_t selection) const {
67 68 69
    std::ostringstream s;
    size_t subnets_num;
    if ((selection & CFGSEL_SUBNET4) == CFGSEL_SUBNET4) {
70
        subnets_num = getCfgSubnets4()->getAll()->size();
71 72 73 74 75 76 77 78 79
        if (subnets_num > 0) {
            s << "added IPv4 subnets: " << subnets_num;
        } else {
            s << "no IPv4 subnets!";
        }
        s << "; ";
    }

    if ((selection & CFGSEL_SUBNET6) == CFGSEL_SUBNET6) {
80
        subnets_num = getCfgSubnets6()->getAll()->size();
81 82 83 84 85 86 87 88
        if (subnets_num > 0) {
            s << "added IPv6 subnets: " << subnets_num;
        } else {
            s << "no IPv6 subnets!";
        }
        s << "; ";
    }

89
    if ((selection & CFGSEL_DDNS) == CFGSEL_DDNS) {
90
        bool ddns_enabled = getD2ClientConfig()->getEnableUpdates();
91 92 93
        s << "DDNS: " << (ddns_enabled ? "enabled" : "disabled") << "; ";
    }

94
    if (s.tellp() == static_cast<std::streampos>(0)) {
95 96 97 98 99 100 101 102 103 104 105
        s << "no config details available";
    }

    std::string summary = s.str();
    size_t last_separator_pos = summary.find_last_of(";");
    if (last_separator_pos == summary.length() - 2) {
        summary.erase(last_separator_pos);
    }
    return (summary);
}

106
bool
107
SrvConfig::sequenceEquals(const SrvConfig& other) {
108 109 110
    return (getSequence() == other.getSequence());
}

111
void
112
SrvConfig::copy(SrvConfig& new_config) const {
113 114
    ConfigBase::copy(new_config);

115
    // Replace interface configuration.
116
    new_config.cfg_iface_.reset(new CfgIface(*cfg_iface_));
117
    // Replace option definitions.
118
    cfg_option_def_->copyTo(*new_config.cfg_option_def_);
119
    cfg_option_->copyTo(*new_config.cfg_option_);
120 121
    // Replace the client class dictionary
    new_config.class_dictionary_.reset(new ClientClassDictionary(*class_dictionary_));
122 123
    // Replace the D2 client configuration
    new_config.setD2ClientConfig(getD2ClientConfig());
124 125 126 127 128 129 130 131
    // Replace configured hooks libraries.
    new_config.hooks_config_.clear();
    using namespace isc::hooks;
    for (HookLibsCollection::const_iterator it =
           hooks_config_.get().begin();
         it != hooks_config_.get().end(); ++it) {
        new_config.hooks_config_.add(it->first, it->second);
    }
132 133
}

134
bool
135
SrvConfig::equals(const SrvConfig& other) const {
136

137
    // Checks common elements: logging & config control
138
    if (!ConfigBase::equals(other)) {
139 140
        return (false);
    }
141

142
    // Common information is equal between objects, so check other values.
143 144 145 146 147 148 149 150 151 152 153 154 155 156
    if ((*cfg_iface_ != *other.cfg_iface_) ||
        (*cfg_option_def_ != *other.cfg_option_def_) ||
        (*cfg_option_ != *other.cfg_option_) ||
        (*class_dictionary_ != *other.class_dictionary_) ||
        (*d2_client_config_ != *other.d2_client_config_)) {
        return (false);
    }
    // Now only configured hooks libraries can differ.
    // If number of configured hooks libraries are different, then
    // configurations aren't equal.
    if (hooks_config_.get().size() != other.hooks_config_.get().size()) {
        return (false);
    }
    // Pass through all configured hooks libraries.
157
    return (hooks_config_.equal(other.hooks_config_));
158 159
}

160 161 162 163 164 165
void
SrvConfig::merge(const ConfigBase& other) {
    ConfigBase::merge(other);

    try {
        const SrvConfig& other_srv_config = dynamic_cast<const SrvConfig&>(other);
166 167 168 169 170 171 172 173 174 175 176

        /// We merge objects in order of dependency (real or theoretical).
        /// @todo merge globals
        /// @todo merge option defs
        /// @todo merge options

        // Merge shared networks.
        cfg_shared_networks4_->merge(*(other_srv_config.getCfgSharedNetworks4()));

        /// Merge subnets.
        cfg_subnets4_->merge(getCfgSharedNetworks4(), *(other_srv_config.getCfgSubnets4()));
177 178

        /// @todo merge other parts of the configuration here.
179 180

    } catch (const std::bad_cast&) {
181 182 183
        isc_throw(InvalidOperation, "internal server error: must use derivation"
                  " of the SrvConfig as an argument of the call to"
                  " SrvConfig::merge()");
184 185 186
    }
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
187 188 189
void
SrvConfig::removeStatistics() {

190
    // Removes statistics for v4 and v6 subnets
Tomek Mrugalski's avatar
Tomek Mrugalski committed
191
    getCfgSubnets4()->removeStatistics();
192 193

    getCfgSubnets6()->removeStatistics();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
194 195 196 197
}

void
SrvConfig::updateStatistics() {
198 199 200 201 202 203 204 205 206 207 208
    // Updating subnet statistics involves updating lease statistics, which
    // is done by the LeaseMgr.  Since servers with subnets, must have a
    // LeaseMgr, we do not bother updating subnet stats for servers without
    // a lease manager, such as D2. @todo We should probably examine why
    // "SrvConfig" is being used by D2.
    if (LeaseMgrFactory::haveInstance()) {
        // Updates  statistics for v4 and v6 subnets
        getCfgSubnets4()->updateStatistics();

        getCfgSubnets6()->updateStatistics();
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
209 210
}

211
void
212 213 214 215 216 217 218 219 220 221 222 223 224 225
SrvConfig::extractConfiguredGlobals(isc::data::ConstElementPtr config) {
    if (config->getType() != Element::map) {
        isc_throw(BadValue, "extractConfiguredGlobals must be given a map element");
    }

    const std::map<std::string, ConstElementPtr>& values = config->mapValue();
    for (auto value = values.begin(); value != values.end(); ++value) {
        if (value->second->getType() != Element::list &&
            value->second->getType() != Element::map) {
                addConfiguredGlobal(value->first, value->second);
        }
    }
}

226 227
ElementPtr
SrvConfig::toElement() const {
228 229 230
    // Toplevel map
    ElementPtr result = ConfigBase::toElement();

231 232 233 234
    // Get family for the configuration manager
    uint16_t family = CfgMgr::instance().getFamily();
    // DhcpX global map
    ElementPtr dhcp = Element::createMap();
235 236

    // Add in explicitly configured globals.
237
    dhcp->setValue(configured_globals_->mapValue());
238

239 240
    // Set user-context
    contextToElement(dhcp);
241

242 243 244 245 246 247 248 249 250 251
    // Set decline-probation-period
    dhcp->set("decline-probation-period",
              Element::create(static_cast<long long>(decline_timer_)));
    // Set echo-client-id (DHCPv4)
    if (family == AF_INET) {
        dhcp->set("echo-client-id", Element::create(echo_v4_client_id_));
    }
    // Set dhcp4o6-port
    dhcp->set("dhcp4o6-port",
              Element::create(static_cast<int>(dhcp4o6_port_)));
252

253 254 255 256 257
    // Set dhcp-ddns
    dhcp->set("dhcp-ddns", d2_client_config_->toElement());
    // Set interfaces-config
    dhcp->set("interfaces-config", cfg_iface_->toElement());
    // Set option-def
Tomek Mrugalski's avatar
Tomek Mrugalski committed
258
    dhcp->set("option-def", cfg_option_def_->toElement());
259
    // Set option-data
Tomek Mrugalski's avatar
Tomek Mrugalski committed
260
    dhcp->set("option-data", cfg_option_->toElement());
261 262

    // Set subnets and shared networks.
263 264 265 266 267 268 269 270 271 272

    // We have two problems to solve:
    //   - a subnet is unparsed once:
    //       * if it is a plain subnet in the global subnet list
    //       * if it is a member of a shared network in the shared network
    //         subnet list
    //   - unparsed subnets must be kept to add host reservations in them.
    //     Of course this can be done only when subnets are unparsed.

    // The list of all unparsed subnets
273
    std::vector<ElementPtr> sn_list;
274

275
    if (family == AF_INET) {
276
        // Get plain subnets
277
        ElementPtr plain_subnets = Element::createList();
278 279 280
        const Subnet4Collection* subnets = cfg_subnets4_->getAll();
        for (Subnet4Collection::const_iterator subnet = subnets->cbegin();
             subnet != subnets->cend(); ++subnet) {
281 282 283 284 285 286
            // Skip subnets which are in a shared-network
            SharedNetwork4Ptr network;
            (*subnet)->getSharedNetwork(network);
            if (network) {
                continue;
            }
287 288
            ElementPtr subnet_cfg = (*subnet)->toElement();
            sn_list.push_back(subnet_cfg);
289
            plain_subnets->add(subnet_cfg);
290 291
        }
        dhcp->set("subnet4", plain_subnets);
292

293
        // Get shared networks
294
        ElementPtr shared_networks = cfg_shared_networks4_->toElement();
295 296
        dhcp->set("shared-networks", shared_networks);

297
        // Get subnets in shared network subnet lists
298 299 300 301 302 303 304 305 306 307
        const std::vector<ElementPtr> networks = shared_networks->listValue();
        for (auto network = networks.cbegin();
             network != networks.cend(); ++network) {
            const std::vector<ElementPtr> sh_list =
                (*network)->get("subnet4")->listValue();
            for (auto subnet = sh_list.cbegin();
                 subnet != sh_list.cend(); ++subnet) {
                sn_list.push_back(*subnet);
            }
        }
308

309
    } else {
310
        // Get plain subnets
311
        ElementPtr plain_subnets = Element::createList();
312 313 314
        const Subnet6Collection* subnets = cfg_subnets6_->getAll();
        for (Subnet6Collection::const_iterator subnet = subnets->cbegin();
             subnet != subnets->cend(); ++subnet) {
315 316 317 318 319 320
            // Skip subnets which are in a shared-network
            SharedNetwork6Ptr network;
            (*subnet)->getSharedNetwork(network);
            if (network) {
                continue;
            }
321 322
            ElementPtr subnet_cfg = (*subnet)->toElement();
            sn_list.push_back(subnet_cfg);
323
            plain_subnets->add(subnet_cfg);
324 325
        }
        dhcp->set("subnet6", plain_subnets);
326

327
        // Get shared networks
328
        ElementPtr shared_networks = cfg_shared_networks6_->toElement();
329
        dhcp->set("shared-networks", shared_networks);
330 331

        // Get subnets in shared network subnet lists
332 333 334 335 336 337 338 339 340 341
        const std::vector<ElementPtr> networks = shared_networks->listValue();
        for (auto network = networks.cbegin();
             network != networks.cend(); ++network) {
            const std::vector<ElementPtr> sh_list =
                (*network)->get("subnet6")->listValue();
            for (auto subnet = sh_list.cbegin();
                 subnet != sh_list.cend(); ++subnet) {
                sn_list.push_back(*subnet);
            }
        }
342
    }
343

344
    // Host reservations
345 346
    CfgHostsList resv_list;
    resv_list.internalize(cfg_hosts_->toElement());
347 348 349 350 351 352 353 354

    // Insert global reservations
    ConstElementPtr global_resvs = resv_list.get(SUBNET_ID_GLOBAL);
    if (global_resvs->size() > 0) {
        dhcp->set("reservations", global_resvs);
    }

    // Insert subnet reservations
355 356
    for (std::vector<ElementPtr>::const_iterator subnet = sn_list.cbegin();
         subnet != sn_list.cend(); ++subnet) {
357 358 359 360 361 362 363 364
        ConstElementPtr id = (*subnet)->get("id");
        if (isNull(id)) {
            isc_throw(ToElementError, "subnet has no id");
        }
        SubnetID subnet_id = id->intValue();
        ConstElementPtr resvs = resv_list.get(subnet_id);
        (*subnet)->set("reservations", resvs);
    }
365

366 367 368 369
    // Set expired-leases-processing
    ConstElementPtr expired = cfg_expiration_->toElement();
    dhcp->set("expired-leases-processing", expired);
    if (family == AF_INET6) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
370
        // Set server-id (DHCPv6)
371
        dhcp->set("server-id", cfg_duid_->toElement());
Tomek Mrugalski's avatar
Tomek Mrugalski committed
372 373 374

        // Set relay-supplied-options (DHCPv6)
        dhcp->set("relay-supplied-options", cfg_rsoo_->toElement());
375 376 377 378
    }
    // Set lease-database
    CfgLeaseDbAccess lease_db(*cfg_db_access_);
    dhcp->set("lease-database", lease_db.toElement());
Francis Dupont's avatar
Francis Dupont committed
379
    // Set hosts-databases
380
    CfgHostDbAccess host_db(*cfg_db_access_);
Francis Dupont's avatar
Francis Dupont committed
381 382 383
    ConstElementPtr hosts_databases = host_db.toElement();
    if (hosts_databases->size() > 0) {
        dhcp->set("hosts-databases", hosts_databases);
384
    }
385 386 387 388 389 390 391 392 393 394 395 396 397 398
    // Set host-reservation-identifiers
    ConstElementPtr host_ids;
    if (family == AF_INET) {
        host_ids = cfg_host_operations4_->toElement();
    } else {
        host_ids = cfg_host_operations6_->toElement();
    }
    dhcp->set("host-reservation-identifiers", host_ids);
    // Set mac-sources (DHCPv6)
    if (family == AF_INET6) {
        dhcp->set("mac-sources", cfg_mac_source_.toElement());
    }
    // Set control-socket (skip if null as empty is not legal)
    if (!isNull(control_socket_)) {
399
        dhcp->set("control-socket", UserContext::toElement(control_socket_));
400 401 402
    }
    // Set client-classes
    ConstElementPtr client_classes = class_dictionary_->toElement();
403 404 405 406
    // @todo accept empty list
    if (!client_classes->empty()) {
        dhcp->set("client-classes", client_classes);
    }
407 408 409 410 411 412
    // Set hooks-libraries
    ConstElementPtr hooks_libs = hooks_config_.toElement();
    dhcp->set("hooks-libraries", hooks_libs);
    // Set DhcpX
    result->set(family == AF_INET ? "Dhcp4" : "Dhcp6", dhcp);

413 414 415
    ConstElementPtr cfg_consist = cfg_consist_->toElement();
    dhcp->set("sanity-checks", cfg_consist);

416 417 418 419 420 421 422
    // Set config-control (if it exists)
    ConstConfigControlInfoPtr info = getConfigControlInfo();
    if (info) {
        ConstElementPtr info_elem = info->toElement();
        dhcp->set("config-control", info_elem);
    }

423 424 425 426
    // Set dhcp-packet-control (if it exists)
    data::ConstElementPtr dhcp_queue_control = getDHCPQueueControl();
    if (dhcp_queue_control) {
        dhcp->set("dhcp-queue-control", dhcp_queue_control);
427 428
    }

429 430 431
    return (result);
}

432 433
}
}