123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- // Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <config.h>
- #include <dhcp/iface_mgr.h>
- #include <dhcpsrv/cfg_subnets4.h>
- #include <dhcpsrv/dhcpsrv_log.h>
- #include <dhcpsrv/lease_mgr_factory.h>
- #include <dhcpsrv/shared_network.h>
- #include <dhcpsrv/subnet_id.h>
- #include <dhcpsrv/addr_utilities.h>
- #include <asiolink/io_address.h>
- #include <stats/stats_mgr.h>
- #include <sstream>
- using namespace isc::asiolink;
- using namespace isc::data;
- namespace isc {
- namespace dhcp {
- void
- CfgSubnets4::add(const Subnet4Ptr& subnet) {
- if (getBySubnetId(subnet->getID())) {
- isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
- << subnet->getID() << "' is already in use");
- } else if (getByPrefix(subnet->toText())) {
- /// @todo: Check that this new subnet does not cross boundaries of any
- /// other already defined subnet.
- isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '"
- << subnet->toText() << "' already exists");
- }
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
- .arg(subnet->toText());
- subnets_.push_back(subnet);
- }
- void
- CfgSubnets4::del(const ConstSubnet4Ptr& subnet) {
- auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
- auto subnet_it = index.find(subnet->getID());
- if (subnet_it == index.end()) {
- isc_throw(BadValue, "no subnet with ID of '" << subnet->getID()
- << "' found");
- }
- index.erase(subnet_it);
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET4)
- .arg(subnet->toText());
- }
- ConstSubnet4Ptr
- CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
- const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
- auto subnet_it = index.find(subnet_id);
- return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
- }
- ConstSubnet4Ptr
- CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
- const auto& index = subnets_.get<SubnetPrefixIndexTag>();
- auto subnet_it = index.find(subnet_text);
- return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
- }
- Subnet4Ptr
- CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
- for (Subnet4Collection::const_iterator subnet = subnets_.begin();
- subnet != subnets_.end(); ++subnet) {
- Cfg4o6& cfg4o6 = (*subnet)->get4o6();
- // Is this an 4o6 subnet at all?
- if (!cfg4o6.enabled()) {
- continue; // No? Let's try the next one.
- }
- // First match criteria: check if we have a prefix/len defined.
- std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
- if (!pref.first.isV6Zero()) {
- // Let's check if the IPv6 address is in range
- IOAddress first = firstAddrInPrefix(pref.first, pref.second);
- IOAddress last = lastAddrInPrefix(pref.first, pref.second);
- if ((first <= selector.remote_address_) &&
- (selector.remote_address_ <= last)) {
- return (*subnet);
- }
- }
- // Second match criteria: check if the interface-id matches
- if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
- cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
- return (*subnet);
- }
- // Third match criteria: check if the interface name matches
- if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
- && cfg4o6.getIface4o6() == selector.iface_name_) {
- return (*subnet);
- }
- }
- // Ok, wasn't able to find any matching subnet.
- return (Subnet4Ptr());
- }
- Subnet4Ptr
- CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
- // First use RAI link select sub-option or subnet select option
- if (!selector.option_select_.isV4Zero()) {
- return (selectSubnet(selector.option_select_,
- selector.client_classes_));
- }
- // If relayed message has been received, try to match the giaddr with the
- // relay address specified for a subnet and/or shared network. It is also
- // possible that the relay address will not match with any of the relay
- // addresses across all subnets, but we need to verify that for all subnets
- // before we can try to use the giaddr to match with the subnet prefix.
- if (!selector.giaddr_.isV4Zero()) {
- for (Subnet4Collection::const_iterator subnet = subnets_.begin();
- subnet != subnets_.end(); ++subnet) {
- // If relay information is specified for this subnet, it must match.
- // Otherwise, we ignore this subnet.
- if (!(*subnet)->getRelayInfo().addr_.isV4Zero()) {
- if (selector.giaddr_ != (*subnet)->getRelayInfo().addr_) {
- continue;
- }
- } else {
- // Relay information is not specified on the subnet level,
- // so let's try matching on the shared network level.
- SharedNetwork4Ptr network;
- (*subnet)->getSharedNetwork(network);
- if (!network || (selector.giaddr_ != network->getRelayInfo().addr_)) {
- continue;
- }
- }
- // If a subnet meets the client class criteria return it.
- if ((*subnet)->clientSupported(selector.client_classes_)) {
- return (*subnet);
- }
- }
- }
- // If we got to this point it means that we were not able to match the
- // giaddr with any of the addresses specified for subnets. Let's determine
- // what address from the client's packet to use to match with the
- // subnets' prefixes.
- IOAddress address = IOAddress::IPV4_ZERO_ADDRESS();
- // If there is a giaddr, use it for subnet selection.
- if (!selector.giaddr_.isV4Zero()) {
- address = selector.giaddr_;
- // If it is a Renew or Rebind, use the ciaddr.
- } else if (!selector.ciaddr_.isV4Zero() &&
- !selector.local_address_.isV4Bcast()) {
- address = selector.ciaddr_;
- // If ciaddr is not specified, use the source address.
- } else if (!selector.remote_address_.isV4Zero() &&
- !selector.local_address_.isV4Bcast()) {
- address = selector.remote_address_;
- // If local interface name is known, use the local address on this
- // interface.
- } else if (!selector.iface_name_.empty()) {
- IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
- // This should never happen in the real life. Hence we throw an
- // exception.
- if (iface == NULL) {
- isc_throw(isc::BadValue, "interface " << selector.iface_name_
- << " doesn't exist and therefore it is impossible"
- " to find a suitable subnet for its IPv4 address");
- }
- // Attempt to select subnet based on the interface name.
- Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
- selector.client_classes_);
- // If it matches - great. If not, we'll try to use a different
- // selection criteria below.
- if (subnet) {
- return (subnet);
- } else {
- // Let's try to get an address from the local interface and
- // try to match it to defined subnet.
- iface->getAddress4(address);
- }
- }
- // Unable to find a suitable address to use for subnet selection.
- if (address.isV4Zero()) {
- return (Subnet4Ptr());
- }
- // We have identified an address in the client's packet that can be
- // used for subnet selection. Match this packet with the subnets.
- return (selectSubnet(address, selector.client_classes_));
- }
- Subnet4Ptr
- CfgSubnets4::selectSubnet(const std::string& iface,
- const ClientClasses& client_classes) const {
- for (Subnet4Collection::const_iterator subnet = subnets_.begin();
- subnet != subnets_.end(); ++subnet) {
- Subnet4Ptr subnet_selected;
- // First, try subnet specific interface name.
- if (!(*subnet)->getIface().empty()) {
- if ((*subnet)->getIface() == iface) {
- subnet_selected = (*subnet);
- }
- } else {
- // Interface not specified for a subnet, so let's try if
- // we can match with shared network specific setting of
- // the interface.
- SharedNetwork4Ptr network;
- (*subnet)->getSharedNetwork(network);
- if (network && !network->getIface().empty() &&
- (network->getIface() == iface)) {
- subnet_selected = (*subnet);
- }
- }
- if (subnet_selected) {
- // If a subnet meets the client class criteria return it.
- if (subnet_selected->clientSupported(client_classes)) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
- DHCPSRV_CFGMGR_SUBNET4_IFACE)
- .arg((*subnet)->toText())
- .arg(iface);
- return (subnet_selected);
- }
- }
- }
- // Failed to find a subnet.
- return (Subnet4Ptr());
- }
- Subnet4Ptr
- CfgSubnets4::getSubnet(const SubnetID id) const {
- /// @todo: Once this code is migrated to multi-index container, use
- /// an index rather than full scan.
- for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
- if ((*subnet)->getID() == id) {
- return (*subnet);
- }
- }
- return (Subnet4Ptr());
- }
- Subnet4Ptr
- CfgSubnets4::selectSubnet(const IOAddress& address,
- const ClientClasses& client_classes) const {
- for (Subnet4Collection::const_iterator subnet = subnets_.begin();
- subnet != subnets_.end(); ++subnet) {
- // Address is in range for the subnet prefix, so return it.
- if (!(*subnet)->inRange(address)) {
- continue;
- }
- // If a subnet meets the client class criteria return it.
- if ((*subnet)->clientSupported(client_classes)) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
- .arg((*subnet)->toText())
- .arg(address.toText());
- return (*subnet);
- }
- }
- // Failed to find a subnet.
- return (Subnet4Ptr());
- }
- void
- CfgSubnets4::removeStatistics() {
- using namespace isc::stats;
- // For each v4 subnet currently configured, remove the statistic.
- StatsMgr& stats_mgr = StatsMgr::instance();
- for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
- subnet4 != subnets_.end(); ++subnet4) {
- SubnetID subnet_id = (*subnet4)->getID();
- stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
- "total-addresses"));
- stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
- "assigned-addresses"));
- stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
- "declined-addresses"));
- stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
- "declined-reclaimed-addresses"));
- stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
- "reclaimed-leases"));
- }
- }
- void
- CfgSubnets4::updateStatistics() {
- using namespace isc::stats;
- StatsMgr& stats_mgr = StatsMgr::instance();
- for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
- subnet4 != subnets_.end(); ++subnet4) {
- SubnetID subnet_id = (*subnet4)->getID();
- stats_mgr.setValue(StatsMgr::
- generateName("subnet", subnet_id, "total-addresses"),
- static_cast<int64_t>
- ((*subnet4)->getPoolCapacity(Lease::
- TYPE_V4)));
- }
- // Only recount the stats if we have subnets.
- if (subnets_.begin() != subnets_.end()) {
- LeaseMgrFactory::instance().recountLeaseStats4();
- }
- }
- ElementPtr
- CfgSubnets4::toElement() const {
- ElementPtr result = Element::createList();
- // Iterate subnets
- for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
- subnet != subnets_.cend(); ++subnet) {
- result->add((*subnet)->toElement());
- }
- return (result);
- }
- } // end of namespace isc::dhcp
- } // end of namespace isc
|