Commit aae2d91f authored by Marcin Siodelski's avatar Marcin Siodelski

[master] Merge branch 'trac5376'

parents 8f97f3d5 1cba2443
......@@ -1328,9 +1328,11 @@ This rather belong to the DDNS configuration
<row><entry>dhcp-lease-time</entry><entry>51</entry><entry>uint32</entry><entry>false</entry><entry>true</entry></row>
-->
<row><entry>dhcp-option-overload</entry><entry>52</entry><entry>uint8</entry><entry>false</entry><entry>false</entry></row>
<!-- Message Type, Server Identifier and Parameter Request List should not be configured by a user.
<!-- Message Type should not be configured by a user.
<row><entry>dhcp-message-type</entry><entry>53</entry><entry>uint8</entry><entry>false</entry><entry>false</entry></row>
-->
<row><entry>dhcp-server-identifier</entry><entry>54</entry><entry>ipv4-address</entry><entry>false</entry><entry>true</entry></row>
<!-- Parameter Request List should not be configured by a user.
<row><entry>dhcp-parameter-request-list</entry><entry>55</entry><entry>uint8</entry><entry>true</entry><entry>true</entry></row>
-->
<row><entry>dhcp-message</entry><entry>56</entry><entry>string</entry><entry>false</entry><entry>false</entry></row>
......@@ -3847,11 +3849,31 @@ i.e. it will assign increasing integer values starting from 1.</para>
received. A single server instance will use multiple server identifiers
if it is receiving queries on multiple interfaces.
</para>
<para>
Currently there is no mechanism to override the default server identifiers
by an administrator. In the future, the configuration mechanism will be used
to specify the custom server identifier.
It is possible to override default server identifier values by specifying
"dhcp-server-identifier" option. This option is only supported on the
global, shared network and subnet level. It must not be specified
on client class and host reservation level.
</para>
<para>
The following example demonstrates how to override server identifier for
a subnet:
<screen>
"subnet4": [
{
"subnet": "192.0.2.0/24",
"option-data": [
{
"name": "dhcp-server-identifier",
"data": "10.2.5.76"
}
],
...
}
]</screen>
</para>
</section>
<section id="dhcp4-subnet-selection">
......
......@@ -11,6 +11,7 @@
#include <dhcp/iface_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_vendor.h>
......@@ -27,6 +28,7 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/cfg_host_operations.h>
#include <dhcpsrv/cfg_iface.h>
#include <dhcpsrv/cfg_shared_networks.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
......@@ -62,6 +64,7 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/pointer_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <iomanip>
......@@ -1142,6 +1145,12 @@ Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
void
Dhcpv4Srv::appendServerID(Dhcpv4Exchange& ex) {
// Do not append generated server identifier if there is one appended already.
// This is when explicitly configured server identifier option is present.
if (ex.getResponse()->getOption(DHO_DHCP_SERVER_IDENTIFIER)) {
return;
}
// Use local address on which the packet has been received as a
// server identifier. In some cases it may be a different address,
// e.g. broadcast packet or DHCPv4o6 packet.
......@@ -1386,7 +1395,8 @@ Dhcpv4Srv::appendBasicOptions(Dhcpv4Exchange& ex) {
static const uint16_t required_options[] = {
DHO_ROUTERS,
DHO_DOMAIN_NAME_SERVERS,
DHO_DOMAIN_NAME };
DHO_DOMAIN_NAME,
DHO_DHCP_SERVER_IDENTIFIER };
static size_t required_options_size =
sizeof(required_options) / sizeof(required_options[0]);
......@@ -2817,7 +2827,53 @@ Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const {
// performance hit should be acceptable. If it turns out to
// be significant, we will have to cache server identifiers
// when sockets are opened.
return (IfaceMgr::instance().hasOpenSocket(server_id));
if (IfaceMgr::instance().hasOpenSocket(server_id)) {
return (true);
}
// There are some cases when an administrator explicitly sets server
// identifier (option 54) that should be used for a given, subnet,
// network etc. It doesn't have to be an address assigned to any of
// the server interfaces. Thus, we have to check if the server
// identifier received is the one that we explicitly set in the
// server configuration. At this point, we don't know which subnet
// the client belongs to so we can't match the server id with any
// subnet. We simply check if this server identifier is configured
// anywhere. This should be good enough to eliminate exchanges
// with other servers in the same network.
/// @todo Currently we only check subnet identifiers configured on the
/// subnet level, shared network level and global level. This should
/// be sufficient for most of cases. At this point, trying to support
/// server identifiers on the class level seems to be an overkill and
/// is probably not needed. Same with host reservations. In fact,
/// at this point we don't know the reservations for the client
/// communicating with the server. We may revise some of these choices
/// in the future.
SrvConfigPtr cfg = CfgMgr::instance().getCurrentCfg();
// Check if there is at least one subnet configured with this server
// identifier.
ConstCfgSubnets4Ptr cfg_subnets = cfg->getCfgSubnets4();
if (cfg_subnets->hasSubnetWithServerId(server_id)) {
return (true);
}
// This server identifier is not configured for any of the subnets, so
// check on the shared network level.
CfgSharedNetworks4Ptr cfg_networks = cfg->getCfgSharedNetworks4();
if (cfg_networks->hasNetworkWithServerId(server_id)) {
return (true);
}
// Finally, it is possible that the server identifier is specified
// on the global level.
ConstCfgOptionPtr cfg_global_options = cfg->getCfgOption();
OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast<OptionCustom>
(cfg_global_options->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_);
return (opt_server_id && (opt_server_id->readAddress() == server_id));
}
void
......
......@@ -525,7 +525,8 @@ protected:
/// - Subnet Mask,
/// - Router,
/// - Name Server,
/// - Domain Name.
/// - Domain Name,
/// - Server Identifier.
///
/// @param ex DHCPv4 exchange holding the client's message to be checked.
void appendBasicOptions(Dhcpv4Exchange& ex);
......@@ -681,10 +682,11 @@ protected:
/// @brief Adds server identifier option to the server's response.
///
/// This method adds a server identifier to the DHCPv4 message. This is set
/// to the local address on which the client's query has been received with
/// the exception of broadcast traffic and DHCPv4o6 query for which a socket
/// on the particular interface is found and its address is used as server id.
/// This method adds a server identifier to the DHCPv4 message if it doesn't
/// exist yet. This is set to the local address on which the client's query has
/// been received with the exception of broadcast traffic and DHCPv4o6 query for
/// which a socket on the particular interface is found and its address is used
/// as server id.
///
/// @note This method doesn't throw exceptions by itself but the underlying
/// classes being used my throw. The reason for this method to not sanity
......
......@@ -87,14 +87,20 @@ namespace {
/// - boot-file-name = "bootfile.efi"
///
/// - Configuration 7:
/// - Used for testing custom value of dhcp-server-identifier option.
/// - 3 subnets: 10.0.0.0/24, 192.0.2.0/26 and 192.0.2.64/26
/// - Custom server identifier specified for 2 subnets subnet.
/// - Custom server identifier specified at global level.
///
/// - Configuration 8:
/// - Simple configuration with a single subnet and single pool
/// - Using MySQL lease database backend to store leases
///
/// - Configuration 8:
/// - Configuration 9:
/// - Simple configuration with a single subnet and single pool
/// - Using PostgreSQL lease database backend to store leases
///
/// - Configuration 9:
/// - Configuration 10:
/// - Simple configuration with a single subnet and single pool
/// - Using Cassandra lease database backend to store leases
const char* DORA_CONFIGS[] = {
......@@ -282,6 +288,50 @@ const char* DORA_CONFIGS[] = {
"}",
// Configuration 7
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"valid-lifetime\": 600,"
"\"option-data\": ["
" {"
" \"name\": \"dhcp-server-identifier\","
" \"data\": \"3.4.5.6\""
" }"
"],"
"\"subnet4\": ["
" {"
" \"subnet\": \"10.0.0.0/24\", "
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"interface\": \"eth0\","
" \"option-data\": ["
" {"
" \"name\": \"dhcp-server-identifier\","
" \"data\": \"1.2.3.4\""
" }"
" ]"
" },"
" {"
" \"subnet\": \"192.0.2.0/26\", "
" \"pools\": [ { \"pool\": \"192.0.2.10-192.0.2.63\" } ],"
" \"interface\": \"eth1\","
" \"option-data\": ["
" {"
" \"name\": \"dhcp-server-identifier\","
" \"data\": \"2.3.4.5\""
" }"
" ]"
" },"
" {"
" \"subnet\": \"192.0.2.64/26\", "
" \"pools\": [ { \"pool\": \"192.0.2.65-192.0.2.100\" } ],"
" \"relay\": {"
" \"ip-address\": \"10.2.3.4\""
" }"
" }"
"]"
"}",
// Configuration 8
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
......@@ -299,7 +349,7 @@ const char* DORA_CONFIGS[] = {
" } ]"
"}",
// Configuration 8
// Configuration 9
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
......@@ -317,7 +367,7 @@ const char* DORA_CONFIGS[] = {
" } ]"
"}",
// Configuration 9
// Configuration 10
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
......@@ -1568,6 +1618,46 @@ TEST_F(DORATest, multiStageBoot) {
testMultiStageBoot(0);
}
// This test verifies that custom server identifier can be specified for
// a subnet.
TEST_F(DORATest, customServerIdentifier) {
Dhcp4Client client1(Dhcp4Client::SELECTING);
// Configure DHCP server.
ASSERT_NO_THROW(configure(DORA_CONFIGS[7], *client1.getServer()));
ASSERT_NO_THROW(client1.doDORA());
// Make sure that the server responded.
ASSERT_TRUE(client1.getContext().response_);
Pkt4Ptr resp = client1.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// The explicitly configured server identifier should take precedence
// over generated server identifier.
EXPECT_EQ("1.2.3.4", client1.config_.serverid_.toText());
// Repeat the test for different subnet.
Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
client2.setIfaceName("eth1");
ASSERT_NO_THROW(client2.doDORA());
ASSERT_TRUE(client2.getContext().response_);
resp = client2.getContext().response_;
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText());
// Create relayed client which will be assigned a lease from the third
// subnet. This subnet inherits server identifier value from the global
// scope.
Dhcp4Client client3(client1.getServer(), Dhcp4Client::SELECTING);
client3.useRelay(true, IOAddress("10.2.3.4"));
ASSERT_NO_THROW(client3.doDORA());
ASSERT_TRUE(client3.getContext().response_);
resp = client3.getContext().response_;
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
EXPECT_EQ("3.4.5.6", client3.config_.serverid_.toText());
}
// Starting tests which require MySQL backend availability. Those tests
// will not be executed if Kea has been compiled without the
// --with-dhcp-mysql.
......@@ -1595,8 +1685,8 @@ public:
// Test that the client using the same hardware address but multiple
// client identifiers will obtain multiple leases (MySQL lease database).
TEST_F(DORAMySQLTest, multiStageBoot) {
// DORA_CONFIGS[7] to be used for server configuration.
testMultiStageBoot(7);
// DORA_CONFIGS[9] to be used for server configuration.
testMultiStageBoot(8);
}
#endif
......@@ -1628,8 +1718,8 @@ public:
// Test that the client using the same hardware address but multiple
// client identifiers will obtain multiple leases (PostgreSQL lease database).
TEST_F(DORAPgSQLTest, multiStageBoot) {
// DORA_CONFIGS[8] to be used for server configuration.
testMultiStageBoot(8);
// DORA_CONFIGS[9] to be used for server configuration.
testMultiStageBoot(9);
}
#endif
......@@ -1658,8 +1748,8 @@ public:
// Test that the client using the same hardware address but multiple
// client identifiers will obtain multiple leases (CQL lease database).
TEST_F(DORACQLTest, multiStageBoot) {
// DORA_CONFIGS[9] to be used for server configuration.
testMultiStageBoot(9);
// DORA_CONFIGS[10] to be used for server configuration.
testMultiStageBoot(10);
}
#endif
......
......@@ -808,6 +808,59 @@ const char* NETWORKS_CONFIG[] = {
" ]"
"}",
// Configuration #15
// - two shared networks, each comes with its own server identifier.
"{"
" \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
" },"
" \"valid-lifetime\": 600,"
" \"shared-networks\": ["
" {"
" \"name\": \"frog\","
" \"interface\": \"eth1\","
" \"option-data\": ["
" {"
" \"name\": \"dhcp-server-identifier\","
" \"data\": \"1.2.3.4\""
" }"
" ],"
" \"subnet4\": ["
" {"
" \"subnet\": \"192.0.2.0/26\","
" \"id\": 10,"
" \"pools\": ["
" {"
" \"pool\": \"192.0.2.1 - 192.0.2.63\""
" }"
" ]"
" }"
" ]"
" },"
" {"
" \"name\": \"dog\","
" \"interface\": \"eth0\","
" \"option-data\": ["
" {"
" \"name\": \"dhcp-server-identifier\","
" \"data\": \"2.3.4.5\""
" }"
" ],"
" \"subnet4\": ["
" {"
" \"subnet\": \"10.0.0.0/26\","
" \"id\": 1000,"
" \"pools\": ["
" {"
" \"pool\": \"10.0.0.1 - 10.0.0.63\""
" }"
" ]"
" }"
" ]"
" }"
" ]"
"}"
};
/// @Brief Test fixture class for DHCPv4 server using shared networks.
......@@ -1686,4 +1739,44 @@ TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectedByClass) {
});
}
// This test verifies that custom server identifier can be specified for a
// shared network.
TEST_F(Dhcpv4SharedNetworkTest, customServerIdentifier) {
Dhcp4Client client1(Dhcp4Client::SELECTING);
client1.setIfaceName("eth1");
// Configure DHCP server.
ASSERT_NO_THROW(configure(NETWORKS_CONFIG[15], *client1.getServer()));
testAssigned([this, &client1] {
ASSERT_NO_THROW(client1.doDORA());
});
// Make sure that the server responded.
ASSERT_TRUE(client1.getContext().response_);
Pkt4Ptr resp = client1.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// The explicitly configured server identifier should take precedence
// over generated server identifier.
EXPECT_EQ("1.2.3.4", client1.config_.serverid_.toText());
// Create another client using different interface.
Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
client2.setIfaceName("eth0");
testAssigned([this, &client2] {
ASSERT_NO_THROW(client2.doDORA());
});
// Make sure that the server responded.
ASSERT_TRUE(client2.getContext().response_);
resp = client2.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// The explicitly configured server identifier should take precedence
// over generated server identifier.
EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText());
}
} // end of anonymous namespace
......@@ -101,7 +101,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_host_operations.cc cfg_host_operations.h
libkea_dhcpsrv_la_SOURCES += cfg_option.cc cfg_option.h
libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h
libkea_dhcpsrv_la_SOURCES += cfg_rsoo.cc cfg_rsoo.h
libkea_dhcpsrv_la_SOURCES += cfg_shared_networks.h
libkea_dhcpsrv_la_SOURCES += cfg_shared_networks.cc cfg_shared_networks.h
libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h
libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h
libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h
......
// Copyright (C) 2017 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <dhcpsrv/cfg_shared_networks.h>
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
bool
CfgSharedNetworks4::hasNetworkWithServerId(const IOAddress& server_id) const {
const auto& index = networks_.get<SharedNetworkServerIdIndexTag>();
auto network_it = index.find(server_id);
return (network_it != index.cend());
}
} // end of namespace isc::dhcp
} // end of namespace isc
......@@ -7,6 +7,7 @@
#ifndef CFG_SHARED_NETWORKS_H
#define CFG_SHARED_NETWORKS_H
#include <asiolink/io_address.h>
#include <cc/cfg_to_element.h>
#include <cc/data.h>
#include <exceptions/exceptions.h>
......@@ -112,6 +113,15 @@ public:
return (&networks_);
}
/// @brief Checks if specified server identifier has been specified for
/// any network.
///
/// @param server_id Server identifier.
///
/// @return true if there is a network with a specified server identifier.
bool hasNetworkWithServerId(const asiolink::IOAddress& server_id) const;
};
/// @brief Pointer to the configuration of IPv4 shared networks.
......
......@@ -68,6 +68,13 @@ CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
}
bool
CfgSubnets4::hasSubnetWithServerId(const asiolink::IOAddress& server_id) const {
const auto& index = subnets_.get<SubnetServerIdIndexTag>();
auto subnet_it = index.find(server_id);
return (subnet_it != index.cend());
}
Subnet4Ptr
CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
......
......@@ -91,6 +91,14 @@ public:
/// subnet doesn't exist.
ConstSubnet4Ptr getByPrefix(const std::string& subnet_prefix) const;
/// @brief Checks if specified server identifier has been specified for
/// any subnet.
///
/// @param server_id Server identifier.
///
/// @return true if there is a subnet with a specified server identifier.
bool hasSubnetWithServerId(const asiolink::IOAddress& server_id) const;
/// @brief Returns a pointer to the selected subnet.
///
/// This method tries to retrieve the subnet for the client using various
......
......@@ -4,8 +4,13 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <dhcp/dhcp4.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/network.h>
#include <boost/pointer_cast.hpp>
using namespace isc::asiolink;
using namespace isc::data;
namespace isc {
......@@ -112,6 +117,21 @@ Network4::toElement() const {
return (map);
}
IOAddress
Network4::getServerId() const {
try {
OptionCustomPtr opt_server_id = boost::dynamic_pointer_cast<OptionCustom>
(cfg_option_->get(DHCP4_OPTION_SPACE, DHO_DHCP_SERVER_IDENTIFIER).option_);
if (opt_server_id) {
return (opt_server_id->readAddress());
}
} catch (const std::exception&) {
// Ignore any exceptions and simply return empty buffer.
}
return (IOAddress::IPV4_ZERO_ADDRESS());
}
ElementPtr
Network6::toElement() const {
ElementPtr map = Network::toElement();
......
......@@ -337,6 +337,12 @@ public:
/// @return A pointer to unparsed network configuration.
virtual data::ElementPtr toElement() const;
/// @brief Returns binary representation of the dhcp-server-identifier option (54).
///
/// @return Server identifier option as IPv4 address. Zero IPv4 address
/// indicates that server identifier hasn't been specified.
virtual asiolink::IOAddress getServerId() const;
private:
/// @brief Should server use client identifiers for client lease
......
......@@ -7,6 +7,7 @@
#ifndef SHARED_NETWORK_H
#define SHARED_NETWORK_H
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <dhcpsrv/assignable_network.h>
......@@ -30,6 +31,9 @@ struct SharedNetworkRandomAccessIndexTag { };
/// @brief A tag for accessing index by shared network name.
struct SharedNetworkNameIndexTag { };
/// @brief A tag for accessing index by server identifier.
struct SharedNetworkServerIdIndexTag { };
/// @brief Shared network holding IPv4 subnets.
///
/// Specialization of the @ref Network4 class for IPv4 shared networks.
......@@ -149,7 +153,15 @@ typedef boost::multi_index_container<
boost::multi_index::tag<SharedNetworkNameIndexTag>,
boost::multi_index::const_mem_fun<SharedNetwork4, std::string,
&SharedNetwork4::getName>
>,
// Third index allows for access by server identifier specified for the
// network.
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<SharedNetworkServerIdIndexTag>,
boost::multi_index::const_mem_fun<Network4, asiolink::IOAddress,
&Network4::getServerId>
>
>
> SharedNetwork4Collection;
......
......@@ -613,13 +613,20 @@ struct SubnetSubnetIdIndexTag { };
/// @brief Tag for the index for searching by subnet prefix.
struct SubnetPrefixIndexTag { };
/// @brief Multi index container holding subnets.
/// @brief Tag for the index for searching by server identifier.
struct SubnetServerIdIndexTag { };
/// @brief A collection of @c Subnet4 objects
///
/// This container provides a set of indexes which can be used to retrieve
/// subnets by various properties.
///
/// This multi index container can hold pointers to @ref Subnet4 or
/// @ref Subnet6 objects representing subnets. It provides indexes for
/// subnet lookups using subnet properties such as: subnet identifier
/// or subnet prefix. It also provides a random access index which
/// allows for using the container like a vector.
/// This multi index container can hold pointers to @ref Subnet4
/// objects representing subnets. It provides indexes for subnet lookups
/// using subnet properties such as: subnet identifier,
/// subnet prefix or server identifier specified for a subnet. It also
/// provides a random access index which allows for using the container
/// like a vector.
///
/// The random access index is used by the DHCP servers which perform
/// a full scan on subnets to find the one that matches some specific
......@@ -632,12 +639,9 @@ struct SubnetPrefixIndexTag { };
/// @todo We should consider optimizing subnet selection by leveraging
/// the indexing capabilities of this container, e.g. searching for
/// a subnet by interface name, relay address etc.
///
/// @tparam SubnetType Type of the subnet: @ref Subnet4 or @ref Subnet6.
template<typename SubnetType>
using SubnetCollection = boost::multi_index_container<
typedef boost::multi_index_container<
// Multi index container holds pointers to the subnets.
boost::shared_ptr<SubnetType>,
Subnet4Ptr,
// The following holds all indexes.
boost::multi_index::indexed_by<
// First is the random access index allowing for accessing
......@@ -654,21 +658,61 @@ using SubnetCollection = boost::multi_index_container<
boost::multi_index::ordered_unique<
boost::multi_index::tag<SubnetPrefixIndexTag>,
boost::multi_index::const_mem_fun<Subnet, std::string, &Subnet::toText>
>,
// Fourth index allows for searching using an output from getServerId
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<SubnetServerIdIndexTag>,
boost::multi_index::const_mem_fun<Network4, asiolink::IOAddress,
&Network4::getServerId>
>
>
>;
/// @brief A collection of @c Subnet4 objects
///
/// This container provides a set of indexes which can be used to retrieve
/// subnets by various properties.
typedef SubnetCollection<Subnet4> Subnet4Collection;
> Subnet4Collection;
/// @brief A collection of @c Subnet6 objects
///
/// This container provides a set of indexes which can be used to retrieve
/// subnets by various properties.
typedef SubnetCollection<Subnet6> Subnet6Collection;