+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
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) {
: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)
}
}
-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);
}
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) );
}
}
}
+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
#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 {
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
}
+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