Kea 3.3.1
dhcp6/client_handler.cc
Go to the documentation of this file.
1// Copyright (C) 2020-2025 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
10#include <dhcp6/dhcp6_log.h>
12#include <stats/stats_mgr.h>
14
15using namespace std;
16using namespace isc::util;
17using namespace isc::log;
18
19namespace isc {
20namespace dhcp {
21
22namespace {
23
24string summary(ClientHandler::ClientPtr holder) {
25 if (!holder) {
26 return ("null");
27 }
28 stringstream tmp;
29 uint8_t msg_type = holder->msg_type_;
30 tmp << "msg_type=" << Pkt6::getName(msg_type)
31 << " (" << static_cast<int>(msg_type) << ")"
32 << ", trans_id=0x" << hex << holder->transid_ << dec;
33 if (holder->relayed_) {
34 tmp << " (relayed)";
35 }
36 return (tmp.str());
37}
38
39} // end of anonymous namespace.
40
42 : thread_(this_thread::get_id()) {
43 // Sanity checks.
44 if (!query) {
45 isc_throw(InvalidParameter, "null query in ClientHandler");
46 }
47 if (!client_id) {
48 isc_throw(InvalidParameter, "null client-id in ClientHandler");
49 }
50
51 // Fill cached values.
52 duid_ = client_id->getDuid();
53 msg_type_ = query->getType();
54 transid_ = query->getTransid();
55 relayed_ = !query->relay_info_.empty();
56}
57
58mutex ClientHandler::mutex_;
59
60ClientHandler::ClientContainer ClientHandler::clients_;
61
63ClientHandler::lookup(const DuidPtr& duid) {
64 // Sanity check.
65 if (!duid) {
66 isc_throw(InvalidParameter, "null duid in ClientHandler::lookup");
67 }
68
69 auto it = clients_.find(duid->getDuid());
70 if (it == clients_.end()) {
71 return (ClientPtr());
72 }
73 return (*it);
74}
75
76void
77ClientHandler::add(const ClientPtr& client) {
78 // Sanity check.
79 if (!client) {
80 isc_throw(InvalidParameter, "null client in ClientHandler::add");
81 }
82
83 // Assume insert will never fail so not checking its result.
84 clients_.insert(client);
85}
86
87void
88ClientHandler::del(const DuidPtr& duid) {
89 // Sanity check.
90 if (!duid) {
91 isc_throw(InvalidParameter, "null duid in ClientHandler::del");
92 }
93
94 // Assume erase will never fail so not checking its result.
95 clients_.erase(duid->getDuid());
96}
97
98ClientHandler::ClientHandler() : client_(), locked_() {
99}
100
102 if (locked_) {
103 lock_guard<mutex> lk(mutex_);
104 unLock();
105 }
106}
107
108bool
110 // Sanity checks.
111 if (!query) {
112 isc_throw(InvalidParameter, "null query in ClientHandler::tryLock");
113 }
114 if (locked_) {
115 isc_throw(Unexpected, "already handling in ClientHandler::tryLock");
116 }
117
118 const DuidPtr& duid = query->getClientId();
119 if (!duid) {
120 // Can't do something useful: cross fingers.
121 return (true);
122 }
123 if (duid->getDuid().empty()) {
124 // A lot of code assumes this will never happen...
125 isc_throw(Unexpected, "empty DUID in ClientHandler::tryLock");
126 }
127 client_.reset(new Client(query, duid));
128
129 ClientPtr holder;
130 Pkt6Ptr next_query;
131 {
132 // Try to acquire the lock and return the holder when it failed.
133 lock_guard<mutex> lk(mutex_);
134 holder = lookup(duid);
135 if (!holder) {
136 locked_ = duid;
137 lock();
138 return (true);
139 }
140 // This query can be a duplicate so put the continuation.
141 if (cont) {
142 next_query = holder->next_query_;
143 holder->next_query_ = query;
144 holder->cont_ = cont;
145 }
146 }
147
148
149 if (cont) {
150 if (next_query) {
151 // Logging a warning as it is supposed to be a rare event
152 // with well behaving clients...
153 // We replaced the next query so a packet was dropped as duplicate.
155 .arg("duid[" + duid->toText() + "]")
156 .arg(next_query->toText())
157 .arg(this_thread::get_id())
158 .arg(summary(holder))
159 .arg(holder->thread_);
160 stats::StatsMgr::instance().addValue("pkt6-duplicate",
161 static_cast<int64_t>(1));
162 stats::StatsMgr::instance().addValue("pkt6-receive-drop",
163 static_cast<int64_t>(1));
164 }
165 } else {
166 // Logging a warning as it is supposed to be a rare event
167 // with well behaving clients...
168 // There is no cont so the query is dropped as duplicate.
170 .arg("duid[" + duid->toText() + "]")
171 .arg(query->toText())
172 .arg(this_thread::get_id())
173 .arg(summary(holder))
174 .arg(holder->thread_);
175 stats::StatsMgr::instance().addValue("pkt6-duplicate",
176 static_cast<int64_t>(1));
177 stats::StatsMgr::instance().addValue("pkt6-receive-drop",
178 static_cast<int64_t>(1));
179
180 }
181 return (false);
182}
183
184void
185ClientHandler::lock() {
186 // Sanity check.
187 if (!locked_) {
188 isc_throw(Unexpected, "nothing to lock in ClientHandler::lock");
189 }
190
191 add(client_);
192}
193
194void
195ClientHandler::unLock() {
196 // Sanity check.
197 if (!locked_) {
198 isc_throw(Unexpected, "nothing to unlock in ClientHandler::unLock");
199 }
200
201 del(locked_);
202 locked_.reset();
203
204 if (!client_ || !client_->cont_) {
205 return;
206 }
207
208 // Try to process next query. As the caller holds the mutex of
209 // the handler class the continuation will be resumed after.
210 MultiThreadingMgr& mt_mgr = MultiThreadingMgr::instance();
211 if (mt_mgr.getMode()) {
212 if (!mt_mgr.getThreadPool().addFront(client_->cont_)) {
214 }
215 }
216}
217
218} // namespace dhcp
219} // 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.
boost::shared_ptr< Client > ClientPtr
The type of shared pointers to clients.
bool tryLock(Pkt4Ptr query, ContinuationPtr cont=ContinuationPtr())
Tries to acquires a client.
const char * getName() const
Returns name of the DHCPv6 message.
Definition pkt6.cc:888
static StatsMgr & instance()
Statistics Manager accessor method.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
ThreadPool< std::function< void()> > & getThreadPool()
Get the dhcp thread pool.
bool getMode() const
Get the multi-threading mode.
#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
isc::log::Logger bad_packet6_logger(DHCP6_BAD_PACKET_LOGGER_NAME)
Logger for rejected packets.
Definition dhcp6_log.h:94
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
boost::shared_ptr< Continuation > ContinuationPtr
Define the type of shared pointers to continuations.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
const int DBG_DHCP6_BASIC
Debug level used to trace basic operations within the code.
Definition dhcp6_log.h:31
isc::log::Logger dhcp6_logger(DHCP6_APP_LOGGER_NAME)
Base logger for DHCPv6 server.
Definition dhcp6_log.h:88
const isc::log::MessageID DHCP6_PACKET_DROP_DUPLICATE
const isc::log::MessageID DHCP6_PACKET_QUEUE_FULL
const int DBGLVL_PKT_HANDLING
This debug level is reserved for logging the details of packet handling, such as dropping the packet ...
Defines the logger used by the top-level component of kea-lfc.
Class (aka static) types, methods and members.
uint32_t transid_
Cached transaction ID.
Client(Pkt4Ptr query, ClientIdPtr client_id, HWAddrPtr hwaddr)
Constructor.
std::vector< uint8_t > duid_
Cached binary client ID.
uint8_t msg_type_
Cached message type.
std::thread::id thread_
The ID of the thread processing the query.
bool addFront(const WorkItemPtr &item)
add a work item to the thread pool at front