Commit f1b3b3c7 authored by Thomas Markwalder's avatar Thomas Markwalder

[5704] host backends and kea-dhcp4/6 support global HR storage

- Added constants for special SubnetIDs:
    SUBNET_ID_GLOBAL, SUBNET_ID_MAX, SUBNET_ID_UNUSED

- Modified code throughout to use these constants, rather than hard-coded
  values.   Note, MySQL and PostgreSQL host backends convert from NULL to
  UNUSED and back.

- kea-dhcp4/6 servers will now parse a "reservations" element at the global
  level.

src/lib/dhcpsrv/subnet_id.h
    Added constants SubnetID SUBNET_ID_GLOBAL, SUBNET_ID_MAX, SUBNET_ID_UNUSED

src/bin/dhcp4/dhcp4_lexer.ll
src/bin/dhcp4/dhcp4_parser.yy
src/bin/dhcp4/json_config_parser.cc
    kea-dhcp4 parsing now handles reservations as a global element

src/bin/dhcp4/tests/config_parser_unittest.cc
    TEST_F(Dhcp4ParserTest, globalReservations) - new test to
    verify global HR parsing

src/bin/dhcp4/tests/dora_unittest.cc
src/lib/dhcpsrv/cfg_hosts.cc
src/lib/dhcpsrv/host.cc
src/lib/dhcpsrv/host_mgr.cc
src/lib/dhcpsrv/mysql_host_data_source.cc
src/lib/dhcpsrv/parsers/host_reservation_parser.cc
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine_utils.cc
src/lib/dhcpsrv/tests/host_mgr_unittest.cc
src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc
src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc
src/lib/dhcpsrv/tests/host_unittest.cc
    Replaced SubnetID 0 with SUBNET_ID_UNUSED

src/lib/dhcpsrv/srv_config.cc
    SrvConfig::toElement() - added global reservations output

src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc
    TEST_F(CfgHostsTest, globalSubnetIDs)
    TEST_F(CfgHostsTest, unusedSubnetIDs) - new tests

src/lib/dhcpsrv/tests/host_unittest.cc
    Replaced SubnetID 0 with SUBNET_ID_UNUSED
    TEST_F(HostTest, toText)  - updated to verify global ID output

src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
    TEST_F(MySqlHostDataSourceTest, globalSubnetId4)
    TEST_F(MySqlHostDataSourceTest, globalSubnetId6) - new tests

src/lib/dhcpsrv/tests/srv_config_unittest.cc
    TEST_F(SrvConfigTest, unparseHR) - added global HRs

src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.*
    GenericHostDataSourceTest::testGlobalSubnetId4()
    GenericHostDataSourceTest::testGlobalSubnetId6()

src/bin/dhcp6/dhcp6_lexer.ll
src/bin/dhcp6/dhcp6_parser.yy
src/bin/dhcp6/json_config_parser.cc
    kea-dhcp6 now parses reservations as a global element

src/bin/dhcp6/tests/config_parser_unittest.cc
    TEST_F(Dhcp6ParserTest, globalReservations) - new test
