Kea 1.5.0
cfg_subnets4.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2018 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
39 LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
40 .arg(subnet->toText());
41 subnets_.push_back(subnet);
42}
43
44void
46 auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
47 auto subnet_it = index.find(subnet->getID());
48 if (subnet_it == index.end()) {
49 isc_throw(BadValue, "no subnet with ID of '" << subnet->getID()
50 << "' found");
51 }
52 index.erase(subnet_it);
53
54 LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET4)
55 .arg(subnet->toText());
56}
57
59CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
60 const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
61 auto subnet_it = index.find(subnet_id);
62 return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
63}
64
66CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
67 const auto& index = subnets_.get<SubnetPrefixIndexTag>();
68 auto subnet_it = index.find(subnet_text);
69 return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
70}
71
72bool
74 const auto& index = subnets_.get<SubnetServerIdIndexTag>();
75 auto subnet_it = index.find(server_id);
76 return (subnet_it != index.cend());
77}
78
81 SubnetSelector selector;
82 selector.ciaddr_ = query->getCiaddr();
83 selector.giaddr_ = query->getGiaddr();
84 selector.local_address_ = query->getLocalAddr();
85 selector.remote_address_ = query->getRemoteAddr();
86 selector.client_classes_ = query->classes_;
87 selector.iface_name_ = query->getIface();
88
89 // If the link-selection sub-option is present, extract its value.
90 // "The link-selection sub-option is used by any DHCP relay agent
91 // that desires to specify a subnet/link for a DHCP client request
92 // that it is relaying but needs the subnet/link specification to
93 // be different from the IP address the DHCP server should use
94 // when communicating with the relay agent." (RFC 3527)
95 //
96 // Try first Relay Agent Link Selection sub-option
97 OptionPtr rai = query->getOption(DHO_DHCP_AGENT_OPTIONS);
98 if (rai) {
99 OptionCustomPtr rai_custom =
100 boost::dynamic_pointer_cast<OptionCustom>(rai);
101 if (rai_custom) {
102 OptionPtr link_select =
103 rai_custom->getOption(RAI_OPTION_LINK_SELECTION);
104 if (link_select) {
105 OptionBuffer link_select_buf = link_select->getData();
106 if (link_select_buf.size() == sizeof(uint32_t)) {
107 selector.option_select_ =
108 IOAddress::fromBytes(AF_INET, &link_select_buf[0]);
109 }
110 }
111 }
112 } else {
113 // Or Subnet Selection option
114 OptionPtr sbnsel = query->getOption(DHO_SUBNET_SELECTION);
115 if (sbnsel) {
116 OptionCustomPtr oc =
117 boost::dynamic_pointer_cast<OptionCustom>(sbnsel);
118 if (oc) {
119 selector.option_select_ = oc->readAddress();
120 }
121 }
122 }
123
124 return (selector);
125}
126
129
130 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
131 subnet != subnets_.end(); ++subnet) {
132 Cfg4o6& cfg4o6 = (*subnet)->get4o6();
133
134 // Is this an 4o6 subnet at all?
135 if (!cfg4o6.enabled()) {
136 continue; // No? Let's try the next one.
137 }
138
139 // First match criteria: check if we have a prefix/len defined.
140 std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
141 if (!pref.first.isV6Zero()) {
142
143 // Let's check if the IPv6 address is in range
144 IOAddress first = firstAddrInPrefix(pref.first, pref.second);
145 IOAddress last = lastAddrInPrefix(pref.first, pref.second);
146 if ((first <= selector.remote_address_) &&
147 (selector.remote_address_ <= last)) {
148 return (*subnet);
149 }
150 }
151
152 // Second match criteria: check if the interface-id matches
153 if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
154 cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
155 return (*subnet);
156 }
157
158 // Third match criteria: check if the interface name matches
159 if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
160 && cfg4o6.getIface4o6() == selector.iface_name_) {
161 return (*subnet);
162 }
163 }
164
165 // Ok, wasn't able to find any matching subnet.
166 return (Subnet4Ptr());
167}
168
171
172 // First use RAI link select sub-option or subnet select option
173 if (!selector.option_select_.isV4Zero()) {
174 return (selectSubnet(selector.option_select_,
175 selector.client_classes_));
176 }
177
178 // If relayed message has been received, try to match the giaddr with the
179 // relay address specified for a subnet and/or shared network. It is also
180 // possible that the relay address will not match with any of the relay
181 // addresses across all subnets, but we need to verify that for all subnets
182 // before we can try to use the giaddr to match with the subnet prefix.
183 if (!selector.giaddr_.isV4Zero()) {
184 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
185 subnet != subnets_.end(); ++subnet) {
186
187 // If relay information is specified for this subnet, it must match.
188 // Otherwise, we ignore this subnet.
189 if ((*subnet)->hasRelays()) {
190 if (!(*subnet)->hasRelayAddress(selector.giaddr_)) {
191 continue;
192 }
193 } else {
194 // Relay information is not specified on the subnet level,
195 // so let's try matching on the shared network level.
196 SharedNetwork4Ptr network;
197 (*subnet)->getSharedNetwork(network);
198 if (!network || !(network->hasRelayAddress(selector.giaddr_))) {
199 continue;
200 }
201 }
202
203 // If a subnet meets the client class criteria return it.
204 if ((*subnet)->clientSupported(selector.client_classes_)) {
205 return (*subnet);
206 }
207 }
208 }
209
210 // If we got to this point it means that we were not able to match the
211 // giaddr with any of the addresses specified for subnets. Let's determine
212 // what address from the client's packet to use to match with the
213 // subnets' prefixes.
214
216 // If there is a giaddr, use it for subnet selection.
217 if (!selector.giaddr_.isV4Zero()) {
218 address = selector.giaddr_;
219
220 // If it is a Renew or Rebind, use the ciaddr.
221 } else if (!selector.ciaddr_.isV4Zero() &&
222 !selector.local_address_.isV4Bcast()) {
223 address = selector.ciaddr_;
224
225 // If ciaddr is not specified, use the source address.
226 } else if (!selector.remote_address_.isV4Zero() &&
227 !selector.local_address_.isV4Bcast()) {
228 address = selector.remote_address_;
229
230 // If local interface name is known, use the local address on this
231 // interface.
232 } else if (!selector.iface_name_.empty()) {
234 // This should never happen in the real life. Hence we throw an
235 // exception.
236 if (iface == NULL) {
237 isc_throw(isc::BadValue, "interface " << selector.iface_name_
238 << " doesn't exist and therefore it is impossible"
239 " to find a suitable subnet for its IPv4 address");
240 }
241
242 // Attempt to select subnet based on the interface name.
243 Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
244 selector.client_classes_);
245
246 // If it matches - great. If not, we'll try to use a different
247 // selection criteria below.
248 if (subnet) {
249 return (subnet);
250 } else {
251 // Let's try to get an address from the local interface and
252 // try to match it to defined subnet.
253 iface->getAddress4(address);
254 }
255 }
256
257 // Unable to find a suitable address to use for subnet selection.
258 if (address.isV4Zero()) {
259 return (Subnet4Ptr());
260 }
261
262 // We have identified an address in the client's packet that can be
263 // used for subnet selection. Match this packet with the subnets.
264 return (selectSubnet(address, selector.client_classes_));
265}
266
268CfgSubnets4::selectSubnet(const std::string& iface,
269 const ClientClasses& client_classes) const {
270 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
271 subnet != subnets_.end(); ++subnet) {
272
273 Subnet4Ptr subnet_selected;
274
275 // First, try subnet specific interface name.
276 if (!(*subnet)->getIface().empty()) {
277 if ((*subnet)->getIface() == iface) {
278 subnet_selected = (*subnet);
279 }
280
281 } else {
282 // Interface not specified for a subnet, so let's try if
283 // we can match with shared network specific setting of
284 // the interface.
285 SharedNetwork4Ptr network;
286 (*subnet)->getSharedNetwork(network);
287 if (network && !network->getIface().empty() &&
288 (network->getIface() == iface)) {
289 subnet_selected = (*subnet);
290 }
291 }
292
293 if (subnet_selected) {
294
295 // If a subnet meets the client class criteria return it.
296 if (subnet_selected->clientSupported(client_classes)) {
298 DHCPSRV_CFGMGR_SUBNET4_IFACE)
299 .arg((*subnet)->toText())
300 .arg(iface);
301 return (subnet_selected);
302 }
303 }
304 }
305
306 // Failed to find a subnet.
307 return (Subnet4Ptr());
308}
309
312
315 for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
316 if ((*subnet)->getID() == id) {
317 return (*subnet);
318 }
319 }
320 return (Subnet4Ptr());
321}
322
325 const ClientClasses& client_classes) const {
326 for (Subnet4Collection::const_iterator subnet = subnets_.begin();
327 subnet != subnets_.end(); ++subnet) {
328
329 // Address is in range for the subnet prefix, so return it.
330 if (!(*subnet)->inRange(address)) {
331 continue;
332 }
333
334 // If a subnet meets the client class criteria return it.
335 if ((*subnet)->clientSupported(client_classes)) {
336 LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
337 .arg((*subnet)->toText())
338 .arg(address.toText());
339 return (*subnet);
340 }
341 }
342
343 // Failed to find a subnet.
344 return (Subnet4Ptr());
345}
346
347void
349 using namespace isc::stats;
350
351 // For each v4 subnet currently configured, remove the statistic.
352 StatsMgr& stats_mgr = StatsMgr::instance();
353 for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
354 subnet4 != subnets_.end(); ++subnet4) {
355 SubnetID subnet_id = (*subnet4)->getID();
356 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
357 "total-addresses"));
358
359 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
360 "assigned-addresses"));
361
362 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
363 "declined-addresses"));
364
365 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
366 "declined-reclaimed-addresses"));
367
368 stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
369 "reclaimed-leases"));
370 }
371}
372
373void
375 using namespace isc::stats;
376
377 StatsMgr& stats_mgr = StatsMgr::instance();
378 for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
379 subnet4 != subnets_.end(); ++subnet4) {
380 SubnetID subnet_id = (*subnet4)->getID();
381
382 stats_mgr.setValue(StatsMgr::
383 generateName("subnet", subnet_id, "total-addresses"),
384 static_cast<int64_t>
385 ((*subnet4)->getPoolCapacity(Lease::
386 TYPE_V4)));
387 }
388
389 // Only recount the stats if we have subnets.
390 if (subnets_.begin() != subnets_.end()) {
392 }
393}
394
398 // Iterate subnets
399 for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
400 subnet != subnets_.cend(); ++subnet) {
401 result->add((*subnet)->toElement());
402 }
403 return (result);
404}
405
406} // end of namespace isc::dhcp
407} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:263
ConstSubnet4Ptr getBySubnetId(const SubnetID &subnet_id) const
Returns const pointer to a subnet identified by the specified subnet identifier.
Definition: cfg_subnets4.cc:59
void del(const ConstSubnet4Ptr &subnet)
Removes subnet from the configuration.
Definition: cfg_subnets4.cc:45
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.
Definition: cfg_subnets4.cc:73
ConstSubnet4Ptr getByPrefix(const std::string &subnet_prefix) const
Returns const pointer to a subnet which matches the specified prefix in the canonical form.
Definition: cfg_subnets4.cc:66
void updateStatistics()
Updates statistics.
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.
void add(const Subnet4Ptr &subnet)
Adds new subnet to the configuration.
Definition: cfg_subnets4.cc:27
void removeStatistics()
Removes statistics.
static SubnetSelector initSelector(const Pkt4Ptr &query)
Build selector from a client's message.
Definition: cfg_subnets4.cc:80
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:34
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:752
static LeaseMgr & instance()
Return current lease manager.
void recountLeaseStats4()
Recalculates per-subnet and global stats for IPv4 leases.
Definition: lease_mgr.cc:65
Statistics Manager class.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
bool del(const std::string &name)
Removes specified statistic.
Definition: stats_mgr.cc:99
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
Definition: stats_mgr.cc:31
#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:22
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:464
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
@ DHO_SUBNET_SELECTION
Definition: dhcp4.h:178
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition: subnet.h:461
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
boost::shared_ptr< Iface > IfacePtr
Definition: iface_mgr.h:457
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
This code is based on similar code from the Dibbler project.
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:25
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:38
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
returns a last address in a given prefix
Defines the logger used by the top-level component of kea-dhcp-ddns.
This structure contains information about DHCP4o6 (RFC7341)
Definition: cfg_4o6.h:21
std::string getIface4o6() const
Returns the DHCP4o6 interface.
Definition: cfg_4o6.h:44
bool enabled() const
Returns whether the DHCP4o6 is enabled or not.
Definition: cfg_4o6.h:32
std::pair< asiolink::IOAddress, uint8_t > getSubnet4o6() const
Returns prefix/len for the IPv6 subnet.
Definition: cfg_4o6.h:57
OptionPtr getInterfaceId() const
Returns the interface-id.
Definition: cfg_4o6.h:71
a common structure for IPv4 and IPv6 leases
Definition: lease.h:35
Tag for the index for searching by subnet prefix.
Definition: subnet.h:742
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:745
Tag for the index for searching by subnet identifier.
Definition: subnet.h:739