Kea 2.5.7
d2_client_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2014-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 <dhcp/iface_mgr.h>
10#include <dhcp_ddns/ncr_udp.h>
12#include <dhcpsrv/dhcpsrv_log.h>
13
14#include <functional>
15#include <string>
16
17using namespace std;
18
19namespace isc {
20namespace dhcp {
21
22D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
23 name_change_sender_(), private_io_service_(),
24 registered_select_fd_(util::WatchSocket::SOCKET_NOT_VALID) {
25 // Default constructor initializes with a disabled configuration.
26}
27
29 stopSender();
30}
31
32void
34 if (ddnsEnabled()) {
38 d2_client_config_->enableUpdates(false);
39 if (name_change_sender_) {
40 stopSender();
41 }
42 }
43}
44
45void
47 if (!new_config) {
49 "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
50 }
51
52 // Don't do anything unless configuration values are actually different.
53 if (*d2_client_config_ != *new_config) {
54 // Make sure we stop sending first.
55 stopSender();
56 if (!new_config->getEnableUpdates()) {
57 // Updating has been turned off.
58 // Destroy current sender (any queued requests are tossed).
59 name_change_sender_.reset();
60 } else {
62 switch (new_config->getNcrProtocol()) {
63 case dhcp_ddns::NCR_UDP: {
64 // Instantiate a new sender.
65 new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
66 new_config->getSenderIp(),
67 new_config->getSenderPort(),
68 new_config->getServerIp(),
69 new_config->getServerPort(),
70 new_config->getNcrFormat(),
71 *this,
72 new_config->getMaxQueueSize()));
73 break;
74 }
75 default:
76 // In theory you can't get here.
77 isc_throw(D2ClientError, "Invalid sender Protocol: "
78 << new_config->getNcrProtocol());
79 break;
80 }
81
82 // Transfer queued requests from previous sender to the new one.
88 if (name_change_sender_) {
89 new_sender->assumeQueue(*name_change_sender_);
90 }
91
92 // Replace the old sender with the new one.
93 name_change_sender_ = new_sender;
94 }
95 }
96
97 // Update the configuration.
98 d2_client_config_ = new_config;
100 .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
101 "DHCP_DDNS updates enabled");
102}
103
104bool
106 return (d2_client_config_->getEnableUpdates());
107}
108
111 return (d2_client_config_);
112}
113
114void
115D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
116 bool& server_s, bool& server_n,
117 const DdnsParams& ddns_params) const {
118 // Per RFC 4702 & 4704, the client N and S flags allow the client to
119 // request one of three options:
120 //
121 // N flag S flag Option
122 // ------------------------------------------------------------------
123 // 0 0 client wants to do forward updates (section 3.2)
124 // 0 1 client wants server to do forward updates (section 3.3)
125 // 1 0 client wants no one to do updates (section 3.4)
126 // 1 1 invalid combination
127 // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
128 //
129 // Make a bit mask from the client's flags and use it to set the response
130 // flags accordingly.
131 const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
132
133 switch (mask) {
134 case 0:
135 if (!ddns_params.getEnableUpdates()) {
136 server_s = false;
137 server_n = true;
138 } else {
139 // If updates are enabled and we are overriding client delegation
140 // then S flag should be true. N-flag should be false.
141 server_s = ddns_params.getOverrideClientUpdate();
142 server_n = false;
143 }
144 break;
145
146 case 1:
147 server_s = ddns_params.getEnableUpdates();
148 server_n = !server_s;
149 break;
150
151 case 2:
152 // If updates are enabled and we are overriding "no updates" then
153 // S flag should be true.
154 server_s = (ddns_params.getEnableUpdates() &&
155 ddns_params.getOverrideNoUpdate());
156 server_n = !server_s;
157 break;
158
159 default:
160 // RFCs declare this an invalid combination.
162 "Invalid client FQDN - N and S cannot both be 1");
163 break;
164 }
165}
166
167std::string
169 const DdnsParams& ddns_params,
170 const bool trailing_dot) const {
171 std::string hostname = address.toText();
172 std::replace(hostname.begin(), hostname.end(),
173 (address.isV4() ? '.' : ':'), '-');
174
175 std::ostringstream gen_name;
176 gen_name << ddns_params.getGeneratedPrefix() << "-" << hostname;
177 return (qualifyName(gen_name.str(), ddns_params, trailing_dot));
178}
179
180
181std::string
182D2ClientMgr::qualifyName(const std::string& partial_name,
183 const DdnsParams& ddns_params,
184 const bool trailing_dot) const {
185 std::ostringstream gen_name;
186
187 gen_name << partial_name;
188 std::string suffix = ddns_params.getQualifyingSuffix();
189 bool suffix_present = true;
190 if (!suffix.empty()) {
191 std::string str = gen_name.str();
192 auto suffix_rit = suffix.rbegin();
193 if (*suffix_rit == '.') {
194 ++suffix_rit;
195 }
196
197 auto gen_rit = str.rbegin();
198 if (*gen_rit == '.') {
199 ++gen_rit;
200 }
201
202 while (suffix_rit != suffix.rend()) {
203 if ((gen_rit == str.rend()) || (*suffix_rit != *gen_rit)) {
204 // They don't match.
205 suffix_present = false;
206 break;
207 }
208
209 ++suffix_rit;
210 ++gen_rit;
211 }
212
213 // Catch the case where name has suffix embedded.
214 // input: foo.barexample.com suffix: example.com
215 if ((suffix_present) && (suffix_rit == suffix.rend())) {
216 if ((gen_rit != str.rend()) && (*gen_rit != '.')) {
217 suffix_present = false;
218 }
219 }
220
221 if (!suffix_present) {
222 size_t len = str.length();
223 if ((len > 0) && (str[len - 1] != '.')) {
224 gen_name << ".";
225 }
226
227 gen_name << suffix;
228 }
229 }
230
231 std::string str = gen_name.str();
232 size_t len = str.length();
233
234 if (trailing_dot) {
235 // If trailing dot should be added but there is no trailing dot,
236 // append it.
237 if ((len > 0) && (str[len - 1] != '.')) {
238 gen_name << ".";
239 }
240
241 } else {
242 // If the trailing dot should not be appended but it is present,
243 // remove it.
244 if ((len > 0) && (str[len - 1] == '.')) {
245 gen_name.str(str.substr(0,len-1));
246 }
247
248 }
249
250 return (gen_name.str());
251}
252
253void
255 if (amSending()) {
256 return;
257 }
258
259 // Create a our own service instance when we are not being multiplexed
260 // into an external service..
261 private_io_service_.reset(new asiolink::IOService());
262 startSender(error_handler, *private_io_service_);
264 .arg(d2_client_config_->toText());
265}
266
267void
269 isc::asiolink::IOService& io_service) {
270 if (amSending()) {
271 return;
272 }
273
274 if (!name_change_sender_) {
275 isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
276 }
277
278 if (!error_handler) {
279 isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
280 }
281
282 // Set the error handler.
283 client_error_handler_ = error_handler;
284
285 // Start the sender on the given service.
286 name_change_sender_->startSending(io_service);
287
288 // Register sender's select-fd with IfaceMgr.
289 // We need to remember the fd that is registered so we can unregister later.
290 // IO error handling in the sender may alter its select-fd.
291 registered_select_fd_ = name_change_sender_->getSelectFd();
292 IfaceMgr::instance().addExternalSocket(registered_select_fd_,
293 std::bind(&D2ClientMgr::runReadyIO,
294 this));
295}
296
297bool
299 return (name_change_sender_ && name_change_sender_->amSending());
300}
301
302void
305 if (registered_select_fd_ != util::WatchSocket::SOCKET_NOT_VALID) {
306 IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
307 registered_select_fd_ = util::WatchSocket::SOCKET_NOT_VALID;
308 }
309
310 // If its not null, call stop.
311 if (amSending()) {
312 name_change_sender_->stopSending();
314 }
315}
316
317void
319 if (!amSending()) {
320 // This is programmatic error so bust them for it.
321 isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
322 }
323
324 try {
325 name_change_sender_->sendRequest(ncr);
326 } catch (const std::exception& ex) {
328 .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
330 }
331}
332
333void
335 Result result,
337 // Handler is mandatory to enter send mode but test it just to be safe.
338 if (!client_error_handler_) {
340 } else {
341 // Handler is not supposed to throw, but catch just in case.
342 try {
343 (client_error_handler_)(result, ncr);
344 } catch (const std::exception& ex) {
346 .arg(ex.what());
347 }
348 }
349}
350
351size_t
353 if (!name_change_sender_) {
354 isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
355 }
356
357 return(name_change_sender_->getQueueSize());
358}
359
360size_t
362 if (!name_change_sender_) {
363 isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
364 }
365
366 return(name_change_sender_->getQueueMaxSize());
367}
368
369
370
372D2ClientMgr::peekAt(const size_t index) const {
373 if (!name_change_sender_) {
374 isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
375 }
376
377 return (name_change_sender_->peekAt(index));
378}
379
380void
382 if (!name_change_sender_) {
383 isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
384 }
385
386 name_change_sender_->clearSendQueue();
387}
388
389void
394 DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
395 } else {
396 invokeClientErrorHandler(result, ncr);
397 }
398}
399
400int
402 if (!amSending()) {
403 isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
404 " not in send mode");
405 }
406
407 return (name_change_sender_->getSelectFd());
408}
409
410void
412 if (!name_change_sender_) {
413 // This should never happen.
414 isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
415 " name_change_sender is null");
416 }
417
418 name_change_sender_->runReadyIO();
419}
420
421}; // namespace dhcp
422
423}; // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Acts as a storage vault for D2 client configuration.
Definition: d2_client_cfg.h:57
An exception that is thrown if an error occurs while configuring the D2 DHCP DDNS client.
Definition: d2_client_cfg.h:34
void invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Calls the client's error handler.
std::string generateFqdn(const asiolink::IOAddress &address, const DdnsParams &ddns_params, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
void analyzeFqdn(const bool client_s, const bool client_n, bool &server_s, bool &server_n, const DdnsParams &ddns_params) const
Determines server flags based on configuration and client flags.
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
void suspendUpdates()
Suspends sending requests.
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
size_t getQueueSize() const
Returns the number of NCRs queued for transmission.
void clearQueue()
Removes all NCRs queued for transmission.
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
void setD2ClientConfig(D2ClientConfigPtr &new_config)
Updates the DHCP-DDNS client configuration to the given value.
bool amSending() const
Returns true if the sender is in send mode, false otherwise.
int getSelectFd()
Fetches the sender's select-fd.
void runReadyIO()
Processes sender IO events.
~D2ClientMgr()
Destructor.
D2ClientMgr()
Constructor.
size_t getQueueMaxSize() const
Returns the maximum number of NCRs allowed in the queue.
virtual void operator()(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR sender callback.
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the nth NCR queued for transmission.
std::string qualifyName(const std::string &partial_name, const DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:48
std::string getGeneratedPrefix() const
Returns the Prefix Kea should use when generating domain-names.
Definition: srv_config.cc:1066
std::string getQualifyingSuffix() const
Returns the suffix Kea should use when to qualify partial domain-names.
Definition: srv_config.cc:1075
bool getOverrideNoUpdate() const
Returns whether or not Kea should perform updates, even if client requested no updates.
Definition: srv_config.cc:1040
bool getEnableUpdates() const
Returns whether or not DHCP DDNS updating is enabled.
Definition: srv_config.cc:1031
bool getOverrideClientUpdate() const
Returns whether or not Kea should perform updates, even if client requested delegation.
Definition: srv_config.cc:1048
void deleteExternalSocket(int socketfd)
Deletes external socket.
Definition: iface_mgr.cc:352
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:54
void addExternalSocket(int socketfd, SocketCallback callback)
Adds external socket and a callback.
Definition: iface_mgr.cc:329
Abstract interface for sending NameChangeRequests.
Definition: ncr_io.h:466
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:476
Provides the ability to send NameChangeRequests via UDP socket.
Definition: ncr_udp.h:441
static const int SOCKET_NOT_VALID
Value used to signify an invalid descriptor.
Definition: watch_socket.h:50
Defines the D2ClientMgr class.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< NameChangeSender > NameChangeSenderPtr
Defines a smart pointer to an instance of a sender.
Definition: ncr_io.h:848
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:242
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
const isc::log::MessageID DHCPSRV_DHCP_DDNS_NCR_SENT
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SENDER_STOPPED
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES
const isc::log::MessageID DHCPSRV_CFGMGR_CFG_DHCP_DDNS
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SENDER_STARTED
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
const isc::log::MessageID DHCPSRV_DHCP_DDNS_NCR_REJECTED
std::function< void(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)> D2ClientErrorHandler
Defines the type for D2 IO error handler.
Definition: d2_client_mgr.h:44
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
const isc::log::MessageID DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION
const isc::log::MessageID DHCPSRV_DHCP_DDNS_HANDLER_NULL
Defines the logger used by the top-level component of kea-lfc.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.