parent 649392e4
This diff is collapsed.
......@@ -938,6 +938,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
\"reservations\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
return isc::dhcp::Dhcp4Parser::make_RESERVATIONS(driver.loc_);
default:
......
This diff is collapsed.
......@@ -1481,7 +1481,7 @@ namespace isc { namespace dhcp {
enum
{
yyeof_ = 0,
yylast_ = 970, ///< Last index in yytable_.
yylast_ = 964, ///< Last index in yytable_.
yynnts_ = 363, ///< Number of nonterminal symbols.
yyfinal_ = 28, ///< Termination state number.
yyterror_ = 1,
......
......@@ -453,6 +453,7 @@ global_param: valid_lifetime
| user_context
| comment
| sanity_checks
| reservations
| unknown_map_entry
;
......
......@@ -477,6 +477,17 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
continue;
}
if (config_pair.first == "reservations") {
HostCollection hosts;
HostReservationsListParser<HostReservationParser4> parser;
parser.parse(SUBNET_ID_GLOBAL, config_pair.second, hosts);
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
srv_cfg->getCfgHosts()->add(*h);
}
continue;
}
// Timers are not used in the global scope. Their values are derived
// to specific subnets (see SimpleParser6::deriveParameters).
// decline-probation-period, dhcp4o6-port, echo-client-id,
......
......@@ -6052,7 +6052,7 @@ TEST_F(Dhcp4ParserTest, comments) {
EXPECT_EQ("aa:bb:cc:dd:ee:ff", host->getHWAddress()->toText(false));
EXPECT_FALSE(host->getDuid());
EXPECT_EQ(100, host->getIPv4SubnetID());
EXPECT_EQ(0, host->getIPv6SubnetID());
EXPECT_EQ(SUBNET_ID_UNUSED, host->getIPv6SubnetID());
EXPECT_EQ("foo.example.com", host->getHostname());
// Check host user context.
......@@ -6103,4 +6103,140 @@ TEST_F(Dhcp4ParserTest, comments) {
#endif
}
// This test verifies that the global host reservations can be specified
TEST_F(Dhcp4ParserTest, globalReservations) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, \n"
"\"renew-timer\": 1000, \n"
"\"reservations\": [\n"
" {\n"
" \"duid\": \"01:02:03:04:05:06:07:08:09:0A\",\n"
" \"ip-address\": \"192.0.200.1\",\n"
" \"hostname\": \"global1\",\n"
" \"option-data\": [\n"
" {\n"
" \"name\": \"name-servers\",\n"
" \"data\": \"192.0.3.15\"\n"
" },\n"
" {\n"
" \"name\": \"default-ip-ttl\",\n"
" \"data\": \"32\"\n"
" }\n"
" ]\n"
" },\n"
" {\n"
" \"hw-address\": \"01:02:03:04:05:06\",\n"
" \"hostname\": \"global2\",\n"
" \"option-data\": [\n"
" {\n"
" \"name\": \"name-servers\",\n"
" \"data\": \"192.0.3.95\"\n"
" },\n"
" {\n"
" \"name\": \"default-ip-ttl\",\n"
" \"data\": \"11\"\n"
" }\n"
" ]\n"
" }],\n"
"\"subnet4\": [ \n"
" { \n"
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],\n"
" \"subnet\": \"192.0.2.0/24\", \n"
" \"id\": 123,\n"
" \"reservations\": [\n"
" ]\n"
" },\n"
" {\n"
" \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],\n"
" \"subnet\": \"192.0.4.0/24\",\n"
" \"id\": 542\n"
" } ],\n"
"\"valid-lifetime\": 4000"
"}\n";
ConstElementPtr json;
try {
(json = parseDHCP4(config));
} catch (const std::exception& ex) {
std::cout << "TKM: " << ex.what() << std::endl;
}
ASSERT_NO_THROW(json = parseDHCP4(config));
extractConfig(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(2, subnets->size());
// Hosts configuration must be available.
CfgHostsPtr hosts_cfg = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_TRUE(hosts_cfg);
// Let's create a hardware address of the host named "global2"
std::vector<uint8_t> hwaddr;
for (unsigned int i = 1; i < 7; ++i) {
hwaddr.push_back(static_cast<uint8_t>(i));
}
// Retrieve the global reservation and sanity check the hostname reserved.
ConstHostPtr host = hosts_cfg->get4(SUBNET_ID_GLOBAL, Host::IDENT_HWADDR,
&hwaddr[0], hwaddr.size());
ASSERT_TRUE(host);
EXPECT_EQ("global2", host->getHostname());
// Check that options are assigned correctly.
Option4AddrLstPtr opt_dns =
retrieveOption<Option4AddrLstPtr>(*host, DHO_NAME_SERVERS);
ASSERT_TRUE(opt_dns);
Option4AddrLst::AddressContainer dns_addrs = opt_dns->getAddresses();
ASSERT_EQ(1, dns_addrs.size());
EXPECT_EQ("192.0.3.95", dns_addrs[0].toText());
OptionUint8Ptr opt_ttl =
retrieveOption<OptionUint8Ptr>(*host, DHO_DEFAULT_IP_TTL);
ASSERT_TRUE(opt_ttl);
EXPECT_EQ(11, static_cast<int>(opt_ttl->getValue()));
// This reservation should be global solely and not assigned to
// either subnet
EXPECT_FALSE(hosts_cfg->get4(123, Host::IDENT_HWADDR,
&hwaddr[0], hwaddr.size()));
EXPECT_FALSE(hosts_cfg->get4(542, Host::IDENT_HWADDR,
&hwaddr[0], hwaddr.size()));
// Do the same test for the DUID based reservation.
std::vector<uint8_t> duid;
for (unsigned int i = 1; i < 0xb; ++i) {
duid.push_back(static_cast<uint8_t>(i));
}
// Retrieve the global reservation and sanity check the hostname reserved.
host = hosts_cfg->get4(SUBNET_ID_GLOBAL, Host::IDENT_DUID, &duid[0], duid.size());
ASSERT_TRUE(host);
EXPECT_EQ("global1", host->getHostname());
// Check that options are assigned correctly.
opt_dns = retrieveOption<Option4AddrLstPtr>(*host, DHO_NAME_SERVERS);
ASSERT_TRUE(opt_dns);
dns_addrs = opt_dns->getAddresses();
ASSERT_EQ(1, dns_addrs.size());
EXPECT_EQ("192.0.3.15", dns_addrs[0].toText());
opt_ttl = retrieveOption<OptionUint8Ptr>(*host, DHO_DEFAULT_IP_TTL);
ASSERT_TRUE(opt_ttl);
EXPECT_EQ(32, static_cast<int>(opt_ttl->getValue()));
// This reservation should be global solely and not assigned to
// either subnet
EXPECT_FALSE(hosts_cfg->get4(123, Host::IDENT_DUID, &duid[0], duid.size()));
EXPECT_FALSE(hosts_cfg->get4(542, Host::IDENT_DUID, &duid[0], duid.size()));
}
}
......@@ -1361,7 +1361,7 @@ TEST_F(DORATest, reservationsWithConflicts) {
HostPtr host(new Host(&client.getHWAddress()->hwaddr_[0],
client.getHWAddress()->hwaddr_.size(),
Host::IDENT_HWADDR, SubnetID(1),
SubnetID(0), IOAddress("10.0.0.9")));
SUBNET_ID_UNUSED, IOAddress("10.0.0.9")));
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
......@@ -1440,7 +1440,7 @@ TEST_F(DORATest, reservationsWithConflicts) {
host.reset(new Host(&clientB.getHWAddress()->hwaddr_[0],
clientB.getHWAddress()->hwaddr_.size(),
Host::IDENT_HWADDR, SubnetID(1),
SubnetID(0), in_pool_addr));
SUBNET_ID_UNUSED, in_pool_addr));
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
......
This diff is collapsed.
......@@ -1222,6 +1222,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
\"reservations\" {
switch(driver.ctx_) {
case isc::dhcp::Parser6Context::DHCP6:
case isc::dhcp::Parser6Context::SUBNET6:
return isc::dhcp::Dhcp6Parser::make_RESERVATIONS(driver.loc_);
default:
......
This diff is collapsed.
......@@ -1500,7 +1500,7 @@ namespace isc { namespace dhcp {
enum
{
yyeof_ = 0,
yylast_ = 991, ///< Last index in yytable_.
yylast_ = 987, ///< Last index in yytable_.
yynnts_ = 377, ///< Number of nonterminal symbols.
yyfinal_ = 30, ///< Termination state number.
yyterror_ = 1,
......
......@@ -454,6 +454,7 @@ global_param: preferred_lifetime
| user_context
| comment
| sanity_checks
| reservations
| unknown_map_entry
;
......
......@@ -589,6 +589,17 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
continue;
}
if (config_pair.first == "reservations") {
HostCollection hosts;
HostReservationsListParser<HostReservationParser6> parser;
parser.parse(SUBNET_ID_GLOBAL, config_pair.second, hosts);
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
srv_config->getCfgHosts()->add(*h);
}
continue;
}
// Timers are not used in the global scope. Their values are derived
// to specific subnets (see SimpleParser6::deriveParameters).
// decline-probation-period, dhcp4o6-port and user-context
......
......@@ -6647,7 +6647,7 @@ TEST_F(Dhcp6ParserTest, comments) {
EXPECT_EQ(Host::IDENT_HWADDR, host->getIdentifierType());
EXPECT_EQ("aa:bb:cc:dd:ee:ff", host->getHWAddress()->toText(false));
EXPECT_FALSE(host->getDuid());
EXPECT_EQ(0, host->getIPv4SubnetID());
EXPECT_EQ(SUBNET_ID_UNUSED, host->getIPv4SubnetID());
EXPECT_EQ(100, host->getIPv6SubnetID());
EXPECT_EQ("foo.example.com", host->getHostname());
......@@ -6689,4 +6689,143 @@ TEST_F(Dhcp6ParserTest, comments) {
EXPECT_EQ("\"No dynamic DNS\"", ctx_d2->get("comment")->str());
}
// This test verifies that the global host reservations can be specified.
TEST_F(Dhcp6ParserTest, globalReservations) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + ",\n"
"\"rebind-timer\": 2000, \n"
"\"renew-timer\": 1000, \n"
"\"reservations\": [\n"
" {\n"
" \"duid\": \"01:02:03:04:05:06:07:08:09:0A\",\n"
" \"ip-addresses\": [ \"2001:db8:2::1234\" ],\n"
" \"hostname\": \"\",\n"
" \"option-data\": [\n"
" {\n"
" \"name\": \"dns-servers\",\n"
" \"data\": \"2001:db8:2::1111\"\n"
" },\n"
" {\n"
" \"name\": \"preference\",\n"
" \"data\": \"11\"\n"
" }\n"
" ]\n"
" },\n"
" {\n"
" \"hw-address\": \"01:02:03:04:05:06\",\n"
" \"ip-addresses\": [ \"2001:db8:2::abcd\" ],\n"
" \"hostname\": \"\",\n"
" \"option-data\": [\n"
" {\n"
" \"name\": \"dns-servers\",\n"
" \"data\": \"2001:db8:2::abbc\"\n"
" },\n"
" {\n"
" \"name\": \"preference\",\n"
" \"data\": \"25\"\n"
" }\n"
" ]\n"
" }\n"
"],\n"
"\"subnet6\": [ \n"
" { \n"
" \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],\n"
" \"subnet\": \"2001:db8:1::/64\", \n"
" \"id\": 123,\n"
" \"reservations\": [\n"
" ]\n"
" },\n"
" {\n"
" \"pools\": [ ],\n"
" \"subnet\": \"2001:db8:2::/64\", \n"
" \"id\": 234\n"
" },\n"
" {\n"
" \"pools\": [ ],\n"
" \"subnet\": \"2001:db8:3::/64\", \n"
" \"id\": 542\n"
" }\n"
"],\n"
"\"preferred-lifetime\": 3000,\n"
"\"valid-lifetime\": 4000 }\n";
ConstElementPtr json;
(json = parseDHCP6(config));
ASSERT_NO_THROW(json = parseDHCP6(config));
extractConfig(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);
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 simplicity the
// address is a collection of numbers from 1 to 6.
std::vector<uint8_t> hwaddr;
for (unsigned int i = 1; i < 7; ++i) {
hwaddr.push_back(static_cast<uint8_t>(i));
}
// Retrieve the reservation and sanity check the address reserved.
ConstHostPtr host = hosts_cfg->get6(SUBNET_ID_GLOBAL, Host::IDENT_HWADDR,
&hwaddr[0], hwaddr.size());
ASSERT_TRUE(host);
IPv6ResrvRange resrv = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
ASSERT_EQ(1, std::distance(resrv.first, resrv.second));
EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_NA,
IOAddress("2001:db8:2::abcd")),
resrv));
// This reservation should be solely assigned to the subnet 234,
// and not to other two.
EXPECT_FALSE(hosts_cfg->get6(123, Host::IDENT_HWADDR,
&hwaddr[0], hwaddr.size()));
EXPECT_FALSE(hosts_cfg->get6(542, Host::IDENT_HWADDR,
&hwaddr[0], hwaddr.size()));
// Check that options are assigned correctly.
Option6AddrLstPtr opt_dns =
retrieveOption<Option6AddrLstPtr>(*host, D6O_NAME_SERVERS);
ASSERT_TRUE(opt_dns);
Option6AddrLst::AddressContainer dns_addrs = opt_dns->getAddresses();
ASSERT_EQ(1, dns_addrs.size());
EXPECT_EQ("2001:db8:2::abbc", dns_addrs[0].toText());
OptionUint8Ptr opt_prf =
retrieveOption<OptionUint8Ptr>(*host, D6O_PREFERENCE);
ASSERT_TRUE(opt_prf);
EXPECT_EQ(25, static_cast<int>(opt_prf->getValue()));
// Do the same test for the DUID based reservation.
std::vector<uint8_t> duid;
for (unsigned int i = 1; i < 0xb; ++i) {
duid.push_back(static_cast<uint8_t>(i));
}
host = hosts_cfg->get6(SUBNET_ID_GLOBAL, Host::IDENT_DUID, &duid[0], duid.size());
ASSERT_TRUE(host);
resrv = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
ASSERT_EQ(1, std::distance(resrv.first, resrv.second));
EXPECT_TRUE(reservationExists(IPv6Resrv(IPv6Resrv::TYPE_NA,
IOAddress("2001:db8:2::1234")),
resrv));
EXPECT_FALSE(hosts_cfg->get6(123, Host::IDENT_DUID, &duid[0], duid.size()));
EXPECT_FALSE(hosts_cfg->get6(542, Host::IDENT_DUID, &duid[0], duid.size()));
// Check that options are assigned correctly.
opt_dns = retrieveOption<Option6AddrLstPtr>(*host, D6O_NAME_SERVERS);
ASSERT_TRUE(opt_dns);
dns_addrs = opt_dns->getAddresses();
ASSERT_EQ(1, dns_addrs.size());
EXPECT_EQ("2001:db8:2::1111", dns_addrs[0].toText());
opt_prf = retrieveOption<OptionUint8Ptr>(*host, D6O_PREFERENCE);
ASSERT_TRUE(opt_prf);
EXPECT_EQ(11, static_cast<int>(opt_prf->getValue()));
}
};
......@@ -446,8 +446,9 @@ CfgHosts::add(const HostPtr& host) {
" is added to the configuration");
}
// At least one subnet ID must be non-zero
if (host->getIPv4SubnetID() == 0 && host->getIPv6SubnetID() == 0) {
// At least one subnet ID must be used
if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED &&
host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
isc_throw(BadValue, "must not use both IPv4 and IPv6 subnet ids of"
" 0 when adding new host reservation");
}
......@@ -489,7 +490,7 @@ CfgHosts::add4(const HostPtr& host) {
}
// Check for duplicates for the specified IPv4 subnet.
if (host->getIPv4SubnetID() > 0) {
if (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) {
if (hwaddr && !hwaddr->hwaddr_.empty() &&
get4(host->getIPv4SubnetID(), Host::IDENT_HWADDR,
&hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) {
......@@ -507,7 +508,7 @@ CfgHosts::add4(const HostPtr& host) {
<< "' as this host has already been added");
}
// Check for duplicates for the specified IPv6 subnet.
} else if (host->getIPv6SubnetID()) {
} else if (host->getIPv6SubnetID() != SUBNET_ID_UNUSED) {
if (duid && !duid->getDuid().empty() &&
get6(host->getIPv6SubnetID(), Host::IDENT_DUID,
&duid->getDuid()[0], duid->getDuid().size())) {
......@@ -528,7 +529,7 @@ CfgHosts::add4(const HostPtr& host) {
// Check if the address is already reserved for the specified IPv4 subnet.
if (!host->getIPv4Reservation().isV4Zero() &&
(host->getIPv4SubnetID() > 0) &&
(host->getIPv4SubnetID() != SUBNET_ID_UNUSED) &&
get4(host->getIPv4SubnetID(), host->getIPv4Reservation())) {
isc_throw(ReservedAddress, "failed to add new host using the HW"
" address '" << (hwaddr ? hwaddr->toText(false) : "(null)")
......@@ -540,7 +541,7 @@ CfgHosts::add4(const HostPtr& host) {
// Check if the (identifier type, identifier) tuple is already used.
const std::vector<uint8_t>& id = host->getIdentifier();
if ((host->getIPv4SubnetID() > 0) && !id.empty()) {
if ((host->getIPv4SubnetID() != SUBNET_ID_UNUSED) && !id.empty()) {
if (get4(host->getIPv4SubnetID(), host->getIdentifierType(), &id[0],
id.size())) {
isc_throw(DuplicateHost, "failed to add duplicate IPv4 host using identifier: "
......@@ -556,7 +557,7 @@ CfgHosts::add4(const HostPtr& host) {
void
CfgHosts::add6(const HostPtr& host) {
if (host->getIPv6SubnetID() == 0) {
if (host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
// This is IPv4-only host. No need to add it to v6 tables.
return;
}
......
......@@ -595,13 +595,13 @@ Host::toText() const {
// Add HW address or DUID.
s << getIdentifierAsText();
// Add IPv4 subnet id if exists (non-zero).
if (ipv4_subnet_id_) {
// Add IPv4 subnet id if exists.
if (ipv4_subnet_id_ != SUBNET_ID_UNUSED) {
s << " ipv4_subnet_id=" << ipv4_subnet_id_;
}
// Add IPv6 subnet id if exists (non-zero).
if (ipv6_subnet_id_) {
// Add IPv6 subnet id if exists.
if (ipv6_subnet_id_ != SUBNET_ID_UNUSED) {
s << " ipv6_subnet_id=" << ipv6_subnet_id_;
}
......
......@@ -181,7 +181,7 @@ HostMgr::get4(const SubnetID& subnet_id,
if (host && host->getNegative()) {
return (ConstHostPtr());
} else if (!host && negative_caching_) {
cacheNegative(subnet_id, SubnetID(0),
cacheNegative(subnet_id, SubnetID(SUBNET_ID_UNUSED),
identifier_type, identifier_begin, identifier_len);
}
return (host);
......@@ -295,7 +295,7 @@ HostMgr::get6(const SubnetID& subnet_id,
if (host && host->getNegative()) {
return (ConstHostPtr());
} else if (!host && negative_caching_) {
cacheNegative(SubnetID(0), subnet_id,
cacheNegative(SubnetID(SUBNET_ID_UNUSED), subnet_id,
identifier_type, identifier_begin, identifier_len);
}
return (host);
......
......@@ -129,7 +129,8 @@ public:
bind_(columns_num_), columns_(columns_num_),
error_(columns_num_, MLM_FALSE), host_id_(0),
dhcp_identifier_length_(0), dhcp_identifier_type_(0),
dhcp4_subnet_id_(0), dhcp6_subnet_id_(0), ipv4_address_(0),
dhcp4_subnet_id_(SUBNET_ID_UNUSED),
dhcp6_subnet_id_(SUBNET_ID_UNUSED), ipv4_address_(0),
hostname_length_(0), dhcp4_client_classes_length_(0),
dhcp6_client_classes_length_(0),
user_context_length_(0),
......@@ -312,7 +313,7 @@ public:
// Can't take an address of intermediate object, so let's store it
// in dhcp4_subnet_id_
dhcp4_subnet_id_ = host->getIPv4SubnetID();
dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == 0 ? MLM_TRUE : MLM_FALSE;
dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
bind_[3].buffer_type = MYSQL_TYPE_LONG;
bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
bind_[3].is_unsigned = MLM_TRUE;
......@@ -322,7 +323,7 @@ public:
// Can't take an address of intermediate object, so let's store it
// in dhcp6_subnet_id_
dhcp6_subnet_id_ = host->getIPv6SubnetID();
dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == 0 ? MLM_TRUE : MLM_FALSE;
dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
bind_[4].buffer_type = MYSQL_TYPE_LONG;
bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
bind_[4].is_unsigned = MLM_TRUE;
......@@ -571,14 +572,14 @@ public:
// Set DHCPv4 subnet ID to the value returned. If NULL returned,
// set to 0.
SubnetID ipv4_subnet_id(0);
SubnetID ipv4_subnet_id(SUBNET_ID_UNUSED);
if (dhcp4_subnet_id_null_ == MLM_FALSE) {
ipv4_subnet_id = static_cast<SubnetID>(dhcp4_subnet_id_);
}
// Set DHCPv6 subnet ID to the value returned. If NULL returned,
// set to 0.
SubnetID ipv6_subnet_id(0);
SubnetID ipv6_subnet_id(SUBNET_ID_UNUSED);
if (dhcp6_subnet_id_null_ == MLM_FALSE) {
ipv6_subnet_id = static_cast<SubnetID>(dhcp6_subnet_id_);
}
......@@ -1742,7 +1743,7 @@ public:
: type_(0), value_len_(0), formatted_value_len_(0), space_(),
space_len_(0), persistent_(false), user_context_(),
user_context_len_(0), client_class_(), client_class_len_(0),
subnet_id_(0), host_id_(0), option_() {
subnet_id_(SUBNET_ID_UNUSED), host_id_(0), option_() {
BOOST_STATIC_ASSERT(9 < OPTION_COLUMNS);
}
......
......@@ -158,8 +158,8 @@ HostReservationParser::parseInternal(const SubnetID&,
}
// Create a host object from the basic parameters we already parsed.
host.reset(new Host(identifier, identifier_name, SubnetID(0),
SubnetID(0), IOAddress("0.0.0.0"), hostname));
host.reset(new Host(identifier, identifier_name, SUBNET_ID_UNUSED,
SUBNET_ID_UNUSED, IOAddress("0.0.0.0"), hostname));
// Add user context
if (user_context) {
......
......@@ -66,11 +66,12 @@ const size_t DHCP_IDENTIFIER_MAX_LEN = 128;
/// listed below:
/// - zero or null IPv4 address indicates that there is no reservation for the
/// IPv4 address for the host,
/// - zero or null subnet identifier (either IPv4 or IPv6) indicates that
/// - null subnet identifier (either IPv4 or IPv6) indicates that
/// this subnet identifier must be ignored. Specifically, this is the case
/// when host reservation is created for the DHCPv4 server, the IPv6 subnet id
/// should be ignored. Conversely, when host reservation is created for the
/// DHCPv6 server, the IPv4 subnet id should be ignored.
/// NOTE! Zero is a the "global" subnet id as Kea 1.5.0
///
/// To exclude those special case values, the Postgres backend uses partial
/// indexes, i.e. the only values that are included in the index are those that
......@@ -208,10 +209,20 @@ public:
bind_array->add(host->getIdentifierType());
// dhcp4_subnet_id : INT NULL
bind_array->add(host->getIPv4SubnetID());