subnet.cc 9.99 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
27
28
29
30
31

using namespace isc::asiolink;

namespace isc {
namespace dhcp {

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)
    :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
32
     t2_(t2), valid_(valid_lifetime),
33
34
35
     last_allocated_ia_(lastAddrInPrefix(prefix, len)),
     last_allocated_ta_(lastAddrInPrefix(prefix, len)),
     last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
36
37
    if ((prefix.isV6() && len > 128) ||
        (prefix.isV4() && len > 32)) {
38
        isc_throw(BadValue,
39
                  "Invalid prefix length specified for subnet: " << len);
40
41
42
    }
}

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

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

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

61
62
    // Actually add new option descriptor.
    option_spaces_.addItem(OptionDescriptor(option, persistent), option_space);
63
64
65
66
}

void
Subnet::delOptions() {
67
    option_spaces_.clearItems();
68
69
}

70
71
Subnet::OptionContainerPtr
Subnet::getOptionDescriptors(const std::string& option_space) const {
72
    return (option_spaces_.getItems(option_space));
73
74
}

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

    return (*range.first);
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
133
134
135
136
    std::stringstream tmp;
    tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
    return (tmp.str());
}

137
138
void Subnet4::checkType(Lease::Type type) const {
    if (type != Lease::TYPE_V4) {
139
140
141
142
        isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
    }
}

143
144
145
146
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)
147
148
    :Subnet(prefix, length, t1, t2, valid_lifetime),
    siaddr_(IOAddress("0.0.0.0")) {
149
    if (!prefix.isV4()) {
150
151
152
153
154
        isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
                  << " specified in subnet4");
    }
}

155
156
void Subnet4::setSiaddr(const isc::asiolink::IOAddress& siaddr) {
    if (!siaddr.isV4()) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
157
        isc_throw(BadValue, "Can't set siaddr to non-IPv4 address "
158
159
160
161
162
163
164
165
166
167
168
169
                  << siaddr.toText());
    }
    siaddr_ = siaddr;
}

    /// @brief returns siaddr for this subnet
    /// @return siaddr value
isc::asiolink::IOAddress Subnet4::getSiaddr() const {
    return (siaddr_);
}


170
const PoolCollection& Subnet::getPools(Lease::Type type) const {
171
172
173
    // check if the type is valid (and throw if it isn't)
    checkType(type);

174
    switch (type) {
175
176
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
177
        return (pools_);
178
    case Lease::TYPE_TA:
179
        return (pools_ta_);
180
    case Lease::TYPE_PD:
181
182
        return (pools_pd_);
    default:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
183
184
        isc_throw(BadValue, "Unsupported pool type: "
                  << static_cast<int>(type));
185
    }
186
}
187

Tomek Mrugalski's avatar
Tomek Mrugalski committed
188
PoolCollection& Subnet::getPoolsWritable(Lease::Type type) {
189
190
    // check if the type is valid (and throw if it isn't)
    checkType(type);
191

192
    switch (type) {
193
194
    case Lease::TYPE_V4:
    case Lease::TYPE_NA:
195
        return (pools_);
196
    case Lease::TYPE_TA:
197
        return (pools_ta_);
198
    case Lease::TYPE_PD:
199
        return (pools_pd_);
200
    default:
201
202
        isc_throw(BadValue, "Invalid pool type specified: "
                  << static_cast<int>(type));
203
    }
204
205
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
206
207
const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& hint,
                        bool anypool /* true */) const {
208
209
210
211
    // check if the type is valid (and throw if it isn't)
    checkType(type);

    const PoolCollection& pools = getPools(type);
212
213

    PoolPtr candidate;
214
215
    for (PoolCollection::const_iterator pool = pools.begin();
         pool != pools.end(); ++pool) {
216

217
        // if we won't find anything better, then let's just use the first pool
218
        if (anypool && !candidate) {
219
220
221
            candidate = *pool;
        }

222
        // if the client provided a pool and there's a pool that hint is valid
223
        // in, then let's use that pool
224
225
226
227
228
229
230
        if ((*pool)->inRange(hint)) {
            return (*pool);
        }
    }
    return (candidate);
}

231
232
233
234
235
236
237
238
239
240
241
242
243
244
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

245
246
247
    // check if the type is valid (and throw if it isn't)
    checkType(pool->getType());

248
    // Add the pool to the appropriate pools collection
Tomek Mrugalski's avatar
Tomek Mrugalski committed
249
    getPoolsWritable(pool->getType()).push_back(pool);
250
251
}

252
253
void
Subnet::delPools(Lease::Type type) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
254
    getPoolsWritable(type).clear();
255
256
}

257
void
258
Subnet::setIface(const std::string& iface_name) {
259
260
261
    iface_ = iface_name;
}

262
std::string
263
Subnet::getIface() const {
264
265
266
    return (iface_);
}

267
void
268
Subnet4::validateOption(const OptionPtr& option) const {
269
    if (!option) {
270
        isc_throw(isc::BadValue,
271
                  "option configured for subnet must not be NULL");
272
    } else if (option->getUniverse() != Option::V4) {
273
        isc_throw(isc::BadValue,
274
                  "expected V4 option to be added to the subnet");
275
276
277
    }
}

278
bool
279
Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
280
281
282
283
284
285

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

286
287
288
289
    const PoolCollection& pools = getPools(type);

    for (PoolCollection::const_iterator pool = pools.begin();
         pool != pools.end(); ++pool) {
290
291
292
293
        if ((*pool)->inRange(addr)) {
            return (true);
        }
    }
294
    // There's no pool that address belongs to
295
296
297
    return (false);
}

298
299
300
301
302
303
304
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){
305
    if (!prefix.isV6()) {
306
307
308
309
310
        isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText()
                  << " specified in subnet6");
    }
}

311
312
313
314
315
316
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");
317
318
319
    }
}

320
void
321
Subnet6::validateOption(const OptionPtr& option) const {
322
    if (!option) {
323
        isc_throw(isc::BadValue,
324
                  "option configured for subnet must not be NULL");
325
    } else if (option->getUniverse() != Option::V6) {
326
        isc_throw(isc::BadValue,
327
                  "expected V6 option to be added to the subnet");
328
329
    }
}
330

331
332
} // end of isc::dhcp namespace
} // end of isc namespace