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>
9 #include <dhcp/option_custom.h>
10 #include <dhcpsrv/cfg_subnets4.h>
11 #include <dhcpsrv/dhcpsrv_log.h>
13 #include <dhcpsrv/shared_network.h>
14 #include <dhcpsrv/subnet_id.h>
15 #include <asiolink/io_address.h>
17 #include <stats/stats_mgr.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 CfgSubnets4::add(const Subnet4Ptr& subnet) {
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 
44 void
45 CfgSubnets4::del(const ConstSubnet4Ptr& subnet) {
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 
59 CfgSubnets4::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 
66 CfgSubnets4::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 
72 bool
73 CfgSubnets4::hasSubnetWithServerId(const asiolink::IOAddress& server_id) const {
74  const auto& index = subnets_.get<SubnetServerIdIndexTag>();
75  auto subnet_it = index.find(server_id);
76  return (subnet_it != index.cend());
77 }
78 
80 CfgSubnets4::initSelector(const Pkt4Ptr& query) {
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 
128 CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
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 
170 CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
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()) {
233  IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
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 
268 CfgSubnets4::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 
311 CfgSubnets4::getSubnet(const SubnetID id) const {
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 
324 CfgSubnets4::selectSubnet(const IOAddress& address,
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 
347 void
348 CfgSubnets4::removeStatistics() {
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 
373 void
374 CfgSubnets4::updateStatistics() {
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()) {
391  LeaseMgrFactory::instance().recountLeaseStats4();
392  }
393 }
394 
396 CfgSubnets4::toElement() const {
397  ElementPtr result = Element::createList();
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
isc::dhcp::OptionBuffer
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
isc::dhcp::DuplicateSubnetID
Exception thrown upon attempt to add subnet with an ID that belongs to the subnet that already exists...
Definition: subnet_id.h:34
isc::dhcp::Cfg4o6::enabled
bool enabled() const
Returns whether the DHCP4o6 is enabled or not.
Definition: cfg_4o6.h:32
isc::dhcp::SubnetSelector::ciaddr_
asiolink::IOAddress ciaddr_
ciaddr from the client's message.
Definition: subnet_selector.h:27
isc::dhcp::SubnetSelector::giaddr_
asiolink::IOAddress giaddr_
giaddr from the client's message.
Definition: subnet_selector.h:29
iface_mgr.h
isc::dhcp::Cfg4o6::getIface4o6
std::string getIface4o6() const
Returns the DHCP4o6 interface.
Definition: cfg_4o6.h:44
isc::dhcp::SubnetSelector::remote_address_
asiolink::IOAddress remote_address_
Source address of the message.
Definition: subnet_selector.h:45
io_address.h
isc::stats::StatsMgr::del
bool del(const std::string &name)
Removes specified statistic.
Definition: stats_mgr.cc:99
isc::dhcp::SubnetServerIdIndexTag
Tag for the index for searching by server identifier.
Definition: subnet.h:745
lease_mgr_factory.h
isc::dhcp::SubnetSelector
Subnet selector used to specify parameters used to select a subnet.
Definition: subnet_selector.h:23
cfg_subnets4.h
isc::data
Definition: cfg_to_element.h:25
isc::dhcp::DHO_DHCP_AGENT_OPTIONS
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
isc::dhcp::SharedNetwork4Ptr
boost::shared_ptr< SharedNetwork4 > SharedNetwork4Ptr
Pointer to SharedNetwork4 object.
Definition: shared_network.h:163
shared_network.h
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
isc::dhcp::firstAddrInPrefix
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
This code is based on similar code from the Dibbler project.
Definition: addr_utilities.cc:176
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
dhcpsrv_log.h
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::dhcp::DHCPSRV_DBG_TRACE
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
isc::dhcp::SubnetSelector::interface_id_
OptionPtr interface_id_
Interface id option.
Definition: subnet_selector.h:37
isc::dhcp::Cfg4o6::getInterfaceId
OptionPtr getInterfaceId() const
Returns the interface-id.
Definition: cfg_4o6.h:71
isc::dhcp::Subnet4Ptr
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:464
isc::dhcp::ConstSubnet4Ptr
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition: subnet.h:458
addr_utilities.h
isc::dhcp::OptionCustomPtr
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
Definition: option_custom.h:464
isc::stats
Definition: context.cc:13
isc::dhcp::SubnetPrefixIndexTag
Tag for the index for searching by subnet prefix.
Definition: subnet.h:742
stats_mgr.h
isc::stats::StatsMgr
Statistics Manager class.
Definition: lib/stats/stats_mgr.h:61
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
isc::dhcp::SubnetSelector::client_classes_
ClientClasses client_classes_
Classes that the client belongs to.
Definition: subnet_selector.h:47
isc::dhcp::SubnetSelector::iface_name_
std::string iface_name_
Name of the interface on which the message was received.
Definition: subnet_selector.h:49
isc::dhcp::lastAddrInPrefix
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
returns a last address in a given prefix
Definition: addr_utilities.cc:187
subnet_id.h
isc::dhcp::SubnetSelector::local_address_
asiolink::IOAddress local_address_
Address on which the message was received.
Definition: subnet_selector.h:43
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
isc::dhcp::SubnetSubnetIdIndexTag
Tag for the index for searching by subnet identifier.
Definition: subnet.h:739
isc::dhcp::SubnetSelector::option_select_
asiolink::IOAddress option_select_
RAI link select or subnet select option.
Definition: subnet_selector.h:31
isc::dhcp::SubnetID
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
isc::dhcp::Lease
a common structure for IPv4 and IPv6 leases
Definition: lease.h:35
isc::dhcp::Cfg4o6::getSubnet4o6
std::pair< asiolink::IOAddress, uint8_t > getSubnet4o6() const
Returns prefix/len for the IPv6 subnet.
Definition: cfg_4o6.h:57
option_custom.h
isc::dhcp::IfacePtr
boost::shared_ptr< Iface > IfacePtr
Definition: iface_mgr.h:457
isc::dhcp::DHO_SUBNET_SELECTION
@ DHO_SUBNET_SELECTION
Definition: dhcp4.h:178
isc::dhcp::dhcpsrv_logger
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
isc::dhcp::ClientClasses
Container for storing client class names.
Definition: classify.h:43
isc::stats::StatsMgr::setValue
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
Definition: stats_mgr.cc:31
isc::dhcp::Cfg4o6
This structure contains information about DHCP4o6 (RFC7341)
Definition: cfg_4o6.h:21