2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 05:27:55 +00:00

[#3049] kea-dhcp4 parsing and UTs

Fully functional in kea-dhcp4 (excluding doc update and CB support)

/doc/examples/kea4/all-keys.json
    Added entries to a pool

/src/bin/dhcp4/dhcp4_lexer.ll
/src/bin/dhcp4/dhcp4_parser.yy
    Added parameters to pools

/src/bin/dhcp4/dhcp4_srv.cc
    Added comment

/src/bin/dhcp4/tests/config_parser_unittest.cc
    TEST_F(Dhcp4ParserTest, poolDdnsParameters) - new test

/src/bin/dhcp4/tests/fqdn_unittest.cc
    TEST_F(NameDhcpv4SrvTest, poolDdnsParametersTest) - new test

/src/bin/dhcp4/tests/get_config_unittest.cc
    Updated

/src/lib/dhcpsrv/parsers/base_network_parser.*
    Moved DDNS parameter parsing to a function template to allow use
    by any class type

/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
    Added DDNS parameter parsing to PoolParser

/src/lib/dhcpsrv/parsers/simple_parser4.cc
    Added paramters to pool

/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
    TEST_F(DhcpParserTest, validDdnsParmatersPool4) - new test
This commit is contained in:
Thomas Markwalder 2025-01-07 13:16:09 -05:00
parent 1f7dddcfab
commit bfe892dfa2
17 changed files with 2223 additions and 1525 deletions

View File

@ -1153,7 +1153,51 @@
// List of client classes which must be evaluated when this pool
// is selected for client assignments.
"evaluate-additional-classes": [ "late" ]
"evaluate-additional-classes": [ "late" ],
// Pool-level value. See description at the global level.
"ddns-generated-prefix": "mypool",
// Pool-level value. See description at the global level.
"ddns-override-client-update": false,
// Pool-level value. See description at the global level.
"ddns-override-no-update": false,
// Pool-level value. See description at the global level.
"ddns-qualifying-suffix": "pool.example.com.",
// Pool-level value. See description at the global level.
"ddns-replace-client-name": "always",
// Pool-level value. See description at the global level.
"ddns-send-updates": true,
// Pool-level value. See description at the global level.
"ddns-update-on-renew": false,
// Pool-level value. See description at the global level.
"ddns-conflict-resolution-mode": "check-with-dhcid",
// Pool-level value. See description at the global level.
"ddns-ttl-percent": 0.55,
// Pool-level value. See description at the global level.
// You cannot specify both ddns-ttl and any of ddns-ttl-percent,
// ddns-ttl-min, or ddns-ttl-max. They are mutually exclusive.
// "ddns-ttl": 500,
// Pool-evel value. See description at the global level.
"ddns-ttl-min": 10000,
// Pool-level value. See description at the global level.
"ddns-ttl-max": 20000,
// Pool-level value. See description at the global level.
"hostname-char-replacement": "x",
// Pool-level value. See description at the global level.
"hostname-char-set": "[^A-Za-z0-9.-]"
},
{
// Restricts this pool to allow only clients

View File

@ -1145,7 +1145,8 @@ Messages printed on debuglevel 55
- DHCP6_QUERY_DATA
- DHCP6_RESPONSE_DATA
- DHCP6_SUBNET_DATA
- DHCPSRV_DDNS_TTL_PERCENT_TOO_SMALL
- DHCPSRV_DDNS_TTL_TOO_LARGE
- DHCPSRV_DDNS_TTL_TOO_SMALL
- DHCPSRV_MEMFILE_LEASE_LOAD
- DHCPSRV_QUEUE_NCR
- DHCP_DDNS_AT_MAX_TRANSACTIONS

View File

@ -8585,19 +8585,19 @@ the database access parameters are changed: in the latter case, the
server closes the currently open database, and opens a database using
the new parameters.
DHCPSRV_DDNS_TTL_PERCENT_TOO_SMALL
==================================
DHCPSRV_DDNS_TTL_TOO_SMALL
==========================
.. code-block:: text
ddns-ttl-percent %1 of lease lifetime %2 is too small, ignoring it
%1 of lease life time %2 is %3, using minimum of %4 instead.
Logged at debug log level 55.
A debug message issued when the DDNS TTL value calculated using the
ddns-ttl-percent is zero. Kea will ignore the value and calculate
the DDNS TTL as though ddsn-ttl-percent were not specified. The
value of ddns-ttl-percent and the lease lifetime are shown in
the message details.
ddns-ttl-percent if specified or (0.33 if not) is too small. Kea will
ignore the value and use the minimum (ddns-ttl-min if specified or 600
seconds if not). The message details include the percentage, the lease
life time, the calculated TTL, and the value actually used.
DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET
===================================
@ -10012,6 +10012,16 @@ A debug message issued when one of the registered interval timers
is unregistered from the Timer Manager. The name of the timer is
included in the message.
DHCPSRV_UNKNOWN_DB
==================
.. code-block:: text
unknown database type: %1
The database access string specified a database type (given in the
message) that is unknown to the software. This is a configuration error.
****
DHCP
****

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2024 Internet Systems Consortium, Inc. ("ISC")
/* Copyright (C) 2016-2025 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
@ -764,6 +764,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_SEND_UPDATES(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-send-updates", driver.loc_);
@ -775,6 +776,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_OVERRIDE_NO_UPDATE(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-override-no-update", driver.loc_);
@ -786,6 +788,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_OVERRIDE_CLIENT_UPDATE(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-override-client-update", driver.loc_);
@ -797,6 +800,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_REPLACE_CLIENT_NAME(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-replace-client-name", driver.loc_);
@ -808,6 +812,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_GENERATED_PREFIX(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-generated-prefix", driver.loc_);
@ -819,6 +824,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_QUALIFYING_SUFFIX(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-qualifying-suffix", driver.loc_);
@ -830,6 +836,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_UPDATE_ON_RENEW(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-update-on-renew", driver.loc_);
@ -852,6 +859,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_CONFLICT_RESOLUTION_MODE(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-conflict-resolution-mode", driver.loc_);
@ -899,6 +907,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_TTL_PERCENT(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-ttl-percent", driver.loc_);
@ -910,6 +919,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_TTL(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-ttl", driver.loc_);
@ -921,6 +931,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_TTL_MIN(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-ttl-min", driver.loc_);
@ -932,6 +943,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_DDNS_TTL_MAX(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("ddns-ttl-max", driver.loc_);
@ -1993,6 +2005,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_HOSTNAME_CHAR_SET(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("hostname-char-set", driver.loc_);
@ -2004,6 +2017,7 @@ ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::POOLS:
return isc::dhcp::Dhcp4Parser::make_HOSTNAME_CHAR_REPLACEMENT(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("hostname-char-replacement", driver.loc_);

File diff suppressed because it is too large Load Diff

View File

@ -5712,7 +5712,7 @@ switch (yykind)
/// Constants.
enum
{
yylast_ = 1448, ///< Last index in yytable_.
yylast_ = 1608, ///< Last index in yytable_.
yynnts_ = 481, ///< Number of nonterminal symbols.
yyfinal_ = 28 ///< Termination state number.
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2024 Internet Systems Consortium, Inc. ("ISC")
/* Copyright (C) 2016-2025 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
@ -2210,6 +2210,20 @@ pool_param: pool_entry
| network_client_classes
| require_client_classes
| evaluate_additional_classes
| ddns_send_updates
| ddns_override_no_update
| ddns_override_client_update
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
| ddns_update_on_renew
| ddns_conflict_resolution_mode
| ddns_ttl_percent
| ddns_ttl
| ddns_ttl_min
| ddns_ttl_max
| hostname_char_set
| hostname_char_replacement
| user_context
| comment
| unknown_map_entry

View File

@ -1,4 +1,4 @@
// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2025 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
@ -3179,6 +3179,8 @@ Dhcpv4Srv::assignLease(Dhcpv4Exchange& ex) {
bool reprocess_client_name = false;
if (lease) {
// Since we have a lease check for pool-level DDNS parameters.
// If there are any we need to call processClientName() again.
auto ddns_params = ex.getContext()->getDdnsParams();
auto pool = ddns_params->setPoolFromAddress(lease->addr_);
if (pool) {

View File

@ -1,4 +1,4 @@
// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2025 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
@ -8450,4 +8450,125 @@ TEST_F(Dhcp4ParserTest, ddnsTtlMax) {
EXPECT_EQ(750, subnet->getDdnsTtlMax(Network::Inheritance::GLOBAL).get());
}
TEST_F(Dhcp4ParserTest, poolDdnsParameters) {
string config = R"(
{
"valid-lifetime": 4000,
"subnet4": [{
"id": 1,
"subnet": "192.0.0.0/16",
"pools": [{
"pool": "192.0.1.0/24",
"ddns-send-updates": true,
"ddns-override-no-update": true,
"ddns-override-client-update": true,
"ddns-replace-client-name": "always",
"ddns-generated-prefix": "prefix",
"ddns-qualifying-suffix": "suffix",
"hostname-char-set": "[a-z]",
"hostname-char-replacement": "X",
"ddns-update-on-renew": true,
"ddns-ttl-percent": 0.5,
"ddns-conflict-resolution-mode": "check-with-dhcid",
"ddns-ttl-min": 200,
"ddns-ttl-max": 500
},
{
"pool": "192.0.2.0/24",
"ddns-ttl": 300
}]
}]
}
)";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config));
extractConfig(config);
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json));
// returned value should be 0 (success)
checkResult(status, 0);
// Commit it so global inheritance works.
CfgMgr::instance().commit();
ConstSubnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.0.0"));
ASSERT_TRUE(subnet);
const PoolCollection pools = subnet->getPools(Lease::TYPE_V4);
ASSERT_GE(pools.size(), 2);
// First pool specifies all but ddns-ttl.
PoolPtr pool = pools.at(0);
ASSERT_TRUE(pool);
ASSERT_FALSE(pool->getDdnsSendUpdates().unspecified());
EXPECT_TRUE(pool->getDdnsSendUpdates().get());
ASSERT_FALSE(pool->getDdnsOverrideNoUpdate().unspecified());
EXPECT_TRUE(pool->getDdnsOverrideNoUpdate().get());
ASSERT_FALSE(pool->getDdnsOverrideClientUpdate().unspecified());
EXPECT_TRUE(pool->getDdnsOverrideClientUpdate().get());
ASSERT_FALSE(pool->getDdnsReplaceClientNameMode().unspecified());
EXPECT_EQ(pool->getDdnsReplaceClientNameMode().get(),
D2ClientConfig::RCM_ALWAYS);
ASSERT_FALSE(pool->getDdnsGeneratedPrefix().unspecified());
EXPECT_EQ(pool->getDdnsGeneratedPrefix().get(), "prefix");
ASSERT_FALSE(pool->getDdnsQualifyingSuffix().unspecified());
EXPECT_EQ(pool->getDdnsQualifyingSuffix().get(), "suffix");
ASSERT_FALSE(pool->getHostnameCharSet().unspecified());
EXPECT_EQ(pool->getHostnameCharSet().get(), "[a-z]");
ASSERT_FALSE(pool->getHostnameCharReplacement().unspecified());
EXPECT_EQ(pool->getHostnameCharReplacement().get(), "X");
ASSERT_FALSE(pool->getDdnsUpdateOnRenew().unspecified());
EXPECT_TRUE(pool->getDdnsUpdateOnRenew().get());
ASSERT_FALSE(pool->getDdnsTtlPercent().unspecified());
EXPECT_EQ(pool->getDdnsTtlPercent().get(), 0.5);
ASSERT_FALSE(pool->getDdnsConflictResolutionMode().unspecified());
EXPECT_EQ(pool->getDdnsConflictResolutionMode().get(), "check-with-dhcid");
ASSERT_TRUE(pool->getDdnsTtl().unspecified());
ASSERT_FALSE(pool->getDdnsTtlMin().unspecified());
EXPECT_EQ(pool->getDdnsTtlMin().get(), 200);
ASSERT_FALSE(pool->getDdnsTtlMax().unspecified());
EXPECT_EQ(pool->getDdnsTtlMax().get(), 500);
// Second pool only specifies ddns-ttl.
pool = pools.at(1);
ASSERT_TRUE(pool);
ASSERT_TRUE(pool->getDdnsSendUpdates().unspecified());
ASSERT_TRUE(pool->getDdnsOverrideNoUpdate().unspecified());
ASSERT_TRUE(pool->getDdnsOverrideClientUpdate().unspecified());
ASSERT_TRUE(pool->getDdnsReplaceClientNameMode().unspecified());
ASSERT_TRUE(pool->getDdnsGeneratedPrefix().unspecified());
ASSERT_TRUE(pool->getDdnsQualifyingSuffix().unspecified());
ASSERT_TRUE(pool->getHostnameCharSet().unspecified());
ASSERT_TRUE(pool->getHostnameCharReplacement().unspecified());
ASSERT_TRUE(pool->getDdnsUpdateOnRenew().unspecified());
ASSERT_TRUE(pool->getDdnsTtlPercent().unspecified());
ASSERT_TRUE(pool->getDdnsConflictResolutionMode().unspecified());
ASSERT_TRUE(pool->getDdnsTtlMin().unspecified());
ASSERT_FALSE(pool->getDdnsTtl().unspecified());
EXPECT_EQ(pool->getDdnsTtl().get(), 300);
ASSERT_TRUE(pool->getDdnsTtlMax().unspecified());
}
} // namespace

View File

@ -1,4 +1,4 @@
// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2025 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
@ -772,7 +772,7 @@ public:
}
/// @brief Verifies that DDNS TTL parameters are used when specified.
/// @brief Verifies that DDNS TTL parameters are used when specified.
///
/// @param valid_flt lease life time
/// @param ddns_ttl_percent expected configured value for ddns-ttl-percent
@ -786,10 +786,10 @@ public:
Optional<uint32_t> ddns_ttl_max = Optional<uint32_t>()) {
std::string config_header = R"(
{
{
"interfaces-config": { "interfaces": [ "*" ] },
"subnet4": [ {
"subnet": "10.0.0.0/24",
"subnet": "10.0.0.0/24",
"interface": "eth1",
"id": 1,
"pools": [ { "pool": "10.0.0.10-10.0.0.10" } ]
@ -859,13 +859,135 @@ public:
resp->getYiaddr().toText(),
"client1.example.com.", "",
time(NULL), lease->valid_lft_, true,
CHECK_WITH_DHCID,
ddns_ttl_percent,
ddns_ttl,
ddns_ttl_min,
CHECK_WITH_DHCID,
ddns_ttl_percent,
ddns_ttl,
ddns_ttl_min,
ddns_ttl_max);
}
/// @brief Verifies that DDNS parameters specified at the pool level
/// are used when specified. We don't verify all of them, just enough
/// enough to ensure proper scoping of values.
void testPoolDdnsParameters() {
std::string config = R"(
{
"interfaces-config": { "interfaces": [ "*" ] },
"dhcp-ddns": { "enable-updates": true },
"subnet4": [{
"subnet": "10.0.0.0/24",
"interface": "eth1",
"id": 1,
"ddns-qualifying-suffix": "subfix.com",
"pools": [{
"pool": "10.0.0.10-10.0.0.10",
"ddns-qualifying-suffix": "poolfix.com",
},
{
"pool": "10.0.0.11-10.0.0.11"
},
{
"pool": "10.0.0.12-10.0.0.12",
"ddns-send-updates": false
}]
}],
"valid-lifetime": 400
})";
// Load theconfiguration with D2 enabled.
ASSERT_NO_FATAL_FAILURE(configure(config, *srv_));
ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
// Create a client and get a lease.
Dhcp4Client client1(srv_, Dhcp4Client::SELECTING);
client1.setIfaceName("eth1");
client1.setIfaceIndex(ETH1_INDEX);
ASSERT_NO_THROW(client1.includeHostname("client1"));
// Do the DORA.
ASSERT_NO_THROW(client1.doDORA());
Pkt4Ptr resp = client1.getContext().response_;
ASSERT_TRUE(resp);
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Make sure the lease is in the database and hostname uses the
// first pool's qualifying suffix.
Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(IOAddress("10.0.0.10"));
ASSERT_TRUE(lease);
EXPECT_EQ("client1.poolfix.com", lease->hostname_);
// Verify the hostname option sent in the response.
OptionStringPtr hostname;
hostname = boost::dynamic_pointer_cast<OptionString>(resp->getOption(DHO_HOST_NAME));
ASSERT_TRUE(hostname);
EXPECT_EQ("client1.poolfix.com", hostname->getValue());
// Verify that an NCR was generated correctly.
ASSERT_EQ(1, CfgMgr::instance().getD2ClientMgr().getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
resp->getYiaddr().toText(),
"client1.poolfix.com.", "",
time(NULL), lease->valid_lft_, true,
CHECK_WITH_DHCID);
// Create another client and get a lease.
Dhcp4Client client2(srv_, Dhcp4Client::SELECTING);
client2.setIfaceName("eth1");
client2.setIfaceIndex(ETH1_INDEX);
ASSERT_NO_THROW(client2.includeHostname("client2"));
// Do the DORA.
ASSERT_NO_THROW(client2.doDORA());
resp = client2.getContext().response_;
ASSERT_TRUE(resp);
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Make sure the lease is in the database and hostname uses the subnet's
// qualifying suffix.
lease = LeaseMgrFactory::instance().getLease4(IOAddress("10.0.0.11"));
ASSERT_TRUE(lease);
EXPECT_EQ("client2.subfix.com", lease->hostname_);
// Verify the hostname option sent in the response.
hostname = boost::dynamic_pointer_cast<OptionString>(resp->getOption(DHO_HOST_NAME));
ASSERT_TRUE(hostname);
EXPECT_EQ("client2.subfix.com", hostname->getValue());
// Verify that an NCR was generated correctly.
ASSERT_EQ(1, CfgMgr::instance().getD2ClientMgr().getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
resp->getYiaddr().toText(),
"client2.subfix.com.", "",
time(NULL), lease->valid_lft_, true,
CHECK_WITH_DHCID);
// Create another third client and get a lease.
Dhcp4Client client3(srv_, Dhcp4Client::SELECTING);
client3.setIfaceName("eth1");
client3.setIfaceIndex(ETH1_INDEX);
ASSERT_NO_THROW(client3.includeHostname("client3"));
// Do the DORA.
ASSERT_NO_THROW(client3.doDORA());
resp = client3.getContext().response_;
ASSERT_TRUE(resp);
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Make sure the lease is in the database and hostname uses the subnet suffix.
lease = LeaseMgrFactory::instance().getLease4(IOAddress("10.0.0.12"));
ASSERT_TRUE(lease);
EXPECT_EQ("client3.subfix.com", lease->hostname_);
// Verify the hostname option sent in the response.
hostname = boost::dynamic_pointer_cast<OptionString>(resp->getOption(DHO_HOST_NAME));
ASSERT_TRUE(hostname);
EXPECT_EQ("client3.subfix.com", hostname->getValue());
// There should not be an NCR in the queue since the third pool
// disables DDNS updates.
ASSERT_EQ(0, CfgMgr::instance().getD2ClientMgr().getQueueSize());
}
///@brief Verify that NameChangeRequest holds valid values.
///
/// Pulls the NCR from the top of the send queue and checks its content
@ -927,7 +1049,7 @@ public:
// time for equality but rather check that the lease expiration time
// is not greater than the current time + lease lifetime.
uint32_t ttl = calculateDdnsTtl(valid_lft, ddns_ttl_percent,
uint32_t ttl = calculateDdnsTtl(valid_lft, ddns_ttl_percent,
ddns_ttl, ddns_ttl_min, ddns_ttl_max);
if (not_strict_expire_check) {
@ -3072,7 +3194,7 @@ TEST_F(NameDhcpv4SrvTest, ddnsTtlTest) {
testDdnsTtlParameters(2100, // valid lft
Optional<double>(), // percent
999, // ttl
Optional<uint32_t>(), // min
Optional<uint32_t>(), // min
Optional<uint32_t>()); // max
}
@ -3080,7 +3202,7 @@ TEST_F(NameDhcpv4SrvTest, ddnsTtlTest) {
TEST_F(NameDhcpv4SrvTest, ddnsTtlMinTest) {
testDdnsTtlParameters(2100, // valid lft
Optional<double>(), // percent
Optional<uint32_t>(), // ttl
Optional<uint32_t>(), // ttl
800, // ttl-min
Optional<uint32_t>()); // ttl-max
}
@ -3089,9 +3211,14 @@ TEST_F(NameDhcpv4SrvTest, ddnsTtlMinTest) {
TEST_F(NameDhcpv4SrvTest, ddnsTtlMaxTest) {
testDdnsTtlParameters(2100, // valid lft
Optional<double>(), // percent
Optional<uint32_t>(), // ttl
Optional<uint32_t>(), // ttl
Optional<uint32_t>(), // ttl-min
500); // ttl-max
500); // ttl-max
}
// Verify pool-level DDNS pararmeters are used.
TEST_F(NameDhcpv4SrvTest, poolDdnsParametersTest) {
testPoolDdnsParameters();
}
} // end of anonymous namespace

View File

@ -1,4 +1,4 @@
// Copyright (C) 2017-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2017-2025 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
@ -2724,6 +2724,38 @@ const char* EXTRACTED_CONFIGS[] = {
" }\n"
" ],\n"
" \"valid-lifetime\": 4000\n"
" }\n",
// CONFIGURATION 87
"{\n"
" \"subnet4\": [\n"
" {\n"
" \"id\": 1,\n"
" \"pools\": [\n"
" {\n"
" \"ddns-conflict-resolution-mode\": \"check-with-dhcid\",\n"
" \"ddns-generated-prefix\": \"prefix\",\n"
" \"ddns-override-client-update\": true,\n"
" \"ddns-override-no-update\": true,\n"
" \"ddns-qualifying-suffix\": \"suffix\",\n"
" \"ddns-replace-client-name\": \"always\",\n"
" \"ddns-send-updates\": true,\n"
" \"ddns-ttl-max\": 500,\n"
" \"ddns-ttl-min\": 200,\n"
" \"ddns-ttl-percent\": 0.5,\n"
" \"ddns-update-on-renew\": true,\n"
" \"hostname-char-replacement\": \"X\",\n"
" \"hostname-char-set\": \"[a-z]\",\n"
" \"pool\": \"192.0.1.0/24\"\n"
" },\n"
" {\n"
" \"ddns-ttl\": 300,\n"
" \"pool\": \"192.0.2.0/24\"\n"
" }\n"
" ],\n"
" \"subnet\": \"192.0.0.0/16\"\n"
" }\n"
" ],\n"
" \"valid-lifetime\": 4000\n"
" }\n"
};
@ -14119,6 +14151,134 @@ const char* UNPARSED_CONFIGS[] = {
" \"t1-percent\": 0.5,\n"
" \"t2-percent\": 0.875,\n"
" \"valid-lifetime\": 4000\n"
" }\n",
// CONFIGURATION 87
"{\n"
" \"allocator\": \"iterative\",\n"
" \"authoritative\": false,\n"
" \"boot-file-name\": \"\",\n"
" \"calculate-tee-times\": false,\n"
" \"ddns-conflict-resolution-mode\": \"check-with-dhcid\",\n"
" \"ddns-generated-prefix\": \"myhost\",\n"
" \"ddns-override-client-update\": false,\n"
" \"ddns-override-no-update\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
" \"ddns-update-on-renew\": false,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"max-queue-size\": 1024,\n"
" \"ncr-format\": \"JSON\",\n"
" \"ncr-protocol\": \"UDP\",\n"
" \"sender-ip\": \"0.0.0.0\",\n"
" \"sender-port\": 0,\n"
" \"server-ip\": \"127.0.0.1\",\n"
" \"server-port\": 53001\n"
" },\n"
" \"dhcp-queue-control\": {\n"
" \"capacity\": 64,\n"
" \"enable-queue\": false,\n"
" \"queue-type\": \"kea-ring4\"\n"
" },\n"
" \"dhcp4o6-port\": 0,\n"
" \"early-global-reservations-lookup\": false,\n"
" \"echo-client-id\": true,\n"
" \"expired-leases-processing\": {\n"
" \"flush-reclaimed-timer-wait-time\": 25,\n"
" \"hold-reclaimed-time\": 3600,\n"
" \"max-reclaim-leases\": 100,\n"
" \"max-reclaim-time\": 250,\n"
" \"reclaim-timer-wait-time\": 10,\n"
" \"unwarned-reclaim-cycles\": 5\n"
" },\n"
" \"hooks-libraries\": [ ],\n"
" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n"
" \"hostname-char-replacement\": \"\",\n"
" \"hostname-char-set\": \"[^A-Za-z0-9.-]\",\n"
" \"interfaces-config\": {\n"
" \"interfaces\": [ ],\n"
" \"re-detect\": false\n"
" },\n"
" \"ip-reservations-unique\": true,\n"
" \"lease-database\": {\n"
" \"type\": \"memfile\"\n"
" },\n"
" \"match-client-id\": true,\n"
" \"multi-threading\": {\n"
" \"enable-multi-threading\": true,\n"
" \"packet-queue-size\": 64,\n"
" \"thread-pool-size\": 0\n"
" },\n"
" \"next-server\": \"0.0.0.0\",\n"
" \"option-data\": [ ],\n"
" \"option-def\": [ ],\n"
" \"parked-packet-limit\": 256,\n"
" \"reservations-global\": false,\n"
" \"reservations-in-subnet\": true,\n"
" \"reservations-lookup-first\": false,\n"
" \"reservations-out-of-pool\": false,\n"
" \"sanity-checks\": {\n"
" \"extended-info-checks\": \"fix\",\n"
" \"lease-checks\": \"warn\"\n"
" },\n"
" \"server-hostname\": \"\",\n"
" \"server-tag\": \"\",\n"
" \"shared-networks\": [ ],\n"
" \"stash-agent-options\": false,\n"
" \"statistic-default-sample-age\": 0,\n"
" \"statistic-default-sample-count\": 20,\n"
" \"store-extended-info\": false,\n"
" \"subnet4\": [\n"
" {\n"
" \"4o6-interface\": \"\",\n"
" \"4o6-interface-id\": \"\",\n"
" \"4o6-subnet\": \"\",\n"
" \"allocator\": \"iterative\",\n"
" \"calculate-tee-times\": false,\n"
" \"id\": 1,\n"
" \"max-valid-lifetime\": 4000,\n"
" \"min-valid-lifetime\": 4000,\n"
" \"option-data\": [ ],\n"
" \"pools\": [\n"
" {\n"
" \"ddns-conflict-resolution-mode\": \"check-with-dhcid\",\n"
" \"ddns-generated-prefix\": \"prefix\",\n"
" \"ddns-override-client-update\": true,\n"
" \"ddns-override-no-update\": true,\n"
" \"ddns-qualifying-suffix\": \"suffix\",\n"
" \"ddns-replace-client-name\": \"always\",\n"
" \"ddns-send-updates\": true,\n"
" \"ddns-ttl-max\": 500,\n"
" \"ddns-ttl-min\": 200,\n"
" \"ddns-ttl-percent\": 0.5,\n"
" \"ddns-update-on-renew\": true,\n"
" \"hostname-char-replacement\": \"X\",\n"
" \"hostname-char-set\": \"[a-z]\",\n"
" \"option-data\": [ ],\n"
" \"pool\": \"192.0.1.0/24\"\n"
" },\n"
" {\n"
" \"ddns-ttl\": 300,\n"
" \"option-data\": [ ],\n"
" \"pool\": \"192.0.2.0/24\"\n"
" }\n"
" ],\n"
" \"relay\": {\n"
" \"ip-addresses\": [ ]\n"
" },\n"
" \"reservations\": [ ],\n"
" \"store-extended-info\": false,\n"
" \"subnet\": \"192.0.0.0/16\",\n"
" \"t1-percent\": 0.5,\n"
" \"t2-percent\": 0.875,\n"
" \"valid-lifetime\": 4000\n"
" }\n"
" ],\n"
" \"t1-percent\": 0.5,\n"
" \"t2-percent\": 0.875,\n"
" \"valid-lifetime\": 4000\n"
" }\n"
};

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2019-2025 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
@ -140,108 +140,7 @@ BaseNetworkParser::parseCacheParams(const ConstElementPtr& network_data,
void
BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
NetworkPtr& network) {
if (network_data->contains("ddns-send-updates")) {
network->setDdnsSendUpdates(getBoolean(network_data, "ddns-send-updates"));
}
if (network_data->contains("ddns-override-no-update")) {
network->setDdnsOverrideNoUpdate(getBoolean(network_data, "ddns-override-no-update"));
}
if (network_data->contains("ddns-override-client-update")) {
network->setDdnsOverrideClientUpdate(getBoolean(network_data, "ddns-override-client-update"));
}
if (network_data->contains("ddns-replace-client-name")) {
network->setDdnsReplaceClientNameMode(getAndConvert<D2ClientConfig::ReplaceClientNameMode,
D2ClientConfig::stringToReplaceClientNameMode>
(network_data, "ddns-replace-client-name",
"ReplaceClientName mode"));
}
if (network_data->contains("ddns-generated-prefix")) {
network->setDdnsGeneratedPrefix(getString(network_data, "ddns-generated-prefix"));
}
if (network_data->contains("ddns-qualifying-suffix")) {
network->setDdnsQualifyingSuffix(getString(network_data, "ddns-qualifying-suffix"));
}
std::string hostname_char_set;
if (network_data->contains("hostname-char-set")) {
hostname_char_set = getString(network_data, "hostname-char-set");
network->setHostnameCharSet(hostname_char_set);
}
std::string hostname_char_replacement;
if (network_data->contains("hostname-char-replacement")) {
hostname_char_replacement = getString(network_data, "hostname-char-replacement");
network->setHostnameCharReplacement(hostname_char_replacement);
}
// We need to validate sanitizer values here so we can detect problems and
// cause a configuration. We don't retain the compilation because it's not
// something we can inherit.
if (!hostname_char_set.empty()) {
try {
str::StringSanitizerPtr sanitizer(new str::StringSanitizer(hostname_char_set,
hostname_char_replacement));
} catch (const std::exception& ex) {
isc_throw(BadValue, "hostname-char-set '" << hostname_char_set
<< "' is not a valid regular expression");
}
}
if (network_data->contains("ddns-update-on-renew")) {
network->setDdnsUpdateOnRenew(getBoolean(network_data, "ddns-update-on-renew"));
}
bool has_ddns_ttl = false;
uint32_t ddns_ttl = 0;
if (network_data->contains("ddns-ttl")) {
ddns_ttl = getInteger(network_data, "ddns-ttl");
network->setDdnsTtl(ddns_ttl);
has_ddns_ttl = true;
}
if (network_data->contains("ddns-ttl-percent")) {
if (has_ddns_ttl) {
isc_throw(BadValue, "cannot specify both ddns-ttl-percent and ddns-ttl");
}
network->setDdnsTtlPercent(getDouble(network_data, "ddns-ttl-percent"));
}
uint32_t ddns_ttl_min = 0;
if (network_data->contains("ddns-ttl-min")) {
if (has_ddns_ttl) {
isc_throw(BadValue, "cannot specify both ddns-ttl-min and ddns-ttl");
}
ddns_ttl_min = getInteger(network_data, "ddns-ttl-min");
network->setDdnsTtlMin(ddns_ttl_min);
}
if (network_data->contains("ddns-ttl-max")) {
if (has_ddns_ttl) {
isc_throw(BadValue, "cannot specify both ddns-ttl-max and ddns-ttl");
}
uint32_t ddns_ttl_max = getInteger(network_data, "ddns-ttl-max");
if (ddns_ttl_max < ddns_ttl_min) {
isc_throw(BadValue, "ddns-ttl-max: " << ddns_ttl_max
<< " must be greater than ddns-ttl-min: " << ddns_ttl_min);
}
network->setDdnsTtlMax(ddns_ttl_max);
}
// For backward compatibility, ddns-conflict-resolution-mode is optional.
if (network_data->contains("ddns-conflict-resolution-mode")) {
network->setDdnsConflictResolutionMode(getString(network_data,
"ddns-conflict-resolution-mode"));
}
parseDdnsParameters(network_data, network);
}
void

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2019-2025 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
@ -146,6 +146,136 @@ public:
/// @throw DhcpConfigError if both entries are present.
static void getClientClassesElem(data::ConstElementPtr params,
ClassAdderFunc adder_func);
/// @brief Parses parameters pertaining to DDNS behavior.
///
/// The parsed parameters are:
/// - ddns-send-updates
/// - ddns-override-no-update
/// - ddns-override-client-update
/// - ddns-replace-client-name
/// - ddns-generated-prefix
/// - ddns-qualifying-suffix
/// - ddns-use-conflict-resolution (retained for backward compatibility)
/// - ddns-update-on-renew
/// - ddns-ttl-percent
/// - ddns-conflict-resolution-mode
///
/// Owner types are expected to have public setters for each of these
/// paramters.
///
/// @tparam DdnsOwnerPtr pointer to the class type that owns the DDNS parameters
/// @param config configuration element holding the DDNS paramters to parse.
/// @param owner Pointer to the DDNS parameter owner object into which parsed values
/// should be stored.
/// @throw BadValue for various invalid values.
template<typename DdnsOwnerTypePtr>
void parseDdnsParameters(const data::ConstElementPtr& config,
DdnsOwnerTypePtr owner) {
if (config->contains("ddns-send-updates")) {
owner->setDdnsSendUpdates(getBoolean(config, "ddns-send-updates"));
}
if (config->contains("ddns-override-no-update")) {
owner->setDdnsOverrideNoUpdate(getBoolean(config, "ddns-override-no-update"));
}
if (config->contains("ddns-override-client-update")) {
owner->setDdnsOverrideClientUpdate(getBoolean(config, "ddns-override-client-update"));
}
if (config->contains("ddns-replace-client-name")) {
owner->setDdnsReplaceClientNameMode(getAndConvert<D2ClientConfig::ReplaceClientNameMode,
D2ClientConfig::stringToReplaceClientNameMode>
(config, "ddns-replace-client-name",
"ReplaceClientName mode"));
}
if (config->contains("ddns-generated-prefix")) {
owner->setDdnsGeneratedPrefix(getString(config, "ddns-generated-prefix"));
}
if (config->contains("ddns-qualifying-suffix")) {
owner->setDdnsQualifyingSuffix(getString(config, "ddns-qualifying-suffix"));
}
std::string hostname_char_set;
if (config->contains("hostname-char-set")) {
hostname_char_set = getString(config, "hostname-char-set");
owner->setHostnameCharSet(hostname_char_set);
}
std::string hostname_char_replacement;
if (config->contains("hostname-char-replacement")) {
hostname_char_replacement = getString(config, "hostname-char-replacement");
owner->setHostnameCharReplacement(hostname_char_replacement);
}
// We need to validate sanitizer values here so we can detect problems and
// cause a configuration. We don't retain the compilation because it's not
// something we can inherit.
if (!hostname_char_set.empty()) {
try {
util::str::StringSanitizerPtr sanitizer(
new util::str::StringSanitizer(hostname_char_set,
hostname_char_replacement));
} catch (const std::exception& ex) {
isc_throw(BadValue, "hostname-char-set '" << hostname_char_set
<< "' is not a valid regular expression");
}
}
if (config->contains("ddns-update-on-renew")) {
owner->setDdnsUpdateOnRenew(getBoolean(config, "ddns-update-on-renew"));
}
bool has_ddns_ttl = false;
uint32_t ddns_ttl = 0;
if (config->contains("ddns-ttl")) {
ddns_ttl = getInteger(config, "ddns-ttl");
owner->setDdnsTtl(ddns_ttl);
has_ddns_ttl = true;
}
if (config->contains("ddns-ttl-percent")) {
if (has_ddns_ttl) {
isc_throw(BadValue, "cannot specify both ddns-ttl-percent and ddns-ttl");
}
owner->setDdnsTtlPercent(getDouble(config, "ddns-ttl-percent"));
}
uint32_t ddns_ttl_min = 0;
if (config->contains("ddns-ttl-min")) {
if (has_ddns_ttl) {
isc_throw(BadValue, "cannot specify both ddns-ttl-min and ddns-ttl");
}
ddns_ttl_min = getInteger(config, "ddns-ttl-min");
owner->setDdnsTtlMin(ddns_ttl_min);
}
if (config->contains("ddns-ttl-max")) {
if (has_ddns_ttl) {
isc_throw(BadValue, "cannot specify both ddns-ttl-max and ddns-ttl");
}
uint32_t ddns_ttl_max = getInteger(config, "ddns-ttl-max");
if (ddns_ttl_max < ddns_ttl_min) {
isc_throw(BadValue, "ddns-ttl-max: " << ddns_ttl_max
<< " must be greater than ddns-ttl-min: " << ddns_ttl_min);
}
owner->setDdnsTtlMax(ddns_ttl_max);
}
// For backward compatibility, ddns-conflict-resolution-mode is optional.
if (config->contains("ddns-conflict-resolution-mode")) {
owner->setDdnsConflictResolutionMode(getString(config,
"ddns-conflict-resolution-mode"));
}
}
};
} // end of namespace isc::dhcp

View File

@ -1,4 +1,4 @@
// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2025 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
@ -537,6 +537,10 @@ PoolParser::parse(PoolStoragePtr pools,
BaseNetworkParser::getAdditionalClassesElem(pool_structure,
std::bind(&Pool::addAdditionalClass,
pool, ph::_1));
// Parse DDNS behavioral parameters.
BaseNetworkParser parser;
parser.parseDdnsParameters(pool_structure, pool);
}
boost::shared_ptr<OptionDataListParser>

View File

@ -1,4 +1,4 @@
// Copyright (C) 2016-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2016-2025 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
@ -332,16 +332,30 @@ const ParamsList SimpleParser4::INHERIT_TO_SUBNET4 = {
/// list and map types for entries.
/// Order follows pool_param rules in bison grammar.
const SimpleKeywords SimpleParser4::POOL4_PARAMETERS = {
{ "pool", Element::string },
{ "pool-id", Element::integer },
{ "option-data", Element::list },
{ "client-class", Element::string },
{ "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "user-context", Element::map },
{ "comment", Element::string },
{ "metadata", Element::map }
{ "pool", Element::string },
{ "pool-id", Element::integer },
{ "option-data", Element::list },
{ "client-class", Element::string },
{ "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "user-context", Element::map },
{ "comment", Element::string },
{ "metadata", Element::map },
{ "ddns-send-updates", Element::boolean },
{ "ddns-override-no-update", Element::boolean },
{ "ddns-override-client-update", Element::boolean },
{ "ddns-replace-client-name", Element::string },
{ "ddns-generated-prefix", Element::string },
{ "ddns-qualifying-suffix", Element::string },
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
{ "ddns-update-on-renew", Element::boolean },
{ "ddns-ttl-percent", Element::real },
{ "ddns-conflict-resolution-mode", Element::string },
{ "ddns-ttl", Element::integer },
{ "ddns-ttl-min", Element::integer },
{ "ddns-ttl-max", Element::integer }
};
/// @brief This table defines all shared network parameters for DHCPv4.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2025 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
@ -4337,4 +4337,110 @@ TEST_F(DhcpParserTest, invalidDdnsTtlParmatersSubnet6) {
invalidDdnsTtlParmatersSubnet<Subnet6ConfigParser>(AF_INET6);
}
// Verifies valid DDNS parameters in v4 pools. As this uses the
// same parser as Network derivatives we skip retesting all the
// invalid permuations. This test ensures all supported
// parameters can be set.
TEST_F(DhcpParserTest, validDdnsParmatersPool4) {
std::string config =
R"^([{
"pool": "192.0.1.0/24",
"ddns-send-updates": true,
"ddns-override-no-update": true,
"ddns-override-client-update": true,
"ddns-replace-client-name": "always",
"ddns-generated-prefix": "prefix",
"ddns-qualifying-suffix": "suffix",
"hostname-char-set": "[a-z]",
"hostname-char-replacement": "X",
"ddns-update-on-renew": true,
"ddns-ttl-percent": 0.5,
"ddns-conflict-resolution-mode": "check-with-dhcid",
"ddns-ttl-min": 200,
"ddns-ttl-max": 500
},
{
"pool": "192.0.2.0/24",
"ddns-ttl": 300
}])^";
ElementPtr config_element = Element::fromJSON(config);
PoolStoragePtr pools(new PoolStorage());
Pools4ListParser parser;
ASSERT_NO_THROW_LOG(parser.parse(pools, config_element));
// Should have two pools.
ASSERT_EQ(pools->size(), 2);
// First pool specifies all but ddns-ttl.
PoolPtr pool = (*pools)[0];
ASSERT_TRUE(pool);
ASSERT_FALSE(pool->getDdnsSendUpdates().unspecified());
EXPECT_TRUE(pool->getDdnsSendUpdates().get());
ASSERT_FALSE(pool->getDdnsOverrideNoUpdate().unspecified());
EXPECT_TRUE(pool->getDdnsOverrideNoUpdate().get());
ASSERT_FALSE(pool->getDdnsOverrideClientUpdate().unspecified());
EXPECT_TRUE(pool->getDdnsOverrideClientUpdate().get());
ASSERT_FALSE(pool->getDdnsReplaceClientNameMode().unspecified());
EXPECT_EQ(pool->getDdnsReplaceClientNameMode().get(),
D2ClientConfig::RCM_ALWAYS);
ASSERT_FALSE(pool->getDdnsGeneratedPrefix().unspecified());
EXPECT_EQ(pool->getDdnsGeneratedPrefix().get(), "prefix");
ASSERT_FALSE(pool->getDdnsQualifyingSuffix().unspecified());
EXPECT_EQ(pool->getDdnsQualifyingSuffix().get(), "suffix");
ASSERT_FALSE(pool->getHostnameCharSet().unspecified());
EXPECT_EQ(pool->getHostnameCharSet().get(), "[a-z]");
ASSERT_FALSE(pool->getHostnameCharReplacement().unspecified());
EXPECT_EQ(pool->getHostnameCharReplacement().get(), "X");
ASSERT_FALSE(pool->getDdnsUpdateOnRenew().unspecified());
EXPECT_TRUE(pool->getDdnsUpdateOnRenew().get());
ASSERT_FALSE(pool->getDdnsTtlPercent().unspecified());
EXPECT_EQ(pool->getDdnsTtlPercent().get(), 0.5);
ASSERT_FALSE(pool->getDdnsConflictResolutionMode().unspecified());
EXPECT_EQ(pool->getDdnsConflictResolutionMode().get(), "check-with-dhcid");
ASSERT_TRUE(pool->getDdnsTtl().unspecified());
ASSERT_FALSE(pool->getDdnsTtlMin().unspecified());
EXPECT_EQ(pool->getDdnsTtlMin().get(), 200);
ASSERT_FALSE(pool->getDdnsTtlMax().unspecified());
EXPECT_EQ(pool->getDdnsTtlMax().get(), 500);
// Second pool only specifies ddns-ttl.
pool = (*pools)[1];
ASSERT_TRUE(pool);
ASSERT_TRUE(pool->getDdnsSendUpdates().unspecified());
ASSERT_TRUE(pool->getDdnsOverrideNoUpdate().unspecified());
ASSERT_TRUE(pool->getDdnsOverrideClientUpdate().unspecified());
ASSERT_TRUE(pool->getDdnsReplaceClientNameMode().unspecified());
ASSERT_TRUE(pool->getDdnsGeneratedPrefix().unspecified());
ASSERT_TRUE(pool->getDdnsQualifyingSuffix().unspecified());
ASSERT_TRUE(pool->getHostnameCharSet().unspecified());
ASSERT_TRUE(pool->getHostnameCharReplacement().unspecified());
ASSERT_TRUE(pool->getDdnsUpdateOnRenew().unspecified());
ASSERT_TRUE(pool->getDdnsTtlPercent().unspecified());
ASSERT_TRUE(pool->getDdnsConflictResolutionMode().unspecified());
ASSERT_TRUE(pool->getDdnsTtlMin().unspecified());
ASSERT_FALSE(pool->getDdnsTtl().unspecified());
EXPECT_EQ(pool->getDdnsTtl().get(), 300);
ASSERT_TRUE(pool->getDdnsTtlMax().unspecified());
}
} // Anonymous namespace