Kea  1.9.9-git
cb_ctl_dhcp6.cc
Go to the documentation of this file.
1 // Copyright (C) 2019-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 #include <dhcpsrv/cb_ctl_dhcp6.h>
9 #include <dhcpsrv/cfgmgr.h>
10 #include <dhcpsrv/dhcpsrv_log.h>
11 #include <dhcpsrv/host_mgr.h>
13 #include <hooks/callout_handle.h>
14 #include <hooks/hooks_manager.h>
15 
16 using namespace isc::db;
17 using namespace isc::data;
18 using namespace isc::process;
19 using namespace isc::hooks;
20 
21 namespace {
22 
24 struct CbCtlHooks {
25  int hook_index_cb6_updated_;
26 
28  CbCtlHooks() {
29  hook_index_cb6_updated_ = HooksManager::registerHook("cb6_updated");
30  }
31 };
32 
33 // Declare a Hooks object. As this is outside any function or method, it
34 // will be instantiated (and the constructor run) when the module is loaded.
35 // As a result, the hook indexes will be defined before any method in this
36 // module is called.
37 CbCtlHooks hooks_;
38 
39 }; // anonymous namespace
40 
41 namespace isc {
42 namespace dhcp {
43 
44 void
45 CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector,
46  const db::ServerSelector& server_selector,
47  const boost::posix_time::ptime& lb_modification_time,
48  const db::AuditEntryCollection& audit_entries) {
49  bool globals_fetched = false;
50 
51  // Let's first delete all the configuration elements for which DELETE audit
52  // entries are found. Although, this may break chronology of the audit in
53  // some cases it should not affect the end result of the data fetch. If the
54  // object was created and then subsequently deleted, we will first try to
55  // delete this object from the local configuration (which will fail because
56  // the object does not exist) and then we will try to fetch it from the
57  // database which will return no result.
58  if (!audit_entries.empty()) {
59 
60  auto cfg = CfgMgr::instance().getCurrentCfg();
61  auto external_cfg = CfgMgr::instance().createExternalCfg();
62 
63  // Get audit entries for deleted global parameters.
64  const auto& index = audit_entries.get<AuditEntryObjectTypeTag>();
65  auto range = index.equal_range(boost::make_tuple("dhcp6_global_parameter",
67  if (range.first != range.second) {
68  // Some globals have been deleted. Since we currently don't track database
69  // identifiers of the global parameters we have to fetch all global
70  // parameters for this server. Next, we simply replace existing
71  // global parameters with the new parameters. This is slightly
72  // inefficient but only slightly. Note that this is a single
73  // database query and the number of global parameters is small.
75  globals = getMgr().getPool()->getAllGlobalParameters6(backend_selector, server_selector);
76  addGlobalsToConfig(external_cfg, globals);
77 
78  // Add defaults.
79  external_cfg->applyDefaultsConfiguredGlobals(SimpleParser6::GLOBAL6_DEFAULTS);
80 
81  // Sanity check it.
82  external_cfg->sanityChecksLifetime("preferred-lifetime");
83  external_cfg->sanityChecksLifetime("valid-lifetime");
84 
85  // Now that we successfully fetched the new global parameters, let's
86  // remove existing ones and merge them into the current configuration.
87  cfg->clearConfiguredGlobals();
88  CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
89  globals_fetched = true;
90  }
91 
92  try {
93  // Get audit entries for deleted option definitions and delete each
94  // option definition from the current configuration for which the
95  // audit entry is found.
96  range = index.equal_range(boost::make_tuple("dhcp6_option_def",
98  for (auto entry = range.first; entry != range.second; ++entry) {
99  cfg->getCfgOptionDef()->del((*entry)->getObjectId());
100  }
101 
102  // Repeat the same for other configuration elements.
103 
104  range = index.equal_range(boost::make_tuple("dhcp6_options",
106  for (auto entry = range.first; entry != range.second; ++entry) {
107  cfg->getCfgOption()->del((*entry)->getObjectId());
108  }
109 
110  range = index.equal_range(boost::make_tuple("dhcp6_shared_network",
112  for (auto entry = range.first; entry != range.second; ++entry) {
113  cfg->getCfgSharedNetworks6()->del((*entry)->getObjectId());
114  }
115 
116  range = index.equal_range(boost::make_tuple("dhcp6_subnet",
118  for (auto entry = range.first; entry != range.second; ++entry) {
119  // If the deleted subnet belongs to a shared network and the
120  // shared network is not being removed, we need to detach the
121  // subnet from the shared network.
122  auto subnet = cfg->getCfgSubnets6()->getBySubnetId((*entry)->getObjectId());
123  if (subnet) {
124  // Check if the subnet belongs to a shared network.
125  SharedNetwork6Ptr network;
126  subnet->getSharedNetwork(network);
127  if (network) {
128  // Detach the subnet from the shared network.
129  network->del(subnet->getID());
130  }
131  // Actually delete the subnet from the configuration.
132  cfg->getCfgSubnets6()->del((*entry)->getObjectId());
133  }
134  }
135 
136  } catch (...) {
137  // Ignore errors thrown when attempting to delete a non-existing
138  // configuration entry. There is no guarantee that the deleted
139  // entry is actually there as we're not processing the audit
140  // chronologically.
141  }
142  }
143 
144  // Create the external config into which we'll fetch backend config data.
145  SrvConfigPtr external_cfg = CfgMgr::instance().createExternalCfg();
146 
147  // First let's fetch the globals and add them to external config.
148  AuditEntryCollection updated_entries;
149  if (!globals_fetched && !audit_entries.empty()) {
150  updated_entries = fetchConfigElement(audit_entries, "dhcp6_global_parameter");
151  }
152  if (!globals_fetched && (audit_entries.empty() || !updated_entries.empty())) {
154  globals = getMgr().getPool()->getModifiedGlobalParameters6(backend_selector, server_selector,
155  lb_modification_time);
156  addGlobalsToConfig(external_cfg, globals);
157  globals_fetched = true;
158  }
159 
160  // Now we fetch the option definitions and add them.
161  if (!audit_entries.empty()) {
162  updated_entries = fetchConfigElement(audit_entries, "dhcp6_option_def");
163  }
164  if (audit_entries.empty() || !updated_entries.empty()) {
165  OptionDefContainer option_defs =
166  getMgr().getPool()->getModifiedOptionDefs6(backend_selector, server_selector,
167  lb_modification_time);
168  for (auto option_def = option_defs.begin(); option_def != option_defs.end(); ++option_def) {
169  if (!audit_entries.empty() && !hasObjectId(updated_entries, (*option_def)->getId())) {
170  continue;
171  }
172  external_cfg->getCfgOptionDef()->add(*option_def);
173  }
174  }
175 
176  // Next fetch the options. They are returned as a container of OptionDescriptors.
177  if (!audit_entries.empty()) {
178  updated_entries = fetchConfigElement(audit_entries, "dhcp6_options");
179  }
180  if (audit_entries.empty() || !updated_entries.empty()) {
181  OptionContainer options = getMgr().getPool()->getModifiedOptions6(backend_selector,
182  server_selector,
183  lb_modification_time);
184  for (auto option = options.begin(); option != options.end(); ++option) {
185  if (!audit_entries.empty() && !hasObjectId(updated_entries, (*option).getId())) {
186  continue;
187  }
188  external_cfg->getCfgOption()->add((*option), (*option).space_name_);
189  }
190  }
191 
192  // Now fetch the shared networks.
193  if (!audit_entries.empty()) {
194  updated_entries = fetchConfigElement(audit_entries, "dhcp6_shared_network");
195  }
196  if (audit_entries.empty() || !updated_entries.empty()) {
197  SharedNetwork6Collection networks =
198  getMgr().getPool()->getModifiedSharedNetworks6(backend_selector, server_selector,
199  lb_modification_time);
200  for (auto network = networks.begin(); network != networks.end(); ++network) {
201  if (!audit_entries.empty() && !hasObjectId(updated_entries, (*network)->getId())) {
202  continue;
203  }
204  // In order to take advantage of the dynamic inheritance of global
205  // parameters to a shared network we need to set a callback function
206  // for each network to allow for fetching global parameters.
207  (*network)->setFetchGlobalsFn([] () -> ConstElementPtr {
208  return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
209  });
210  external_cfg->getCfgSharedNetworks6()->add((*network));
211  }
212  }
213 
214  // Next we fetch subnets.
215  if (!audit_entries.empty()) {
216  updated_entries = fetchConfigElement(audit_entries, "dhcp6_subnet");
217  }
218  if (audit_entries.empty() || !updated_entries.empty()) {
219  Subnet6Collection subnets = getMgr().getPool()->getModifiedSubnets6(backend_selector,
220  server_selector,
221  lb_modification_time);
222  for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
223  if (!audit_entries.empty() && !hasObjectId(updated_entries, (*subnet)->getID())) {
224  continue;
225  }
226  // In order to take advantage of the dynamic inheritance of global
227  // parameters to a subnet we need to set a callback function for each
228  // subnet to allow for fetching global parameters.
229  (*subnet)->setFetchGlobalsFn([] () -> ConstElementPtr {
230  return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
231  });
232  external_cfg->getCfgSubnets6()->add((*subnet));
233  }
234  }
235 
236  if (audit_entries.empty()) {
237  // If we're configuring the server after startup, we do not apply the
238  // ip-reservations-unique setting here. It will be applied when the
239  // configuration is committed.
240  auto const& cfg = CfgMgr::instance().getStagingCfg();
241  external_cfg->sanityChecksLifetime(*cfg, "preferred-lifetime");
242  external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
243  CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
244  } else {
245  if (globals_fetched) {
246  // ip-reservations-unique parameter requires special handling because
247  // setting it to false may be unsupported by some host backends.
248  bool ip_unique = true;
249  auto ip_unique_param = external_cfg->getConfiguredGlobal("ip-reservations-unique");
250  if (ip_unique_param && (ip_unique_param->getType() == Element::boolean)) {
251  ip_unique = ip_unique_param->boolValue();
252  }
253  // First try to use the new setting to configure the HostMgr because it
254  // may fail if the backend does not support it.
255  if (!HostMgr::instance().setIPReservationsUnique(ip_unique)) {
256  // The new setting is unsupported by the backend, so do not apply this
257  // setting at all.
259  external_cfg->addConfiguredGlobal("ip-reservations-unique", Element::create(true));
260  }
261  }
262  auto const& cfg = CfgMgr::instance().getCurrentCfg();
263  external_cfg->sanityChecksLifetime(*cfg, "preferred-lifetime");
264  external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
265  CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
266  }
268 
269  if (!audit_entries.empty() &&
270  HooksManager::calloutsPresent(hooks_.hook_index_cb6_updated_)) {
271  CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
272 
273  // Use the RAII wrapper to make sure that the callout handle state is
274  // reset when this object goes out of scope. All hook points must do
275  // it to prevent possible circular dependency between the callout
276  // handle and its arguments.
277  ScopedCalloutHandleState callout_handle_state(callout_handle);
278 
279  // Pass a shared pointer to audit entries.
280  AuditEntryCollectionPtr ptr(new AuditEntryCollection(audit_entries));
281  callout_handle->setArgument("audit_entries", ptr);
282 
283  // Call the callouts
284  HooksManager::callCallouts(hooks_.hook_index_cb6_updated_, *callout_handle);
285 
286  // Ignore the result.
287  }
288 }
289 
290 } // end of namespace isc::dhcp
291 } // end of namespace isc
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
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
boost::shared_ptr< AuditEntryCollection > AuditEntryCollectionPtr
Definition: audit_entry.h:294
boost::shared_ptr< SrvConfig > SrvConfigPtr
Non-const pointer to the SrvConfig.
Definition: srv_config.h:1036
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.
Wrapper class around callout handle which automatically resets handle's state.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
Server selector for associating objects in a database with specific servers.
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
boost::multi_index_container< SharedNetwork6Ptr, 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< SharedNetwork6, std::string,&SharedNetwork6::getName > >, 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 > > >> SharedNetwork6Collection
Multi index container holding shared networks.
boost::multi_index_container< Subnet6Ptr, 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< SubnetModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime,&data::BaseStampedElement::getModificationTime > > >> Subnet6Collection
A collection of Subnet6 objects.
Definition: subnet.h:914
Defines the logger used by the top-level component of kea-dhcp-ddns.
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.
Config Backend selector.
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
const isc::log::MessageID DHCPSRV_CFGMGR_CONFIG6_MERGED
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
const isc::log::MessageID DHCPSRV_CFGMGR_IPV6_RESERVATIONS_NON_UNIQUE_IGNORED
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
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
Tag used to access index by object type.
Definition: audit_entry.h:229