/src/kea/src/bin/dhcp4/client_handler.cc
Line | Count | Source |
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 | | |
9 | | #include <dhcp4/client_handler.h> |
10 | | #include <dhcp4/dhcp4_log.h> |
11 | | #include <exceptions/exceptions.h> |
12 | | #include <stats/stats_mgr.h> |
13 | | #include <util/multi_threading_mgr.h> |
14 | | |
15 | | using namespace std; |
16 | | using namespace isc::util; |
17 | | using namespace isc::log; |
18 | | |
19 | | namespace isc { |
20 | | namespace dhcp { |
21 | | |
22 | | ClientHandler::Client::Client(Pkt4Ptr query, ClientIdPtr client_id, |
23 | | HWAddrPtr hwaddr) |
24 | 0 | : query_(query), htype_(HTYPE_ETHER), thread_(this_thread::get_id()) { |
25 | | // Sanity checks. |
26 | 0 | if (!query) { |
27 | 0 | isc_throw(InvalidParameter, "null query in ClientHandler"); |
28 | 0 | } |
29 | 0 | if (!client_id && (!hwaddr || hwaddr->hwaddr_.empty())) { |
30 | 0 | isc_throw(InvalidParameter, |
31 | 0 | "null client-id and hwaddr in ClientHandler"); |
32 | 0 | } |
33 | | |
34 | 0 | if (client_id) { |
35 | 0 | client_id_ = client_id->getClientId(); |
36 | 0 | } |
37 | 0 | if (hwaddr && !hwaddr->hwaddr_.empty()) { |
38 | 0 | htype_ = hwaddr->htype_; |
39 | 0 | hwaddr_ = hwaddr->hwaddr_; |
40 | 0 | } |
41 | 0 | } |
42 | | |
43 | | mutex ClientHandler::mutex_; |
44 | | |
45 | | ClientHandler::ClientByIdContainer ClientHandler::clients_client_id_; |
46 | | |
47 | | ClientHandler::ClientByHWAddrContainer ClientHandler::clients_hwaddr_; |
48 | | |
49 | | ClientHandler::ClientPtr |
50 | 0 | ClientHandler::lookup(const ClientIdPtr& client_id) { |
51 | | // Sanity check. |
52 | 0 | if (!client_id) { |
53 | 0 | isc_throw(InvalidParameter, "null client-id in ClientHandler::lookup"); |
54 | 0 | } |
55 | | |
56 | 0 | auto it = clients_client_id_.find(client_id->getClientId()); |
57 | 0 | if (it == clients_client_id_.end()) { |
58 | 0 | return (ClientPtr()); |
59 | 0 | } |
60 | 0 | return (*it); |
61 | 0 | } |
62 | | |
63 | | ClientHandler::ClientPtr |
64 | 0 | ClientHandler::lookup(const HWAddrPtr& hwaddr) { |
65 | | // Sanity checks. |
66 | 0 | if (!hwaddr) { |
67 | 0 | isc_throw(InvalidParameter, "null hwaddr in ClientHandler::lookup"); |
68 | 0 | } |
69 | 0 | if (hwaddr->hwaddr_.empty()) { |
70 | 0 | isc_throw(InvalidParameter, "empty hwaddr in ClientHandler::lookup"); |
71 | 0 | } |
72 | | |
73 | 0 | auto key = boost::make_tuple(hwaddr->htype_, hwaddr->hwaddr_); |
74 | 0 | auto it = clients_hwaddr_.find(key); |
75 | 0 | if (it == clients_hwaddr_.end()) { |
76 | 0 | return (ClientPtr()); |
77 | 0 | } |
78 | 0 | return (*it); |
79 | 0 | } |
80 | | |
81 | | void |
82 | 0 | ClientHandler::addById(const ClientPtr& client) { |
83 | | // Sanity check. |
84 | 0 | if (!client) { |
85 | 0 | isc_throw(InvalidParameter, "null client in ClientHandler::addById"); |
86 | 0 | } |
87 | | |
88 | | // Assume insert will never fail so not checking its result. |
89 | 0 | clients_client_id_.insert(client); |
90 | 0 | } |
91 | | |
92 | | void |
93 | 0 | ClientHandler::addByHWAddr(const ClientPtr& client) { |
94 | | // Sanity check. |
95 | 0 | if (!client) { |
96 | 0 | isc_throw(InvalidParameter, |
97 | 0 | "null client in ClientHandler::addByHWAddr"); |
98 | 0 | } |
99 | | |
100 | | // Assume insert will never fail so not checking its result. |
101 | 0 | clients_hwaddr_.insert(client); |
102 | 0 | } |
103 | | |
104 | | void |
105 | 0 | ClientHandler::del(const ClientIdPtr& client_id) { |
106 | | // Sanity check. |
107 | 0 | if (!client_id) { |
108 | 0 | isc_throw(InvalidParameter, "null duid in ClientHandler::del"); |
109 | 0 | } |
110 | | |
111 | | // Assume erase will never fail so not checking its result. |
112 | 0 | clients_client_id_.erase(client_id->getClientId()); |
113 | 0 | } |
114 | | |
115 | | void |
116 | 0 | ClientHandler::del(const HWAddrPtr& hwaddr) { |
117 | | // Sanity checks. |
118 | 0 | if (!hwaddr) { |
119 | 0 | isc_throw(InvalidParameter, "null hwaddr in ClientHandler::del"); |
120 | 0 | } |
121 | 0 | if (hwaddr->hwaddr_.empty()) { |
122 | 0 | isc_throw(InvalidParameter, "empty hwaddr in ClientHandler::del"); |
123 | 0 | } |
124 | | |
125 | 0 | auto key = boost::make_tuple(hwaddr->htype_, hwaddr->hwaddr_); |
126 | | // Assume erase will never fail so not checking its result. |
127 | 0 | auto it = clients_hwaddr_.find(key); |
128 | 0 | if (it == clients_hwaddr_.end()) { |
129 | | // Should not happen. |
130 | 0 | return; |
131 | 0 | } |
132 | 0 | clients_hwaddr_.erase(it); |
133 | 0 | } |
134 | | |
135 | | ClientHandler::ClientHandler() |
136 | 1.00k | : client_(), locked_client_id_(), locked_hwaddr_() { |
137 | 1.00k | } |
138 | | |
139 | | ClientHandler::~ClientHandler() { |
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_)) { |
158 | | LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_PACKET_QUEUE_FULL); |
159 | | } |
160 | | } |
161 | | } |
162 | | |
163 | | bool |
164 | 0 | ClientHandler::tryLock(Pkt4Ptr query, ContinuationPtr cont) { |
165 | | // Sanity checks. |
166 | 0 | if (!query) { |
167 | 0 | isc_throw(InvalidParameter, "null query in ClientHandler::tryLock"); |
168 | 0 | } |
169 | 0 | if (locked_client_id_) { |
170 | 0 | isc_throw(Unexpected, |
171 | 0 | "already handling client-id in ClientHandler::tryLock"); |
172 | 0 | } |
173 | 0 | if (locked_hwaddr_) { |
174 | 0 | isc_throw(Unexpected, |
175 | 0 | "already handling hwaddr in ClientHandler::tryLock"); |
176 | 0 | } |
177 | | |
178 | | // Get identifiers. |
179 | 0 | OptionPtr opt_client_id = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER); |
180 | 0 | ClientIdPtr client_id; |
181 | 0 | if (opt_client_id) { |
182 | 0 | client_id.reset(new ClientId(opt_client_id->getData())); |
183 | 0 | } |
184 | 0 | HWAddrPtr hwaddr = query->getHWAddr(); |
185 | 0 | if (hwaddr && hwaddr->hwaddr_.empty()) { |
186 | 0 | hwaddr.reset(); |
187 | 0 | } |
188 | 0 | if (!client_id && !hwaddr) { |
189 | | // Can't do something useful: cross fingers. |
190 | 0 | return (true); |
191 | 0 | } |
192 | | |
193 | 0 | ClientPtr holder_id; |
194 | 0 | ClientPtr holder_hw; |
195 | 0 | Pkt4Ptr next_query_id; |
196 | 0 | Pkt4Ptr next_query_hw; |
197 | 0 | client_.reset(new Client(query, client_id, hwaddr)); |
198 | |
|
199 | 0 | { |
200 | 0 | lock_guard<mutex> lk(mutex_); |
201 | | // Try first duid. |
202 | 0 | if (client_id) { |
203 | | // Try to acquire the by-client-id lock and return the holder |
204 | | // when it failed. |
205 | 0 | holder_id = lookup(client_id); |
206 | 0 | if (!holder_id) { |
207 | 0 | locked_client_id_ = client_id; |
208 | 0 | lockById(); |
209 | 0 | } else if (cont) { |
210 | 0 | next_query_id = holder_id->next_query_; |
211 | 0 | holder_id->next_query_ = query; |
212 | 0 | holder_id->cont_ = cont; |
213 | 0 | } |
214 | 0 | } |
215 | 0 | if (!holder_id) { |
216 | 0 | if (!hwaddr) { |
217 | 0 | return (true); |
218 | 0 | } |
219 | | // Try to acquire the by-hw-addr lock and return the holder |
220 | | // when it failed. |
221 | 0 | holder_hw = lookup(hwaddr); |
222 | 0 | if (!holder_hw) { |
223 | 0 | locked_hwaddr_ = hwaddr; |
224 | 0 | lockByHWAddr(); |
225 | 0 | return (true); |
226 | 0 | } else if (cont) { |
227 | 0 | next_query_hw = holder_hw->next_query_; |
228 | 0 | holder_hw->next_query_ = query; |
229 | 0 | holder_hw->cont_ = cont; |
230 | 0 | } |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | 0 | if (holder_id) { |
235 | | // This query is a by-id duplicate so put the continuation. |
236 | 0 | if (cont) { |
237 | 0 | if (next_query_id) { |
238 | | // Logging a warning as it is supposed to be a rare event |
239 | | // with well behaving clients... |
240 | 0 | LOG_DEBUG(bad_packet4_logger, DBGLVL_PKT_HANDLING, DHCP4_PACKET_DROP_0011) |
241 | 0 | .arg(next_query_id->getHWAddrLabel()) |
242 | 0 | .arg(next_query_id->toText()) |
243 | 0 | .arg(this_thread::get_id()) |
244 | 0 | .arg(holder_id->query_->getHWAddrLabel()) |
245 | 0 | .arg(holder_id->query_->toText()) |
246 | 0 | .arg(holder_id->thread_); |
247 | 0 | stats::StatsMgr::instance().addValue("pkt4-queue-full", |
248 | 0 | static_cast<int64_t>(1)); |
249 | 0 | stats::StatsMgr::instance().addValue("pkt4-receive-drop", |
250 | 0 | static_cast<int64_t>(1)); |
251 | 0 | } |
252 | 0 | } else { |
253 | | // Logging a warning as it is supposed to be a rare event |
254 | | // with well behaving clients... |
255 | 0 | LOG_DEBUG(bad_packet4_logger, DBGLVL_PKT_HANDLING, DHCP4_PACKET_DROP_0011) |
256 | 0 | .arg(query->getHWAddrLabel()) |
257 | 0 | .arg(query->toText()) |
258 | 0 | .arg(this_thread::get_id()) |
259 | 0 | .arg(holder_id->query_->getHWAddrLabel()) |
260 | 0 | .arg(holder_id->query_->toText()) |
261 | 0 | .arg(holder_id->thread_); |
262 | 0 | stats::StatsMgr::instance().addValue("pkt4-queue-full", |
263 | 0 | static_cast<int64_t>(1)); |
264 | 0 | stats::StatsMgr::instance().addValue("pkt4-receive-drop", |
265 | 0 | static_cast<int64_t>(1)); |
266 | 0 | } |
267 | 0 | } else { |
268 | | // This query is a by-hw duplicate so put the continuation. |
269 | 0 | if (cont) { |
270 | 0 | if (next_query_hw) { |
271 | | // Logging a warning as it is supposed to be a rare event |
272 | | // with well behaving clients... |
273 | 0 | LOG_DEBUG(bad_packet4_logger, DBGLVL_PKT_HANDLING, DHCP4_PACKET_DROP_0012) |
274 | 0 | .arg(next_query_hw->getHWAddrLabel()) |
275 | 0 | .arg(next_query_hw->toText()) |
276 | 0 | .arg(this_thread::get_id()) |
277 | 0 | .arg(holder_hw->query_->getHWAddrLabel()) |
278 | 0 | .arg(holder_hw->query_->toText()) |
279 | 0 | .arg(holder_hw->thread_); |
280 | 0 | stats::StatsMgr::instance().addValue("pkt4-queue-full", |
281 | 0 | static_cast<int64_t>(1)); |
282 | 0 | stats::StatsMgr::instance().addValue("pkt4-receive-drop", |
283 | 0 | static_cast<int64_t>(1)); |
284 | 0 | } |
285 | 0 | } else { |
286 | | // Logging a warning as it is supposed to be a rare event |
287 | | // with well behaving clients... |
288 | 0 | LOG_DEBUG(bad_packet4_logger, DBGLVL_PKT_HANDLING, DHCP4_PACKET_DROP_0012) |
289 | 0 | .arg(query->getHWAddrLabel()) |
290 | 0 | .arg(query->toText()) |
291 | 0 | .arg(this_thread::get_id()) |
292 | 0 | .arg(holder_hw->query_->getHWAddrLabel()) |
293 | 0 | .arg(holder_hw->query_->toText()) |
294 | 0 | .arg(holder_hw->thread_); |
295 | 0 | stats::StatsMgr::instance().addValue("pkt4-queue-full", |
296 | 0 | static_cast<int64_t>(1)); |
297 | 0 | stats::StatsMgr::instance().addValue("pkt4-receive-drop", |
298 | 0 | static_cast<int64_t>(1)); |
299 | 0 | } |
300 | 0 | } |
301 | 0 | return (false); |
302 | 0 | } |
303 | | |
304 | | void |
305 | 0 | ClientHandler::lockById() { |
306 | | // Sanity check. |
307 | 0 | if (!locked_client_id_) { |
308 | 0 | isc_throw(Unexpected, "nothing to lock in ClientHandler::lockById"); |
309 | 0 | } |
310 | | |
311 | 0 | addById(client_); |
312 | 0 | } |
313 | | |
314 | | void |
315 | 0 | ClientHandler::lockByHWAddr() { |
316 | | // Sanity check. |
317 | 0 | if (!locked_hwaddr_) { |
318 | 0 | isc_throw(Unexpected, |
319 | 0 | "nothing to lock in ClientHandler::lockByHWAddr"); |
320 | 0 | } |
321 | | |
322 | 0 | addByHWAddr(client_); |
323 | 0 | } |
324 | | |
325 | | void |
326 | 0 | ClientHandler::unLockById() { |
327 | | // Sanity check. |
328 | 0 | if (!locked_client_id_) { |
329 | 0 | isc_throw(Unexpected, |
330 | 0 | "nothing to unlock in ClientHandler::unLockById"); |
331 | 0 | } |
332 | | |
333 | 0 | del(locked_client_id_); |
334 | 0 | locked_client_id_.reset(); |
335 | 0 | } |
336 | | |
337 | | void |
338 | 0 | ClientHandler::unLockByHWAddr() { |
339 | | // Sanity check. |
340 | 0 | if (!locked_hwaddr_) { |
341 | 0 | isc_throw(Unexpected, |
342 | 0 | "nothing to unlock in ClientHandler::unLockByHWAddr"); |
343 | 0 | } |
344 | | |
345 | 0 | del(locked_hwaddr_); |
346 | 0 | locked_hwaddr_.reset(); |
347 | 0 | } |
348 | | |
349 | | } // namespace dhcp |
350 | | } // namespace isc |