Kea  1.5.0
cfg_subnets6.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/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 
39  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
40  .arg(subnet->toText());
41  subnets_.push_back(subnet);
42 }
43 
44 void
45 CfgSubnets6::del(const ConstSubnet6Ptr& 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_SUBNET6)
55  .arg(subnet->toText());
56 }
57 
59 CfgSubnets6::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) : ConstSubnet6Ptr());
63 }
64 
66 CfgSubnets6::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) : ConstSubnet6Ptr());
70 }
71 
73 CfgSubnets6::initSelector(const Pkt6Ptr& query) {
74  // Initialize subnet selector with the values used to select the subnet.
75  SubnetSelector selector;
76  selector.iface_name_ = query->getIface();
77  selector.remote_address_ = query->getRemoteAddr();
78  selector.first_relay_linkaddr_ = IOAddress("::");
79  selector.client_classes_ = query->classes_;
80 
81  // Initialize fields specific to relayed messages.
82  if (!query->relay_info_.empty()) {
83  BOOST_REVERSE_FOREACH(Pkt6::RelayInfo relay, query->relay_info_) {
84  if (!relay.linkaddr_.isV6Zero() &&
85  !relay.linkaddr_.isV6LinkLocal()) {
86  selector.first_relay_linkaddr_ = relay.linkaddr_;
87  break;
88  }
89  }
90  selector.interface_id_ =
91  query->getAnyRelayOption(D6O_INTERFACE_ID,
92  Pkt6::RELAY_GET_FIRST);
93  }
94 
95  return (selector);
96 }
97 
99 CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
100  Subnet6Ptr subnet;
101 
102  // If relay agent link address is set to zero it means that we're dealing
103  // with a directly connected client.
104  if (selector.first_relay_linkaddr_ == IOAddress("::")) {
105  // If interface name is known try to match it with interface names
106  // specified for configured subnets.
107  if (!selector.iface_name_.empty()) {
108  subnet = selectSubnet(selector.iface_name_,
109  selector.client_classes_);
110  }
111 
112  // If interface name didn't match, try the client's address.
113  if (!subnet && selector.remote_address_ != IOAddress("::")) {
114  subnet = selectSubnet(selector.remote_address_,
115  selector.client_classes_);
116  }
117 
118  // If relay agent link address is set, we're dealing with a relayed message.
119  } else {
120 
121  // Find the subnet using the Interface Id option, if present.
122  subnet = selectSubnet(selector.interface_id_, selector.client_classes_);
123 
124  // If Interface ID option could not be matched for any subnet, try
125  // the relay agent link address.
126  if (!subnet) {
127  subnet = selectSubnet(selector.first_relay_linkaddr_,
128  selector.client_classes_,
129  true);
130  }
131  }
132 
133  // Return subnet found, or NULL if not found.
134  return (subnet);
135 }
136 
138 CfgSubnets6::selectSubnet(const asiolink::IOAddress& address,
139  const ClientClasses& client_classes,
140  const bool is_relay_address) const {
141 
142  // If the specified address is a relay address we first need to match
143  // it with the relay addresses specified for all subnets.
144  if (is_relay_address) {
145  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
146  subnet != subnets_.end(); ++subnet) {
147 
148  // If the specified address matches a relay address, return this
149  // subnet.
150  if (is_relay_address &&
151  ((*subnet)->hasRelayAddress(address)) &&
152  (*subnet)->clientSupported(client_classes)) {
154  DHCPSRV_CFGMGR_SUBNET6_RELAY)
155  .arg((*subnet)->toText()).arg(address.toText());
156  return (*subnet);
157  }
158 
159  }
160  }
161 
162  // No success so far. Check if the specified address is in range
163  // with any subnet.
164  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
165  subnet != subnets_.end(); ++subnet) {
166  if ((*subnet)->inRange(address) &&
167  (*subnet)->clientSupported(client_classes)) {
168  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
169  .arg((*subnet)->toText()).arg(address.toText());
170  return (*subnet);
171  }
172  }
173 
174  // Nothing found.
175  return (Subnet6Ptr());
176 }
177 
178 
180 CfgSubnets6::selectSubnet(const std::string& iface_name,
181  const ClientClasses& client_classes) const {
182 
183  // If empty interface specified, we can't select subnet by interface.
184  if (!iface_name.empty()) {
185  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
186  subnet != subnets_.end(); ++subnet) {
187 
188  // If interface name matches with the one specified for the subnet
189  // and the client is not rejected based on the classification,
190  // return the subnet.
191  if ((iface_name == (*subnet)->getIface()) &&
192  (*subnet)->clientSupported(client_classes)) {
193 
195  DHCPSRV_CFGMGR_SUBNET6_IFACE)
196  .arg((*subnet)->toText()).arg(iface_name);
197  return (*subnet);
198  }
199  }
200  }
201 
202  // No subnet found for this interface name.
203  return (Subnet6Ptr());
204 }
205 
207 CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
208  const ClientClasses& client_classes) const {
209  // We can only select subnet using an interface id, if the interface
210  // id is known.
211  if (interface_id) {
212  for (Subnet6Collection::const_iterator subnet = subnets_.begin();
213  subnet != subnets_.end(); ++subnet) {
214 
215  // If interface id matches for the subnet and the subnet is not
216  // rejected based on the classification.
217  if ((*subnet)->getInterfaceId() &&
218  (*subnet)->getInterfaceId()->equals(interface_id) &&
219  (*subnet)->clientSupported(client_classes)) {
220 
222  DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
223  .arg((*subnet)->toText());
224  return (*subnet);
225  }
226  }
227  }
228  // No subnet found.
229  return (Subnet6Ptr());
230 }
231 
233 CfgSubnets6::getSubnet(const SubnetID id) const {
234 
237  for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
238  if ((*subnet)->getID() == id) {
239  return (*subnet);
240  }
241  }
242  return (Subnet6Ptr());
243 }
244 
245 void
246 CfgSubnets6::removeStatistics() {
247  using namespace isc::stats;
248 
249  StatsMgr& stats_mgr = StatsMgr::instance();
250  // For each v6 subnet currently configured, remove the statistics.
251  for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
252  subnet6 != subnets_.end(); ++subnet6) {
253  SubnetID subnet_id = (*subnet6)->getID();
254  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-nas"));
255 
256  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
257  "assigned-nas"));
258 
259  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-pds"));
260 
261  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
262  "assigned-pds"));
263 
264  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
265  "declined-addresses"));
266 
267  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
268  "declined-reclaimed-addresses"));
269 
270  stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
271  "reclaimed-leases"));
272  }
273 }
274 
275 void
276 CfgSubnets6::updateStatistics() {
277  using namespace isc::stats;
278 
279  StatsMgr& stats_mgr = StatsMgr::instance();
280  // For each v6 subnet currently configured, calculate totals
281  for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
282  subnet6 != subnets_.end(); ++subnet6) {
283  SubnetID subnet_id = (*subnet6)->getID();
284 
285  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
286  "total-nas"),
287  static_cast<int64_t>
288  ((*subnet6)->getPoolCapacity(Lease::TYPE_NA)));
289 
290  stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
291  "total-pds"),
292  static_cast<int64_t>
293  ((*subnet6)->getPoolCapacity(Lease::TYPE_PD)));
294  }
295 
296  // Only recount the stats if we have subnets.
297  if (subnets_.begin() != subnets_.end()) {
298  LeaseMgrFactory::instance().recountLeaseStats6();
299  }
300 }
301 
303 CfgSubnets6::toElement() const {
304  ElementPtr result = Element::createList();
305  // Iterate subnets
306  for (Subnet6Collection::const_iterator subnet = subnets_.cbegin();
307  subnet != subnets_.cend(); ++subnet) {
308  result->add((*subnet)->toElement());
309  }
310  return (result);
311 }
312 
313 } // end of namespace isc::dhcp
314 } // 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:34
asiolink::IOAddress remote_address_
Source address of the message.
Tag for the index for searching by subnet identifier.
Definition: subnet.h:739
OptionPtr interface_id_
Interface id option.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
Tag for the index for searching by subnet prefix.
Definition: subnet.h:742
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
structure that describes a single relay information
Definition: pkt6.h:85
bool del(const std::string &name)
Removes specified statistic.
Definition: stats_mgr.cc:99
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition: subnet.h:623
Defines the logger used by the top-level component of kea-dhcp-ddns.
#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:629
Container for storing client class names.
Definition: classify.h:43
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
Definition: stats_mgr.cc:31
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24