Kea 1.9.11
cfg_subnets4.cc
Go to the documentation of this file.
1// Copyright (C) 2014-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 <dhcp/iface_mgr.h>
11#include <dhcpsrv/dhcpsrv_log.h>
14#include <dhcpsrv/subnet_id.h>
15#include <asiolink/io_address.h>
17#include <stats/stats_mgr.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 IPv4 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 IPv4 subnet with ID " <<subnet_id);
52 }
53 Subnet4Ptr 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 (Subnet4Ptr());
62 }
63}
64
65void
67 del(subnet->getID());
68}
69
70void
71CfgSubnets4::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 Subnet4Ptr subnet = *subnet_it;
80
81 index.erase(subnet_it);
82
84 .arg(subnet->toText());
85}
86
87void
89 CfgSubnets4& 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_id_it = index_id.find((*other_subnet)->getID());
103 if (subnet_id_it != index_id.end()) {
104
105 // Subnet found.
106 auto existing_subnet = *subnet_id_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 // Updating the prefix can lead to problems... e.g. pools
115 // and reservations going outside range.
116 // @todo: check prefix change.
117
118 // We're going to replace the existing subnet with the other
119 // version. If it belongs to a shared network, we need
120 // remove it from that network.
121 SharedNetwork4Ptr network;
122 existing_subnet->getSharedNetwork(network);
123 if (network) {
124 network->del(existing_subnet->getID());
125 }
126
127 // Now we remove the existing subnet.
128 index_id.erase(subnet_id_it);
129 }
130
131 // Check if there is a subnet with the same prefix.
132 auto subnet_prefix_it = index_prefix.find((*other_subnet)->toText());
133 if (subnet_prefix_it != index_prefix.end()) {
134
135 // Subnet found.
136 auto existing_subnet = *subnet_prefix_it;
137
138 // Updating the id can lead to problems... e.g. reservation
139 // for the previous subnet ID.
140 // @todo: check reservations
141
142 // We're going to replace the existing subnet with the other
143 // version. If it belongs to a shared network, we need
144 // remove it from that network.
145 SharedNetwork4Ptr network;
146 existing_subnet->getSharedNetwork(network);
147 if (network) {
148 network->del(existing_subnet->getID());
149 }
150
151 // Now we remove the existing subnet.
152 index_prefix.erase(subnet_prefix_it);
153 }
154
155 // Create the subnet's options based on the given definitions.
156 (*other_subnet)->getCfgOption()->createOptions(cfg_def);
157 for (auto pool : (*other_subnet)->getPoolsWritable(Lease::TYPE_V4)) {
158 pool->getCfgOption()->createOptions(cfg_def);
159 }
160
161 // Add the "other" subnet to the our collection of subnets.
162 static_cast<void>(subnets_.insert(*other_subnet));
163
164 // If it belongs to a shared network, find the network and
165 // add the subnet to it
166 std::string network_name = (*other_subnet)->getSharedNetworkName();
167 if (!network_name.empty()) {
168 SharedNetwork4Ptr network = networks->getByName(network_name);
169 if (network) {
170 network->add(*other_subnet);
171 } else {
172 // This implies the shared-network collection we were given
173 // is out of sync with the subnets we were given.
174 isc_throw(InvalidOperation, "Cannot assign subnet ID of "
175 << (*other_subnet)->getID()
176 << " to shared network: " << network_name
177 << ", network does not exist");
178 }
179 }
180 }
181}
182
184CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
185 const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
186 auto subnet_it = index.find(subnet_id);
187 return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
188}
189
191CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
192 const auto& index = subnets_.get<SubnetPrefixIndexTag>();
193 auto subnet_it = index.find(subnet_text);
194 return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
195}
196
197bool
199 const auto& index = subnets_.get<SubnetServerIdIndexTag>();
200 auto subnet_it = index.find(server_id);
201 return (subnet_it != index.cend());
202}
203
206 SubnetSelector selector;
207 selector.ciaddr_ = query->getCiaddr();
208 selector.giaddr_ = query->getGiaddr();
209 selector.local_address_ = query->getLocalAddr();
210 selector.remote_address_ = query->getRemoteAddr();
211 selector.client_classes_ = query->classes_;
212 selector.iface_name_ = query->getIface();
213
214 // If the link-selection sub-option is present, extract its value.
215 // "The link-selection sub-option is used by any DHCP relay agent
216 // that desires to specify a subnet/link for a DHCP client request
217 // that it is relaying but needs the subnet/link specification to
218 // be different from the IP address the DHCP server should use
219 // when communicating with the relay agent." (RFC 3527)
220 //
221 // Try first Relay Agent Link Selection sub-option
222 OptionPtr rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
223 if (rai) {
224 OptionCustomPtr rai_custom =
225 boost::dynamic_pointer_cast<OptionCustom>(rai);
226 if (rai_custom) {
227 OptionPtr link_select =
228 rai_custom->getOption(RAI_OPTION_LINK_SELECTION);
229 if (link_select) {
230 OptionBuffer link_select_buf = link_select->getData();
231 if (link_select_buf.size() == sizeof(uint32_t)) {
232 selector.option_select_ =
233 IOAddress::fromBytes(AF_INET, &link_select_buf[0]);
234 return (selector);
235 }
236 }
237 }
238 }
239 // The query does not include a RAI option or that option does
240 // not contain the link-selection sub-option. Try subnet-selection
241 // option.
242 OptionPtr sbnsel = query->getOption(DHO_SUBNET_SELECTION);
243 if (sbnsel) {
244 OptionCustomPtr oc =
245 boost::dynamic_pointer_cast<OptionCustom>(sbnsel);
246 if (oc) {
247 selector.option_select_ = oc->readAddress();
248 }
249 }
250 return (selector);
251}
252
255
256 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
257 subnet != subnets_.end(); ++subnet) {
258 Cfg4o6& cfg4o6 = (*subnet)->get4o6();
259
260 // Is this an 4o6 subnet at all?
261 if (!cfg4o6.enabled()) {
262 continue; // No? Let's try the next one.
263 }
264
265 // First match criteria: check if we have a prefix/len defined.
266 std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
267 if (!pref.first.isV6Zero()) {
268
269 // Let's check if the IPv6 address is in range
270 IOAddress first = firstAddrInPrefix(pref.first, pref.second);
271 IOAddress last = lastAddrInPrefix(pref.first, pref.second);
272 if ((first <= selector.remote_address_) &&
273 (selector.remote_address_ <= last)) {
274 return (*subnet);
275 }
276 }
277
278 // Second match criteria: check if the interface-id matches
279 if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
280 cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
281 return (*subnet);
282 }
283
284 // Third match criteria: check if the interface name matches
285 if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
286 && cfg4o6.getIface4o6() == selector.iface_name_) {
287 return (*subnet);
288 }
289 }
290
291 // Ok, wasn't able to find any matching subnet.
292 return (Subnet4Ptr());
293}
294
297
298 // First use RAI link select sub-option or subnet select option
299 if (!selector.option_select_.isV4Zero()) {
300 return (selectSubnet(selector.option_select_,
301 selector.client_classes_));
302 }
303
304 // If relayed message has been received, try to match the giaddr with the
305 // relay address specified for a subnet and/or shared network. It is also
306 // possible that the relay address will not match with any of the relay
307 // addresses across all subnets, but we need to verify that for all subnets
308 // before we can try to use the giaddr to match with the subnet prefix.
309 if (!selector.giaddr_.isV4Zero()) {
310 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
311 subnet != subnets_.end(); ++subnet) {
312
313 // If relay information is specified for this subnet, it must match.
314 // Otherwise, we ignore this subnet.
315 if ((*subnet)->hasRelays()) {
316 if (!(*subnet)->hasRelayAddress(selector.giaddr_)) {
317 continue;
318 }
319 } else {
320 // Relay information is not specified on the subnet level,
321 // so let's try matching on the shared network level.
322 SharedNetwork4Ptr network;
323 (*subnet)->getSharedNetwork(network);
324 if (!network || !(network->hasRelayAddress(selector.giaddr_))) {
325 continue;
326 }
327 }
328
329 // If a subnet meets the client class criteria return it.
330 if ((*subnet)->clientSupported(selector.client_classes_)) {
331 return (*subnet);
332 }
333 }
334 }
335
336 // If we got to this point it means that we were not able to match the
337 // giaddr with any of the addresses specified for subnets. Let's determine
338 // what address from the client's packet to use to match with the
339 // subnets' prefixes.
340
342 // If there is a giaddr, use it for subnet selection.
343 if (!selector.giaddr_.isV4Zero()) {
344 address = selector.giaddr_;
345
346 // If it is a Renew or Rebind, use the ciaddr.
347 } else if (!selector.ciaddr_.isV4Zero() &&
348 !selector.local_address_.isV4Bcast()) {
349 address = selector.ciaddr_;
350
351 // If ciaddr is not specified, use the source address.
352 } else if (!selector.remote_address_.isV4Zero() &&
353 !selector.local_address_.isV4Bcast()) {
354 address = selector.remote_address_;
355
356 // If local interface name is known, use the local address on this
357 // interface.
358 } else if (!selector.iface_name_.empty()) {
360 // This should never happen in the real life. Hence we throw an
361 // exception.
362 if (iface == NULL) {
363 isc_throw(isc::BadValue, "interface " << selector.iface_name_
364 << " doesn't exist and therefore it is impossible"
365 " to find a suitable subnet for its IPv4 address");
366 }
367
368 // Attempt to select subnet based on the interface name.
369 Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
370 selector.client_classes_);
371
372 // If it matches - great. If not, we'll try to use a different
373 // selection criteria below.
374 if (subnet) {
375 return (subnet);
376 } else {
377 // Let's try to get an address from the local interface and
378 // try to match it to defined subnet.
379 iface->getAddress4(address);
380 }
381 }
382
383 // Unable to find a suitable address to use for subnet selection.
384 if (address.isV4Zero()) {
385 return (Subnet4Ptr());
386 }
387
388 // We have identified an address in the client's packet that can be
389 // used for subnet selection. Match this packet with the subnets.
390 return (selectSubnet(address, selector.client_classes_));
391}
392
394CfgSubnets4::selectSubnet(const std::string& iface,
395 const ClientClasses& client_classes) const {
396 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
397 subnet != subnets_.end(); ++subnet) {
398
399 Subnet4Ptr subnet_selected;
400
401 // First, try subnet specific interface name.
402 if (!(*subnet)->getIface(Network4::Inheritance::NONE).empty()) {
403 if ((*subnet)->getIface(Network4::Inheritance::NONE) == iface) {
404 subnet_selected = (*subnet);
405 }
406
407 } else {
408 // Interface not specified for a subnet, so let's try if
409 // we can match with shared network specific setting of
410 // the interface.
411 SharedNetwork4Ptr network;
412 (*subnet)->getSharedNetwork(network);
413 if (network &&
414 (network->getIface(Network4::Inheritance::NONE) == iface)) {
415 subnet_selected = (*subnet);
416 }
417 }
418
419 if (subnet_selected) {
420
421 // If a subnet meets the client class criteria return it.
422 if (subnet_selected->clientSupported(client_classes)) {
425 .arg((*subnet)->toText())
426 .arg(iface);
427 return (subnet_selected);
428 }
429 }
430 }
431
432 // Failed to find a subnet.
433 return (Subnet4Ptr());
434}
435
438
441 for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
442 if ((*subnet)->getID() == id) {
443 return (*subnet);
444 }
445 }
446 return (Subnet4Ptr());
447}
448
451 const ClientClasses& client_classes) const {
452 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
453 subnet != subnets_.end(); ++subnet) {
454
455 // Address is in range for the subnet prefix, so return it.
456 if (!(*subnet)->inRange(address)) {
457 continue;
458 }
459
460 // If a subnet meets the client class criteria return it.
461 if ((*subnet)->clientSupported(client_classes)) {
463 .arg((*subnet)->toText())
464 .arg(address.toText());
465 return (*subnet);
466 }
467 }
468
469 // Failed to find a subnet.
470 return (Subnet4Ptr());
471}
472
473void
475 using namespace isc::stats;
476
477 // For each v4 subnet currently configured, remove the statistic.
478 StatsMgr& stats_mgr = StatsMgr::instance();
479 for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
480 subnet4 != subnets_.end(); ++subnet4) {
481 SubnetID subnet_id = (*subnet4)->getID();
482 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
483 "total-addresses"));
484
485 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
486 "assigned-addresses"));
487
488 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
489 "cumulative-assigned-addresses"));
490
491 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
492 "declined-addresses"));
493
494 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
495 "reclaimed-declined-addresses"));
496
497 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
498 "reclaimed-leases"));
499 }
500}
501
502void
504 using namespace isc::stats;
505
506 StatsMgr& stats_mgr = StatsMgr::instance();
507 for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
508 subnet4 != subnets_.end(); ++subnet4) {
509 SubnetID subnet_id = (*subnet4)->getID();
510
511 stats_mgr.setValue(StatsMgr::
512 generateName("subnet", subnet_id, "total-addresses"),
513 static_cast<int64_t>
514 ((*subnet4)->getPoolCapacity(Lease::
515 TYPE_V4)));
516 const std::string& name =
517 StatsMgr::generateName("subnet", subnet_id, "cumulative-assigned-addresses");
518 if (!stats_mgr.getObservation(name)) {
519 stats_mgr.setValue(name, static_cast<int64_t>(0));
520 }
521 }
522
523 // Only recount the stats if we have subnets.
524 if (subnets_.begin() != subnets_.end()) {
526 }
527}
528
531 ElementPtr result = Element::createList();
532 // Iterate subnets
533 for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
534 subnet != subnets_.cend(); ++subnet) {
535 result->add((*subnet)->toElement());
536 }
537 return (result);
538}
539
540} // end of namespace isc::dhcp
541} // 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 DHCPv4 server.
Definition: cfg_subnets4.h:33
ConstSubnet4Ptr getBySubnetId(const SubnetID &subnet_id) const
Returns const pointer to a subnet identified by the specified subnet identifier.
void del(const ConstSubnet4Ptr &subnet)
Removes subnet from the configuration.
Definition: cfg_subnets4.cc:66
virtual isc::data::ElementPtr toElement() const
Unparse a configuration object.
bool hasSubnetWithServerId(const asiolink::IOAddress &server_id) const
Checks if specified server identifier has been specified for any subnet.
ConstSubnet4Ptr getByPrefix(const std::string &subnet_prefix) const
Returns const pointer to a subnet which matches the specified prefix in the canonical form.
void updateStatistics()
Updates statistics.
void merge(CfgOptionDefPtr cfg_def, CfgSharedNetworks4Ptr networks, CfgSubnets4 &other)
Merges specified subnet configuration into this configuration.
Definition: cfg_subnets4.cc:88
Subnet4Ptr selectSubnet4o6(const SubnetSelector &selector) const
Attempts to do subnet selection based on DHCP4o6 information.
Subnet4Ptr selectSubnet(const SubnetSelector &selector) const
Returns a pointer to the selected subnet.
Subnet4Ptr getSubnet(const SubnetID id) const
Returns subnet with specified subnet-id value.
Subnet4Ptr replace(const Subnet4Ptr &subnet)
Replaces subnet in the configuration.
Definition: cfg_subnets4.cc:45
void add(const Subnet4Ptr &subnet)
Adds new subnet to the configuration.
Definition: cfg_subnets4.cc:27
void removeStatistics()
Removes statistics.
const Subnet4Collection * getAll() const
Returns pointer to the collection of all IPv4 subnets.
Definition: cfg_subnets4.h:119
static SubnetSelector initSelector(const Pkt4Ptr &query)
Build selector from a client's message.
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 IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
IfacePtr getIface(int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:875
static LeaseMgr & instance()
Return current lease manager.
void recountLeaseStats4()
Recalculates per-subnet and global stats for IPv4 leases.
Definition: lease_mgr.cc:67
Statistics Manager class.
ObservationPtr getObservation(const std::string &name) const
Returns an observation.
#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_SUBNET4_IFACE
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:522
const isc::log::MessageID DHCPSRV_CFGMGR_UPDATE_SUBNET4
const isc::log::MessageID DHCPSRV_CFGMGR_ADD_SUBNET4
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
@ DHO_SUBNET_SELECTION
Definition: dhcp4.h:180
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition: subnet.h:516
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition: iface_mgr.h:463
const isc::log::MessageID DHCPSRV_CFGMGR_DEL_SUBNET4
const isc::log::MessageID DHCPSRV_CFGMGR_SUBNET4_ADDR
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
boost::shared_ptr< CfgSharedNetworks4 > CfgSharedNetworks4Ptr
Pointer to the configuration of IPv4 shared networks.
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
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.
This structure contains information about DHCP4o6 (RFC7341)
Definition: cfg_4o6.h:22
util::Optional< std::string > getIface4o6() const
Returns the DHCP4o6 interface.
Definition: cfg_4o6.h:45
util::Optional< std::pair< asiolink::IOAddress, uint8_t > > getSubnet4o6() const
Returns prefix/len for the IPv6 subnet.
Definition: cfg_4o6.h:58
bool enabled() const
Returns whether the DHCP4o6 is enabled or not.
Definition: cfg_4o6.h:33
OptionPtr getInterfaceId() const
Returns the interface-id.
Definition: cfg_4o6.h:72
a common structure for IPv4 and IPv6 leases
Definition: lease.h:35
@ TYPE_V4
IPv4 lease.
Definition: lease.h:54
Tag for the index for searching by subnet prefix.
Definition: subnet.h:805
Subnet selector used to specify parameters used to select a subnet.
asiolink::IOAddress local_address_
Address on which the message was received.
asiolink::IOAddress option_select_
RAI link select or subnet select option.
std::string iface_name_
Name of the interface on which the message was received.
asiolink::IOAddress ciaddr_
ciaddr from the client's message.
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 giaddr_
giaddr from the client's message.
Tag for the index for searching by server identifier.
Definition: subnet.h:808
Tag for the index for searching by subnet identifier.
Definition: subnet.h:802