cfg_hosts.cc 26.4 KB
Newer Older
1
// Copyright (C) 2014-2018 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 9
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
10
#include <dhcpsrv/cfg_hosts.h>
11
#include <dhcpsrv/cfg_hosts_util.h>
12
#include <dhcpsrv/hosts_log.h>
13
#include <dhcpsrv/cfgmgr.h>
14
#include <exceptions/exceptions.h>
15
#include <util/encode/hex.h>
16
#include <ostream>
17 18
#include <string>
#include <vector>
19 20

using namespace isc::asiolink;
21
using namespace isc::data;
22 23 24 25

namespace isc {
namespace dhcp {

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
ConstHostCollection
CfgHosts::getAll(const Host::IdentifierType& identifier_type,
                 const uint8_t* identifier_begin,
                 const size_t identifier_len) const {
    // Do not issue logging message here because it will be logged by
    // the getAllInternal method.
    ConstHostCollection collection;
    getAllInternal<ConstHostCollection>(identifier_type, identifier_begin,
                                        identifier_len, collection);
    return (collection);
}

HostCollection
CfgHosts::getAll(const Host::IdentifierType& identifier_type,
                 const uint8_t* identifier_begin, const size_t identifier_len) {
    // Do not issue logging message here because it will be logged by
    // the getAllInternal method.
    HostCollection collection;
    getAllInternal<HostCollection>(identifier_type, identifier_begin,
                                   identifier_len, collection);
    return (collection);
}

49
ConstHostCollection
50
CfgHosts::getAll4(const IOAddress& address) const {
51 52
    // Do not issue logging message here because it will be logged by
    // the getAllInternal4 method.
53 54 55
    ConstHostCollection collection;
    getAllInternal4<ConstHostCollection>(address, collection);
    return (collection);
56 57 58
}

HostCollection
59
CfgHosts::getAll4(const IOAddress& address) {
60 61
    // Do not issue logging message here because it will be logged by
    // the getAllInternal4 method.
62 63 64
    HostCollection collection;
    getAllInternal4<HostCollection>(address, collection);
    return (collection);
65 66
}

67 68
ConstHostCollection
CfgHosts::getAll6(const IOAddress& address) const {
69 70
    // Do not issue logging message here because it will be logged by
    // the getAllInternal6 method.
71 72 73 74 75 76 77
    ConstHostCollection collection;
    getAllInternal6<ConstHostCollection>(address, collection);
    return (collection);
}

HostCollection
CfgHosts::getAll6(const IOAddress& address) {
78 79
    // Do not issue logging message here because it will be logged by
    // the getAllInternal6 method.
80 81 82 83 84
    HostCollection collection;
    getAllInternal6<HostCollection>(address, collection);
    return (collection);
}

85 86
template<typename Storage>
void
87 88 89
CfgHosts::getAllInternal(const Host::IdentifierType& identifier_type,
                         const uint8_t* identifier,
                         const size_t identifier_len,
90
                         Storage& storage) const {
91

92 93 94 95 96
    // Convert host identifier into textual format for logging purposes.
    // This conversion is exception free.
    std::string identifier_text = Host::getIdentifierAsText(identifier_type,
                                                            identifier,
                                                            identifier_len);
97 98 99
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_IDENTIFIER)
        .arg(identifier_text);

100
    // Use the identifier and identifier type as a composite key.
101
    const HostContainerIndex0& idx = hosts_.get<0>();
102
    boost::tuple<const std::vector<uint8_t>, const Host::IdentifierType> t =
103 104 105
        boost::make_tuple(std::vector<uint8_t>(identifier,
                                               identifier + identifier_len),
                                               identifier_type);
106

107
    // Append each Host object to the storage.
108 109
    for (HostContainerIndex0::iterator host = idx.lower_bound(t);
         host != idx.upper_bound(t);
110
         ++host) {
111 112 113 114
        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
                  HOSTS_CFG_GET_ALL_IDENTIFIER_HOST)
            .arg(identifier_text)
            .arg((*host)->toText());
115 116
        storage.push_back(*host);
    }
117

118
    // Log how many hosts have been found.
119 120 121
    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_IDENTIFIER_COUNT)
        .arg(identifier_text)
        .arg(storage.size());
122 123
}

124 125 126
template<typename Storage>
void
CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const {
127 128 129
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_ADDRESS4)
        .arg(address.toText());

