Kea 2.2.0
dhcp4/client_handler.cc
Go to the documentation of this file.
1// Copyright (C) 2020-2021 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, DuidPtr 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 duid_ = client_id->getDuid();
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 DuidPtr& duid) {
51 // Sanity check.
52 if (!duid) {
53 isc_throw(InvalidParameter, "null duid in ClientHandler::lookup");
54 }
55
56 auto it = clients_client_id_.find(duid->getDuid());
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 DuidPtr& duid) {
106 // Sanity check.
107 if (!duid) {
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(duid->getDuid());
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.
155 MultiThreadingMgr& mt_mgr = MultiThreadingMgr::instance();
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 DuidPtr duid;
181 if (opt_client_id) {
182 duid.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 (!duid && !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, duid, hwaddr));
198
199 {
200 lock_guard<mutex> lk(mutex_);
201 // Try first duid.
202 if (duid) {
203 // Try to acquire the by-client-id lock and return the holder
204 // when it failed.
205 holder_id = lookup(duid);
206 if (!holder_id) {
207 locked_client_id_ = duid;
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->toText())
242 .arg(this_thread::get_id())
243 .arg(holder_id->query_->toText())
244 .arg(holder_id->thread_);
245 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
246 static_cast<int64_t>(1));
247 }
248 } else {
249 // Logging a warning as it is supposed to be a rare event
250 // with well behaving clients...
252 .arg(query->toText())
253 .arg(this_thread::get_id())
254 .arg(holder_id->query_->toText())
255 .arg(holder_id->thread_);
256 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
257 static_cast<int64_t>(1));
258 }
259 } else {
260 // This query is a by-hw duplicate so put the continuation.
261 if (cont) {
262 if (next_query_hw) {
263 // Logging a warning as it is supposed to be a rare event
264 // with well behaving clients...
266 .arg(next_query_hw->toText())
267 .arg(this_thread::get_id())
268 .arg(holder_hw->query_->toText())
269 .arg(holder_hw->thread_);
270 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
271 static_cast<int64_t>(1));
272 }
273 } else {
274 // Logging a warning as it is supposed to be a rare event
275 // with well behaving clients...
277 .arg(query->toText())
278 .arg(this_thread::get_id())
279 .arg(holder_hw->query_->toText())
280 .arg(holder_hw->thread_);
281 stats::StatsMgr::instance().addValue("pkt4-receive-drop",
282 static_cast<int64_t>(1));
283 }
284 }
285 return (false);
286}
287
288void
289ClientHandler::lockById() {
290 // Sanity check.
291 if (!locked_client_id_) {
292 isc_throw(Unexpected, "nothing to lock in ClientHandler::lockById");
293 }
294
295 addById(client_);
296}
297
298void
299ClientHandler::lockByHWAddr() {
300 // Sanity check.
301 if (!locked_hwaddr_) {
302 isc_throw(Unexpected,
303 "nothing to lock in ClientHandler::lockByHWAddr");
304 }
305
306 addByHWAddr(client_);
307}
308
309void
310ClientHandler::unLockById() {
311 // Sanity check.
312 if (!locked_client_id_) {
313 isc_throw(Unexpected,
314 "nothing to unlock in ClientHandler::unLockById");
315 }
316
317 del(locked_client_id_);
318 locked_client_id_.reset();
319}
320
321void
322ClientHandler::unLockByHWAddr() {
323 // Sanity check.
324 if (!locked_hwaddr_) {
325 isc_throw(Unexpected,
326 "nothing to unlock in ClientHandler::unLockByHWAddr");
327 }
328
329 del(locked_hwaddr_);
330 locked_hwaddr_.reset();
331}
332
333} // namespace dhcp
334} // 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.
ClientHandler()
Public interface.
Holds Client identifier or client IPv4 address.
Definition: duid.h:111
static StatsMgr & instance()
Statistics Manager accessor method.
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
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition: dhcp4.h:130
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
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< 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:36
const int DBGLVL_PKT_HANDLING
This debug level is reserved for logging the details of packet handling, such as dropping the packet ...
Definition: log_dbglevels.h:58
Definition: edns.h:19
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
Definition: thread_pool.h:105