Kea 3.1.1
bulk_lease_query6.cc
Go to the documentation of this file.
1// Copyright (C) 2022-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/dhcp6.h>
10#include <dhcp/libdhcp++.h>
11#include <dhcp/pkt6.h>
12#include <dhcp/option_custom.h>
13#include <dhcp/option_int.h>
15#include <dhcp/option6_iaaddr.h>
17#include <dhcpsrv/cfgmgr.h>
19#include <bulk_lease_query6.h>
21#include <lease_query_impl6.h>
22#include <lease_query_log.h>
23#include <blq_service.h>
24#include <stats/stats_mgr.h>
25#include <util/encode/encode.h>
26
27#include <boost/shared_ptr.hpp>
28#include <boost/pointer_cast.hpp>
29
30using namespace isc;
31using namespace isc::asiolink;
32using namespace isc::data;
33using namespace isc::dhcp;
34using namespace isc::lease_query;
35using namespace isc::log;
36using namespace isc::stats;
37
38std::string
40 Pkt6Ptr pkt = boost::dynamic_pointer_cast<Pkt6>(msg->getPkt());
41 if (!pkt) {
42 return ("<none>");
43 }
45}
46
47void
49 // Take the page size from the MT LQ listener manager.
50 auto service = BulkLeaseQueryService::instance();
51 if (service) {
52 page_size_ = service->getMaxLeasePerFetch();
53 }
54
55 query6_ = boost::dynamic_pointer_cast<Pkt6>(query_->getQuery());
56 if (!query6_) {
57 // Shouldn't happen
58 isc_throw(BadValue, "BulkLeaseQuery6 has no DHCPv6 query");
59 }
60
61 // The query must have a client id option.
62 DuidPtr req_clientid = query6_->getClientId();
63 if (!req_clientid) {
64 isc_throw(BadValue, "DHCPV6_LEASEQUERY must supply a D6O_CLIENTID");
65 }
66
67 // If the query has a server id option it should be for us.
69
70 // Get the lease query option.
71 lq_option_ = boost::dynamic_pointer_cast<OptionCustom>
72 (query6_->getOption(D6O_LQ_QUERY));
73 if (!lq_option_) {
74 isc_throw(BadValue, "DHCPV6_LEASEQUERY must supply a D6O_LQ_QUERY option");
75 }
76
77 // Extract the type and link fields.
78 try {
79 query_type_ = lq_option_->readInteger<uint8_t>(0);
80 link_addr_ = lq_option_->readAddress(1);
81 } catch (const std::exception& ex) {
82 // unpack() should catch this?
83 isc_throw(BadValue, "error reading query field(s): " << ex.what());
84 }
85
86 // Based on the query type, extract the requisite options.
88 OptionPtr opt;
89 switch (query_type_) {
91 opt = lq_option_->getOption(D6O_IAADDR);
92 query_iaaddr_ = boost::dynamic_pointer_cast<Option6IAAddr>(opt);
93 if (!query_iaaddr_) {
95 "missing D6O_IAADDR");
96 sendReply(status);
97 return;
98 }
99 break;
101 opt = lq_option_->getOption(D6O_CLIENTID);
102 if (!opt) {
104 "missing D6O_CLIENTID");
105 sendReply(status);
106 return;
107 }
108 try {
109 query_client_id_.reset(new DUID(opt->getData()));
110 } catch (const std::exception& ex) {
112 "malformed D6O_CLIENTID");
113 sendReply(status);
114 return;
115 }
116 break;
118 opt = lq_option_->getOption(D6O_RELAY_ID);
119 if (!opt) {
121 "missing D6O_RELAY_ID");
122 sendReply(status);
123 return;
124 }
125 try {
126 query_relay_id_.reset(new DUID(opt->getData()));
127 } catch (const std::exception& ex) {
129 "malformed D6O_RELAY_ID");
130 sendReply(status);
131 return;
132 }
133 break;
136 if (!query_remote_id_) {
138 "missing D6O_REMOTE_ID");
139 sendReply(status);
140 return;
141 } else if (query_remote_id_->getData().empty()) {
143 "empty D6O_REMOTE_ID");
144 sendReply(status);
145 return;
146 }
147 break;
149 if (link_addr_.isV6Zero()) {
151 "undefined link address");
152 sendReply(status);
153 return;
154 }
155 break;
156 default:
158 "unknown query-type");
159 sendReply(status);
160 return;
161 }
162}
163
164void
166 if (started_) {
167 isc_throw(InvalidOperation, "BulkLeaseQuery6 already in progress");
168 }
169 started_ = true;
170
171 switch (query_type_) {
172 case LQ6QT_BY_ADDRESS:
174 return;
177 return;
180 return;
183 return;
186 return;
187 default:
188 isc_throw(InvalidOperation, "unknown query-type");
189 }
190}
191
192void
194 if (!query_iaaddr_) {
195 isc_throw(InvalidOperation, "no query ip address");
196 }
197 Lease6Collection leases;
199
200 const LeaseQueryImpl6& impl6 = dynamic_cast<const LeaseQueryImpl6&>(LeaseQueryImplFactory::getImpl());
202 leases, impl6.getPrefixLengthList());
203 if (leases.empty()) {
204 // Error case.
205 sendReply(status);
206 return;
207 }
208 if (leases.size() > 1) {
209 isc_throw(Unexpected, "more than one lease for an address");
210 }
211
212 // Construct and send the reply message.
214 // Add the client option.
215 reply->addOption(LeaseQueryImpl6::makeClientOption(leases.front()));
216 // Add the status.
217 reply->addOption(status);
218 // Send the reply.
219 send(reply);
220
221 // Construct and send the done message.
223 send(done);
224
225 // Done.
226 setDone();
227}
228
229void
231 if (!query_client_id_) {
232 isc_throw(InvalidOperation, "no query client id");
233 }
234 Lease6Collection leases;
238 leases);
239
240 if (leases.empty()) {
241 // Error case.
242 sendReply(status);
243 return;
244 }
245
246 bool first = true;
247 for (auto const& lease : leases) {
248 if (first) {
249 first = false;
250 // Create and send the reply message.
252 // Add the client option.
253 reply->addOption(LeaseQueryImpl6::makeClientOption(lease));
254 // Add the status.
255 reply->addOption(status);
256 // Send the reply.
257 send(reply);
258 continue;
259 }
260 // Create and send the response.
262 // Add the client option.
263 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
264 // Send the response.
265 send(response);
266 }
267
268 // Construct and send the done message.
270 send(done);
271
272 // Done.
273 setDone();
274}
275
276void
278 if (!query_relay_id_) {
279 isc_throw(InvalidOperation, "no query relay id");
280 }
282 if (!LeaseMgrFactory::instance().getExtendedInfoTablesEnabled()) {
284 "extended info tables are disabled");
285 sendReply(status);
286 return;
287 }
288
289 Lease6Collection leases;
294 links_,
295 leases);
296 if (leases.empty()) {
297 // Error case.
298 sendReply(status);
299 return;
300 }
301
302 bool first = true;
303 for (auto const& lease : leases) {
304 if (first) {
305 first = false;
306 // Create and send the reply message.
308 // Add the client option.
309 reply->addOption(LeaseQueryImpl6::makeClientOption(lease));
310 // Add the status.
311 reply->addOption(status);
312 // Send the reply.
313 send(reply);
314 continue;
315 }
316 // Create and send the response.
318 // Add the client option.
319 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
320 // Send the response.
321 send(response);
322 }
323
325 boost::static_pointer_cast<BulkLeaseQuery6>(shared_from_this())));
326}
327
328void
330 if (!query_relay_id_) {
331 isc_throw(InvalidOperation, "no query relay id");
332 }
333 Lease6Collection leases;
334 const IOAddress position = start_addr_;
338 links_,
339 leases);
340 if (start_addr_ == position) {
341 // Construct and send the done message.
343 send(done);
344
345 // Done.
346 setDone();
347
348 return;
349 }
350
351 for (auto const& lease : leases) {
352 // Create and send the response
354 // Add the client option.
355 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
356 // Send the response.
357 send(response);
358 }
359
361 boost::static_pointer_cast<BulkLeaseQuery6>(shared_from_this())));
362}
363
364void
366 if (!query_remote_id_) {
367 isc_throw(InvalidOperation, "no query remote id");
368 }
370 if (!LeaseMgrFactory::instance().getExtendedInfoTablesEnabled()) {
372 "extended info tables are disabled");
373 sendReply(status);
374 return;
375 }
376
377 Lease6Collection leases;
382 links_,
383 leases);
384 if (leases.empty()) {
385 // Error case.
386 sendReply(status);
387 return;
388 }
389
390 bool first = true;
391 for (auto const& lease : leases) {
392 if (first) {
393 first = false;
394 // Create and send the reply message.
396 // Add the client option.
397 reply->addOption(LeaseQueryImpl6::makeClientOption(lease));
398 // Add the status.
399 reply->addOption(status);
400 // Send the reply.
401 send(reply);
402 continue;
403 }
404 // Create and send the response.
406 // Add the client option.
407 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
408 // Send the response.
409 send(response);
410 }
411
413 boost::static_pointer_cast<BulkLeaseQuery6>(shared_from_this())));
414}
415
416
417void
419 if (!query_remote_id_) {
420 isc_throw(InvalidOperation, "no query remote id");
421 }
422 Lease6Collection leases;
423 const IOAddress position = start_addr_;
427 links_,
428 leases);
429 if (start_addr_ == position) {
430 // Construct and send the done message.
432 send(done);
433
434 // Done.
435 setDone();
436
437 return;
438 }
439
440 for (auto const& lease : leases) {
441 // Create and send the response
443 // Add the client option.
444 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
445 // Send the response.
446 send(response);
447 }
448
450 boost::static_pointer_cast<BulkLeaseQuery6>(shared_from_this())));
451}
452
453void
455 // In fact by-link-address does not use extended info tables,
456 // more it can be supported by SQL lease backends.
458 if (!LeaseMgrFactory::instance().getExtendedInfoTablesEnabled()) {
460 "extended info tables are disabled");
461 sendReply(status);
462 return;
463 }
464
465 Lease6Collection leases;
469 links_,
470 leases);
471 if (leases.empty()) {
472 // Error case.
473 sendReply(status);
474 return;
475 }
476
477 bool first = true;
478 for (auto const& lease : leases) {
479 if (first) {
480 first = false;
481 // Create and send the reply message.
483 // Add the client option.
484 reply->addOption(LeaseQueryImpl6::makeClientOption(lease));
485 // Add the status.
486 reply->addOption(status);
487 // Send the reply.
488 send(reply);
489 continue;
490 }
491 // Create and send the response.
493 // Add the client option.
494 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
495 // Send the response.
496 send(response);
497 }
498
500 boost::static_pointer_cast<BulkLeaseQuery6>(shared_from_this())));
501}
502
503void
505 Lease6Collection leases;
506 const IOAddress position = start_addr_;
509 links_,
510 leases);
511 if (start_addr_ == position) {
512 // Construct and send the done message.
514 send(done);
515
516 // Done.
517 setDone();
518
519 return;
520 }
521
522 for (auto const& lease : leases) {
523 // Create and send the response
525 // Add the client option.
526 response->addOption(LeaseQueryImpl6::makeClientOption(lease));
527 // Send the response.
528 send(response);
529 }
530
532 boost::static_pointer_cast<BulkLeaseQuery6>(shared_from_this())));
533}
534
535void
538 BlqResponsePtr resp(new BlqResponse(response));
539 if (!push_to_send_(resp)) {
540 isc_throw(QueryTerminated, "connection closed");
541 }
542}
543
544void
546 // Create and send reply message.
548 // Add the status.
549 reply->addOption(status);
550 // Send the reply.
551 send(reply);
552
553 // It is final.
554 setDone();
555}
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown when an unexpected error condition occurs.
Holds DUID (DHCPv6 Unique Identifier)
Definition duid.h:142
static TrackingLeaseMgr & instance()
Return current lease manager.
Holds a bulk lease query response packet.
Definition blq_msg.h:124
asiolink::IOAddress start_addr_
The start address (for paged processing).
dhcp::SubnetIDSet links_
The links the link address (when not ::) belongs to.
void bulkQueryByRemoteIdNext()
Subsequent processing of a by remote id bulk query.
void bulkQueryByRelayIdNext()
Subsequent processing of a by relay id bulk query.
static void doBulkQueryByLinkAddressNext(BulkLeaseQuery6Ptr ptr)
Class/static subsequent processing of a by link address bulk query.
static std::string leaseQueryLabel(const BlqMsgPtr &msg)
Convenience method for generating per packet logging info.
static void doBulkQueryByRemoteIdNext(BulkLeaseQuery6Ptr ptr)
Class/static subsequent processing of a by remote id bulk query.
void bulkQueryByRemoteId()
Start processing of a by remote id bulk query.
void bulkQueryByRelayId()
Start processing of a by relay id bulk query.
virtual void send(dhcp::Pkt6Ptr response) const
Send a response.
dhcp::OptionPtr query_remote_id_
The remote id (for a by remote id bulk query).
dhcp::OptionCustomPtr lq_option_
The lease query option.
virtual void init()
Initialization.
virtual void sendReply(dhcp::OptionPtr status)
Send a final reply.
void bulkQueryByClientId()
Start processing of a by client id bulk query.
dhcp::DuidPtr query_client_id_
The query client id (for a by client id bulk query).
void bulkQueryByIpAddress()
Start processing of a by ip address bulk query.
void bulkQueryByLinkAddress()
Start processing of a by link address bulk query.
dhcp::Option6IAAddrPtr query_iaaddr_
The query ip address (for a by ip address bulk query).
dhcp::DuidPtr query_relay_id_
The query relay id (for a by relay id bulk query).
virtual void start()
Start processing.
dhcp::Pkt6Ptr query6_
The DHCPv6 query.
void bulkQueryByLinkAddressNext()
Subsequent processing of a by link address bulk query.
asiolink::IOAddress link_addr_
The link address.
size_t page_size_
The page size (for paged processing, taken from the MT Lease query manager or defaults to 10).
static void doBulkQueryByRelayIdNext(BulkLeaseQuery6Ptr ptr)
Class/static subsequent processing of a by relay id bulk query.
static BulkLeaseQueryServicePtr instance()
Returns a pointer to the sole instance of the BulkLeaseQueryService, can return null.
uint8_t query_type_
The query type.
virtual void setDone()
Set the done flag.
BlqPostCb post_
The post callback.
bool started_
The processing is in progress.
BlqPushToSendCb push_to_send_
The pushToSend callback.
Provides configuration and control flow for processing queries.
static void testServerId(const dhcp::Pkt6Ptr &query)
Validates the server-id of a received DHCPV6_LEASEQUERY.
static void queryByRelayIdNext(const dhcp::DuidPtr &relay_id, asiolink::IOAddress &start_addr, const size_t page_size, const dhcp::SubnetIDSet &links, dhcp::Lease6Collection &leases)
Subsequent query for active leases matching a relay id (i.e.
static dhcp::Pkt6Ptr initDone(const dhcp::Pkt6Ptr &query)
Creates the final query done response.
static dhcp::Option6StatusCodePtr queryByRemoteIdStart(const dhcp::OptionBuffer &remote_id, asiolink::IOAddress &start_addr, const size_t page_size, const asiolink::IOAddress &link_addr, dhcp::SubnetIDSet &links, dhcp::Lease6Collection &leases)
Initial query for active leases matching a remote id.
static dhcp::Option6StatusCodePtr queryByRelayIdStart(const dhcp::DuidPtr &relay_id, asiolink::IOAddress &start_addr, const size_t page_size, const asiolink::IOAddress &link_addr, dhcp::SubnetIDSet &links, dhcp::Lease6Collection &leases)
Initial query for active leases matching a relay id (i.e.
static dhcp::Option6StatusCodePtr makeStatusOption(const DHCPv6StatusCode &status_code, const std::string message="")
Constructs a D6O_STATUS_CODE option.
static dhcp::Option6StatusCodePtr queryByLinkStart(asiolink::IOAddress &start_addr, const size_t page_size, const asiolink::IOAddress &link_addr, dhcp::SubnetIDSet &links, dhcp::Lease6Collection &leases)
Initial query for active leases of a given link.
static dhcp::OptionPtr makeClientOption(dhcp::Lease6Collection &leases)
Constructs a client option based on a collection of leases.
const PrefixLengthList & getPrefixLengthList() const
Fetch the prefix length list.
static std::string leaseQueryLabel(const dhcp::Pkt6Ptr &packet)
Convenience method for generating per packet logging info.
static dhcp::Option6StatusCodePtr queryByIpAddress(const asiolink::IOAddress &iaaddr, dhcp::Lease6Collection &leases, const PrefixLengthList &prefix_lengths=PrefixLengthList())
Queries for an active lease matching an ip address.
static void queryByLinkNext(asiolink::IOAddress &start_addr, const size_t page_size, dhcp::SubnetIDSet &links, dhcp::Lease6Collection &leases)
Subsequent query for active leases of a given link.
static dhcp::Pkt6Ptr initReply(const dhcp::Pkt6Ptr &query)
Creates the initial query reply.
static dhcp::Option6StatusCodePtr queryByClientId(const dhcp::DuidPtr &client_id, const asiolink::IOAddress &link_addr, dhcp::Lease6Collection &leases)
Queries for active leases matching a client id (i.e.
static void queryByRemoteIdNext(const dhcp::OptionBuffer &remote_id, asiolink::IOAddress &start_addr, const size_t page_size, const dhcp::SubnetIDSet &links, dhcp::Lease6Collection &leases)
Subsequent query for active leases matching a remote id.
static dhcp::Pkt6Ptr initData(const dhcp::Pkt6Ptr &query)
Creates the query data response.
static const LeaseQueryImpl & getImpl()
Fetch the LeaseQueryImpl singleton.
Thrown on hook termination.
#define LQ6QT_BY_REMOTE_ID
Definition dhcp6.h:342
@ STATUS_NotAllowed
Definition dhcp6.h:186
@ STATUS_MalformedQuery
Definition dhcp6.h:184
@ STATUS_UnknownQueryType
Definition dhcp6.h:183
@ D6O_CLIENTID
Definition dhcp6.h:21
@ D6O_REMOTE_ID
Definition dhcp6.h:57
@ D6O_RELAY_ID
Definition dhcp6.h:73
@ D6O_LQ_QUERY
Definition dhcp6.h:64
@ D6O_IAADDR
Definition dhcp6.h:25
#define LQ6QT_BY_ADDRESS
Definition dhcp6.h:338
#define LQ6QT_BY_RELAY_ID
Definition dhcp6.h:340
#define LQ6QT_BY_CLIENTID
Definition dhcp6.h:339
#define LQ6QT_BY_LINK_ADDRESS
Definition dhcp6.h:341
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define CHECK_TERMINATED
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition lease.h:693
boost::shared_ptr< Option6StatusCode > Option6StatusCodePtr
Pointer to the isc::dhcp::Option6StatusCode.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
boost::shared_ptr< BlqResponse > BlqResponsePtr
Defines a shared pointer to an BlqResponse.
Definition blq_msg.h:143
boost::shared_ptr< BlqMsg > BlqMsgPtr
Defines a shared pointer to an BlqMsg.
Definition blq_msg.h:93
Defines the logger used by the top-level component of kea-lfc.