Kea 2.0.2
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>
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
20using namespace isc::asiolink;
21using namespace isc::data;
22
23namespace isc {
24namespace dhcp {
25
26void
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
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
65void
67 del(subnet->getID());
68}
69
70void
71CfgSubnets6::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
87void
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
186CfgSubnets6::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
193CfgSubnets6::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
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,
220 }
221
222 return (selector);
223}
224
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
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
319CfgSubnets6::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
346CfgSubnets6::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
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
384void
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
420void
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()) {
456 }
457}
458
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
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
Holds subnets configured for the DHCPv6 server.
Definition: cfg_subnets6.h:34
void updateStatistics()
Updates statistics.
Subnet6Ptr replace(const Subnet6Ptr &subnet)
Replaces subnet in the configuration.
Definition: cfg_subnets6.cc:45
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
void merge(CfgOptionDefPtr cfg_def, CfgSharedNetworks6Ptr networks, CfgSubnets6 &other)
Merges specified subnet configuration into this configuration.
Definition: cfg_subnets6.cc:88
Subnet6Ptr selectSubnet(const SubnetSelector &selector) const
Selects a subnet using parameters specified in the selector.
Subnet6Ptr getSubnet(const SubnetID id) const
Returns subnet with specified subnet-id value.
void removeStatistics()
Removes statistics.
const Subnet6Collection * getAll() const
Returns pointer to the collection of all IPv6 subnets.
Definition: cfg_subnets6.h:120
void add(const Subnet6Ptr &subnet)
Adds new subnet to the configuration.
Definition: cfg_subnets6.cc:27
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client's message.
void del(const ConstSubnet6Ptr &subnet)
Removes subnet from the configuration.
Definition: cfg_subnets6.cc:66
ConstSubnet6Ptr getByPrefix(const std::string &subnet_prefix) const
Returns const pointer to a subnet which matches the specified prefix in the canonical form.
ConstSubnet6Ptr getBySubnetId(const SubnetID &subnet_id) const
Returns const pointer to a subnet identified by the specified subnet identifier.
Container for storing client class names.
Definition: classify.h:43
Exception thrown upon attempt to add subnet with an ID that belongs to the subnet that already exists...
Definition: subnet_id.h:35
static LeaseMgr & instance()
Return current lease manager.
void recountLeaseStats6()
Recalculates per-subnet and global stats for IPv6 leases.
Definition: lease_mgr.cc:215
@ RELAY_GET_FIRST
Definition: pkt6.h:77
Statistics Manager class.
ObservationPtr getObservation(const std::string &name) const
Returns an observation.
@ D6O_INTERFACE_ID
Definition: dhcp6.h:38
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
bool del(const std::string &name)
Removes specified statistic.
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_IFACE
const isc::log::MessageID DHCPSRV_CFGMGR_ADD_SUBNET6
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_RELAY
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition: subnet.h:664
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
const isc::log::MessageID DHCPSRV_CFGMGR_UPDATE_SUBNET6
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:670
boost::shared_ptr< SharedNetwork6 > SharedNetwork6Ptr
Pointer to SharedNetwork6 object.
boost::shared_ptr< CfgSharedNetworks6 > CfgSharedNetworks6Ptr
Pointer to the configuration of IPv6 shared networks.
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET6_IFACE_ID
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
const isc::log::MessageID DHCPSRV_CFGMGR_DEL_SUBNET6
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
Defines the logger used by the top-level component of kea-lfc.
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:53
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition: lease.h:51
structure that describes a single relay information
Definition: pkt6.h:85
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
Tag for the index for searching by subnet prefix.
Definition: subnet.h:805
Subnet selector used to specify parameters used to select a subnet.
std::string iface_name_
Name of the interface on which the message was received.
ClientClasses client_classes_
Classes that the client belongs to.
asiolink::IOAddress remote_address_
Source address of the message.
OptionPtr interface_id_
Interface id option.
asiolink::IOAddress first_relay_linkaddr_
First relay link address.
Tag for the index for searching by subnet identifier.
Definition: subnet.h:802