Kea  2.3.3-git
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 
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, 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 
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 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 
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 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 
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  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 
288 void
289 ClientHandler::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 
298 void
299 ClientHandler::lockByHWAddr() {
300  // Sanity check.
301  if (!locked_hwaddr_) {
303  "nothing to lock in ClientHandler::lockByHWAddr");
304  }
305 
306  addByHWAddr(client_);
307 }
308 
309 void
310 ClientHandler::unLockById() {
311  // Sanity check.
312  if (!locked_client_id_) {
314  "nothing to unlock in ClientHandler::unLockById");
315  }
316 
317  del(locked_client_id_);
318  locked_client_id_.reset();
319 }
320 
321 void
322 ClientHandler::unLockByHWAddr() {
323  // Sanity check.
324  if (!locked_hwaddr_) {
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
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
const isc::log::MessageID DHCP4_PACKET_QUEUE_FULL
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
isc::log::Logger bad_packet4_logger(DHCP4_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition: dhcp4_log.h:97
STL namespace.
Multi Threading Manager.
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
static StatsMgr & instance()
Statistics Manager accessor method.
const int DBG_DHCP4_BASIC
Debug level used to trace basic operations within the code.
Definition: dhcp4_log.h:33
bool addFront(const WorkItemPtr &item)
add a work item to the thread pool at front
Definition: thread_pool.h:105
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
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: edns.h:19
A generic exception that is thrown when an unexpected error condition occurs.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
virtual ~ClientHandler()
Destructor.
const isc::log::MessageID DHCP4_PACKET_DROP_0011
bool getMode() const
Get the multi-threading mode.
Ethernet 10Mbps.
Definition: dhcp4.h:56
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
Defines the logger used by the top-level component of kea-lfc.
ThreadPool< std::function< void()> > & getThreadPool()
Get the dhcp thread pool.
Holds Client identifier or client IPv4 address.
Definition: duid.h:111
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< Continuation > ContinuationPtr
Define the type of shared pointers to continuations.
Contains declarations for loggers used by the DHCPv4 server component.