Kea 3.1.1
bulk_lease_query4.cc
Go to the documentation of this file.
1// Copyright (C) 2023-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/dhcp4.h>
10#include <dhcp/libdhcp++.h>
11#include <dhcp/pkt4.h>
12#include <dhcp/option_custom.h>
13#include <dhcp/option_int.h>
14#include <dhcpsrv/cfgmgr.h>
16#include <bulk_lease_query4.h>
17#include <lease_query_impl4.h>
18#include <lease_query_log.h>
19#include <blq_service.h>
20#include <stats/stats_mgr.h>
21#include <util/encode/encode.h>
22
23#include <boost/shared_ptr.hpp>
24#include <boost/pointer_cast.hpp>
25
26using namespace isc;
27using namespace isc::asiolink;
28using namespace isc::data;
29using namespace isc::dhcp;
30using namespace isc::lease_query;
31using namespace isc::log;
32using namespace isc::stats;
33
34std::string
36 switch (status) {
38 return ("Success");
40 return ("UnspecFail");
42 return ("QueryTerminated");
44 return ("MalformedQuery");
46 return ("NotAllowed");
47 default:
48 return ("(unknown status code)");
49 }
50}
51
52std::string
54 switch (state) {
56 return ("AVAILABLE");
58 return ("ACTIVE");
60 return ("EXPIRED");
62 return ("RELEASED");
64 return ("ABANDONED");
65 case BLQ_STATE_RESET:
66 return ("RESET");
68 return ("REMOTE");
70 return ("TRANSITIONING");
71 default:
72 return ("(unknown state)");
73 }
74}
75
76std::string
78 Pkt4Ptr pkt = boost::dynamic_pointer_cast<Pkt4>(msg->getPkt());
79 if (!pkt) {
80 return ("<none>");
81 }
83}
84
85void
87 // Take the page size from the MT LQ listener manager.
88 auto service = BulkLeaseQueryService::instance();
89 if (service) {
90 page_size_ = service->getMaxLeasePerFetch();
91 }
92
93 query4_ = boost::dynamic_pointer_cast<Pkt4>(query_->getQuery());
94 if (!query4_) {
95 // Shouldn't happen
96 isc_throw(BadValue, "BulkLeaseQuery4 has no DHCPv4 query");
97 }
98
99 // Check addresses.
100 if (!query4_->getCiaddr().isV4Zero()) {
101 sendDone(BLQ_STATUS_MalformedQuery, "ciaddr must be zero");
102 return;
103 }
104 if (!query4_->getYiaddr().isV4Zero()) {
105 sendDone(BLQ_STATUS_MalformedQuery, "yiaddr must be zero");
106 return;
107 }
108 if (!query4_->getSiaddr().isV4Zero()) {
109 sendDone(BLQ_STATUS_MalformedQuery, "siaddr must be zero");
110 return;
111 }
112
113 // Get the client identifier.
115 if (query_client_id_) {
116 query_mask_ |= 1;
117 }
118
119 // Get the hardware address.
120 query_hwaddr_ = query4_->getHWAddr();
121 if (query_hwaddr_->htype_ || query_hwaddr_->hwaddr_.size()) {
122 query_mask_ |= 2;
123 }
124
125 // Get the RAI.
126 OptionPtr rai = query4_->getOption(DHO_DHCP_AGENT_OPTIONS);
127
128 // Get the relay identifier.
129 if (rai) {
130 query_relay_id_ = rai->getOption(RAI_OPTION_RELAY_ID);
131 if (query_relay_id_) {
132 if (query_relay_id_->getData().empty()) {
133 sendDone(BLQ_STATUS_MalformedQuery, "empty relay id option");
134 return;
135 }
136 query_mask_ |= 4;
137 }
138 }
139
140 // Get the remote identifier.
141 if (rai) {
142 query_remote_id_ = rai->getOption(RAI_OPTION_REMOTE_ID);
143 if (query_remote_id_) {
144 if (query_remote_id_->getData().empty()) {
145 sendDone(BLQ_STATUS_MalformedQuery, "empty remote id option");
146 return;
147 }
148 query_mask_ |= 8;
149 }
150 }
151
152 // Get the query start time.
153 OptionPtr opt = query4_->getOption(DHO_QUERY_START_TIME);
154 if (opt) {
155 OptionUint32Ptr qst =
156 boost::dynamic_pointer_cast<OptionInt<uint32_t>>(opt);
157 if (qst) {
158 query_start_time_ = qst->getValue();
159 }
160 if (!qst || (static_cast<int32_t>(query_start_time_) < 0)) {
161 sendDone(BLQ_STATUS_MalformedQuery, "illegal 'query-start-time'");
162 return;
163 }
164 }
165
166 // Get the query end time.
167 opt = query4_->getOption(DHO_QUERY_END_TIME);
168 if (opt) {
169 OptionUint32Ptr qst =
170 boost::dynamic_pointer_cast<OptionInt<uint32_t>>(opt);
171 if (qst) {
172 query_end_time_ = qst->getValue();
173 }
174 if (!qst || (static_cast<int32_t>(query_end_time_) < 0)) {
175 sendDone(BLQ_STATUS_MalformedQuery, "illegal 'query-end-time'");
176 return;
177 }
178 }
179 if ((query_start_time_ > 0) && (query_end_time_ > 0) &&
181 sendDone(BLQ_STATUS_MalformedQuery, "query-start-time > query-end-time");
182 return;
183 }
184
185 // Get the VPN-ID.
186 opt = query4_->getOption(DHO_VSS);
187 if (opt) {
188 sendDone(BLQ_STATUS_NotAllowed, "VPNs are not supported");
189 return;
190 }
191
192 // Do the query based on which query attribute we have,
193 // or error out.
194 switch (query_mask_) {
195 case 0:
196 // Query for all configured addresses (not implemented).
198 "query for all configured addresses is not supported");
199 return;
200
201 case 1:
202 case 2:
203 case 4:
204 case 8:
205 // Supported query by something.
206 return;
207
208 default:
209 // Multiple query types.
210 sendDone(BLQ_STATUS_MalformedQuery, "multiple queries");
211 return;
212
213 }
214}
215
216void
218 if (started_) {
219 isc_throw(InvalidOperation, "BulkLeaseQuery4 already in progress");
220 }
221 started_ = true;
222
223 switch (query_mask_) {
224 case 1:
225 // Query by client id.
227 return;
228 case 2:
229 // Query by hardware addr.
231 return;
232 case 4:
233 // Query by relay id.
235 return;
236 case 8:
237 // Query by remote id.
239 return;
240 default:
241 isc_throw(InvalidOperation, "unknown query-type");
242 }
243}
244
245void
247 if (!query_client_id_) {
248 isc_throw(InvalidOperation, "no query client id");
249 }
250
251 // Fetch leases.
252 ClientId client_id(query_client_id_->getData());
254
255 // Send responses.
256 for (auto const& lease : leases) {
257 if (lease->state_ != Lease::STATE_DEFAULT) {
258 // Not active.
259 continue;
260 }
261 if (lease->expired()) {
262 // Already expired.
263 continue;
264 }
265 if ((query_start_time_ > 0) && (lease->cltt_ < query_start_time_)) {
266 // Too old.
267 continue;
268 }
269 if ((query_end_time_ > 0) && (lease->cltt_ > query_end_time_)) {
270 // Too young.
271 continue;
272 }
273
274 // Send response.
275 sendActive(lease);
276 }
277
278 // Send the done message.
280}
281
282void
284 if (!query_hwaddr_) {
285 isc_throw(InvalidOperation, "no query hardware addr");
286 }
287
288 Lease4Collection leases =
290
291 // Send responses.
292 for (auto const& lease : leases) {
293 if (lease->state_ != Lease::STATE_DEFAULT) {
294 // Not active.
295 continue;
296 }
297 if (lease->expired()) {
298 // Already expired.
299 continue;
300 }
301 if ((query_start_time_ > 0) && (lease->cltt_ < query_start_time_)) {
302 // Too old.
303 continue;
304 }
305 if ((query_end_time_ > 0) && (lease->cltt_ > query_end_time_)) {
306 // Too young.
307 continue;
308 }
309
310 // Send response.
311 sendActive(lease);
312 }
313
314 // Send the done message.
316}
317
318void
320 if (!query_relay_id_) {
321 isc_throw(InvalidOperation, "no query relay id");
322 }
323
324 auto const& relay_id = query_relay_id_->getData();
325 Lease4Collection leases;
326
327 // Database possible call: check if the hook was terminated.
329
330 leases =
336 // when empty it is done.
337 if (leases.empty()) {
339 return;
340 }
341
342 // Record the last address to restart from this point.
343 start_addr_ = leases.back()->addr_;
344
345 // Send responses.
346 for (auto const& lease : leases) {
347 if (lease->state_ != Lease::STATE_DEFAULT) {
348 // Not active.
349 continue;
350 }
351 if (lease->expired()) {
352 // Already expired.
353 continue;
354 }
355
356 // Send response.
357 sendActive(lease);
358 }
359
361 boost::static_pointer_cast<BulkLeaseQuery4>(shared_from_this())));
362}
363
364void
366 if (!query_remote_id_) {
367 isc_throw(InvalidOperation, "no query remote id");
368 }
369
370 auto const& remote_id = query_remote_id_->getData();
371 Lease4Collection leases;
372
373 // Database possible call: check if the hook was terminated.
375
376 leases =
382 // when empty it is done.
383 if (leases.empty()) {
385 return;
386 }
387
388 // Record the last address to restart from this point.
389 start_addr_ = leases.back()->addr_;
390
391 // Send responses.
392 for (auto const& lease : leases) {
393 if (lease->state_ != Lease::STATE_DEFAULT) {
394 // Not active.
395 continue;
396 }
397 if (lease->expired()) {
398 // Already expired.
399 continue;
400 }
401
402 // Send response.
403 sendActive(lease);
404 }
405
407 boost::static_pointer_cast<BulkLeaseQuery4>(shared_from_this())));
408}
409
410void
413
414 // Add the dhcp-server-identifier in the first response.
415 if (sent_ == 0) {
418 auto const& srv = BulkLeaseQueryService::instance();
419 if (!srv) {
420 isc_throw(Unexpected, "can't find bulk lease query service");
421 }
422 opt->writeAddress(srv->getLeaseQueryIp());
423 response->addOption(opt);
424 }
425 ++sent_;
426
427 // Send it.
428 BlqResponsePtr resp(new BlqResponse(response));
429 if (!push_to_send_(resp)) {
430 isc_throw(QueryTerminated, "connection closed");
431 }
432}
433
434void
435BulkLeaseQuery4::sendDone(BLQStatusCode status, const std::string& message) {
436 // Create and send done message.
437 Pkt4Ptr done(new Pkt4(DHCPLEASEQUERYDONE, query4_->getTransid()));
438
439 // Zero out the hwaddr type.
440 done->setHWAddr(HWAddrPtr(new HWAddr(std::vector<uint8_t>{}, 0)));
441
442 // Add the status.
443 if ((status != BLQ_STATUS_Success) || !message.empty()) {
446 opt->writeInteger(static_cast<uint8_t>(status), 0);
447 opt->writeString(message, 1);
448 done->addOption(opt);
449 }
450
451 // Send it.
452 send(done);
453
454 // It is final.
455 setDone();
456}
457
458void
460 // Should not happen.
461 if (!lease) {
462 return;
463 }
464
465 // Get subnet.
467 ->getCfgSubnets4()->getSubnet(lease->subnet_id_);
468 if (!subnet) {
469 // Should not happen: simply ignore this lease.
470 return;
471 }
472
473 // Create and send active message.
474 Pkt4Ptr active(new Pkt4(DHCPLEASEACTIVE, query4_->getTransid()));
475
476 // Add client IP address.
477 active->setCiaddr(lease->addr_);
478
479 // Add client hardware address.
480 if (lease->hwaddr_) {
481 active->setHWAddr(lease->hwaddr_);
482 } else {
483 active->setHWAddr(HWAddrPtr(new HWAddr(std::vector<uint8_t>{}, 0)));
484 }
485
486 // Add client identifier.
487 if (lease->client_id_) {
489 lease->client_id_->getClientId()));
490 active->addOption(opt);
491 }
492
493 // Add base time.
495 active->addOption(opt);
496
497 // Add lease life time, T1 and T2.
498 LeaseQueryImpl4::addLeaseTimes(active, lease, subnet);
499
500 // Add relay-agent-info (82) from the extended info in the lease's
501 // user-context and add it to the response.
503
504 // Send it.
505 send(active);
506}
std::string getStateName(BLQStates state)
std::string getStatusCodeName(BLQStatusCode status)
#define DHO_VSS
Virtual Subnet Selection Option code point (RFC 6607 and 6926).
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.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
Holds Client identifier or client IPv4 address.
Definition duid.h:222
static TrackingLeaseMgr & instance()
Return current lease manager.
virtual Lease4Collection getLeases4ByRemoteId(const OptionBuffer &remote_id, const asiolink::IOAddress &lower_bound_address, const LeasePageSize &page_size, const time_t &qry_start_time=0, const time_t &qry_end_time=0)=0
Returns existing IPv4 leases with a given remote-id.
virtual Lease4Collection getLeases4ByRelayId(const OptionBuffer &relay_id, const asiolink::IOAddress &lower_bound_address, const LeasePageSize &page_size, const time_t &qry_start_time=0, const time_t &qry_end_time=0)=0
Returns existing IPv4 leases with a given relay-id.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const =0
Returns an IPv4 lease for specified IPv4 address.
Wraps value holding size of the page with leases.
Definition lease_mgr.h:46
static const OptionDefinition & DHO_DHCP_SERVER_IDENTIFIER_DEF()
Get definition of DHO_DHCP_SERVER_IDENTIFIER option.
static const OptionDefinition & DHO_STATUS_CODE_DEF()
Get definition of DHO_STATUS_CODE option.
Option with defined data fields represented as buffers that can be accessed using data field index.
Base class representing a DHCP option definition.
Represents DHCPv4 packet.
Definition pkt4.h:37
Holds a bulk lease query response packet.
Definition blq_msg.h:124
dhcp::HWAddrPtr query_hwaddr_
The query hardware address (for a by hardware address bulk query).
void bulkQueryByClientId()
Start processing of a by client id bulk query.
size_t sent_
Sent response counter.
virtual void start()
Start processing.
time_t query_start_time_
The query start time.
time_t query_end_time_
The query end time.
virtual void send(dhcp::Pkt4Ptr response)
Send a response.
static std::string leaseQueryLabel(const BlqMsgPtr &msg)
Convenience method for generating per packet logging info.
static void doBulkQueryByRemoteId(BulkLeaseQuery4Ptr ptr)
Class/static subsequent processing of a by remote id bulk query.
void bulkQueryByRelayId()
Start processing of a by relay id bulk query.
dhcp::OptionPtr query_client_id_
The query client id (for a by client id bulk query).
asiolink::IOAddress start_addr_
The start address (for paged processing).
void bulkQueryByHWAddr()
Start processing of a by harware address bulk query.
size_t page_size_
The page size (for paged processing, taken from the MT Lease query manager or defaults to 10).
dhcp::OptionPtr query_remote_id_
The remote id (for a by remote id bulk query).
virtual void sendDone(BLQStatusCode status, const std::string &message="")
Send a DHCPLEASEQUERYDONE message.
dhcp::OptionPtr query_relay_id_
The query relay id (for a by relay id bulk query).
static void doBulkQueryByRelayId(BulkLeaseQuery4Ptr ptr)
Class/static subsequent processing of a by relay id bulk query.
dhcp::Pkt4Ptr query4_
The DHCPv4 query.
void bulkQueryByRemoteId()
Start processing of a by remote id bulk query.
virtual void sendActive(const dhcp::Lease4Ptr &lease)
Send a DHCPLEASEACTIVE message.
virtual void init()
Initialization.
static BulkLeaseQueryServicePtr instance()
Returns a pointer to the sole instance of the BulkLeaseQueryService, can return null.
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.
static void addLeaseTimes(dhcp::Pkt4Ptr response, const dhcp::Lease4Ptr &lease, const dhcp::Subnet4Ptr &subnet)
Adds life time, T1, and T2 options to a query response.
static std::string leaseQueryLabel(const dhcp::Pkt4Ptr &packet)
Convenience method for generating per packet logging info.
static void addRelayAgentInfo(dhcp::Pkt4Ptr response, const dhcp::Lease4Ptr &lease)
Adds relay-agent-info option to a query response.
Thrown on hook termination.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionInt< uint32_t > OptionUint32
Definition option_int.h:34
boost::shared_ptr< OptionUint32 > OptionUint32Ptr
Definition option_int.h:35
#define CHECK_TERMINATED
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition subnet.h:461
@ DHO_QUERY_START_TIME
Definition dhcp4.h:208
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition dhcp4.h:130
@ DHO_BASE_TIME
Definition dhcp4.h:206
@ DHO_QUERY_END_TIME
Definition dhcp4.h:209
@ DHO_DHCP_AGENT_OPTIONS
Definition dhcp4.h:151
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:556
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
@ DHCPLEASEQUERYDONE
Definition dhcp4.h:249
@ DHCPLEASEACTIVE
Definition dhcp4.h:247
@ RAI_OPTION_RELAY_ID
Definition dhcp4.h:276
@ RAI_OPTION_REMOTE_ID
Definition dhcp4.h:266
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition lease.h:520
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition lease.h:315
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
BLQStatusCode
Status Codes.
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.
Hardware type that represents information from DHCPv4 packet.
Definition hwaddr.h:20
static const uint32_t STATE_DEFAULT
A lease in the default state.
Definition lease.h:69