130 131 132 133 134 135 136 137 138 139 140
    // Must not specify address other than IPv4.
    if (!address.isV4()) {
        isc_throw(BadHostAddress, "must specify an IPv4 address when searching"
                  " for a host, specified address was " << address);
    }
    // Search for the Host using the reserved IPv4 address as a key.
    const HostContainerIndex1& idx = hosts_.get<1>();
    HostContainerIndex1Range r = idx.equal_range(address);
    // Append each Host object to the storage.
    for (HostContainerIndex1::iterator host = r.first; host != r.second;
         ++host) {
141 142 143 144
        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
                  HOSTS_CFG_GET_ALL_ADDRESS4_HOST)
            .arg(address.toText())
            .arg((*host)->toText());
145 146
        storage.push_back(*host);
    }
147 148 149 150

    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_ADDRESS4_COUNT)
        .arg(address.toText())
        .arg(storage.size());
151 152
}

153 154 155
template<typename Storage>
void
CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const {
156 157 158
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_ADDRESS6)
        .arg(address.toText());

159 160 161 162 163
    // Must not specify address other than IPv6.
    if (!address.isV6()) {
        isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
                  " for a host, specified address was " << address);
    }
164
    // Search for the Host using the reserved IPv6 address as a key.
165 166 167 168 169
    const HostContainerIndex1& idx = hosts_.get<1>();
    HostContainerIndex1Range r = idx.equal_range(address);
    // Append each Host object to the storage.
    for (HostContainerIndex1::iterator host = r.first; host != r.second;
         ++host) {
170 171 172 173
        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
                  HOSTS_CFG_GET_ALL_ADDRESS6_HOST)
            .arg(address.toText())
            .arg((*host)->toText());
174 175
        storage.push_back(*host);
    }
176 177 178 179

    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_ADDRESS6_COUNT)
        .arg(address.toText())
        .arg(storage.size());
180 181
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
ConstHostPtr
CfgHosts::get4(const SubnetID& subnet_id,
               const Host::IdentifierType& identifier_type,
               const uint8_t* identifier_begin,
               const size_t identifier_len) const {
    return (getHostInternal(subnet_id, false, identifier_type, identifier_begin,
                            identifier_len));
}

HostPtr
CfgHosts::get4(const SubnetID& subnet_id,
               const Host::IdentifierType& identifier_type,
               const uint8_t* identifier_begin,
               const size_t identifier_len) {
    return (getHostInternal(subnet_id, false, identifier_type, identifier_begin,
                            identifier_len));
198 199
}

200 201
ConstHostPtr
CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
202 203 204
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4)
        .arg(subnet_id).arg(address.toText());

205 206 207 208
    ConstHostCollection hosts = getAll4(address);
    for (ConstHostCollection::const_iterator host = hosts.begin();
         host != hosts.end(); ++host) {
        if ((*host)->getIPv4SubnetID() == subnet_id) {
209
            LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
210 211 212 213
                      HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_HOST)
                .arg(subnet_id)
                .arg(address.toText())
                .arg((*host)->toText());
214 215 216
            return (*host);
        }
    }
217 218 219

    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_NULL)
        .arg(subnet_id).arg(address.toText());
220 221 222 223
    return (ConstHostPtr());
}


224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
ConstHostPtr
CfgHosts::get6(const SubnetID& subnet_id,
               const Host::IdentifierType& identifier_type,
               const uint8_t* identifier_begin,
               const size_t identifier_len) const {
    return (getHostInternal(subnet_id, true, identifier_type, identifier_begin,
                            identifier_len));
}

HostPtr
CfgHosts::get6(const SubnetID& subnet_id,
               const Host::IdentifierType& identifier_type,
               const uint8_t* identifier_begin,
               const size_t identifier_len) {
    return (getHostInternal(subnet_id, true, identifier_type, identifier_begin,
                            identifier_len));
240 241 242
}

ConstHostPtr
243 244
CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
    return (getHostInternal6<ConstHostPtr>(prefix, prefix_len));
245 246 247
}

HostPtr
248 249
CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) {
    return (getHostInternal6<HostPtr>(prefix, prefix_len));
250 251
}

