return (identifier_type_);
}
+Host::IdentifierType
+Host::getIdentifierType(const std::string& identifier_name) {
+ if (identifier_name == "hw-address") {
+ return (IDENT_HWADDR);
+
+ } else if (identifier_name == "duid") {
+ return (IDENT_DUID);
+
+ } else if (identifier_name == "circuit-id") {
+ return (IDENT_CIRCUIT_ID);
+
+ } else {
+ isc_throw(isc::BadValue, "invalid client identifier type '"
+ << identifier_name << "'");
+ }
+}
+
HWAddrPtr
Host::getHWAddress() const {
return ((identifier_type_ == IDENT_HWADDR) ?
void
Host::setIdentifier(const std::string& identifier, const std::string& name) {
- // HW address and DUID are special cases because they are typically
- // specified as values with colons between consecutive octets. Thus,
- // we use the HWAddr and DUID classes to validate them and to
- // convert them into binary format.
- if (name == "hw-address") {
- HWAddr hwaddr(HWAddr::fromText(identifier));
- identifier_type_= IDENT_HWADDR;
- identifier_value_ = hwaddr.hwaddr_;
-
- } else if (name == "duid") {
- identifier_type_ = IDENT_DUID;
- DUID duid(DUID::fromText(identifier));
- identifier_value_ = duid.getDuid();
-
- } else {
- if (name == "circuit-id") {
- identifier_type_ = IDENT_CIRCUIT_ID;
+ // Empty identifier is not allowed.
+ if (identifier.empty()) {
+ isc_throw(isc::BadValue, "empty host identifier used");
+ }
- } else {
- isc_throw(isc::BadValue, "invalid client identifier type '"
- << name << "' when creating host instance");
- }
+ // Set identifier type.
+ identifier_type_ = getIdentifierType(name);
- // Here we're converting values other than DUID and HW address. These
- // values can either be specified as strings of hexadecimal digits or
- // strings in quotes. The latter are copied to a vector excluding quote
- // characters.
+ // Idetifier value can either be specified as string of hexadecimal
+ // digits or a string in quotes. The latter is copied to a vector excluding
+ // quote characters.
- // Try to convert the values in quotes into a vector of ASCII codes.
- // If the identifier lacks opening and closing quote, this will return
- // an empty value, in which case we'll try to decode it as a string of
- // hexadecimal digits.
+ // Try to convert the values in quotes into a vector of ASCII codes.
+ // If the identifier lacks opening and closing quote, this will return
+ // an empty value, in which case we'll try to decode it as a string of
+ // hexadecimal digits.
+ try {
std::vector<uint8_t> binary = util::str::quotedStringToBinary(identifier);
if (binary.empty()) {
- try {
- util::encode::decodeHex(identifier, binary);
-
- } catch (...) {
- // The string doesn't match any known pattern, so we have to
- // report an error at this point.
- isc_throw(isc::BadValue, "invalid host identifier value '"
- << identifier << "'");
- }
+ util::str::decodeFormattedHexString(identifier, binary);
}
-
// Successfully decoded the identifier, so let's use it.
identifier_value_.swap(binary);
+
+ } catch (...) {
+ // The string doesn't match any known pattern, so we have to
+ // report an error at this point.
+ isc_throw(isc::BadValue, "invalid host identifier value '"
+ << identifier << "'");
}
}
namespace {
+/// @brief Holds a type of the last identifier in @c IdentifierType enum.
+///
+/// This value must be updated when new identifiers are added to the enum.
+const Host::IdentifierType LAST_IDENTIFIER_TYPE = Host::IDENT_CIRCUIT_ID;
+
/// @brief Test fixture class for @c HostReservationParser.
class HostReservationParserTest : public ::testing::Test {
protected:
return (OptionPtr());
}
- void
- expectFailure(const HostReservationParser& parser,
- const std::string& config) const;
-
/// @brief This test verifies that it is possible to specify an empty list
/// of options for a host.
///
// This test verfies that the parser can parse the reservation entry for
// which hw-address is a host identifier.
TEST_F(HostReservationParserTest, dhcp4HWaddr) {
- std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
- "\"ip-address\": \"192.0.2.134\","
- "\"hostname\": \"foo.example.com\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(1));
- ASSERT_NO_THROW(parser.build(config_element));
-
- CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
- HostCollection hosts;
- ASSERT_NO_THROW(hosts = cfg_hosts->getAll(hwaddr_, DuidPtr()));
-
- ASSERT_EQ(1, hosts.size());
-
- EXPECT_EQ(1, hosts[0]->getIPv4SubnetID());
- EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
- EXPECT_EQ("192.0.2.134", hosts[0]->getIPv4Reservation().toText());
- EXPECT_EQ("foo.example.com", hosts[0]->getHostname());
+ testIdentifier4("hw-address", "1:2:3:4:5:6", Host::IDENT_HWADDR,
+ hwaddr_->hwaddr_);
}
-// This test verfies that the parser can parse the reservation entry for
+// This test verifies that the parser can parse the reservation entry for
// which DUID is a host identifier.
TEST_F(HostReservationParserTest, dhcp4DUID) {
- std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
- "\"ip-address\": \"192.0.2.112\","
- "\"hostname\": \"\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- ASSERT_NO_THROW(parser.build(config_element));
-
- CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
- HostCollection hosts;
- ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
-
- ASSERT_EQ(1, hosts.size());
+ testIdentifier4("duid", "01:02:03:04:05:06:07:08:09:0A",
+ Host::IDENT_DUID, duid_->getDuid());
+}
- EXPECT_EQ(10, hosts[0]->getIPv4SubnetID());
- EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
- EXPECT_EQ("192.0.2.112", hosts[0]->getIPv4Reservation().toText());
- EXPECT_TRUE(hosts[0]->getHostname().empty());
+// This test verifies that the parser can parse the reservation entry for
+// which DUID specified as a string of hexadecimal digits with '0x' prefix
+// is a host identifier
+TEST_F(HostReservationParserTest, dhcp4DUIDWithPrefix) {
+ testIdentifier4("duid", "0x0102030405060708090A",
+ Host::IDENT_DUID, duid_->getDuid());
}
// This test verifies that the parser can parse a reservation entry for
circuit_id_);
}
+// This test verifies that the parser can parse a reservation entry for
+// which circuit-id is an identifier. The circuit-id is specified in
+// hexadecimal format with a '0x' prefix.
+TEST_F(HostReservationParserTest, dhcp4CircuitIdHexWithPrefix) {
+ testIdentifier4("circuit-id", "0x686F776479", Host::IDENT_CIRCUIT_ID,
+ circuit_id_);
+}
+
// This test verifies that the parser can parse the reservation entry
// when IPv4 address is specified, but hostname is not.
TEST_F(HostReservationParserTest, dhcp4NoHostname) {
TEST_F(HostReservationParserTest, dhcp4IPv6Address) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"ip-address\": \"2001:db8:1::1\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the configuration parser for host reservations
TEST_F(HostReservationParserTest, noIdentifier) {
std::string config = "{ \"ip-address\": \"192.0.2.112\","
"\"hostname\": \"\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the configuration parser for host reservations
// throws an exception when neither ip address nor hostname is specified.
TEST_F(HostReservationParserTest, noResource) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the parser can parse the reservation entry
TEST_F(HostReservationParserTest, emptyHostname) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"hostname\": \"\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the configuration parser for host reservations
TEST_F(HostReservationParserTest, malformedAddress) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"ip-address\": \"192.0.2.bogus\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the configuration parser for host reservations
TEST_F(HostReservationParserTest, zeroAddress) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"ip-address\": \"0.0.0.0\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the configuration parser for host reservations
TEST_F(HostReservationParserTest, bcastAddress) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"ip-address\": \"255.255.255.255\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verifies that the configuration parser for host reservations
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"hostname\": \"foo.bar.isc.org\","
"\"ip-addresses\": \"2001:db8:1::1\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser4 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser4>(config);
}
// This test verfies that the parser can parse the IPv6 reservation entry for
"\"ip-addresses\": [ \"2001:db8:1::100\", \"2001:db8:1::200\" ],"
"\"prefixes\": [ ],"
"\"hostname\": \"foo.example.com\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- // The parser should throw exception.
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verfies that the parser can parse the IPv6 reservation entry
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"ip-addresses\": [ \"192.0.2.3\", \"2001:db8:1::200\" ],"
"\"prefixes\": [ ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that the configuration parser throws an exception
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"ip-addresses\": [ \"\" ],"
"\"prefixes\": [ ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that the configuration parser throws an exception
TEST_F(HostReservationParserTest, dhcp6InvalidPrefixLength) {
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"prefixes\": [ \"2001:db8:1::/abc\" ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that the configuration parser throws an exception
TEST_F(HostReservationParserTest, dhcp6NullPrefix) {
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"prefixes\": [ \"/64\" ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that the configuration parser throws an exception
TEST_F(HostReservationParserTest, dhcp6NullPrefix2) {
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"prefixes\": [ \"/\" ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that the configuration parser throws an exception
TEST_F(HostReservationParserTest, dhcp6DuplicatedAddress) {
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"ip-addresses\": [ \"2001:db8:1::1\", \"2001:db8:1::1\" ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that the configuration parser throws an exception
TEST_F(HostReservationParserTest, dhcp6DuplicatedPrefix) {
std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
"\"prefixes\": [ \"2001:db8:0101::/64\", \"2001:db8:0101::/64\" ] }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(12));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
-
// This test verifies that the configuration parser for host reservations
// throws an exception when unsupported parameter is specified.
TEST_F(HostReservationParserTest, dhcp6invalidParameterName) {
std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
"\"hostname\": \"foo.bar.isc.org\","
"\"ip-address\": \"192.0.2.3\" }";
-
- ElementPtr config_element = Element::fromJSON(config);
-
- HostReservationParser6 parser(SubnetID(10));
- EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ testInvalidConfig<HostReservationParser6>(config);
}
// This test verifies that it is possible to specify DHCPv4 options for
testInvalidConfig<HostReservationParser6>(config);
}
+// This test verifies that host identifiers for DHCPv4 are mutually exclusive.
+TEST_F(HostReservationParserTest, mutuallyExclusiveIdentifiers4) {
+ std::vector<std::string> identifiers;
+ identifiers.push_back("hw-address");
+ identifiers.push_back("duid");
+ identifiers.push_back("circuit-id");
+
+ for (unsigned int i = 0; i < identifiers.size(); ++i) {
+ // j points to an index of the next identifier. If it
+ // overflows, we set it to 0.
+ unsigned int j = (i + 1) % (identifiers.size());
+ Host::IdentifierType first = static_cast<Host::IdentifierType>(i);
+ Host::IdentifierType second = static_cast<Host::IdentifierType>(j);
+
+ SCOPED_TRACE("Using identifiers " + Host::getIdentifierName(first)
+ + " and " + Host::getIdentifierName(second));
+
+ // Create configuration with two different identifiers.
+ std::ostringstream config;
+ config << "{ \"" << Host::getIdentifierName(first) << "\": \"121314151617\","
+ "\"" << Host::getIdentifierName(second) << "\": \"0A0B0C0D0E0F\","
+ "\"ip-address\": \"192.0.2.3\" }";
+ testInvalidConfig<HostReservationParser4>(config.str());
+ }
+}
+
+// This test verifies that host identifiers for DHCPv6 are mutually exclusive.
+TEST_F(HostReservationParserTest, mutuallyExclusiveIdentifiers6) {
+ std::vector<std::string> identifiers;
+ identifiers.push_back("hw-address");
+ identifiers.push_back("duid");
+
+ for (unsigned int i = 0; i < identifiers.size(); ++i) {
+ // j points to an index of the next identifier. If it
+ // overflows, we set it to 0.
+ unsigned int j = (i + 1) % (identifiers.size());
+
+ SCOPED_TRACE("Using identifiers " + identifiers[i] + " and "
+ + identifiers[j]);
+
+ // Create configuration with two different identifiers.
+ std::ostringstream config;
+ config << "{ \"" << identifiers[i] << "\": \"121314151617\","
+ "\"" << identifiers[j] << "\": \"0A0B0C0D0E0F\","
+ "\"ip-addresses\": \"2001:db8:1::1\" }";
+ testInvalidConfig<HostReservationParser6>(config.str());
+ }
+}
+
} // end of anonymous namespace
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+#include <vector>
using namespace isc::data;
using namespace isc::dhcp;
EXPECT_EQ("bar.example.com", hosts[0]->getHostname());
}
+// This test verifies that an attempt to add two reservations with the
+// same identifier value will return an error.
+TEST_F(HostReservationsListParserTest, duplicatedIdentifierValue4) {
+ std::vector<std::string> identifiers;
+ identifiers.push_back("hw-address");
+ identifiers.push_back("duid");
+ identifiers.push_back("circuit-id");
+
+ for (unsigned int i = 0; i < identifiers.size(); ++i) {
+ SCOPED_TRACE("Using identifier " + identifiers[i]);
+
+ std::ostringstream config;
+ config <<
+ "[ "
+ " { "
+ " \"" << identifiers[i] << "\": \"010203040506\","
+ " \"ip-address\": \"192.0.2.134\","
+ " \"hostname\": \"foo.example.com\""
+ " }, "
+ " { "
+ " \"" << identifiers[i] << "\": \"010203040506\","
+ " \"ip-address\": \"192.0.2.110\","
+ " \"hostname\": \"bar.example.com\""
+ " } "
+ "]";
+
+ ElementPtr config_element = Element::fromJSON(config.str());
+
+ HostReservationsListParser<HostReservationParser4> parser(SubnetID(1));
+ EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ }
+}
+
// This test verifies that the parser for the list of the host reservations
// parses IPv6 reservations correctly.
TEST_F(HostReservationsListParserTest, ipv6Reservations) {
EXPECT_EQ(80, prefixes.first->second.getPrefixLen());
}
+// This test verifies that an attempt to add two reservations with the
+// same identifier value will return an error.
+TEST_F(HostReservationsListParserTest, duplicatedIdentifierValue6) {
+ std::vector<std::string> identifiers;
+ identifiers.push_back("hw-address");
+ identifiers.push_back("duid");
+
+ for (unsigned int i = 0; i < identifiers.size(); ++i) {
+ SCOPED_TRACE("Using identifier " + identifiers[i]);
+
+ std::ostringstream config;
+ config <<
+ "[ "
+ " { "
+ " \"" << identifiers[i] << "\": \"010203040506\","
+ " \"ip-addresses\": [ \"2001:db8:1::123\" ],"
+ " \"hostname\": \"foo.example.com\""
+ " }, "
+ " { "
+ " \"" << identifiers[i] << "\": \"010203040506\","
+ " \"ip-addresses\": [ \"2001:db8:1::123\" ],"
+ " \"hostname\": \"bar.example.com\""
+ " } "
+ "]";
+
+ ElementPtr config_element = Element::fromJSON(config.str());
+
+ HostReservationsListParser<HostReservationParser6> parser(SubnetID(1));
+ EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+ }
+}
+
+
+
} // end of anonymous namespace