Kea  1.9.9-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 
18 namespace isc {
19 namespace dhcp {
20 
21 ClientHandler::Client::Client(Pkt4Ptr query, DuidPtr client_id,
22  HWAddrPtr hwaddr)
23  : query_(query), htype_(HTYPE_ETHER), thread_(this_thread::get_id()) {
24  // Sanity checks.
25  if (!query) {
26  isc_throw(InvalidParameter, "null query in ClientHandler");
27  }
28  if (!client_id && (!hwaddr || hwaddr->hwaddr_.empty())) {
30  "null client-id and hwaddr in ClientHandler");
31  }
32 
33  if (client_id) {
34  duid_ = client_id->getDuid();
35  }
36  if (hwaddr && !hwaddr->hwaddr_.empty()) {
37  htype_ = hwaddr->htype_;
38  hwaddr_ = hwaddr->hwaddr_;
39  }
40 }
41 
42 mutex ClientHandler::mutex_;
43 
44 ClientHandler::ClientByIdContainer ClientHandler::clients_client_id_;
45 
46 ClientHandler::ClientByHWAddrContainer ClientHandler::clients_hwaddr_;
47 
48 ClientHandler::ClientPtr
49 ClientHandler::lookup(const DuidPtr& duid) {
50  // Sanity check.
51  if (!duid) {
52  isc_throw(InvalidParameter, "null duid in ClientHandler::lookup");
53  }
54 
55  auto it = clients_client_id_.find(duid->getDuid());
56  if (it == clients_client_id_.end()) {
57  return (ClientPtr());
58  }
59  return (*it);
60 }
61 
62 ClientHandler::ClientPtr
63 ClientHandler::lookup(const HWAddrPtr& hwaddr) {
64  // Sanity checks.
65  if (!hwaddr) {
66  isc_throw(InvalidParameter, "null hwaddr in ClientHandler::lookup");
67  }
68  if (hwaddr->hwaddr_.empty()) {
69  isc_throw(InvalidParameter, "empty hwaddr in ClientHandler::lookup");
70  }
71 
72  auto key = boost::make_tuple(hwaddr->htype_, hwaddr->hwaddr_);
73  auto it = clients_hwaddr_.find(key);
74  if (it == clients_hwaddr_.end()) {
75  return (ClientPtr());
76  }
77  return (*it);
78 }
79 
80 void
81 ClientHandler::addById(const ClientPtr& client) {
82  // Sanity check.
83  if (!client) {
84  isc_throw(InvalidParameter, "null client in ClientHandler::addById");
85  }
86 
87  // Assume insert will never fail so not checking its result.
88  clients_client_id_.insert(client);
89 }
90 
91 void
92 ClientHandler::addByHWAddr(const ClientPtr& client) {
93  // Sanity check.
94  if (!client) {
96  "null client in ClientHandler::addByHWAddr");
97  }
98 
99  // Assume insert will never fail so not checking its result.
100  clients_hwaddr_.insert(client);
101 }
102 
103 void
104 ClientHandler::del(const DuidPtr& duid) {
105  // Sanity check.
106  if (!duid) {
107  isc_throw(InvalidParameter, "null duid in ClientHandler::del");
108  }
109 
110  // Assume erase will never fail so not checking its result.
111  clients_client_id_.erase(duid->getDuid());
112 }
113 
114 void
115 ClientHandler::del(const HWAddrPtr& hwaddr) {
116  // Sanity checks.
117  if (!hwaddr) {
118  isc_throw(InvalidParameter, "null hwaddr in ClientHandler::del");
119  }
120  if (hwaddr->hwaddr_.empty()) {
121  isc_throw(InvalidParameter, "empty hwaddr in ClientHandler::del");
122  }
123 
124  auto key = boost::make_tuple(hwaddr->htype_, hwaddr->hwaddr_);
125  // Assume erase will never fail so not checking its result.
126  auto it = clients_hwaddr_.find(key);
127  if (it == clients_hwaddr_.end()) {
128  // Should not happen.
129  return;
130  }
131  clients_hwaddr_.erase(it);
132 }
133 
134 ClientHandler::ClientHandler()
135  : client_(), locked_client_id_(), locked_hwaddr_() {
136 }
137 
139  bool unlocked = false;
140  lock_guard<mutex> lk(mutex_);
141  if (locked_client_id_) {
142  unlocked = true;
143  unLockById();
144  }
145  if (locked_hwaddr_) {
146  unlocked = true;
147  unLockByHWAddr();
148  }
149  if (!unlocked || !client_ || !client_->cont_) {
150  return;
151  }
152  // Try to process next query. As the caller holds the mutex of
153  // the handler class the continuation will be resumed after.
154  MultiThreadingMgr& mt_mgr = MultiThreadingMgr::instance();
155  if (mt_mgr.getMode()) {
156  if (!mt_mgr.getThreadPool().addFront(client_->cont_)) {
158  }
159  }
160 }
161 
162 bool
164  // Sanity checks.
165  if (!query) {
166  isc_throw(InvalidParameter, "null query in ClientHandler::tryLock");
167  }
168  if (locked_client_id_) {
170  "already handling client-id in ClientHandler::tryLock");
171  }
172  if (locked_hwaddr_) {
174  "already handling hwaddr in ClientHandler::tryLock");
175  }
176 
177  // Get identifiers.
178  OptionPtr opt_client_id = query->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
179  DuidPtr duid;
180  if (opt_client_id) {
181  duid.reset(new ClientId(opt_client_id->getData()));
182  }
183  HWAddrPtr hwaddr = query->getHWAddr();
184  if (hwaddr && hwaddr->hwaddr_.empty()) {
185  hwaddr.reset();
186  }
187  if (!duid && !hwaddr) {
188  // Can't do something useful: cross fingers.
189  return (true);
190  }
191 
192  ClientPtr holder_id;
193  ClientPtr holder_hw;
194  Pkt4Ptr next_query_id;
195  Pkt4Ptr next_query_hw;
196  client_.reset(new Client(query, duid, hwaddr));
197 
198  {
199  lock_guard<mutex> lk(mutex_);
200  // Try first duid.
201  if (duid) {
202  // Try to acquire the by-client-id lock and return the holder
203  // when it failed.
204  holder_id = lookup(duid);
205  if (!holder_id) {
206  locked_client_id_ = duid;
207  lockById();
208  } else if (cont) {
209  next_query_id = holder_id->next_query_;
210  holder_id->next_query_ = query;
211  holder_id->cont_ = cont;
212  }
213  }
214  if (!holder_id) {
215  if (!hwaddr) {
216  return (true);
217  }
218  // Try to acquire the by-hw-addr lock and return the holder
219  // when it failed.
220  holder_hw = lookup(hwaddr);
221  if (!holder_hw) {
222  locked_hwaddr_ = hwaddr;
223  lockByHWAddr();
224  return (true);
225  } else if (cont) {
226  next_query_hw = holder_hw->next_query_;
227  holder_hw->next_query_ = query;
228  holder_hw->cont_ = cont;
229  }
230  }
231  }
232 
233  if (holder_id) {
234  // This query is a by-id duplicate so put the continuation.
235  if (cont) {
236  if (next_query_id) {
237  // Logging a warning as it is supposed to be a rare event
238  // with well behaving clients...
240  .arg(next_query_id->toText())
241  .arg(this_thread::get_id())
242  .arg(holder_id->query_->toText())
243  .arg(holder_id->thread_);
244  stats::StatsMgr::instance().addValue("pkt4-receive-drop",
245  static_cast<int64_t>(1));
246  }
247  } else {
248  // Logging a warning as it is supposed to be a rare event
249  // with well behaving clients...
251  .arg(query->toText())
252  .arg(this_thread::get_id())
253  .arg(holder_id->query_->toText())
254  .arg(holder_id->thread_);
255  stats::StatsMgr::instance().addValue("pkt4-receive-drop",
256  static_cast<int64_t>(1));
257  }
258  } else {
259  // This query is a by-hw duplicate so put the continuation.
260  if (cont) {
261  if (next_query_hw) {
262  // Logging a warning as it is supposed to be a rare event
263  // with well behaving clients...
265  .arg(next_query_hw->toText())
266  .arg(this_thread::get_id())
267  .arg(holder_hw->query_->toText())
268  .arg(holder_hw->thread_);
269  stats::StatsMgr::instance().addValue("pkt4-receive-drop",
270  static_cast<int64_t>(1));
271  }
272  } else {
273  // Logging a warning as it is supposed to be a rare event
274  // with well behaving clients...
276  .arg(query->toText())
277  .arg(this_thread::get_id())
278  .arg(holder_hw->query_->toText())
279  .arg(holder_hw->thread_);
280  stats::StatsMgr::instance().addValue("pkt4-receive-drop",
281  static_cast<int64_t>(1));
282  }
283  }
284  return (false);
285 }
286 
287 void
288 ClientHandler::lockById() {
289  // Sanity check.
290  if (!locked_client_id_) {
291  isc_throw(Unexpected, "nothing to lock in ClientHandler::lockById");
292  }
293 
294  addById(client_);
295 }
296 
297 void
298 ClientHandler::lockByHWAddr() {
299  // Sanity check.
300  if (!locked_hwaddr_) {
301  isc_throw(Unexpected,
302  "nothing to lock in ClientHandler::lockByHWAddr");
303  }
304 
305  addByHWAddr(client_);
306 }
307 
308 void
309 ClientHandler::unLockById() {
310  // Sanity check.
311  if (!locked_client_id_) {
312  isc_throw(Unexpected,
313  "nothing to unlock in ClientHandler::unLockById");
314  }
315 
316  del(locked_client_id_);
317  locked_client_id_.reset();
318 }
319 
320 void
321 ClientHandler::unLockByHWAddr() {
322  // Sanity check.
323  if (!locked_hwaddr_) {
324  isc_throw(Unexpected,
325  "nothing to unlock in ClientHandler::unLockByHWAddr");
326  }
327 
328  del(locked_hwaddr_);
329  locked_hwaddr_.reset();
330 }
331 
332 } // namespace dhcp
333 } // namespace isc
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
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.
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.
bool getMode() const
Get the multi-threading mode.
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
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-dhcp-ddns.
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.