Kea 3.1.1
host_cache_impl.cc
Go to the documentation of this file.
1// Copyright (C) 2020-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <dhcp/duid.h>
10#include <dhcp/hwaddr.h>
11#include <host_cache.h>
12#include <host_cache_impl.h>
13
14#include <boost/foreach.hpp>
15
16using namespace std;
17using namespace isc::asiolink;
18using namespace isc::data;
19using namespace isc::dhcp;
20
21namespace isc {
22namespace host_cache {
23
26
29
30bool
32 if (!host || (host->getIPv6SubnetID() == SUBNET_ID_UNUSED)) {
33 return (true);
34 }
35 const IPv6ResrvRange& resrv = host->getIPv6Reservations();
36 if (std::distance(resrv.first, resrv.second) == 0) {
37 return (true);
38 }
39 // For each IPv6 reservation, insert (address, host) tuple.
40 BOOST_FOREACH(auto const& it, resrv) {
41 auto ret = cache6_.insert(HostResrv6Tuple(it.second, host));
42 // No reason to fail but check anyway.
43 if (!ret.second) {
44 return (false);
45 }
46 }
47 return (true);
48}
49
50void
52 if (!host || (host->getIPv6SubnetID() == SUBNET_ID_UNUSED)) {
53 return;
54 }
55 const IPv6ResrvRange& resrv = host->getIPv6Reservations();
56 if (std::distance(resrv.first, resrv.second) == 0) {
57 return;
58 }
59 // For each IPv6 reservation, remove it.
60 const SubnetID& subnet_id = host->getIPv6SubnetID();
62 cache6_.get<Resv6AddressIndexTag>();
64 cache6_.get<Resv6SubnetAddressIndexTag>();
65 BOOST_FOREACH(auto const& it, resrv) {
66 const IOAddress& prefix = it.second.getPrefix();
67 if (it.first == IPv6Resrv::TYPE_NA) {
69 make_pair(idx1.lower_bound(boost::make_tuple(subnet_id, prefix)),
70 idx1.upper_bound(boost::make_tuple(subnet_id, prefix)));
71 for (Resv6ContainerSubnetAddressIndex::iterator p = r.first;
72 p != r.second; ++p) {
73 if (p->host_ != host) {
74 // Should not happen
75 continue;
76 }
77 idx1.erase(p);
78 break;
79 }
80 } else if (it.first == IPv6Resrv::TYPE_PD) {
81 uint8_t prefix_len = it.second.getPrefixLen();
83 make_pair(idx0.lower_bound(prefix), idx0.upper_bound(prefix));
84 for (Resv6ContainerAddressIndex::iterator p = r.first;
85 p != r.second; ++p) {
86 if (p->resrv_.getPrefixLen() == prefix_len) {
87 if (p->host_ != host) {
88 // Should not happen
89 continue;
90 }
91 idx0.erase(p);
92 break;
93 }
94 }
95 }
96 }
97}
98
101 if (host) {
102 const HostContainerHashedIndex& idx = cache_.get<HostHashedIndexTag>();
103 HostContainerHashedIndex::iterator it = idx.find(host);
104 if (it == idx.end()) {
105 // Should not happen
106 // Remove related reservations unconditionally
107 removeResv6(host);
108 return (HostPtr());
109 }
110 cache_.relocate(cache_.end(), cache_.project<HostSequencedIndexTag>(it));
111 }
112 return (host);
113}
114
117 const dhcp::Host::IdentifierType& identifier_type,
118 const uint8_t* identifier_begin,
119 const size_t identifier_len) {
120 HostContainerSequencedIndex::iterator host =
121 getHostInternal(subnet_id, false, identifier_type,
122 identifier_begin, identifier_len);
123 if (host == cache_.end()) {
124 return (ConstHostPtr());
125 }
126 cache_.relocate(cache_.end(), host);
127 return (*host);
128}
129
132 const asiolink::IOAddress& address) {
133 HostContainerSequencedIndex::iterator host =
134 getHostInternal4(subnet_id, address);
135 if (host == cache_.end()) {
136 return (ConstHostPtr());
137 }
138 cache_.relocate(cache_.end(), host);
139 return (*host);
140}
141
144 const dhcp::Host::IdentifierType& identifier_type,
145 const uint8_t* identifier_begin,
146 const size_t identifier_len) {
147 HostContainerSequencedIndex::iterator host =
148 getHostInternal(subnet_id, true, identifier_type,
149 identifier_begin, identifier_len);
150 if (host == cache_.end()) {
151 return (ConstHostPtr());
152 }
153 cache_.relocate(cache_.end(), host);
154 return (*host);
155}
156
159 const uint8_t prefix_len) {
160 HostPtr host = getHostInternal6(prefix, prefix_len);
161 return (relocate(host));
162}
163
166 const asiolink::IOAddress& address) {
167 HostPtr host = getHostInternal6(subnet_id, address);
168 return (relocate(host));
169}
170
171HostContainerSequencedIndex::iterator
172HostCacheImpl::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
173 const Host::IdentifierType& identifier_type,
174 const uint8_t* identifier,
175 const size_t identifier_len) {
176 // Use the identifier and identifier type as a composite key.
178 cache_.get<HostIdentifierIndexTag>();
179 boost::tuple<const vector<uint8_t>, const Host::IdentifierType> t =
180 boost::make_tuple(vector<uint8_t>(identifier,
181 identifier + identifier_len),
182 identifier_type);
183
184 for (HostContainerIdentifierIndex::iterator host = idx.lower_bound(t);
185 host != idx.upper_bound(t); ++host) {
186 // Check if this is IPv4 subnet or IPv6 subnet.
187 SubnetID host_subnet_id = subnet6 ? (*host)->getIPv6SubnetID() :
188 (*host)->getIPv4SubnetID();
189
190 if (subnet_id == host_subnet_id) {
191 return (cache_.project<HostSequencedIndexTag>(host));
192 }
193 }
194 return (cache_.end());
195}
196
197HostContainerSequencedIndex::iterator
199 const IOAddress& address) {
200 // Search for the Host using the reserved IPv4 address as a key.
201 const HostContainerAddress4Index& idx =
202 cache_.get<HostAddress4IndexTag>();
203 HostContainerAddress4IndexRange r = idx.equal_range(address);
204 for (HostContainerAddress4Index::iterator host = r.first;
205 host != r.second; ++host) {
206 // Find matching subnet ID.
207 if ((*host)->getIPv4SubnetID() == subnet_id) {
208 return (cache_.project<HostSequencedIndexTag>(host));
209 }
210 }
211 return (cache_.end());
212}
213
216 const uint8_t prefix_len) {
217 // Let's get all reservations for the prefix
218 const Resv6ContainerAddressIndex& idx =
219 cache6_.get<Resv6AddressIndexTag>();
220 Resv6ContainerAddressIndexRange r = make_pair(idx.lower_bound(prefix),
221 idx.upper_bound(prefix));
222 BOOST_FOREACH(auto const& resrv, r) {
223 if (resrv.resrv_.getPrefixLen() == prefix_len) {
224 return (resrv.host_);
225 }
226 }
227 return (HostPtr());
228}
229
232 const asiolink::IOAddress& address) {
233 // Search for the reservation that match subnet_id, address.
235 cache6_.get<Resv6SubnetAddressIndexTag>();
236 // The index is ordered unique so the range size can be only 0 or 1.
238 make_pair(idx.lower_bound(boost::make_tuple(subnet_id, address)),
239 idx.upper_bound(boost::make_tuple(subnet_id, address)));
240
241 BOOST_FOREACH(auto const& resrv, r) {
242 return (resrv.host_);
243 }
244 return (HostPtr());
245}
246
247size_t
248HostCacheImpl::insert(const ConstHostPtr& host, bool overwrite) {
249 HWAddrPtr hwaddr = host->getHWAddress();
250 DuidPtr duid = host->getDuid();
251 const vector<uint8_t>& id = host->getIdentifier();
252 size_t conflicts = 0;
253
254 // Check for duplicates for the specified IPv4 subnet.
255 if (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) {
256 // Identifier
257 if (!id.empty()) {
258 HostContainerSequencedIndex::iterator dup;
259 while ((dup = getHostInternal(host->getIPv4SubnetID(), false,
260 host->getIdentifierType(),
261 &id[0], id.size()))
262 != cache_.end()) {
263 ++conflicts;
264 if (!overwrite) {
265 return (conflicts);
266 } else {
267 removeResv6(*dup);
268 cache_.erase(dup);
269 }
270 }
271 }
272 // Reserved IPv4 address.
273 const asiolink::IOAddress& address = host->getIPv4Reservation();
274 if (!address.isV4Zero()) {
275 HostContainerSequencedIndex::iterator dup;
276 while ((dup = getHostInternal4(host->getIPv4SubnetID(),
277 address)) != cache_.end()) {
278 ++conflicts;
279 if (!overwrite) {
280 return (conflicts);
281 } else {
282 removeResv6(*dup);
283 cache_.erase(dup);
284 }
285 }
286 }
287 }
288
289 // Check for duplicates for the specified IPv6 subnet.
290 if (host->getIPv6SubnetID() != SUBNET_ID_UNUSED) {
291 // Identifier.
292 if (!id.empty()) {
293 HostContainerSequencedIndex::iterator dup;
294 while ((dup = getHostInternal(host->getIPv6SubnetID(), true,
295 host->getIdentifierType(),
296 &id[0], id.size()))
297 != cache_.end()) {
298 ++conflicts;
299 if (!overwrite) {
300 return (conflicts);
301 } else {
302 removeResv6(*dup);
303 cache_.erase(dup);
304 }
305 }
306 }
307 // Reservations.
308 const IPv6ResrvRange& resrv = host->getIPv6Reservations();
309 BOOST_FOREACH(auto const& it, resrv) {
310 if (it.first == IPv6Resrv::TYPE_NA) {
311 HostPtr dup;
312 while ((dup = getHostInternal6(host->getIPv6SubnetID(),
313 it.second.getPrefix()))) {
314 ++conflicts;
315 if (!overwrite) {
316 return (conflicts);
317 } else {
318 remove(dup);
319 }
320 }
321 } else if (it.first == IPv6Resrv::TYPE_PD) {
322 HostPtr dup;
323 while ((dup = getHostInternal6(it.second.getPrefix(),
324 it.second.getPrefixLen()))) {
325 ++conflicts;
326 if (!overwrite) {
327 return (conflicts);
328 } else {
329 remove(dup);
330 }
331 }
332 }
333 }
334 }
335
336 // This is a new instance - add a copy of it.
337 HostPtr host_copy(new Host(*host));
338 auto ret = cache_.push_back(host_copy);
339 if (!ret.second) {
340 // isc_throw(Unexpected, "can't insert");
341 ++conflicts;
342 return (conflicts);
343 } else if (!insertResv6(host_copy)) {
344 // isc_throw(Unexpected, "can't insert IPv6 reservation");
345 ++conflicts;
346 remove(host_copy);
347 return (conflicts);
348 }
349
350 // Adjust cache size
351 if (maximum_ && (cache_.size() > maximum_)) {
352 flush(cache_.size() - maximum_);
353 }
354
355 return (conflicts);
356}
357
358void
360 if (!host) {
361 return;
362 }
363
364 // At least one subnet ID must be used.
365 if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED &&
366 host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
367 return;
368 }
369
373 bool found(false);
374 for (HostPtr const& h : cache_) {
375 if (h->getHostId() == host->getHostId()) {
376 found = true;
377 break;
378 }
379 }
380 if (!found) {
381 isc_throw(NotFound, "host ID " << host->getHostId() << " not found");
382 }
383
384 // We're sure the host exists, so an insert with overwrite is equivalent to
385 // an update.
386 insert(host, /* overwrite = */ true);
387}
388
389bool
391 return (insert(host, false) == 0);
392}
393
394string
396 const asiolink::IOAddress& addr) {
397 string txt;
398 // Search for the Host using the reserved IPv4 address as a key.
400 HostContainerAddress4IndexRange r = idx.equal_range(addr);
401 for (HostContainerAddress4Index::iterator host = r.first;
402 host != r.second; ++host) {
403 // Find matching subnet ID.
404 if ((*host)->getIPv4SubnetID() == subnet_id) {
405 txt = (*host)->toText();
406 removeResv6(*host);
407 idx.erase(host);
408 return (txt);
409 }
410 }
411 return (txt);
412}
413
414string
416 const asiolink::IOAddress& addr) {
417 string txt;
418 // Search for the reservation that match subnet_id, address.
420 cache6_.get<Resv6SubnetAddressIndexTag>();
421 Resv6ContainerSubnetAddressIndex::iterator resrv =
422 idx.find(boost::make_tuple(subnet_id, addr));
423 if (resrv == idx.end()) {
424 return (txt);
425 }
426 txt = resrv->host_->toText();
427 remove(resrv->host_);
428 return (txt);
429}
430
431string
433 const dhcp::Host::IdentifierType& identifier_type,
434 const uint8_t* identifier_begin,
435 const size_t identifier_len) {
436 string txt;
437 // Use the identifier and identifier type as a composite key.
439 boost::tuple<const vector<uint8_t>, const Host::IdentifierType> t =
440 boost::make_tuple(vector<uint8_t>(identifier_begin,
441 identifier_begin + identifier_len),
442 identifier_type);
443 for (HostContainerIdentifierIndex::iterator host = idx.lower_bound(t);
444 host != idx.upper_bound(t); ++host) {
445 SubnetID host_subnet_id = (*host)->getIPv4SubnetID();
446 if (subnet_id == host_subnet_id) {
447 txt = (*host)->toText();
448 removeResv6(*host);
449 idx.erase(host);
450 return (txt);
451 }
452 }
453 return (txt);
454}
455
456string
458 const dhcp::Host::IdentifierType& identifier_type,
459 const uint8_t* identifier_begin,
460 const size_t identifier_len) {
461 string txt;
462 // Use the identifier and identifier type as a composite key.
464 cache_.get<HostIdentifierIndexTag>();
465 boost::tuple<const vector<uint8_t>, const Host::IdentifierType> t =
466 boost::make_tuple(vector<uint8_t>(identifier_begin,
467 identifier_begin + identifier_len),
468 identifier_type);
469 for (HostContainerIdentifierIndex::iterator host = idx.lower_bound(t);
470 host != idx.upper_bound(t); ++host) {
471 SubnetID host_subnet_id = (*host)->getIPv6SubnetID();
472 if (subnet_id == host_subnet_id) {
473 txt = (*host)->toText();
474 removeResv6(*host);
475 idx.erase(host);
476 return (txt);
477 }
478 }
479 return (txt);
480}
481
482bool
484 // Using the hashed index
485 HostContainerHashedIndex& idx = cache_.get<HostHashedIndexTag>();
486 HostContainerHashedIndex::iterator host_it = idx.find(host);
487 // Remove related reservations unconditionally
488 removeResv6(host);
489 if (host_it == idx.end()) {
490 return (false);
491 }
492 idx.erase(host_it);
493 return (true);
494}
495
496void
497HostCacheImpl::flush(size_t count) {
498 // Flush all entries is handled by clear.
499 if (count == 0) {
500 return;
501 }
503 HostContainerSequencedIndex::iterator host = idx.begin();
504 while ((host != idx.end()) && (count > 0)) {
505 removeResv6(*host);
506 idx.erase(host);
507 host = idx.begin();
508 --count;
509 }
510}
511
515 // Iterate using sequenced index to keep the order
516 const HostContainerSequencedIndex& idx =
517 cache_.get<HostSequencedIndexTag>();
518 for (auto const& host : idx) {
519 // Convert host to element representation
521 // Push it on the list
522 result->add(map);
523 }
524 return (result);
525}
526
529 const uint8_t* identifier_begin,
530 const size_t identifier_len) const {
531 // Use the identifier and identifier type as a composite key.
533 cache_.get<HostIdentifierIndexTag>();
534 boost::tuple<const vector<uint8_t>, const Host::IdentifierType> t =
535 boost::make_tuple(vector<uint8_t>(identifier_begin,
536 identifier_begin + identifier_len),
537 identifier_type);
538 ConstHostCollection result;
539 for (HostContainerIdentifierIndex::iterator host = idx.lower_bound(t);
540 host != idx.upper_bound(t); ++host) {
541 result.push_back(*host);
542 }
543 return (result);
544}
545
546} // end of namespace isc::host_cache
547} // end of namespace isc
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
A generic exception that is thrown when an object can not be found.
Represents a device with IPv4 and/or IPv6 reservations.
Definition host.h:327
IdentifierType
Type of the host identifier.
Definition host.h:337
void flush(size_t count)
Flush entries.
dhcp::ConstHostPtr get6(const dhcp::SubnetID &subnet_id, const dhcp::Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Returns a host connected to the IPv6 subnet.
void removeResv6(const dhcp::HostPtr &host)
Remove IPv6 reservations.
HostContainerSequencedIndex::iterator getHostInternal4(const dhcp::SubnetID &subnet_id, const asiolink::IOAddress &address)
Returns a host connected to the IPv4 subnet and having a reservation for a specified IPv4 address.
dhcp::HostPtr relocate(dhcp::HostPtr host)
Relocate an entry to the end of sequenced index.
size_t maximum_
Maximum number of elements (0 means unbound).
virtual dhcp::ConstHostCollection get(const dhcp::Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
virtual ~HostCacheImpl()
Destructor.
void update(isc::dhcp::ConstHostPtr const &host)
Implements isc::dhcp::BaseHostDataSource::update() for HostCacheImpl.
dhcp::HostPtr getHostInternal6(const asiolink::IOAddress &prefix, const uint8_t prefix_len)
Returns a host using the specified IPv6 prefix.
bool add(const dhcp::HostPtr &host)
Adds a new host to the collection.
HostContainerSequencedIndex::iterator getHostInternal(const dhcp::SubnetID &subnet_id, const bool subnet6, const dhcp::Host::IdentifierType &identifier_type, const uint8_t *identifier, const size_t identifier_len)
Returns Host object connected to a subnet.
std::string del6(const dhcp::SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete a host by (subnet-id6, address)
std::string del4(const dhcp::SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete a host by (subnet-id4, address)
data::ElementPtr toElement() const
Unparse cache content.
dhcp::ConstHostPtr get4(const dhcp::SubnetID &subnet_id, const dhcp::Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Returns a host connected to the IPv4 subnet.
bool insertResv6(const dhcp::HostPtr &host)
Insert IPv6 reservations.
bool remove(const dhcp::HostPtr &host)
Remove a host from the cache.
size_t insert(const dhcp::ConstHostPtr &host, bool overwrite)
Insert a host into the cache.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition host.h:837
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition host.h:843
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition host.h:273
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition host.h:840
HostContainer::index< HostAddress4IndexTag >::type HostContainerAddress4Index
Reserved Ipv4 address index type in the HostContainer.
Definition container.h:122
std::pair< Resv6ContainerSubnetAddressIndex::iterator, Resv6ContainerSubnetAddressIndex::iterator > Resv6ContainerSubnetAddressIndexRange
Results range returned using the Resv6ContainerSubnetAddressIndex.
Definition container.h:237
HostContainer::index< HostIdentifierIndexTag >::type HostContainerIdentifierIndex
Identifier index type in the HostContainer.
Definition container.h:112
std::pair< HostContainerAddress4Index::iterator, HostContainerAddress4Index::iterator > HostContainerAddress4IndexRange
Results range returned using the HostContainerAddress4Index.
Definition container.h:126
HostContainer::index< HostHashedIndexTag >::type HostContainerHashedIndex
Hashed index type in the HostContainer.
Definition container.h:106
std::pair< Resv6ContainerAddressIndex::iterator, Resv6ContainerAddressIndex::iterator > Resv6ContainerAddressIndexRange
Results range returned using the Resv6ContainerAddressIndex.
Definition container.h:226
ElementPtr toElement(const ConstHostPtr &host)
Unparse a host cache entry.
Definition entry.cc:25
Resv6Container::index< Resv6AddressIndexTag >::type Resv6ContainerAddressIndex
First index type in the Resv6Container.
Definition container.h:222
HostContainer::index< HostSequencedIndexTag >::type HostContainerSequencedIndex
Sequenced index type in the HostContainer.
Definition container.h:101
Resv6Container::index< Resv6SubnetAddressIndexTag >::type Resv6ContainerSubnetAddressIndex
Second index type in the Resv6Container.
Definition container.h:233
Defines the logger used by the top-level component of kea-lfc.
Tag for the index for searching by reserved IPv4 address.
Definition container.h:34
Tag for the hashed index.
Definition container.h:28
Tag for the index for searching by identifier.
Definition container.h:31
Defines one entry for the Host Container for v6 hosts.
Definition container.h:134
Tag for the sequenced index.
Definition container.h:25
Tag for the index for searching by address.
Definition container.h:161
Tag for the index for searching by subnet and address.
Definition container.h:164