2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 13:07:50 +00:00

[#720] Implemented #2790 fix

This commit is contained in:
Francis Dupont 2023-03-11 00:32:09 +01:00
parent 5b8e4c6da6
commit cc7f318dd4
8 changed files with 757 additions and 372 deletions

View File

@ -1,3 +1,8 @@
2109. [bug] fdupont
Compatibility flags e.g. lenient-option-parsing were not
saved by config-get and similar commands.
(Gitlab #2790)
2108. [func] fdupont
Added a new exclude-first-last-24 DHCPv4 compatibility flag
which when set to true (default is false) skips addresses

View File

@ -594,6 +594,12 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
ConstElementPtr compatibility = mutable_cfg->get("compatibility");
if (compatibility) {
for (auto kv : compatibility->mapValue()) {
if (!kv.second || (kv.second->getType() != Element::boolean)) {
isc_throw(DhcpConfigError,
"compatibility parameter values must be "
<< "boolean (" << kv.first << " at "
<< kv.second->getPosition() << ")");
}
if (kv.first == "lenient-option-parsing") {
CfgMgr::instance().getStagingCfg()->setLenientOptionParsing(
kv.second->boolValue());

View File

@ -1680,18 +1680,106 @@ TEST_F(Dhcp4ParserTest, echoClientId) {
// Now check that "false" configuration is really applied.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json_false));
checkResult(status, 0);
ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->getEchoClientId());
CfgMgr::instance().clear();
// Now check that "true" configuration is really applied.
EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json_true));
checkResult(status, 0);
ASSERT_TRUE(CfgMgr::instance().getStagingCfg()->getEchoClientId());
// In any case revert back to the default value (true)
CfgMgr::instance().getStagingCfg()->setEchoClientId(true);
}
// Check whether it is possible to configure compatibility flags.
TEST_F(Dhcp4ParserTest, compatibility) {
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"compatibility\": { "
" \"lenient-option-parsing\": true,"
" \"ignore-rai-link-selection\": true,"
" \"exclude-first-last-24\": true"
"},"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config)) << "bad config: " << config;
extractConfig(config);
// Check defaults: they should be false.
EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getLenientOptionParsing());
EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getIgnoreRAILinkSelection());
EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getExcludeFirstLast24());
// Check the configuration was really applied.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json));
checkResult(status, 0);
EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getLenientOptionParsing());
EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getIgnoreRAILinkSelection());
EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getExcludeFirstLast24());
}
// Check that unknown compatibility flag raises error.
TEST_F(Dhcp4ParserTest, compatibilityUnknown) {
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"compatibility\": { "
" \"foo-bar\": true"
"},"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
// Syntax is incorrect.
EXPECT_THROW(parseDHCP4(config), Dhcp4ParseError);
ConstElementPtr json;
EXPECT_NO_THROW(json = parseJSON(config));
// Unknown keyword is detected.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json));
string expected = "unsupported compatibility parameter: ";
expected += "foo-bar (<string>:1:127)";
checkResult(status, 1, expected);
}
// Check that not boolean compatibility flag value raises error.
TEST_F(Dhcp4ParserTest, compatibilityNotBool) {
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"compatibility\": { "
" \"lenient-option-parsing\": 1"
"},"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
// Syntax is incorrect.
EXPECT_THROW(parseDHCP4(config), Dhcp4ParseError);
ConstElementPtr json;
EXPECT_NO_THROW(json = parseJSON(config));
// Bad value type is detected.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv4SrvTest::configure(*srv_, json));
string expected = "compatibility parameter values must be boolean ";
expected += "(lenient-option-parsing at <string>:1:142)";
checkResult(status, 1, expected);
}
// This test checks that the global match-client-id parameter is optional
// and that values under the subnet are used.
TEST_F(Dhcp4ParserTest, matchClientIdNoGlobal) {
@ -7626,5 +7714,4 @@ TEST_F(Dhcp4ParserTest, parkedPacketLimit) {
ASSERT_THROW(parseDHCP4(bad_limit), std::exception);
}
} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -727,9 +727,20 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
ConstElementPtr compatibility = mutable_cfg->get("compatibility");
if (compatibility) {
for (auto kv : compatibility->mapValue()) {
if (!kv.second || (kv.second->getType() != Element::boolean)) {
isc_throw(DhcpConfigError,
"compatibility parameter values must be "
<< "boolean (" << kv.first << " at "
<< kv.second->getPosition() << ")");
}
if (kv.first == "lenient-option-parsing") {
CfgMgr::instance().getStagingCfg()->setLenientOptionParsing(
kv.second->boolValue());
} else {
isc_throw(DhcpConfigError,
"unsupported compatibility parameter: "
<< kv.first << " (" << kv.second->getPosition()
<< ")");
}
}
}

