Kea 2.2.0
cb_ctl_dhcp4.cc
Go to the documentation of this file.
1// Copyright (C) 2019-2022 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
17using namespace isc::db;
18using namespace isc::data;
19using namespace isc::process;
20using namespace isc::hooks;
21
22namespace {
23
25struct CbCtlHooks {
26 int hook_index_cb4_updated_;
27
29 CbCtlHooks() {
30 hook_index_cb4_updated_ = HooksManager::registerHook("cb4_updated");
31 }
32};
33
34// Declare a Hooks object. As this is outside any function or method, it
35// will be instantiated (and the constructor run) when the module is loaded.
36// As a result, the hook indexes will be defined before any method in this
37// module is called.
38CbCtlHooks hooks_;
39
40}; // anonymous namespace
41
42namespace isc {
43namespace dhcp {
44
45void
47 const ServerSelector& server_selector,
48 const boost::posix_time::ptime& lb_modification_time,
49 const AuditEntryCollection& audit_entries) {
50
51 bool globals_fetched = false;
52
53 // Let's first delete all the configuration elements for which DELETE audit
54 // entries are found. Although, this may break chronology of the audit in
55 // some cases it should not affect the end result of the data fetch. If the
56 // object was created and then subsequently deleted, we will first try to
57 // delete this object from the local configuration (which will fail because
58 // the object does not exist) and then we will try to fetch it from the
59 // database which will return no result.
60 if (!audit_entries.empty()) {
61
62 auto cfg = CfgMgr::instance().getCurrentCfg();
63 auto external_cfg = CfgMgr::instance().createExternalCfg();
64
65 // Get audit entries for deleted global parameters.
66 const auto& index = audit_entries.get<AuditEntryObjectTypeTag>();
67 auto range = index.equal_range(boost::make_tuple("dhcp4_global_parameter",
68 AuditEntry::ModificationType::DELETE));
69 if (range.first != range.second) {
70 // Some globals have been deleted. Since we currently don't track database
71 // identifiers of the global parameters we have to fetch all global
72 // parameters for this server. Next, we simply replace existing
73 // global parameters with the new parameters. This is slightly
74 // inefficient but only slightly. Note that this is a single
75 // database query and the number of global parameters is small.
77 globals = getMgr().getPool()->getAllGlobalParameters4(backend_selector, server_selector);
78 addGlobalsToConfig(external_cfg, globals);
79
80 // Add defaults.
81 external_cfg->applyDefaultsConfiguredGlobals(SimpleParser4::GLOBAL4_DEFAULTS);
82
83 // Sanity check it.
84 external_cfg->sanityChecksLifetime("valid-lifetime");
85
86 // Now that we successfully fetched the new global parameters, let's
87 // remove existing ones and merge them into the current configuration.
88 cfg->clearConfiguredGlobals();
89 CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
90 globals_fetched = true;
91 }
92
93 try {
94 // Get audit entries for deleted option definitions and delete each
95 // option definition from the current configuration for which the
96 // audit entry is found.
97 range = index.equal_range(boost::make_tuple("dhcp4_option_def",
98 AuditEntry::ModificationType::DELETE));
99 for (auto entry = range.first; entry != range.second; ++entry) {
100 cfg->getCfgOptionDef()->del((*entry)->getObjectId());
101 }
102
103 // Repeat the same for other configuration elements.
104
105 range = index.equal_range(boost::make_tuple("dhcp4_options",
106 AuditEntry::ModificationType::DELETE));
107 for (auto entry = range.first; entry != range.second; ++entry) {
108 cfg->getCfgOption()->del((*entry)->getObjectId());
109 }
110
111 range = index.equal_range(boost::make_tuple("dhcp4_client_class",
112 AuditEntry::ModificationType::DELETE));
113 for (auto entry = range.first; entry != range.second; ++entry) {
114 cfg->getClientClassDictionary()->removeClass((*entry)->getObjectId());
115 }
116
117 range = index.equal_range(boost::make_tuple("dhcp4_shared_network",
118 AuditEntry::ModificationType::DELETE));
119 for (auto entry = range.first; entry != range.second; ++entry) {
120 cfg->getCfgSharedNetworks4()->del((*entry)->getObjectId());
121 }
122
123 range = index.equal_range(boost::make_tuple("dhcp4_subnet",
124 AuditEntry::ModificationType::DELETE));
125 for (auto entry = range.first; entry != range.second; ++entry) {
126 // If the deleted subnet belongs to a shared network and the
127 // shared network is not being removed, we need to detach the
128 // subnet from the shared network.
129 auto subnet = cfg->getCfgSubnets4()->getBySubnetId((*entry)->getObjectId());
130 if (subnet) {
131 // Check if the subnet belongs to a shared network.
132 SharedNetwork4Ptr network;
133 subnet->getSharedNetwork(network);
134 if (network) {
135 // Detach the subnet from the shared network.
136 network->del(subnet->getID());
137 }
138 // Actually delete the subnet from the configuration.
139 cfg->getCfgSubnets4()->del((*entry)->getObjectId());
140 }
141 }
142
143 } catch (...) {
144 // Ignore errors thrown when attempting to delete a non-existing
145 // configuration entry. There is no guarantee that the deleted
146 // entry is actually there as we're not processing the audit
147 // chronologically.
148 }
149 }
150
151 // Create the external config into which we'll fetch backend config data.
153
154 // First let's fetch the globals and add them to external config.
155 AuditEntryCollection updated_entries;
156 if (!globals_fetched && !audit_entries.empty()) {
157 updated_entries = fetchConfigElement(audit_entries, "dhcp4_global_parameter");
158 }
159 if (!globals_fetched && (audit_entries.empty() || !updated_entries.empty())) {
161 globals = getMgr().getPool()->getModifiedGlobalParameters4(backend_selector, server_selector,
162 lb_modification_time);
163 addGlobalsToConfig(external_cfg, globals);
164 globals_fetched = true;
165 }
166
167 // Now we fetch the option definitions and add them.
168 if (!audit_entries.empty()) {
169 updated_entries = fetchConfigElement(audit_entries, "dhcp4_option_def");
170 }
171 if (audit_entries.empty() || !updated_entries.empty()) {
172 OptionDefContainer option_defs =
173 getMgr().getPool()->getModifiedOptionDefs4(backend_selector, server_selector,
174 lb_modification_time);
175 for (auto option_def = option_defs.begin(); option_def != option_defs.end(); ++option_def) {
176 if (!audit_entries.empty() && !hasObjectId(updated_entries, (*option_def)->getId())) {
177 continue;
178 }
179 external_cfg->getCfgOptionDef()->add(*option_def);
180 }
181 }
182
183 // Next fetch the options. They are returned as a container of OptionDescriptors.
184 if (!audit_entries.empty()) {
185 updated_entries = fetchConfigElement(audit_entries, "dhcp4_options");
186 }
187 if (audit_entries.empty() || !updated_entries.empty()) {
188 OptionContainer options = getMgr().getPool()->getModifiedOptions4(backend_selector,
189 server_selector,
190 lb_modification_time);
191 for (auto option = options.begin(); option != options.end(); ++option) {
192 if (!audit_entries.empty() && !hasObjectId(updated_entries, (*option).getId())) {
193 continue;
194 }
195 external_cfg->getCfgOption()->add((*option), (*option).space_name_);
196 }
197 }
198
199 // Fetch client classes. They are returned in a ClientClassDictionary.
200 if (!audit_entries.empty()) {
201 updated_entries = fetchConfigElement(audit_entries, "dhcp4_client_class");
202 }
203 if (audit_entries.empty() || !updated_entries.empty()) {
204 ClientClassDictionary client_classes = getMgr().getPool()->getAllClientClasses4(backend_selector,
205 server_selector);
206 // Match expressions are not initialized for classes returned from the config backend.
207 // We have to ensure to initialize them before they can be used by the server.
208 client_classes.initMatchExpr(AF_INET);
209
210 // Class options also need to be created when returned from the config backend.
211 client_classes.createOptions(external_cfg->getCfgOptionDef());
212
213 external_cfg->setClientClassDictionary(boost::make_shared<ClientClassDictionary>(client_classes));
214 }
215
216 // Now fetch the shared networks.
217 if (!audit_entries.empty()) {
218 updated_entries = fetchConfigElement(audit_entries, "dhcp4_shared_network");
219 }
220 if (audit_entries.empty() || !updated_entries.empty()) {
221 SharedNetwork4Collection networks =
222 getMgr().getPool()->getModifiedSharedNetworks4(backend_selector, server_selector,
223 lb_modification_time);
224 for (auto network = networks.begin(); network != networks.end(); ++network) {
225 if (!audit_entries.empty() && !hasObjectId(updated_entries, (*network)->getId())) {
226 continue;
227 }
228 // In order to take advantage of the dynamic inheritance of global
229 // parameters to a shared network we need to set a callback function
230 // for each network to allow for fetching global parameters.
231 (*network)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
232 return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
233 });
234 external_cfg->getCfgSharedNetworks4()->add((*network));
235 }
236 }
237
238 // Next we fetch subnets.
239 if (!audit_entries.empty()) {
240 updated_entries = fetchConfigElement(audit_entries, "dhcp4_subnet");
241 }
242 if (audit_entries.empty() || !updated_entries.empty()) {
243 Subnet4Collection subnets = getMgr().getPool()->getModifiedSubnets4(backend_selector,
244 server_selector,
245 lb_modification_time);
246 for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
247 if (!audit_entries.empty() && !hasObjectId(updated_entries, (*subnet)->getID())) {
248 continue;
249 }
250 // In order to take advantage of the dynamic inheritance of global
251 // parameters to a subnet we need to set a callback function for each
252 // subnet to allow for fetching global parameters.
253 (*subnet)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
254 return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
255 });
256 external_cfg->getCfgSubnets4()->add((*subnet));
257 }
258 }
259
260 if (audit_entries.empty()) {
261 // If we're configuring the server after startup, we do not apply the
262 // ip-reservations-unique setting here. It will be applied when the
263 // configuration is committed.
264 auto const& cfg = CfgMgr::instance().getStagingCfg();
265 external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
266 CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
267 } else {
268 if (globals_fetched) {
269 // ip-reservations-unique parameter requires special handling because
270 // setting it to false may be unsupported by some host backends.
271 bool ip_unique = true;
272 auto ip_unique_param = external_cfg->getConfiguredGlobal("ip-reservations-unique");
273 if (ip_unique_param && (ip_unique_param->getType() == Element::boolean)) {
274 ip_unique = ip_unique_param->boolValue();
275 }
276 // First try to use the new setting to configure the HostMgr because it
277 // may fail if the backend does not support it.
278 if (!HostMgr::instance().setIPReservationsUnique(ip_unique)) {
279 // The new setting is unsupported by the backend, so do not apply this
280 // setting at all.
282 external_cfg->addConfiguredGlobal("ip-reservations-unique", Element::create(true));
283 }
284 }
285 auto const& cfg = CfgMgr::instance().getCurrentCfg();
286 external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
287 CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
288 }
290
291 if (!audit_entries.empty() &&
292 HooksManager::calloutsPresent(hooks_.hook_index_cb4_updated_)) {
293 CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
294
295 // Use the RAII wrapper to make sure that the callout handle state is
296 // reset when this object goes out of scope. All hook points must do
297 // it to prevent possible circular dependency between the callout
298 // handle and its arguments.
299 ScopedCalloutHandleState callout_handle_state(callout_handle);
300
301 // Pass a shared pointer to audit entries.
302 AuditEntryCollectionPtr ptr(new AuditEntryCollection(audit_entries));
303 callout_handle->setArgument("audit_entries", ptr);
304
305 // Call the callouts
306 HooksManager::callCallouts(hooks_.hook_index_cb4_updated_, *callout_handle);
307
308 // Ignore the result.
309 }
310}
311
312} // end of namespace isc::dhcp
313} // end of namespace isc
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
Config Backend selector.
Server selector for associating objects in a database with specific servers.
void addGlobalsToConfig(SrvConfigPtr external_cfg, data::StampedValueCollection &cb_globals) const
Adds globals fetched from config backend(s) to a SrvConfig instance.
Definition: cb_ctl_dhcp.h:45
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.
Definition: cb_ctl_dhcp4.cc:46
SrvConfigPtr createExternalCfg()
Creates an external configuration and returns pointer to it.
Definition: cfgmgr.cc:177
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
void mergeIntoStagingCfg(const uint32_t seq)
Merges external configuration with the given sequence number into the staging configuration.
Definition: cfgmgr.cc:190
void mergeIntoCurrentCfg(const uint32_t seq)
Merges external configuration with the given sequence number into the current configuration.
Definition: cfgmgr.cc:195
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition: cfgmgr.cc:167
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
Maintains a list of ClientClassDef's.
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:105
static const isc::data::SimpleDefaults GLOBAL4_DEFAULTS
This table defines default global values for DHCPv4.
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
Returns audit entries for new or updated configuration elements of specific type to be fetched from t...
Definition: cb_ctl_base.h:262
ConfigBackendDHCPv4Mgr & getMgr() const
Returns the instance of the Config Backend Manager used by this object.
Definition: cb_ctl_base.h:312
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.
Definition: audit_entry.h:291
boost::shared_ptr< AuditEntryCollection > AuditEntryCollectionPtr
Definition: audit_entry.h:294
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.
Definition: cfg_globals.h:159
const isc::log::MessageID DHCPSRV_CFGMGR_CONFIG4_MERGED
const isc::log::MessageID DHCPSRV_CFGMGR_IPV4_RESERVATIONS_NON_UNIQUE_IGNORED
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:1165
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:893
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 > > > > OptionContainer
Multi index container for DHCP option descriptors.
Definition: cfg_option.h:269
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.
Definition: cb_ctl_base.h:364
Defines the logger used by the top-level component of kea-lfc.
Tag used to access index by object type.
Definition: audit_entry.h:229