Kea 3.3.3
lease_query_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2023-2026 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
10#include <asiolink/io_address.h>
11#include <dhcp/dhcp4.h>
12#include <dhcp/dhcp6.h>
13#include <dhcp/pkt4.h>
14#include <dhcp/pkt6.h>
19
20using namespace isc::asiolink;
21using namespace isc::dhcp;
22using namespace isc::log;
23using namespace isc::tcp;
24namespace ph = std::placeholders;
25
26namespace isc {
27namespace lease_query {
28
30LeaseQueryConnection(const IOServicePtr& io_service,
31 const tcp::TcpConnectionAcceptorPtr& acceptor,
32 const TlsContextPtr& tls_context,
33 TcpConnectionPool& connection_pool,
34 const TcpConnectionAcceptorCallback& acceptor_callback,
35 const TcpConnectionFilterCallback& filter_callback,
36 const long idle_timeout,
37 const uint16_t family,
38 const size_t max_concurrent_queries,
39 const size_t read_max)
40 : TcpConnection(io_service,
41 acceptor,
42 tls_context,
43 connection_pool,
44 acceptor_callback,
45 filter_callback,
46 idle_timeout,
47 read_max),
48 family_(family),
49 io_service_(io_service),
50 stopping_(false),
51 can_send_(true),
52 max_concurrent_queries_(max_concurrent_queries) {
53}
54
55void
57 {
58 std::lock_guard<std::mutex> lck(responses_mutex_);
59 can_send_ = false;
60 responses_.clear();
61 response_to_send_.reset();
62 }
63 running_queries_.clear();
64 pending_queries_.clear();
66}
67
68void
70 {
71 std::lock_guard<std::mutex> lck(responses_mutex_);
72 can_send_ = false;
73 responses_.clear();
74 response_to_send_.reset();
75 }
76 running_queries_.clear();
77 pending_queries_.clear();
79}
80
81void
83 bool stop_now = false;
84 {
85 std::lock_guard<std::mutex> lck(responses_mutex_);
86 if (stopping_ || !can_send_) {
87 return;
88 }
89 stopping_ = true;
90 if (!response_to_send_) {
91 stop_now = true;
92 can_send_ = false;
93 }
94 }
95 if (stop_now) {
97 }
98}
99
104
105void
107 TcpStreamRequestPtr stream_req =
108 boost::dynamic_pointer_cast<TcpStreamRequest>(request);
109 if (!stream_req) {
110 // Should never happen.
111 isc_throw(isc::Unexpected, "request not a TcpStreamRequest");
112 }
113
114 // Unpack the stream request.
115 stream_req->unpack();
116 auto length = stream_req->getRequestSize();
117 if (!length) {
118 // log an error and get out.
122 return;
123 }
124
125 const uint8_t* data = stream_req->getRequest();
126
127 // The request data is a packed DHCP<family> packet. We need to unpack it.
128 BlqQueryPtr query;
129 try {
130 if (family_ == AF_INET) {
131 query = unpackQuery4(data, length);
132 } else {
133 query = unpackQuery6(data, length);
134 }
135 } catch (const std::exception& ex) {
138 .arg(ex.what());
140 return;
141 }
142
143 if (!query) {
145 return;
146 }
147
151 .arg(query->getQuery()->toText());
152
153 if (findQuery(query->getXid())) {
156 .arg(query->getXid());
157 return;
158 }
159
160 if (noPendingQuery() &&
161 ((max_concurrent_queries_ == 0) ||
163 startQuery(query);
164 } else {
170 addPendingQuery(query);
171 }
172}
173
174void
176 bool deferred_stop = false;
177 BlqResponsePtr do_send;
178 {
179 std::lock_guard<std::mutex> lck(responses_mutex_);
180 if (!can_send_) {
181 return;
182 }
183 if (!response_to_send_) {
184 if (stopping_) {
185 deferred_stop = true;
186 can_send_ = false;
187 } else if (responses_.size()) {
188 do_send = response_to_send_ = responses_.front();
189 responses_.pop_front();
190 }
191 }
192 }
193
194 // Deferred stop.
195 if (deferred_stop) {
197 } else
198
199 // Start sending the next response (if one).
200 if (do_send) {
201 try {
206 .arg(do_send->getResponse()->toText());
207 } catch (const std::exception& ex) {
211 .arg(ex.what());
212 std::lock_guard<std::mutex> lck(responses_mutex_);
213 response_to_send_.reset();
214 do_send.reset();
215 }
216 }
217}
218
221 if (!blq_response) {
222 isc_throw(BadValue, "LeaseQueryConnection::makeTcpResponse blq_response cannot be empty");
223 }
224
225 // We need the v6 packet in wire form, so pack it.
226 blq_response->getResponse()->pack();
227 auto buffer = blq_response->getResponse()->getBuffer();
228 const uint8_t* data = buffer.getData();
229 auto length = buffer.getLength();
230
231 // Create the stream response from the packet data.
232 TcpStreamResponsePtr tcp_response(new TcpStreamResponse());
233 tcp_response->setResponseData(data, length);
234
235 // Pack the stream response, making it ready to send.
236 tcp_response->pack();
237 return (tcp_response);
238}
239
240bool
242 {
243 std::lock_guard<std::mutex> lck(responses_mutex_);
244 response_to_send_.reset();
245 }
247 // Return true if we're sending again, to skip starting idle timer.
248 std::lock_guard<std::mutex> lck(responses_mutex_);
249 return ((response_to_send_ != 0));
250}
251
253LeaseQueryConnection::unpackQuery4(const uint8_t* data, size_t length) const {
254 Pkt4Ptr pkt(new Pkt4(data, length));
255 pkt->updateTimestamp();
256 auto endpoint = getRemoteEndpoint();
257 pkt->setRemoteAddr(IOAddress(endpoint.address()));
258 pkt->setRemotePort(endpoint.port());
259 pkt->unpack();
260 if (pkt->getType() != DHCPBULKLEASEQUERY) {
263 .arg(pkt->getName());
264 return (BlqQueryPtr());
265 }
266
267 return (BlqQueryPtr(new BlqQuery(pkt)));
268}
269
271LeaseQueryConnection::unpackQuery6(const uint8_t* data, size_t length) const {
272 Pkt6Ptr pkt(new Pkt6(data, length));
273 pkt->updateTimestamp();
274 auto endpoint = getRemoteEndpoint();
275 pkt->setRemoteAddr(IOAddress(endpoint.address()));
276 pkt->setRemotePort(endpoint.port());
277 pkt->unpack();
278 if (pkt->getType() != DHCPV6_LEASEQUERY) {
281 .arg(pkt->getName());
282 return (BlqQueryPtr());
283 }
284
285 return (BlqQueryPtr(new BlqQuery(pkt)));
286}
287
288void
290 {
291 std::lock_guard<std::mutex> lck(responses_mutex_);
292 if (stopping_ || !can_send_) {
293 return;
294 }
295 }
296 // Create a BulkLeaseQuery instance
297 BulkLeaseQueryPtr bulk_lease_query;
299 boost::static_pointer_cast<LeaseQueryConnection>(shared_from_this());
300 BlqPostCb post_cb = std::bind(&LeaseQueryConnection::doPost, wptr, ph::_1);
301 BlqPushToSendCb push_to_send_cb =
302 std::bind(&LeaseQueryConnection::doPushToSend, wptr, ph::_1);
303 BlqQueryCompleteCb query_complete_cb =
304 std::bind(&LeaseQueryConnection::doQueryComplete, wptr, ph::_1);
305 try {
306 if (family_ == AF_INET) {
307 bulk_lease_query.reset(new BulkLeaseQuery4(query,
308 post_cb,
309 push_to_send_cb,
310 query_complete_cb));
311 } else {
312 bulk_lease_query.reset(new BulkLeaseQuery6(query,
313 post_cb,
314 push_to_send_cb,
315 query_complete_cb));
316 }
317 } catch (const lease_query::QueryTerminated&) {
318 return;
319 } catch (const std::exception& ex) {
322 .arg(family_ == AF_INET ?
325 .arg(ex.what());
327 return;
328 }
329
330 // Check for early errors.
331 if (bulk_lease_query->isProcessed()) {
332 return;
333 }
334
335 // Add the query to the list of in-progress queries.
336 addRunningQuery(bulk_lease_query);
337
338 // Start processing.
339 post(std::bind(&BulkLeaseQuery::processStart, bulk_lease_query));
340}
341
344 BlqQueryPtr query =
345 boost::dynamic_pointer_cast<BlqQuery>(pending_queries_.pop());
346 if (query) {
351 }
352 return (query);
353}
354
355void
357 {
358 std::lock_guard<std::mutex> lck(responses_mutex_);
359 if (stopping_ || !can_send_) {
360 return;
361 }
362 }
363 // Something to do?
364 if (noPendingQuery()) {
365 return;
366 }
367 // Pop and process waiting queries until queue is empty or list full.
368 while ((max_concurrent_queries_ == 0) ||
371 if (!query) {
372 return;
373 }
374 startQuery(query);
375 }
376}
377
378bool
380 {
381 std::lock_guard<std::mutex> lck(responses_mutex_);
382 if (stopping_ || !can_send_) {
383 return (false);
384 }
385 responses_.push_back(response);
386 }
388 boost::static_pointer_cast<LeaseQueryConnection>(shared_from_this());
390 return (true);
391}
392
393void
395 {
396 std::lock_guard<std::mutex> lck(responses_mutex_);
397 if (stopping_ || !can_send_) {
398 return;
399 }
400 }
401 io_service_->post([callback]() {
402 try {
403 callback();
404 } catch (const lease_query::QueryTerminated&) {
405 return;
406 } catch (const std::exception& ex) {
409 .arg(ex.what());
410 return;
411 }
412 });
413}
414
415void
417 {
418 std::lock_guard<std::mutex> lck(responses_mutex_);
419 if (stopping_ || !can_send_) {
420 return;
421 }
422 }
425}
426
427size_t
429 std::lock_guard<std::mutex> lck(responses_mutex_);
430 return (responses_.size());
431}
432
435 auto endpoint = getRemoteEndpoint();
436 if (endpoint != NO_ENDPOINT()) {
437 return (endpoint.address());
438 }
439
440 return (family_ == AF_INET ? IOAddress::IPV4_ZERO_ADDRESS()
442}
443
444} // end of namespace isc::lease_query
445} // end of namespace isc
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 when an unexpected error condition occurs.
Represents DHCPv4 packet.
Definition pkt4.h:37
Represents a DHCPv6 packet.
Definition pkt6.h:44
Holds a bulk lease query query packet.
Definition blq_msg.h:96
Provides control flow for processing a bulk query.
static std::string leaseQueryLabel(const BlqMsgPtr &msg)
Convenience method for generating per packet logging info.
Provides control flow for processing a bulk query.
static std::string leaseQueryLabel(const BlqMsgPtr &msg)
Convenience method for generating per packet logging info.
static void processStart(BulkLeaseQueryPtr ptr)
Class/static start processing.
LeaseQueryConnection(const asiolink::IOServicePtr &io_service, const tcp::TcpConnectionAcceptorPtr &acceptor, const asiolink::TlsContextPtr &tls_context, tcp::TcpConnectionPool &connection_pool, const tcp::TcpConnectionAcceptorCallback &acceptor_callback, const tcp::TcpConnectionFilterCallback &filter_callback, const long idle_timeout, const uint16_t family, const size_t max_concurrent_queries, const size_t read_max=32768)
Constructor.
size_t max_concurrent_queries_
Maximum number of concurrent queries allowed.
size_t getNumResponses() const
Returns the number of responses in the response queue.
virtual bool responseSent(tcp::TcpResponsePtr response)
Processes a response once it has been sent.
bool findQuery(const Xid &xid) const
Find queries based on Xid in the query list and queue.
virtual tcp::TcpRequestPtr createRequest()
Creates a new empty request ready to receive data.
size_t getNumRunningQueries() const
Returns the number of queries in the in-progress list.
virtual void processNextQuery()
Process next waiting query.
bool noPendingQuery() const
Returns True if the queue of waiting queries is empty.
virtual void requestReceived(tcp::TcpRequestPtr request)
Processes a completely received request.
virtual void sendNextResponse()
Sends the next response in the response queue.
BlqQueryPtr unpackQuery6(const uint8_t *buffer, size_t length) const
Unpacks a DHCPv6 packet from a data buffer.
XidQueue< BlqMsg > pending_queries_
Queue of queries waiting to enter processing.
virtual void close()
Closes the socket.
BlqResponseList responses_
List of responses waiting to be sent.
static bool doPushToSend(LeaseQueryConnectionWPtr wptr, BlqResponsePtr response)
Class/static version of pushToSend.
uint16_t family_
Protocol family AF_INET or AF_INET6.
BlqQueryPtr popPendingQuery()
Pops a query from the queue of waiting queries.
XidQueue< BulkLeaseQuery > running_queries_
List of in-process queries.
virtual void stopThisConnection()
Stops current connection.
virtual void shutdown()
Shutdown the socket.
std::string getRemoteEndpointAddressAsText() const
Export getRemoteEndpointAddressAsText.
tcp::TcpResponsePtr makeTcpResponse(BlqResponsePtr blq_response) const
Constructs a ready to send TcpResponse from and BlqResponse.
std::mutex responses_mutex_
Mutex used to lock during responses access.
BlqResponsePtr response_to_send_
Tracks the response currently being sent.
void removeRunningQuery(const Xid &xid)
Removes a query from the in-progress query list.
static void doSendNextResponse(LeaseQueryConnectionWPtr wptr)
Class/static version of sendNextResponse.
void addPendingQuery(BlqQueryPtr query)
Queues a query to the end of the queue of waiting queries.
void startQuery(BlqQueryPtr query_msg)
Start query processing.
asiolink::IOServicePtr io_service_
IOService that drives the connection events.
virtual void queryComplete(const Xid &xid)
Finishes up when a query has been completed (e.g.
virtual void post(const BlqPostCbArg &callback)
Posts an event callback to the connection's IOService.
static void doPost(LeaseQueryConnectionWPtr wptr, const BlqPostCbArg &callback)
Class/static version of post.
BlqQueryPtr unpackQuery4(const uint8_t *buffer, size_t length) const
Unpacks a DHCPv4 packet from a data buffer.
asiolink::IOAddress getRequesterAddress() const
Returns the requester's ip address.
static void doQueryComplete(LeaseQueryConnectionWPtr wptr, const Xid &xid)
Class/static version of queryComplete.
void addRunningQuery(BulkLeaseQueryPtr query)
Adds a query to the end of the list of in-progress queries.
virtual bool pushToSend(BlqResponsePtr response)
Adds a response to the connection's outbound queue of responses.
Thrown on hook termination.
Pool of active TCP connections.
virtual void shutdown()
Shutdown the socket.
virtual void stopThisConnection()
Stops current connection.
virtual void close()
Closes the socket.
void asyncSendResponse(TcpResponsePtr response)
Sends TCP response asynchronously.
const boost::asio::ip::tcp::endpoint getRemoteEndpoint() const
Fetches the remote endpoint for the connection's socket.
static const boost::asio::ip::tcp::endpoint & NO_ENDPOINT()
Returns an empty end point.
TcpConnection(const asiolink::IOServicePtr &io_service, const TcpConnectionAcceptorPtr &acceptor, const asiolink::TlsContextPtr &tls_context, TcpConnectionPool &connection_pool, const TcpConnectionAcceptorCallback &acceptor_callback, const TcpConnectionFilterCallback &connection_filter, const long idle_timeout, const size_t read_max=32768)
Constructor.
Implement a simple length:data input stream message.
Implements a simple length:data output stream message.
@ DHCPV6_LEASEQUERY
Definition dhcp6.h:222
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:556
@ DHCPBULKLEASEQUERY
Definition dhcp4.h:248
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
const isc::log::MessageID BULK_LEASE_QUERY6_UNSUPPORTED_MSG_TYPE
const isc::log::MessageID BULK_LEASE_QUERY_DUPLICATE_XID
const isc::log::MessageID BULK_LEASE_QUERY_INVALID_REQUEST
const isc::log::MessageID BULK_LEASE_QUERY_RESPONSE_SENT
boost::shared_ptr< BlqResponse > BlqResponsePtr
Defines a shared pointer to an BlqResponse.
Definition blq_msg.h:143
std::function< void(const BlqPostCbArg &)> BlqPostCb
Type of BLQ post callback.
std::function< void(Xid)> BlqQueryCompleteCb
Type of BLQ queryComplete callback.
std::function< void()> BlqPostCbArg
Type of BLQ post callback argument..
uint32_t Xid
Defines a Bulk LeaseQuery transaction id.
Definition blq_msg.h:18
boost::weak_ptr< LeaseQueryConnection > LeaseQueryConnectionWPtr
Defines a weak pointer to a LeaseQueryConnection.
const isc::log::MessageID BULK_LEASE_QUERY_QUERY_RECEIVED
const isc::log::MessageID BULK_LEASE_QUERY_EMPTY_REQUEST
const isc::log::MessageID BULK_LEASE_QUERY_AT_MAX_CONCURRENT_QUERIES
const isc::log::MessageID BULK_LEASE_QUERY_RESPONSE_SEND_ERROR
boost::shared_ptr< BlqQuery > BlqQueryPtr
Defines a shared pointer to an BlqQuery.
Definition blq_msg.h:116
const isc::log::MessageID BULK_LEASE_QUERY4_UNSUPPORTED_MSG_TYPE
const isc::log::MessageID BULK_LEASE_QUERY_DEQUEUED
boost::shared_ptr< BulkLeaseQuery > BulkLeaseQueryPtr
Defines a shared pointer to a BulkLeaseQuery object.
isc::log::Logger lease_query_logger("lease-query-hooks")
const isc::log::MessageID BULK_LEASE_QUERY_UNPACK_ERROR
std::function< bool(BlqResponsePtr)> BlqPushToSendCb
Type of BLQ pushToSend callback.
const isc::log::MessageID BULK_LEASE_QUERY_PROCESSING_UNEXPECTED_FAILURE
const int DBGLVL_TRACE_BASIC
Trace basic operations.
std::function< bool(const boost::asio::ip::tcp::endpoint &)> TcpConnectionFilterCallback
Type of the callback for filtering new connections by ip address.
boost::shared_ptr< TcpConnectionAcceptor > TcpConnectionAcceptorPtr
Type of shared pointer to TCP acceptors.
boost::shared_ptr< TcpRequest > TcpRequestPtr
Defines a smart pointer to a TcpRequest.
boost::shared_ptr< TcpResponse > TcpResponsePtr
boost::shared_ptr< TcpStreamResponse > TcpStreamResponsePtr
Pointer to a TcpStreamResponse.
boost::shared_ptr< TcpStreamRequest > TcpStreamRequestPtr
Pointer to a TcpStreamRequest.
std::function< void(const boost::system::error_code &)> TcpConnectionAcceptorCallback
Type of the callback for the TCP acceptor used in this library.
Defines the logger used by the top-level component of kea-lfc.