Kea  1.9.9-git
cfg_subnets6.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-2020 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 other_subnets = other.getAll();
97  for (auto other_subnet = other_subnets->begin();
98  other_subnet != other_subnets->end();
99  ++other_subnet) {
100 
101  // Check if there is a subnet with the same ID.
102  auto subnet_it = index_id.find((*other_subnet)->getID());
103  if (subnet_it != index_id.end()) {
104 
105  // Subnet found.
106  auto existing_subnet = *subnet_it;
107 
108  // If the existing subnet and other subnet
109  // are the same instance skip it.
110  if (existing_subnet == *other_subnet) {
111  continue;
112  }
113 
114  // We're going to replace the existing subnet with the other
115  // version. If it belongs to a shared network, we need
116  // remove it from that network.
117  SharedNetwork6Ptr network;
118  existing_subnet->getSharedNetwork(network);
119  if (network) {
120  network->del(existing_subnet->getID());
121  }
122 
123  // Now we remove the existing subnet.
124  index_id.erase(subnet_it);
125  }
126 
127  // Check if there is a subnet with the same prefix.
128  auto subnet_prefix_it = index_prefix.find((*other_subnet)->toText());
129  if (subnet_prefix_it != index_prefix.end()) {
130 
131  // Subnet found.
132  auto existing_subnet = *subnet_prefix_it;
133 
134  // Updating the id can lead to problems... e.g. reservation
135  // for the previous subnet ID.
136  // @todo: check reservations
137 
138  // We're going to replace the existing subnet with the other
139  // version. If it belongs to a shared network, we need
140  // remove it from that network.
141  SharedNetwork6Ptr network;
142  existing_subnet->getSharedNetwork(network);
143  if (network) {
144  network->del(existing_subnet->getID());
145  }
146 
147  // Now we remove the existing subnet.
148  index_prefix.erase(subnet_prefix_it);
149  }
150 
151  // Create the subnet's options based on the given definitions.
152  (*other_subnet)->getCfgOption()->createOptions(cfg_def);
153 
154  // Create the options for pool based on the given definitions.
155  for (auto pool : (*other_subnet)->getPoolsWritable(Lease::TYPE_NA)) {
156  pool->getCfgOption()->createOptions(cfg_def);
157  }
158 
159  for (auto pool : (*other_subnet)->getPoolsWritable(Lease::TYPE_PD)) {
160  pool->getCfgOption()->createOptions(cfg_def);
161  }
162 
163  // Add the "other" subnet to the our collection of subnets.
164  static_cast<void>(subnets_.insert(*other_subnet));
165 
166  // If it belongs to a shared network, find the network and
167  // add the subnet to it
168  std::string network_name = (*other_subnet)->getSharedNetworkName();
169  if (!network_name.empty()) {
170  SharedNetwork6Ptr network = networks->getByName(network_name);
171  if (network) {
172  network->add(*other_subnet);
173  } else {
174  // This implies the shared-network collection we were given
175  // is out of sync with the subnets we were given.
176  isc_throw(InvalidOperation, "Cannot assign subnet ID of "
177  << (*other_subnet)->getID()
178  << " to shared network: " << network_name
179  << ", network does not exist");
180  }
181  }
182  }
183 }
184 
186 CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const {
187  const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
188  auto subnet_it = index.find(subnet_id);
189  return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
190 }
191 
193 CfgSubnets6::getByPrefix(const std::string& subnet_text) const {
194  const auto& index = subnets_.get<SubnetPrefixIndexTag>();
195  auto subnet_it = index.find(subnet_text);
196  return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
197 }
198 
200 CfgSubnets6::initSelector(const Pkt6Ptr& query) {
201  // Initialize subnet selector with the values used to select the subnet.
202  SubnetSelector selector;
203  selector.iface_name_ = query->getIface();
204  selector.remote_address_ = query->getRemoteAddr();
205  selector.first_relay_linkaddr_ = IOAddress("::");
206  selector.client_classes_ = query->classes_;
207 
208  // Initialize fields specific to relayed messages.
209  if (!query->relay_info_.empty()) {
210  BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query->relay_info_) {
211  if (!relay.linkaddr_.isV6Zero() &&
212  !relay.linkaddr_.isV6LinkLocal()) {
213  selector.first_relay_linkaddr_ = relay.linkaddr_;
214  break;
215  }
216  }
217  selector.interface_id_ =
218  query->getAnyRelayOption(D6O_INTERFACE_ID,
219  Pkt6::RELAY_GET_FIRST);
220  }
221 
222  return (selector);
223 }
224 
226 CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
227  Subnet6Ptr subnet;
228 
229  // If relay agent link address is set to zero it means that we're dealing
230  // with a directly connected client.
231  if (selector.first_relay_linkaddr_ == IOAddress("::")) {
232  // If interface name is known try to match it with interface names
233  // specified for configured subnets.
234  if (!selector.iface_name_.empty()) {
235  subnet = selectSubnet(selector.iface_name_,
236  selector.client_classes_);
237  }
238 
239  // If interface name didn't match, try the client's address.
240  if (!subnet && selector.remote_address_ != IOAddress("::")) {
241  subnet = selectSubnet(selector.remote_address_,
242  selector.client_classes_);
243  }
244 
245  // If relay agent link address is set, we're dealing with a relayed message.
246  } else {
247 
248  // Find the subnet using the Interface Id option, if present.
249  subnet = selectSubnet(selector.interface_id_, selector.client_classes_);
250 
251  // If Interface ID option could not be matched for any subnet, try
252  // the relay agent link address.
253  if (!subnet) {
254  subnet = selectSubnet(selector.first_relay_linkaddr_,
255  selector.client_classes_,
256  true);
257  }
258  }
259 
260  // Return subnet found, or NULL if not found.
261  return (subnet);
262 }
263 
265 CfgSubnets6::selectSubnet(const asiolink::IOAddress& address,
266  const ClientClasses& client_classes,
267  const bool is_relay_address) const {
268 
269  // If the specified address is a relay address we first need to match
270  // it with the relay addresses specified for all subnets.
271  if (is_relay_address) {
272  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
273  subnet != subnets_.end(); ++subnet) {
274 
275  // If the specified address matches a relay address, return this
276  // subnet.
277  if ((*subnet)->hasRelays()) {
278  if (!(*subnet)->hasRelayAddress(address)) {
279  continue;
280  }
281 
282  } else {
283  SharedNetwork6Ptr network;
284  (*subnet)->getSharedNetwork(network);
285  if (!network || !network->hasRelayAddress(address)) {
286  continue;
287  }
288  }
289 
290  if ((*subnet)->clientSupported(client_classes)) {
291  // The relay address is matching the one specified for a subnet
292  // or its shared network.
295  .arg((*subnet)->toText()).arg(address.toText());
296  return (*subnet);
297  }
298  }
299  }
300 
301  // No success so far. Check if the specified address is in range
302  // with any subnet.
303  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
304  subnet != subnets_.end(); ++subnet) {
305  if ((*subnet)->inRange(address) &&
306  (*subnet)->clientSupported(client_classes)) {
308  .arg((*subnet)->toText()).arg(address.toText());
309  return (*subnet);
310  }
311  }
312 
313  // Nothing found.
314  return (Subnet6Ptr());
315 }
316 
317 
319 CfgSubnets6::selectSubnet(const std::string& iface_name,
320  const ClientClasses& client_classes) const {
321 
322  // If empty interface specified, we can't select subnet by interface.
323  if (!iface_name.empty()) {
324  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
325  subnet != subnets_.end(); ++subnet) {
326 
327  // If interface name matches with the one specified for the subnet
328  // and the client is not rejected based on the classification,
329  // return the subnet.
330  if (((*subnet)->getIface() == iface_name) &&
331  (*subnet)->clientSupported(client_classes)) {
332 
335  .arg((*subnet)->toText()).arg(iface_name);
336  return (*subnet);
337  }
338  }
339  }
340 
341  // No subnet found for this interface name.
342  return (Subnet6Ptr());
343 }
344 
346 CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
347  const ClientClasses& client_classes) const {
348  // We can only select subnet using an interface id, if the interface
349  // id is known.
350  if (interface_id) {
351  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
352  subnet != subnets_.end(); ++subnet) {
353 
354  // If interface id matches for the subnet and the subnet is not
355  // rejected based on the classification.
356  if ((*subnet)->getInterfaceId() &&
357  (*subnet)->getInterfaceId()->equals(interface_id) &&
358  (*subnet)->clientSupported(client_classes)) {
359 
362  .arg((*subnet)->toText());
363  return (*subnet);
364  }
365  }
366  }
367  // No subnet found.
368  return (Subnet6Ptr());
369 }
370 
372 CfgSubnets6::getSubnet(const SubnetID id) const {
373 
376  for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
377  if ((*subnet)->getID() == id) {
378  return (*subnet);
379  }
380  }
381  return (Subnet6Ptr());
382 }
383 
384 void
385 CfgSubnets6::removeStatistics() {
386  using namespace isc::stats;
387 
388  StatsMgr& stats_mgr = StatsMgr::instance();
389  // For each v6 subnet currently configured, remove the statistics.
390  for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
391  subnet6 != subnets_.end(); ++subnet6) {
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 (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
427  subnet6 != subnets_.end(); ++subnet6) {
428  SubnetID subnet_id = (*subnet6)->getID();
429 
430  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
431  "total-nas"),
432  static_cast<int64_t>
433  ((*subnet6)->getPoolCapacity(Lease::TYPE_NA)));
434 
435  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
436  "total-pds"),
437  static_cast<int64_t>
438  ((*subnet6)->getPoolCapacity(Lease::TYPE_PD)));
439 
440  const std::string& name_nas =
441  StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-nas");
442  if (!stats_mgr.getObservation(name_nas)) {
443  stats_mgr.setValue(name_nas, static_cast<int64_t>(0));
444  }
445 
446  const std::string& name_pds =
447  StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-pds");
448  if (!stats_mgr.getObservation(name_pds)) {
449  stats_mgr.setValue(name_pds, static_cast<int64_t>(0));
450  }
451  }
452 
453  // Only recount the stats if we have subnets.
454  if (subnets_.begin() != subnets_.end()) {
455  LeaseMgrFactory::instance().recountLeaseStats6();
456  }
457 }
458 
460 CfgSubnets6::toElement() const {
461  ElementPtr result = Element::createList();
462  // Iterate subnets
463  for (Subnet6Collection::const_iterator subnet = subnets_.cbegin();
464  subnet != subnets_.cend(); ++subnet) {
465  result->add((*subnet)->toElement());
466  }
467  return (result);
468 }
469 
470 } // end of namespace isc::dhcp
471 } // 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.
Tag for the index for searching by subnet identifier.
Definition: subnet.h:802
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:20
Tag for the index for searching by subnet prefix.
Definition: subnet.h:805
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
ObservationPtr getObservation(const std::string &name) const
Returns an observation.
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:664
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-dhcp-ddns.
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_RELAY
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1079
const Subnet6Collection * getAll() const
Returns pointer to the collection of all IPv6 subnets.
Definition: cfg_subnets6.h:120
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:670
const isc::log::MessageID DHCPSRV_CFGMGR_ADD_SUBNET6
Container for storing client class names.
Definition: classify.h:43
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24