252
ConstHostPtr
253 254
CfgHosts::get6(const SubnetID& subnet_id,
               const asiolink::IOAddress& address) const {
255 256 257
    // Do not log here because getHostInternal6 logs.
    return (getHostInternal6<ConstHostPtr, ConstHostCollection>(subnet_id, address));
}
258

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
HostPtr
CfgHosts::get6(const SubnetID& subnet_id,
               const asiolink::IOAddress& address) {
    // Do not log here because getHostInternal6 logs.
    return (getHostInternal6<HostPtr, HostCollection>(subnet_id, address));
}

template<typename ReturnType, typename Storage>
ReturnType
CfgHosts::getHostInternal6(const SubnetID& subnet_id,
                           const asiolink::IOAddress& address) const {
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6)
        .arg(subnet_id).arg(address.toText());

    Storage storage;
    getAllInternal6<Storage>(subnet_id, address, storage);
275 276
    switch (storage.size()) {
    case 0:
277 278 279 280
        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
                  HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_NULL)
            .arg(subnet_id)
            .arg(address.toText());
281
        return (HostPtr());
282

283
    case 1:
284
        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
285 286 287 288
                  HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_HOST)
            .arg(subnet_id)
            .arg(address.toText())
            .arg((*storage.begin())->toText());
289
        return (*storage.begin());
290

291 292 293 294 295
    default:
        isc_throw(DuplicateHost,  "more than one reservation found"
                  " for the host belonging to the subnet with id '"
                  << subnet_id << "' and using the address '"
                  << address.toText() << "'");
296
    }
297

298 299
}

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
template<typename ReturnType>
ReturnType
CfgHosts::getHostInternal6(const asiolink::IOAddress& prefix,
                           const uint8_t prefix_len) const {
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_PREFIX)
        .arg(prefix.toText()).arg(static_cast<int>(prefix_len));

    // Let's get all reservations that match subnet_id, address.
    const HostContainer6Index0& idx = hosts6_.get<0>();
    HostContainer6Index0Range r = make_pair(idx.lower_bound(prefix),
                                            idx.upper_bound(prefix));
    for (HostContainer6Index0::iterator resrv = r.first; resrv != r.second;
         ++resrv) {
        if (resrv->resrv_.getPrefixLen() == prefix_len) {
            LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
                      HOSTS_CFG_GET_ONE_PREFIX_HOST)
                .arg(prefix.toText())
                .arg(static_cast<int>(prefix_len))
                .arg(resrv->host_->toText());
            return (resrv->host_);
        }
    }

    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
              HOSTS_CFG_GET_ONE_PREFIX_NULL)
        .arg(prefix.toText())
        .arg(static_cast<int>(prefix_len));
    return (ReturnType());
}

330 331 332 333 334
template<typename Storage>
void
CfgHosts::getAllInternal6(const SubnetID& subnet_id,
                          const asiolink::IOAddress& address,
                          Storage& storage) const {
335 336 337
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6)
        .arg(subnet_id).arg(address.toText());

338 339 340 341 342 343 344 345
    // Must not specify address other than IPv6.
    if (!address.isV6()) {
        isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
                  " for a host, specified address was " << address);
    }

    // Let's get all reservations that match subnet_id, address.
    const HostContainer6Index1& idx = hosts6_.get<1>();
346 347
    HostContainer6Index1Range r = make_pair(idx.lower_bound(boost::make_tuple(subnet_id, address)),
                                            idx.upper_bound(boost::make_tuple(subnet_id, address)));
348 349 350 351 352 353

    // For each IPv6 reservation, add the host to the results list. Fortunately,
    // in all sane cases, there will be only one such host. (Each host can have
    // multiple addresses reserved, but for each (address, subnet_id) there should
    // be at most one host reserving it).
    for(HostContainer6Index1::iterator resrv = r.first; resrv != r.second; ++resrv) {
354 355 356 357
        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
                  HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST)
            .arg(subnet_id)
            .arg(address.toText())
358
            .arg(resrv->host_->toText());
359 360
        storage.push_back(resrv->host_);
    }
361 362 363 364 365 366

    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
              HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT)
        .arg(subnet_id)
        .arg(address.toText())
        .arg(storage.size());
367 368
}

369 370
HostPtr
CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
371 372 373 374
                          const Host::IdentifierType& identifier_type,
                          const uint8_t* identifier,
                          const size_t identifier_len) const {

375
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER)
376 377
        .arg(subnet6 ? "IPv6" : "IPv4")
        .arg(subnet_id)
