cfgmgr.cc 14 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
//
// 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.

15
#include <asiolink/io_address.h>
16
#include <dhcp/iface_mgr.h>
17
#include <dhcp/libdhcp++.h>
18
#include <dhcpsrv/cfgmgr.h>
19
#include <dhcpsrv/dhcpsrv_log.h>
20
#include <string>
21

22 23 24
using namespace isc::asiolink;
using namespace isc::util;

25 26 27
namespace isc {
namespace dhcp {

28 29 30 31 32 33
CfgMgr&
CfgMgr::instance() {
    static CfgMgr cfg_mgr;
    return (cfg_mgr);
}

34 35 36 37 38 39 40 41 42 43 44
void
CfgMgr::addOptionSpace4(const OptionSpacePtr& space) {
    if (!space) {
        isc_throw(InvalidOptionSpace,
                  "provided option space object is NULL.");
    }
    OptionSpaceCollection::iterator it = spaces4_.find(space->getName());
    if (it != spaces4_.end()) {
        isc_throw(InvalidOptionSpace, "option space " << space->getName()
                  << " already added.");
    }
45
    spaces4_.insert(make_pair(space->getName(), space));
46 47 48 49 50 51 52 53 54 55 56 57 58
}

void
CfgMgr::addOptionSpace6(const OptionSpacePtr& space) {
    if (!space) {
        isc_throw(InvalidOptionSpace,
                  "provided option space object is NULL.");
    }
    OptionSpaceCollection::iterator it = spaces6_.find(space->getName());
    if (it != spaces6_.end()) {
        isc_throw(InvalidOptionSpace, "option space " << space->getName()
                  << " already added.");
    }
59
    spaces6_.insert(make_pair(space->getName(), space));
60 61
}

62 63 64 65 66 67 68 69
void
CfgMgr::addOptionDef(const OptionDefinitionPtr& def,
                     const std::string& option_space) {
    // @todo we need better validation of the provided option space name here.
    // This will be implemented when #2313 is merged.
    if (option_space.empty()) {
        isc_throw(BadValue, "option space name must not be empty");
    } else if (!def) {
70
        // Option definition must point to a valid object.
71
        isc_throw(MalformedOptionDefinition, "option definition must not be NULL");
72

73
    } else if (getOptionDef(option_space, def->getCode())) {
74
        // Option definition must not be overriden.
75 76
        isc_throw(DuplicateOptionDefinition, "option definition already added"
                  << " to option space " << option_space);
77

78 79 80
    // We must not override standard (assigned) option for which there is a
    // definition in libdhcp++. The standard options belong to dhcp4 or dhcp6
    // option space.
81
    } else if ((option_space == "dhcp4" &&
82 83
                LibDHCP::isStandardOption(Option::V4, def->getCode()) &&
                LibDHCP::getOptionDef(Option::V4, def->getCode())) ||
84
               (option_space == "dhcp6" &&
85 86
                LibDHCP::isStandardOption(Option::V6, def->getCode()) &&
                LibDHCP::getOptionDef(Option::V6, def->getCode()))) {
87 88 89 90
        isc_throw(BadValue, "unable to override definition of option '"
                  << def->getCode() << "' in standard option space '"
                  << option_space << "'.");

91
    }
92 93
    // Actually add a new item.
    option_def_spaces_.addItem(def, option_space);
94 95
}

96
OptionDefContainerPtr
97
CfgMgr::getOptionDefs(const std::string& option_space) const {
98 99
    // @todo Validate the option space once the #2313 is implemented.

100
    return (option_def_spaces_.getItems(option_space));
101 102 103 104 105
}

OptionDefinitionPtr
CfgMgr::getOptionDef(const std::string& option_space,
                     const uint16_t option_code) const {
106 107
    // @todo Validate the option space once the #2313 is implemented.

108
    // Get a reference to option definitions for a particular option space.
109
    OptionDefContainerPtr defs = getOptionDefs(option_space);
110
    // If there are no matching option definitions then return the empty pointer.
111
    if (!defs || defs->empty()) {
112 113
        return (OptionDefinitionPtr());
    }
114 115
    // If there are some option definitions for a particular option space
    // use an option code to get the one we want.
116
    const OptionDefContainerTypeIndex& idx = defs->get<1>();
117
    const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
118
    // If there is no definition that matches option code, return empty pointer.
119 120 121
    if (std::distance(range.first, range.second) == 0) {
        return (OptionDefinitionPtr());
    }
122 123 124
    // If there is more than one definition matching an option code, return
    // the first one. This should not happen because we check for duplicates
    // when addOptionDef is called.
125 126 127
    return (*range.first);
}

128
Subnet6Ptr
129
CfgMgr::getSubnet6(const std::string& iface,
130
                   const isc::dhcp::ClientClasses& classes) {
131 132 133 134 135 136 137 138

    if (!iface.length()) {
        return (Subnet6Ptr());
    }

    // If there is more than one, we need to choose the proper one
    for (Subnet6Collection::iterator subnet = subnets6_.begin();
         subnet != subnets6_.end(); ++subnet) {
139 140 141 142 143 144

        // If client is rejected because of not meeting client class criteria...
        if (!(*subnet)->clientSupported(classes)) {
            continue;
        }

145 146 147 148 149 150 151 152 153 154
        if (iface == (*subnet)->getIface()) {
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
                      DHCPSRV_CFGMGR_SUBNET6_IFACE)
                .arg((*subnet)->toText()).arg(iface);
            return (*subnet);
        }
    }
    return (Subnet6Ptr());
}

155
Subnet6Ptr
156
CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint,
157
                   const isc::dhcp::ClientClasses& classes,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
158
                   const bool relay) {
159 160 161 162

    // If there is more than one, we need to choose the proper one
    for (Subnet6Collection::iterator subnet = subnets6_.begin();
         subnet != subnets6_.end(); ++subnet) {
163

164 165 166 167 168
        // If client is rejected because of not meeting client class criteria...
        if (!(*subnet)->clientSupported(classes)) {
            continue;
        }

169 170 171
        // If the hint is a relay address, and there is relay info specified
        // for this subnet and those two match, then use this subnet.
        if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
172 173 174
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
                      DHCPSRV_CFGMGR_SUBNET6_RELAY)
                .arg((*subnet)->toText()).arg(hint.toText());
175 176 177
            return (*subnet);
        }

