Commit b1217e69 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac3150' (DHCP::CfgMgr support for PD)

Conflicts:
	ChangeLog
parents 7430591b e6f0e891
677. [func] tomek
libdhcpsrv: CfgMgr is now able to store IA, TA and PD pools in
Subnet6 structures.
(Trac #3150, git e6f0e89162bac0adae3ce3141437a282d5183162)
676. [bug] muks
We now also allow the short name ("hmac-md5"), along with the long
name ("hmac-md5.sig-alg.reg.int") that was allowed before for
......@@ -21,7 +26,7 @@
for IAPREFIX (Option6_IAPrefix) has been added.
(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
672. [func] tmark
672. [func] tmark
Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
This class provides the common structure and methods to implement the state
models described in the DHCP_DDNS design, plus integration with DNSClient
......
......@@ -135,7 +135,8 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
.arg(LeaseMgrFactory::instance().getName());
// Instantiate allocation engine
alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
false /* false = IPv4 */));
// Register hook points
hook_index_pkt4_receive_ = Hooks.hook_index_pkt4_receive_;
......
......@@ -141,13 +141,13 @@ protected:
///
/// @param addr is the IPv6 prefix of the pool.
/// @param len is the prefix length.
/// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
/// passed in as an int32_t and cast to Pool6Type to accommodate a
/// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
/// passed in as an int32_t and cast to PoolType to accommodate a
/// polymorphic interface.
/// @return returns a PoolPtr to the new Pool4 object.
PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
{
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool::PoolType>
(ptype), addr, len)));
}
......@@ -155,13 +155,13 @@ protected:
///
/// @param min is the first IPv6 address in the pool.
/// @param max is the last IPv6 address in the pool.
/// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
/// passed in as an int32_t and cast to Pool6Type to accommodate a
/// @param ptype is the type of IPv6 pool (Pool::PoolType). Note this is
/// passed in as an int32_t and cast to PoolType to accommodate a
/// polymorphic interface.
/// @return returns a PoolPtr to the new Pool4 object.
PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
{
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool::PoolType>
(ptype), min, max)));
}
};
......
......@@ -53,8 +53,8 @@ AllocEngineHooks Hooks;
namespace isc {
namespace dhcp {
AllocEngine::IterativeAllocator::IterativeAllocator()
:Allocator() {
AllocEngine::IterativeAllocator::IterativeAllocator(Pool::PoolType lease_type)
:Allocator(lease_type) {
}
isc::asiolink::IOAddress
......@@ -94,9 +94,9 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
// Let's get the last allocated address. It is usually set correctly,
// but there are times when it won't be (like after removing a pool or
// perhaps restarting the server).
IOAddress last = subnet->getLastAllocated();
IOAddress last = subnet->getLastAllocated(pool_type_);
const PoolCollection& pools = subnet->getPools();
const PoolCollection& pools = subnet->getPools(pool_type_);
if (pools.empty()) {
isc_throw(AllocFailed, "No pools defined in selected subnet");
......@@ -117,7 +117,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
if (it == pools.end()) {
// ok to access first element directly. We checked that pools is non-empty
IOAddress next = pools[0]->getFirstAddress();
subnet->setLastAllocated(next);
subnet->setLastAllocated(pool_type_, next);
return (next);
}
......@@ -126,7 +126,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
IOAddress next = increaseAddress(last); // basically addr++
if ((*it)->inRange(next)) {
// the next one is in the pool as well, so we haven't hit pool boundary yet
subnet->setLastAllocated(next);
subnet->setLastAllocated(pool_type_, next);
return (next);
}
......@@ -136,18 +136,18 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
// Really out of luck today. That was the last pool. Let's rewind
// to the beginning.
next = pools[0]->getFirstAddress();
subnet->setLastAllocated(next);
subnet->setLastAllocated(pool_type_, next);
return (next);
}
// there is a next pool, let's try first address from it
next = (*it)->getFirstAddress();
subnet->setLastAllocated(next);
subnet->setLastAllocated(pool_type_, next);
return (next);
}
AllocEngine::HashedAllocator::HashedAllocator()
:Allocator() {
AllocEngine::HashedAllocator::HashedAllocator(Pool::PoolType lease_type)
:Allocator(lease_type) {
isc_throw(NotImplemented, "Hashed allocator is not implemented");
}
......@@ -159,8 +159,8 @@ AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
isc_throw(NotImplemented, "Hashed allocator is not implemented");
}
AllocEngine::RandomAllocator::RandomAllocator()
:Allocator() {
AllocEngine::RandomAllocator::RandomAllocator(Pool::PoolType lease_type)
:Allocator(lease_type) {
isc_throw(NotImplemented, "Random allocator is not implemented");
}
......@@ -173,17 +173,21 @@ AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
}
AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts,
bool ipv6)
:attempts_(attempts) {
Pool::PoolType pool_type = ipv6?Pool::TYPE_IA:Pool::TYPE_V4;
switch (engine_type) {
case ALLOC_ITERATIVE:
allocator_ = boost::shared_ptr<Allocator>(new IterativeAllocator());
allocator_.reset(new IterativeAllocator(pool_type));
break;
case ALLOC_HASHED:
allocator_ = boost::shared_ptr<Allocator>(new HashedAllocator());
allocator_.reset(new HashedAllocator(pool_type));
break;
case ALLOC_RANDOM:
allocator_ = boost::shared_ptr<Allocator>(new RandomAllocator());
allocator_.reset(new RandomAllocator(pool_type));
break;
default:
......
......@@ -77,10 +77,21 @@ protected:
pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
const isc::asiolink::IOAddress& hint) = 0;
/// @brief Default constructor.
///
/// Specifies which type of leases this allocator will assign
/// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
Allocator(Pool::PoolType pool_type)
:pool_type_(pool_type) {
}
/// @brief virtual destructor
virtual ~Allocator() {
}
protected:
/// @brief defines lease type allocation
Pool::PoolType pool_type_;
};
/// @brief Address/prefix allocator that iterates over all addresses
......@@ -95,7 +106,8 @@ protected:
/// @brief default constructor
///
/// Does not do anything
IterativeAllocator();
/// @param type - specifies allocation type
IterativeAllocator(Pool::PoolType type);
/// @brief returns the next address from pools in a subnet
///
......@@ -123,7 +135,8 @@ protected:
public:
/// @brief default constructor (does nothing)
HashedAllocator();
/// @param type - specifies allocation type
HashedAllocator(Pool::PoolType type);
/// @brief returns an address based on hash calculated from client's DUID.
///
......@@ -145,7 +158,8 @@ protected:
public:
/// @brief default constructor (does nothing)
RandomAllocator();
/// @param type - specifies allocation type
RandomAllocator(Pool::PoolType type);
/// @brief returns an random address from pool of specified subnet
///
......@@ -180,7 +194,8 @@ protected:
/// @param engine_type selects allocation algorithm
/// @param attempts number of attempts for each lease allocation before
/// we give up (0 means unlimited)
AllocEngine(AllocType engine_type, unsigned int attempts);
/// @param ipv6 specifies if the engine should work for IPv4 or IPv6
AllocEngine(AllocType engine_type, unsigned int attempts, bool ipv6 = true);
/// @brief Returns IPv4 lease.
///
......
......@@ -21,9 +21,9 @@ using namespace isc::asiolink;
namespace isc {
namespace dhcp {
Pool::Pool(const isc::asiolink::IOAddress& first,
Pool::Pool(PoolType type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:id_(getNextID()), first_(first), last_(last) {
:id_(getNextID()), first_(first), last_(last), type_(type) {
}
bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
......@@ -32,7 +32,7 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
Pool4::Pool4(const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:Pool(first, last) {
:Pool(Pool::TYPE_V4, first, last) {
// check if specified address boundaries are sane
if (!first.isV4() || !last.isV4()) {
isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
......@@ -43,9 +43,8 @@ Pool4::Pool4(const isc::asiolink::IOAddress& first,
}
}
Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len)
:Pool(prefix, IOAddress("0.0.0.0")) {
Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
:Pool(Pool::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
// check if the prefix is sane
if (!prefix.isV4()) {
......@@ -62,15 +61,21 @@ Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
}
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:Pool(first, last), type_(type) {
:Pool(type, first, last), prefix_len_(128) {
// check if specified address boundaries are sane
if (!first.isV6() || !last.isV6()) {
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
}
if ( (type != Pool::TYPE_IA) && (type != Pool::TYPE_TA) &&
(type != Pool::TYPE_PD)) {
isc_throw(BadValue, "Invalid Pool6 type: " << static_cast<int>(type)
<< ", must be TYPE_IA, TYPE_TA or TYPE_PD");
}
if (last < first) {
isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
// This check is a bit strict. If we decide that it is too strict,
......@@ -88,23 +93,34 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
// parameters are for IA and TA only. There is another dedicated
// constructor for that (it uses prefix/length)
if ((type != TYPE_IA) && (type != TYPE_TA)) {
isc_throw(BadValue, "Invalid Pool6 type specified");
isc_throw(BadValue, "Invalid Pool6 type specified:"
<< static_cast<int>(type));
}
}
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len)
:Pool(prefix, IOAddress("::")),
type_(type) {
Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len, uint8_t delegated_len /* = 128 */)
:Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) {
// check if the prefix is sane
if (!prefix.isV6()) {
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
}
// check if the prefix length is sane
// check if the prefix length is sane
if (prefix_len == 0 || prefix_len > 128) {
isc_throw(BadValue, "Invalid prefix length");
isc_throw(BadValue, "Invalid prefix length: " << prefix_len);
}
if (prefix_len > delegated_len) {
isc_throw(BadValue, "Delegated length (" << static_cast<int>(delegated_len)
<< ") must be longer than prefix length ("
<< static_cast<int>(prefix_len) << ")");
}
if ( ( (type == TYPE_IA) || (type == TYPE_TA)) && (delegated_len != 128)) {
isc_throw(BadValue, "For IA or TA pools, delegated prefix length must "
<< " be 128.");
}
/// @todo: We should probably implement checks against weird addresses
......
......@@ -32,6 +32,25 @@ class Pool {
public:
/// @brief specifies Pool type
///
/// Currently there are 3 pool types defined in DHCPv6:
/// - Non-temporary addresses (conveyed in IA_NA)
/// - Temporary addresses (conveyed in IA_TA)
/// - Delegated Prefixes (conveyed in IA_PD)
///
/// The fourth one (TYPE_V4) is used in DHCPv4 use cases when getPool()
/// code is shared between v4 and v6 code.
///
/// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
/// support for it is not planned for now.
typedef enum {
TYPE_IA,
TYPE_TA,
TYPE_PD,
TYPE_V4
} PoolType;
/// @brief returns Pool-id
///
/// @return pool-id value
......@@ -58,6 +77,20 @@ public:
/// @return true, if the address is in pool
bool inRange(const isc::asiolink::IOAddress& addr) const;
/// @brief Returns pool type (v4, v6 non-temporary, v6 temp, v6 prefix)
/// @return returns pool type
PoolType getType() const {
return (type_);
}
/// @brief virtual destructor
///
/// We need Pool to be a polymorphic class, so we could dynamic cast
/// from PoolPtr to Pool6Ptr if we need to. A class becomes polymorphic,
/// when there is at least one virtual method.
virtual ~Pool() {
}
protected:
/// @brief protected constructor
......@@ -65,7 +98,12 @@ protected:
/// This constructor is protected to prevent anyone from instantiating
/// Pool class directly. Instances of Pool4 and Pool6 should be created
/// instead.
Pool(const isc::asiolink::IOAddress& first,
///
/// @param type type of the pool
/// @param first first address of a range
/// @param last last address of a range
Pool(PoolType type,
const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last);
/// @brief returns the next unique Pool-ID
......@@ -91,6 +129,9 @@ protected:
///
/// @todo: This field is currently not used.
std::string comments_;
/// @brief defines a pool type
PoolType type_;
};
/// @brief Pool information for IPv4 addresses
......@@ -117,9 +158,6 @@ public:
/// @brief a pointer an IPv4 Pool
typedef boost::shared_ptr<Pool4> Pool4Ptr;
/// @brief a container for IPv4 Pools
typedef std::vector<Pool4Ptr> Pool4Collection;
/// @brief Pool information for IPv6 addresses and prefixes
///
/// It holds information about pool6, i.e. a range of IPv6 address space that
......@@ -127,55 +165,70 @@ typedef std::vector<Pool4Ptr> Pool4Collection;
class Pool6 : public Pool {
public:
/// @brief specifies Pool type
///
/// Currently there are 3 pool types defined in DHCPv6:
/// - Non-temporary addresses (conveyed in IA_NA)
/// - Temporary addresses (conveyed in IA_TA)
/// - Delegated Prefixes (conveyed in IA_PD)
/// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
/// support for it is not planned for now.
typedef enum {
TYPE_IA,
TYPE_TA,
TYPE_PD
} Pool6Type;
/// @brief the constructor for Pool6 "min-max" style definition
///
/// @param type type of the pool (IA, TA or PD)
/// @throw BadValue if PD is define (PD can be only prefix/len)
///
/// @param type type of the pool (IA or TA)
/// @param first the first address in a pool
/// @param last the last address in a pool
Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
Pool6(PoolType type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last);
/// @brief the constructor for Pool6 "prefix/len" style definition
///
/// For addressed, this is just a prefix/len definition. For prefixes,
/// there is one extra additional parameter delegated_len. It specifies
/// a size of delegated prefixes that the pool will be split into. For
/// example pool 2001:db8::/56, delegated_len=64 means that there is a
/// pool 2001:db8::/56. It will be split into 256 prefixes of length /64,
/// e.g. 2001:db8:0:1::/64, 2001:db8:0:2::/64 etc.
///
/// Naming convention:
/// A smaller prefix length yields a shorter prefix which describes a larger
/// set of addresses. A larger length yields a longer prefix which describes
/// a smaller set of addresses.
///
/// Obviously, prefix_len must define shorter or equal prefix length than
/// delegated_len, so prefix_len <= delegated_len. Note that it is slightly
/// confusing: bigger (larger) prefix actually has smaller prefix length,
/// e.g. /56 is a bigger prefix than /64, but has shorter (smaller) prefix
/// length.
///
/// @throw BadValue if delegated_len is defined for non-PD types or
/// when delegated_len < prefix_len
///
/// @param type type of the pool (IA, TA or PD)
/// @param prefix specifies prefix of the pool
/// @param prefix_len specifies length of the prefix of the pool
Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len);
/// @param prefix_len specifies prefix length of the pool
/// @param delegated_len specifies lenght of the delegated prefixes
Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len, uint8_t delegated_len = 128);
/// @brief returns pool type
///
/// @return pool type
Pool6Type getType() const {
PoolType getType() const {
return (type_);
}
private:
/// @brief defines a pool type
Pool6Type type_;
/// @brief returns delegated prefix length
///
/// This may be useful for "prefix/len" style definition for
/// addresses, but is mostly useful for prefix pools.
/// @return prefix length (1-128)
uint8_t getLength() {
return (prefix_len_);
}
private:
/// @brief Defines prefix length (for TYPE_PD only)
uint8_t prefix_len_;
};
/// @brief a pointer an IPv6 Pool
typedef boost::shared_ptr<Pool6> Pool6Ptr;
/// @brief a container for IPv6 Pools
typedef std::vector<Pool6Ptr> Pool6Collection;
/// @brief a pointer to either IPv4 or IPv6 Pool
typedef boost::shared_ptr<Pool> PoolPtr;
......
......@@ -30,15 +30,17 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
const Triplet<uint32_t>& valid_lifetime)
:id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
t2_(t2), valid_(valid_lifetime),
last_allocated_(lastAddrInPrefix(prefix, len)) {
last_allocated_ia_(lastAddrInPrefix(prefix, len)),
last_allocated_ta_(lastAddrInPrefix(prefix, len)),
last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
if ((prefix.isV6() && len > 128) ||
(prefix.isV4() && len > 32)) {
isc_throw(BadValue,
isc_throw(BadValue,
"Invalid prefix length specified for subnet: " << len);
}
}
bool
bool
Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
......@@ -86,13 +88,58 @@ Subnet::getOptionDescriptor(const std::string& option_space,
return (*range.first);
}
std::string
isc::asiolink::IOAddress Subnet::getLastAllocated(Pool::PoolType type) const {
// check if the type is valid (and throw if it isn't)
checkType(type);
switch (type) {
case Pool::TYPE_V4:
case Pool::TYPE_IA:
return last_allocated_ia_;
case Pool::TYPE_TA:
return last_allocated_ta_;
case Pool::TYPE_PD:
return last_allocated_pd_;
default:
isc_throw(BadValue, "Pool type " << type << " not supported");
}
}
void Subnet::setLastAllocated(Pool::PoolType type,
const isc::asiolink::IOAddress& addr) {
// check if the type is valid (and throw if it isn't)
checkType(type);
switch (type) {
case Pool::TYPE_V4:
case Pool::TYPE_IA:
last_allocated_ia_ = addr;
return;
case Pool::TYPE_TA:
last_allocated_ta_ = addr;
return;
case Pool::TYPE_PD:
last_allocated_pd_ = addr;
return;
default:
isc_throw(BadValue, "Pool type " << type << " not supported");
}
}
std::string
Subnet::toText() const {
std::stringstream tmp;
tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
return (tmp.str());
}
void Subnet4::checkType(Pool::PoolType type) const {
if (type != Pool::TYPE_V4) {
isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
}
}
Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
......@@ -104,35 +151,55 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
}
}
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_) << ") subnet4");
const PoolCollection& Subnet::getPools(Pool::PoolType type) const {
// check if the type is valid (and throw if it isn't)
checkType(type);
switch (type) {
case Pool::TYPE_V4:
case Pool::TYPE_IA:
return (pools_);
case Pool::TYPE_TA:
return (pools_ta_);
case Pool::TYPE_PD:
return (pools_pd_);
default:
isc_throw(BadValue, "Unsupported pool type: " << type);
}
/// @todo: Check that pools do not overlap
pools_.push_back(pool);
}
PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
PoolPtr Subnet::getPool(Pool::PoolType type, isc::asiolink::IOAddress hint) {
// check if the type is valid (and throw if it isn't)
checkType(type);
PoolCollection* pools = NULL;
switch (type) {
case Pool::TYPE_V4:
case Pool::TYPE_IA:
pools = &pools_;
break;
case Pool::TYPE_TA: