subnet.cc 10.6 KB
Newer Older
1
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
#include <config.h>

17
#include <asiolink/io_address.h>
18
#include <dhcp/option_space.h>
19
20
21
#include <dhcpsrv/addr_utilities.h>
#include <dhcpsrv/subnet.h>

22
#include <sstream>
23
24
25
26
27
28

using namespace isc::asiolink;

namespace isc {
namespace dhcp {

29
30
31
// This is an initial value of subnet-id. See comments in subnet.h for details.
SubnetID Subnet::static_id_ = 1;

32
33
34
Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
               const Triplet<uint32_t>& t1,
               const Triplet<uint32_t>& t2,
35
               const Triplet<uint32_t>& valid_lifetime,
36
37
38
               const isc::dhcp::Subnet::RelayInfo& relay,
               const SubnetID id)
    :id_(id == 0 ? generateNextID() : id), prefix_(prefix), prefix_len_(len),
39
     t1_(t1), t2_(t2), valid_(valid_lifetime),
40
41
     last_allocated_ia_(lastAddrInPrefix(prefix, len)),
     last_allocated_ta_(lastAddrInPrefix(prefix, len)),
42
     last_allocated_pd_(lastAddrInPrefix(prefix, len)), relay_(relay),
43
     host_reservation_mode_(HR_ALL), cfg_option_(new CfgOption())
44
      {
45
46
    if ((prefix.isV6() && len > 128) ||
        (prefix.isV4() && len > 32)) {
47
        isc_throw(BadValue,
48
                  "Invalid prefix length specified for subnet: " << len);
49
50
51
    }
}

52
53
54
55
Subnet::RelayInfo::RelayInfo(const isc::asiolink::IOAddress& addr)
    :addr_(addr) {
}

56
bool
57
Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
58
59
60
61
62
63
    IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
    IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);

    return ((first <= addr) && (addr <= last));
}

64
void
65
Subnet::setRelayInfo(const isc::dhcp::Subnet::RelayInfo& relay) {
66
67
68
    relay_ = relay;
}

69
70
71
72
73
74
75
bool
Subnet::clientSupported(const isc::dhcp::ClientClasses& classes) const {
    if (white_list_.empty()) {
        return (true); // There is no class defined for this subnet, so we do
                       // support everyone.
    }

76
77
78
79
80
81
82
83
    for (ClientClasses::const_iterator it = white_list_.begin();
         it != white_list_.end(); ++it) {
        if (classes.contains(*it)) {
            return (true);
        }
    }

    return (false);
84
85
86
87
}

void
Subnet::allowClientClass(const isc::dhcp::ClientClass& class_name) {
88
    white_list_.insert(class_name);
89
90
}

91
isc::asiolink::IOAddress Subnet::getLastAllocated(Lease::Type type) const {
92
93
94
    // check if the type is valid (and throw if it isn't)
    checkType(type);

95
    switch (type) {
96
97
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
98
        return last_allocated_ia_;
99
    case Lease::TYPE_TA:
100
        return last_allocated_ta_;
101
    case Lease::TYPE_PD:
102
103
104
105
106
107
        return last_allocated_pd_;
    default:
        isc_throw(BadValue, "Pool type " << type << " not supported");
    }
}

108
void Subnet::setLastAllocated(Lease::Type type,
109
110
111
112
113
                              const isc::asiolink::IOAddress& addr) {

    // check if the type is valid (and throw if it isn't)
    checkType(type);

114
    switch (type) {
115
116
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
117
118
        last_allocated_ia_ = addr;
        return;
119
    case Lease::TYPE_TA:
120
121
        last_allocated_ta_ = addr;
        return;
122
    case Lease::TYPE_PD:
123
124
125
126
127
128
129
        last_allocated_pd_ = addr;
        return;
    default:
        isc_throw(BadValue, "Pool type " << type << " not supported");
    }
}

130
std::string
131
Subnet::toText() const {
132
    std::stringstream tmp;
133
    tmp << prefix_ << "/" << static_cast<unsigned int>(prefix_len_);
134
135
136
    return (tmp.str());
}

137
uint64_t
Tomek Mrugalski's avatar
Tomek Mrugalski committed
138
Subnet::getPoolCapacity(Lease::Type type) const {
139
140
141
    switch (type) {
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
142
        return sumPoolCapacity(pools_);
143
    case Lease::TYPE_TA:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
144
        return sumPoolCapacity(pools_ta_);
145
    case Lease::TYPE_PD:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
146
        return sumPoolCapacity(pools_pd_);
147
148
149
150
151
152
153
    default:
        isc_throw(BadValue, "Unsupported pool type: "
                  << static_cast<int>(type));
    }
}

uint64_t
Tomek Mrugalski's avatar
Tomek Mrugalski committed
154
Subnet::sumPoolCapacity(const PoolCollection& pools) const {
155
156
    uint64_t sum = 0;
    for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
157
        uint64_t x = (*p)->getCapacity();
158
159
160
161
162
163
164
165
166
167
168
169
170

        // Check if we can add it. If sum + x > uint64::max, then we would have
        // overflown if we tried to add it.
        if (x > std::numeric_limits<uint64_t>::max() - sum) {
            return (std::numeric_limits<uint64_t>::max());
        }

        sum += x;
    }

    return (sum);
}

171
172
void Subnet4::checkType(Lease::Type type) const {
    if (type != Lease::TYPE_V4) {
173
174
175
176
        isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
    }
}

177
178
179
Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                 const Triplet<uint32_t>& t1,
                 const Triplet<uint32_t>& t2,
180
181
                 const Triplet<uint32_t>& valid_lifetime,
                 const SubnetID id)
182
    : Subnet(prefix, length, t1, t2, valid_lifetime, RelayInfo(IOAddress("0.0.0.0")), id),
183
      siaddr_(IOAddress("0.0.0.0")), match_client_id_(true) {
184
    if (!prefix.isV4()) {
185
186
187
188
189
        isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
                  << " specified in subnet4");
    }
}

190
191
void Subnet4::setSiaddr(const isc::asiolink::IOAddress& siaddr) {
    if (!siaddr.isV4()) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
192
        isc_throw(BadValue, "Can't set siaddr to non-IPv4 address "
193
                  << siaddr);
194
195
196
197
198
199
200
201
    }
    siaddr_ = siaddr;
}

isc::asiolink::IOAddress Subnet4::getSiaddr() const {
    return (siaddr_);
}

202
const PoolCollection& Subnet::getPools(Lease::Type type) const {
203
204
205
    // check if the type is valid (and throw if it isn't)
    checkType(type);

206
    switch (type) {
207
208
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
209
        return (pools_);
210
    case Lease::TYPE_TA:
211
        return (pools_ta_);
212
    case Lease::TYPE_PD:
213
214
        return (pools_pd_);
    default:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
215
216
        isc_throw(BadValue, "Unsupported pool type: "
                  << static_cast<int>(type));
217
    }
218
}
219

Tomek Mrugalski's avatar
Tomek Mrugalski committed
220
PoolCollection& Subnet::getPoolsWritable(Lease::Type type) {
221
222
    // check if the type is valid (and throw if it isn't)
    checkType(type);
223

224
    switch (type) {
225
226
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
227
        return (pools_);
228
    case Lease::TYPE_TA:
229
        return (pools_ta_);
230
    case Lease::TYPE_PD:
231
        return (pools_pd_);
232
    default:
233
234
        isc_throw(BadValue, "Invalid pool type specified: "
                  << static_cast<int>(type));
235
    }
236
237
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
238
239
const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& hint,
                        bool anypool /* true */) const {
240
241
242
243
    // check if the type is valid (and throw if it isn't)
    checkType(type);

    const PoolCollection& pools = getPools(type);
244
245

    PoolPtr candidate;
246
247
    for (PoolCollection::const_iterator pool = pools.begin();
         pool != pools.end(); ++pool) {
248

249
        // if we won't find anything better, then let's just use the first pool
250
        if (anypool && !candidate) {
251
252
253
            candidate = *pool;
        }

254
        // if the client provided a pool and there's a pool that hint is valid
255
        // in, then let's use that pool
256
257
258
259
260
261
262
        if ((*pool)->inRange(hint)) {
            return (*pool);
        }
    }
    return (candidate);
}

