From: Razvan Becheriu Date: Sun, 25 Jun 2023 17:27:09 +0000 (+0300) Subject: [#2942] make splitOptions4 thread safe X-Git-Tag: Kea-2.4.0~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0b883b66742e38af91f6654b6d397dd3c80ca5e2;p=thirdparty%2Fkea.git [#2942] make splitOptions4 thread safe --- diff --git a/src/bin/shell/tests/basic_auth_tests.sh.in b/src/bin/shell/tests/basic_auth_tests.sh.in index 1eebdf6e71..590c666116 100644 --- a/src/bin/shell/tests/basic_auth_tests.sh.in +++ b/src/bin/shell/tests/basic_auth_tests.sh.in @@ -193,4 +193,4 @@ shell_command_test "shell.bad-auth" \ "Failed to run: HTTP Error 401: Unauthorized" shell_command_test "shell.authorized" \ "--auth-user pet --auth-password meow" "list-commands" "" \ - "[ { \"arguments\": [ \"build-report\", \"config-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"list-commands\", \"shutdown\", \"status-get\", \"version-get\" ], \"result\": 0 } ]" + "[ { \"arguments\": [ \"build-report\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"list-commands\", \"shutdown\", \"status-get\", \"version-get\" ], \"result\": 0 } ]" diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index f8ac769ed6..9334249647 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -436,7 +436,7 @@ LibDHCP::unpackOptions6(const OptionBuffer& buf, const string& option_space, opt = def->optionFactory(Option::V6, opt_type, buf.begin() + offset, buf.begin() + offset + opt_len); - } catch (const SkipThisOptionError&) { + } catch (const SkipThisOptionError&) { opt.reset(); } } @@ -516,7 +516,7 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space, return (last_offset); } - uint8_t opt_len = buf[offset++]; + uint8_t opt_len = buf[offset++]; if (offset + opt_len > buf.size()) { // We peeked at the option header of the next option, but // discovered that it would end up beyond buffer end, so @@ -603,7 +603,7 @@ LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space, opt = def->optionFactory(Option::V4, opt_type, buf.begin() + offset, buf.begin() + offset + opt_len); - } catch (const SkipThisOptionError&) { + } catch (const SkipThisOptionError&) { opt.reset(); } } @@ -946,7 +946,7 @@ LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf, << static_cast(opt_type)); } - uint8_t opt_len = buf[offset++]; + uint8_t opt_len = buf[offset++]; if (offset + opt_len > offset_end) { isc_throw(SkipRemainingOptionsError, "Option parse failed. Tried to parse " @@ -1064,19 +1064,47 @@ LibDHCP::splitOptions4(OptionCollection& options, OptionCollection copy = options; // Iterate over all options in the container. for (auto const& option : options) { - OptionPtr candidate = option.second; + OptionPtr candidate = option.second->clone(); OptionCollection& sub_options = candidate->getMutableOptions(); // Split suboptions recursively, if any. OptionCollection distinct_options; bool updated = false; bool found_suboptions = false; + // There are 3 cases when the total size is larger than (255 - used): + // 1. option has no suboptions and has large data + // 2. option has large suboptions and has no data + // 3. option has both options and suboptions: + // 3.1. suboptions are large and data is large + // 3.2. suboptions are large and data is small + // 3.3. suboptions are small and data is large + // 3.4. suboptions are small and data is small but combined they are large + // All other combinations reside in total size smaller than (255 - used): + // 4. no split of any suboption or data: + // 4.1 option has no suboptions and has small data + // 4.2 option has small suboptions and has no data + // 4.3 option has both small suboptions and small data + // 4.4 option has no suboptions and has no data if (sub_options.size()) { + // The 2. and 3. and 4.2 and 4.3 cases are handled here (the suboptions part). ScopedOptionsCopyPtr candidate_scoped_options(new ScopedSubOptionsCopy(candidate)); found_suboptions = LibDHCP::splitOptions4(sub_options, scoped_options, used + candidate->getHeaderLen()); + // There are 3 cases here: + // 2. option has large suboptions and has no data + // 3. option has both options and suboptions: + // 3.1. suboptions are large and data is large so there is suboption splitting + // and found_suboptions is true + // 3.2. suboptions are large and data is small so there is suboption splitting + // and found_suboptions is true + // 3.3. suboptions are small and data is large so there is no suboption splitting + // and found_suboptions is false + // 3.4. suboptions are small and data is small so there is no suboption splitting + // and found_suboptions is false but combined they are large + // 4. no split of any suboption or data // Also split if the overflow is caused by adding the suboptions // to the option data. - if (found_suboptions || candidate->len() > 255) { + if (found_suboptions || candidate->len() > (255 - used)) { + // The 2. and 3. cases are handled here (the suboptions part). updated = true; scoped_options.push_back(candidate_scoped_options); // Erase the old options from the new container so that only @@ -1100,6 +1128,7 @@ LibDHCP::splitOptions4(OptionCollection& options, } } } + // The 1. and 3. and 4. cases are handled here (the data part). // Create a new option containing only data that needs to be split // and no suboptions (which are inserted in completely separate // options which are added at the end). @@ -1125,7 +1154,16 @@ LibDHCP::splitOptions4(OptionCollection& options, // data must be split and serialized. uint32_t size = buf.getLength() - header_len; // Only split if data does not fit in the current option. + // There are 3 cases here: + // 1. option has no suboptions and has large data + // 3. option has both options and suboptions: + // 3.1. suboptions are large and data is large + // 3.2. suboptions are large and data is small + // 3.3. suboptions are small and data is large + // 3.4. suboptions are small and data is small but combined they are large + // 4. no split of any suboption or data if (size > len) { + // The 1. and 3.1. and 3.3 cases are handled here (the data part). // Erase the old option from the new container so that only new // options are present. if (!updated) { @@ -1159,7 +1197,8 @@ LibDHCP::splitOptions4(OptionCollection& options, // Add the new option to the new container. copy.insert(make_pair(candidate->getType(), new_option)); } - } else if (candidate->len() > 255 && size) { + } else if ((candidate->len() > (255 - used)) && size) { + // The 3.2 and 3.4 cases are handled here (the data part). // Also split if the overflow is caused by adding the suboptions // to the option data (which should be of non zero size). // Add the new option to the new container. @@ -1201,7 +1240,7 @@ LibDHCP::OptionFactoryRegister(Option::Universe u, uint16_t opt_type, { if (v6factories_.find(opt_type) != v6factories_.end()) { isc_throw(BadValue, "There is already DHCPv6 factory registered " - << "for option type " << opt_type); + << "for option type " << opt_type); } v6factories_[opt_type] = factory; return; @@ -1221,7 +1260,7 @@ LibDHCP::OptionFactoryRegister(Option::Universe u, uint16_t opt_type, } if (v4factories_.find(opt_type) != v4factories_.end()) { isc_throw(BadValue, "There is already DHCPv4 factory registered " - << "for option type " << opt_type); + << "for option type " << opt_type); } v4factories_[opt_type] = factory; return; diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index 2c8f321f95..a8bea8edbe 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -223,6 +224,378 @@ public: } } + /// @brief Test which verifies that split options throws if there is no + /// space left in the packet buffer. + /// + /// @param option The packet option. + static void splitOptionNoBuffer(OptionPtr option) { + isc::util::OutputBuffer buf(0); + OptionCollection col; + col.insert(std::make_pair(231, option)); + ManagedScopedOptionsCopyContainer scoped_options; + ASSERT_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_, 253), BadValue); + } + + /// @brief Test which verifies that split options works if there is only one + /// byte available for data in the packet buffer. + /// + /// @param option The packet option. + static void splitOptionOneByteLeftBuffer(OptionPtr option) { + isc::util::OutputBuffer buf(0); + Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); + OptionCollection& col = pkt->options_; + col.clear(); + col.insert(std::make_pair(231, option)); + std::string expected = pkt->toText(); + { + ScopedPkt4OptionsCopy initial_scoped_options(*pkt); + ManagedScopedOptionsCopyContainer scoped_options; + ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_, 252)); + ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); + ASSERT_NE(expected, pkt->toText()); + + ASSERT_EQ(64, col.size()); + uint8_t index = 0; + for (auto const& option : col) { + ASSERT_EQ(option.first, 231); + ASSERT_EQ(1, option.second->getData().size()); + ASSERT_EQ(index, option.second->getData()[0]); + index++; + } + } + ASSERT_EQ(expected, pkt->toText()); + } + + /// @brief Test which verifies that split options for v4 is working correctly. + /// + /// @param bottom_opt The packet option. + /// @param middle_opt The packet option. + /// @param top_opt The packet option. + static void splitOptionWithSuboptionAtLimit(OptionPtr bottom_opt, + OptionPtr middle_opt, + OptionPtr top_opt) { + uint32_t bottom_size = 128; + uint32_t middle_size = 1; + uint32_t top_size = 249; + isc::util::OutputBuffer buf(0); + Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); + OptionCollection& col = pkt->options_; + col.clear(); + col.insert(std::make_pair(170, bottom_opt)); + uint32_t index = 0; + uint8_t opt_count = 0; + std::string expected = pkt->toText(); + { + ScopedPkt4OptionsCopy initial_scoped_options(*pkt); + ManagedScopedOptionsCopyContainer scoped_options; + ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); + ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); + ASSERT_NE(expected, pkt->toText()); + + for (auto const& opt : col) { + ASSERT_LE(opt.second->len(), 255); + } + + ASSERT_EQ(3 * bottom_opt->getHeaderLen() + 2 * middle_opt->getHeaderLen() + + top_opt->getHeaderLen() + bottom_size + middle_size + top_size, + buf.getLength()); + + ASSERT_EQ(3, col.size()); + for (auto const& top_subopt : col) { + ASSERT_EQ(top_subopt.second->getType(), 170); + if (opt_count == 0) { + ASSERT_EQ(top_subopt.second->getData().size(), bottom_size); + index = 0; + for (auto const& value : top_subopt.second->getData()) { + ASSERT_EQ(value, static_cast(index)); + index++; + } + ASSERT_EQ(top_subopt.second->getOptions().size(), 0); + } else { + ASSERT_EQ(top_subopt.second->getOptions().size(), 1); + for (auto const& middle_subopt : top_subopt.second->getOptions()) { + ASSERT_EQ(middle_subopt.first, 171); + if (opt_count == 1) { + ASSERT_EQ(middle_subopt.second->getData().size(), middle_size); + index = 0; + for (auto const& value : middle_subopt.second->getData()) { + ASSERT_EQ(value, static_cast(index)); + index++; + } + ASSERT_EQ(middle_subopt.second->getOptions().size(), 0); + } else { + ASSERT_EQ(middle_subopt.second->getData().size(), 0); + ASSERT_EQ(middle_subopt.second->getOptions().size(), 1); + auto const& top_subopt = middle_subopt.second->getOptions().find(172); + ASSERT_NE(top_subopt, middle_subopt.second->getOptions().end()); + ASSERT_EQ(top_subopt->second->getType(), 172); + ASSERT_EQ(top_subopt->second->getData().size(), top_size); + index = 0; + for (auto const& value : top_subopt->second->getData()) { + ASSERT_EQ(value, static_cast(index)); + index++; + } + ASSERT_EQ(top_subopt->second->getOptions().size(), 0); + } + } + } + opt_count++; + } + } + ASSERT_EQ(expected, pkt->toText()); + + OptionCollection col_back; + std::list deferred_options; + + size_t opts_len = buf.getLength(); + vector opts_buffer; + InputBuffer buffer_in(buf.getData(), opts_len); + + // Use readVector because a function which parses option requires + // a vector as an input. + buffer_in.readVector(opts_buffer, opts_len); + ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, + col_back, deferred_options)); + + ASSERT_EQ(3, col_back.size()); + opt_count = 0; + for (auto const& top_subopt : col_back) { + ASSERT_EQ(top_subopt.second->getType(), 170); + if (opt_count == 0) { + ASSERT_EQ(top_subopt.second->getData().size(), bottom_size); + index = 0; + for (auto const& value : top_subopt.second->getData()) { + ASSERT_EQ(value, static_cast(index)); + index++; + } + ASSERT_EQ(top_subopt.second->getOptions().size(), 0); + } else { + ASSERT_EQ(top_subopt.second->getOptions().size(), 0); + index = 171; + for (auto const& value : top_subopt.second->getData()) { + ASSERT_EQ(value, static_cast(index)); + if (index == 171 && opt_count == 1) { + index = middle_size; + } else if (index == middle_size && opt_count == 1) { + index = 0; + } else if (index == 171 && opt_count == 2) { + index = top_size + top_opt->getHeaderLen(); + } else if (index == top_size + top_opt->getHeaderLen() && opt_count == 2) { + index = 172; + opt_count++; + } else if (index == 172 && opt_count == 3) { + index = top_size; + } else if (index == top_size && opt_count == 3) { + index = 0; + opt_count++; + } else { + index++; + } + } + } + opt_count++; + } + } + + /// @brief Test which verifies that split options for v4 is working correctly. + /// + /// @param option The packet option. + static void splitLongOption(OptionPtr option) { + isc::util::OutputBuffer buf(0); + Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); + OptionCollection& col = pkt->options_; + col.clear(); + col.insert(std::make_pair(231, option)); + std::string expected = pkt->toText(); + { + ScopedPkt4OptionsCopy initial_scoped_options(*pkt); + ManagedScopedOptionsCopyContainer scoped_options; + ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); + ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); + ASSERT_NE(expected, pkt->toText()); + + ASSERT_EQ(11, col.size()); + ASSERT_EQ(2560 + 11 * option->getHeaderLen(), buf.getLength()); + } + ASSERT_EQ(expected, pkt->toText()); + + OptionCollection col_back; + std::list deferred_options; + + size_t opts_len = buf.getLength(); + vector opts_buffer; + InputBuffer buffer_in(buf.getData(), opts_len); + + // Use readVector because a function which parses option requires + // a vector as an input. + buffer_in.readVector(opts_buffer, opts_len); + ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, + col_back, deferred_options)); + + uint32_t index = 0; + ASSERT_EQ(11, col_back.size()); + for (auto const& option : col_back) { + ASSERT_EQ(option.first, 231); + for (auto const& value : option.second->getData()) { + ASSERT_EQ(value, static_cast(index)); + index++; + } + } + ASSERT_EQ(index, 2560); + } + + /// @brief Test which verifies that split options for v4 is working correctly + /// even if every suboption is smaller than 255 bytes, but the parent option + /// still overflows. + /// + /// @param rai The packet option. + /// @param circuit_id_opt The packet option. + /// @param remote_id_opt The packet option. + /// @param subscriber_id_opt The packet option. + static void splitOptionWithSuboptionWhichOverflow(OptionPtr rai, + OptionPtr circuit_id_opt, + OptionPtr remote_id_opt, + OptionPtr subscriber_id_opt) { + isc::util::OutputBuffer buf(0); + Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); + OptionCollection& col = pkt->options_; + col.clear(); + col.insert(std::make_pair(DHO_DHCP_AGENT_OPTIONS, rai)); + std::string expected = pkt->toText(); + { + ScopedPkt4OptionsCopy initial_scoped_options(*pkt); + ManagedScopedOptionsCopyContainer scoped_options; + ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); + ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); + ASSERT_NE(expected, pkt->toText()); + + ASSERT_EQ(3, col.size()); + ASSERT_EQ(3 * rai->getHeaderLen() + circuit_id_opt->getHeaderLen() + + remote_id_opt->getHeaderLen() + subscriber_id_opt->getHeaderLen() + + 3 * 128, buf.getLength()); + } + ASSERT_EQ(expected, pkt->toText()); + + OptionCollection col_back; + std::list deferred_options; + + size_t opts_len = buf.getLength(); + vector opts_buffer; + InputBuffer buffer_in(buf.getData(), opts_len); + + // Use readVector because a function which parses option requires + // a vector as an input. + buffer_in.readVector(opts_buffer, opts_len); + ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, + col_back, deferred_options)); + + uint8_t index = 0; + uint8_t opt_number = 0; + uint32_t opt_type = RAI_OPTION_AGENT_CIRCUIT_ID; + ASSERT_EQ(3, col_back.size()); + for (auto const& option : col_back) { + ASSERT_EQ(option.first, DHO_DHCP_AGENT_OPTIONS); + for (auto const& sub_option : option.second->getOptions()) { + if (sub_option.first != opt_type) { + opt_type = sub_option.first; + ASSERT_EQ(index, 128); + index = 0; + opt_number++; + } + if (opt_number == 0) { + ASSERT_EQ(sub_option.first, RAI_OPTION_AGENT_CIRCUIT_ID); + } else if (opt_number == 1) { + ASSERT_EQ(sub_option.first, RAI_OPTION_REMOTE_ID); + } else if (opt_number == 2){ + ASSERT_EQ(sub_option.first, RAI_OPTION_SUBSCRIBER_ID); + } + for (auto const& value : sub_option.second->getData()) { + ASSERT_EQ(value, index); + index++; + } + } + } + ASSERT_EQ(index, 128); + } + + /// @brief Test which verifies that split options for v4 is working correctly. + /// + /// @param rai The packet option. + /// @param circuit_id_opt The packet option. + /// @param remote_id_opt The packet option. + /// @param subscriber_id_opt The packet option. + void splitLongOptionWithLongSuboption(OptionPtr rai, + OptionPtr circuit_id_opt, + OptionPtr remote_id_opt, + OptionPtr subscriber_id_opt) { + isc::util::OutputBuffer buf(0); + Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); + OptionCollection& col = pkt->options_; + col.clear(); + col.insert(std::make_pair(DHO_DHCP_AGENT_OPTIONS, rai)); + std::string expected = pkt->toText(); + { + ScopedPkt4OptionsCopy initial_scoped_options(*pkt); + ManagedScopedOptionsCopyContainer scoped_options; + ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); + ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); + ASSERT_NE(expected, pkt->toText()); + + ASSERT_EQ(23, col.size()); + ASSERT_EQ((11 + 1 + 11) * rai->getHeaderLen() + 11 * circuit_id_opt->getHeaderLen() + + remote_id_opt->getHeaderLen() + 11 * subscriber_id_opt->getHeaderLen() + + 2560 + 64 + 2560, buf.getLength()); + } + ASSERT_EQ(expected, pkt->toText()); + + OptionCollection col_back; + std::list deferred_options; + + size_t opts_len = buf.getLength(); + vector opts_buffer; + InputBuffer buffer_in(buf.getData(), opts_len); + + // Use readVector because a function which parses option requires + // a vector as an input. + buffer_in.readVector(opts_buffer, opts_len); + ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, + col_back, deferred_options)); + + uint32_t index = 0; + uint8_t opt_number = 0; + uint32_t opt_type = RAI_OPTION_AGENT_CIRCUIT_ID; + ASSERT_EQ(23, col_back.size()); + for (auto const& option : col_back) { + ASSERT_EQ(option.first, DHO_DHCP_AGENT_OPTIONS); + for (auto const& sub_option : option.second->getOptions()) { + if (sub_option.first != opt_type) { + opt_type = sub_option.first; + if (opt_number == 0) { + ASSERT_EQ(index, 2560); + } else if (opt_number == 1) { + ASSERT_EQ(index, 64); + } else if (opt_number == 2){ + ASSERT_EQ(index, 2560); + } + index = 0; + opt_number++; + } + if (opt_number == 0) { + ASSERT_EQ(sub_option.first, RAI_OPTION_AGENT_CIRCUIT_ID); + } else if (opt_number == 1) { + ASSERT_EQ(sub_option.first, RAI_OPTION_REMOTE_ID); + } else if (opt_number == 2){ + ASSERT_EQ(sub_option.first, RAI_OPTION_SUBSCRIBER_ID); + } + for (auto const& value : sub_option.second->getData()) { + ASSERT_EQ(value, static_cast(index)); + index++; + } + } + } + ASSERT_EQ(index, 2560); + } + private: /// @brief Test DHCPv4 or DHCPv6 option definition. @@ -311,7 +684,6 @@ const uint8_t v6packed[] = { 0x04, 0x02, // CM MAC Address Suboption 0x00, 0x06, // Length 0x74, 0x56, 0x12, 0x29, 0x97, 0xD0, // Actual MAC Address - }; TEST_F(LibDhcpTest, optionFactory) { @@ -428,7 +800,6 @@ TEST_F(LibDhcpTest, packOptions6) { } TEST_F(LibDhcpTest, unpackOptions6) { - // just couple of random options // Option is used as a simple option implementation // More advanced uses are validated in tests dedicated for @@ -691,11 +1062,41 @@ TEST_F(LibDhcpTest, splitOptionNoBuffer) { boost::shared_ptr option; ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V4, buf_in))); ASSERT_TRUE(option); - isc::util::OutputBuffer buf(0); - OptionCollection col; - col.insert(std::make_pair(231, option)); - ManagedScopedOptionsCopyContainer scoped_options; - ASSERT_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_, 253), BadValue); + + splitOptionNoBuffer(option); +} + +// This test verifies that split options throws if there is no space left in the +// packet buffer. +TEST_F(LibDhcpTest, splitOptionNoBufferMultiThreading) { + OptionDefinition opt_def("option-foo", 231, "my-space", "binary", + "option-foo-space"); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + OptionBuffer buf_in(2560); + for (uint32_t i = 0; i < 2560; ++i) { + buf_in[i] = i; + } + + boost::shared_ptr option; + ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V4, buf_in))); + ASSERT_TRUE(option); + + typedef function CallBack; + ThreadPool tp; + tp.start(256); + + for (uint32_t count = 0; count < 1024; ++count) { + auto const& work = [&] { + splitOptionNoBuffer(option); + }; + + boost::shared_ptr call_back = boost::make_shared(work); + tp.add(call_back); + } + ASSERT_TRUE(tp.wait(5)); } // This test verifies that split options works if there is only one byte @@ -715,29 +1116,154 @@ TEST_F(LibDhcpTest, splitOptionOneByteLeftBuffer) { boost::shared_ptr option; ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V4, buf_in))); ASSERT_TRUE(option); - isc::util::OutputBuffer buf(0); - Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); - OptionCollection& col = pkt->options_; - col.clear(); - col.insert(std::make_pair(231, option)); - std::string expected = pkt->toText(); - { - ScopedPkt4OptionsCopy initial_scoped_options(*pkt); - ManagedScopedOptionsCopyContainer scoped_options; - ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_, 252)); - ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); - ASSERT_NE(expected, pkt->toText()); - ASSERT_EQ(64, col.size()); - uint8_t index = 0; - for (auto const& option : col) { - ASSERT_EQ(option.first, 231); - ASSERT_EQ(1, option.second->getData().size()); - ASSERT_EQ(index, option.second->getData()[0]); - index++; - } + splitOptionOneByteLeftBuffer(option); +} + +// This test verifies that split options works if there is only one byte +// available for data in the packet buffer. +TEST_F(LibDhcpTest, splitOptionOneByteLeftBufferMultiThreading) { + OptionDefinition opt_def("option-foo", 231, "my-space", "binary", + "option-foo-space"); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + OptionBuffer buf_in(64); + for (uint32_t i = 0; i < 64; ++i) { + buf_in[i] = i; + } + + boost::shared_ptr option; + ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V4, buf_in))); + ASSERT_TRUE(option); + + typedef function CallBack; + ThreadPool tp; + tp.start(256); + + for (uint32_t count = 0; count < 1024; ++count) { + auto const& work = [&] { + splitOptionOneByteLeftBuffer(option); + }; + + boost::shared_ptr call_back = boost::make_shared(work); + tp.add(call_back); + } + ASSERT_TRUE(tp.wait(5)); +} + +// This test verifies that split options for v4 is working correctly. +TEST_F(LibDhcpTest, splitOptionWithSuboptionAtLimit) { + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + uint32_t bottom_size = 128; + OptionBuffer bottom_buf_in(bottom_size); + for (uint32_t i = 0; i < bottom_size; ++i) { + bottom_buf_in[i] = i; + } + + OptionDefinitionPtr top_def(new OptionDefinition("top", 170, DHCP4_OPTION_SPACE, OPT_BINARY_TYPE, "miggle")); + OptionPtr bottom_opt(new OptionCustom(*top_def, Option::V4, bottom_buf_in)); + ASSERT_TRUE(bottom_opt); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + uint32_t middle_size = 1; + OptionBuffer middle_buf_in(middle_size); + for (uint32_t i = 0; i < middle_size; ++i) { + middle_buf_in[i] = i; + } + + OptionDefinitionPtr middle_def(new OptionDefinition("top", 171, "middle", OPT_BINARY_TYPE, "")); + OptionPtr middle_opt(new OptionCustom(*middle_def, Option::V4, middle_buf_in)); + ASSERT_TRUE(middle_opt); + bottom_opt->addOption(middle_opt); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + uint32_t top_size = 249; + OptionBuffer top_buf_in(top_size); + for (uint32_t i = 0; i < top_size; ++i) { + top_buf_in[i] = i; + } + + OptionPtr top_opt(new Option(Option::V4, 172, top_buf_in)); + ASSERT_TRUE(top_opt); + middle_opt->addOption(top_opt); + + OptionDefSpaceContainer defs; + defs.addItem(top_def); + defs.addItem(middle_def); + LibDHCP::setRuntimeOptionDefs(defs); + LibDHCP::commitRuntimeOptionDefs(); + + splitOptionWithSuboptionAtLimit(bottom_opt, middle_opt, top_opt); +} + +// This test verifies that split options for v4 is working correctly. +TEST_F(LibDhcpTest, splitOptionWithSuboptionAtLimitMultiThreading) { + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + uint32_t bottom_size = 128; + OptionBuffer bottom_buf_in(bottom_size); + for (uint32_t i = 0; i < bottom_size; ++i) { + bottom_buf_in[i] = i; + } + + OptionDefinitionPtr top_def(new OptionDefinition("top", 170, DHCP4_OPTION_SPACE, OPT_BINARY_TYPE, "miggle")); + OptionPtr bottom_opt(new OptionCustom(*top_def, Option::V4, bottom_buf_in)); + ASSERT_TRUE(bottom_opt); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + uint32_t middle_size = 1; + OptionBuffer middle_buf_in(middle_size); + for (uint32_t i = 0; i < middle_size; ++i) { + middle_buf_in[i] = i; + } + + OptionDefinitionPtr middle_def(new OptionDefinition("top", 171, "middle", OPT_BINARY_TYPE, "")); + OptionPtr middle_opt(new OptionCustom(*middle_def, Option::V4, middle_buf_in)); + ASSERT_TRUE(middle_opt); + bottom_opt->addOption(middle_opt); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + uint32_t top_size = 249; + OptionBuffer top_buf_in(top_size); + for (uint32_t i = 0; i < top_size; ++i) { + top_buf_in[i] = i; + } + + OptionPtr top_opt(new Option(Option::V4, 172, top_buf_in)); + ASSERT_TRUE(top_opt); + middle_opt->addOption(top_opt); + + OptionDefSpaceContainer defs; + defs.addItem(top_def); + defs.addItem(middle_def); + LibDHCP::setRuntimeOptionDefs(defs); + LibDHCP::commitRuntimeOptionDefs(); + + typedef function CallBack; + ThreadPool tp; + tp.start(256); + + for (uint32_t count = 0; count < 1024; ++count) { + auto const& work = [&] { + splitOptionWithSuboptionAtLimit(bottom_opt, middle_opt, top_opt); + }; + boost::shared_ptr call_back = boost::make_shared(work); + tp.add(call_back); } - ASSERT_EQ(expected, pkt->toText()); + ASSERT_TRUE(tp.wait(5)); } // This test verifies that split options for v4 is working correctly. @@ -756,47 +1282,40 @@ TEST_F(LibDhcpTest, splitLongOption) { boost::shared_ptr option; ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V4, buf_in))); ASSERT_TRUE(option); - isc::util::OutputBuffer buf(0); - Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); - OptionCollection& col = pkt->options_; - col.clear(); - col.insert(std::make_pair(231, option)); - std::string expected = pkt->toText(); - { - ScopedPkt4OptionsCopy initial_scoped_options(*pkt); - ManagedScopedOptionsCopyContainer scoped_options; - ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); - ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); - ASSERT_NE(expected, pkt->toText()); - ASSERT_EQ(11, col.size()); - ASSERT_EQ(2560 + 11 * option->getHeaderLen(), buf.getLength()); + splitLongOption(option); +} + +// This test verifies that split options for v4 is working correctly. +TEST_F(LibDhcpTest, splitLongOptionMultiThreading) { + OptionDefinition opt_def("option-foo", 231, "my-space", "binary", + "option-foo-space"); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + OptionBuffer buf_in(2560); + for (uint32_t i = 0; i < 2560; ++i) { + buf_in[i] = i; } - ASSERT_EQ(expected, pkt->toText()); - OptionCollection col_back; - std::list deferred_options; + boost::shared_ptr option; + ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V4, buf_in))); + ASSERT_TRUE(option); - size_t opts_len = buf.getLength(); - vector opts_buffer; - InputBuffer buffer_in(buf.getData(), opts_len); + typedef function CallBack; + ThreadPool tp; + tp.start(256); - // Use readVector because a function which parses option requires - // a vector as an input. - buffer_in.readVector(opts_buffer, opts_len); - ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, - col_back, deferred_options)); + for (uint32_t count = 0; count < 1024; ++count) { + auto const& work = [&] { + splitLongOption(option); + }; - uint32_t index = 0; - ASSERT_EQ(11, col_back.size()); - for (auto const& option : col_back) { - ASSERT_EQ(option.first, 231); - for (auto const& value : option.second->getData()) { - ASSERT_EQ(value, static_cast(index)); - index++; - } + boost::shared_ptr call_back = boost::make_shared(work); + tp.add(call_back); } - ASSERT_EQ(index, 2560); + ASSERT_TRUE(tp.wait(5)); } // This test verifies that split options for v4 is working correctly even if @@ -832,66 +1351,55 @@ TEST_F(LibDhcpTest, splitOptionWithSuboptionWhichOverflow) { ASSERT_TRUE(subscriber_id_opt); rai->addOption(subscriber_id_opt); - isc::util::OutputBuffer buf(0); - Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); - OptionCollection& col = pkt->options_; - col.clear(); - col.insert(std::make_pair(DHO_DHCP_AGENT_OPTIONS, rai)); - std::string expected = pkt->toText(); - { - ScopedPkt4OptionsCopy initial_scoped_options(*pkt); - ManagedScopedOptionsCopyContainer scoped_options; - ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); - ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); - ASSERT_NE(expected, pkt->toText()); - - ASSERT_EQ(3, col.size()); - ASSERT_EQ(3 * rai->getHeaderLen() + circuit_id_opt->getHeaderLen() + - remote_id_opt->getHeaderLen() + subscriber_id_opt->getHeaderLen() + - 3 * 128, buf.getLength()); + splitOptionWithSuboptionWhichOverflow(rai, circuit_id_opt, remote_id_opt, subscriber_id_opt); +} + +// This test verifies that split options for v4 is working correctly even if +// every suboption is smaller than 255 bytes, but the parent option still +// overflows. +TEST_F(LibDhcpTest, splitOptionWithSuboptionWhichOverflowMultiThreading) { + OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, + DHO_DHCP_AGENT_OPTIONS); + ASSERT_TRUE(rai_def); + // Create RAI options which should be fused by the server. + OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4)); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + OptionBuffer buf_in(128); + for (uint32_t i = 0; i < 128; ++i) { + buf_in[i] = i; } - ASSERT_EQ(expected, pkt->toText()); - OptionCollection col_back; - std::list deferred_options; + OptionPtr circuit_id_opt(new Option(Option::V4, + RAI_OPTION_AGENT_CIRCUIT_ID, buf_in)); + ASSERT_TRUE(circuit_id_opt); + rai->addOption(circuit_id_opt); - size_t opts_len = buf.getLength(); - vector opts_buffer; - InputBuffer buffer_in(buf.getData(), opts_len); + OptionPtr remote_id_opt(new Option(Option::V4, + RAI_OPTION_REMOTE_ID, buf_in)); + ASSERT_TRUE(remote_id_opt); + rai->addOption(remote_id_opt); - // Use readVector because a function which parses option requires - // a vector as an input. - buffer_in.readVector(opts_buffer, opts_len); - ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, - col_back, deferred_options)); + OptionPtr subscriber_id_opt(new Option(Option::V4, + RAI_OPTION_SUBSCRIBER_ID, buf_in)); + ASSERT_TRUE(subscriber_id_opt); + rai->addOption(subscriber_id_opt); - uint8_t index = 0; - uint8_t opt_number = 0; - uint32_t opt_type = RAI_OPTION_AGENT_CIRCUIT_ID; - ASSERT_EQ(3, col_back.size()); - for (auto const& option : col_back) { - ASSERT_EQ(option.first, DHO_DHCP_AGENT_OPTIONS); - for (auto const& sub_option : option.second->getOptions()) { - if (sub_option.first != opt_type) { - opt_type = sub_option.first; - ASSERT_EQ(index, 128); - index = 0; - opt_number++; - } - if (opt_number == 0) { - ASSERT_EQ(sub_option.first, RAI_OPTION_AGENT_CIRCUIT_ID); - } else if (opt_number == 1) { - ASSERT_EQ(sub_option.first, RAI_OPTION_REMOTE_ID); - } else if (opt_number == 2){ - ASSERT_EQ(sub_option.first, RAI_OPTION_SUBSCRIBER_ID); - } - for (auto const& value : sub_option.second->getData()) { - ASSERT_EQ(value, index); - index++; - } - } + typedef function CallBack; + ThreadPool tp; + tp.start(256); + + for (uint32_t count = 0; count < 1024; ++count) { + auto const& work = [&] { + splitOptionWithSuboptionWhichOverflow(rai, circuit_id_opt, remote_id_opt, subscriber_id_opt); + }; + + boost::shared_ptr call_back = boost::make_shared(work); + tp.add(call_back); } - ASSERT_EQ(index, 128); + ASSERT_TRUE(tp.wait(5)); } // This test verifies that split options for v4 is working correctly. @@ -933,72 +1441,61 @@ TEST_F(LibDhcpTest, splitLongOptionWithLongSuboption) { ASSERT_TRUE(subscriber_id_opt); rai->addOption(subscriber_id_opt); - isc::util::OutputBuffer buf(0); - Pkt4Ptr pkt(new Pkt4(DHCPOFFER, 1234)); - OptionCollection& col = pkt->options_; - col.clear(); - col.insert(std::make_pair(DHO_DHCP_AGENT_OPTIONS, rai)); - std::string expected = pkt->toText(); - { - ScopedPkt4OptionsCopy initial_scoped_options(*pkt); - ManagedScopedOptionsCopyContainer scoped_options; - ASSERT_NO_THROW(LibDHCP::splitOptions4(col, scoped_options.scoped_options_)); - ASSERT_NO_THROW(LibDHCP::packOptions4(buf, col, true)); - ASSERT_NE(expected, pkt->toText()); - - ASSERT_EQ(23, col.size()); - ASSERT_EQ((11 + 1 + 11) * rai->getHeaderLen() + 11 * circuit_id_opt->getHeaderLen() + - remote_id_opt->getHeaderLen() + 11 * subscriber_id_opt->getHeaderLen() + - 2560 + 64 + 2560, buf.getLength()); + splitLongOptionWithLongSuboption(rai, circuit_id_opt, remote_id_opt, subscriber_id_opt); +} + +// This test verifies that split options for v4 is working correctly. +TEST_F(LibDhcpTest, splitLongOptionWithLongSuboptionMultiThreading) { + OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, + DHO_DHCP_AGENT_OPTIONS); + ASSERT_TRUE(rai_def); + // Create RAI options which should be fused by the server. + OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4)); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + OptionBuffer buf_in(2560); + for (uint32_t i = 0; i < 2560; ++i) { + buf_in[i] = i; } - ASSERT_EQ(expected, pkt->toText()); - - OptionCollection col_back; - std::list deferred_options; - - size_t opts_len = buf.getLength(); - vector opts_buffer; - InputBuffer buffer_in(buf.getData(), opts_len); - - // Use readVector because a function which parses option requires - // a vector as an input. - buffer_in.readVector(opts_buffer, opts_len); - ASSERT_NO_THROW(LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, - col_back, deferred_options)); - - uint32_t index = 0; - uint8_t opt_number = 0; - uint32_t opt_type = RAI_OPTION_AGENT_CIRCUIT_ID; - ASSERT_EQ(23, col_back.size()); - for (auto const& option : col_back) { - ASSERT_EQ(option.first, DHO_DHCP_AGENT_OPTIONS); - for (auto const& sub_option : option.second->getOptions()) { - if (sub_option.first != opt_type) { - opt_type = sub_option.first; - if (opt_number == 0) { - ASSERT_EQ(index, 2560); - } else if (opt_number == 1) { - ASSERT_EQ(index, 64); - } else if (opt_number == 2){ - ASSERT_EQ(index, 2560); - } - index = 0; - opt_number++; - } - if (opt_number == 0) { - ASSERT_EQ(sub_option.first, RAI_OPTION_AGENT_CIRCUIT_ID); - } else if (opt_number == 1) { - ASSERT_EQ(sub_option.first, RAI_OPTION_REMOTE_ID); - } else if (opt_number == 2){ - ASSERT_EQ(sub_option.first, RAI_OPTION_SUBSCRIBER_ID); - } - for (auto const& value : sub_option.second->getData()) { - ASSERT_EQ(value, static_cast(index)); - index++; - } - } + + OptionPtr circuit_id_opt(new Option(Option::V4, + RAI_OPTION_AGENT_CIRCUIT_ID, buf_in)); + ASSERT_TRUE(circuit_id_opt); + rai->addOption(circuit_id_opt); + + // Create a buffer holding some binary data. This data will be + // used as reference when we read back the data from a created + // option. + OptionBuffer small_buf_in(64); + for (uint32_t i = 0; i < 64; ++i) { + small_buf_in[i] = i; } - ASSERT_EQ(index, 2560); + + OptionPtr remote_id_opt(new Option(Option::V4, + RAI_OPTION_REMOTE_ID, small_buf_in)); + ASSERT_TRUE(remote_id_opt); + rai->addOption(remote_id_opt); + + OptionPtr subscriber_id_opt(new Option(Option::V4, + RAI_OPTION_SUBSCRIBER_ID, buf_in)); + ASSERT_TRUE(subscriber_id_opt); + rai->addOption(subscriber_id_opt); + + typedef function CallBack; + ThreadPool tp; + tp.start(256); + + for (uint32_t count = 0; count < 1024; ++count) { + auto const& work = [&] { + splitLongOptionWithLongSuboption(rai, circuit_id_opt, remote_id_opt, subscriber_id_opt); + }; + + boost::shared_ptr call_back = boost::make_shared(work); + tp.add(call_back); + } + ASSERT_TRUE(tp.wait(5)); } // This test verifies that fuse options for v4 is working correctly. @@ -1235,7 +1732,6 @@ TEST_F(LibDhcpTest, extendVivso) { // This test verifies that pack options for v4 is working correctly. TEST_F(LibDhcpTest, packOptions4) { - vector payload[5]; for (unsigned i = 0; i < 5; i++) { payload[i].resize(3); @@ -1313,7 +1809,6 @@ TEST_F(LibDhcpTest, packOptions4) { // This test verifies that pack options for v4 is working correctly // and RAI option is packed last. TEST_F(LibDhcpTest, packOptions4Order) { - uint8_t expected[] = { 12, 3, 0, 1, 2, // Just a random option 99, 3, 10, 11, 12, // Another random option @@ -1346,7 +1841,6 @@ TEST_F(LibDhcpTest, packOptions4Order) { } TEST_F(LibDhcpTest, unpackOptions4) { - vector v4packed(v4_opts, v4_opts + sizeof(v4_opts)); isc::dhcp::OptionCollection options; // list of options list deferred; @@ -1784,7 +2278,6 @@ TEST_F(LibDhcpTest, option43End) { // There should be 0 suboptions. EXPECT_EQ(0, options.size()); - // Create option definition for option 255. OptionDefinitionPtr opt_def255(new OptionDefinition("max", 255, space, "uint8")); @@ -1851,7 +2344,6 @@ TEST_F(LibDhcpTest, option43Factory) { // Verifies that an Host Name (option 12), will be dropped when empty, // while subsequent options will still be unpacked. TEST_F(LibDhcpTest, emptyHostName) { - uint8_t opts[] = { 12, 0, // Empty Hostname 60, 3, 10, 11, 12 // Class Id @@ -1878,9 +2370,7 @@ TEST_F(LibDhcpTest, emptyHostName) { EXPECT_EQ(0, memcmp(&x->second->getData()[0], opts + 4, 3)); }; - TEST_F(LibDhcpTest, stdOptionDefs4) { - // Create a buffer that holds dummy option data. // It will be used to create most of the options. std::vector buf(48, 1); @@ -2349,7 +2839,6 @@ TEST_F(LibDhcpTest, stdOptionDefs4) { // This test have to be extended once all option definitions are // created. TEST_F(LibDhcpTest, stdOptionDefs6) { - // Create a buffer that holds dummy option data. // It will be used to create most of the options. std::vector buf(48, 1); @@ -2702,7 +3191,6 @@ TEST_F(LibDhcpTest, getOptionDefByName6) { } } - // This test checks if the DHCPv4 option definition can be searched by // an option name. TEST_F(LibDhcpTest, getOptionDefByName4) { @@ -2870,7 +3358,6 @@ TEST_F(LibDhcpTest, fqdnListTrunc) { // tests whether v6 vendor-class option can be parsed properly. TEST_F(LibDhcpTest, vendorClass6) { - isc::dhcp::OptionCollection options; // Will store parsed option here // Exported from wireshark: vendor-class option with enterprise-id = 4491 @@ -2962,8 +3449,6 @@ TEST_F(LibDhcpTest, option43) { // These options are have complex structure, so dedicated tests are needed // to test them reliably. TEST_F(LibDhcpTest, sw46options) { - - // This constant defines the following structure: // MAP-E container // - BR address option diff --git a/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc index 6ada1aaeb2..74d668e28f 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc @@ -182,7 +182,7 @@ MySqlExtendedInfoTest::testInitLease4() { // Use the page version as it returns leases in order. EXPECT_NO_THROW(got = lease_mgr_->getLeases4(zero, LeasePageSize(100))); ASSERT_EQ(leases4.size(), got.size()); - auto compare = [](Lease4Ptr& left, Lease4Ptr& right) { + auto compare = [](Lease4Ptr const& left, Lease4Ptr const& right) { return (left->addr_ < right->addr_); }; std::sort(got.begin(), got.end(), compare); @@ -1011,7 +1011,7 @@ MySqlExtendedInfoTest::testInitLease6() { Lease6Collection got; EXPECT_NO_THROW(got = lease_mgr_->getLeases6()); ASSERT_EQ(leases6.size(), got.size()); - auto compare = [](Lease6Ptr& left, Lease6Ptr& right) { + auto compare = [](Lease6Ptr const& left, Lease6Ptr const& right) { return (left->addr_ < right->addr_); }; std::sort(got.begin(), got.end(), compare); diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc index 9c4f36484c..7107632428 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc @@ -182,7 +182,7 @@ PgSqlExtendedInfoTest::testInitLease4() { // Use the page version as it returns leases in order. EXPECT_NO_THROW(got = lease_mgr_->getLeases4(zero, LeasePageSize(100))); ASSERT_EQ(leases4.size(), got.size()); - auto compare = [](Lease4Ptr& left, Lease4Ptr& right) { + auto compare = [](Lease4Ptr const& left, Lease4Ptr const& right) { return (left->addr_ < right->addr_); }; std::sort(got.begin(), got.end(), compare); @@ -1011,7 +1011,7 @@ PgSqlExtendedInfoTest::testInitLease6() { Lease6Collection got; EXPECT_NO_THROW(got = lease_mgr_->getLeases6()); ASSERT_EQ(leases6.size(), got.size()); - auto compare = [](Lease6Ptr& left, Lease6Ptr& right) { + auto compare = [](Lease6Ptr const& left, Lease6Ptr const& right) { return (left->addr_ < right->addr_); }; std::sort(got.begin(), got.end(), compare);