378
        .arg(Host::getIdentifierAsText(identifier_type, identifier, identifier_len));
379

380
    // Get all hosts for a specified identifier. This may return multiple hosts
381 382 383
    // for different subnets, but the number of hosts returned should be low
    // because one host presumably doesn't show up in many subnets.
    HostCollection hosts;
384 385
    getAllInternal<HostCollection>(identifier_type, identifier, identifier_len,
                                   hosts);
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

    HostPtr host;
    // Iterate over the returned hosts and select those for which the
    // subnet id matches.
    for (HostCollection::const_iterator host_it = hosts.begin();
         host_it != hosts.end(); ++host_it) {
        // Check if this is IPv4 subnet or IPv6 subnet.
        SubnetID host_subnet_id = subnet6 ? (*host_it)->getIPv6SubnetID() :
            (*host_it)->getIPv4SubnetID();

        if (subnet_id == host_subnet_id) {
            // If this is the first occurrence of the host for this subnet,
            // remember it. But, if we find that this is second @c Host object
            // for the same client, it is a misconfiguration. Most likely,
            // the administrator has specified one reservation for a HW
            // address and another one for the DUID, which gives an ambiguous
            // result, and we don't know which reservation we should choose.
            // Therefore, throw an exception.
            if (!host) {
                host = *host_it;

            } else {
                isc_throw(DuplicateHost,  "more than one reservation found"
                          " for the host belonging to the subnet with id '"
410 411 412 413
                          << subnet_id << "' and using the identifier '"
                          << Host::getIdentifierAsText(identifier_type,
                                                       identifier,
                                                       identifier_len)
414 415 416 417
                          << "'");
            }
        }
    }
418

419
    if (host) {
420
        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
421
                  HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_HOST)
422
            .arg(subnet_id)
423 424
            .arg(Host::getIdentifierAsText(identifier_type, identifier,
                                           identifier_len))
425
            .arg(host->toText());
426 427 428

    } else {
        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
429
                  HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_NULL)
430
            .arg(subnet_id)
431 432 433
            .arg(Host::getIdentifierAsText(identifier_type, identifier,
                                           identifier_len));
    }
434

435 436 437 438 439
    return (host);
}

void
CfgHosts::add(const HostPtr& host) {
440 441 442
    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_ADD_HOST)
        .arg(host ? host->toText() : "(no-host)");

443 444 445 446 447
    // Sanity check that the host is non-null.
    if (!host) {
        isc_throw(BadValue, "specified host object must not be NULL when it"
                  " is added to the configuration");
    }
448

449 450 451
    // At least one subnet ID must be used
    if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED && 
        host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
452 453 454
        isc_throw(BadValue, "must not use both IPv4 and IPv6 subnet ids of"
                  " 0 when adding new host reservation");
    }
455

456
    add4(host);
457

458
    add6(host);
459 460 461 462
}