View File

@ -1688,6 +1688,89 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
EXPECT_EQ(4, (*++subnet)->getID());
}
// Check whether it is possible to configure compatibility flags.
TEST_F(Dhcp6ParserTest, compatibility) {
string config = "{ " + genIfaceConfig() + "," +
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"compatibility\": { "
" \"lenient-option-parsing\": true"
"},"
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
" \"subnet\": \"2001:db8:1::/64\" } ],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP6(config)) << "bad config: " << config;
extractConfig(config);
// Check defaults: they should be false.
EXPECT_FALSE(CfgMgr::instance().getStagingCfg()->getLenientOptionParsing());
// Check the configuration was really applied.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv6SrvTest::configure(srv_, json));
checkResult(status, 0);
EXPECT_TRUE(CfgMgr::instance().getStagingCfg()->getLenientOptionParsing());
}
// Check that unknown compatibility flag raises error.
TEST_F(Dhcp6ParserTest, compatibilityUnknown) {
string config = "{ " + genIfaceConfig() + "," +
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"compatibility\": { "
" \"foo-bar\": true"
"},"
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
" \"subnet\": \"2001:db8:1::/64\" } ],"
"\"valid-lifetime\": 4000 }";
// Syntax is incorrect.
EXPECT_THROW(parseDHCP6(config), Dhcp6ParseError);
ConstElementPtr json;
EXPECT_NO_THROW(json = parseJSON(config));
// Unknown keyword is detected.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv6SrvTest::configure(srv_, json));
string expected = "unsupported compatibility parameter: ";
expected += "foo-bar (<string>:1:154)";
checkResult(status, 1, expected);
}
// Check that not boolean compatibility flag value raises error.
TEST_F(Dhcp6ParserTest, compatibilityNotBool) {
string config = "{ " + genIfaceConfig() + "," +
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"compatibility\": { "
" \"lenient-option-parsing\": 1"
"},"
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
" \"subnet\": \"2001:db8:1::/64\" } ],"
"\"valid-lifetime\": 4000 }";
// Syntax is incorrect.
EXPECT_THROW(parseDHCP6(config), Dhcp6ParseError);
ConstElementPtr json;
EXPECT_NO_THROW(json = parseJSON(config));
// Bad value type is detected.
ConstElementPtr status;
EXPECT_NO_THROW(status = Dhcpv6SrvTest::configure(srv_, json));
string expected = "compatibility parameter values must be boolean ";
expected += "(lenient-option-parsing at <string>:1:169)";
checkResult(status, 1, expected);
}
// This test checks if it is possible to override global values
// on a per subnet basis.
TEST_F(Dhcp6ParserTest, subnetLocal) {

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2023 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
@ -48,7 +48,7 @@ SrvConfig::SrvConfig()
d2_client_config_(new D2ClientConfig()),
configured_globals_(new CfgGlobals()), cfg_consist_(new CfgConsistency()),
lenient_option_parsing_(false), ignore_rai_link_selection_(false),
reservations_lookup_first_(false) {
exclude_first_last_24_(false), reservations_lookup_first_(false) {
}
SrvConfig::SrvConfig(const uint32_t sequence)
@ -67,7 +67,7 @@ SrvConfig::SrvConfig(const uint32_t sequence)
d2_client_config_(new D2ClientConfig()),
configured_globals_(new CfgGlobals()), cfg_consist_(new CfgConsistency()),
lenient_option_parsing_(false), ignore_rai_link_selection_(false),
reservations_lookup_first_(false) {
exclude_first_last_24_(false), reservations_lookup_first_(false) {
}
std::string
@ -632,6 +632,21 @@ SrvConfig::toElement() const {
}
}
// Set compatibility flags.
ElementPtr compatibility = Element::createMap();
if (getLenientOptionParsing()) {
compatibility->set("lenient-option-parsing", Element::create(true));
}
if (getIgnoreRAILinkSelection()) {
compatibility->set("ignore-rai-link-selection", Element::create(true));
}
if (getExcludeFirstLast24()) {
compatibility->set("exclude-first-last-24", Element::create(true));
}
if (compatibility->size() > 0) {
dhcp->set("compatibility", compatibility);
}
// Set decline-probation-period
dhcp->set("decline-probation-period",
Element::create(static_cast<long long>(decline_timer_)));

View File

@ -309,6 +309,38 @@ TEST_F(SrvConfigTest, echoClientId) {
EXPECT_TRUE(conf1.getEchoClientId());
}
// This test verifies that compatibility flags are correctly managed.
TEST_F(SrvConfigTest, compatibility) {
SrvConfig conf;
// Check that defaults are false.
EXPECT_FALSE(conf.getLenientOptionParsing());
EXPECT_FALSE(conf.getIgnoreRAILinkSelection());
EXPECT_FALSE(conf.getExcludeFirstLast24());
// Check that they can be modified to true.
conf.setLenientOptionParsing(true);
conf.setIgnoreRAILinkSelection(true);
conf.setExcludeFirstLast24(true);
EXPECT_TRUE(conf.getLenientOptionParsing());
EXPECT_TRUE(conf.getIgnoreRAILinkSelection());
EXPECT_TRUE(conf.getExcludeFirstLast24());
// Check that default values can be restored.
conf.setLenientOptionParsing(false);
conf.setIgnoreRAILinkSelection(false);
conf.setExcludeFirstLast24(false);
EXPECT_FALSE(conf.getLenientOptionParsing());
EXPECT_FALSE(conf.getIgnoreRAILinkSelection());
EXPECT_FALSE(conf.getExcludeFirstLast24());
// Check the other constructor has the same default.
SrvConfig conf1(1);
EXPECT_FALSE(conf1.getLenientOptionParsing());
EXPECT_FALSE(conf1.getIgnoreRAILinkSelection());
EXPECT_FALSE(conf1.getExcludeFirstLast24());
}
// This test verifies that host reservations lookup first flag can be configured.
TEST_F(SrvConfigTest, reservationsLookupFirst) {
SrvConfig conf;
@ -600,10 +632,19 @@ TEST_F(SrvConfigTest, unparse) {
CfgMgr::instance().setFamily(AF_INET);
conf.setEchoClientId(false);
conf.setDhcp4o6Port(6767);
// Add compatibility flags.
conf.setLenientOptionParsing(true);
conf.setIgnoreRAILinkSelection(true);
conf.setExcludeFirstLast24(true);
params = "\"compatibility\": {\n";
params += " \"lenient-option-parsing\": true,\n";
params += " \"ignore-rai-link-selection\": true,\n";
params += " \"exclude-first-last-24\": true\n";
params += "},\n";
// Add "configured globals"
conf.addConfiguredGlobal("renew-timer", Element::create(777));
conf.addConfiguredGlobal("comment", Element::create("bar"));
params = "\"echo-client-id\": false,\n";
params += "\"echo-client-id\": false,\n";
params += "\"dhcp4o6-port\": 6767,\n";
params += "\"renew-timer\": 777,\n";
params += "\"comment\": \"bar\"\n";
@ -612,7 +653,14 @@ TEST_F(SrvConfigTest, unparse) {
// Verify direct non-default parameters and configured globals
CfgMgr::instance().setFamily(AF_INET6);
params = ",\"dhcp4o6-port\": 6767,\n";
// Add compatibility flag.
conf.setIgnoreRAILinkSelection(false);
conf.setExcludeFirstLast24(false);
params = ",\"compatibility\": {\n";
params += " \"lenient-option-parsing\": true\n";
params += "},\n";
// Add "configured globals"
params += "\"dhcp4o6-port\": 6767,\n";
params += "\"renew-timer\": 777,\n";
params += "\"comment\": \"bar\"\n";
isc::test::runToElementTest<SrvConfig>