Kea 3.1.1
ddns_tuning_callouts.cc
Go to the documentation of this file.
1// Copyright (C) 2022-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>
9#include <ddns_tuning.h>
10#include <ddns_tuning_log.h>
12#include <dhcp/option_string.h>
15#include <dhcpsrv/cfgmgr.h>
16#include <dhcpsrv/subnet.h>
17#include <dhcp/pkt4.h>
18#include <dhcp/pkt6.h>
19#include <hooks/hooks.h>
20#include <process/daemon.h>
21#include <string>
22
23namespace isc {
24namespace ddns_tuning {
25
27
28} // end of namespace ddns_tuning
29} // end of namespace isc
30
31using namespace isc;
32using namespace isc::log;
33using namespace isc::data;
34using namespace isc::db;
35using namespace isc::dhcp;
36using namespace isc::ddns_tuning;
37using namespace isc::hooks;
38using namespace isc::process;
39using namespace std;
40
41// Functions accessed by the hooks framework use C linkage to avoid the name
42// mangling that accompanies use of the C++ compiler as well as to avoid
43// issues related to namespaces.
44extern "C" {
45
58 // Only repopulate the cache if CB updated subnets. Global expression is done
59 // via hook parameter, so that can only happen on a reconfigure.
60 // Audit entries track by StampedElement::id and there is no mapping currently
61 // in Subnet4Collection between those and SubnetIDs. There is no efficient way
62 // to select added or updated subnets, nor to identify those that were deleted,
63 // so we'll just repopulate the entire cache if there were any subnet changes.
64 AuditEntryCollectionPtr audit_entries;
65 handle.getArgument("audit_entries", audit_entries);
66
67 auto const& object_type_idx = audit_entries->get<AuditEntryObjectTypeTag>();
68 auto range = object_type_idx.equal_range("dhcp4_subnet");
69 if (std::distance(range.first, range.second)) {
70 return (impl->repopulateCache(CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()));
71 }
72
73 return (0);
74}
75
87 SrvConfigPtr cfg;
88 handle.getArgument("server_config", cfg);
89 int result = impl->repopulateCache(cfg->getCfgSubnets4());
90 if (result) {
92 const string error("Errors were detected in the ddns tuning hooks library configuration.");
93 handle.setArgument("error", error);
94 }
95 return (result);
96}
97
113 // Catch insanity.
114 if (!impl) {
115 isc_throw(Unexpected, "ddns4_update called with no impl!");
116 }
117
118 // Punt if set to DROP.
120 if (status == CalloutHandle::NEXT_STEP_DROP) {
121 return (0);
122 }
123
124 // Get the parameters we currently use.
125 string hostname;
126 Pkt4Ptr query;
127 Pkt4Ptr response;
128 ConstSubnet4Ptr subnet;
129 DdnsParamsPtr ddns_params;
130
131 handle.getArgument("hostname", hostname);
132 handle.getArgument("query4", query);
133 handle.getArgument("response4", response);
134 handle.getArgument("subnet4", subnet);
135 handle.getArgument("ddns-params", ddns_params);
136
137 try {
138 // Attempt to calculate the host name. If the result is a not empty and
139 // different from the original qualify, update the response, and then
140 // the hostname argument.
141 string calc_hostname = impl->calculateHostname(query, subnet);
142 if (!calc_hostname.empty() && (calc_hostname != hostname)) {
143 // Count the number of DNS labels in the name.
144 auto label_count = OptionDataTypeUtil::getLabelCount(calc_hostname);
145
146 // If there are two labels, it means that we have an unqualified name
147 // and we need to add the qualifying suffix.
148 if (label_count == 2) {
150 calc_hostname = d2_mgr.qualifyName(calc_hostname, *ddns_params, false);
151 }
152
154 .arg(hostname)
155 .arg(calc_hostname)
156 .arg(query->getLabel());
157
158 // Send the new name back to the caller.
159 handle.setArgument("hostname", calc_hostname);
160 }
161 } catch (const exception& ex) {
163 .arg(query->getLabel()).arg(ex.what());
164 return (1);
165 }
166
167 // Set direction flags to false if the client belongs to "SKIP_DDNS"
168 if (query->inClass("SKIP_DDNS")) {
170 .arg(query->getLabel());
171 handle.setArgument("fwd-update", false);
172 handle.setArgument("rev-update", false);
173 }
174
175 return (0);
176}
177
190 // Only repopulate the cache if CB updated subnets. Global expression is done
191 // via hook parameter, so that can only happen on a reconfigure.
192 // Audit entries track by StampedElement::id and there is no mapping currently
193 // in Subnet6Collection between those and SubnetIDs. There is no efficient way
194 // to select added or updated subnets, nor to identify those that were deleted,
195 // so we'll just repopulate the entire cache if there were any subnet changes.
196 AuditEntryCollectionPtr audit_entries;
197 handle.getArgument("audit_entries", audit_entries);
198
199 auto const& object_type_idx = audit_entries->get<AuditEntryObjectTypeTag>();
200 auto range = object_type_idx.equal_range("dhcp6_subnet");
201 if (std::distance(range.first, range.second)) {
202 return (impl->repopulateCache(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()));
203 }
204
205 return (0);
206}
207
219 SrvConfigPtr cfg;
220 handle.getArgument("server_config", cfg);
221 int result = impl->repopulateCache(cfg->getCfgSubnets6());
222 if (result) {
224 const string error("Errors were detected in the ddns tuning hooks library configuration.");
225 handle.setArgument("error", error);
226 }
227 return (result);
228}
229
245 // Catch insanity.
246 if (!impl) {
247 isc_throw(Unexpected, "ddns6_update called with no impl!");
248 }
249
250 // Punt if set to DROP.
252 if (status == CalloutHandle::NEXT_STEP_DROP) {
253 return (0);
254 }
255
256 // Get the parameters we currently use.
257 string hostname;
258 Pkt6Ptr query;
259 Pkt6Ptr response;
260 ConstSubnet6Ptr subnet;
261 DdnsParamsPtr ddns_params;
262
263 handle.getArgument("hostname", hostname);
264 handle.getArgument("query6", query);
265 handle.getArgument("response6", response);
266 handle.getArgument("subnet6", subnet);
267 handle.getArgument("ddns-params", ddns_params);
268
269 try {
270 // Attempt to calculate the host name. If the result is a not empty and
271 // different from the original qualify, update the response, and then
272 // the hostname argument.
273 string calc_hostname = impl->calculateHostname(query, subnet);
274 if (!calc_hostname.empty() && (calc_hostname != hostname)) {
275 // Count the number of DNS labels in the name.
276 auto label_count = OptionDataTypeUtil::getLabelCount(calc_hostname);
277
278 // If there are two labels, it means that we have an unqualified name
279 // and we need to add the qualifying suffix.
280 if (label_count == 2) {
282 calc_hostname = d2_mgr.qualifyName(calc_hostname, *ddns_params, true);
283 }
284
286 .arg(hostname)
287 .arg(calc_hostname)
288 .arg(query->getLabel());
289
290 // Send the new name back to the caller.
291 handle.setArgument("hostname", calc_hostname);
292 }
293 } catch (const exception& ex) {
295 .arg(query->getLabel()).arg(ex.what());
296 return (1);
297 }
298
299 // Set direction flags to false if the client belongs to "SKIP_DDNS"
300 if (query->inClass("SKIP_DDNS")) {
302 .arg(query->getLabel());
303 handle.setArgument("fwd-update", false);
304 handle.setArgument("rev-update", false);
305 }
306
307 return (0);
308}
309
314int load(LibraryHandle& handle) {
315 try {
316 // Make the hook library not loadable by d2 or ca.
317 uint16_t family = CfgMgr::instance().getFamily();
318 const string& proc_name = Daemon::getProcName();
319 if (family == AF_INET) {
320 if (proc_name != "kea-dhcp4") {
321 isc_throw(isc::Unexpected, "Bad process name: " << proc_name
322 << ", expected kea-dhcp4");
323 }
324 } else {
325 if (proc_name != "kea-dhcp6") {
326 isc_throw(isc::Unexpected, "Bad process name: " << proc_name
327 << ", expected kea-dhcp6");
328 }
329 }
330
331 impl.reset(new DdnsTuningImpl(family));
332 ConstElementPtr json = handle.getParameters();
333 impl->configure(json);
334 } catch (const exception& ex) {
336 .arg(ex.what());
337 return (1);
338 }
339
341 return (0);
342}
343
347int unload() {
348 impl.reset();
350 return (0);
351}
352
357 return (1);
358}
359
360} // end extern "C"
CalloutNextStep
Specifies allowed next steps.
@ NEXT_STEP_DROP
drop the packet
A generic exception that is thrown when an unexpected error condition occurs.
DDNS Tuning implementation.
Definition ddns_tuning.h:37
D2ClientMgr & getD2ClientMgr()
Fetches the DHCP-DDNS manager.
Definition cfgmgr.cc:69
uint16_t getFamily() const
Returns address family.
Definition cfgmgr.h:246
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
D2ClientMgr isolates Kea from the details of being a D2 client.
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.
static unsigned int getLabelCount(const std::string &text_name)
Return the number of labels in the Name.
Per-packet callout handle.
@ NEXT_STEP_DROP
drop the packet
CalloutNextStep getStatus() const
Returns the next processing step.
void setStatus(const CalloutNextStep next)
Sets the next processing step.
void getArgument(const std::string &name, T &value) const
Get argument.
void setArgument(const std::string &name, T value)
Set argument.
isc::data::ConstElementPtr getParameters()
Get configuration parameter common code.
static std::string getProcName()
returns the process name This value is used as when forming the default PID file name
Definition daemon.cc:151
This file contains several functions and constants that are used for handling commands and responses ...
int dhcp4_srv_configured(CalloutHandle &handle)
This callout is called at the "dhcp4_srv_configured" hook.
int ddns4_update(CalloutHandle &handle)
This callout is called at the "ddns4_update" hook.
int multi_threading_compatible()
This function is called to retrieve the multi-threading compatibility.
int unload()
This function is called when the library is unloaded.
int dhcp6_srv_configured(CalloutHandle &handle)
This callout is called at the "dhcp6_srv_configured" hook.
int ddns6_update(CalloutHandle &handle)
This callout is called at the "ddns6_update" hook.
int cb4_updated(CalloutHandle &handle)
This callout is called at the "cb4_updated" hook.
int cb6_updated(CalloutHandle &handle)
This callout is called at the "cb6_updated" hook.
int load(LibraryHandle &handle)
This function is called when the library is loaded.
const isc::log::MessageID DDNS_TUNING_LOAD_ERROR
const isc::log::MessageID DDNS_TUNING_UNLOAD
const isc::log::MessageID DDNS_TUNING4_CALCULATED_HOSTNAME
const isc::log::MessageID DDNS_TUNING6_SKIPPING_DDNS
const isc::log::MessageID DDNS_TUNING4_PROCESS_ERROR
const isc::log::MessageID DDNS_TUNING4_SKIPPING_DDNS
const isc::log::MessageID DDNS_TUNING6_PROCESS_ERROR
const isc::log::MessageID DDNS_TUNING_LOAD_OK
const isc::log::MessageID DDNS_TUNING6_CALCULATED_HOSTNAME
#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_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
@ error
Definition db_log.h:118
boost::shared_ptr< AuditEntryCollection > AuditEntryCollectionPtr
DdnsTuningImplPtr impl
isc::log::Logger ddns_tuning_logger("ddns-tuning-hooks")
boost::shared_ptr< DdnsTuningImpl > DdnsTuningImplPtr
The type of shared pointers to DDNS Tuning implementations.
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:458
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:556
boost::shared_ptr< DdnsParams > DdnsParamsPtr
Defines a pointer for DdnsParams instances.
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Defines the logger used by the top-level component of kea-lfc.
Tag used to access index by object type.