Commit fb5e1883 authored by Marcin Siodelski's avatar Marcin Siodelski

[master] Merge branch 'trac3560'

parents ad44a39d 33f53f5e
......@@ -14,7 +14,7 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libkea-dhcp++.la
libkea_dhcp___la_SOURCES =
libkea_dhcp___la_SOURCES += classify.h
libkea_dhcp___la_SOURCES += classify.cc classify.h
libkea_dhcp___la_SOURCES += dhcp6.h dhcp4.h
libkea_dhcp___la_SOURCES += duid.cc duid.h
libkea_dhcp___la_SOURCES += hwaddr.cc hwaddr.h
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// 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 <dhcp/classify.h>
#include <util/strutil.h>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/constants.hpp>
#include <boost/algorithm/string/split.hpp>
#include <vector>
namespace isc {
namespace dhcp {
ClientClasses::ClientClasses(const std::string& class_names)
: std::set<ClientClass>() {
std::vector<std::string> split_text;
boost::split(split_text, class_names, boost::is_any_of(","),
boost::algorithm::token_compress_off);
for (int i = 0; i < split_text.size(); ++i) {
std::string trimmed = util::str::trim(split_text[i]);
// Ignore empty class names.
if (!trimmed.empty()) {
insert(ClientClass(trimmed));
}
}
}
} // end of namespace isc::dhcp
} // end of namespace isc
......@@ -53,6 +53,17 @@ namespace dhcp {
/// documentation. See http://www.cplusplus.com/reference/set/set/.
class ClientClasses : public std::set<ClientClass> {
public:
/// @brief Default constructor.
ClientClasses() : std::set<ClientClass>() {
}
/// @brief Constructor from comma separated values.
///
/// @param class_names A string containing a client classes separated
/// with commas. The class names are trimmed before insertion to the set.
ClientClasses(const std::string& class_names);
/// @brief returns if class x belongs to the defined classes
///
/// @param x client class to be checked
......
......@@ -20,6 +20,7 @@
#include <boost/algorithm/string/constants.hpp>
#include <boost/algorithm/string/split.hpp>
#include <iomanip>
#include <cctype>
#include <sstream>
#include <vector>
......@@ -30,20 +31,20 @@ namespace dhcp {
DUID::DUID(const std::vector<uint8_t>& duid) {
if (duid.size() > MAX_DUID_LEN) {
isc_throw(OutOfRange, "DUID too large");
isc_throw(isc::BadValue, "DUID too large");
}
if (duid.empty()) {
isc_throw(OutOfRange, "Empty DUIDs are not allowed");
isc_throw(isc::BadValue, "Empty DUIDs are not allowed");
}
duid_ = duid;
}
DUID::DUID(const uint8_t* data, size_t len) {
if (len > MAX_DUID_LEN) {
isc_throw(OutOfRange, "DUID too large");
isc_throw(isc::BadValue, "DUID too large");
}
if (len == 0) {
isc_throw(OutOfRange, "Empty DUIDs/Client-ids not allowed");
isc_throw(isc::BadValue, "Empty DUIDs/Client-ids not allowed");
}
duid_ = std::vector<uint8_t>(data, data + len);
......@@ -58,19 +59,35 @@ DUID::decode(const std::string& text) {
std::ostringstream s;
for (size_t i = 0; i < split_text.size(); ++i) {
// Check that only hexadecimal digits are used.
size_t ch_index = 0;
while (ch_index < split_text[i].length()) {
if (!isxdigit(split_text[i][ch_index])) {
isc_throw(isc::BadValue, "invalid value '"
<< split_text[i][ch_index] << "' in"
<< " DUID '" << text << "'");
}
++ch_index;
}
if (split_text.size() > 1) {
// If there are multiple tokens and the current one is empty, it
// means that two consecutive colons were specified. This is not
// allowed for client identifier.
if ((split_text.size() > 1) && split_text[i].empty()) {
isc_throw(isc::BadValue, "invalid identifier '" << text << "': "
<< " tokens must be separated with a single colon");
if (split_text[i].empty()) {
isc_throw(isc::BadValue, "invalid identifier '"
<< text << "': " << " tokens must be"
" separated with a single colon");
} else if (split_text[i].size() > 2) {
isc_throw(isc::BadValue, "invalid identifier '"
<< text << "'");
}
}
} else if (split_text[i].size() == 1) {
if (split_text[i].size() % 2) {
s << "0";
} else if (split_text[i].size() > 2) {
isc_throw(isc::BadValue, "invalid identifier '" << text << "'");
}
s << split_text[i];
}
......@@ -133,7 +150,7 @@ bool DUID::operator!=(const DUID& other) const {
ClientId::ClientId(const std::vector<uint8_t>& clientid)
: DUID(clientid) {
if (clientid.size() < MIN_CLIENT_ID_LEN) {
isc_throw(OutOfRange, "client-id is too short (" << clientid.size()
isc_throw(isc::BadValue, "client-id is too short (" << clientid.size()
<< "), at least 2 is required");
}
}
......@@ -142,7 +159,7 @@ ClientId::ClientId(const std::vector<uint8_t>& clientid)
ClientId::ClientId(const uint8_t *clientid, size_t len)
: DUID(clientid, len) {
if (len < MIN_CLIENT_ID_LEN) {
isc_throw(OutOfRange, "client-id is too short (" << len
isc_throw(isc::BadValue, "client-id is too short (" << len
<< "), at least 2 is required");
}
}
......
......@@ -98,8 +98,8 @@ class DUID {
/// representing bytes of DUID must be separated by colons. Usually the
/// single byte is represented by two hexadecimal digits. However, this
/// function allows one digit per byte. In this case, a zero is prepended
/// before the conversion. For example, a DUID 0:1:2::4:5 equals to
/// 00:01:02:00:04:05.
/// before the conversion. For example, a DUID 0:1:2:3:4:5 equals to
/// 00:01:02:03:04:05.
///
/// @param text DUID in the hexadecimal format with digits representing
/// individual bytes separated by colons.
......
......@@ -34,14 +34,14 @@ HWAddr::HWAddr()
HWAddr::HWAddr(const uint8_t* hwaddr, size_t len, uint16_t htype)
:hwaddr_(hwaddr, hwaddr + len), htype_(htype) {
if (len > MAX_HWADDR_LEN) {
isc_throw(InvalidParameter, "hwaddr length exceeds MAX_HWADDR_LEN");
isc_throw(isc::BadValue, "hwaddr length exceeds MAX_HWADDR_LEN");
}
}
HWAddr::HWAddr(const std::vector<uint8_t>& hwaddr, uint16_t htype)
:hwaddr_(hwaddr), htype_(htype) {
if (hwaddr.size() > MAX_HWADDR_LEN) {
isc_throw(InvalidParameter,
isc_throw(isc::BadValue,
"address vector size exceeds MAX_HWADDR_LEN");
}
}
......
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -37,7 +37,6 @@ TEST(ClassifyTest, ClientClasses) {
EXPECT_FALSE(classes.contains("alpha"));
EXPECT_FALSE(classes.contains("beta"));
EXPECT_FALSE(classes.contains("gamma"));
classes.insert("beta");
EXPECT_FALSE(classes.contains(""));
EXPECT_FALSE(classes.contains("alpha"));
......@@ -50,3 +49,39 @@ TEST(ClassifyTest, ClientClasses) {
EXPECT_TRUE (classes.contains("beta"));
EXPECT_TRUE (classes.contains("gamma"));
}
// Check if ClientClasses object can be created from the string of comma
// separated values.
TEST(ClassifyTest, ClientClassesFromString) {
{
ClientClasses classes("alpha, beta, gamma");
EXPECT_EQ(3, classes.size());
EXPECT_FALSE(classes.contains(""));
EXPECT_TRUE(classes.contains("alpha"));
EXPECT_TRUE(classes.contains("beta"));
EXPECT_TRUE(classes.contains("gamma"));
}
{
ClientClasses classes("alpha, , beta ,");
EXPECT_EQ(2, classes.size());
EXPECT_TRUE(classes.contains("alpha"));
EXPECT_FALSE(classes.contains(""));
EXPECT_TRUE(classes.contains("beta"));
}
{
ClientClasses classes("");
EXPECT_TRUE(classes.empty());
}
{
ClientClasses classes(" ");
EXPECT_TRUE(classes.empty());
}
{
ClientClasses classes(", ,, ,");
EXPECT_TRUE(classes.empty());
}
}
......@@ -84,24 +84,24 @@ TEST(DuidTest, size) {
EXPECT_THROW(
scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_LEN + 1)),
OutOfRange);
BadValue);
// that's one too much
data2.push_back(128);
EXPECT_THROW(
scoped_ptr<DUID> toolarge2(new DUID(data2)),
OutOfRange);
BadValue);
// empty duids are not allowed
vector<uint8_t> empty;
EXPECT_THROW(
scoped_ptr<DUID> emptyDuid(new DUID(empty)),
OutOfRange);
BadValue);
EXPECT_THROW(
scoped_ptr<DUID> emptyDuid2(new DUID(data, 0)),
OutOfRange);
BadValue);
}
// This test verifies if the implementation supports all defined
......@@ -222,33 +222,33 @@ TEST(ClientIdTest, size) {
EXPECT_THROW(
scoped_ptr<ClientId> toolarge1(new ClientId(data, MAX_CLIENT_ID_LEN + 1)),
OutOfRange);
BadValue);
// that's one too much
data2.push_back(128);
EXPECT_THROW(
scoped_ptr<ClientId> toolarge2(new ClientId(data2)),
OutOfRange);
BadValue);
// empty client-ids are not allowed
vector<uint8_t> empty;
EXPECT_THROW(
scoped_ptr<ClientId> empty_client_id1(new ClientId(empty)),
OutOfRange);
BadValue);
EXPECT_THROW(
scoped_ptr<ClientId> empty_client_id2(new ClientId(data, 0)),
OutOfRange);
BadValue);
// client-id must be at least 2 bytes long
vector<uint8_t> shorty(1,17); // just a single byte with value 17
EXPECT_THROW(
scoped_ptr<ClientId> too_short_client_id1(new ClientId(shorty)),
OutOfRange);
BadValue);
EXPECT_THROW(
scoped_ptr<ClientId> too_short_client_id1(new ClientId(data, 1)),
OutOfRange);
BadValue);
}
// This test checks if the comparison operators are sane.
......@@ -299,6 +299,11 @@ TEST(ClientIdTest, fromText) {
cid = ClientId::fromText("00:a:bb:D:ee:EF:ab")
);
EXPECT_EQ("00:0a:bb:0d:ee:ef:ab", cid->toText());
// ClientId without any colons is allowed.
ASSERT_NO_THROW(
cid = ClientId::fromText("0010abcdee");
);
EXPECT_EQ("00:10:ab:cd:ee", cid->toText());
// Repeated colon sign in the ClientId is not allowed.
EXPECT_THROW(
ClientId::fromText("00::bb:D:ee:EF:ab"),
......@@ -310,6 +315,23 @@ TEST(ClientIdTest, fromText) {
ClientId::fromText("00:01:021:03:04:05:06"),
isc::BadValue
);
// ClientId with two spaces between the colons should not be allowed.
EXPECT_THROW(
ClientId::fromText("00:01: :03:04:05:06"),
isc::BadValue
);
// ClientId with one space between the colons should not be allowed.
EXPECT_THROW(
ClientId::fromText("00:01: :03:04:05:06"),
isc::BadValue
);
// ClientId with three spaces between the colons should not be allowed.
EXPECT_THROW(
ClientId::fromText("00:01: :03:04:05:06"),
isc::BadValue
);
}
......
......@@ -60,10 +60,10 @@ TEST(HWAddrTest, constructor) {
// Check that over the limit data length throws exception
EXPECT_THROW(HWAddr(&big_data_vector[0], big_data_vector.size(), HTYPE_ETHER),
InvalidParameter);
BadValue);
// Check that over the limit vector throws exception
EXPECT_THROW(HWAddr(big_data_vector, HTYPE_ETHER), InvalidParameter);
EXPECT_THROW(HWAddr(big_data_vector, HTYPE_ETHER), BadValue);
}
// This test checks if the comparison operators are sane.
......
......@@ -58,6 +58,7 @@ libkea_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libkea_dhcpsrv_la_SOURCES += dhcp_config_parser.h
libkea_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.h
libkea_dhcpsrv_la_SOURCES += host.cc host.h
libkea_dhcpsrv_la_SOURCES += key_from_key.h
libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
......@@ -76,6 +77,7 @@ libkea_dhcpsrv_la_SOURCES += option_space_container.h
libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
libkea_dhcpsrv_la_SOURCES += subnet_id.h
libkea_dhcpsrv_la_SOURCES += triplet.h
libkea_dhcpsrv_la_SOURCES += utils.h
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// 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 <dhcpsrv/host.h>
#include <util/strutil.h>
#include <exceptions/exceptions.h>
namespace isc {
namespace dhcp {
IPv6Resrv::IPv6Resrv(const asiolink::IOAddress& prefix,
const uint8_t prefix_len)
: prefix_(asiolink::IOAddress("::")), prefix_len_(128) {
// Validate and set the actual values.
set(prefix, prefix_len);
}
void
IPv6Resrv::set(const asiolink::IOAddress& prefix, const uint8_t prefix_len) {
if (!prefix.isV6() || prefix.isV6Multicast()) {
isc_throw(isc::BadValue, "invalid prefix '" << prefix
<< " for new IPv6 reservation");
} else if (prefix_len > 128) {
isc_throw(isc::BadValue, "invalid prefix length '"
<< static_cast<int>(prefix_len)
<< "' for new IPv6 reservation");
}
prefix_ = prefix;
prefix_len_ = prefix_len;
}
bool
IPv6Resrv::operator==(const IPv6Resrv& other) const {
return (prefix_ == other.prefix_ &&
prefix_len_ == other.prefix_len_);
}
bool
IPv6Resrv::operator!=(const IPv6Resrv& other) const {
return (!operator==(other));
}
Host::Host(const uint8_t* identifier, const size_t identifier_len,
const IdentifierType& identifier_type,
const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
const asiolink::IOAddress& ipv4_reservation,
const std::string& hostname,
const std::string& dhcp4_client_classes,
const std::string& dhcp6_client_classes)
: hw_address_(), duid_(), ipv4_subnet_id_(ipv4_subnet_id),
ipv6_subnet_id_(ipv6_subnet_id), ipv4_reservation_(ipv4_reservation),
hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
dhcp6_client_classes_(dhcp6_client_classes) {
// Initialize HWAddr or DUID
setIdentifier(identifier, identifier_len, identifier_type);
}
Host::Host(const std::string& identifier, const std::string& identifier_name,
const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
const asiolink::IOAddress& ipv4_reservation,
const std::string& hostname,
const std::string& dhcp4_client_classes,
const std::string& dhcp6_client_classes)
: hw_address_(), duid_(), ipv4_subnet_id_(ipv4_subnet_id),
ipv6_subnet_id_(ipv6_subnet_id), ipv4_reservation_(ipv4_reservation),
hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
dhcp6_client_classes_(dhcp6_client_classes) {
// Initialize HWAddr or DUID
setIdentifier(identifier, identifier_name);
}
void
Host::setIdentifier(const uint8_t* identifier, const size_t len,
const IdentifierType& type) {
switch (type) {
case IDENT_HWADDR:
hw_address_ = HWAddrPtr(new HWAddr(identifier, len, HTYPE_ETHER));
duid_.reset();
break;
case IDENT_DUID:
duid_ = DuidPtr(new DUID(identifier, len));
hw_address_.reset();
break;
default:
isc_throw(isc::BadValue, "invalid client identifier type '"
<< static_cast<int>(type) << "' when creating host "
" instance");
}
}
void
Host::setIdentifier(const std::string& identifier, const std::string& name) {
if (name == "hw-address") {
hw_address_ = HWAddrPtr(new HWAddr(HWAddr::fromText(identifier)));
duid_.reset();
} else if (name == "duid") {
duid_ = DuidPtr(new DUID(DUID::fromText(identifier)));
hw_address_.reset();
} else {
isc_throw(isc::BadValue, "invalid client identifier type '"
<< name << "' when creating host instance");
}
}
void
Host::addReservation(const IPv6Resrv& reservation) {
ipv6_reservations_.insert(IPv6ResrvTuple(reservation.getType(),
reservation));
}
IPv6ResrvRange
Host::getIPv6Reservations(const IPv6Resrv::Type& type) const {
return (ipv6_reservations_.equal_range(type));
}
void
Host::addClientClassInternal(ClientClasses& classes,
const std::string& class_name) {
std::string trimmed = util::str::trim(class_name);
if (!class_name.empty()) {
classes.insert(ClientClass(class_name));
}
}
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// 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.
#ifndef HOST_H
#define HOST_H
#include <asiolink/io_address.h>
#include <dhcp/classify.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/subnet_id.h>
#include <list>
#include <map>
#include <string>
#include <utility>
namespace isc {
namespace dhcp {
/// @brief IPv6 reservation for a host.
///
/// This class represents a reservation for a host of a single IPv6
/// address or prefix (in @c Host object).
///
/// The class holds the address and prefix length, a value of 128
/// for the latter implying that the reservation is for a single
/// IPv6 address.
class IPv6Resrv {
public:
/// @brief Type of the reservation.
///
/// Currently supported types are NA and PD.
enum Type {
TYPE_NA,
TYPE_PD
};
/// @brief Constructor.
///
/// Creates a reservation from the IPv6 address and prefix length
/// value. If the prefix length is not specified, the default value
/// of 128 is used. This value indicates that the reservation is made
/// for an IPv6 address.
///
/// @param prefix Address or prefix to be reserved.
/// @param prefix_len Prefix length.
///
/// @throw isc::BadValue if prefix is not IPv6 prefix, is a
/// multicast address or the prefix length is greater than 128.
IPv6Resrv(const asiolink::IOAddress& prefix,
const uint8_t prefix_len = 128);
/// @brief Returns prefix for the reservation.
const asiolink::IOAddress& getPrefix() const {
return (prefix_);
}
/// @brief Returns prefix length.
uint8_t getPrefixLen() const {
return (prefix_len_);
}
/// @brief Returns reservation type.
///
/// The type of reservation is determined using a prefix length.
///
/// @return NA for prefix length equal to 128, PD otherwise.
Type getType() const {
return (prefix_len_ == 128 ? TYPE_NA : TYPE_PD);
}
/// @brief Sets a new prefix and prefix length.
///
/// @param prefix New prefix.
/// @param prefix_len New prefix length.
///
/// @throw isc::BadValue if prefix is not IPv6 prefix, is a
/// multicast address or the prefix length is greater than 128.
void set(const asiolink::IOAddress& prefix, const uint8_t prefix_len);
/// @brief Equality operator.
///
/// @param other Reservation to compare to.
bool operator==(const IPv6Resrv& other) const;
/// @brief Inequality operator.
///
/// @param other Reservation to compare to.
bool operator!=(const IPv6Resrv& other) const;
private:
asiolink::IOAddress prefix_; ///< Prefix
uint8_t prefix_len_; ///< Prefix length.
};
/// @brief Collection of IPv6 reservations for the host.
typedef std::multimap<IPv6Resrv::Type, IPv6Resrv> IPv6ResrvCollection;
typedef IPv6ResrvCollection::const_iterator IPv6ResrvIterator;
typedef std::pair<IPv6Resrv::Type, IPv6Resrv> IPv6ResrvTuple;
typedef std::pair<IPv6ResrvIterator, IPv6ResrvIterator> IPv6ResrvRange;
/// @brief Represents a device with IPv4 and/or IPv6 reservations.
///
/// This class represents a network device which can be identified
/// by a unique property, such as MAC address on the interface or
/// client identifier (DUID), and for which some resources are statically
/// assigned:
/// - IPv4 address which the device obtains when it contacts a DHCPv4 server
/// - IPv6 address(es) which the device obtains when it contacts a DHCPv6
/// server
/// - IPv6 prefix(es) obtained when the device contacts the DHCPv6 server
/// and requests allocation of prefixes using prefix delegation mechanism
/// - hostname which is used for dynamic DNS updates for both DHCPv4 and
/// DHCPv6 exchanges.