void
CfgHosts::add4(const HostPtr& host) {
463

464 465
    HWAddrPtr hwaddr = host->getHWAddress();
    DuidPtr duid = host->getDuid();
466 467

    // There should be at least one resource reserved: hostname, IPv4
468
    // address, siaddr, sname, file or IPv6 address or prefix.
469
    /// @todo: this check should be done in add(), not in add4()
470
    if (host->getHostname().empty() &&
471
        (host->getIPv4Reservation().isV4Zero()) &&
472 473 474 475
        !host->hasIPv6Reservation() &&
        host->getNextServer().isV4Zero() &&
        host->getServerHostname().empty() &&
        host->getBootFileName().empty() &&
476
        host->getCfgOption4()->empty() &&
477 478 479
        host->getCfgOption6()->empty() &&
        host->getClientClasses4().empty() &&
        host->getClientClasses6().empty()) {
480 481 482 483 484 485
        std::ostringstream s;
        if (hwaddr) {
            s << "for DUID: " << hwaddr->toText();
        } else if (duid) {
            s << "for HW address: " << duid->toText();
        }
486
        isc_throw(BadValue, "specified reservation " << s.str()
487
                  << " must include at least one resource, i.e. "
488 489
                  "hostname, IPv4 address, IPv6 address/prefix, "
                  "options");
490 491
    }

492
    // Check for duplicates for the specified IPv4 subnet.
493
    if (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) {
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
        if (hwaddr && !hwaddr->hwaddr_.empty() &&
            get4(host->getIPv4SubnetID(), Host::IDENT_HWADDR,
                 &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) {
            isc_throw(DuplicateHost, "failed to add new host using the HW"
                      << " address '" << hwaddr->toText(false)
                      << "' to the IPv4 subnet id '" << host->getIPv4SubnetID()
                      << "' as this host has already been added");
        }
        if (duid && !duid->getDuid().empty() &&
            get4(host->getIPv4SubnetID(), Host::IDENT_DUID,
                 &duid->getDuid()[0], duid->getDuid().size())) {
            isc_throw(DuplicateHost, "failed to add new host using the "
                      << "DUID '" << duid->toText()
                      << "' to the IPv4 subnet id '" << host->getIPv4SubnetID()
                      << "' as this host has already been added");
        }
510
    // Check for duplicates for the specified IPv6 subnet.
511
    } else if (host->getIPv6SubnetID() != SUBNET_ID_UNUSED) {
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
        if (duid && !duid->getDuid().empty() &&
            get6(host->getIPv6SubnetID(), Host::IDENT_DUID,
                 &duid->getDuid()[0], duid->getDuid().size())) {
            isc_throw(DuplicateHost, "failed to add new host using the "
                      << "DUID '" << duid->toText()
                      << "' to the IPv6 subnet id '" << host->getIPv6SubnetID()
                      << "' as this host has already been added");
        }
        if (hwaddr && !hwaddr->hwaddr_.empty() &&
            get6(host->getIPv6SubnetID(), Host::IDENT_HWADDR,
                 &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) {
            isc_throw(DuplicateHost, "failed to add new host using the HW"
                      << " address '" << hwaddr->toText(false)
                      << "' to the IPv6 subnet id '" << host->getIPv6SubnetID()
                      << "' as this host has already been added");
        }
528
    }
529

530 531
    // Check if the address is already reserved for the specified IPv4 subnet.
    if (!host->getIPv4Reservation().isV4Zero() &&
532
        (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) &&
533 534 535 536 537 538 539 540 541
        get4(host->getIPv4SubnetID(), host->getIPv4Reservation())) {
        isc_throw(ReservedAddress, "failed to add new host using the HW"
                  " address '" << (hwaddr ? hwaddr->toText(false) : "(null)")
                  << " and DUID '" << (duid ? duid->toText() : "(null)")
                  << "' to the IPv4 subnet id '" << host->getIPv4SubnetID()
                  << "' for the address " << host->getIPv4Reservation()
                  << ": There's already a reservation for this address");
    }

542 543
    // Check if the (identifier type, identifier) tuple is already used.
    const std::vector<uint8_t>& id = host->getIdentifier();
544
    if ((host->getIPv4SubnetID() != SUBNET_ID_UNUSED) && !id.empty()) {
545 546
        if (get4(host->getIPv4SubnetID(), host->getIdentifierType(), &id[0],
                 id.size())) {
547
            isc_throw(DuplicateHost, "failed to add duplicate IPv4 host using identifier: "
548 549 550 551
                      << Host::getIdentifierAsText(host->getIdentifierType(),
                                                   &id[0], id.size()));
        }
    }
552 553 554 555 556 557 558

    // This is a new instance - add it.
    hosts_.insert(host);
}

void
CfgHosts::add6(const HostPtr& host) {
559

560
    if (host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
561 562 563 564
        // This is IPv4-only host. No need to add it to v6 tables.
        return;
    }

565 566 567 568 569 570
    HWAddrPtr hwaddr = host->getHWAddress();
    DuidPtr duid = host->getDuid();

    // Get all reservations for this host.
    IPv6ResrvRange reservations = host->getIPv6Reservations();

571
    // Check if there are any IPv6 reservations.
572
    if (std::distance(reservations.first, reservations.second) == 0) {
573 574
        // If there aren't, we don't need to add this to hosts6_, which is used
        // for getting hosts by their IPv6 address reservations.
575 576 577
        return;
    }

578
    // Now for each reservation, insert corresponding (address, host) tuple.
579 580
    for (IPv6ResrvIterator it = reservations.first; it != reservations.second;
         ++it) {
581 582 583 584 585 586 587 588 589 590 591

        // If there's an entry for this (subnet-id, address), reject it.
        if (get6(host->getIPv6SubnetID(), it->second.getPrefix())) {
            isc_throw(DuplicateHost, "failed to add address reservation for "
                      << "host using the HW address '"
                      << (hwaddr ? hwaddr->toText(false) : "(null)")
                      << " and DUID '" << (duid ? duid->toText() : "(null)")
                      << "' to the IPv6 subnet id '" << host->getIPv6SubnetID()
                      << "' for address/prefix " << it->second.getPrefix()
                      << ": There's already reservation for this address/prefix");
        }
592 593
        hosts6_.insert(HostResrv6Tuple(it->second, host));
    }
594 595
}

596
bool
597
CfgHosts::del(const SubnetID& /*subnet_id*/, const asiolink::IOAddress& /*addr*/) {
598 599 600 601 602
    /// @todo: Implement host removal
    isc_throw(NotImplemented, "sorry, not implemented");
    return (false);
}

603 604 605 606 607 608 609 610 611 612 613 614
size_t
CfgHosts::delAll4(const SubnetID& subnet_id) {
    HostContainerIndex2& idx = hosts_.get<2>();
    size_t erased = idx.erase(subnet_id);

    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL_ALL_SUBNET4)
        .arg(erased)
        .arg(subnet_id);

    return (erased);
}

615
bool
616 617 618 619
CfgHosts::del4(const SubnetID& /*subnet_id*/,
               const Host::IdentifierType& /*identifier_type*/,
               const uint8_t* /*identifier_begin*/,
               const size_t /*identifier_len*/) {
620 621 622 623 624
    /// @todo: Implement host removal
    isc_throw(NotImplemented, "sorry, not implemented");
    return (false);
}

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
size_t
CfgHosts::delAll6(const SubnetID& subnet_id) {
    // Delete IPv6 reservations.
    HostContainer6Index2& idx6 = hosts6_.get<2>();
    size_t erased_addresses = idx6.erase(subnet_id);

    // Delete hosts.
    HostContainerIndex3& idx = hosts_.get<3>();
    size_t erased_hosts = idx.erase(subnet_id);

    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL_ALL_SUBNET6)
        .arg(erased_hosts)
        .arg(erased_addresses)
        .arg(subnet_id);

    return (erased_hosts);
}

