Kea  2.5.3
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 
9 #include <dhcp4/client_handler.h>
10 #include <dhcp4/dhcp4_log.h>
11 #include <exceptions/exceptions.h>
12 #include <stats/stats_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  : 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 
43 mutex ClientHandler::mutex_;
44 
45 ClientHandler::ClientByIdContainer ClientHandler::clients_client_id_;
46 
47 ClientHandler::ClientByHWAddrContainer ClientHandler::clients_hwaddr_;
48 
49 ClientHandler::ClientPtr
50 ClientHandler::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 
63 ClientHandler::ClientPtr
64 ClientHandler::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 
81 void
82 ClientHandler::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 
92 void
93 ClientHandler::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 
104 void
105 ClientHandler::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 
115 void
116 ClientHandler::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 
135 ClientHandler::ClientHandler()
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 
163 bool
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 
296 void
297 ClientHandler::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 
306 void
307 ClientHandler::lockByHWAddr() {
308  // Sanity check.
309  if (!locked_hwaddr_) {
310  isc_throw(Unexpected,
311  "nothing to lock in ClientHandler::lockByHWAddr");
312  }
313 
314  addByHWAddr(client_);
315 }
316 
317 void
318 ClientHandler::unLockById() {
319  // Sanity check.
320  if (!locked_client_id_) {
321  isc_throw(Unexpected,
322  "nothing to unlock in ClientHandler::unLockById");
323  }
324 
325  del(locked_client_id_);
326  locked_client_id_.reset();
327 }
328 
329 void
330 ClientHandler::unLockByHWAddr() {
331  // Sanity check.
332  if (!locked_hwaddr_) {
333  isc_throw(Unexpected,
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:218
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: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:210
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