Commit 00b49298 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3628'

Conflicts:
	src/lib/dhcpsrv/Makefile.am
	src/lib/dhcpsrv/cfg_hosts.cc
	src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc
parents 07a01365 ad0f44ba
......@@ -14,7 +14,7 @@
#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <asiolink/io_error.h>
......
......@@ -18,7 +18,7 @@
#include <cc/data.h>
#include <d2/d2_asio.h>
#include <d2/d_cfg_mgr.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dns/tsig.h>
#include <exceptions/exceptions.h>
......
......@@ -16,7 +16,7 @@
#include <d2/d2_log.h>
#include <dhcp/libdhcp++.h>
#include <d2/d_cfg_mgr.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
......
......@@ -17,7 +17,7 @@
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <stdint.h>
#include <string>
......
......@@ -14,7 +14,7 @@
#include <config/ccsession.h>
#include <config/module_spec.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <d2/d_cfg_mgr.h>
#include <d_test_stubs.h>
......
......@@ -93,7 +93,7 @@
{ "item_name": "array",
"item_type": "boolean",
"item_optional": false,
"item_default": False
"item_default": false
},
{ "item_name": "record-types",
......@@ -147,7 +147,7 @@
{ "item_name": "csv-format",
"item_type": "boolean",
"item_optional": false,
"item_default": False
"item_default": false
},
{ "item_name": "space",
"item_type": "string",
......@@ -316,7 +316,7 @@
{ "item_name": "csv-format",
"item_type": "boolean",
"item_optional": false,
"item_default": False
"item_default": false
},
{ "item_name": "space",
"item_type": "string",
......@@ -324,6 +324,43 @@
"item_default": "dhcp4"
} ]
}
},
{ "item_name": "reservations",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "reservation",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{
"item_name": "hw-address",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "duid",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "hostname",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{
"item_name": "ip-address",
"item_type": "string",
"item_optional": false,
"item_default": "0.0.0.0"
} ]
}
} ]
}
},
......
......@@ -19,9 +19,11 @@
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp4/json_config_parser.h>
#include <dhcpsrv/dbaccess_parser.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/parsers/dbaccess_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
......@@ -144,6 +146,14 @@ public:
<< subnet->getPosition() << ")");
}
}
// Parse Host Reservations for this subnet if any.
ConstElementPtr reservations = subnet->get("reservations");
if (reservations) {
ParserPtr parser(new HostReservationsListParser<
HostReservationParser4>(subnet_->getID()));
parser->build(reservations);
}
}
/// @brief Commits subnet configuration.
......
......@@ -13,8 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <cc/data.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <stdint.h>
#include <string>
......
......@@ -28,6 +28,7 @@
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/cfg_hosts.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/testutils/config_result_check.h>
#include <hooks/hooks_manager.h>
......@@ -3237,4 +3238,209 @@ TEST_F(Dhcp4ParserTest, classifySubnets) {
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
}
// This test verifies that the host reservations can be specified for
// respective IPv4 subnets.
TEST_F(Dhcp4ParserTest, reservations) {
ConstElementPtr x;
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
" { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\", "
" \"id\": 123,"
" \"reservations\": ["
" ]"
" },"
" {"
" \"reservations\": ["
" {"
" \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
" \"ip-address\": \"192.0.3.112\","
" \"hostname\": \"\""
" },"
" {"
" \"hw-address\": \"01:02:03:04:05:06\","
" \"ip-address\": \"192.0.3.120\","
" \"hostname\": \"\""
" }"
" ],"
" \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
" \"subnet\": \"192.0.3.0/24\", "
" \"id\": 234"
" },"
" {"
" \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
" \"subnet\": \"192.0.4.0/24\","
" \"id\": 542,"
" \"reservations\": ["
" {"
" \"duid\": \"0A:09:08:07:06:05:04:03:02:01\","
" \"ip-address\": \"192.0.4.101\","
" \"hostname\": \"\""
" },"
" {"
" \"hw-address\": \"06:05:04:03:02:01\","
" \"ip-address\": \"192.0.4.102\","
" \"hostname\": \"\""
" }"
" ]"
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
// Make sure all subnets have been successfully configured. There is no
// need to sanity check the subnet properties because it should have
// been already tested by other tests.
const Subnet4Collection* subnets =
CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->getAll();
ASSERT_TRUE(subnets);
ASSERT_EQ(3, subnets->size());
// Hosts configuration must be available.
CfgHostsPtr hosts_cfg = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_TRUE(hosts_cfg);
// Let's create an object holding hardware address of the host having
// a reservation in the subnet having id of 234. For simlicity the
// address is a collection of numbers from 1 to 6.
std::vector<uint8_t> hwaddr_vec;
for (int i = 1; i < 7; ++i) {
hwaddr_vec.push_back(static_cast<uint8_t>(i));
}
HWAddrPtr hwaddr(new HWAddr(hwaddr_vec, HTYPE_ETHER));
// Retrieve the reservation and sanity check the address reserved.
ConstHostPtr host = hosts_cfg->get4(234, hwaddr);
ASSERT_TRUE(host);
EXPECT_EQ("192.0.3.120", host->getIPv4Reservation().toText());
// This reservation should be solely assigned to the subnet 234,
// and not to other two.
EXPECT_FALSE(hosts_cfg->get4(123, hwaddr));
EXPECT_FALSE(hosts_cfg->get4(542, hwaddr));
// Do the same test for the DUID based reservation.
std::vector<uint8_t> duid_vec;
for (int i = 1; i < 0xb; ++i) {
duid_vec.push_back(static_cast<uint8_t>(i));
}
DuidPtr duid(new DUID(duid_vec));
host = hosts_cfg->get4(234, HWAddrPtr(), duid);
ASSERT_TRUE(host);
EXPECT_EQ("192.0.3.112", host->getIPv4Reservation().toText());
EXPECT_FALSE(hosts_cfg->get4(123, HWAddrPtr(), duid));
EXPECT_FALSE(hosts_cfg->get4(542, HWAddrPtr(), duid));
// The HW address used for one of the reservations in the subnet 542
// consists of numbers from 6 to 1. So, let's just reverse the order
// of the address from the previous test.
hwaddr->hwaddr_.assign(hwaddr_vec.rbegin(), hwaddr_vec.rend());
host = hosts_cfg->get4(542, hwaddr);
EXPECT_TRUE(host);
EXPECT_EQ("192.0.4.102", host->getIPv4Reservation().toText());
// This reservation must not belong to other subnets.
EXPECT_FALSE(hosts_cfg->get4(123, hwaddr));
EXPECT_FALSE(hosts_cfg->get4(234, hwaddr));
// Repeat the test for the DUID based reservation in this subnet.
duid.reset(new DUID(std::vector<uint8_t>(duid_vec.rbegin(),
duid_vec.rend())));
host = hosts_cfg->get4(542, HWAddrPtr(), duid);
ASSERT_TRUE(host);
EXPECT_EQ("192.0.4.101", host->getIPv4Reservation().toText());
EXPECT_FALSE(hosts_cfg->get4(123, HWAddrPtr(), duid));
EXPECT_FALSE(hosts_cfg->get4(234, HWAddrPtr(), duid));
}
// This test verfies that the bogus host reservation would trigger a
// server configuration error.
TEST_F(Dhcp4ParserTest, reservationBogus) {
// Case 1: misspelled hw-address parameter.
ConstElementPtr x;
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
" { "
" \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
" \"subnet\": \"192.0.4.0/24\","
" \"id\": 542,"
" \"reservations\": ["
" {"
" \"hw-addre\": \"06:05:04:03:02:01\","
" \"ip-address\": \"192.0.4.102\","
" \"hostname\": \"\""
" }"
" ]"
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
CfgMgr::instance().clear();
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 1);
// Case 2: DUID and HW Address both specified.
config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
" { "
" \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
" \"subnet\": \"192.0.4.0/24\","
" \"id\": 542,"
" \"reservations\": ["
" {"
" \"duid\": \"01:02:03:04:05:06\","
" \"hw-address\": \"06:05:04:03:02:01\","
" \"ip-address\": \"192.0.4.102\","
" \"hostname\": \"\""
" }"
" ]"
" } ],"
"\"valid-lifetime\": 4000 }";
json = Element::fromJSON(config);
// Remove existing configuration, if any.
CfgMgr::instance().clear();
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 1);
// Case 3: Neither ip address nor hostname specified.
config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
" { "
" \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
" \"subnet\": \"192.0.4.0/24\","
" \"id\": 542,"
" \"reservations\": ["
" {"
" \"hw-address\": \"06:05:04:03:02:01\""
" }"
" ]"
" } ],"
"\"valid-lifetime\": 4000 }";
json = Element::fromJSON(config);
// Remove existing configuration, if any.
CfgMgr::instance().clear();
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 1);
}
}
......@@ -87,7 +87,7 @@
{ "item_name": "array",
"item_type": "boolean",
"item_optional": false,
"item_default": False
"item_default": false
},
{ "item_name": "record-types",
......@@ -141,7 +141,7 @@
{ "item_name": "csv-format",
"item_type": "boolean",
"item_optional": false,
"item_default": False
"item_default": false
},
{ "item_name": "space",
"item_type": "string",
......@@ -354,7 +354,7 @@
{ "item_name": "csv-format",
"item_type": "boolean",
"item_optional": false,
"item_default": False
"item_default": false
},
{ "item_name": "space",
"item_type": "string",
......@@ -362,6 +362,63 @@
"item_default": "dhcp6"
} ]
}
},
{ "item_name": "reservations",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "reservation",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{
"item_name": "hw-address",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "duid",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "hostname",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{
"item_name": "ip-addresses",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "ip-address-reservation",
"item_type": "string",
"item_optional": false,
"item_default": ""
}
},
{
"item_name": "prefixes",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "prefix-reservation",
"item_type": "string",
"item_optional": false,
"item_default": ""
}
} ]
}
} ]
}
},
......
......@@ -21,12 +21,14 @@
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dbaccess_parser.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/triplet.h>
#include <dhcpsrv/parsers/dbaccess_parser.h>
#include <dhcpsrv/parsers/dhcp_config_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
#include <log/logger_support.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
......@@ -350,6 +352,13 @@ public:
<< subnet->getPosition() << ")");
}
// Parse Host Reservations for this subnet if any.
ConstElementPtr reservations = subnet->get("reservations");
if (reservations) {
ParserPtr parser(new HostReservationsListParser<
HostReservationParser6>(subnet_->getID()));
parser->build(reservations);
}
}
}
......
......@@ -19,8 +19,8 @@
/// DHCPv4 and DHCPv6. They should be merged. See ticket #2355.
#include <cc/data.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <dhcpsrv/dhcp_parsers.h>
#include <string>
......
......@@ -15,8 +15,8 @@
#include <config.h>
#include <asiolink/asiolink.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/parsers/dhcp_config_parser.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
......
......@@ -25,6 +25,7 @@
#include <dhcp6/dhcp6_srv.h>
#include <dhcpsrv/addr_utilities.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/cfg_hosts.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_selector.h>
#include <dhcpsrv/testutils/config_result_check.h>
......@@ -3384,4 +3385,246 @@ TEST_F(Dhcp6ParserTest, invalidD2ClientConfig) {
ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
}
/// @brief Checks if the reservation is in the range of reservations.
///
/// @param resrv Reservation to be searched for.
/// @param range Range of reservations returned by the @c Host object
/// in which the reservation will be searched.
bool reservationExists(const IPv6Resrv& resrv, const IPv6ResrvRange& range) {
for (IPv6ResrvIterator it = range.first; it != range.second;
++it) {
if (resrv == it->second) {
return (true);
}
}
return (false);
}
// This test verifies that the host reservations can be specified for
// respective IPv6 subnets.
TEST_F(Dhcp6ParserTest, reservations) {
ConstElementPtr x;
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ "
" { "
" \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
" \"subnet\": \"2001:db8:1::/64\", "
" \"id\": 123,"
" \"reservations\": ["
" ]"
" },"
" {"
" \"reservations\": ["
" {"
" \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
" \"ip-addresses\": [ \"2001:db8:2::1234\" ],"
" \"hostname\": \"\""
" },"
" {"
" \"hw-address\": \"01:02:03:04:05:06\","
" \"ip-addresses\": [ \"2001:db8:2::abcd\" ],"
" \"hostname\": \"\""
" }"
" ],"
" \"pools\": [ ],"
" \"subnet\": \"2001:db8:2::/64\", "
" \"id\": 234"
" },"
" {"
" \"pools\": [ ],"
" \"subnet\": \"2001:db8:3::/64\", "
" \"id\": 542,"
" \"reservations\": ["
" {"
" \"duid\": \"0A:09:08:07:06:05:04:03:02:01\","
" \"prefixes\": [ \"2001:db8:3:2::/96\" ],"
" \"hostname\": \"\""
" },"
" {"
" \"hw-address\": \"06:05:04:03:02:01\","
" \"prefixes\": [ \"2001:db8:3:1::/96\" ],"
" \"hostname\": \"\""
" }"
" ]"
" } "
"], "
"\"preferred-lifetime\": 3000,"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
checkResult(x, 0);
// Make sure all subnets have been successfully configured. There is no
// need to sanity check the subnet properties because it should have
// been already tested by other tests.
const Subnet6Collection* subnets =
CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
ASSERT_TRUE(subnets);