Kea  2.3.3-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 
386 CfgSubnets6::getLinks(const IOAddress& link_addr, uint8_t& link_len) const {
387  SubnetIDSet links;
388  bool link_len_set = false;
389  for (auto const& subnet : subnets_) {
390  if (!subnet->inRange(link_addr)) {
391  continue;
392  }
393  uint8_t plen = subnet->get().second;
394  if (!link_len_set || (plen < link_len)) {
395  link_len = plen;
396  }
397  links.insert(subnet->getID());
398  }
399  return (links);
400 }
401 
402 void
403 CfgSubnets6::removeStatistics() {
404  using namespace isc::stats;
405 
406  StatsMgr& stats_mgr = StatsMgr::instance();
407  // For each v6 subnet currently configured, remove the statistics.
408  for (auto const& subnet6 : subnets_) {
409  SubnetID subnet_id = subnet6->getID();
410  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-nas"));
411 
412  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
413  "assigned-nas"));
414 
415  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
416  "cumulative-assigned-nas"));
417 
418  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-pds"));
419 
420  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
421  "assigned-pds"));
422 
423  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
424  "cumulative-assigned-pds"));
425 
426  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
427  "declined-addresses"));
428 
429  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
430  "reclaimed-declined-addresses"));
431 
432  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
433  "reclaimed-leases"));
434  }
435 }
436 
437 void
438 CfgSubnets6::updateStatistics() {
439  using namespace isc::stats;
440 
441  StatsMgr& stats_mgr = StatsMgr::instance();
442  // For each v6 subnet currently configured, calculate totals
443  for (auto const& subnet6 : subnets_) {
444  SubnetID subnet_id = subnet6->getID();
445 
446  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
447  "total-nas"),
448  static_cast<int64_t>
449  (subnet6->getPoolCapacity(Lease::TYPE_NA)));
450 
451  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
452  "total-pds"),
453  static_cast<int64_t>
454  (subnet6->getPoolCapacity(Lease::TYPE_PD)));
455 
456  const std::string& name_nas =
457  StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-nas");
458  if (!stats_mgr.getObservation(name_nas)) {
459  stats_mgr.setValue(name_nas, static_cast<int64_t>(0));
460  }
461 
462  const std::string& name_pds =
463  StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-pds");
464  if (!stats_mgr.getObservation(name_pds)) {
465  stats_mgr.setValue(name_pds, static_cast<int64_t>(0));
466  }
467  }
468 
469  // Only recount the stats if we have subnets.
470  if (subnets_.begin() != subnets_.end()) {
471  LeaseMgrFactory::instance().recountLeaseStats6();
472  }
473 }
474 
476 CfgSubnets6::toElement() const {
477  ElementPtr result = Element::createList();
478  // Iterate subnets
479  for (auto const& subnet : subnets_) {
480  result->add(subnet->toElement());
481  }
482  return (result);
483 }
484 
485 } // end of namespace isc::dhcp
486 } // 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:36
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.
Tag for the index for searching by subnet identifier.
Definition: subnet.h:746
const Subnet6Collection * getAll() const
Returns pointer to the collection of all IPv6 subnets.
Definition: cfg_subnets6.h:120
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:749
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
Statistics Manager class.
ClientClasses client_classes_
Classes that the client belongs to.
std::set< dhcp::SubnetID > SubnetIDSet
Ordered list aka set of subnetIDs.
Definition: subnet_id.h:43
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:598
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.
const isc::log::MessageID DHCPSRV_SUBNET6_SELECT_BY_INTERFACE_ID_NO_MATCH
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:1139
const isc::log::MessageID DHCPSRV_SUBNET6_SELECT_BY_ADDRESS_NO_MATCH
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
const isc::log::MessageID DHCPSRV_SUBNET6_SELECT_BY_INTERFACE_NO_MATCH
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:604
const isc::log::MessageID DHCPSRV_CFGMGR_ADD_SUBNET6
Container for storing client class names.
Definition: classify.h:108
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:25