Kea  2.1.7-git
cfg_subnets6.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-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>
8 #include <dhcp/dhcp6.h>
9 #include <dhcp/option_custom.h>
11 #include <dhcpsrv/cfg_subnets6.h>
12 #include <dhcpsrv/dhcpsrv_log.h>
14 #include <dhcpsrv/subnet_id.h>
15 #include <stats/stats_mgr.h>
16 #include <boost/foreach.hpp>
17 #include <string.h>
18 #include <sstream>
19 
20 using namespace isc::asiolink;
21 using namespace isc::data;
22 
23 namespace isc {
24 namespace dhcp {
25 
26 void
27 CfgSubnets6::add(const Subnet6Ptr& subnet) {
28  if (getBySubnetId(subnet->getID())) {
29  isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
30  << subnet->getID() << "' is already in use");
31 
32  } else if (getByPrefix(subnet->toText())) {
35  isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '"
36  << subnet->toText() << "' already exists");
37  }
38 
40  .arg(subnet->toText());
41  static_cast<void>(subnets_.insert(subnet));
42 }
43 
45 CfgSubnets6::replace(const Subnet6Ptr& subnet) {
46  // Get the subnet with the same ID.
47  const SubnetID& subnet_id = subnet->getID();
48  auto& index = subnets_.template get<SubnetSubnetIdIndexTag>();
49  auto subnet_it = index.find(subnet_id);
50  if (subnet_it == index.end()) {
51  isc_throw(BadValue, "There is no IPv6 subnet with ID " << subnet_id);
52  }
53  Subnet6Ptr old = *subnet_it;
54  bool ret = index.replace(subnet_it, subnet);
55 
57  .arg(subnet_id).arg(ret);
58  if (ret) {
59  return (old);
60  } else {
61  return (Subnet6Ptr());
62  }
63 }
64 
65 void
66 CfgSubnets6::del(const ConstSubnet6Ptr& subnet) {
67  del(subnet->getID());
68 }
69 
70 void
71 CfgSubnets6::del(const SubnetID& subnet_id) {
72  auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
73  auto subnet_it = index.find(subnet_id);
74  if (subnet_it == index.end()) {
75  isc_throw(BadValue, "no subnet with ID of '" << subnet_id
76  << "' found");
77  }
78 
79  Subnet6Ptr subnet = *subnet_it;
80 
81  index.erase(subnet_it);
82 
84  .arg(subnet->toText());
85 }
86 
87 void
89  CfgSubnets6& other) {
90  auto& index_id = subnets_.get<SubnetSubnetIdIndexTag>();
91  auto& index_prefix = subnets_.get<SubnetPrefixIndexTag>();
92 
93  // Iterate over the subnets to be merged. They will replace the existing
94  // subnets with the same id. All new subnets will be inserted into the
95  // configuration into which we're merging.
96  auto const& other_subnets = other.getAll();
97  for (auto const& other_subnet : *other_subnets) {
98 
99  // Check if there is a subnet with the same ID.
100  auto subnet_it = index_id.find(other_subnet->getID());
101  if (subnet_it != index_id.end()) {
102 
103  // Subnet found.
104  auto existing_subnet = *subnet_it;
105 
106  // If the existing subnet and other subnet
107  // are the same instance skip it.
108  if (existing_subnet == other_subnet) {
109  continue;
110  }
111 
112  // We're going to replace the existing subnet with the other
113  // version. If it belongs to a shared network, we need
114  // remove it from that network.
115  SharedNetwork6Ptr network;
116  existing_subnet->getSharedNetwork(network);
117  if (network) {
118  network->del(existing_subnet->getID());
119  }
120 
121  // Now we remove the existing subnet.
122  index_id.erase(subnet_it);
123  }
124 
125  // Check if there is a subnet with the same prefix.
126  auto subnet_prefix_it = index_prefix.find(other_subnet->toText());
127  if (subnet_prefix_it != index_prefix.end()) {
128 
129  // Subnet found.
130  auto existing_subnet = *subnet_prefix_it;
131 
132  // Updating the id can lead to problems... e.g. reservation
133  // for the previous subnet ID.
134  // @todo: check reservations
135 
136  // We're going to replace the existing subnet with the other
137  // version. If it belongs to a shared network, we need
138  // remove it from that network.
139  SharedNetwork6Ptr network;
140  existing_subnet->getSharedNetwork(network);
141  if (network) {
142  network->del(existing_subnet->getID());
143  }
144 
145  // Now we remove the existing subnet.
146  index_prefix.erase(subnet_prefix_it);
147  }
148 
149  // Create the subnet's options based on the given definitions.
150  other_subnet->getCfgOption()->createOptions(cfg_def);
151 
152  // Create the options for pool based on the given definitions.
153  for (auto const& pool : other_subnet->getPoolsWritable(Lease::TYPE_NA)) {
154  pool->getCfgOption()->createOptions(cfg_def);
155  }
156 
157  // Create the options for pd pool based on the given definitions.
158  for (auto const& pool : other_subnet->getPoolsWritable(Lease::TYPE_PD)) {
159  pool->getCfgOption()->createOptions(cfg_def);
160  }
161 
162  // Add the "other" subnet to the our collection of subnets.
163  static_cast<void>(subnets_.insert(other_subnet));
164 
165  // If it belongs to a shared network, find the network and
166  // add the subnet to it
167  std::string network_name = other_subnet->getSharedNetworkName();
168  if (!network_name.empty()) {
169  SharedNetwork6Ptr network = networks->getByName(network_name);
170  if (network) {
171  network->add(other_subnet);
172  } else {
173  // This implies the shared-network collection we were given
174  // is out of sync with the subnets we were given.
175  isc_throw(InvalidOperation, "Cannot assign subnet ID of "
176  << other_subnet->getID()
177  << " to shared network: " << network_name
178  << ", network does not exist");
179  }
180  }
181  }
182 }
183 
185 CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const {
186  const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
187  auto subnet_it = index.find(subnet_id);
188  return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
189 }
190 
192 CfgSubnets6::getByPrefix(const std::string& subnet_text) const {
193  const auto& index = subnets_.get<SubnetPrefixIndexTag>();
194  auto subnet_it = index.find(subnet_text);
195  return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
196 }
197 
199 CfgSubnets6::initSelector(const Pkt6Ptr& query) {
200  // Initialize subnet selector with the values used to select the subnet.
201  SubnetSelector selector;
202  selector.iface_name_ = query->getIface();
203  selector.remote_address_ = query->getRemoteAddr();
204  selector.first_relay_linkaddr_ = IOAddress("::");
205  selector.client_classes_ = query->classes_;
206 
207  // Initialize fields specific to relayed messages.
208  if (!query->relay_info_.empty()) {
209  BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query->relay_info_) {
210  if (!relay.linkaddr_.isV6Zero() &&
211  !relay.linkaddr_.isV6LinkLocal()) {
212  selector.first_relay_linkaddr_ = relay.linkaddr_;
213  break;
214  }
215  }
216  selector.interface_id_ =
217  query->getAnyRelayOption(D6O_INTERFACE_ID,
218  Pkt6::RELAY_GET_FIRST);
219  }
220 
221  return (selector);
222 }
223 
225 CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
226  Subnet6Ptr subnet;
227 
228  // If relay agent link address is set to zero it means that we're dealing
229  // with a directly connected client.
230  if (selector.first_relay_linkaddr_ == IOAddress("::")) {
231  // If interface name is known try to match it with interface names
232  // specified for configured subnets.
233  if (!selector.iface_name_.empty()) {
234  subnet = selectSubnet(selector.iface_name_,
235  selector.client_classes_);
236  }
237 
238  // If interface name didn't match, try the client's address.
239  if (!subnet && selector.remote_address_ != IOAddress("::")) {
240  subnet = selectSubnet(selector.remote_address_,
241  selector.client_classes_);
242  }
243 
244  // If relay agent link address is set, we're dealing with a relayed message.
245  } else {
246  // Find the subnet using the Interface Id option, if present.
247  subnet = selectSubnet(selector.interface_id_, selector.client_classes_);
248 
249  // If Interface ID option could not be matched for any subnet, try
250  // the relay agent link address.
251  if (!subnet) {
252  subnet = selectSubnet(selector.first_relay_linkaddr_,
253  selector.client_classes_,
254  true);
255  }
256  }
257 
258  // Return subnet found, or NULL if not found.
259  return (subnet);
260 }
261 
263 CfgSubnets6::selectSubnet(const asiolink::IOAddress& address,
264  const ClientClasses& client_classes,
265  const bool is_relay_address) const {
266  // If the specified address is a relay address we first need to match
267  // it with the relay addresses specified for all subnets.
268  if (is_relay_address) {
269  for (auto const& subnet : subnets_) {
270 
271  // If the specified address matches a relay address, return this
272  // subnet.
273  if (subnet->hasRelays()) {
274  if (!subnet->hasRelayAddress(address)) {
275  continue;
276  }
277 
278  } else {
279  SharedNetwork6Ptr network;
280  subnet->getSharedNetwork(network);
281  if (!network || !network->hasRelayAddress(address)) {
282  continue;
283  }
284  }
285 
286  if (subnet->clientSupported(client_classes)) {
287  // The relay address is matching the one specified for a subnet
288  // or its shared network.
291  .arg(subnet->toText()).arg(address.toText());
292  return (subnet);
293  }
294  }
295  }
296 
297  // No success so far. Check if the specified address is in range
298  // with any subnet.
299  for (auto const& subnet : subnets_) {
300  if (subnet->inRange(address) && subnet->clientSupported(client_classes)) {
302  .arg(subnet->toText()).arg(address.toText());
303  return (subnet);
304  }
305  }
306 
309  .arg(address.toText());
310 
311  // Nothing found.
312  return (Subnet6Ptr());
313 }
314 
316 CfgSubnets6::selectSubnet(const std::string& iface_name,
317  const ClientClasses& client_classes) const {
318  // If empty interface specified, we can't select subnet by interface.
319  if (!iface_name.empty()) {
320  for (auto const& subnet : subnets_) {
321 
322  // If interface name matches with the one specified for the subnet
323  // and the client is not rejected based on the classification,
324  // return the subnet.
325  if ((subnet->getIface() == iface_name) &&
326  subnet->clientSupported(client_classes)) {
329  .arg(subnet->toText()).arg(iface_name);
330  return (subnet);
331  }
332  }
333  }
334 
337  .arg(iface_name);
338 
339  // No subnet found for this interface name.
340  return (Subnet6Ptr());
341 }
342 
344 CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
345  const ClientClasses& client_classes) const {
346  // We can only select subnet using an interface id, if the interface
347  // id is known.
348  if (interface_id) {
349  for (auto const& subnet : subnets_) {
350 
351  // If interface id matches for the subnet and the subnet is not
352  // rejected based on the classification.
353  if (subnet->getInterfaceId() &&
354  subnet->getInterfaceId()->equals(interface_id) &&
355  subnet->clientSupported(client_classes)) {
356 
359  .arg(subnet->toText());
360  return (subnet);
361  }
362  }
363 
366  .arg(interface_id->toText());
367  }
368 
369  // No subnet found.
370  return (Subnet6Ptr());
371 }
372 
374 CfgSubnets6::getSubnet(const SubnetID id) const {
377  for (auto const& subnet : subnets_) {
378  if (subnet->getID() == id) {
379  return (subnet);
380  }
381  }
382  return (Subnet6Ptr());
383 }
384 
385 void
386 CfgSubnets6::removeStatistics() {
387  using namespace isc::stats;
388 
389  StatsMgr& stats_mgr = StatsMgr::instance();
390  // For each v6 subnet currently configured, remove the statistics.
391  for (auto const& subnet6 : subnets_) {
392  SubnetID subnet_id = subnet6->getID();
393  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-nas"));
394 
395  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
396  "assigned-nas"));
397 
398  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
399  "cumulative-assigned-nas"));
400 
401  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-pds"));
402 
403  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
404  "assigned-pds"));
405 
406  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
407  "cumulative-assigned-pds"));
408 
409  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
410  "declined-addresses"));
411 
412  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
413  "reclaimed-declined-addresses"));
414 
415  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
416  "reclaimed-leases"));
417  }
418 }
419 
420 void
421 CfgSubnets6::updateStatistics() {
422  using namespace isc::stats;
423 
424  StatsMgr& stats_mgr = StatsMgr::instance();
425  // For each v6 subnet currently configured, calculate totals
426  for (auto const& subnet6 : subnets_) {
427  SubnetID subnet_id = subnet6->getID();
428 
429  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
430  "total-nas"),
431  static_cast<int64_t>
432  (subnet6->getPoolCapacity(Lease::TYPE_NA)));
433 
434  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
435  "total-pds"),
436  static_cast<int64_t>
437  (subnet6->getPoolCapacity(Lease::TYPE_PD)));
438 
439  const std::string& name_nas =
440  StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-nas");
441  if (!stats_mgr.getObservation(name_nas)) {
442  stats_mgr.setValue(name_nas, static_cast<int64_t>(0));
443  }
444 
445  const std::string& name_pds =
446  StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-pds");
447  if (!stats_mgr.getObservation(name_pds)) {
448  stats_mgr.setValue(name_pds, static_cast<int64_t>(0));
449  }
450  }
451 
452  // Only recount the stats if we have subnets.
453  if (subnets_.begin() != subnets_.end()) {
454  LeaseMgrFactory::instance().recountLeaseStats6();
455  }
456 }
457 
459 CfgSubnets6::toElement() const {
460  ElementPtr result = Element::createList();
461  // Iterate subnets
462  for (auto const& subnet : subnets_) {
463  result->add(subnet->toElement());
464  }
465  return (result);
466 }
467 
468 } // end of namespace isc::dhcp
469 } // end of namespace isc
Exception thrown upon attempt to add subnet with an ID that belongs to the subnet that already exists...
Definition: subnet_id.h:35
boost::shared_ptr< CfgSharedNetworks6 > CfgSharedNetworks6Ptr
Pointer to the configuration of IPv6 shared networks.
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6
asiolink::IOAddress remote_address_
Source address of the message.
ObservationPtr getObservation(const std::string &name) const
Returns an observation.
const isc::log::MessageID DHCPSRV_SUBNET6_SELECT_BY_INTERFACE_FAILED
Tag for the index for searching by subnet identifier.
Definition: subnet.h:804
const Subnet6Collection * getAll() const
Returns pointer to the collection of all IPv6 subnets.
Definition: cfg_subnets6.h:120
const isc::log::MessageID DHCPSRV_SUBNET6_SELECT_BY_ADDRESS_FAILED
Holds subnets configured for the DHCPv6 server.
Definition: cfg_subnets6.h:34
OptionPtr interface_id_
Interface id option.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
Tag for the index for searching by subnet prefix.
Definition: subnet.h:807
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
Statistics Manager class.
ClientClasses client_classes_
Classes that the client belongs to.
asiolink::IOAddress first_relay_linkaddr_
First relay link address.
Subnet selector used to specify parameters used to select a subnet.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
const isc::log::MessageID DHCPSRV_CFGMGR_UPDATE_SUBNET6
structure that describes a single relay information
Definition: pkt6.h:85
bool del(const std::string &name)
Removes specified statistic.
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition: subnet.h:666
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_IFACE_ID
const isc::log::MessageID DHCPSRV_CFGMGR_DEL_SUBNET6
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_IFACE
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_RELAY
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1134
A generic exception that is thrown if a function is called in a prohibited way.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
std::string iface_name_
Name of the interface on which the message was received.
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:672
const isc::log::MessageID DHCPSRV_CFGMGR_ADD_SUBNET6
const isc::log::MessageID DHCPSRV_SUBNET6_SELECT_BY_INTERFACE_ID_FAILED
Container for storing client class names.
Definition: classify.h:68
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition: subnet_id.h:24