Kea 3.3.3
ddns_tuning_callouts.cc
Go to the documentation of this file.
1// Copyright (C) 2022-2026 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
15#include <dhcp/option_string.h>
16#include <dhcp/pkt4.h>
17#include <dhcp/pkt6.h>
18#include <dhcpsrv/cfgmgr.h>
19#include <dhcpsrv/subnet.h>
20#include <hooks/hooks.h>
21#include <process/daemon.h>
22
23#include <string>
24
25namespace isc {
26namespace ddns_tuning {
27
29
30} // end of namespace ddns_tuning
31} // end of namespace isc
32
33using namespace isc;
34using namespace isc::log;
35using namespace isc::data;
36using namespace isc::db;
37using namespace isc::dhcp;
38using namespace isc::ddns_tuning;
39using namespace isc::hooks;
40using namespace isc::process;
41using namespace std;
42
43// Functions accessed by the hooks framework use C linkage to avoid the name
44// mangling that accompanies use of the C++ compiler as well as to avoid
45// issues related to namespaces.
46extern "C" {
47
60 // Only repopulate the cache if CB updated subnets. Global expression is done
61 // via hook parameter, so that can only happen on a reconfigure.
62 // Audit entries track by StampedElement::id and there is no mapping currently
63 // in Subnet4Collection between those and SubnetIDs. There is no efficient way
64 // to select added or updated subnets, nor to identify those that were deleted,
65 // so we'll just repopulate the entire cache if there were any subnet changes.
66 AuditEntryCollectionPtr audit_entries;
67 handle.getArgument("audit_entries", audit_entries);
68
69 auto const& object_type_idx = audit_entries->get<AuditEntryObjectTypeTag>();
70 auto range = object_type_idx.equal_range("dhcp4_subnet");
71 if (std::distance(range.first, range.second)) {
72 return (impl->repopulateCache(CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()));
73 }
74
75 return (0);
76}
77
89 SrvConfigPtr cfg;
90 handle.getArgument("server_config", cfg);
91 int result = impl->repopulateCache(cfg->getCfgSubnets4());
92 if (result) {
94 const string error("Errors were detected in the ddns tuning hooks library configuration.");
95 handle.setArgument("error", error);
96 }
97 return (result);
98}
99
115 // Catch insanity.
116 if (!impl) {
117 isc_throw(Unexpected, "ddns4_update called with no impl!");
118 }
119
120 // Punt if set to DROP.
122 if (status == CalloutHandle::NEXT_STEP_DROP) {
123 return (0);
124 }
125
126 // Get the parameters we currently use.
127 string hostname;
128 Pkt4Ptr query;
129 Pkt4Ptr response;
130 ConstSubnet4Ptr subnet;
131 DdnsParamsPtr ddns_params;
132
133 handle.getArgument("hostname", hostname);
134 handle.getArgument("query4", query);
135 handle.getArgument("response4", response);
136 handle.getArgument("subnet4", subnet);
137 handle.getArgument("ddns-params", ddns_params);
138
139 try {
140 // Attempt to calculate the host name. If the result is a not empty and
141 // different from the original qualify, update the response, and then
142 // the hostname argument.
143 string calc_hostname = impl->calculateHostname(query, subnet);
144 if (!calc_hostname.empty() && (calc_hostname != hostname)) {
145 // Count the number of DNS labels in the name.
146 auto label_count = OptionDataTypeUtil::getLabelCount(calc_hostname);
147
148 // If there are two labels, it means that we have an unqualified name
149 // and we need to add the qualifying suffix.
150 if (label_count == 2) {
152 calc_hostname = d2_mgr.qualifyName(calc_hostname, *ddns_params, false);
153 }
154
156 .arg(hostname)
157 .arg(calc_hostname)
158 .arg(query->getLabel());
159
160 // Send the new name back to the caller.
161 handle.setArgument("hostname", calc_hostname);
162 }
163 } catch (const exception& ex) {
165 .arg(query->getLabel()).arg(ex.what());
166 return (1);
167 }
168
169 // Set direction flags to false if the client belongs to "SKIP_DDNS"
170 if (query->inClass("SKIP_DDNS")) {
172 .arg(query->getLabel());
173 handle.setArgument("fwd-update", false);
174 handle.setArgument("rev-update", false);
175 }
176
177 return (0);
178}
179
192 // Only repopulate the cache if CB updated subnets. Global expression is done
193 // via hook parameter, so that can only happen on a reconfigure.
194 // Audit entries track by StampedElement::id and there is no mapping currently
195 // in Subnet6Collection between those and SubnetIDs. There is no efficient way
196 // to select added or updated subnets, nor to identify those that were deleted,
197 // so we'll just repopulate the entire cache if there were any subnet changes.
198 AuditEntryCollectionPtr audit_entries;
199 handle.getArgument("audit_entries", audit_entries);
200
201 auto const& object_type_idx = audit_entries->get<AuditEntryObjectTypeTag>();
202 auto range = object_type_idx.equal_range("dhcp6_subnet");
203 if (std::distance(range.first, range.second)) {
204 return (impl->repopulateCache(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()));
205 }
206
207 return (0);
208}
209
221 SrvConfigPtr cfg;
222 handle.getArgument("server_config", cfg);
223 int result = impl->repopulateCache(cfg->getCfgSubnets6());
224 if (result) {
226 const string error("Errors were detected in the ddns tuning hooks library configuration.");
227 handle.setArgument("error", error);
228 }
229 return (result);
230}
231
247 // Catch insanity.
248 if (!impl) {
249 isc_throw(Unexpected, "ddns6_update called with no impl!");
250 }
251
252 // Punt if set to DROP.
254 if (status == CalloutHandle::NEXT_STEP_DROP) {
255 return (0);
256 }
257
258 // Get the parameters we currently use.
259 string hostname;
260 Pkt6Ptr query;
261 Pkt6Ptr response;
262 ConstSubnet6Ptr subnet;
263 DdnsParamsPtr ddns_params;
264
265 handle.getArgument("hostname", hostname);
266 handle.getArgument("query6", query);
267 handle.getArgument("response6", response);
268 handle.getArgument("subnet6", subnet);
269 handle.getArgument("ddns-params", ddns_params);
270
271 try {
272 // Attempt to calculate the host name. If the result is a not empty and
273 // different from the original qualify, update the response, and then
274 // the hostname argument.
275 string calc_hostname = impl->calculateHostname(query, subnet);
276 if (!calc_hostname.empty() && (calc_hostname != hostname)) {
277 // Count the number of DNS labels in the name.
278 auto label_count = OptionDataTypeUtil::getLabelCount(calc_hostname);
279
280 // If there are two labels, it means that we have an unqualified name
281 // and we need to add the qualifying suffix.
282 if (label_count == 2) {
284 calc_hostname = d2_mgr.qualifyName(calc_hostname, *ddns_params, true);
285 }
286
288 .arg(hostname)
289 .arg(calc_hostname)
290 .arg(query->getLabel());
291
292 // Send the new name back to the caller.
293 handle.setArgument("hostname", calc_hostname);
294 }
295 } catch (const exception& ex) {
297 .arg(query->getLabel()).arg(ex.what());
298 return (1);
299 }
300
301 // Set direction flags to false if the client belongs to "SKIP_DDNS"
302 if (query->inClass("SKIP_DDNS")) {
304 .arg(query->getLabel());
305 handle.setArgument("fwd-update", false);
306 handle.setArgument("rev-update", false);
307 }
308
309 return (0);
310}
311
316int load(LibraryHandle& handle) {
317 try {
318 // Make the hook library not loadable by d2.
319 uint16_t family = CfgMgr::instance().getFamily();
320 const string& proc_name = Daemon::getProcName();
321 if (family == AF_INET) {
322 if (proc_name != "kea-dhcp4") {
323 isc_throw(isc::Unexpected, "Bad process name: " << proc_name
324 << ", expected kea-dhcp4");
325 }
326 } else {
327 if (proc_name != "kea-dhcp6") {
328 isc_throw(isc::Unexpected, "Bad process name: " << proc_name
329 << ", expected kea-dhcp6");
330 }
331 }
332
333 impl.reset(new DdnsTuningImpl(family));
334 ConstElementPtr json = handle.getParameters();
335 impl->configure(json);
336 } catch (const exception& ex) {
338 .arg(ex.what());
339 return (1);
340 }
341
343 return (0);
344}
345
349int unload() {
350 impl.reset();
352 return (0);
353}
354
359 return (1);
360}
361
362} // 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:156
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:30
@ error
Definition db_log.h:127
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:620
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:455
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.