263
264
void
Subnet::addPool(const PoolPtr& pool) {
265
266
267
    // check if the type is valid (and throw if it isn't)
    checkType(pool->getType());

268
269
270
271
272
273
274
275
276
277
    // Check that the pool is in range with a subnet only if this is
    // not a pool of IPv6 prefixes. The IPv6 prefixes delegated for
    // the particular subnet don't need to match the prefix of the
    // subnet.
    if (pool->getType() != Lease::TYPE_PD) {
        if (!inRange(pool->getFirstAddress()) || !inRange(pool->getLastAddress())) {
            isc_throw(BadValue, "a pool of type "
                      << Lease::typeToText(pool->getType())
                      << ", with the following address range: "
                      << pool->getFirstAddress() << "-"
278
                      << pool->getLastAddress() << " does not match"
279
280
281
282
                      << " the prefix of a subnet: "
                      << prefix_ << "/" << static_cast<int>(prefix_len_)
                      << " to which it is being added");
        }
283
284
285
286
    }

    /// @todo: Check that pools do not overlap

287
    // Add the pool to the appropriate pools collection
Tomek Mrugalski's avatar
Tomek Mrugalski committed
288
    getPoolsWritable(pool->getType()).push_back(pool);
289
290
}

291
292
void
Subnet::delPools(Lease::Type type) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
293
    getPoolsWritable(type).clear();
294
295
}

296
void
297
Subnet::setIface(const std::string& iface_name) {
298
299
300
    iface_ = iface_name;
}

301
std::string
302
Subnet::getIface() const {
303
304
305
    return (iface_);
}

306
bool
307
Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
308
309
310
311
312
313

    // Let's start with checking if it even belongs to that subnet.
    if (!inRange(addr)) {
        return (false);
    }

314
315
316
317
    const PoolCollection& pools = getPools(type);

    for (PoolCollection::const_iterator pool = pools.begin();
         pool != pools.end(); ++pool) {
318
319
320
321
        if ((*pool)->inRange(addr)) {
            return (true);
        }
    }
322
    // There's no pool that address belongs to
323
324
325
    return (false);
}

326
327
328
329
Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
                 const Triplet<uint32_t>& t1,
                 const Triplet<uint32_t>& t2,
                 const Triplet<uint32_t>& preferred_lifetime,
330
331
                 const Triplet<uint32_t>& valid_lifetime,
                 const SubnetID id)
332
    :Subnet(prefix, length, t1, t2, valid_lifetime, RelayInfo(IOAddress("::")), id),
333
334
     preferred_(preferred_lifetime), rapid_commit_(false),
     alloc_leases_on_renew_(true) {
335
    if (!prefix.isV6()) {
336
        isc_throw(BadValue, "Non IPv6 prefix " << prefix
337
338
339
340
                  << " specified in subnet6");
    }
}

341
342
343
344
345
346
void Subnet6::checkType(Lease::Type type) const {
    if ( (type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
         (type != Lease::TYPE_PD)) {
        isc_throw(BadValue, "Invalid Pool type: " << Lease::typeToText(type)
                  << "(" << static_cast<int>(type)
                  << "), must be TYPE_NA, TYPE_TA or TYPE_PD for Subnet6");
347
348
349
    }
}

350
351
} // end of isc::dhcp namespace
} // end of isc namespace