subnet.cc 11 KB
Newer Older
1
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
// 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.

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

20
#include <sstream>
21
22
23
24
25
26

using namespace isc::asiolink;

namespace isc {
namespace dhcp {

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

30
31
32
33
Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
               const Triplet<uint32_t>& t1,
               const Triplet<uint32_t>& t2,
               const Triplet<uint32_t>& valid_lifetime)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
34
    :id_(generateNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
35
     t2_(t2), valid_(valid_lifetime),
36
37
38
     last_allocated_ia_(lastAddrInPrefix(prefix, len)),
     last_allocated_ta_(lastAddrInPrefix(prefix, len)),
     last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
39
40
    if ((prefix.isV6() && len > 128) ||
        (prefix.isV4() && len > 32)) {
41
        isc_throw(BadValue,
42
                  "Invalid prefix length specified for subnet: " << len);
43
44
45
    }
}

46
bool
47
Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
48
49
50
51
52
53
    IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
    IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);

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

54
void
55
Subnet::addOption(const OptionPtr& option, bool persistent,
56
                  const std::string& option_space) {
57
58
59
60
    // Check that the option space name is valid.
    if (!OptionSpace::validateName(option_space)) {
        isc_throw(isc::BadValue, "invalid option space name: '"
                  << option_space << "'");
61
    }
62
    validateOption(option);
63

64
65
    // Actually add new option descriptor.
    option_spaces_.addItem(OptionDescriptor(option, persistent), option_space);
66
67
68
69
}

void
Subnet::delOptions() {
70
    option_spaces_.clearItems();
71
72
}

73
74
Subnet::OptionContainerPtr
Subnet::getOptionDescriptors(const std::string& option_space) const {
75
    return (option_spaces_.getItems(option_space));
76
77
}

78
Subnet::OptionDescriptor
79
80
Subnet::getOptionDescriptor(const std::string& option_space,
                            const uint16_t option_code) {
81
82
    OptionContainerPtr options = getOptionDescriptors(option_space);
    if (!options || options->empty()) {
83
84
        return (OptionDescriptor(false));
    }
85
    const OptionContainerTypeIndex& idx = options->get<1>();
86
87
88
89
90
91
    const OptionContainerTypeRange& range = idx.equal_range(option_code);
    if (std::distance(range.first, range.second) == 0) {
        return (OptionDescriptor(false));
    }

    return (*range.first);
92
93
}

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
void Subnet::addVendorOption(const OptionPtr& option, bool persistent,
                             uint32_t vendor_id){

    validateOption(option);

    vendor_option_spaces_.addItem(OptionDescriptor(option, persistent), vendor_id);
}

Subnet::OptionContainerPtr
Subnet::getVendorOptionDescriptors(uint32_t vendor_id) const {
    return (vendor_option_spaces_.getItems(vendor_id));
}

Subnet::OptionDescriptor
Subnet::getVendorOptionDescriptor(uint32_t vendor_id, uint16_t option_code) {
    OptionContainerPtr options = getVendorOptionDescriptors(vendor_id);
    if (!options || options->empty()) {
        return (OptionDescriptor(false));
    }
    const OptionContainerTypeIndex& idx = options->get<1>();
    const OptionContainerTypeRange& range = idx.equal_range(option_code);
    if (std::distance(range.first, range.second) == 0) {
        return (OptionDescriptor(false));
    }

    return (*range.first);
}

void Subnet::delVendorOptions() {
    vendor_option_spaces_.clearItems();
}

126
isc::asiolink::IOAddress Subnet::getLastAllocated(Lease::Type type) const {
127
128
129
    // check if the type is valid (and throw if it isn't)
    checkType(type);

130
    switch (type) {
131
132
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
133
        return last_allocated_ia_;
134
    case Lease::TYPE_TA:
135
        return last_allocated_ta_;
136
    case Lease::TYPE_PD:
137
138
139
140
141
142
        return last_allocated_pd_;
    default:
        isc_throw(BadValue, "Pool type " << type << " not supported");
    }
}

143
void Subnet::setLastAllocated(Lease::Type type,
144
145
146
147
148
                              const isc::asiolink::IOAddress& addr) {

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

149
    switch (type) {
150
151
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
152
153
        last_allocated_ia_ = addr;
        return;
154
    case Lease::TYPE_TA:
155
156
        last_allocated_ta_ = addr;
        return;
157
    case Lease::TYPE_PD:
158
159
160
161
162
163
164
        last_allocated_pd_ = addr;
        return;
    default:
        isc_throw(BadValue, "Pool type " << type << " not supported");
    }
}

165
std::string
166
Subnet::toText() const {
167
168
169
170
171
    std::stringstream tmp;
    tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
    return (tmp.str());
}

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

178
179
180
181
Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                 const Triplet<uint32_t>& t1,
                 const Triplet<uint32_t>& t2,
                 const Triplet<uint32_t>& valid_lifetime)
182
183
    :Subnet(prefix, length, t1, t2, valid_lifetime),
    siaddr_(IOAddress("0.0.0.0")) {
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
194
195
196
197
198
199
200
201
                  << siaddr.toText());
    }
    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
265
266
267
268
269
270
271
272
273
274
275
276
void
Subnet::addPool(const PoolPtr& pool) {
    IOAddress first_addr = pool->getFirstAddress();
    IOAddress last_addr = pool->getLastAddress();

    if (!inRange(first_addr) || !inRange(last_addr)) {
        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-"
                  << last_addr.toText()
                  << " does not belong in this (" << prefix_.toText() << "/"
                  << static_cast<int>(prefix_len_) << ") subnet");
    }

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

277
278
279
    // check if the type is valid (and throw if it isn't)
    checkType(pool->getType());

280
    // Add the pool to the appropriate pools collection
Tomek Mrugalski's avatar
Tomek Mrugalski committed
281
    getPoolsWritable(pool->getType()).push_back(pool);
282
283
}

284
285
void
Subnet::delPools(Lease::Type type) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
286
    getPoolsWritable(type).clear();
287
288
}

289
void
290
Subnet::setIface(const std::string& iface_name) {
291
292
293
    iface_ = iface_name;
}

294
std::string
295
Subnet::getIface() const {
296
297
298
    return (iface_);
}

299
void
300
Subnet4::validateOption(const OptionPtr& option) const {
301
    if (!option) {
302
        isc_throw(isc::BadValue,
303
                  "option configured for subnet must not be NULL");
304
    } else if (option->getUniverse() != Option::V4) {
305
        isc_throw(isc::BadValue,
306
                  "expected V4 option to be added to the subnet");
307
308
309
    }
}

310
bool
311
Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
312
313
314
315
316
317

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

318
319
320
321
    const PoolCollection& pools = getPools(type);

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

330
331
332
333
334
335
336
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,
                 const Triplet<uint32_t>& valid_lifetime)
    :Subnet(prefix, length, t1, t2, valid_lifetime),
     preferred_(preferred_lifetime){
337
    if (!prefix.isV6()) {
338
339
340
341
342
        isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText()
                  << " specified in subnet6");
    }
}

343
344
345
346
347
348
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");
349
350
351
    }
}

352
void
353
Subnet6::validateOption(const OptionPtr& option) const {
354
    if (!option) {
355
        isc_throw(isc::BadValue,
356
                  "option configured for subnet must not be NULL");
357
    } else if (option->getUniverse() != Option::V6) {
358
        isc_throw(isc::BadValue,
359
                  "expected V6 option to be added to the subnet");
360
361
    }
}
362

363
364
} // end of isc::dhcp namespace
} // end of isc namespace