2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

[#3440] Checkpoint: added code, UT to update

This commit is contained in:
Francis Dupont
2024-06-26 12:39:55 +02:00
parent 9e9e28de44
commit ef94b64b25
4 changed files with 90 additions and 94 deletions

View File

@@ -30,6 +30,8 @@
#include <limits>
#include <list>
#include <unordered_map>
#include <vector>
using namespace std;
using namespace isc::dhcp;
@@ -482,6 +484,69 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
// The buffer being read comprises a set of options, each starting with
// a one-byte type code and a one-byte length field.
// Track seen options in a first pass.
vector<bool> seen(256, false);
unordered_map<uint8_t, size_t> counts;
while (offset < buf.size()) {
// Get the option type
uint8_t opt_type = buf[offset++];
// DHO_END is a special, one octet long option
// Valid in dhcp4 space or when check is true and
// there is a sub-option configured for this code.
if ((opt_type == DHO_END) && (space_is_dhcp4 || flex_end)) {
// Done.
break;
}
// DHO_PAD is just a padding after DHO_END. Let's continue parsing
// in case we receive a message without DHO_END.
// Valid in dhcp4 space or when check is true and
// there is a sub-option configured for this code.
if ((opt_type == DHO_PAD) && (space_is_dhcp4 || flex_pad)) {
continue;
}
if (offset + 1 > buf.size()) {
// Error case.
break;
}
uint8_t opt_len = buf[offset++];
if (offset + opt_len > buf.size()) {
// Error case.
break;
}
// See below for this special case.
if (space_is_dhcp4 && opt_len == 0 && opt_type == DHO_HOST_NAME) {
continue;
}
offset += opt_len;
// Mark as seen.
if (!seen[opt_type]) {
seen[opt_type] = true;
continue;
}
// Already seen.
size_t& count = counts[opt_type];
if (count == 0) {
// Default value for size_t is 0 but this option was already seen.
count = 2;
} else {
++count;
}
}
// Fusing option buffers.
unordered_map<uint8_t, OptionBuffer> fused;
// Second pass.
offset = 0;
while (offset < buf.size()) {
// Save the current offset for backtracking
last_offset = offset;
@@ -536,6 +601,25 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
continue;
}
OptionBuffer obuf(buf.begin() + offset, buf.begin() + offset + opt_len);
offset += opt_len;
// Concatenate multiple instance of an option.
try {
size_t count = counts.at(opt_type);
OptionBuffer& previous = fused[opt_type];
previous.insert(previous.end(), obuf.begin(), obuf.end());
if (count <= 1) {
// last occurrence: build the option.
obuf = previous;
} else {
counts[opt_type] = count - 1;
continue;
}
} catch (const std::out_of_range&) {
// Regular case.
}
// Get all definitions with the particular option code. Note
// that option code is non-unique within this container
// however at this point we expect to get one option
@@ -592,9 +676,7 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
" This will be supported once support for option spaces"
" is implemented");
} else if (num_defs == 0) {
opt = OptionPtr(new Option(Option::V4, opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
opt = OptionPtr(new Option(Option::V4, opt_type, obuf));
opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
} else {
try {
@@ -602,9 +684,7 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
// the option instance from the provided buffer chunk.
const OptionDefinitionPtr& def = *(range.first);
isc_throw_assert(def);
opt = def->optionFactory(Option::V4, opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len);
opt = def->optionFactory(Option::V4, opt_type, obuf);
} catch (const SkipThisOptionError&) {
opt.reset();
}
@@ -614,79 +694,11 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
if (opt) {
options.insert(std::make_pair(opt_type, opt));
}
offset += opt_len;
}
last_offset = offset;
return (last_offset);
}
bool
LibDHCP::fuseOptions4(OptionCollection& options) {
bool result = false;
// We need to loop until all options have been fused.
for (;;) {
uint32_t found = 0;
bool found_suboptions = false;
// Iterate over all options in the container.
for (auto const& option : options) {
OptionPtr candidate = option.second;
OptionCollection& sub_options = candidate->getMutableOptions();
// Fuse suboptions recursively, if any.
if (sub_options.size()) {
// Fusing suboptions might result in new options with multiple
// options having the same code, so we need to iterate again
// until no option needs fusing.
found_suboptions = LibDHCP::fuseOptions4(sub_options);
if (found_suboptions) {
result = true;
}
}
OptionBuffer data;
OptionCollection suboptions;
// Make a copy of the options so we can safely iterate over the
// old container.
OptionCollection copy = options;
for (auto const& old_option : copy) {
if (old_option.first == option.first) {
// Copy the option data to the buffer.
data.insert(data.end(), old_option.second->getData().begin(),
old_option.second->getData().end());
suboptions.insert(old_option.second->getOptions().begin(),
old_option.second->getOptions().end());
// Other options might need fusing, so we need to iterate
// again until no options needs fusing.
found++;
}
}
if (found > 1) {
result = true;
// Erase the old options from the new container so that only the
// new option is present.
copy.erase(option.first);
// Create new option with entire data.
OptionPtr new_option(new Option(candidate->getUniverse(),
candidate->getType(), data));
// Recreate suboptions container.
new_option->getMutableOptions() = suboptions;
// Add the new option to the new container.
copy.insert(make_pair(candidate->getType(), new_option));
// After all options have been fused and new option added,
// update the option container with the new container.
options = copy;
break;
} else {
found = 0;
}
}
// No option needs fusing, so we can exit the loop.
if ((found <= 1) && !found_suboptions) {
break;
}
}
return (result);
}
namespace { // Anonymous namespace.
// VIVCO part of extendVendorOptions4.

View File

@@ -291,14 +291,6 @@ public:
size_t* relay_msg_offset = 0,
size_t* relay_msg_len = 0);
/// @brief Fuse multiple options with the same option code in long options
/// (RFC3396).
///
/// @param options The option container which needs to be updated with fused
/// options.
/// @return True if any option has been fused, false otherwise.
static bool fuseOptions4(isc::dhcp::OptionCollection& options);
/// @brief Extend vendor options from fused options in multiple OptionVendor
/// or OptionVendorClass options and add respective suboptions or values.
///

View File

@@ -222,14 +222,6 @@ Pkt4::unpack() {
// }
(void)offset;
// The RFC3396 adds support for multiple options using the same code fused
// into long options.
// All instances of the same option are fused together, including merging
// the suboption lists and fusing suboptions. As a result, the options will
// store more than 255 bytes of data and the regex parsers can effectively
// access the entire data.
LibDHCP::fuseOptions4(options_);
// Kea supports multiple vendor options so it needs to split received and
// fused options in multiple OptionVendor instances.
LibDHCP::extendVendorOptions4(options_);

View File

@@ -1548,7 +1548,7 @@ TEST_F(LibDhcpTest, fuseLongOption) {
col.insert(std::make_pair(231, option));
}
ASSERT_EQ(256, col.size());
LibDHCP::fuseOptions4(col);
//// LibDHCP::fuseOptions4(col);
ASSERT_EQ(1, col.size());
uint8_t index = 0;
@@ -1588,7 +1588,7 @@ TEST_F(LibDhcpTest, fuseLongOptionWithLongSuboption) {
col.insert(std::make_pair(213, rai));
ASSERT_EQ(1, col.size());
ASSERT_EQ(256, col.begin()->second->getOptions().size());
LibDHCP::fuseOptions4(col);
//// LibDHCP::fuseOptions4(col);
ASSERT_EQ(1, col.size());
ASSERT_EQ(1, col.begin()->second->getOptions().size());
@@ -1644,7 +1644,7 @@ TEST_F(LibDhcpTest, extendVivco) {
options.insert(make_pair(DHO_VIVCO_SUBOPTIONS, opt2));
options.insert(make_pair(DHO_VIVCO_SUBOPTIONS, opt3));
EXPECT_EQ(options.size(), 3);
EXPECT_NO_THROW(LibDHCP::fuseOptions4(options));
//// LibDHCP::fuseOptions4(options);
EXPECT_EQ(options.size(), 1);
EXPECT_NO_THROW(LibDHCP::extendVendorOptions4(options));
EXPECT_EQ(options.size(), 2);
@@ -1712,7 +1712,7 @@ TEST_F(LibDhcpTest, extendVivso) {
option.second->getType(),
buffer))));
}
ASSERT_NO_THROW(LibDHCP::fuseOptions4(options));
//// LibDHCP::fuseOptions4(options);
ASSERT_EQ(options.size(), 1);
ASSERT_EQ(options.count(DHO_VIVSO_SUBOPTIONS), 1);
ASSERT_EQ(options.find(DHO_VIVSO_SUBOPTIONS)->second->getType(), DHO_VIVSO_SUBOPTIONS);