Commit 28c4b257 authored by Shawn Routhier's avatar Shawn Routhier
Browse files

Merge branch 'master' of ssh://git.kea.isc.org/git/kea

Conflicts:
	ChangeLog
parents 8b3ea8e2 36773ccf
1120. [bug] sar
1121. [bug] sar
Update the classification document to match the output from
the debug statements.
(Trac NA, git TBD)
1120. [func] marcin
Extended MySQL host data source to retrieve DHCPv4 and DHCPv6
options associated with hosts from a MySQL database.
(Trac #4281, git b8a306a27d1cae03f6bc5223c30806f5cd1b64f4)
1119. [func] sar
Add debug logging to the classification tokens. This uses
the loggers "kea-dhcp4.eval" and "kea-dhcp6.eval" to capture
......
......@@ -165,6 +165,12 @@ EOF
EOF
ERRCODE=$?
assert_eq 0 $ERRCODE "host_identifier_type table is missing or broken. (returned status code %d, expected %d)"
# Seventh table: dhcp_option_scope
mysql -u$db_user -p$db_password $db_name >/dev/null 2>&1 <<EOF
SELECT scope_id, scope_name FROM dhcp_option_scope;
EOF
ERRCODE=$?
assert_eq 0 $ERRCODE "dhcp_option_scope table is missing or broken. (returned status code %d, expected %d)"
# Let's wipe the whole database
mysql_wipe
......
......@@ -59,8 +59,17 @@ namespace {
/// uses HW address for lease lookup, rather than client id
///
/// - Configuration 4:
/// - The same as configuration 2, but using different values in
/// 'host-reservation-identifiers'
/// - Used for testing host reservations where circuit-id takes precedence
/// over hw-address, and the hw-address takes precedence over duid.
/// - 1 subnet: 10.0.0.0/24
/// - 3 reservations for this subnet:
/// - IP address 10.0.0.7 for HW address aa:bb:cc:dd:ee:ff
/// - IP address 10.0.0.8 for DUID 01:02:03:04:05
/// - IP address 10.0.0.9 for circuit-id 'charter950'
///
/// - Configuration 5:
/// - The same as configuration 4, but using the following order of
/// host-reservation-identifiers: duid, circuit-id, hw-address.
///
const char* DORA_CONFIGS[] = {
// Configuration 0
......@@ -164,7 +173,33 @@ const char* DORA_CONFIGS[] = {
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"host-reservation-identifiers\": [ \"circuit-id\", \"hw-address\" ],"
"\"host-reservation-identifiers\": [ \"circuit-id\", \"hw-address\", \"duid\" ],"
"\"valid-lifetime\": 600,"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"reservations\": [ "
" {"
" \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
" \"ip-address\": \"10.0.0.7\""
" },"
" {"
" \"duid\": \"01:02:03:04:05\","
" \"ip-address\": \"10.0.0.8\""
" },"
" {"
" \"circuit-id\": \"'charter950'\","
" \"ip-address\": \"10.0.0.9\""
" }"
" ]"
"} ]"
"}",
// Configuration 5
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"host-reservation-identifiers\": [ \"duid\", \"circuit-id\", \"hw-address\" ],"
"\"valid-lifetime\": 600,"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
......@@ -727,6 +762,38 @@ TEST_F(DORATest, reservation) {
ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_));
}
// This test checks that it is possible to make a reservation by
// DUID carried in the Client Identifier option.
TEST_F(DORATest, reservationByDUID) {
Dhcp4Client client(Dhcp4Client::SELECTING);
// Use relay agent so as the circuit-id can be inserted.
client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
// Modify HW address so as the server doesn't assign reserved
// address by HW address.
client.modifyHWAddr();
// Specify DUID for which address 10.0.0.8 is reserved.
// The value specified as client id includes:
// - FF is a client identifier type for DUID,
// - 45454545 - represents 4 bytes for IAID
// - 01:02:03:04:05 - is an actual DUID for which there is a
// reservation.
client.includeClientId("FF:45:45:45:45:01:02:03:04:05");
// Configure DHCP server.
configure(DORA_CONFIGS[2], *client.getServer());
// Client A performs 4-way exchange and should obtain a reserved
// address.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_TRUE(client.getContext().response_);
Pkt4Ptr resp = client.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ("10.0.0.8", client.config_.lease_.addr_.toText());
}
// This test checks that it is possible to make a reservation by
// circuit-id inserted by the relay agent.
TEST_F(DORATest, reservationByCircuitId) {
......@@ -758,6 +825,13 @@ TEST_F(DORATest, hostIdentifiersOrder) {
client.setHWAddress("aa:bb:cc:dd:ee:ff");
// Use relay agent so as the circuit-id can be inserted.
client.useRelay(true, IOAddress("10.0.0.1"), IOAddress("10.0.0.2"));
// Specify DUID for which address 10.0.0.8 is reserved.
// The value specified as client id includes:
// - FF is a client identifier type for DUID,
// - 45454545 - represents 4 bytes for IAID
// - 01:02:03:04:05 - is an actual DUID for which there is a
// reservation.
client.includeClientId("FF:45:45:45:45:01:02:03:04:05");
// Specify circuit-id.
client.setCircuitId("charter950");
......@@ -778,7 +852,7 @@ TEST_F(DORATest, hostIdentifiersOrder) {
// Reconfigure the server to change the preference order of the
// host identifiers. The 'circuit-id' should now take precedence over
// the hw-address.
// the hw-address and duid.
configure(DORA_CONFIGS[4], *client.getServer());
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
......@@ -790,6 +864,19 @@ TEST_F(DORATest, hostIdentifiersOrder) {
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
// Reconfigure the server to change the preference order of the
// host identifiers. The 'duid' should now take precedence over
// the hw-address and circuit-id
configure(DORA_CONFIGS[5], *client.getServer());
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_TRUE(client.getContext().response_);
resp = client.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ("10.0.0.8", client.config_.lease_.addr_.toText());
}
// This test checks that setting the match-client-id value to false causes
......
......@@ -561,6 +561,26 @@ TEST(Element, escape) {
EXPECT_NO_THROW(Element::fromJSON("\" \n \r \t \f \n \n \t\""));
}
// This test verifies that a backslash can be used in element content
// when the element is created using constructor.
TEST(Element, backslash1) {
string input = "SMSBoot\\x64";// One slash passed to elem constructor...
string exp = "SMSBoot\\x64"; // ... should result in one slash in the actual option.
StringElement elem(input);
EXPECT_EQ(exp, elem.stringValue());
}
// This test verifies that a backslash can be used in element content
// when the element is created using fromJSON.
TEST(Element, backslash2) {
string input = "\"SMSBoot\\\\x64\""; // Two slashes put in the config file...
string exp = "SMSBoot\\x64"; // ... should result in one slash in the actual option.
ElementPtr elem = Element::fromJSON(input);
EXPECT_EQ(exp, elem->stringValue());
}
TEST(Element, ListElement) {
// this function checks the specific functions for ListElements
ElementPtr el = Element::fromJSON("[ 1, \"bar\", 3 ]");
......
// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-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
......@@ -21,9 +21,11 @@
#include <util/buffer.h>
#include <dhcp/option_definition.h>
#include <boost/lexical_cast.hpp>
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <limits>
#include <list>
using namespace std;
......@@ -844,6 +846,32 @@ LibDHCP::initVendorOptsIsc6() {
initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
}
uint32_t
LibDHCP::optionSpaceToVendorId(const std::string& option_space) {
// 8 is a minimal length of "vendor-X" format
if ((option_space.size() < 8) || (option_space.substr(0,7) != "vendor-")) {
return (0);
}
int64_t check;
try {
// text after "vendor-", supposedly numbers only
std::string x = option_space.substr(7);
check = boost::lexical_cast<int64_t>(x);
} catch (const boost::bad_lexical_cast &) {
return (0);
}
if ((check < 0) || (check > std::numeric_limits<uint32_t>::max())) {
return (0);
}
// value is small enough to fit
return (static_cast<uint32_t>(check));
}
void initOptionSpace(OptionDefContainer& defs,
const OptionDefParams* params,
size_t params_size) {
......
// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-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
......@@ -14,6 +14,7 @@
#include <util/staged_value.h>
#include <iostream>
#include <stdint.h>
#include <string>
namespace isc {
......@@ -321,6 +322,21 @@ public:
/// @brief Commits runtime option definitions.
static void commitRuntimeOptionDefs();
/// @brief Converts option space name to vendor id.
///
/// If the option space name is specified in the following format:
/// "vendor-X" where X is an uint32_t number, it is assumed to be
/// a vendor space and the uint32_t number is returned by this function.
/// If the option space name is invalid this method will return 0, which
/// is not a valid vendor-id, to signal an error.
///
/// @todo remove this function once when the conversion is dealt by the
/// appropriate functions returning options by option space names.
///
/// @param option_space Option space name.
/// @return vendor id.
static uint32_t optionSpaceToVendorId(const std::string& option_space);
private:
/// Initialize standard DHCPv4 option definitions.
......
......@@ -105,6 +105,7 @@ public:
/// for a specified identifier. This method may return multiple hosts
/// because a particular client may have reservations in multiple subnets.
///
/// @param identifier_type Identifier type.
/// @param identifier_begin Pointer to a begining of a buffer containing
/// an identifier.
/// @param identifier_len Identifier length.
......
......@@ -63,7 +63,7 @@ public:
/// @brief Adds new identifier type to a collection of identifiers
/// to be used by the server to search for host reservations.
///
/// @param identifier_type Name of the identifier to be added. It
/// @param identifier_name Name of the identifier to be added. It
/// must be one of the names supported by the @ref Host::getIdentifierType
/// function.
void addIdentifierType(const std::string& identifier_name);
......
// 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <dhcp/libdhcp++.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/cfg_option.h>
#include <boost/lexical_cast.hpp>
#include <dhcp/dhcp6.h>
#include <limits>
#include <string>
namespace isc {
......@@ -17,6 +16,7 @@ namespace dhcp {
bool
OptionDescriptor::equals(const OptionDescriptor& other) const {
return (persistent_ == other.persistent_ &&
formatted_value_ == other.formatted_value_ &&
option_->equals(other.option_));
}
......@@ -37,7 +37,12 @@ CfgOption::equals(const CfgOption& other) const {
void
CfgOption::add(const OptionPtr& option, const bool persistent,
const std::string& option_space) {
if (!option) {
add(OptionDescriptor(option, persistent), option_space);
}
void
CfgOption::add(const OptionDescriptor& desc, const std::string& option_space) {
if (!desc.option_) {
isc_throw(isc::BadValue, "option being configured must not be NULL");
} else if (!OptionSpace::validateName(option_space)) {
......@@ -45,13 +50,27 @@ CfgOption::add(const OptionPtr& option, const bool persistent,
<< option_space << "'");
}
const uint32_t vendor_id = optionSpaceToVendorId(option_space);
const uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
if (vendor_id) {
vendor_options_.addItem(OptionDescriptor(option, persistent),
vendor_id);
vendor_options_.addItem(desc, vendor_id);
} else {
options_.addItem(OptionDescriptor(option, persistent), option_space);
options_.addItem(desc, option_space);
}
}
std::list<std::string>
CfgOption::getVendorIdsSpaceNames() const {
std::list<uint32_t> ids = getVendorIds();
std::list<std::string> names;
for (std::list<uint32_t>::const_iterator id = ids.begin();
id != ids.end(); ++id) {
std::ostringstream s;
// Vendor space name is constructed as "vendor-XYZ" where XYZ is an
// uint32_t value, without leading zeros.
s << "vendor-" << *id;
names.push_back(s.str());
}
return (names);
}
void
......@@ -152,44 +171,5 @@ CfgOption::getAll(const uint32_t vendor_id) const {
return (vendor_options_.getItems(vendor_id));
}
uint32_t
CfgOption::optionSpaceToVendorId(const std::string& option_space) {
if (option_space.size() < 8) {
// 8 is a minimal length of "vendor-X" format
return (0);
}
if (option_space.substr(0,7) != "vendor-") {
return (0);
}
// text after "vendor-", supposedly numbers only
std::string x = option_space.substr(7);
int64_t check;
try {
check = boost::lexical_cast<int64_t>(x);
} catch (const boost::bad_lexical_cast &) {
/// @todo: Should we throw here?
// isc_throw(BadValue, "Failed to parse vendor-X value (" << x
// << ") as unsigned 32-bit integer.");
return (0);
}
if (check > std::numeric_limits<uint32_t>::max()) {
/// @todo: Should we throw here?
//isc_throw(BadValue, "Value " << x << "is too large"
// << " for unsigned 32-bit integer.");
return (0);
}
if (check < 0) {
/// @todo: Should we throw here?
// isc_throw(BadValue, "Value " << x << "is negative."
// << " Only 0 or larger are allowed for unsigned 32-bit integer.");
return (0);
}
// value is small enough to fit
return (static_cast<uint32_t>(check));
}
} // end of namespace isc::dhcp
} // end of namespace isc
// 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
......@@ -18,7 +18,6 @@
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <string>
#include <set>
#include <list>
namespace isc {
......@@ -31,24 +30,47 @@ namespace dhcp {
/// to DHCP client only on request (persistent = false) or always
/// (persistent = true).
struct OptionDescriptor {
/// Option instance.
/// @brief Option instance.
OptionPtr option_;
/// Persistent flag, if true option is always sent to the client,
/// if false option is sent to the client on request.
/// @brief Persistence flag.
///
/// If true, option is always sent to the client. If false, option is
/// sent to the client when requested using ORO or PRL option.
bool persistent_;
/// @brief Option value in textual (CSV) format.
///
/// This field is used to convey option value in human readable format,
/// the same as used to specify option value in the server configuration.
/// This value is optional and can be held in the host reservations
/// database instead of the binary format.
///
/// Note that this value is carried in the option descriptor, rather than
/// @c Option instance because it is a server specific value (same as
/// persistence flag).
///
/// An example of the formatted value is: "2001:db8:1::1, 23, some text"
/// for the option which carries IPv6 address, a number and a text.
std::string formatted_value_;
/// @brief Constructor.
///
/// @param opt option
/// @param persist if true option is always sent.
OptionDescriptor(const OptionPtr& opt, bool persist)
: option_(opt), persistent_(persist) {};
/// @param formatted_value option value in the textual format. Default
/// value is empty indicating that the value is not set.
OptionDescriptor(const OptionPtr& opt, bool persist,
const std::string& formatted_value = "")
: option_(opt), persistent_(persist),
formatted_value_(formatted_value) {};
/// @brief Constructor
///
/// @param persist if true option is always sent.
OptionDescriptor(bool persist)
: option_(OptionPtr()), persistent_(persist) {};
: option_(OptionPtr()), persistent_(persist),
formatted_value_() {};
/// @brief Checks if the one descriptor is equal to another.
///
......@@ -254,6 +276,16 @@ public:
void add(const OptionPtr& option, const bool persistent,
const std::string& option_space);
/// @brief A variant of the @ref CfgOption::add method which takes option
/// descriptor as an argument.
///
/// @param desc Option descriptor holding option instance and other
/// parameters pertaining to the option.
/// @param option_space Option space name.
///
/// @throw isc::BadValue if the option space is invalid.
void add(const OptionDescriptor& desc, const std::string& option_space);
/// @brief Merges this configuration to another configuration.
///
/// This method iterates over the configuration items held in this
......@@ -337,20 +369,30 @@ public:
return (*od_itr);
}
/// @brief Converts option space name to vendor id.
/// @brief Returns a list of configured option space names.
///
/// If the option space name is specified in the following format:
/// "vendor-X" where X is an uint32_t number, it is assumed to be
/// a vendor space and the uint32_t number is returned by this function.
/// If the option space name is invalid this method will return 0, which
/// is not a valid vendor-id, to signal an error.
/// The returned option space names exclude vendor option spaces,
/// such as "vendor-1234". These are returned by the
/// @ref getVendorIdsSpaceNames.
///
/// @return List comprising option space names.
std::list<std::string> getOptionSpaceNames() const {
return (options_.getOptionSpaceNames());
}
/// @brief Returns a list of all configured vendor identifiers.
std::list<uint32_t> getVendorIds() const {
return (vendor_options_.getOptionSpaceNames());
}
/// @brief Returns a list of option space names for configured vendor ids.
///
/// @todo remove this function once when the conversion is dealt by the
/// appropriate functions returning options by option space names.
/// For each vendor-id the option space name returned is constructed
/// as "vendor-XYZ" where XYZ is a @c uint32_t value without leading
/// zeros.
///
/// @param option_space Option space name.
/// @return vendor id.
static uint32_t optionSpaceToVendorId(const std::string& option_space);
/// @return List comprising option space names for vendor options.
std::list<std::string> getVendorIdsSpaceNames() const;
private:
......
......@@ -539,6 +539,15 @@ information from the MySQL hosts database.
The code has issued a rollback call. All outstanding transaction will
be rolled back and not committed to the database.
% DHCPSRV_MYSQL_START_TRANSACTION starting new MySQL transaction
A debug message issued whena new MySQL transaction is being started.
This message is typically not issued when inserting data into a
single table because the server doesn't explicitly start
transactions in this case. This message is issued when data is
inserted into multiple tables with multiple INSERT statements
and there may be a need to rollback the whole transaction if
any of these INSERT statements fail.
% DHCPSRV_MYSQL_UPDATE_ADDR4 updating IPv4 lease for address %1
A debug message issued when the server is attempting to update IPv4
lease from the MySQL database for the specified address.
......
......@@ -81,7 +81,7 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
dhcp6_client_classes_(dhcp6_client_classes), host_id_(0),
cfg_option4_(), cfg_option6_() {
cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
// Initialize host identifier.
setIdentifier(identifier, identifier_len, identifier_type);
......
......@@ -317,7 +317,7 @@ public:
/// @brief Returns host identifier in a textual form.
///
/// @return Identifier in the form of <type>=<value>.
/// @return Identifier in the form of type=value.
std::string getIdentifierAsText() const;
/// @brief Returns name of the identifier of a specified type.
......@@ -328,7 +328,7 @@ public:
/// @param type Identifier type.
/// @param value Pointer to a buffer holding identifier.
/// @param length Length of the identifier.
/// @return Identifier in the form of <type>=<value>.
/// @return Identifier in the form of type=value.
static std::string getIdentifierAsText(const IdentifierType& type,
const uint8_t* value,
const size_t length);
......
......@@ -116,6 +116,7 @@ public:
/// reservations from the primary data source are placed before the
/// reservations from the alternate source.
///
/// @param identifier_type Identifier type.
/// @param identifier_begin Pointer to a begining of a buffer containing
/// an identifier.
/// @param identifier_len Identifier length.
......
......@@ -31,6 +31,26 @@ const int MLM_MYSQL_FETCH_FAILURE = 1;
const int MYSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
MySqlTransaction::MySqlTransaction(MySqlConnection& conn)
: conn_(conn), committed_(false) {
conn_.startTransaction();
}
MySqlTransaction::~MySqlTransaction() {
// Rollback if the MySqlTransaction::commit wasn't explicitly
// called.
if (!committed_) {
conn_.rollback();
}
}
void
MySqlTransaction::commit() {
conn_.commit();
committed_ = true;
}
// Open the database using the parameters passed to the constructor.
void
......@@ -306,20 +326,35 @@ MySqlConnection::convertFromDatabaseTime(const MYSQL_TIME& expire,
cltt = mktime(&expire_tm) - valid_lifetime;
}