Kea 3.2.0-git
cb_ctl_dhcp4.cc
Go to the documentation of this file.
1// Copyright (C) 2019-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>
9#include <dhcpsrv/cfgmgr.h>
11#include <dhcpsrv/dhcpsrv_log.h>
12#include <dhcpsrv/host_mgr.h>
15#include <hooks/hooks_manager.h>
16#include <boost/foreach.hpp>
17
18using namespace isc::db;
19using namespace isc::data;
20using namespace isc::process;
21using namespace isc::hooks;
22
23namespace {
24
26struct CbCtlHooks {
27 int hook_index_cb4_updated_;
28
30 CbCtlHooks() {
31 hook_index_cb4_updated_ = HooksManager::registerHook("cb4_updated");
32 }
33};
34
35// Declare a Hooks object. As this is outside any function or method, it
36// will be instantiated (and the constructor run) when the module is loaded.
37// As a result, the hook indexes will be defined before any method in this
38// module is called.
39CbCtlHooks hooks_;
40
41} // anonymous namespace
42
43namespace isc {
44namespace dhcp {
45
46void
48 const ServerSelector& server_selector,
49 const boost::posix_time::ptime& lb_modification_time,
50 const AuditEntryCollection& audit_entries) {
51
52 auto globals_fetched = false;
53 auto reconfig = audit_entries.empty();
54 auto cb_update = !reconfig;
55 auto current_cfg = CfgMgr::instance().getCurrentCfg();
56 auto staging_cfg = CfgMgr::instance().getStagingCfg();
57
58 // Let's first delete all the configuration elements for which DELETE audit
59 // entries are found. Although, this may break chronology of the audit in
60 // some cases it should not affect the end result of the data fetch. If the
61 // object was created and then subsequently deleted, we will first try to
62 // delete this object from the local configuration (which will fail because
63 // the object does not exist) and then we will try to fetch it from the
64 // database which will return no result.
65 if (cb_update) {
66
67 auto external_cfg = CfgMgr::instance().createExternalCfg();
68
69 // Get audit entries for deleted global parameters.
70 auto const& index = audit_entries.get<AuditEntryObjectTypeTag>();
71 auto range = index.equal_range(boost::make_tuple("dhcp4_global_parameter",
73 if (range.first != range.second) {
74 // Some globals have been deleted. Since we currently don't track database
75 // identifiers of the global parameters we have to fetch all global
76 // parameters for this server. Next, we simply replace existing
77 // global parameters with the new parameters. This is slightly
78 // inefficient but only slightly. Note that this is a single
79 // database query and the number of global parameters is small.
81 globals = getMgr().getPool()->getAllGlobalParameters4(backend_selector, server_selector);
83
84 // Add defaults.
85 external_cfg->applyDefaultsConfiguredGlobals(SimpleParser4::GLOBAL4_DEFAULTS);
86
87 // Sanity check it.
88 external_cfg->sanityChecksLifetime("valid-lifetime");
89 external_cfg->sanityChecksDdnsTtlParameters();
90 external_cfg->sanityChecksSflqAllocator();
91
92 // Now that we successfully fetched the new global parameters, let's
93 // remove existing ones and merge them into the current configuration.
94 current_cfg->clearConfiguredGlobals();
95 CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
96 globals_fetched = true;
97 }
98
99 try {
100 // Get audit entries for deleted option definitions and delete each
101 // option definition from the current configuration for which the
102 // audit entry is found.
103 range = index.equal_range(boost::make_tuple("dhcp4_option_def",
105 BOOST_FOREACH(auto const& entry, range) {
106 current_cfg->getCfgOptionDef()->del(entry->getObjectId());
107 }
108
109 // Repeat the same for other configuration elements.
110
111 range = index.equal_range(boost::make_tuple("dhcp4_options",
113 BOOST_FOREACH(auto const& entry, range) {
114 current_cfg->getCfgOption()->del(entry->getObjectId());
115 }
116
117 range = index.equal_range(boost::make_tuple("dhcp4_client_class",
119 BOOST_FOREACH(auto const& entry, range) {
120 current_cfg->getClientClassDictionary()->removeClass(entry->getObjectId());
121 }
122
123 range = index.equal_range(boost::make_tuple("dhcp4_shared_network",
125 BOOST_FOREACH(auto const& entry, range) {
126 current_cfg->getCfgSharedNetworks4()->del(entry->getObjectId());
127 }
128
129 range = index.equal_range(boost::make_tuple("dhcp4_subnet",
131 BOOST_FOREACH(auto const& entry, range) {
132 // If the deleted subnet belongs to a shared network and the
133 // shared network is not being removed, we need to detach the
134 // subnet from the shared network.
135 auto subnet = current_cfg->getCfgSubnets4()->getBySubnetId(entry->getObjectId());
136 if (subnet) {
137 // Check if the subnet belongs to a shared network.
138 SharedNetwork4Ptr network;
139 subnet->getSharedNetwork(network);
140 if (network) {
141 // Detach the subnet from the shared network.
142 network->del(subnet->getID());
143 }
144 // Actually delete the subnet from the configuration.
145 current_cfg->getCfgSubnets4()->del(entry->getObjectId());
146 }
147 }
148
149 } catch (...) {
150 // Ignore errors thrown when attempting to delete a non-existing
151 // configuration entry. There is no guarantee that the deleted
152 // entry is actually there as we're not processing the audit
153 // chronologically.
154 }
155 }
156
157 // Create the external config into which we'll fetch backend config data.
158 auto external_cfg = CfgMgr::instance().createExternalCfg();
159
160 // First let's fetch the globals and add them to external config.
161 AuditEntryCollection updated_entries;
162 if (!globals_fetched) {
163 if (cb_update) {
164 updated_entries = fetchConfigElement(audit_entries, "dhcp4_global_parameter");
165 }
166 if (reconfig || !updated_entries.empty()) {
168 globals = getMgr().getPool()->getModifiedGlobalParameters4(backend_selector, server_selector,
169 lb_modification_time);
171 globals_fetched = true;
172 }
173 }
174
175 // Now we fetch the option definitions and add them.
176 if (cb_update) {
177 updated_entries = fetchConfigElement(audit_entries, "dhcp4_option_def");
178 }
179 if (reconfig || !updated_entries.empty()) {
180 OptionDefContainer option_defs =
181 getMgr().getPool()->getModifiedOptionDefs4(backend_selector, server_selector,
182 lb_modification_time);
183 for (auto const& option_def : option_defs) {
184 if (!audit_entries.empty() && !hasObjectId(updated_entries, option_def->getId())) {
185 continue;
186 }
187 external_cfg->getCfgOptionDef()->add(option_def);
188 }
189 }
190
191 // Next fetch the options. They are returned as a container of OptionDescriptors.
192 if (cb_update) {
193 updated_entries = fetchConfigElement(audit_entries, "dhcp4_options");
194 }
195 if (reconfig || !updated_entries.empty()) {
196 OptionContainer options = getMgr().getPool()->getModifiedOptions4(backend_selector,
197 server_selector,
198 lb_modification_time);
199 for (auto const& option : options) {
200 if (!audit_entries.empty() && !hasObjectId(updated_entries, option.getId())) {
201 continue;
202 }
203 external_cfg->getCfgOption()->add(option, option.space_name_);
204 }
205 }
206
207 // Fetch client classes. They are returned in a ClientClassDictionary.
208 if (cb_update) {
209 updated_entries = fetchConfigElement(audit_entries, "dhcp4_client_class");
210 }
211 if (reconfig || !updated_entries.empty()) {
212 ClientClassDictionary client_classes = getMgr().getPool()->getAllClientClasses4(backend_selector,
213 server_selector);
214 // Match expressions are not initialized for classes returned from the config backend.
215 // We have to ensure to initialize them before they can be used by the server.
216 client_classes.initMatchExpr(AF_INET);
217
218 // Class options also need to be created when returned from the config backend.
219 client_classes.createOptions(external_cfg->getCfgOptionDef());
220 client_classes.encapsulateOptions();
221
222 external_cfg->setClientClassDictionary(boost::make_shared<ClientClassDictionary>(client_classes));
223 }
224
225 // Allocator selection at the global level can affect subnets and shared networks
226 // for which the allocator hasn't been specified explicitly. Let's see if the
227 // allocator has been specified at the global level.
228 std::string global_allocator;
229 auto allocator = external_cfg->getConfiguredGlobal(CfgGlobals::ALLOCATOR);
230 if (allocator && (allocator->getType() == Element::string)) {
231 global_allocator = allocator->stringValue();
232 }
233
234 // If we're fetching the changes from the config backend we also want
235 // to see if the global allocator has changed. Let's get the currently
236 // used allocator too.
237 auto allocator_changed = false;
238 // We're only affected by the allocator change if this is the update from
239 // the configuration backend.
240 if (cb_update) {
241 allocator = CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::ALLOCATOR);
242 if (allocator && (allocator->getType() == Element::string)) {
243 allocator_changed = (global_allocator != allocator->stringValue());
244 }
245 }
246
247 // Now fetch the shared networks.
248 if (cb_update) {
249 updated_entries = fetchConfigElement(audit_entries, "dhcp4_shared_network");
250 }
252 if (allocator_changed || reconfig) {
253 // A change of the allocator or the server reconfiguration can affect all
254 // shared networks. Get all shared networks.
255 networks = getMgr().getPool()->getAllSharedNetworks4(backend_selector, server_selector);
256
257 } else if (!updated_entries.empty()) {
258 // An update from the config backend when the global allocator hasn't changed
259 // means that we only need to handle the modified subnets.
260 networks = getMgr().getPool()->getModifiedSharedNetworks4(backend_selector, server_selector,
261 lb_modification_time);
262 }
263 // Iterate over all shared networks that may require reconfiguration.
264 for (auto const& network : networks) {
265 if (!allocator_changed && cb_update && !hasObjectId(updated_entries, network->getId())) {
266 continue;
267 }
268 // In order to take advantage of the dynamic inheritance of global
269 // parameters to a shared network we need to set a callback function
270 // for each network to allow for fetching global parameters.
271 network->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
272 return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
273 });
274 network->setDefaultAllocatorType(global_allocator);
275 external_cfg->getCfgSharedNetworks4()->add(network);
276 }
277
278 // Next, fetch the subnets.
279 if (cb_update) {
280 updated_entries = fetchConfigElement(audit_entries, "dhcp4_subnet");
281 }
282 Subnet4Collection subnets;
283 if (allocator_changed || reconfig) {
284 // A change of the allocator or the server reconfiguration can affect all
285 // subnets. Get all subnets.
286 subnets = getMgr().getPool()->getAllSubnets4(backend_selector, server_selector);
287
288 } else if (!updated_entries.empty()) {
289 // An update from the config backend when the global allocator hasn't changed
290 // means that we only need to handle the modified subnets.
291 subnets = getMgr().getPool()->getModifiedSubnets4(backend_selector,
292 server_selector,
293 lb_modification_time);
294 }
295 // Iterate over all subnets that may require reconfiguration.
296 for (auto const& subnet : subnets) {
297 if (!allocator_changed && cb_update && !hasObjectId(updated_entries, subnet->getID())) {
298 continue;
299 }
300 // In order to take advantage of the dynamic inheritance of global
301 // parameters to a subnet we need to set a callback function for each
302 // subnet to allow for fetching global parameters.
303 subnet->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
304 return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
305 });
306 subnet->setDefaultAllocatorType(global_allocator);
307 external_cfg->getCfgSubnets4()->add(subnet);
308 }
309
310 if (reconfig) {
311 // If we're configuring the server after startup, we do not apply the
312 // ip-reservations-unique setting here. It will be applied when the
313 // configuration is committed.
314 external_cfg->sanityChecksLifetime(*staging_cfg, "valid-lifetime");
315 external_cfg->sanityChecksDdnsTtlParameters();
316 CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
317
318 } else {
319 if (globals_fetched) {
320 // ip-reservations-unique parameter requires special handling because
321 // setting it to false may be unsupported by some host backends.
322 bool ip_unique = true;
323 auto ip_unique_param = external_cfg->getConfiguredGlobal("ip-reservations-unique");
324 if (ip_unique_param && (ip_unique_param->getType() == Element::boolean)) {
325 ip_unique = ip_unique_param->boolValue();
326 }
327 // First try to use the new setting to configure the HostMgr because it
328 // may fail if the backend does not support it.
329 if (!HostMgr::instance().setIPReservationsUnique(ip_unique)) {
330 // The new setting is unsupported by the backend, so do not apply this
331 // setting at all.
333 external_cfg->addConfiguredGlobal("ip-reservations-unique", Element::create(true));
334 }
335 }
336 external_cfg->sanityChecksLifetime(*current_cfg, "valid-lifetime");
337 external_cfg->sanityChecksDdnsTtlParameters();
338 CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
339 CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->initAllocatorsAfterConfigure();
340 }
341
343
344 if (cb_update &&
345 HooksManager::calloutsPresent(hooks_.hook_index_cb4_updated_)) {
347
348 // Use the RAII wrapper to make sure that the callout handle state is
349 // reset when this object goes out of scope. All hook points must do
350 // it to prevent possible circular dependency between the callout
351 // handle and its arguments.
352 ScopedCalloutHandleState callout_handle_state(callout_handle);
353
354 // Pass a shared pointer to audit entries.
355 AuditEntryCollectionPtr ptr(new AuditEntryCollection(audit_entries));
356 callout_handle->setArgument("audit_entries", ptr);
357
358 // Call the callouts
359 HooksManager::callCallouts(hooks_.hook_index_cb4_updated_, *callout_handle);
360
361 // Ignore the result.
362 }
363}
364
365} // end of namespace isc::dhcp
366} // end of namespace isc
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:299
Config Backend selector.
Server selector for associating objects in a database with specific servers.
void translateAndAddGlobalsToConfig(SrvConfigPtr external_cfg, data::StampedValueCollection &cb_globals, data::SimpleKeywords global_lists) const
Definition cb_ctl_dhcp.h:74
virtual void databaseConfigApply(const db::BackendSelector &backend_selector, const db::ServerSelector &server_selector, const boost::posix_time::ptime &lb_modification_time, const db::AuditEntryCollection &audit_entries)
DHCPv4 server specific method to fetch and apply back end configuration into the local configuration.
SrvConfigPtr createExternalCfg()
Creates an external configuration and returns pointer to it.
Definition cfgmgr.cc:130
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
void mergeIntoStagingCfg(const uint32_t seq)
Merges external configuration with the given sequence number into the staging configuration.
Definition cfgmgr.cc:143
void mergeIntoCurrentCfg(const uint32_t seq)
Merges external configuration with the given sequence number into the current configuration.
Definition cfgmgr.cc:148
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition cfgmgr.cc:121
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
Maintains a list of ClientClassDef's.
void encapsulateOptions() const
Iterates over the classes in the dictionary and encapsulates suboptions.
void createOptions(const CfgOptionDefPtr &cfg_option_def)
Iterates over the classes in the dictionary and recreates the options.
void initMatchExpr(uint16_t family)
Iterates over the classes in the dictionary and ensures that that match expressions are initialized.
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition host_mgr.cc:114
static const isc::data::SimpleDefaults GLOBAL4_DEFAULTS
This table defines default global values for DHCPv4.
static const isc::data::SimpleKeywords GLOBAL4_LIST_PARAMETERS
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static boost::shared_ptr< CalloutHandle > createCalloutHandle()
Return callout handle.
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
Wrapper class around callout handle which automatically resets handle's state.
db::AuditEntryCollection fetchConfigElement(const db::AuditEntryCollection &audit_entries, const std::string &object_type) const
Defines classes for storing client class definitions.
#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
boost::multi_index_container< StampedValuePtr, boost::multi_index::indexed_by< boost::multi_index::hashed_non_unique< boost::multi_index::tag< StampedValueNameIndexTag >, boost::multi_index::const_mem_fun< StampedValue, std::string, &StampedValue::getName > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< StampedValueModificationTimeIndexTag >, boost::multi_index::const_mem_fun< BaseStampedElement, boost::posix_time::ptime, &BaseStampedElement::getModificationTime > > > > StampedValueCollection
Multi index container for StampedValue.
boost::multi_index_container< AuditEntryPtr, boost::multi_index::indexed_by< boost::multi_index::ordered_non_unique< boost::multi_index::tag< AuditEntryObjectTypeTag >, boost::multi_index::composite_key< AuditEntry, boost::multi_index::const_mem_fun< AuditEntry, std::string, &AuditEntry::getObjectType >, boost::multi_index::const_mem_fun< AuditEntry, AuditEntry::ModificationType, &AuditEntry::getModificationType > > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< AuditEntryModificationTimeIdTag >, boost::multi_index::composite_key< AuditEntry, boost::multi_index::const_mem_fun< AuditEntry, boost::posix_time::ptime, &AuditEntry::getModificationTime >, boost::multi_index::const_mem_fun< AuditEntry, uint64_t, &AuditEntry::getRevisionId > > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< AuditEntryObjectIdTag >, boost::multi_index::const_mem_fun< AuditEntry, uint64_t, &AuditEntry::getObjectId > > > > AuditEntryCollection
Multi index container holding AuditEntry instances.
boost::shared_ptr< AuditEntryCollection > AuditEntryCollectionPtr
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition dhcpsrv_log.h:56
boost::shared_ptr< const CfgGlobals > ConstCfgGlobalsPtr
Const shared pointer to a CfgGlobals instance.
boost::multi_index_container< OptionDescriptor, boost::multi_index::indexed_by< boost::multi_index::sequenced<>, boost::multi_index::hashed_non_unique< KeyFromKeyExtractor< boost::multi_index::const_mem_fun< Option, uint16_t, &Option::getType >, boost::multi_index::member< OptionDescriptor, OptionPtr, &OptionDescriptor::option_ > > >, boost::multi_index::hashed_non_unique< boost::multi_index::member< OptionDescriptor, bool, &OptionDescriptor::persistent_ > >, boost::multi_index::ordered_non_unique< boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< OptionIdIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, uint64_t, &data::BaseStampedElement::getId > >, boost::multi_index::hashed_non_unique< boost::multi_index::member< OptionDescriptor, bool, &OptionDescriptor::cancelled_ > >, boost::multi_index::hashed_non_unique< boost::multi_index::composite_key< OptionDescriptor, KeyFromKeyExtractor< boost::multi_index::const_mem_fun< Option, uint16_t, &Option::getType >, boost::multi_index::member< OptionDescriptor, OptionPtr, &OptionDescriptor::option_ > >, boost::multi_index::member< OptionDescriptor, ClientClasses, &OptionDescriptor::client_classes_ > > > > > OptionContainer
Multi index container for DHCP option descriptors.
Definition cfg_option.h:347
const isc::log::MessageID DHCPSRV_CFGMGR_CONFIG4_MERGED
const isc::log::MessageID DHCPSRV_CFGMGR_IPV4_RESERVATIONS_NON_UNIQUE_IGNORED
boost::multi_index_container< Subnet4Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetServerIdIndexTag >, boost::multi_index::const_mem_fun< Network4, asiolink::IOAddress, &Network4::getServerId > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > > > Subnet4Collection
A collection of Subnet4 objects.
Definition subnet.h:863
boost::multi_index_container< SharedNetwork4Ptr, boost::multi_index::indexed_by< boost::multi_index::random_access< boost::multi_index::tag< SharedNetworkRandomAccessIndexTag > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< SharedNetworkIdIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, uint64_t, &data::BaseStampedElement::getId > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SharedNetworkNameIndexTag >, boost::multi_index::const_mem_fun< SharedNetwork4, std::string, &SharedNetwork4::getName > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SharedNetworkServerIdIndexTag >, boost::multi_index::const_mem_fun< Network4, asiolink::IOAddress, &Network4::getServerId > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SharedNetworkModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > > > SharedNetwork4Collection
Multi index container holding shared networks.
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
boost::multi_index_container< OptionDefinitionPtr, boost::multi_index::indexed_by< boost::multi_index::sequenced<>, boost::multi_index::hashed_non_unique< boost::multi_index::const_mem_fun< OptionDefinition, uint16_t, &OptionDefinition::getCode > >, boost::multi_index::hashed_non_unique< boost::multi_index::const_mem_fun< OptionDefinition, std::string, &OptionDefinition::getName > >, boost::multi_index::ordered_non_unique< boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::StampedElement::getModificationTime > >, boost::multi_index::hashed_non_unique< boost::multi_index::tag< OptionIdIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, uint64_t, &data::BaseStampedElement::getId > > > > OptionDefContainer
Multi index container for DHCP option definitions.
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
bool hasObjectId(const db::AuditEntryCollection &audit_entries, const uint64_t &object_id)
Checks if an object is in a collection od audit entries.
Defines the logger used by the top-level component of kea-lfc.
Tag used to access index by object type.