4XX. [func] tomek
- A new library (libb10-dhcpsrv) has been create. Currently its
- functionality is limited to a Configuration Manager. CfgMgr
- currently only supports basic configuration storage for DHCPv6
- server, but that capability is expected to be expanded in a near
- future.
+ A new library (libb10-dhcpsrv) has been created. At present, it
+ only holds the code for the DHCP Configuration Manager. Currently
+ this object only supports basic configuration storage for the DHCPv6
+ server, but that capability will be expanded.
(Trac #2238, git TBD)
475. [func] naokikambe
/// Operations within one protocol family are obvious.
/// Comparisons between v4 and v6 will allways return v4
/// being smaller. This follows boost::asio::ip implementation
- bool smallerThan(const IOAddress& other) const {
- if (this->getFamily() < other.getFamily()) {
- return (true);
- }
- if (this->getFamily() > other.getFamily()) {
- return (false);
- }
- if (this->getFamily() == AF_INET6) {
- return (this->asio_address_.to_v6() < other.asio_address_.to_v6());
- } else {
- return (this->asio_address_.to_v4() < other.asio_address_.to_v4());
+ bool lessThan(const IOAddress& other) const {
+ if (this->getFamily() == other.getFamily()) {
+ if (this->getFamily() == AF_INET6) {
+ return (this->asio_address_.to_v6() < other.asio_address_.to_v6());
+ } else {
+ return (this->asio_address_.to_v4() < other.asio_address_.to_v4());
+ }
}
+ return (this->getFamily() < other.getFamily());
}
/// \brief Checks if one address is smaller or equal than the other
if (equals(other)) {
return (true);
}
- return (smallerThan(other));
+ return (lessThan(other));
}
/// \brief Checks if one address is smaller than the other
///
/// See \ref smaller_than method for details.
bool operator<(const IOAddress& other) const {
- return (smallerThan(other));
+ return (lessThan(other));
}
/// \brief Checks if one address is smaller or equal than the other
EXPECT_EQ(addr3.toText(), "192.0.2.5");
}
-TEST(IOAddressTest, compare) {
+TEST(IOAddressTest, lessThanEqual) {
IOAddress addr1("192.0.2.5");
IOAddress addr2("192.0.2.6");
IOAddress addr3("0.0.0.0");
#include <dhcp/addr_utilities.h>
+using namespace isc::asiolink;
+
namespace isc {
namespace dhcp {
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
- uint8_t len) {
+ uint8_t len) {
static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
- uint8_t packed[16];
+ uint8_t packed[V6ADDRESS_LEN];
+ // First we copy the whole address as 16 bytes.
memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
+ // If the length is divisible by 8, it is simple. We just zero out the host
+ // part. Otherwise we need to handle the byte that has to be partially
+ // zeroed.
if (len % 8 != 0) {
+
+ // Get the appropriate mask. It has relevant bits (those that should
+ // stay) set and irrelevant (those that should be wiped) cleared.
uint8_t mask = bitMask[len % 8];
+
+ // Let's leave only whatever the mask says should not be cleared.
packed[len / 8] = packed[len / 8] & mask;
- len = (len/8 + 1) * 8;
+
+ // Since we have just dealt with this byte, let's move the prefix length
+ // to the beginning of the next byte (len is expressed in bits).
+ len = (len / 8 + 1) * 8;
}
- for (int i = len / 8; i < 16; ++i) {
+
+ // Clear out the remaining bits.
+ for (int i = len / 8; i < sizeof(packed); ++i) {
packed[i] = 0x0;
}
+ // Finally, let's wrap this into nice and easy IOAddress object.
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
}
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
- uint8_t len) {
+ uint8_t len) {
static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
- uint8_t packed[16];
+ uint8_t packed[V6ADDRESS_LEN];
+ // First we copy the whole address as 16 bytes.
memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
+ // if the length is divisible by 8, it is simple. We just fill the host part
+ // with ones. Otherwise we need to handle the byte that has to be partially
+ // zeroed.
if (len % 8 != 0) {
+ // Get the appropriate mask. It has relevant bits (those that should
+ // stay) set and irrelevant (those that should be set to 1) cleared.
uint8_t mask = bitMask[len % 8];
+
+ // Let's set those irrelevant bits with 1. It would be perhaps
+ // easier to not use negation here and invert bitMask content. However,
+ // with this approach, we can use the same mask in first and last
+ // address calculations.
packed[len / 8] = packed[len / 8] | ~mask;
- len = (len/8 + 1) * 8;
+
+ // Since we have just dealt with this byte, let's move the prefix length
+ // to the beginning of the next byte (len is expressed in bits).
+ len = (len / 8 + 1) * 8;
}
- for (int i = len / 8; i < 16; ++i) {
+
+ // Finally set remaining bits to 1.
+ for (int i = len / 8; i < sizeof(packed); ++i) {
packed[i] = 0xff;
}
+ // Finally, let's wrap this into nice and easy IOAddress object.
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
}
namespace dhcp {
/// This code is based on similar code from the Dibbler project. I, Tomasz Mrugalski,
-/// as a sole creater of that code hereby release it under BSD license for the benefit
+/// as a sole creator of that code hereby release it under BSD license for the benefit
/// of the BIND10 project.
/// @brief returns a first address in a given prefix
///
/// Example: For 2001:db8:1::deaf:beef and length /120 the function will return
-/// 2001:db8:1::dead:bee0. See also @ref lastAddrInPrefix.
+/// 2001:db8:1::dead:be00. See also @ref lastAddrInPrefix.
+///
+/// @todo It currently works for v6 only and will throw if v4 address is passed.
///
/// @param prefix and address that belongs to a prefix
/// @param len prefix length
///
/// @return first address from a prefix
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
- uint8_t len);
+ uint8_t len);
/// @brief returns a last address in a given prefix
///
/// Example: For 2001:db8:1::deaf:beef and length /112 the function will return
/// 2001:db8:1::dead:ffff. See also @ref firstAddrInPrefix.
///
+/// @todo It currently works for v6 only and will throw if v4 address is passed.
+///
/// @param prefix and address that belongs to a prefix
/// @param len prefix length
///
/// @return first address from a prefix
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
- uint8_t len);
+ uint8_t len);
};
};
:id_(getNextID()), first_(first), last_(last) {
}
-bool Pool::inRange(const isc::asiolink::IOAddress& addr) {
- return ( first_.smallerEqual(addr) && addr.smallerEqual(last_) );
+bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
+ return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
}
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
// 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.
+ // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning
+ // would be useful.
// first_ = last;
// last_ = first;
}
+
// 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)
isc_throw(BadValue, "Invalid prefix length");
}
+ /// @todo: We should probably implement checks against weird addresses
+ /// here, like ::, starting with fe80, starting with ff etc. .
+
// Let's now calculate the last address in defined pool
last_ = lastAddrInPrefix(prefix, prefix_len);
}
}
}
-bool Subnet::inRange(const isc::asiolink::IOAddress& addr) {
+bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
- return ( (first <= addr) && (addr <= last) );
+ return ((first <= addr) && (addr <= last));
}
Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
:Subnet(prefix, length, t1, t2, valid_lifetime),
preferred_(preferred_lifetime){
if (prefix.getFamily() != AF_INET6) {
- isc_throw(BadValue, "Invalid prefix " << prefix.toText()
+ isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText()
<< " specified in subnet6");
}
}
<< ") subnet6");
}
+ /// @todo: Check that pools do not overlap
+
pools_.push_back(pool);
}
CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
// If there's only one subnet configured, let's just use it
+ // The idea is to keep small deployments easy. In a small network - one
+ // router that also runs DHCPv6 server. Users specifies a single pool and
+ // expects it to just work. Without this, the server would complain that it
+ // doesn't have IP address on its interfaces that matches that
+ // configuration. Such requirement makes sense in IPv4, but not in IPv6.
+ // The server does not need to have a global address (using just link-local
+ // is ok for DHCPv6 server) from the pool it serves.
if (subnets6_.size() == 1) {
return (subnets6_[0]);
}
class Subnet6;
-/// @brief this template specifes a parameter value
+/// @brief this template specifies a parameter value
///
/// This template class is used to store configuration parameters, like lifetime or T1.
/// It defines 3 parameters: min, default, and max value. There are 2 constructors:
///
/// Typically: uint32_t to Triplet assignment. It is very convenient
/// to be able to simply write Triplet<uint32_t> x = 7;
- Triplet<T>& operator = (T base_type) {
- return Triplet<T>(base_type);
+ Triplet<T> operator=(T other) {
+ min_ = other;
+ default_ = other;
+ max_ = other;
+ return *this;
}
/// @brief triplet to base type conversion
///
/// Typically: Triplet to uint32_t assignment. It is very convenient
/// to be able to simply write uint32_t z = x; (where x is a Triplet)
- operator T () const {
+ operator T() const {
return (default_);
}
/// @throw BadValue if min <= def <= max rule is violated
Triplet(T min, T def, T max)
:min_(min), default_(def), max_(max) {
- if ( (min_>def) || (def > max_) ) {
+ if ( (min_ > def) || (def > max_) ) {
isc_throw(BadValue, "Invalid triplet values.");
}
}
/// @brief returns Pool-id
///
+ /// @return pool-id value
/// Pool-id is an unique value that can be used to identify a pool.
uint32_t getId() const {
return (id_);
/// @brief Checks if a given address is in the range.
///
/// @return true, if the address is in pool
- bool inRange(const isc::asiolink::IOAddress& addr);
+ bool inRange(const isc::asiolink::IOAddress& addr) const;
protected:
/// @brief pool-id
///
- /// This ID is used to indentify this specific pool.
+ /// This ID is used to identify this specific pool.
uint32_t id_;
/// @brief The first address in a pool
return (type_);
}
-protected:
+private:
/// @brief defines a pool type
Pool6Type type_;
class Subnet {
public:
/// @brief checks if specified address is in range
- bool inRange(const isc::asiolink::IOAddress& addr);
+ bool inRange(const isc::asiolink::IOAddress& addr) const;
/// @brief return valid-lifetime for addresses in that prefix
Triplet<uint32_t> getValid() const {
/// This class represents an IPv6 subnet.
class Subnet6 : public Subnet {
public:
+
+ /// @brief Constructor with all parameters
+ ///
+ /// @param prefix Subnet6 prefix
+ /// @param length prefix length
+ /// @param t1 renewal timer (in seconds)
+ /// @param t2 rebind timer (in seconds)
+ /// @param preferred_lifetime preferred lifetime of leases (in seconds)
+ /// @param valid_lifetime preferred lifetime of leases (in seconds)
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);
+ /// @brief Returns preverred lifetime (in seconds)
+ ///
+ /// @return a triplet with preferred lifetime
Triplet<uint32_t> getPreferred() const {
return (preferred_);
}
+ /// @brief Returns a pool that specified address belongs to
+ ///
+ /// @param hint address that the returned pool should cover (optional)
+ /// @return Pointer to found pool6 (or NULL)
Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint =
isc::asiolink::IOAddress("::"));
+ /// @brief Adds a new pool.
+ /// @param pool pool to be added
void addPool6(const Pool6Ptr& pool);
+ /// @brief returns all pools
+ ///
+ /// The reference is only valid as long as the object that
+ /// returned it.
+ ///
+ /// @return a collection of all pools
const Pool6Collection& getPools() const {
return pools_;
}
protected:
- /// collection of pools in that list
+ /// @brief collection of pools in that list
Pool6Collection pools_;
+ /// @brief a triplet with preferred lifetime (in seconds)
Triplet<uint32_t> preferred_;
};
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
if USE_CLANGPP
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
libdhcp___unittests_LDADD += $(GTEST_LDADD)
endif
EXPECT_EQ("2001::3f", lastAddrInPrefix(addr2, 122).toText());
EXPECT_EQ("2001::7f", lastAddrInPrefix(addr2, 121).toText());
EXPECT_EQ("2001::ff", lastAddrInPrefix(addr2, 120).toText());
+
+ // Let's check extreme cases
+ IOAddress anyAddr("::");
+ EXPECT_EQ("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ lastAddrInPrefix(anyAddr, 1).toText());
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ lastAddrInPrefix(anyAddr, 0).toText());
+ EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
}
TEST(Pool6Test, firstAddrInPrefix) {
// constructor validation
TEST(TripletTest, constructor) {
- uint32_t min = 10;
- uint32_t value = 20;
- uint32_t max = 30;
+ const uint32_t min = 10;
+ const uint32_t value = 20;
+ const uint32_t max = 30;
Triplet<uint32_t> x(min, value, max);
- EXPECT_EQ(10, x.getMin());
- EXPECT_EQ(20, x.get());
- EXPECT_EQ(30, x.getMax());
+ EXPECT_EQ(min, x.getMin());
+ EXPECT_EQ(value, x.get());
+ EXPECT_EQ(max, x.getMax());
// requested values below min should return allowed min value
- EXPECT_EQ(10, x.get(5));
+ EXPECT_EQ(min, x.get(min - 5));
- EXPECT_EQ(10, x.get(10));
+ EXPECT_EQ(min, x.get(min));
// requesting a value from within the range (min < x < max) should
// return the requested value
EXPECT_EQ(17, x.get(17));
- EXPECT_EQ(30, x.get(30));
+ EXPECT_EQ(max, x.get(max));
- EXPECT_EQ(30, x.get(35));
+ EXPECT_EQ(max, x.get(max + 5));
// this will be boring. It is expected to return 42 no matter what
Triplet<uint32_t> y(42);
uint32_t x = 47;
+ Triplet<uint32_t> foo(1,2,3);
+ Triplet<uint32_t> bar(4,5,6);
+
+ foo = bar;
+
+ EXPECT_EQ(4, foo.getMin());
+ EXPECT_EQ(5, foo.get());
+ EXPECT_EQ(6, foo.getMax());
+
// assignment operator: uint32_t => triplet
- Triplet<uint32_t> y = x;
+ Triplet<uint32_t> y(0);
+ y = x;
- EXPECT_EQ(47, y.get());
+ EXPECT_EQ(x, y.get());
// let's try the other way around: triplet => uint32_t
- uint32_t z = y;
+ uint32_t z = 0;
+ z = y;
- EXPECT_EQ(47, z);
+ EXPECT_EQ(x, z);
}
// check if specified values are sane
EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());
+ // No such thing as /130 prefix
+ EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 130),
+ BadValue);
+
+ // /0 prefix does not make sense
+ EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 0),
+ BadValue);
+
// This is Pool6, IPv4 addresses do not belong here
EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
BadValue);
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
int result = RUN_ALL_TESTS();