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

[2238] CfgMgr (+tests) implemented.

parent d1c7c905
4XX. [func] tomek
Configuration Manager for DHCP servers has been
implemented. Currently only supports basic configuration storage
for DHCPv6 server, but that capability is expected to be expanded
in a near future.
(Trac #2238, git TBD)
473. [bug] jelte
TCP connections now time out in b10-auth if no (or not all) query
data is sent by the client. The timeout value defaults to 5000
......
......@@ -27,8 +27,8 @@ Pool::Pool(const isc::asiolink::IOAddress& first,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
const Triplet<uint32_t>& valid_lifetime)
:id_(getNextID()), first_(first), last_(last), t1_(t1), t2_(t2), valid_(valid_lifetime) {
:id_(getNextID()), first_(first), last_(last), t1_(t1), t2_(t2),
valid_(valid_lifetime) {
}
bool Pool::inRange(const isc::asiolink::IOAddress& addr) {
......@@ -44,19 +44,23 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
:Pool(first, last, t1, t2, valid_lifetime),
type_(type), prefix_len_(0), preferred_(preferred_lifetime) {
// check if specified address boundaries are sane
if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
}
if (last < first) {
isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
// This check is strict. If we decide that it is too strict,
// This check is a bit strict. If we decide that it is too strict,
// we need to comment it and uncomment lines below.
// On one hand, letting the user specify 2001::f - 2001::1 is nice, but
// on the other hand, 2001::1 may be a typo and the user really meant
// 2001::1:0 (or 1something), so a at least a warning would be useful.
// first_ = last;
// last_ = first;
}
if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
}
// TYPE_PD is not supported by this constructor. first-last style
// parameters are for IA and TA only. There is another dedicated
// constructor for that (it uses prefix/length)
......@@ -65,30 +69,31 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
}
}
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& addr,
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
const Triplet<uint32_t>& preferred_lifetime,
const Triplet<uint32_t>& valid_lifetime)
:Pool(addr, IOAddress("::"), t1, t2, valid_lifetime),
:Pool(prefix, IOAddress("::"), t1, t2, valid_lifetime),
type_(type), prefix_len_(prefix_len), preferred_(preferred_lifetime) {
if (prefix_len == 0 || prefix_len > 128) {
isc_throw(BadValue, "Invalid prefix length");
// check if the prefix is sane
if (prefix.getFamily() != AF_INET6) {
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
}
if (addr.getFamily() != AF_INET6) {
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
// check if the prefix length is sane
if (prefix_len == 0 || prefix_len > 128) {
isc_throw(BadValue, "Invalid prefix length");
}
// Let's now calculate the last address in defined pool
last_ = lastAddrInPrefix(addr, prefix_len);
last_ = lastAddrInPrefix(prefix, prefix_len);
}
Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len)
:id_(getNextID()), prefix_(prefix), len_(len) {
:id_(getNextID()), prefix_(prefix), prefix_len_(len) {
if ( (prefix.getFamily() == AF_INET6 && len > 128) ||
(prefix.getFamily() == AF_INET && len > 32) ) {
isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
......@@ -96,8 +101,8 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len)
}
bool Subnet::inRange(const isc::asiolink::IOAddress& addr) {
IOAddress first = firstAddrInPrefix(prefix_, len_);
IOAddress last = lastAddrInPrefix(prefix_, len_);
IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
return ( (first <= addr) && (addr <= last) );
}
......@@ -109,8 +114,79 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length)
}
}
void Subnet6::addPool6(const Pool6Ptr& pool) {
IOAddress first_addr = pool->getFirstAddress();
IOAddress last_addr = pool->getLastAddress();
if (!inRange(first_addr) || !inRange(last_addr)) {
isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText()
<< " does not belong in this (" << prefix_ << "/" << prefix_len_
<< ") subnet6");
}
pools_.push_back(pool);
}
Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
Pool6Ptr candidate;
for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
// if we won't find anything better, then let's just use the first pool
if (!candidate) {
candidate = *pool;
}
// if the client provided a pool and there's a pool that hint is valid in,
// then let's use that pool
if ((*pool)->inRange(hint)) {
return (*pool);
}
}
return (candidate);
}
CfgMgr&
CfgMgr::instance() {
static CfgMgr cfg_mgr;
return (cfg_mgr);
}
Subnet6Ptr
CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
// If there's only one subnet configured, let's just use it
if (subnets6_.size() == 1) {
return (subnets6_[0]);
}
// If there is more than one, we need to choose the proper one
for (Subnet6Collection::iterator subnet = subnets6_.begin();
subnet != subnets6_.end(); ++subnet) {
if ((*subnet)->inRange(hint)) {
return (*subnet);
}
}
// sorry, we don't support that subnet
return (Subnet6Ptr());
}
Subnet6Ptr CfgMgr::getSubnet6(OptionPtr /*interfaceId*/) {
/// @todo: Implement get subnet6 by interface-id (for relayed traffic)
isc_throw(NotImplemented, "Relayed DHCPv6 traffic is not supported yet.");
}
void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
subnets6_.push_back(subnet);
}
CfgMgr::CfgMgr() {
}
CfgMgr::~CfgMgr() {
}
}; // end of isc::dhcp namespace
}; // end of isc namespace
......@@ -19,8 +19,10 @@
#include <map>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <asiolink/io_address.h>
#include <util/buffer.h>
#include <dhcp/option.h>
namespace isc {
namespace dhcp {
......@@ -241,18 +243,63 @@ protected:
isc::asiolink::IOAddress prefix_;
uint8_t len_;
uint8_t prefix_len_;
Pool6Collection pool_;
};
class Subnet6 : public Subnet {
public:
Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length);
Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint = isc::asiolink::IOAddress("::"));
void addPool6(const Pool6Ptr& pool);
const Pool6Collection& getPools() const {
return pools_;
}
protected:
/// collection of pools in that list
Pool6Collection pools_;
};
typedef boost::shared_ptr<Subnet6> Subnet6Ptr;
typedef std::vector<Subnet6Ptr> Subnet6Collection;
class CfgMgr : public boost::noncopyable {
public:
static CfgMgr& instance();
/// @brief get subnet by address
///
/// Finds a matching subnet, based on an address. This can be used
/// in two cases: when trying to find an appropriate lease based on
/// a) relay link address (that must be the address that is on link)
/// b) our global address on the interface the message was received on
/// (for directly connected clients)
Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
/// @brief get subnet by interface-id
///
/// Another possibility is to find a subnet based on interface-id.
/// @todo This method is not currently supported.
Subnet6Ptr getSubnet6(OptionPtr interfaceId);
void addSubnet6(const Subnet6Ptr& subnet);
protected:
/// @brief Protected constructor.
CfgMgr();
virtual ~CfgMgr();
Subnet6Collection subnets6_;
};
} // namespace isc::dhcp
} // namespace isc
......
......@@ -187,15 +187,109 @@ TEST(Pool6Test, unique_id) {
}
TEST(Subnet6Test, constructor) {
EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64));
EXPECT_THROW(Subnet6 subnet2(IOAddress("2001:db8:1::"), 129),
BadValue); // invalid prefix length
EXPECT_THROW(Subnet6 subnet3(IOAddress("192.168.0.0"), 32),
BadValue); // IPv4 addresses are not allowed in Subnet6
}
TEST(Subnet6Test, in_range) {
Subnet6 subnet(IOAddress("2001:db8:1::"), 64);
EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff")));
EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:1:1::")));
EXPECT_FALSE(subnet.inRange(IOAddress("::")));
EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff")));
EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:1:1::")));
EXPECT_FALSE(subnet.inRange(IOAddress("::")));
}
TEST(Subnet6Test, Pool6InSubnet6) {
Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56));
Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"),
64, 101, 102, 103, 104));
Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:2::"),
64, 201, 202, 203, 204));
Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:3::"),
64, 301, 302, 303, 304));
subnet->addPool6(pool1);
// If there's only one pool, get that pool
Pool6Ptr mypool = subnet->getPool6();
EXPECT_EQ(mypool, pool1);
subnet->addPool6(pool2);
subnet->addPool6(pool3);
// If there are more than one pool and we didn't provide hint, we
// should get the first pool
mypool = subnet->getPool6();
EXPECT_EQ(mypool, pool1);
// If we provide a hint, we should get a pool that this hint belongs to
mypool = subnet->getPool6(IOAddress("2001:db8:1:3::dead:beef"));
EXPECT_EQ(mypool, pool3);
}
TEST(Subnet6Test, Subnet6_Pool6_checks) {
Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56));
// this one is in subnet
Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"),
64, 101, 102, 103, 104));
subnet->addPool6(pool1);
Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"),
48, 201, 202, 203, 204)); // this one is larger than the subnet!
EXPECT_THROW(subnet->addPool6(pool2), BadValue);
// this one is totally out of blue
Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"),
16, 301, 302, 303, 304));
EXPECT_THROW(subnet->addPool6(pool3), BadValue);
}
// This test verifies if the configuration manager is able to hold and return
// valid leases
TEST(CfgMgrTest, subnet6) {
CfgMgr& cfg_mgr = CfgMgr::instance();
ASSERT_TRUE(&cfg_mgr != 0);
Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48));
Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48));
Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48));
// there shouldn't be any subnet configured at this stage
EXPECT_EQ( Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("2000::1")));
cfg_mgr.addSubnet6(subnet1);
// Now we have only one subnet, any request will be served from it
EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2001:db8::1")));
cfg_mgr.addSubnet6(subnet2);
cfg_mgr.addSubnet6(subnet3);
EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123")));
EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1")));
}
} // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment