cfg_subnets4.cc 11.2 KB
Newer Older
1
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6

7
#include <config.h>
8 9 10
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/dhcpsrv_log.h>
11
#include <dhcpsrv/lease_mgr_factory.h>
12
#include <dhcpsrv/subnet_id.h>
13 14
#include <dhcpsrv/addr_utilities.h>
#include <asiolink/io_address.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
15
#include <stats/stats_mgr.h>
16
#include <sstream>
17 18

using namespace isc::asiolink;
19
using namespace isc::data;
20 21 22 23 24 25 26 27 28

namespace isc {
namespace dhcp {

void
CfgSubnets4::add(const Subnet4Ptr& subnet) {
    /// @todo: Check that this new subnet does not cross boundaries of any
    /// other already defined subnet.
    if (isDuplicate(*subnet)) {
29
        isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
30 31 32 33 34 35 36
                  << subnet->getID() << "' is already in use");
    }
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
              .arg(subnet->toText());
    subnets_.push_back(subnet);
}

37 38
ConstSubnet4Ptr
CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
39
    const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
40 41 42 43 44 45 46 47 48 49 50
    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());
}

51 52 53 54 55 56 57 58 59 60 61 62 63 64
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();
65
        if (!pref.first.isV6Zero()) {
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

            // 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());
}

93
Subnet4Ptr
94
CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
95

96 97 98 99 100 101
    // First use RAI link select sub-option or subnet select option
    if (!selector.option_select_.isV4Zero()) {
        return (selectSubnet(selector.option_select_,
                             selector.client_classes_));
    }

102 103
    // If relayed message has been received, try to match the giaddr with the
    // relay address specified for a subnet. It is also possible that the relay
Andrei Pavel's avatar
Andrei Pavel committed
104
    // address will not match with any of the relay addresses across all
105 106
    // subnets, but we need to verify that for all subnets before we can try
    // to use the giaddr to match with the subnet prefix.
Francis Dupont's avatar
Francis Dupont committed
107
    if (!selector.giaddr_.isV4Zero()) {
108 109
        for (Subnet4Collection::const_iterator subnet = subnets_.begin();
             subnet != subnets_.end(); ++subnet) {
110 111 112

            // Check if the giaddr is equal to the one defined for the subnet.
            if (selector.giaddr_ != (*subnet)->getRelayInfo().addr_) {
113 114 115
                continue;
            }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
116
            // If a subnet meets the client class criteria return it.
117
            if ((*subnet)->clientSupported(selector.client_classes_)) {
118 119 120 121 122 123 124 125 126 127
                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.

Francis Dupont's avatar
Francis Dupont committed
128
    IOAddress address = IOAddress::IPV4_ZERO_ADDRESS();
129
    // If there is a giaddr, use it for subnet selection.
Francis Dupont's avatar
Francis Dupont committed
130
    if (!selector.giaddr_.isV4Zero()) {
131 132 133
        address = selector.giaddr_;

    // If it is a Renew or Rebind, use the ciaddr.
Francis Dupont's avatar
Francis Dupont committed
134 135
    } else if (!selector.ciaddr_.isV4Zero() &&
               !selector.local_address_.isV4Bcast()) {
136 137 138
        address = selector.ciaddr_;

    // If ciaddr is not specified, use the source address.
Francis Dupont's avatar
Francis Dupont committed
139 140
    } else if (!selector.remote_address_.isV4Zero() &&
               !selector.local_address_.isV4Bcast()) {
141 142 143 144
        address = selector.remote_address_;

    // If local interface name is known, use the local address on this
    // interface.
145
    } else if (!selector.iface_name_.empty()) {
146
        IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
147 148 149
        // This should never happen in the real life. Hence we throw an
        // exception.
        if (iface == NULL) {
150
            isc_throw(isc::BadValue, "interface " << selector.iface_name_
151 152 153
                      << " doesn't exist and therefore it is impossible"
                      " to find a suitable subnet for its IPv4 address");
        }
154 155 156 157 158 159 160 161 162

        // 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);
163 164 165 166
        } else {
            // Let's try to get an address from the local interface and
            // try to match it to defined subnet.
            iface->getAddress4(address);
167
        }
168 169 170
    }

    // Unable to find a suitable address to use for subnet selection.
Francis Dupont's avatar
Francis Dupont committed
171
    if (address.isV4Zero()) {
172 173 174 175
        return (Subnet4Ptr());
    }

    // We have identified an address in the client's packet that can be
Tomek Mrugalski's avatar
Tomek Mrugalski committed
176
    // used for subnet selection. Match this packet with the subnets.
177
    return (selectSubnet(address, selector.client_classes_));
178 179
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
Subnet4Ptr
CfgSubnets4::selectSubnet(const std::string& iface,
                 const ClientClasses& client_classes) const {
    for (Subnet4Collection::const_iterator subnet = subnets_.begin();
         subnet != subnets_.end(); ++subnet) {

        // If there's no interface specified for this subnet, proceed to
        // the next subnet.
        if ((*subnet)->getIface().empty()) {
            continue;
        }

        // If it's specified, but does not match, proceed to the next
        // subnet.
        if ((*subnet)->getIface() != iface) {
            continue;
        }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
198
        // If a subnet meets the client class criteria return it.
199
        if ((*subnet)->clientSupported(client_classes)) {
200 201
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
                      DHCPSRV_CFGMGR_SUBNET4_IFACE)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
202 203
                .arg((*subnet)->toText())
                .arg(iface);
204 205 206 207 208 209 210 211
            return (*subnet);
        }
    }

    // Failed to find a subnet.
    return (Subnet4Ptr());
}

212 213 214 215 216 217 218 219 220 221 222 223 224
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());
}

225
Subnet4Ptr
226
CfgSubnets4::selectSubnet(const IOAddress& address,
227
                 const ClientClasses& client_classes) const {
228 229
    for (Subnet4Collection::const_iterator subnet = subnets_.begin();
         subnet != subnets_.end(); ++subnet) {
230

231 232
        // Address is in range for the subnet prefix, so return it.
        if (!(*subnet)->inRange(address)) {
233 234 235
            continue;
        }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
236
        // If a subnet meets the client class criteria return it.
237
        if ((*subnet)->clientSupported(client_classes)) {
238
            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
239 240
                .arg((*subnet)->toText())
                .arg(address.toText());
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
            return (*subnet);
        }
    }

    // Failed to find a subnet.
    return (Subnet4Ptr());
}

bool
CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
    for (Subnet4Collection::const_iterator subnet_it = subnets_.begin();
         subnet_it != subnets_.end(); ++subnet_it) {
        if ((*subnet_it)->getID() == subnet.getID()) {
            return (true);
        }
    }
    return (false);
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
260 261 262 263 264
void
CfgSubnets4::removeStatistics() {
    using namespace isc::stats;

    // For each v4 subnet currently configured, remove the statistic.
265
    StatsMgr& stats_mgr = StatsMgr::instance();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
266 267
    for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
         subnet4 != subnets_.end(); ++subnet4) {
268 269 270
        SubnetID subnet_id = (*subnet4)->getID();
        stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
                                             "total-addresses"));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
271

272 273
        stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
                                             "assigned-addresses"));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
274

275 276 277 278 279
        stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
                                             "declined-addresses"));

        stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
                                             "declined-reclaimed-addresses"));
280 281 282

        stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
                                             "reclaimed-leases"));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
283 284
    }
}
285

Tomek Mrugalski's avatar
Tomek Mrugalski committed
286 287 288
void
CfgSubnets4::updateStatistics() {
    using namespace isc::stats;
289

290 291 292 293 294 295 296 297 298 299 300
    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)));
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
301

302 303
    // Only recount the stats if we have subnets.
    if (subnets_.begin() != subnets_.end()) {
304
            LeaseMgrFactory::instance().recountLeaseStats4();
Tomek Mrugalski's avatar
Tomek Mrugalski committed
305 306
    }
}
307

308 309 310 311 312 313
ElementPtr
CfgSubnets4::toElement() const {
    ElementPtr result = Element::createList();
    // Iterate subnets
    for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
         subnet != subnets_.cend(); ++subnet) {
314
        result->add((*subnet)->toElement());
315 316 317 318
    }
    return (result);
}

319 320
} // end of namespace isc::dhcp
} // end of namespace isc