178
        if ((*subnet)->inRange(hint)) {
179
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
180
                      .arg((*subnet)->toText()).arg(hint.toText());
181 182 183 184 185
            return (*subnet);
        }
    }

    // sorry, we don't support that subnet
186 187
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_NO_SUBNET6)
              .arg(hint.toText());
188 189 190
    return (Subnet6Ptr());
}

191
Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option,
192
                              const isc::dhcp::ClientClasses& classes) {
193 194 195 196
    if (!iface_id_option) {
        return (Subnet6Ptr());
    }

197 198
    // Let's iterate over all subnets and for those that have interface-id
    // defined, check if the interface-id is equal to what we are looking for
199 200
    for (Subnet6Collection::iterator subnet = subnets6_.begin();
         subnet != subnets6_.end(); ++subnet) {
201 202 203 204 205 206

        // If client is rejected because of not meeting client class criteria...
        if (!(*subnet)->clientSupported(classes)) {
            continue;
        }

207 208 209 210 211 212 213 214 215
        if ( (*subnet)->getInterfaceId() &&
             ((*subnet)->getInterfaceId()->equal(iface_id_option))) {
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
                      DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
                .arg((*subnet)->toText());
            return (*subnet);
        }
    }
    return (Subnet6Ptr());
216 217 218
}

void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
219 220
    /// @todo: Check that this new subnet does not cross boundaries of any
    /// other already defined subnet.
221
    /// @todo: Check that there is no subnet with the same interface-id
222 223
    if (isDuplicate(*subnet)) {
        isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
224
                  << subnet->getID() << "' is already in use");
225
    }
226 227
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
              .arg(subnet->toText());
228 229 230
    subnets6_.push_back(subnet);
}

231
Subnet4Ptr
232
CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint,
233
                   const isc::dhcp::ClientClasses& classes,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
234
                   bool relay) const {
235 236
    // Iterate over existing subnets to find a suitable one for the
    // given address.
237
    for (Subnet4Collection::const_iterator subnet = subnets4_.begin();
238
         subnet != subnets4_.end(); ++subnet) {
239 240 241 242 243 244

        // If client is rejected because of not meeting client class criteria...
        if (!(*subnet)->clientSupported(classes)) {
            continue;
        }

245 246 247
        // If the hint is a relay address, and there is relay info specified
        // for this subnet and those two match, then use this subnet.
        if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
248 249 250
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
                      DHCPSRV_CFGMGR_SUBNET4_RELAY)
                .arg((*subnet)->toText()).arg(hint.toText());
251 252 253
            return (*subnet);
        }

254
        // Let's check if the client belongs to the given subnet
255
        if ((*subnet)->inRange(hint)) {
256 257 258
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
                      DHCPSRV_CFGMGR_SUBNET4)
                      .arg((*subnet)->toText()).arg(hint.toText());
259 260 261 262 263
            return (*subnet);
        }
    }

    // sorry, we don't support that subnet
264 265
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_NO_SUBNET4)
              .arg(hint.toText());
