Kea  2.3.5-git
dhcp6/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 <dhcp6/client_handler.h>
10 #include <dhcp6/dhcp6_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(Pkt6Ptr query, DuidPtr client_id)
23  : query_(query), thread_(this_thread::get_id()) {
24  // Sanity checks.
25  if (!query) {
26  isc_throw(InvalidParameter, "null query in ClientHandler");
27  }
28  if (!client_id) {
29  isc_throw(InvalidParameter, "null client-id in ClientHandler");
30  }
31 
32  duid_ = client_id->getDuid();
33 }
34 
35 mutex ClientHandler::mutex_;
36 
37 ClientHandler::ClientContainer ClientHandler::clients_;
38 
39 ClientHandler::ClientPtr
40 ClientHandler::lookup(const DuidPtr& duid) {
41  // Sanity check.
42  if (!duid) {
43  isc_throw(InvalidParameter, "null duid in ClientHandler::lookup");
44  }
45 
46  auto it = clients_.find(duid->getDuid());
47  if (it == clients_.end()) {
48  return (ClientPtr());
49  }
50  return (*it);
51 }
52 
53 void
54 ClientHandler::add(const ClientPtr& client) {
55  // Sanity check.
56  if (!client) {
57  isc_throw(InvalidParameter, "null client in ClientHandler::add");
58  }
59 
60  // Assume insert will never fail so not checking its result.
61  clients_.insert(client);
62 }
63 
64 void
65 ClientHandler::del(const DuidPtr& duid) {
66  // Sanity check.
67  if (!duid) {
68  isc_throw(InvalidParameter, "null duid in ClientHandler::del");
69  }
70 
71  // Assume erase will never fail so not checking its result.
72  clients_.erase(duid->getDuid());
73 }
74 
75 ClientHandler::ClientHandler() : client_(), locked_() {
76 }
77 
79  if (locked_) {
80  lock_guard<mutex> lk(mutex_);
81  unLock();
82  }
83 }
84 
85 bool
87  // Sanity checks.
88  if (!query) {
89  isc_throw(InvalidParameter, "null query in ClientHandler::tryLock");
90  }
91  if (locked_) {
92  isc_throw(Unexpected, "already handling in ClientHandler::tryLock");
93  }
94 
95  const DuidPtr& duid = query->getClientId();
96  if (!duid) {
97  // Can't do something useful: cross fingers.
98  return (true);
99  }
100  if (duid->getDuid().empty()) {
101  // A lot of code assumes this will never happen...
102  isc_throw(Unexpected, "empty DUID in ClientHandler::tryLock");
103  }
104 
105  ClientPtr holder;
106  Pkt6Ptr next_query;
107  client_.reset(new Client(query, duid));
108 
109  {
110  // Try to acquire the lock and return the holder when it failed.
111  lock_guard<mutex> lk(mutex_);
112  holder = lookup(duid);
113  if (!holder) {
114  locked_ = duid;
115  lock();
116  return (true);
117  }
118  // This query can be a duplicate so put the continuation.
119  if (cont) {
120  next_query = holder->next_query_;
121  holder->next_query_ = query;
122  holder->cont_ = cont;
123  }
124  }
125 
126  if (cont) {
127  if (next_query) {
128  // Logging a warning as it is supposed to be a rare event
129  // with well behaving clients...
131  .arg(next_query->toText())
132  .arg(this_thread::get_id())
133  .arg(holder->query_->toText())
134  .arg(holder->thread_);
135  stats::StatsMgr::instance().addValue("pkt6-receive-drop",
136  static_cast<int64_t>(1));
137  }
138  } else {
139  // Logging a warning as it is supposed to be a rare event
140  // with well behaving clients...
142  .arg(query->toText())
143  .arg(this_thread::get_id())
144  .arg(holder->query_->toText())
145  .arg(holder->thread_);
146  stats::StatsMgr::instance().addValue("pkt6-receive-drop",
147  static_cast<int64_t>(1));
148  }
149  return (false);
150 }
151 
152 void
153 ClientHandler::lock() {
154  // Sanity check.
155  if (!locked_) {
156  isc_throw(Unexpected, "nothing to lock in ClientHandler::lock");
157  }
158 
159  add(client_);
160 }
161 
162 void
163 ClientHandler::unLock() {
164  // Sanity check.
165  if (!locked_) {
166  isc_throw(Unexpected, "nothing to unlock in ClientHandler::unLock");
167  }
168 
169  del(locked_);
170  locked_.reset();
171 
172  if (!client_ || !client_->cont_) {
173  return;
174  }
175 
176  // Try to process next query. As the caller holds the mutex of
177  // the handler class the continuation will be resumed after.
178  MultiThreadingMgr& mt_mgr = MultiThreadingMgr::instance();
179  if (mt_mgr.getMode()) {
180  if (!mt_mgr.getThreadPool().addFront(client_->cont_)) {
182  }
183  }
184 }
185 
186 } // namespace dhcp
187 } // namespace isc
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
const isc::log::MessageID DHCP6_PACKET_DROP_DUPLICATE
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
const int DBG_DHCP6_BASIC
Debug level used to trace basic operations within the code.
Definition: dhcp6_log.h:31
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
const isc::log::MessageID DHCP6_PACKET_QUEUE_FULL
static StatsMgr & instance()
Statistics Manager accessor method.
bool addFront(const WorkItemPtr &item)
add a work item to the thread pool at front
Definition: thread_pool.h:105
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
Definition: edns.h:19
ClientHandler()
Public interface.
A generic exception that is thrown when an unexpected error condition occurs.
virtual ~ClientHandler()
Destructor.
bool getMode() const
Get the multi-threading mode.
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.
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
isc::log::Logger bad_packet6_logger(DHCP6_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition: dhcp6_log.h:94
boost::shared_ptr< Continuation > ContinuationPtr
Define the type of shared pointers to continuations.
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition: dhcp6_log.h:88