Kea 2.7.6
dhcp4/client_handler.cc
Go to the documentation of this file.
1// Copyright (C) 2020-2023 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 <dhcp4/dhcp4_log.h>
12#include <stats/stats_mgr.h>
14
15using namespace std;
16using namespace isc::util;
17using namespace isc::log;
18
19namespace isc {
20namespace dhcp {
21
22ClientHandler::Client::Client(Pkt4Ptr query, ClientIdPtr client_id,
23 HWAddrPtr hwaddr)
24 : query_(query), htype_(HTYPE_ETHER), thread_(this_thread::get_id()) {
25 // Sanity checks.
26 if (!query) {
27 isc_throw(InvalidParameter, "null query in ClientHandler");
28 }
29 if (!client_id && (!hwaddr || hwaddr->hwaddr_.empty())) {
31 "null client-id and hwaddr in ClientHandler");
32 }
33
34 if (client_id) {
35 client_id_ = client_id->getClientId();
36 }
37 if (hwaddr && !hwaddr->hwaddr_.empty()) {
38 htype_ = hwaddr->htype_;
39 hwaddr_ = hwaddr->hwaddr_;
40 }
41}
42
43mutex ClientHandler::mutex_;
44
45ClientHandler::ClientByIdContainer ClientHandler::clients_client_id_;
46
47ClientHandler::ClientByHWAddrContainer ClientHandler::clients_hwaddr_;
48
49ClientHandler::ClientPtr
50ClientHandler::lookup(const ClientIdPtr& client_id) {
51 // Sanity check.
52 if (!client_id) {
53 isc_throw(InvalidParameter, "null client-id in ClientHandler::lookup");
54 }
55
56 auto it = clients_client_id_.find(client_id->getClientId());
57 if (it == clients_client_id_.end()) {
58 return (ClientPtr());
59 }
60 return (*it);
61}
62
63ClientHandler::ClientPtr
64ClientHandler::lookup(const HWAddrPtr& hwaddr) {
65 // Sanity checks.
66 if (!hwaddr) {
67 isc_throw(InvalidParameter, "null hwaddr in ClientHandler::lookup");
68 }
69 if (hwaddr->hwaddr_.empty()) {
70 isc_throw(InvalidParameter, "empty hwaddr in ClientHandler::lookup");
71 }
72
73 auto key = boost::make_tuple(hwaddr->htype_, hwaddr->hwaddr_);
74 auto it = clients_hwaddr_.find(key);
75 if (it == clients_hwaddr_.end()) {
76 return (ClientPtr());
77 }
78 return (*it);
79}
80
81void
82ClientHandler::addById(const ClientPtr& client) {
83 // Sanity check.
84 if (!client) {
85 isc_throw(InvalidParameter, "null client in ClientHandler::addById");
86 }
87
88 // Assume insert will never fail so not checking its result.
89 clients_client_id_.insert(client);
90}
91
92void
93ClientHandler::addByHWAddr(const ClientPtr& client) {
94 // Sanity check.
95 if (!client) {
97 "null client in ClientHandler::addByHWAddr");
98 }
99
100 // Assume insert will never fail so not checking its result.
101 clients_hwaddr_.insert(client);
102}
103
104void
105ClientHandler::del(const ClientIdPtr& client_id) {
106 // Sanity check.
107 if (!client_id) {
108 isc_throw(InvalidParameter, "null duid in ClientHandler::del");
109 }
110
111 // Assume erase will never fail so not checking its result.
112 clients_client_id_.erase(client_id->getClientId());
113}
114
115void
116ClientHandler::del(const HWAddrPtr& hwaddr) {
117 // Sanity checks.
118 if (!hwaddr) {
119 isc_throw(InvalidParameter, "null hwaddr in ClientHandler::del");
120 }
121 if (hwaddr->hwaddr_.empty()) {
122 isc_throw(InvalidParameter, "empty hwaddr in ClientHandler::del");
123 }
124
125 auto key = boost::make_tuple(hwaddr->htype_, hwaddr->hwaddr_);
126 // Assume erase will never fail so not checking its result.
127 auto it = clients_hwaddr_.find(key);
128 if (it == clients_hwaddr_.end()) {
129 // Should not happen.
130 return;
131 }
132 clients_hwaddr_.erase(it);
133}
134
136 : client_(), locked_client_id_(), locked_hwaddr_() {
137}
138
140 bool unlocked = false;
141 lock_guard<mutex> lk(mutex_);
142 if (locked_client_id_) {
143 unlocked = true;
144 unLockById();
145 }
146 if (locked_hwaddr_) {
147 unlocked = true;
148 unLockByHWAddr();
149 }
150 if (!unlocked || !client_ || !client_->cont_) {
151 return;
152 }
153 // Try to process next query. As the caller holds the mutex of
154 // the handler class the continuation will be resumed after.
156 if (mt_mgr.getMode()) {
157 if (!mt_mgr.getThreadPool().addFront(client_->cont_)) {
159 }
160 }
161}
162
163bool
165 // Sanity checks.
166 if (!query) {
167 isc_throw(InvalidParameter, "null query in ClientHandler::tryLock");
168 }
169 if (locked_client_id_) {
171 "already handling client-id in ClientHandler::tryLock");
172 }
173 if (locked_hwaddr_) {
175 "already handling hwaddr in ClientHandler::tryLock");
176 }
177
178 // Get identifiers.
179 OptionPtr opt_client_id = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
180 ClientIdPtr client_id;
181 if (opt_client_id) {
182 client_id.reset(new ClientId(opt_client_id->getData()));
183 }
184 HWAddrPtr hwaddr = query->getHWAddr();
185 if (hwaddr && hwaddr->hwaddr_.empty()) {
186 hwaddr.reset();
187 }
188 if (!client_id && !hwaddr) {
189 // Can't do something useful: cross fingers.
190 return (true);
191 }
192
193 ClientPtr holder_id;
194 ClientPtr holder_hw;
195 Pkt4Ptr next_query_id;
196 Pkt4Ptr next_query_hw;
197 client_.reset(new Client(query, client_id, hwaddr));
198
199 {
200 lock_guard<mutex> lk(mutex_);
201 // Try first duid.
202 if (client_id) {
203 // Try to acquire the by-client-id lock and return the holder
204 // when it failed.
205 holder_id = lookup(client_id);
206 if (!holder_id) {
207 locked_client_id_ = client_id;
208 lockById();
209 } else if (cont) {
210 next_query_id = holder_id->next_query_;
211 holder_id->next_query_ = query;
212 holder_id->cont_ = cont;
213 }
214 }
215 if (!holder_id) {
216 if (!hwaddr) {
217 return (true);
218 }
219 // Try to acquire the by-hw-addr lock and return the holder
220 // when it failed.
221 holder_hw = lookup(hwaddr);
222 if (!holder_hw) {
223 locked_hwaddr_ = hwaddr;
224 lockByHWAddr();
225 return (true);
226 } else if (cont) {
227 next_query_hw = holder_hw->next_query_;
228 holder_hw->next_query_ = query;
229 holder_hw->cont_ = cont;
230 }
231 }
232 }
233
234 if (holder_id) {
235 // This query is a by-id duplicate so put the continuation.
236 if (cont) {
237 if (next_query_id) {
238 // Logging a warning as it is supposed to be a rare event
239 // with well behaving clients...
241 .arg(next_query_id->getHWAddrLabel())
242 .arg(next_query_id->toText())
243 .arg(this_thread::get_id())
244 .arg(holder_id->query_->getHWAddrLabel())
245 .arg(holder_id->query_->toText())
246 .arg(holder_id->thread_);
247 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
248 static_cast<int64_t>(1));
249 }
250 } else {
251 // Logging a warning as it is supposed to be a rare event
252 // with well behaving clients...
254 .arg(query->getHWAddrLabel())
255 .arg(query->toText())
256 .arg(this_thread::get_id())
257 .arg(holder_id->query_->getHWAddrLabel())
258 .arg(holder_id->query_->toText())
259 .arg(holder_id->thread_);
260 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
261 static_cast<int64_t>(1));
262 }
263 } else {
264 // This query is a by-hw duplicate so put the continuation.
265 if (cont) {
266 if (next_query_hw) {
267 // Logging a warning as it is supposed to be a rare event
268 // with well behaving clients...
270 .arg(next_query_hw->getHWAddrLabel())
271 .arg(next_query_hw->toText())
272 .arg(this_thread::get_id())
273 .arg(holder_hw->query_->getHWAddrLabel())
274 .arg(holder_hw->query_->toText())
275 .arg(holder_hw->thread_);
276 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
277 static_cast<int64_t>(1));
278 }
279 } else {
280 // Logging a warning as it is supposed to be a rare event
281 // with well behaving clients...
283 .arg(query->getHWAddrLabel())
284 .arg(query->toText())
285 .arg(this_thread::get_id())
286 .arg(holder_hw->query_->getHWAddrLabel())
287 .arg(holder_hw->query_->toText())
288 .arg(holder_hw->thread_);
289 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
290 static_cast<int64_t>(1));
291 }
292 }
293 return (false);
294}
295
296void
297ClientHandler::lockById() {
298 // Sanity check.
299 if (!locked_client_id_) {
300 isc_throw(Unexpected, "nothing to lock in ClientHandler::lockById");
301 }
302
303 addById(client_);
304}
305
306void
307ClientHandler::lockByHWAddr() {
308 // Sanity check.
309 if (!locked_hwaddr_) {
311 "nothing to lock in ClientHandler::lockByHWAddr");
312 }
313
314 addByHWAddr(client_);
315}
316
317void
318ClientHandler::unLockById() {
319 // Sanity check.
320 if (!locked_client_id_) {
322 "nothing to unlock in ClientHandler::unLockById");
323 }
324
325 del(locked_client_id_);
326 locked_client_id_.reset();
327}
328
329void
330ClientHandler::unLockByHWAddr() {
331 // Sanity check.
332 if (!locked_hwaddr_) {
334 "nothing to unlock in ClientHandler::unLockByHWAddr");
335 }
336
337 del(locked_hwaddr_);
338 locked_hwaddr_.reset();
339}
340
341} // namespace dhcp
342} // namespace isc
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
A generic exception that is thrown when an unexpected error condition occurs.
virtual ~ClientHandler()
Destructor.
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
Holds Client identifier or client IPv4 address.
Definition duid.h:222
static StatsMgr & instance()
Statistics Manager accessor method.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
ThreadPool< std::function< void()> > & getThreadPool()
Get the dhcp thread pool.
bool getMode() const
Get the multi-threading mode.
Contains declarations for loggers used by the DHCPv4 server component.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID DHCP4_PACKET_DROP_0011
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:555
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
const isc::log::MessageID DHCP4_PACKET_QUEUE_FULL
isc::log::Logger bad_packet4_logger(DHCP4_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition dhcp4_log.h:97
const int DBG_DHCP4_BASIC
Debug level used to trace basic operations within the code.
Definition dhcp4_log.h:33
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition duid.h:216
boost::shared_ptr< Continuation > ContinuationPtr
Define the type of shared pointers to continuations.
isc::log::Logger dhcp4_logger(DHCP4_APP_LOGGER_NAME)
Base logger for DHCPv4 server.
Definition dhcp4_log.h:90
const isc::log::MessageID DHCP4_PACKET_DROP_0012
@ HTYPE_ETHER
Ethernet 10Mbps.
Definition dhcp4.h:56
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
const int DBGLVL_PKT_HANDLING
This debug level is reserved for logging the details of packet handling, such as dropping the packet ...
Defines the logger used by the top-level component of kea-lfc.
bool addFront(const WorkItemPtr &item)
add a work item to the thread pool at front