266 267 268
    return (Subnet4Ptr());
}

269
Subnet4Ptr
270 271
CfgMgr::getSubnet4(const std::string& iface_name,
                   const isc::dhcp::ClientClasses& classes) const {
272
    Iface* iface = IfaceMgr::instance().getIface(iface_name);
273
    // This should never happen in the real life. Hence we throw an exception.
274
    if (iface == NULL) {
275 276 277
        isc_throw(isc::BadValue, "interface " << iface_name <<
                  " doesn't exist and therefore it is impossible"
                  " to find a suitable subnet for its IPv4 address");
278
    }
279
    IOAddress addr("0.0.0.0");
280 281 282
    // If IPv4 address assigned to the interface exists, find a suitable
    // subnet for it, else return NULL pointer to indicate that no subnet
    // could be found.
283
    return (iface->getAddress4(addr) ? getSubnet4(addr, classes) : Subnet4Ptr());
284 285
}

286 287 288
void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
    /// @todo: Check that this new subnet does not cross boundaries of any
    /// other already defined subnet.
289 290
    if (isDuplicate(*subnet)) {
        isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
291
                  << subnet->getID() << "' is already in use");
292
    }
293 294
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
              .arg(subnet->toText());
295 296 297
    subnets4_.push_back(subnet);
}

298
void CfgMgr::deleteOptionDefs() {
299
    option_def_spaces_.clearItems();
300 301
}

302
void CfgMgr::deleteSubnets4() {
303
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DELETE_SUBNET4);
304 305 306 307
    subnets4_.clear();
}

void CfgMgr::deleteSubnets6() {
308
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DELETE_SUBNET6);
309 310 311
    subnets6_.clear();
}

312

313 314 315 316
std::string CfgMgr::getDataDir() {
    return (datadir_);
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
bool
CfgMgr::isDuplicate(const Subnet4& subnet) const {
    for (Subnet4Collection::const_iterator subnet_it = subnets4_.begin();
         subnet_it != subnets4_.end(); ++subnet_it) {
        if ((*subnet_it)->getID() == subnet.getID()) {
            return (true);
        }
    }
    return (false);
}

bool
CfgMgr::isDuplicate(const Subnet6& subnet) const {
    for (Subnet6Collection::const_iterator subnet_it = subnets6_.begin();
         subnet_it != subnets6_.end(); ++subnet_it) {
        if ((*subnet_it)->getID() == subnet.getID()) {
            return (true);
        }
    }
    return (false);
}


340 341 342 343 344 345
void
CfgMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
    d2_client_mgr_.setD2ClientConfig(new_config);
}

bool
346 347
CfgMgr::ddnsEnabled() {
    return (d2_client_mgr_.ddnsEnabled());
348 349 350 351 352 353 354
}

const D2ClientConfigPtr&
CfgMgr::getD2ClientConfig() const {
    return (d2_client_mgr_.getD2ClientConfig());
}

355 356 357 358 359
D2ClientMgr&
CfgMgr::getD2ClientMgr() {
    return (d2_client_mgr_);
}

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
void
CfgMgr::ensureCurrentAllocated() {
    if (!configuration_ || configs_.empty()) {
        configuration_.reset(new Configuration());
        configs_.push_back(configuration_);
    }
}

void
CfgMgr::clear() {
    configs_.clear();
    ensureCurrentAllocated();
}

void
CfgMgr::commit() {
376 377
    ensureCurrentAllocated();
    if (configs_.back()->sequenceEquals(*configuration_)) {
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
        configuration_ = configs_.back();
    }
}

void
CfgMgr::rollback() {
    ensureCurrentAllocated();
    if (!configuration_->sequenceEquals(*configs_.back())) {
        configs_.pop_back();
    }
}

ConstConfigurationPtr
CfgMgr::getCurrent() {
    ensureCurrentAllocated();
    return (configuration_);
}

ConfigurationPtr
CfgMgr::getStaging() {
    ensureCurrentAllocated();
    if (configuration_->sequenceEquals(*configs_.back())) {
        uint32_t sequence = configuration_->getSequence();
        configs_.push_back(ConfigurationPtr(new Configuration(++sequence)));
    }
    return (configs_.back());
}

406
CfgMgr::CfgMgr()
407
    : datadir_(DHCP_DATA_DIR), echo_v4_client_id_(true),
408
      d2_client_mgr_() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
409 410 411
    // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
    // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
    // See AM_CPPFLAGS definition in Makefile.am
412
    ensureCurrentAllocated();
413 414 415 416
}

CfgMgr::~CfgMgr() {
}
417 418 419

}; // end of isc::dhcp namespace
}; // end of isc namespace