643
bool
644 645 646 647
CfgHosts::del6(const SubnetID& /*subnet_id*/,
               const Host::IdentifierType& /*identifier_type*/,
               const uint8_t* /*identifier_begin*/,
               const size_t /*identifier_len*/) {
648 649 650 651 652
    /// @todo: Implement host removal
    isc_throw(NotImplemented, "sorry, not implemented");
    return (false);
}

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
ElementPtr
CfgHosts::toElement() const {
    uint16_t family = CfgMgr::instance().getFamily();
    if (family == AF_INET) {
        return (toElement4());
    } else if (family == AF_INET6) {
        return (toElement6());
    } else {
        isc_throw(ToElementError, "CfgHosts::toElement: unknown "
                  "address family: " << family);
    }
}

ElementPtr
CfgHosts::toElement4() const {
    CfgHostsList result;
    // Iterate using arbitrary the index 0
    const HostContainerIndex0& idx = hosts_.get<0>();
    for (HostContainerIndex0::const_iterator host = idx.begin();
         host != idx.end(); ++host) {
673 674 675 676 677

        // Convert host to element representation
        ElementPtr map = (*host)->toElement4();

        // Push it on the list
678 679 680 681 682 683 684 685 686 687 688 689 690
        SubnetID subnet_id = (*host)->getIPv4SubnetID();
        result.add(subnet_id, map);
    }
    return (result.externalize());
}

ElementPtr
CfgHosts::toElement6() const {
    CfgHostsList result;
    // Iterate using arbitrary the index 0
    const HostContainerIndex0& idx = hosts_.get<0>();
    for (HostContainerIndex0::const_iterator host = idx.begin();
         host != idx.end(); ++host) {
691 692 693 694 695

        // Convert host to Element representation
        ElementPtr map = (*host)->toElement6();

        // Push it on the list
696 697 698 699 700 701
        SubnetID subnet_id = (*host)->getIPv6SubnetID();
        result.add(subnet_id, map);
    }
    return (result.externalize());
}

702 703
} // end of namespace isc::dhcp
} // end of namespace isc