Commit 699593c6 authored by Francis Dupont's avatar Francis Dupont

[master] Merge branch 'master' of ssh://git.kea.isc.org/git/kea

parents 0e455268 8f971edc
1320. [doc] marcin
Improved documentation of shared networks within Kea Administrator
Reference Manual.
(Trac #5381, git c4be6a71ed3705c182d7ba4417a06ed8fa59f2b5)
1319. [func] marcin
Added support for DHCPv4 option 54 (dhcp-server-identifier).
(Trac #5376, git aae2d91f101b8c61c0abfb3482de04a8dd074121)
1318. [doc] marcin
Updated Kea Administrator's Manual with the information about
new capability of flex-id hook library to use value derived from
......
......@@ -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>
......@@ -3530,6 +3532,32 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
distinction is based on the type of device, rather than address space
exhaustion.</para>
<para>A client connected to a shared network may be assigned an address from
any of the address pools defined within the subnets belonging to the shared
network. Internally, the server selects one of the subnets belonging to a
shared network and tries to allocate an address from this subnet. If the
server is unable to allocate an address from the selected subnet (e.g. due
to address pools exhaustion) it will use another subnet from the same shared
network and try to allocate an address from this subnet etc. Therefore, in the
typical case, the server will allocate all addresses available for a given
subnet before it starts allocating addresses from other subnets belonging to
the same shared network. However, in certain situations the client can be
allocated an address from the other subnets before the address pools in the
first subnet get exhausted, e.g. when the client provides a hint that
belongs to another subnet or the client has reservations in a different than
default subnet.
</para>
<note>
<para>It is strongly discouraged for the Kea deployments to assume that the
server doesn't allocate addresses from other subnets until it uses all
the addresses from the first subnet in the shared network. Apart from the
fact that hints, host reservations and client classification affect subnet
selection, it is also foreseen that we will enhance allocation strategies
for shared networks in the future versions of Kea, so as the selection
of subnets within a shared network is equally probable (unpredictable).</para>
</note>
<para>In order to define a shared network an additional configuration scope
is introduced:
<screen>
......@@ -3541,6 +3569,11 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
// and it must be unique among all shared networks.
"name": "my-secret-lair-level-1",
// Subnet selector can be specifed on the shared network level.
// Subnets from this shared network will be selected for directly
// connected clients sending requests to server's "eth0" interface.
"interface": "eth0",
// This starts a list of subnets in this shared network.
// There are two subnets in this example.
"subnet4": [
......@@ -3555,19 +3588,20 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
],
} ]</userinput>, // end of shared-networks
// It is likely that in your network you'll have a mix of regular,
// "plain" subnets and shared networks. It is perfectly valid to mix
// them in the same config file.
//
// This is regular subnet. It's not part of any shared-network.
"subnet4": [
{
"subnet": "192.0.3.0/24",
"pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ]
}
]
// It is likely that in your network you'll have a mix of regular,
// "plain" subnets and shared networks. It is perfectly valid to mix
// them in the same config file.
//
// This is regular subnet. It's not part of any shared-network.
"subnet4": [
{
"subnet": "192.0.3.0/24",
"pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ],
"interface": "eth1"
}
]
} // end of Dhcp4
} // end of Dhcp4
}
</screen>
</para>
......@@ -3594,6 +3628,8 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
{
"name": "lab-network3",
"interface": "eth0",
// This applies to all subnets in this shared network, unless
// values are overridden on subnet scope.
<userinput>"valid-lifetime": 600</userinput>,
......@@ -3753,6 +3789,7 @@ based on option 93 values.
"shared-networks": [
{
"name": "galah",
"interface": "eth0",
"subnet4": [
{
"subnet": "192.0.2.0/26",
......@@ -3777,6 +3814,18 @@ not able to use any subnets will be refused service. Although, this may be a
desired outcome if one desires to service only clients of known properties
(e.g. only VoIP phones allowed on a given link).</para>
<para>
Note that it is possible to achieve similar effect as presented in this
section without the use of shared networks. If the subnets are placed in
the global subnets scope, rather than in the shared network, the server
will still use classification rules to pick the right subnet for a given
class of devices. The major benefit of placing subnets within the
shared network is that common parameters for the logically grouped
subnets can be specified once, in the shared network scope, e.g.
"interface" or "relay" parameter. All subnets belonging to this shared
network will inherit those parameters.
</para>
</section>
<section>
......@@ -3790,6 +3839,7 @@ desired outcome if one desires to service only clients of known properties
"shared-networks": [
{
"name": "frog",
"interface": "eth0",
"subnet4": [
{
"subnet": "192.0.2.0/26",
......@@ -3832,7 +3882,8 @@ subnets belonging to the same shared network.</para>
<para>While not strictly mandatory, it is strongly recommended to use explicit
"id" values for subnets if you plan to use database storage for host
reservations. If ID is not specified, the values for it be autogenerated,
i.e. it will assign increasing integer values starting from 1.</para>
i.e. it will assign increasing integer values starting from 1. Thus, the
autogenerated IDs are not stable across configuration changes.</para>
</section>
</section>
......@@ -3847,11 +3898,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">
......
......@@ -3063,6 +3063,32 @@ If not specified, the default value is:
modems. In this case, the distinction is based on the type of device, rather
than coming out of running out address space.</para>
<para>A client connected to a shared network may be assigned a lease (address
or prefix) from any of the pools defined within the subnets belonging to the
shared network. Internally, the server selects one of the subnets belonging to the
shared network and tries to allocate a lease from this subnet. If the
server is unable to allocate a lease from the selected subnet (e.g. due
to pools exhaustion) it will use another subnet from the same shared
network and try to allocate a lease from this subnet etc. Therefore, in the
typical case, the server will allocate all leases available in a given
subnet before it starts allocating leases from other subnets belonging to
the same shared network. However, in certain situations the client can be
allocated a lease from the other subnets before the pools in the first
subnet get exhausted, e.g. when the client provides a hint that belongs
to another subnet or the client has reservations in a different than
default subnet.
</para>
<note>
<para>It is strongly discouraged for the Kea deployments to assume that the
server doesn't allocate leases from other subnets until it uses all
the leases from the first subnet in the shared network. Apart from the
fact that hints, host reservations and client classification affect subnet
selection, it is also foreseen that we will enhance allocation strategies
for shared networks in the future versions of Kea, so as the selection
of subnets within a shared network is equally probable (unpredictable).</para>
</note>
<para>In order to define a shared network an additional configuration scope
is introduced:
<screen>
......@@ -3074,6 +3100,13 @@ If not specified, the default value is:
// and it must be unique among all shared networks.
"name": "ipv6-lab-1",
// Subnet selector can be specifed on the shared network level.
// Subnets from this shared network will be selected for clients
// communicating via relay agent having the specified IP address.
"relay": {
"ip-address": "2001:db8:2:34::1"
},
// This starts a list of subnets in this shared network.
// There are two subnets in this example.
"subnet6": [
......@@ -3088,19 +3121,22 @@ If not specified, the default value is:
]
} ]</userinput>, // end of shared-networks
// It is likely that in your network you'll have a mix of regular,
// "plain" subnets and shared networks. It is perfectly valid to mix
// them in the same config file.
//
// This is regular subnet. It's not part of any shared-network.
"subnet6": [
{
"subnet": "2001:db9::/48",
"pools": [ { "pool": "2001:db9::/64" } ]
// It is likely that in your network you'll have a mix of regular,
// "plain" subnets and shared networks. It is perfectly valid to mix
// them in the same config file.
//
// This is regular subnet. It's not part of any shared-network.
"subnet6": [
{
"subnet": "2001:db9::/48",
"pools": [ { "pool": "2001:db9::/64" } ],
"relay": {
"ip-address": "2001:db8:1:2::1"
}
]
}
]
} // end of Dhcp6
} // end of Dhcp6
}
</screen>
</para>
......@@ -3126,6 +3162,9 @@ If not specified, the default value is:
"shared-networks": [
{
"name": "lab-network3",
"relay": {
"ip-address": "2001:db8:2:34::1"
},
// This applies to all subnets in this shared network, unless
// values are overridden on subnet scope.
......@@ -3295,6 +3334,9 @@ based on option 1234 values.
"shared-networks": [
{
"name": "galah",
"relay": {
"ip-address": "2001:db8:2:34::1"
},
"subnet6": [
{
"subnet": "2001:db8:1::/64",
......@@ -3319,6 +3361,18 @@ not able to use any subnets will be refused service. Although, this may be
desired outcome if one desires to service only clients of known properties
(e.g. only VoIP phones allowed on a given link).</para>
<para>
Note that it is possible to achieve similar effect as presented in this
section without the use of shared networks. If the subnets are placed in
the global subnets scope, rather than in the shared network, the server
will still use classification rules to pick the right subnet for a given
class of devices. The major benefit of placing subnets within the
shared network is that common parameters for the logically grouped
subnets can be specified once, in the shared network scope, e.g.
"interface" or "relay" parameter. All subnets belonging to this shared
network will inherit those parameters.
</para>
</section>
<section>
......@@ -3332,6 +3386,9 @@ desired outcome if one desires to service only clients of known properties
"shared-networks": [
{
"name": "frog",
"relay": {
"ip-address": "2001:db8:2:34::1"
},
"subnet6": [
{
"subnet": "2001:db8:1::/64",
......@@ -3374,7 +3431,9 @@ subnets belonging to the same shared network.</para>
<para>While not strictly mandatory, it is strongly recommended to use explicit
"id" values for subnets if you plan to use database storage for host
reservations. If ID is not specified, the values for it be autogenerated,
i.e. it will assign increasing integer values starting from 1.</para>
i.e. it will assign increasing integer values starting from 1. Thus, the
autogenerated IDs are not stable across configuration changes.
</para>
</section>
</section>
......
......@@ -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\","
"