mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 13:37:55 +00:00
[master] Merge branch 'trac1555'
Conflicts: src/bin/dhcp6/dhcp6_messages.mes src/bin/dhcp6/dhcp6_srv.cc src/bin/dhcp6/dhcp6_srv.h
This commit is contained in:
commit
f48a3bff3f
@ -3599,7 +3599,7 @@ $</screen>
|
||||
will be available. It will look similar to this:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp4</userinput>
|
||||
Dhcp4/interface/ list (default)
|
||||
Dhcp4/interfaces/ list (default)
|
||||
Dhcp4/renew-timer 1000 integer (default)
|
||||
Dhcp4/rebind-timer 2000 integer (default)
|
||||
Dhcp4/valid-lifetime 4000 integer (default)
|
||||
@ -3686,6 +3686,60 @@ Dhcp4/subnet4 [] list (default)
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section id="dhcp4-interface-selection">
|
||||
<title>Interface selection</title>
|
||||
<para>
|
||||
When DHCPv4 server starts up, by default it will listen to the DHCP
|
||||
traffic and respond to it on all interfaces detected during startup.
|
||||
However, in many cases it is desired to configure the server to listen and
|
||||
respond on selected interfaces only. The sample commands in this section
|
||||
show how to make interface selection using bindctl.
|
||||
</para>
|
||||
<para>
|
||||
The default configuration can be presented with the following command:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp4/interfaces</userinput>
|
||||
<userinput>Dhcp4/interfaces[0] "*" string</userinput></screen>
|
||||
An asterisk sign plays a role of the wildcard and means "listen on all interfaces".
|
||||
</para>
|
||||
<para>
|
||||
In order to override the default configuration, the existing entry can be replaced
|
||||
with the actual interface name:
|
||||
<screen>
|
||||
> <userinput>config set Dhcp4/interfaces[0] eth1</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Other interface names can be added on one-by-one basis:
|
||||
<screen>
|
||||
> <userinput>config add Dhcp4/interfaces eth2</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Configuration will now contain two interfaces which can be presented as follows:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp4/interfaces</userinput>
|
||||
<userinput>Dhcp4/interfaces[0] "eth1" string</userinput>
|
||||
<userinput>Dhcp4/interfaces[1] "eth2" string</userinput></screen>
|
||||
When configuration gets committed, the server will start to listen on
|
||||
eth1 and eth2 interfaces only.
|
||||
</para>
|
||||
<para>
|
||||
It is possible to use wildcard interface name (asterisk) concurrently with explicit
|
||||
interface names:
|
||||
<screen>
|
||||
> <userinput>config add Dhcp4/interfaces *</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
This will result in the following configuration:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp4/interfaces</userinput>
|
||||
<userinput>Dhcp4/interfaces[0] "eth1" string</userinput>
|
||||
<userinput>Dhcp4/interfaces[1] "eth2" string</userinput>
|
||||
<userinput>Dhcp4/interfaces[2] "*" string</userinput></screen>
|
||||
The presence of the wildcard name implies that server will listen on all interfaces.
|
||||
In order to fall back to the previous configuration when server listens on eth1 and eth2:
|
||||
<screen>
|
||||
> <userinput>config remove Dhcp4/interfaces[2]</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="dhcp4-address-config">
|
||||
<title>Configuration of Address Pools</title>
|
||||
<para>
|
||||
@ -4366,7 +4420,7 @@ Dhcp4/renew-timer 1000 integer (default)
|
||||
will be available. It will look similar to this:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp6</userinput>
|
||||
Dhcp6/interface/ list (default)
|
||||
Dhcp6/interfaces/ list (default)
|
||||
Dhcp6/renew-timer 1000 integer (default)
|
||||
Dhcp6/rebind-timer 2000 integer (default)
|
||||
Dhcp6/preferred-lifetime 3000 integer (default)
|
||||
@ -4459,6 +4513,59 @@ Dhcp6/subnet6/ list
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section id="dhcp6-interface-selection">
|
||||
<title>Interface selection</title>
|
||||
<para>
|
||||
When DHCPv6 server starts up, by default it will listen to the DHCP
|
||||
traffic and respond to it on all interfaces detected during startup.
|
||||
However, in many cases it is desired to configure the server to listen and
|
||||
respond on selected interfaces only. The sample commands in this section
|
||||
show how to make interface selection using bindctl.
|
||||
</para>
|
||||
<para>
|
||||
The default configuration can be presented with the following command:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp6/interfaces</userinput>
|
||||
<userinput>Dhcp6/interfaces[0] "*" string</userinput></screen>
|
||||
An asterisk sign plays a role of the wildcard and means "listen on all interfaces".
|
||||
</para>
|
||||
<para>
|
||||
In order to override the default configuration, the existing entry can be replaced
|
||||
with the actual interface name:
|
||||
<screen>
|
||||
> <userinput>config set Dhcp6/interfaces[0] eth1</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Other interface names can be added on one-by-one basis:
|
||||
<screen>
|
||||
> <userinput>config add Dhcp6/interfaces eth2</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Configuration will now contain two interfaces which can be presented as follows:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp6/interfaces</userinput>
|
||||
<userinput>Dhcp6/interfaces[0] "eth1" string</userinput>
|
||||
<userinput>Dhcp6/interfaces[1] "eth2" string</userinput></screen>
|
||||
When configuration gets committed, the server will start to listen on
|
||||
eth1 and eth2 interfaces only.
|
||||
</para>
|
||||
<para>
|
||||
It is possible to use wildcard interface name (asterisk) concurrently with explicit
|
||||
interface names:
|
||||
<screen>
|
||||
> <userinput>config add Dhcp6/interfaces *</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
This will result in the following configuration:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp6/interfaces</userinput>
|
||||
<userinput>Dhcp6/interfaces[0] "eth1" string</userinput>
|
||||
<userinput>Dhcp6/interfaces[1] "eth2" string</userinput>
|
||||
<userinput>Dhcp6/interfaces[2] "*" string</userinput></screen>
|
||||
The presence of the wildcard name implies that server will listen on all interfaces.
|
||||
In order to fall back to the previous configuration when server listens on eth1 and eth2:
|
||||
<screen>
|
||||
> <userinput>config remove Dhcp6/interfaces[2]</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Subnet and Address Pool</title>
|
||||
|
@ -53,10 +53,10 @@ public:
|
||||
/// @param dummy first param, option names are always "Dhcp4/option-data[n]"
|
||||
/// @param options is the option storage in which to store the parsed option
|
||||
/// upon "commit".
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
Dhcp4OptionDataParser(const std::string&,
|
||||
OptionStoragePtr options, ParserContextPtr global_context)
|
||||
Dhcp4OptionDataParser(const std::string&,
|
||||
OptionStoragePtr options, ParserContextPtr global_context)
|
||||
:OptionDataParser("", options, global_context) {
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public:
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed.
|
||||
/// @param options storage where the parameter value is to be stored.
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
/// @return returns a pointer to a new OptionDataParser. Caller is
|
||||
/// is responsible for deleting it when it is no longer needed.
|
||||
@ -75,16 +75,16 @@ public:
|
||||
|
||||
protected:
|
||||
/// @brief Finds an option definition within the server's option space
|
||||
///
|
||||
/// Given an option space and an option code, find the correpsonding
|
||||
///
|
||||
/// Given an option space and an option code, find the correpsonding
|
||||
/// option defintion within the server's option defintion storage.
|
||||
///
|
||||
/// @param option_space name of the parameter option space
|
||||
/// @param option_code numeric value of the parameter to find
|
||||
/// @return OptionDefintionPtr of the option defintion or an
|
||||
/// @param option_space name of the parameter option space
|
||||
/// @param option_code numeric value of the parameter to find
|
||||
/// @return OptionDefintionPtr of the option defintion or an
|
||||
/// empty OptionDefinitionPtr if not found.
|
||||
/// @throw DhcpConfigError if the option space requested is not valid
|
||||
/// for this server.
|
||||
/// @throw DhcpConfigError if the option space requested is not valid
|
||||
/// for this server.
|
||||
virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
|
||||
std::string& option_space, uint32_t option_code) {
|
||||
OptionDefinitionPtr def;
|
||||
@ -100,11 +100,11 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Parser for IPv4 pool definitions.
|
||||
/// @brief Parser for IPv4 pool definitions.
|
||||
///
|
||||
/// This is the IPv4 derivation of the PoolParser class and handles pool
|
||||
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
||||
/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
|
||||
/// This is the IPv4 derivation of the PoolParser class and handles pool
|
||||
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
||||
/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
|
||||
/// PoolStorage container.
|
||||
///
|
||||
/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
|
||||
@ -126,9 +126,9 @@ protected:
|
||||
///
|
||||
/// @param addr is the IPv4 prefix of the pool.
|
||||
/// @param len is the prefix length.
|
||||
/// @param ignored dummy parameter to provide symmetry between the
|
||||
/// @param ignored dummy parameter to provide symmetry between the
|
||||
/// PoolParser derivations. The V6 derivation requires a third value.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t) {
|
||||
return (PoolPtr(new Pool4(addr, len)));
|
||||
}
|
||||
@ -137,9 +137,9 @@ protected:
|
||||
///
|
||||
/// @param min is the first IPv4 address in the pool.
|
||||
/// @param max is the last IPv4 address in the pool.
|
||||
/// @param ignored dummy parameter to provide symmetry between the
|
||||
/// @param ignored dummy parameter to provide symmetry between the
|
||||
/// PoolParser derivations. The V6 derivation requires a third value.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t) {
|
||||
return (PoolPtr(new Pool4(min, max)));
|
||||
}
|
||||
@ -147,8 +147,8 @@ protected:
|
||||
|
||||
/// @brief This class parses a single IPv4 subnet.
|
||||
///
|
||||
/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
|
||||
/// the whole subnet definition. It creates parsersfor received configuration
|
||||
/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
|
||||
/// the whole subnet definition. It creates parsersfor received configuration
|
||||
/// parameters as needed.
|
||||
class Subnet4ConfigParser : public SubnetConfigParser {
|
||||
public:
|
||||
@ -158,7 +158,7 @@ public:
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
Subnet4ConfigParser(const std::string&)
|
||||
:SubnetConfigParser("", globalContext()) {
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Adds the created subnet to a server's configuration.
|
||||
/// @throw throws Unexpected if dynamic cast fails.
|
||||
@ -167,7 +167,7 @@ public:
|
||||
Subnet4Ptr sub4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
|
||||
if (!sub4ptr) {
|
||||
// If we hit this, it is a programming error.
|
||||
isc_throw(Unexpected,
|
||||
isc_throw(Unexpected,
|
||||
"Invalid cast in Subnet4ConfigParser::commit");
|
||||
}
|
||||
|
||||
@ -191,13 +191,13 @@ protected:
|
||||
(config_id.compare("renew-timer") == 0) ||
|
||||
(config_id.compare("rebind-timer") == 0)) {
|
||||
parser = new Uint32Parser(config_id, uint32_values_);
|
||||
} else if ((config_id.compare("subnet") == 0) ||
|
||||
} else if ((config_id.compare("subnet") == 0) ||
|
||||
(config_id.compare("interface") == 0)) {
|
||||
parser = new StringParser(config_id, string_values_);
|
||||
} else if (config_id.compare("pool") == 0) {
|
||||
parser = new Pool4Parser(config_id, pools_);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id, options_,
|
||||
parser = new OptionDataListParser(config_id, options_,
|
||||
global_context_,
|
||||
Dhcp4OptionDataParser::factory);
|
||||
} else {
|
||||
@ -210,7 +210,7 @@ protected:
|
||||
|
||||
|
||||
/// @brief Determines if the given option space name and code describe
|
||||
/// a standard option for the DCHP4 server.
|
||||
/// a standard option for the DCHP4 server.
|
||||
///
|
||||
/// @param option_space is the name of the option space to consider
|
||||
/// @param code is the numeric option code to consider
|
||||
@ -230,12 +230,12 @@ protected:
|
||||
}
|
||||
|
||||
/// @brief Issues a DHCP4 server specific warning regarding duplicate subnet
|
||||
/// options.
|
||||
///
|
||||
/// options.
|
||||
///
|
||||
/// @param code is the numeric option code of the duplicate option
|
||||
/// @param addr is the subnet address
|
||||
/// @param addr is the subnet address
|
||||
/// @todo a means to know the correct logger and perhaps a common
|
||||
/// message would allow this method to be emitted by the base class.
|
||||
/// message would allow this method to be emitted by the base class.
|
||||
virtual void duplicate_option_warning(uint32_t code,
|
||||
isc::asiolink::IOAddress& addr) {
|
||||
LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
|
||||
@ -243,10 +243,10 @@ protected:
|
||||
}
|
||||
|
||||
/// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
|
||||
/// and prefix length.
|
||||
///
|
||||
/// and prefix length.
|
||||
///
|
||||
/// @param addr is IPv4 address of the subnet.
|
||||
/// @param len is the prefix length
|
||||
/// @param len is the prefix length
|
||||
void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
|
||||
// Get all 'time' parameters using inheritance.
|
||||
// If the subnet-specific value is defined then use it, else
|
||||
@ -338,32 +338,32 @@ namespace dhcp {
|
||||
///
|
||||
/// @param config_id pointer to received global configuration entry
|
||||
/// @return parser for specified global DHCPv4 parameter
|
||||
/// @throw NotImplemented if trying to create a parser for unknown
|
||||
/// @throw NotImplemented if trying to create a parser for unknown
|
||||
/// config element
|
||||
DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
|
||||
DhcpConfigParser* parser = NULL;
|
||||
if ((config_id.compare("valid-lifetime") == 0) ||
|
||||
(config_id.compare("renew-timer") == 0) ||
|
||||
(config_id.compare("rebind-timer") == 0)) {
|
||||
parser = new Uint32Parser(config_id,
|
||||
parser = new Uint32Parser(config_id,
|
||||
globalContext()->uint32_values_);
|
||||
} else if (config_id.compare("interface") == 0) {
|
||||
} else if (config_id.compare("interfaces") == 0) {
|
||||
parser = new InterfaceListConfigParser(config_id);
|
||||
} else if (config_id.compare("subnet4") == 0) {
|
||||
parser = new Subnets4ListConfigParser(config_id);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id,
|
||||
globalContext()->options_,
|
||||
parser = new OptionDataListParser(config_id,
|
||||
globalContext()->options_,
|
||||
globalContext(),
|
||||
Dhcp4OptionDataParser::factory);
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser = new OptionDefListParser(config_id,
|
||||
parser = new OptionDefListParser(config_id,
|
||||
globalContext()->option_defs_);
|
||||
} else if (config_id.compare("version") == 0) {
|
||||
parser = new StringParser(config_id,
|
||||
parser = new StringParser(config_id,
|
||||
globalContext()->string_values_);
|
||||
} else if (config_id.compare("lease-database") == 0) {
|
||||
parser = new DbAccessParser(config_id);
|
||||
parser = new DbAccessParser(config_id);
|
||||
} else {
|
||||
isc_throw(NotImplemented,
|
||||
"Parser error: Global configuration parameter not supported: "
|
||||
@ -384,7 +384,7 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
/// @todo: Append most essential info here (like "2 new subnets configured")
|
||||
string config_details;
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND,
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND,
|
||||
DHCP4_CONFIG_START).arg(config_set->str());
|
||||
|
||||
// Some of the values specified in the configuration depend on
|
||||
@ -397,6 +397,7 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
ParserCollection independent_parsers;
|
||||
ParserPtr subnet_parser;
|
||||
ParserPtr option_parser;
|
||||
ParserPtr iface_parser;
|
||||
|
||||
// The subnet parsers implement data inheritance by directly
|
||||
// accessing global storage. For this reason the global data
|
||||
@ -428,6 +429,11 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
subnet_parser = parser;
|
||||
} else if (config_pair.first == "option-data") {
|
||||
option_parser = parser;
|
||||
} else if (config_pair.first == "interfaces") {
|
||||
// The interface parser is independent from any other
|
||||
// parser and can be run here before any other parsers.
|
||||
iface_parser = parser;
|
||||
parser->build(config_pair.second);
|
||||
} else {
|
||||
// Those parsers should be started before other
|
||||
// parsers so we can call build straight away.
|
||||
@ -483,6 +489,10 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
if (subnet_parser) {
|
||||
subnet_parser->commit();
|
||||
}
|
||||
|
||||
if (iface_parser) {
|
||||
iface_parser->commit();
|
||||
}
|
||||
}
|
||||
catch (const isc::Exception& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
|
||||
|
@ -30,7 +30,7 @@ namespace dhcp {
|
||||
|
||||
class Dhcpv4Srv;
|
||||
|
||||
/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration
|
||||
/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration
|
||||
/// values.
|
||||
///
|
||||
/// This function parses configuration information stored in @c config_set
|
||||
@ -44,7 +44,7 @@ class Dhcpv4Srv;
|
||||
/// (such as malformed configuration or invalid configuration parameter),
|
||||
/// this function returns appropriate error code.
|
||||
///
|
||||
/// This function is called every time a new configuration is received. The
|
||||
/// This function is called every time a new configuration is received. The
|
||||
/// extra parameter is a reference to DHCPv4 server component. It is currently
|
||||
/// not used and CfgMgr::instance() is accessed instead.
|
||||
///
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@ -20,17 +20,17 @@
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcpsrv/dhcp_config_parser.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <dhcp4/spec_config.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/buffer.h>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::cc;
|
||||
@ -101,7 +101,27 @@ ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
|
||||
}
|
||||
|
||||
// Configure the server.
|
||||
return (configureDhcp4Server(*server_, merged_config));
|
||||
ConstElementPtr answer = configureDhcp4Server(*server_, merged_config);
|
||||
|
||||
// Check that configuration was successful. If not, do not reopen sockets.
|
||||
int rcode = 0;
|
||||
parseAnswer(rcode, answer);
|
||||
if (rcode != 0) {
|
||||
return (answer);
|
||||
}
|
||||
|
||||
// Configuration may change active interfaces. Therefore, we have to reopen
|
||||
// sockets according to new configuration. This operation is not exception
|
||||
// safe and we really don't want to emit exceptions to the callback caller.
|
||||
// Instead, catch an exception and create appropriate answer.
|
||||
try {
|
||||
server_->openActiveSockets(server_->getPort(), server_->useBroadcast());
|
||||
} catch (std::exception& ex) {
|
||||
std::ostringstream err;
|
||||
err << "failed to open sockets after server reconfiguration: " << ex.what();
|
||||
answer = isc::config::createAnswer(1, err.str());
|
||||
}
|
||||
return (answer);
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
@ -172,8 +192,13 @@ void ControlledDhcpv4Srv::establishSession() {
|
||||
|
||||
try {
|
||||
configureDhcp4Server(*this, config_session_->getFullConfig());
|
||||
// Configuration may disable or enable interfaces so we have to
|
||||
// reopen sockets according to new configuration.
|
||||
openActiveSockets(getPort(), useBroadcast());
|
||||
|
||||
} catch (const DhcpConfigError& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
|
||||
|
||||
}
|
||||
|
||||
/// Integrate the asynchronous I/O model of BIND 10 configuration
|
||||
@ -228,6 +253,5 @@ ControlledDhcpv4Srv::execDhcpv4ServerCommand(const std::string& command_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@ -130,6 +130,7 @@ protected:
|
||||
/// when there is a new command or configuration sent over msgq.
|
||||
static void sessionReader(void);
|
||||
|
||||
|
||||
/// @brief IOService object, used for all ASIO operations.
|
||||
isc::asiolink::IOService io_service_;
|
||||
|
||||
|
@ -3,16 +3,16 @@
|
||||
"module_name": "Dhcp4",
|
||||
"module_description": "DHCPv4 server daemon",
|
||||
"config_data": [
|
||||
{ "item_name": "interface",
|
||||
{ "item_name": "interfaces",
|
||||
"item_type": "list",
|
||||
"item_optional": false,
|
||||
"item_default": [ "all" ],
|
||||
"item_default": [ "*" ],
|
||||
"list_item_spec":
|
||||
{
|
||||
"item_name": "interface_name",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": "all"
|
||||
"item_default": "*"
|
||||
}
|
||||
} ,
|
||||
|
||||
|
@ -14,6 +14,11 @@
|
||||
|
||||
$NAMESPACE isc::dhcp
|
||||
|
||||
% DHCP4_ACTIVATE_INTERFACE activating interface %1
|
||||
This message is printed when DHCPv4 server enabled an interface to be used
|
||||
to receive DHCPv4 traffic. IPv4 socket on this interface will be opened once
|
||||
Interface Manager starts up procedure of opening sockets.
|
||||
|
||||
% DHCP4_CCSESSION_STARTED control channel session started on socket %1
|
||||
A debug message issued during startup after the IPv4 DHCP server has
|
||||
successfully established a session with the BIND 10 control channel.
|
||||
@ -60,6 +65,11 @@ This informational message is printed every time DHCPv4 server is started
|
||||
and gives both the type and name of the database being used to store
|
||||
lease and other information.
|
||||
|
||||
% DHCP4_DEACTIVATE_INTERFACE deactivate interface %1
|
||||
This message is printed when DHCPv4 server disables an interface from being
|
||||
used to receive DHCPv4 traffic. Sockets on this interface will not be opened
|
||||
by the Interface Manager until interface is enabled.
|
||||
|
||||
% DHCP4_LEASE_ADVERT lease %1 advertised (client client-id %2, hwaddr %3)
|
||||
This debug message indicates that the server successfully advertised
|
||||
a lease. It is up to the client to choose one server out of othe advertised
|
||||
@ -82,6 +92,11 @@ specified client after receiving a REQUEST message from it. There are many
|
||||
possible reasons for such a failure. Additional messages will indicate the
|
||||
reason.
|
||||
|
||||
% DHCP4_NO_SOCKETS_OPEN no interface configured to listen to DHCP traffic
|
||||
This warning message is issued when current server configuration specifies
|
||||
no interfaces that server should listen on, or specified interfaces are not
|
||||
configured to receive the traffic.
|
||||
|
||||
% DHCP4_NOT_RUNNING IPv4 DHCP server is not running
|
||||
A warning message is issued when an attempt is made to shut down the
|
||||
IPv4 DHCP server but it is not running.
|
||||
|
@ -58,7 +58,8 @@ static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
|
||||
// grants those options and a single, fixed, hardcoded lease.
|
||||
|
||||
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
|
||||
const bool direct_response_desired) {
|
||||
const bool direct_response_desired)
|
||||
: port_(port), use_bcast_(use_bcast) {
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
|
||||
try {
|
||||
// First call to instance() will create IfaceMgr (it's a singleton)
|
||||
@ -73,7 +74,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
|
||||
if (port) {
|
||||
// open sockets only if port is non-zero. Port 0 is used
|
||||
// for non-socket related testing.
|
||||
IfaceMgr::instance().openSockets4(port, use_bcast);
|
||||
IfaceMgr::instance().openSockets4(port_, use_bcast_);
|
||||
}
|
||||
|
||||
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
|
||||
@ -820,5 +821,49 @@ Dhcpv4Srv::sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::openActiveSockets(const uint16_t port,
|
||||
const bool use_bcast) {
|
||||
IfaceMgr::instance().closeSockets();
|
||||
|
||||
// Get the reference to the collection of interfaces. This reference should
|
||||
// be valid as long as the program is run because IfaceMgr is a singleton.
|
||||
// Therefore we can safely iterate over instances of all interfaces and
|
||||
// modify their flags. Here we modify flags which indicate whether socket
|
||||
// should be open for a particular interface or not.
|
||||
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
|
||||
for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
|
||||
iface != ifaces.end(); ++iface) {
|
||||
Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
|
||||
if (iface_ptr == NULL) {
|
||||
isc_throw(isc::Unexpected, "Interface Manager returned NULL"
|
||||
<< " instance of the interface when DHCPv4 server was"
|
||||
<< " trying to reopen sockets after reconfiguration");
|
||||
}
|
||||
if (CfgMgr::instance().isActiveIface(iface->getName())) {
|
||||
iface_ptr->inactive4_ = false;
|
||||
LOG_INFO(dhcp4_logger, DHCP4_ACTIVATE_INTERFACE)
|
||||
.arg(iface->getFullName());
|
||||
|
||||
} else {
|
||||
// For deactivating interface, it should be sufficient to log it
|
||||
// on the debug level because it is more useful to know what
|
||||
// interface is activated which is logged on the info level.
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
|
||||
DHCP4_DEACTIVATE_INTERFACE).arg(iface->getName());
|
||||
iface_ptr->inactive4_ = true;
|
||||
|
||||
}
|
||||
}
|
||||
// Let's reopen active sockets. openSockets4 will check internally whether
|
||||
// sockets are marked active or inactive.
|
||||
// @todo Optimization: we should not reopen all sockets but rather select
|
||||
// those that have been affected by the new configuration.
|
||||
if (!IfaceMgr::instance().openSockets4(port, use_bcast)) {
|
||||
LOG_WARN(dhcp4_logger, DHCP4_NO_SOCKETS_OPEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace dhcp
|
||||
} // namespace isc
|
||||
|
@ -113,6 +113,46 @@ public:
|
||||
/// be freed by the caller.
|
||||
static const char* serverReceivedPacketName(uint8_t type);
|
||||
|
||||
///
|
||||
/// @name Public accessors returning values required to (re)open sockets.
|
||||
///
|
||||
/// These accessors must be public because sockets are reopened from the
|
||||
/// static configuration callback handler. This callback handler invokes
|
||||
/// @c ControlledDhcpv4Srv::openActiveSockets which requires parameters
|
||||
/// which has to be retrieved from the @c ControlledDhcpv4Srv object.
|
||||
/// They are retrieved using these public functions
|
||||
//@{
|
||||
///
|
||||
/// @brief Get UDP port on which server should listen.
|
||||
///
|
||||
/// Typically, server listens on UDP port number 67. Other ports are used
|
||||
/// for testing purposes only.
|
||||
///
|
||||
/// @return UDP port on which server should listen.
|
||||
uint16_t getPort() const {
|
||||
return (port_);
|
||||
}
|
||||
|
||||
/// @brief Return bool value indicating that broadcast flags should be set
|
||||
/// on sockets.
|
||||
///
|
||||
/// @return A bool value indicating that broadcast should be used (if true).
|
||||
bool useBroadcast() const {
|
||||
return (use_bcast_);
|
||||
}
|
||||
//@}
|
||||
|
||||
/// @brief Open sockets which are marked as active in @c CfgMgr.
|
||||
///
|
||||
/// This function reopens sockets according to the current settings in the
|
||||
/// Configuration Manager. It holds the list of the interfaces which server
|
||||
/// should listen on. This function will open sockets on these interfaces
|
||||
/// only. This function is not exception safe.
|
||||
///
|
||||
/// @param port UDP port on which server should listen.
|
||||
/// @param use_bcast should broadcast flags be set on the sockets.
|
||||
static void openActiveSockets(const uint16_t port, const bool use_bcast);
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief verifies if specified packet meets RFC requirements
|
||||
@ -310,6 +350,9 @@ private:
|
||||
/// during normal operation (e.g. to use different allocators)
|
||||
boost::shared_ptr<AllocEngine> alloc_engine_;
|
||||
|
||||
uint16_t port_; ///< UDP port number on which server listens.
|
||||
bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
|
||||
|
||||
};
|
||||
|
||||
}; // namespace isc::dhcp
|
||||
|
@ -51,11 +51,12 @@ public:
|
||||
// deal with sockets here, just check if configuration handling
|
||||
// is sane.
|
||||
srv_.reset(new Dhcpv4Srv(0));
|
||||
CfgMgr::instance().deleteActiveIfaces();
|
||||
}
|
||||
|
||||
// Checks if global parameter of name have expected_value
|
||||
void checkGlobalUint32(string name, uint32_t expected_value) {
|
||||
const Uint32StoragePtr uint32_defaults =
|
||||
const Uint32StoragePtr uint32_defaults =
|
||||
globalContext()->uint32_values_;
|
||||
try {
|
||||
uint32_t actual_value = uint32_defaults->getParam(name);
|
||||
@ -138,7 +139,7 @@ public:
|
||||
/// describing an option.
|
||||
std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
|
||||
std::ostringstream stream;
|
||||
stream << "{ \"interface\": [ \"all\" ],"
|
||||
stream << "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
@ -245,7 +246,7 @@ public:
|
||||
void resetConfiguration() {
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"valid-lifetime\": 4000, "
|
||||
@ -322,7 +323,7 @@ TEST_F(Dhcp4ParserTest, emptySubnet) {
|
||||
ConstElementPtr status;
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
|
||||
Element::fromJSON("{ \"interface\": [ \"all\" ],"
|
||||
Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ ], "
|
||||
@ -342,7 +343,7 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
@ -372,7 +373,7 @@ TEST_F(Dhcp4ParserTest, subnetLocal) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
@ -403,7 +404,7 @@ TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
@ -427,7 +428,7 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
@ -949,7 +950,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
|
||||
// configuration does not include options configuration.
|
||||
TEST_F(Dhcp4ParserTest, optionDataDefaults) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1022,7 +1023,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
|
||||
// The definition is not required for the option that
|
||||
// belongs to the 'dhcp4' option space as it is the
|
||||
// standard option.
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1100,7 +1101,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
|
||||
// at the very end (when all other parameters are configured).
|
||||
|
||||
// Starting stage 1. Configure sub-options and their definitions.
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1149,7 +1150,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
|
||||
// the configuration from the stage 2 is repeated because BIND
|
||||
// configuration manager sends whole configuration for the lists
|
||||
// where at least one element is being modified or added.
|
||||
config = "{ \"interface\": [ \"all\" ],"
|
||||
config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1245,7 +1246,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
|
||||
// option setting.
|
||||
TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"option-data\": [ {"
|
||||
@ -1317,7 +1318,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
|
||||
// for multiple subnets.
|
||||
TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
@ -1597,7 +1598,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
|
||||
// In the first stahe we create definitions of suboptions
|
||||
// that we will add to the base option.
|
||||
// Let's create some dummy options: foo and foo2.
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1650,7 +1651,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
|
||||
// We add our dummy options to this option space and thus
|
||||
// they should be included as sub-options in the 'vendor-opts'
|
||||
// option.
|
||||
config = "{ \"interface\": [ \"all\" ],"
|
||||
config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1749,5 +1750,69 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
|
||||
EXPECT_FALSE(desc.option->getOption(3));
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to select subset of interfaces
|
||||
// on which server should listen.
|
||||
TEST_F(Dhcp4ParserTest, selectedInterfaces) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interfaces\": [ \"eth0\", \"eth1\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
|
||||
};
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
// Make sure the config manager is clean and there is no hanging
|
||||
// interface configuration.
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
|
||||
// Apply configuration.
|
||||
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
|
||||
ASSERT_TRUE(status);
|
||||
checkResult(status, 0);
|
||||
|
||||
// eth0 and eth1 were explicitly selected. eth2 was not.
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
EXPECT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to configure the server in such a way
|
||||
// that it listens on all interfaces.
|
||||
TEST_F(Dhcp4ParserTest, allInterfaces) {
|
||||
ConstElementPtr x;
|
||||
// This configuration specifies two interfaces on which server should listen
|
||||
// but it also includes asterisk. The asterisk switches server into the
|
||||
// mode when it listens on all interfaces regardless of what interface names
|
||||
// were specified in the "interfaces" parameter.
|
||||
string config = "{ \"interfaces\": [ \"eth0\", \"*\", \"eth1\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
// Make sure there is no old configuration.
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
|
||||
// Apply configuration.
|
||||
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
|
||||
ASSERT_TRUE(status);
|
||||
checkResult(status, 0);
|
||||
|
||||
// All interfaces should be now active.
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -67,10 +67,10 @@ public:
|
||||
/// @param dummy first param, option names are always "Dhcp6/option-data[n]"
|
||||
/// @param options is the option storage in which to store the parsed option
|
||||
/// upon "commit".
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options,
|
||||
ParserContextPtr global_context)
|
||||
Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options,
|
||||
ParserContextPtr global_context)
|
||||
:OptionDataParser("", options, global_context) {
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ public:
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed.
|
||||
/// @param options storage where the parameter value is to be stored.
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
/// @return returns a pointer to a new OptionDataParser. Caller is
|
||||
/// is responsible for deleting it when it is no longer needed.
|
||||
@ -90,16 +90,16 @@ public:
|
||||
|
||||
protected:
|
||||
/// @brief Finds an option definition within the server's option space
|
||||
///
|
||||
/// Given an option space and an option code, find the correpsonding
|
||||
///
|
||||
/// Given an option space and an option code, find the correpsonding
|
||||
/// option defintion within the server's option defintion storage.
|
||||
///
|
||||
/// @param option_space name of the parameter option space
|
||||
/// @param option_code numeric value of the parameter to find
|
||||
/// @return OptionDefintionPtr of the option defintion or an
|
||||
/// @param option_space name of the parameter option space
|
||||
/// @param option_code numeric value of the parameter to find
|
||||
/// @return OptionDefintionPtr of the option defintion or an
|
||||
/// empty OptionDefinitionPtr if not found.
|
||||
/// @throw DhcpConfigError if the option space requested is not valid
|
||||
/// for this server.
|
||||
/// @throw DhcpConfigError if the option space requested is not valid
|
||||
/// for this server.
|
||||
virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
|
||||
std::string& option_space, uint32_t option_code) {
|
||||
OptionDefinitionPtr def;
|
||||
@ -115,11 +115,11 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Parser for IPv4 pool definitions.
|
||||
/// @brief Parser for IPv4 pool definitions.
|
||||
///
|
||||
/// This is the IPv6 derivation of the PoolParser class and handles pool
|
||||
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
||||
/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
|
||||
/// This is the IPv6 derivation of the PoolParser class and handles pool
|
||||
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
|
||||
/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen
|
||||
/// PoolStorage container.
|
||||
///
|
||||
/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
|
||||
@ -142,9 +142,9 @@ protected:
|
||||
/// @param addr is the IPv6 prefix of the pool.
|
||||
/// @param len is the prefix length.
|
||||
/// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
|
||||
/// passed in as an int32_t and cast to Pool6Type to accommodate a
|
||||
/// passed in as an int32_t and cast to Pool6Type to accommodate a
|
||||
/// polymorphic interface.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
|
||||
{
|
||||
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
|
||||
@ -156,9 +156,9 @@ protected:
|
||||
/// @param min is the first IPv6 address in the pool.
|
||||
/// @param max is the last IPv6 address in the pool.
|
||||
/// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
|
||||
/// passed in as an int32_t and cast to Pool6Type to accommodate a
|
||||
/// passed in as an int32_t and cast to Pool6Type to accommodate a
|
||||
/// polymorphic interface.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
/// @return returns a PoolPtr to the new Pool4 object.
|
||||
PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
|
||||
{
|
||||
return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
|
||||
@ -168,8 +168,8 @@ protected:
|
||||
|
||||
/// @brief This class parses a single IPv6 subnet.
|
||||
///
|
||||
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
|
||||
/// the whole subnet definition. It creates parsersfor received configuration
|
||||
/// This is the IPv6 derivation of the SubnetConfigParser class and it parses
|
||||
/// the whole subnet definition. It creates parsersfor received configuration
|
||||
/// parameters as needed.
|
||||
class Subnet6ConfigParser : public SubnetConfigParser {
|
||||
public:
|
||||
@ -178,7 +178,7 @@ public:
|
||||
///
|
||||
/// @param ignored first parameter
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
Subnet6ConfigParser(const std::string&)
|
||||
Subnet6ConfigParser(const std::string&)
|
||||
:SubnetConfigParser("", globalContext()) {
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ protected:
|
||||
} else if (config_id.compare("pool") == 0) {
|
||||
parser = new Pool6Parser(config_id, pools_);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id, options_,
|
||||
parser = new OptionDataListParser(config_id, options_,
|
||||
global_context_,
|
||||
Dhcp6OptionDataParser::factory);
|
||||
} else {
|
||||
@ -233,14 +233,14 @@ protected:
|
||||
|
||||
|
||||
/// @brief Determines if the given option space name and code describe
|
||||
/// a standard option for the DHCP6 server.
|
||||
/// a standard option for the DHCP6 server.
|
||||
///
|
||||
/// @param option_space is the name of the option space to consider
|
||||
/// @param code is the numeric option code to consider
|
||||
/// @return returns true if the space and code are part of the server's
|
||||
/// standard options.
|
||||
bool isServerStdOption(std::string option_space, uint32_t code) {
|
||||
return ((option_space.compare("dhcp6") == 0)
|
||||
return ((option_space.compare("dhcp6") == 0)
|
||||
&& LibDHCP::isStandardOption(Option::V6, code));
|
||||
}
|
||||
|
||||
@ -253,23 +253,23 @@ protected:
|
||||
}
|
||||
|
||||
/// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
|
||||
/// options.
|
||||
///
|
||||
/// options.
|
||||
///
|
||||
/// @param code is the numeric option code of the duplicate option
|
||||
/// @param addr is the subnet address
|
||||
/// @todo A means to know the correct logger and perhaps a common
|
||||
/// message would allow this message to be emitted by the base class.
|
||||
virtual void duplicate_option_warning(uint32_t code,
|
||||
virtual void duplicate_option_warning(uint32_t code,
|
||||
isc::asiolink::IOAddress& addr) {
|
||||
LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
|
||||
.arg(code).arg(addr.toText());
|
||||
}
|
||||
|
||||
/// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
|
||||
/// and prefix length.
|
||||
///
|
||||
/// and prefix length.
|
||||
///
|
||||
/// @param addr is IPv6 prefix of the subnet.
|
||||
/// @param len is the prefix length
|
||||
/// @param len is the prefix length
|
||||
void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
|
||||
// Get all 'time' parameters using inheritance.
|
||||
// If the subnet-specific value is defined then use it, else
|
||||
@ -292,13 +292,13 @@ protected:
|
||||
|
||||
// Specifying both interface for locally reachable subnets and
|
||||
// interface id for relays is mutually exclusive. Need to test for
|
||||
// this condition.
|
||||
// this condition.
|
||||
if (!ifaceid.empty()) {
|
||||
std::string iface;
|
||||
try {
|
||||
iface = string_values_->getParam("interface");
|
||||
} catch (const DhcpConfigError &) {
|
||||
// iface not mandatory
|
||||
// iface not mandatory
|
||||
}
|
||||
|
||||
if (!iface.empty()) {
|
||||
@ -403,7 +403,7 @@ namespace dhcp {
|
||||
///
|
||||
/// @param config_id pointer to received global configuration entry
|
||||
/// @return parser for specified global DHCPv6 parameter
|
||||
/// @throw NotImplemented if trying to create a parser for unknown config
|
||||
/// @throw NotImplemented if trying to create a parser for unknown config
|
||||
/// element
|
||||
DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
|
||||
DhcpConfigParser* parser = NULL;
|
||||
@ -411,22 +411,22 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
|
||||
(config_id.compare("valid-lifetime") == 0) ||
|
||||
(config_id.compare("renew-timer") == 0) ||
|
||||
(config_id.compare("rebind-timer") == 0)) {
|
||||
parser = new Uint32Parser(config_id,
|
||||
parser = new Uint32Parser(config_id,
|
||||
globalContext()->uint32_values_);
|
||||
} else if (config_id.compare("interface") == 0) {
|
||||
} else if (config_id.compare("interfaces") == 0) {
|
||||
parser = new InterfaceListConfigParser(config_id);
|
||||
} else if (config_id.compare("subnet6") == 0) {
|
||||
parser = new Subnets6ListConfigParser(config_id);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id,
|
||||
globalContext()->options_,
|
||||
parser = new OptionDataListParser(config_id,
|
||||
globalContext()->options_,
|
||||
globalContext(),
|
||||
Dhcp6OptionDataParser::factory);
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser = new OptionDefListParser(config_id,
|
||||
parser = new OptionDefListParser(config_id,
|
||||
globalContext()->option_defs_);
|
||||
} else if (config_id.compare("version") == 0) {
|
||||
parser = new StringParser(config_id,
|
||||
parser = new StringParser(config_id,
|
||||
globalContext()->string_values_);
|
||||
} else if (config_id.compare("lease-database") == 0) {
|
||||
parser = new DbAccessParser(config_id);
|
||||
@ -450,7 +450,7 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
/// @todo: Append most essential info here (like "2 new subnets configured")
|
||||
string config_details;
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
|
||||
DHCP6_CONFIG_START).arg(config_set->str());
|
||||
|
||||
// Some of the values specified in the configuration depend on
|
||||
@ -463,6 +463,7 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
ParserCollection independent_parsers;
|
||||
ParserPtr subnet_parser;
|
||||
ParserPtr option_parser;
|
||||
ParserPtr iface_parser;
|
||||
|
||||
// The subnet parsers implement data inheritance by directly
|
||||
// accessing global storage. For this reason the global data
|
||||
@ -495,6 +496,11 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
subnet_parser = parser;
|
||||
} else if (config_pair.first == "option-data") {
|
||||
option_parser = parser;
|
||||
} else if (config_pair.first == "interfaces") {
|
||||
// The interface parser is independent from any other parser and
|
||||
// can be run here before other parsers.
|
||||
parser->build(config_pair.second);
|
||||
iface_parser = parser;
|
||||
} else {
|
||||
// Those parsers should be started before other
|
||||
// parsers so we can call build straight away.
|
||||
@ -548,6 +554,10 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
if (subnet_parser) {
|
||||
subnet_parser->commit();
|
||||
}
|
||||
|
||||
if (iface_parser) {
|
||||
iface_parser->commit();
|
||||
}
|
||||
}
|
||||
catch (const isc::Exception& ex) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
|
||||
|
@ -31,8 +31,8 @@ class Dhcpv6Srv;
|
||||
|
||||
/// @brief Configures DHCPv6 server
|
||||
///
|
||||
/// This function is called every time a new configuration is received. The
|
||||
/// extra parameter is a reference to DHCPv6 server component. It is currently
|
||||
/// This function is called every time a new configuration is received. The
|
||||
/// extra parameter is a reference to DHCPv6 server component. It is currently
|
||||
/// not used and CfgMgr::instance() is accessed instead.
|
||||
///
|
||||
/// This method does not throw. It catches all exceptions and returns them as
|
||||
@ -53,7 +53,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set);
|
||||
///
|
||||
/// @returns a reference to the global context
|
||||
ParserContextPtr& globalContext();
|
||||
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@ -20,6 +20,7 @@
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcpsrv/dhcp_config_parser.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcp6/config_parser.h>
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
@ -100,7 +101,27 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
|
||||
}
|
||||
|
||||
// Configure the server.
|
||||
return (configureDhcp6Server(*server_, merged_config));
|
||||
ConstElementPtr answer = configureDhcp6Server(*server_, merged_config);
|
||||
|
||||
// Check that configuration was successful. If not, do not reopen sockets.
|
||||
int rcode = 0;
|
||||
parseAnswer(rcode, answer);
|
||||
if (rcode != 0) {
|
||||
return (answer);
|
||||
}
|
||||
|
||||
// Configuration may change active interfaces. Therefore, we have to reopen
|
||||
// sockets according to new configuration. This operation is not exception
|
||||
// safe and we really don't want to emit exceptions to the callback caller.
|
||||
// Instead, catch an exception and create appropriate answer.
|
||||
try {
|
||||
server_->openActiveSockets(server_->getPort());
|
||||
} catch (const std::exception& ex) {
|
||||
std::ostringstream err;
|
||||
err << "failed to open sockets after server reconfiguration: " << ex.what();
|
||||
answer = isc::config::createAnswer(1, err.str());
|
||||
}
|
||||
return (answer);
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
@ -172,8 +193,13 @@ void ControlledDhcpv6Srv::establishSession() {
|
||||
try {
|
||||
// Pull the full configuration out from the session.
|
||||
configureDhcp6Server(*this, config_session_->getFullConfig());
|
||||
// Configuration may disable or enable interfaces so we have to
|
||||
// reopen sockets according to new configuration.
|
||||
openActiveSockets(getPort());
|
||||
|
||||
} catch (const DhcpConfigError& ex) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
|
||||
|
||||
}
|
||||
|
||||
/// Integrate the asynchronous I/O model of BIND 10 configuration
|
||||
@ -228,6 +254,5 @@ ControlledDhcpv6Srv::execDhcpv6ServerCommand(const std::string& command_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -3,16 +3,16 @@
|
||||
"module_name": "Dhcp6",
|
||||
"module_description": "DHCPv6 server daemon",
|
||||
"config_data": [
|
||||
{ "item_name": "interface",
|
||||
{ "item_name": "interfaces",
|
||||
"item_type": "list",
|
||||
"item_optional": false,
|
||||
"item_default": [ "all" ],
|
||||
"item_default": [ "*" ],
|
||||
"list_item_spec":
|
||||
{
|
||||
"item_name": "interface_name",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": "all"
|
||||
"item_default": "*"
|
||||
}
|
||||
} ,
|
||||
|
||||
|
@ -14,6 +14,11 @@
|
||||
|
||||
$NAMESPACE isc::dhcp
|
||||
|
||||
% DHCP6_ACTIVATE_INTERFACE activating interface %1
|
||||
This message is printed when DHCPv6 server enabled an interface to be used
|
||||
to receive DHCPv6 traffic. IPv6 socket on this interface will be opened once
|
||||
Interface Manager starts up procedure of opening sockets.
|
||||
|
||||
% DHCP6_CCSESSION_STARTED control channel session started on socket %1
|
||||
A debug message issued during startup after the IPv6 DHCP server has
|
||||
successfully established a session with the BIND 10 control channel.
|
||||
@ -65,6 +70,11 @@ This informational message is printed every time the IPv6 DHCP server
|
||||
is started. It indicates what database backend type is being to store
|
||||
lease and other information.
|
||||
|
||||
% DHCP6_DEACTIVATE_INTERFACE deactivate interface %1
|
||||
This message is printed when DHCPv6 server disables an interface from being
|
||||
used to receive DHCPv6 traffic. Sockets on this interface will not be opened
|
||||
by the Interface Manager until interface is enabled.
|
||||
|
||||
% DHCP6_HOOK_PACKET_RCVD_SKIP received DHCPv6 packet was dropped, because a callout set skip flag.
|
||||
This debug message is printed when a callout installed on pkt6_received
|
||||
hook point sets skip flag. For this particular hook point, the setting
|
||||
@ -120,6 +130,11 @@ IPv6 DHCP server but it is not running.
|
||||
During startup the IPv6 DHCP server failed to detect any network
|
||||
interfaces and is therefore shutting down.
|
||||
|
||||
% DHCP6_NO_SOCKETS_OPEN no interface configured to listen to DHCP traffic
|
||||
This warning message is issued when current server configuration specifies
|
||||
no interfaces that server should listen on, or specified interfaces are not
|
||||
configured to receive the traffic.
|
||||
|
||||
% DHCP6_OPEN_SOCKET opening sockets on port %1
|
||||
A debug message issued during startup, this indicates that the IPv6 DHCP
|
||||
server is about to open sockets on the specified port.
|
||||
|
@ -95,7 +95,7 @@ static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
|
||||
|
||||
Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
|
||||
:alloc_engine_(), serverid_(), shutdown_(true), hook_index_pkt6_receive_(-1),
|
||||
hook_index_subnet6_select_(-1), hook_index_pkt6_send_(-1)
|
||||
hook_index_subnet6_select_(-1), hook_index_pkt6_send_(-1), port_(port)
|
||||
{
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
|
||||
@ -111,7 +111,7 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
|
||||
return;
|
||||
}
|
||||
IfaceMgr::instance().openSockets6(port);
|
||||
IfaceMgr::instance().openSockets6(port_);
|
||||
}
|
||||
|
||||
string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
|
||||
@ -1255,5 +1255,47 @@ isc::hooks::CalloutHandlePtr Dhcpv6Srv::getCalloutHandle(const Pkt6Ptr& pkt) {
|
||||
return (callout_handle);
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv6Srv::openActiveSockets(const uint16_t port) {
|
||||
IfaceMgr::instance().closeSockets();
|
||||
|
||||
// Get the reference to the collection of interfaces. This reference should be
|
||||
// valid as long as the program is run because IfaceMgr is a singleton.
|
||||
// Therefore we can safely iterate over instances of all interfaces and modify
|
||||
// their flags. Here we modify flags which indicate wheter socket should be
|
||||
// open for a particular interface or not.
|
||||
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
|
||||
for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
|
||||
iface != ifaces.end(); ++iface) {
|
||||
Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
|
||||
if (iface_ptr == NULL) {
|
||||
isc_throw(isc::Unexpected, "Interface Manager returned NULL"
|
||||
<< " instance of the interface when DHCPv6 server was"
|
||||
<< " trying to reopen sockets after reconfiguration");
|
||||
}
|
||||
if (CfgMgr::instance().isActiveIface(iface->getName())) {
|
||||
iface_ptr->inactive4_ = false;
|
||||
LOG_INFO(dhcp6_logger, DHCP6_ACTIVATE_INTERFACE)
|
||||
.arg(iface->getFullName());
|
||||
|
||||
} else {
|
||||
// For deactivating interface, it should be sufficient to log it
|
||||
// on the debug level because it is more useful to know what
|
||||
// interface is activated which is logged on the info level.
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
|
||||
DHCP6_DEACTIVATE_INTERFACE).arg(iface->getName());
|
||||
iface_ptr->inactive6_ = true;
|
||||
|
||||
}
|
||||
}
|
||||
// Let's reopen active sockets. openSockets6 will check internally whether
|
||||
// sockets are marked active or inactive.
|
||||
// @todo Optimization: we should not reopen all sockets but rather select
|
||||
// those that have been affected by the new configuration.
|
||||
if (!IfaceMgr::instance().openSockets6(port)) {
|
||||
LOG_WARN(dhcp6_logger, DHCP6_NO_SOCKETS_OPEN);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -88,6 +88,32 @@ public:
|
||||
/// @brief Instructs the server to shut down.
|
||||
void shutdown();
|
||||
|
||||
/// @brief Get UDP port on which server should listen.
|
||||
///
|
||||
/// Typically, server listens on UDP port 547. Other ports are only
|
||||
/// used for testing purposes.
|
||||
///
|
||||
/// This accessor must be public because sockets are reopened from the
|
||||
/// static configuration callback handler. This callback handler invokes
|
||||
/// @c ControlledDhcpv4Srv::openActiveSockets which requires port parameter
|
||||
/// which has to be retrieved from the @c ControlledDhcpv4Srv object.
|
||||
/// They are retrieved using this public function.
|
||||
///
|
||||
/// @return UDP port on which server should listen.
|
||||
uint16_t getPort() const {
|
||||
return (port_);
|
||||
}
|
||||
|
||||
/// @brief Open sockets which are marked as active in @c CfgMgr.
|
||||
///
|
||||
/// This function reopens sockets according to the current settings in the
|
||||
/// Configuration Manager. It holds the list of the interfaces which server
|
||||
/// should listen on. This function will open sockets on these interfaces
|
||||
/// only. This function is not exception safe.
|
||||
///
|
||||
/// @param port UDP port on which server should listen.
|
||||
static void openActiveSockets(const uint16_t port);
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief verifies if specified packet meets RFC requirements
|
||||
@ -361,6 +387,9 @@ private:
|
||||
int hook_index_pkt6_receive_;
|
||||
int hook_index_subnet6_select_;
|
||||
int hook_index_pkt6_send_;
|
||||
|
||||
/// UDP port number on which server listens.
|
||||
uint16_t port_;
|
||||
};
|
||||
|
||||
}; // namespace isc::dhcp
|
||||
|
@ -66,11 +66,12 @@ public:
|
||||
<< " while the test assumes that it doesn't, to execute"
|
||||
<< " some negative scenarios. Can't continue this test.";
|
||||
}
|
||||
|
||||
// Reset configuration for each test.
|
||||
resetConfiguration();
|
||||
}
|
||||
|
||||
~Dhcp6ParserTest() {
|
||||
// Reset configuration database after each test.
|
||||
resetConfiguration();
|
||||
};
|
||||
|
||||
// Checks if config_result (result of DHCP server configuration) has
|
||||
@ -133,7 +134,7 @@ public:
|
||||
std::string>& params)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "{ \"interface\": [ \"all\" ],"
|
||||
stream << "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -173,13 +174,13 @@ public:
|
||||
///
|
||||
/// This function resets configuration data base by
|
||||
/// removing all subnets and option-data. Reset must
|
||||
/// be performed after each test to make sure that
|
||||
/// be performed before each test to make sure that
|
||||
/// contents of the database do not affect result of
|
||||
/// subsequent tests.
|
||||
/// the test being executed.
|
||||
void resetConfiguration() {
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -213,6 +214,12 @@ public:
|
||||
<< " after the test. Configuration function returned"
|
||||
<< " error code " << rcode_ << std::endl;
|
||||
}
|
||||
|
||||
// The default setting is to listen on all interfaces. In order to
|
||||
// properly test interface configuration we disable listening on
|
||||
// all interfaces before each test and later check that this setting
|
||||
// has been overriden by the configuration used in the test.
|
||||
CfgMgr::instance().deleteActiveIfaces();
|
||||
}
|
||||
|
||||
/// @brief Test invalid option parameter value.
|
||||
@ -324,7 +331,7 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
|
||||
ConstElementPtr status;
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
|
||||
Element::fromJSON("{ \"interface\": [ \"all\" ],"
|
||||
Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -343,7 +350,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -377,7 +384,7 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -415,7 +422,7 @@ TEST_F(Dhcp6ParserTest, subnetInterface) {
|
||||
|
||||
// There should be at least one interface
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -448,7 +455,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
|
||||
|
||||
// There should be at least one interface
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -479,7 +486,7 @@ TEST_F(Dhcp6ParserTest, interfaceGlobal) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -549,7 +556,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
|
||||
// parameter.
|
||||
TEST_F(Dhcp6ParserTest, interfaceIdGlobal) {
|
||||
|
||||
const string config = "{ \"interface\": [ \"all\" ],"
|
||||
const string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -604,7 +611,7 @@ TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -632,7 +639,7 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
|
||||
|
||||
ConstElementPtr x;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -1152,7 +1159,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
|
||||
// configuration does not include options configuration.
|
||||
TEST_F(Dhcp6ParserTest, optionDataDefaults) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
@ -1234,7 +1241,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
|
||||
// The definition is not required for the option that
|
||||
// belongs to the 'dhcp6' option space as it is the
|
||||
// standard option.
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1312,7 +1319,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
|
||||
// at the very end (when all other parameters are configured).
|
||||
|
||||
// Starting stage 1. Configure sub-options and their definitions.
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1361,7 +1368,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
|
||||
// the configuration from the stage 2 is repeated because BIND
|
||||
// configuration manager sends whole configuration for the lists
|
||||
// where at least one element is being modified or added.
|
||||
config = "{ \"interface\": [ \"all\" ],"
|
||||
config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1455,7 +1462,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
|
||||
// for multiple subnets.
|
||||
TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -1698,7 +1705,7 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
|
||||
// In the first stahe we create definitions of suboptions
|
||||
// that we will add to the base option.
|
||||
// Let's create some dummy options: foo and foo2.
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1751,7 +1758,7 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
|
||||
// We add our dummy options to this option space and thus
|
||||
// they should be included as sub-options in the 'vendor-opts'
|
||||
// option.
|
||||
config = "{ \"interface\": [ \"all\" ],"
|
||||
config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000,"
|
||||
"\"renew-timer\": 1000,"
|
||||
"\"option-data\": [ {"
|
||||
@ -1850,4 +1857,77 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
|
||||
EXPECT_FALSE(desc.option->getOption(112));
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to select subset of interfaces on
|
||||
// which server should listen.
|
||||
TEST_F(Dhcp6ParserTest, selectedInterfaces) {
|
||||
|
||||
// Make sure there is no garbage interface configuration in the CfgMgr.
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interfaces\": [ \"eth0\", \"eth1\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||
|
||||
// returned value must be 1 (values error)
|
||||
// as the pool does not belong to that subnet
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
|
||||
// eth0 and eth1 were explicitly selected. eth2 was not.
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
EXPECT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to configure the server to listen on
|
||||
// all interfaces.
|
||||
TEST_F(Dhcp6ParserTest, allInterfaces) {
|
||||
|
||||
// Make sure there is no garbage interface configuration in the CfgMgr.
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
// This configuration specifies two interfaces on which server should listen
|
||||
// bu also includes keyword 'all'. This keyword switches server into the
|
||||
// mode when it listens on all interfaces regardless of what interface names
|
||||
// were specified in the "interfaces" parameter.
|
||||
string config = "{ \"interfaces\": [ \"eth0\", \"eth1\", \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||
|
||||
// returned value must be 1 (values error)
|
||||
// as the pool does not belong to that subnet
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
|
||||
// All interfaces should be now active.
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
@ -632,7 +632,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
|
||||
// and the requested options are actually assigned.
|
||||
TEST_F(Dhcpv6SrvTest, advertiseOptions) {
|
||||
ConstElementPtr x;
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"all\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -2409,7 +2409,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
|
||||
|
||||
// Configure 2 subnets, both directly reachable over local interface
|
||||
// (let's not complicate the matter with relays)
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -2477,7 +2477,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
|
||||
|
||||
// Configure 2 subnets, both directly reachable over local interface
|
||||
// (let's not complicate the matter with relays)
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
string config = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
|
@ -51,7 +51,8 @@ IfaceMgr::instance() {
|
||||
Iface::Iface(const std::string& name, int ifindex)
|
||||
:name_(name), ifindex_(ifindex), mac_len_(0), hardware_type_(0),
|
||||
flag_loopback_(false), flag_up_(false), flag_running_(false),
|
||||
flag_multicast_(false), flag_broadcast_(false), flags_(0)
|
||||
flag_multicast_(false), flag_broadcast_(false), flags_(0),
|
||||
inactive4_(false), inactive6_(false)
|
||||
{
|
||||
memset(mac_, 0, sizeof(mac_));
|
||||
}
|
||||
@ -295,7 +296,8 @@ bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
|
||||
|
||||
if (iface->flag_loopback_ ||
|
||||
!iface->flag_up_ ||
|
||||
!iface->flag_running_) {
|
||||
!iface->flag_running_ ||
|
||||
iface->inactive4_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -361,7 +363,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
|
||||
|
||||
if (iface->flag_loopback_ ||
|
||||
!iface->flag_up_ ||
|
||||
!iface->flag_running_) {
|
||||
!iface->flag_running_ ||
|
||||
iface->inactive6_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -309,6 +309,14 @@ public:
|
||||
/// Interface flags (this value is as is returned by OS,
|
||||
/// it may mean different things on different OSes).
|
||||
uint32_t flags_;
|
||||
|
||||
/// Indicates that IPv4 sockets should (true) or should not (false)
|
||||
/// be opened on this interface.
|
||||
bool inactive4_;
|
||||
|
||||
/// Indicates that IPv6 sockets should (true) or should not (false)
|
||||
/// be opened on this interface.
|
||||
bool inactive6_;
|
||||
};
|
||||
|
||||
/// @brief Handles network interfaces, transmission and reception.
|
||||
|
@ -267,9 +267,61 @@ std::string CfgMgr::getDataDir() {
|
||||
return (datadir_);
|
||||
}
|
||||
|
||||
void
|
||||
CfgMgr::addActiveIface(const std::string& iface) {
|
||||
if (isIfaceListedActive(iface)) {
|
||||
isc_throw(DuplicateListeningIface,
|
||||
"attempt to add duplicate interface '" << iface << "'"
|
||||
" to the set of interfaces on which server listens");
|
||||
}
|
||||
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_IFACE)
|
||||
.arg(iface);
|
||||
active_ifaces_.push_back(iface);
|
||||
}
|
||||
|
||||
void
|
||||
CfgMgr::activateAllIfaces() {
|
||||
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
|
||||
DHCPSRV_CFGMGR_ALL_IFACES_ACTIVE);
|
||||
all_ifaces_active_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
CfgMgr::deleteActiveIfaces() {
|
||||
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
|
||||
DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES);
|
||||
active_ifaces_.clear();
|
||||
all_ifaces_active_ = false;
|
||||
}
|
||||
|
||||
bool
|
||||
CfgMgr::isActiveIface(const std::string& iface) const {
|
||||
|
||||
// @todo Verify that the interface with the specified name is
|
||||
// present in the system.
|
||||
|
||||
// If all interfaces are marked active, there is no need to check that
|
||||
// the name of this interface has been explicitly listed.
|
||||
if (all_ifaces_active_) {
|
||||
return (true);
|
||||
}
|
||||
return (isIfaceListedActive(iface));
|
||||
}
|
||||
|
||||
bool
|
||||
CfgMgr::isIfaceListedActive(const std::string& iface) const {
|
||||
for (ActiveIfacesCollection::const_iterator it = active_ifaces_.begin();
|
||||
it != active_ifaces_.end(); ++it) {
|
||||
if (iface == *it) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
CfgMgr::CfgMgr()
|
||||
:datadir_(DHCP_DATA_DIR) {
|
||||
: datadir_(DHCP_DATA_DIR),
|
||||
all_ifaces_active_(false) {
|
||||
// DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
|
||||
// Note: the definition of DHCP_DATA_DIR needs to include quotation marks
|
||||
// See AM_CPPFLAGS definition in Makefile.am
|
||||
|
@ -30,10 +30,21 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief Exception thrown when the same interface has been specified twice.
|
||||
///
|
||||
/// In particular, this exception is thrown when adding interface to the set
|
||||
/// of interfaces on which server is supposed to listen.
|
||||
class DuplicateListeningIface : public Exception {
|
||||
public:
|
||||
DuplicateListeningIface(const char* file, size_t line, const char* what) :
|
||||
isc::Exception(file, line, what) { };
|
||||
};
|
||||
|
||||
|
||||
/// @brief Configuration Manager
|
||||
///
|
||||
@ -249,6 +260,43 @@ public:
|
||||
/// @return data directory
|
||||
std::string getDataDir();
|
||||
|
||||
/// @brief Adds the name of the interface to the set of interfaces on which
|
||||
/// server should listen.
|
||||
///
|
||||
/// @param iface A name of the interface being added to the listening set.
|
||||
void addActiveIface(const std::string& iface);
|
||||
|
||||
/// @brief Sets the flag which indicates that server is supposed to listen
|
||||
/// on all available interfaces.
|
||||
///
|
||||
/// This function does not close or open sockets. It simply marks that
|
||||
/// server should start to listen on all interfaces the next time sockets
|
||||
/// are reopened. Server should examine this flag when it gets reconfigured
|
||||
/// and configuration changes the interfaces that server should listen on.
|
||||
void activateAllIfaces();
|
||||
|
||||
/// @brief Clear the collection of the interfaces that server should listen
|
||||
/// on.
|
||||
///
|
||||
/// Apart from clearing the list of interfaces specified with
|
||||
/// @c CfgMgr::addListeningInterface, it also disables listening on all
|
||||
/// interfaces if it has been enabled using
|
||||
/// @c CfgMgr::activateAllInterfaces.
|
||||
/// Likewise @c CfgMgr::activateAllIfaces, this function does not close or
|
||||
/// open sockets. It marks all interfaces inactive for DHCP traffic.
|
||||
/// Server should examine this new setting when it attempts to
|
||||
/// reopen sockets (as a result of reconfiguration).
|
||||
void deleteActiveIfaces();
|
||||
|
||||
/// @brief Check if specified interface should be used to listen to DHCP
|
||||
/// traffic.
|
||||
///
|
||||
/// @param iface A name of the interface to be checked.
|
||||
///
|
||||
/// @return true if the specified interface belongs to the set of the
|
||||
/// interfaces on which server is configured to listen.
|
||||
bool isActiveIface(const std::string& iface) const;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Protected constructor.
|
||||
@ -280,6 +328,20 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Checks if the specified interface is listed as active.
|
||||
///
|
||||
/// This function searches for the specified interface name on the list of
|
||||
/// active interfaces: @c CfgMgr::active_ifaces_. It does not take into
|
||||
/// account @c CfgMgr::all_ifaces_active_ flag. If this flag is set to true
|
||||
/// but the specified interface does not belong to
|
||||
/// @c CfgMgr::active_ifaces_, it will return false.
|
||||
///
|
||||
/// @param iface interface name.
|
||||
///
|
||||
/// @return true if specified interface belongs to
|
||||
/// @c CfgMgr::active_ifaces_.
|
||||
bool isIfaceListedActive(const std::string& iface) const;
|
||||
|
||||
/// @brief A collection of option definitions.
|
||||
///
|
||||
/// A collection of option definitions that can be accessed
|
||||
@ -295,6 +357,16 @@ private:
|
||||
|
||||
/// @brief directory where data files (e.g. server-id) are stored
|
||||
std::string datadir_;
|
||||
|
||||
/// @name A collection of interface names on which server listens.
|
||||
//@{
|
||||
typedef std::list<std::string> ActiveIfacesCollection;
|
||||
std::list<std::string> active_ifaces_;
|
||||
//@}
|
||||
|
||||
/// A flag which indicates that server should listen on all available
|
||||
/// interfaces.
|
||||
bool all_ifaces_active_;
|
||||
};
|
||||
|
||||
} // namespace isc::dhcp
|
||||
|
@ -33,6 +33,10 @@ using namespace isc::data;
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
namespace {
|
||||
const char* ALL_IFACES_KEYWORD = "*";
|
||||
}
|
||||
|
||||
// *********************** ParserContext *************************
|
||||
|
||||
ParserContext::ParserContext(Option::Universe universe):
|
||||
@ -53,17 +57,17 @@ ParserContext::ParserContext(const ParserContext& rhs):
|
||||
universe_(rhs.universe_) {
|
||||
}
|
||||
|
||||
ParserContext&
|
||||
ParserContext&
|
||||
ParserContext::operator=(const ParserContext& rhs) {
|
||||
if (this != &rhs) {
|
||||
boolean_values_ =
|
||||
boolean_values_ =
|
||||
BooleanStoragePtr(new BooleanStorage(*(rhs.boolean_values_)));
|
||||
uint32_values_ =
|
||||
uint32_values_ =
|
||||
Uint32StoragePtr(new Uint32Storage(*(rhs.uint32_values_)));
|
||||
string_values_ =
|
||||
string_values_ =
|
||||
StringStoragePtr(new StringStorage(*(rhs.string_values_)));
|
||||
options_ = OptionStoragePtr(new OptionStorage(*(rhs.options_)));
|
||||
option_defs_ =
|
||||
option_defs_ =
|
||||
OptionDefStoragePtr(new OptionDefStorage(*(rhs.option_defs_)));
|
||||
universe_ = rhs.universe_;
|
||||
}
|
||||
@ -77,14 +81,14 @@ DebugParser::DebugParser(const std::string& param_name)
|
||||
:param_name_(param_name) {
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
DebugParser::build(ConstElementPtr new_config) {
|
||||
value_ = new_config;
|
||||
std::cout << "Build for token: [" << param_name_ << "] = ["
|
||||
<< value_->str() << "]" << std::endl;
|
||||
<< value_->str() << "]" << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
DebugParser::commit() {
|
||||
// Debug message. The whole DebugParser class is used only for parser
|
||||
// debugging, and is not used in production code. It is very convenient
|
||||
@ -102,7 +106,7 @@ template<> void ValueParser<bool>::build(isc::data::ConstElementPtr value) {
|
||||
try {
|
||||
value_ = value->boolValue();
|
||||
} catch (const isc::data::TypeError &) {
|
||||
isc_throw(BadValue, " Wrong value type for " << param_name_
|
||||
isc_throw(BadValue, " Wrong value type for " << param_name_
|
||||
<< " : build called with a non-boolean element.");
|
||||
}
|
||||
}
|
||||
@ -140,33 +144,83 @@ template <> void ValueParser<std::string>::build(ConstElementPtr value) {
|
||||
|
||||
// ******************** InterfaceListConfigParser *************************
|
||||
|
||||
InterfaceListConfigParser::InterfaceListConfigParser(const std::string&
|
||||
param_name) {
|
||||
if (param_name != "interface") {
|
||||
InterfaceListConfigParser::
|
||||
InterfaceListConfigParser(const std::string& param_name)
|
||||
: activate_all_(false),
|
||||
param_name_(param_name) {
|
||||
if (param_name_ != "interfaces") {
|
||||
isc_throw(BadValue, "Internal error. Interface configuration "
|
||||
"parser called for the wrong parameter: " << param_name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
InterfaceListConfigParser::build(ConstElementPtr value) {
|
||||
// First, we iterate over all specified entries and add it to the
|
||||
// local container so as we can do some basic validation, e.g. eliminate
|
||||
// duplicates.
|
||||
BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
|
||||
interfaces_.push_back(iface->str());
|
||||
std::string iface_name = iface->stringValue();
|
||||
if (iface_name != ALL_IFACES_KEYWORD) {
|
||||
// Let's eliminate duplicates. We could possibly allow duplicates,
|
||||
// but if someone specified duplicated interface name it is likely
|
||||
// that he mistyped the configuration. Failing here should draw his
|
||||
// attention.
|
||||
if (isIfaceAdded(iface_name)) {
|
||||
isc_throw(isc::dhcp::DhcpConfigError, "duplicate interface"
|
||||
<< " name '" << iface_name << "' specified in '"
|
||||
<< param_name_ << "' configuration parameter");
|
||||
}
|
||||
// @todo check that this interface exists in the system!
|
||||
// The IfaceMgr exposes mechanisms to check this.
|
||||
|
||||
// Add the interface name if ok.
|
||||
interfaces_.push_back(iface_name);
|
||||
|
||||
} else {
|
||||
activate_all_ = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
InterfaceListConfigParser::commit() {
|
||||
/// @todo: Implement per interface listening. Currently always listening
|
||||
/// on all interfaces.
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
// Remove active interfaces and clear a flag which marks all interfaces
|
||||
// active
|
||||
cfg_mgr.deleteActiveIfaces();
|
||||
|
||||
if (activate_all_) {
|
||||
// Activate all interfaces. There is not need to add their names
|
||||
// explicitly.
|
||||
cfg_mgr.activateAllIfaces();
|
||||
|
||||
} else {
|
||||
// Explicitly add names of the interfaces which server should listen on.
|
||||
BOOST_FOREACH(std::string iface, interfaces_) {
|
||||
cfg_mgr.addActiveIface(iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceListConfigParser::isIfaceAdded(const std::string& iface) const {
|
||||
for (IfaceListStorage::const_iterator it = interfaces_.begin();
|
||||
it != interfaces_.end(); ++it) {
|
||||
if (iface == *it) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
// **************************** OptionDataParser *************************
|
||||
OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
|
||||
ParserContextPtr global_context)
|
||||
: boolean_values_(new BooleanStorage()),
|
||||
string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
|
||||
options_(options), option_descriptor_(false),
|
||||
: boolean_values_(new BooleanStorage()),
|
||||
string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
|
||||
options_(options), option_descriptor_(false),
|
||||
global_context_(global_context) {
|
||||
if (!options_) {
|
||||
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
|
||||
@ -179,22 +233,22 @@ OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDataParser::build(ConstElementPtr option_data_entries) {
|
||||
BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
|
||||
ParserPtr parser;
|
||||
if (param.first == "name" || param.first == "data" ||
|
||||
param.first == "space") {
|
||||
StringParserPtr name_parser(new StringParser(param.first,
|
||||
string_values_));
|
||||
StringParserPtr name_parser(new StringParser(param.first,
|
||||
string_values_));
|
||||
parser = name_parser;
|
||||
} else if (param.first == "code") {
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(param.first,
|
||||
uint32_values_));
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(param.first,
|
||||
uint32_values_));
|
||||
parser = code_parser;
|
||||
} else if (param.first == "csv-format") {
|
||||
BooleanParserPtr value_parser(new BooleanParser(param.first,
|
||||
boolean_values_));
|
||||
BooleanParserPtr value_parser(new BooleanParser(param.first,
|
||||
boolean_values_));
|
||||
parser = value_parser;
|
||||
} else {
|
||||
isc_throw(DhcpConfigError,
|
||||
@ -216,12 +270,12 @@ OptionDataParser::build(ConstElementPtr option_data_entries) {
|
||||
createOption();
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDataParser::commit() {
|
||||
if (!option_descriptor_.option) {
|
||||
// Before we can commit the new option should be configured. If it is
|
||||
// Before we can commit the new option should be configured. If it is
|
||||
// not than somebody must have called commit() before build().
|
||||
isc_throw(isc::InvalidOperation,
|
||||
isc_throw(isc::InvalidOperation,
|
||||
"parser logic error: no option has been configured and"
|
||||
" thus there is nothing to commit. Has build() been called?");
|
||||
}
|
||||
@ -245,7 +299,7 @@ OptionDataParser::commit() {
|
||||
options_->addItem(option_descriptor_, option_space_);
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDataParser::createOption() {
|
||||
// Option code is held in the uint32_t storage but is supposed to
|
||||
// be uint16_t value. We need to check that value in the configuration
|
||||
@ -262,7 +316,7 @@ OptionDataParser::createOption() {
|
||||
|
||||
// Check that the option name has been specified, is non-empty and does not
|
||||
// contain spaces
|
||||
std::string option_name = string_values_->getParam("name");
|
||||
std::string option_name = string_values_->getParam("name");
|
||||
if (option_name.empty()) {
|
||||
isc_throw(DhcpConfigError, "name of the option with code '"
|
||||
<< option_code << "' is empty");
|
||||
@ -271,7 +325,7 @@ OptionDataParser::createOption() {
|
||||
<< "', space character is not allowed");
|
||||
}
|
||||
|
||||
std::string option_space = string_values_->getParam("space");
|
||||
std::string option_space = string_values_->getParam("space");
|
||||
if (!OptionSpace::validateName(option_space)) {
|
||||
isc_throw(DhcpConfigError, "invalid option space name '"
|
||||
<< option_space << "' specified for option '"
|
||||
@ -287,7 +341,7 @@ OptionDataParser::createOption() {
|
||||
// need to search for its definition among user-configured
|
||||
// options. They are expected to be in the global storage
|
||||
// already.
|
||||
OptionDefContainerPtr defs =
|
||||
OptionDefContainerPtr defs =
|
||||
global_context_->option_defs_->getItems(option_space);
|
||||
|
||||
// The getItems() should never return the NULL pointer. If there are
|
||||
@ -341,16 +395,16 @@ OptionDataParser::createOption() {
|
||||
<< " does not have a definition.");
|
||||
}
|
||||
|
||||
// @todo We have a limited set of option definitions intiialized at
|
||||
// the moment. In the future we want to initialize option definitions
|
||||
// for all options. Consequently an error will be issued if an option
|
||||
// @todo We have a limited set of option definitions intiialized at
|
||||
// the moment. In the future we want to initialize option definitions
|
||||
// for all options. Consequently an error will be issued if an option
|
||||
// definition does not exist for a particular option code. For now it is
|
||||
// ok to create generic option if definition does not exist.
|
||||
OptionPtr option(new Option(global_context_->universe_,
|
||||
OptionPtr option(new Option(global_context_->universe_,
|
||||
static_cast<uint16_t>(option_code), binary));
|
||||
// The created option is stored in option_descriptor_ class member
|
||||
// until the commit stage when it is inserted into the main storage.
|
||||
// If an option with the same code exists in main storage already the
|
||||
// The created option is stored in option_descriptor_ class member
|
||||
// until the commit stage when it is inserted into the main storage.
|
||||
// If an option with the same code exists in main storage already the
|
||||
// old option is replaced.
|
||||
option_descriptor_.option = option;
|
||||
option_descriptor_.persistent = false;
|
||||
@ -372,9 +426,9 @@ OptionDataParser::createOption() {
|
||||
// an instance of our option.
|
||||
try {
|
||||
OptionPtr option = csv_format ?
|
||||
def->optionFactory(global_context_->universe_,
|
||||
def->optionFactory(global_context_->universe_,
|
||||
option_code, data_tokens) :
|
||||
def->optionFactory(global_context_->universe_,
|
||||
def->optionFactory(global_context_->universe_,
|
||||
option_code, binary);
|
||||
Subnet::OptionDescriptor desc(option, false);
|
||||
option_descriptor_.option = option;
|
||||
@ -392,10 +446,10 @@ OptionDataParser::createOption() {
|
||||
}
|
||||
|
||||
// **************************** OptionDataListParser *************************
|
||||
OptionDataListParser::OptionDataListParser(const std::string&,
|
||||
OptionDataListParser::OptionDataListParser(const std::string&,
|
||||
OptionStoragePtr options, ParserContextPtr global_context,
|
||||
OptionDataParserFactory* optionDataParserFactory)
|
||||
: options_(options), local_options_(new OptionStorage()),
|
||||
: options_(options), local_options_(new OptionStorage()),
|
||||
global_context_(global_context),
|
||||
optionDataParserFactory_(optionDataParserFactory) {
|
||||
if (!options_) {
|
||||
@ -414,11 +468,11 @@ OptionDataListParser::OptionDataListParser(const std::string&,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDataListParser::build(ConstElementPtr option_data_list) {
|
||||
BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
|
||||
boost::shared_ptr<OptionDataParser>
|
||||
parser((*optionDataParserFactory_)("option-data",
|
||||
boost::shared_ptr<OptionDataParser>
|
||||
parser((*optionDataParserFactory_)("option-data",
|
||||
local_options_, global_context_));
|
||||
|
||||
// options_ member will hold instances of all options thus
|
||||
@ -430,7 +484,7 @@ OptionDataListParser::build(ConstElementPtr option_data_list) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDataListParser::commit() {
|
||||
BOOST_FOREACH(ParserPtr parser, parsers_) {
|
||||
parser->commit();
|
||||
@ -443,7 +497,7 @@ OptionDataListParser::commit() {
|
||||
}
|
||||
|
||||
// ******************************** OptionDefParser ****************************
|
||||
OptionDefParser::OptionDefParser(const std::string&,
|
||||
OptionDefParser::OptionDefParser(const std::string&,
|
||||
OptionDefStoragePtr storage)
|
||||
: storage_(storage), boolean_values_(new BooleanStorage()),
|
||||
string_values_(new StringStorage()), uint32_values_(new Uint32Storage()) {
|
||||
@ -453,23 +507,23 @@ OptionDefParser::OptionDefParser(const std::string&,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDefParser::build(ConstElementPtr option_def) {
|
||||
// Parse the elements that make up the option definition.
|
||||
BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
|
||||
std::string entry(param.first);
|
||||
ParserPtr parser;
|
||||
if (entry == "name" || entry == "type" || entry == "record-types"
|
||||
if (entry == "name" || entry == "type" || entry == "record-types"
|
||||
|| entry == "space" || entry == "encapsulate") {
|
||||
StringParserPtr str_parser(new StringParser(entry,
|
||||
StringParserPtr str_parser(new StringParser(entry,
|
||||
string_values_));
|
||||
parser = str_parser;
|
||||
} else if (entry == "code") {
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(entry,
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(entry,
|
||||
uint32_values_));
|
||||
parser = code_parser;
|
||||
} else if (entry == "array") {
|
||||
BooleanParserPtr array_parser(new BooleanParser(entry,
|
||||
BooleanParserPtr array_parser(new BooleanParser(entry,
|
||||
boolean_values_));
|
||||
parser = array_parser;
|
||||
} else {
|
||||
@ -501,7 +555,7 @@ OptionDefParser::build(ConstElementPtr option_def) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDefParser::commit() {
|
||||
if (storage_ && option_definition_ &&
|
||||
OptionSpace::validateName(option_space_name_)) {
|
||||
@ -509,7 +563,7 @@ OptionDefParser::commit() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDefParser::createOptionDef() {
|
||||
// Get the option space name and validate it.
|
||||
std::string space = string_values_->getParam("space");
|
||||
@ -589,7 +643,7 @@ OptionDefParser::createOptionDef() {
|
||||
}
|
||||
|
||||
// ******************************** OptionDefListParser ************************
|
||||
OptionDefListParser::OptionDefListParser(const std::string&,
|
||||
OptionDefListParser::OptionDefListParser(const std::string&,
|
||||
OptionDefStoragePtr storage) :storage_(storage) {
|
||||
if (!storage_) {
|
||||
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
|
||||
@ -597,7 +651,7 @@ OptionDefListParser::OptionDefListParser(const std::string&,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDefListParser::build(ConstElementPtr option_def_list) {
|
||||
// Clear existing items in the storage.
|
||||
// We are going to replace all of them.
|
||||
@ -616,7 +670,7 @@ OptionDefListParser::build(ConstElementPtr option_def_list) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
OptionDefListParser::commit() {
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
cfg_mgr.deleteOptionDefs();
|
||||
@ -648,7 +702,7 @@ PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
PoolParser::build(ConstElementPtr pools_list) {
|
||||
BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
|
||||
// That should be a single pool representation. It should contain
|
||||
@ -676,7 +730,7 @@ PoolParser::build(ConstElementPtr pools_list) {
|
||||
// will result in interpreting the first digit as output
|
||||
// value and throwing exception if length is written on two
|
||||
// digits (because there are extra characters left over).
|
||||
|
||||
|
||||
// No checks for values over 128. Range correctness will
|
||||
// be checked in Pool4 constructor.
|
||||
len = boost::lexical_cast<int>(prefix_len);
|
||||
@ -708,7 +762,7 @@ PoolParser::build(ConstElementPtr pools_list) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
PoolParser::commit() {
|
||||
if (pools_) {
|
||||
// local_pools_ holds the values produced by the build function.
|
||||
@ -720,9 +774,9 @@ PoolParser::commit() {
|
||||
|
||||
//****************************** SubnetConfigParser *************************
|
||||
|
||||
SubnetConfigParser::SubnetConfigParser(const std::string&,
|
||||
ParserContextPtr global_context)
|
||||
: uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
|
||||
SubnetConfigParser::SubnetConfigParser(const std::string&,
|
||||
ParserContextPtr global_context)
|
||||
: uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
|
||||
pools_(new PoolStorage()), options_(new OptionStorage()),
|
||||
global_context_(global_context) {
|
||||
// The first parameter should always be "subnet", but we don't check
|
||||
@ -733,7 +787,7 @@ SubnetConfigParser::SubnetConfigParser(const std::string&,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
SubnetConfigParser::build(ConstElementPtr subnet) {
|
||||
BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
|
||||
ParserPtr parser(createSubnetConfigParser(param.first));
|
||||
@ -757,8 +811,8 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
|
||||
createSubnet();
|
||||
}
|
||||
|
||||
void
|
||||
SubnetConfigParser::appendSubOptions(const std::string& option_space,
|
||||
void
|
||||
SubnetConfigParser::appendSubOptions(const std::string& option_space,
|
||||
OptionPtr& option) {
|
||||
// Only non-NULL options are stored in option container.
|
||||
// If this option pointer is NULL this is a serious error.
|
||||
@ -812,7 +866,7 @@ SubnetConfigParser::appendSubOptions(const std::string& option_space,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
SubnetConfigParser::createSubnet() {
|
||||
std::string subnet_txt;
|
||||
try {
|
||||
@ -843,11 +897,11 @@ SubnetConfigParser::createSubnet() {
|
||||
isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
|
||||
uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
|
||||
|
||||
// Call the subclass's method to instantiate the subnet
|
||||
// Call the subclass's method to instantiate the subnet
|
||||
initSubnet(addr, len);
|
||||
|
||||
// Add pools to it.
|
||||
for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
|
||||
for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
|
||||
++it) {
|
||||
subnet_->addPool(*it);
|
||||
}
|
||||
@ -922,7 +976,7 @@ SubnetConfigParser::createSubnet() {
|
||||
// values we don't add option from the global storage
|
||||
// if there is one already.
|
||||
Subnet::OptionDescriptor existing_desc =
|
||||
subnet_->getOptionDescriptor(option_space,
|
||||
subnet_->getOptionDescriptor(option_space,
|
||||
desc.option->getType());
|
||||
if (!existing_desc.option) {
|
||||
// Add sub-options (if any).
|
||||
@ -933,15 +987,15 @@ SubnetConfigParser::createSubnet() {
|
||||
}
|
||||
}
|
||||
|
||||
isc::dhcp::Triplet<uint32_t>
|
||||
isc::dhcp::Triplet<uint32_t>
|
||||
SubnetConfigParser::getParam(const std::string& name) {
|
||||
uint32_t value = 0;
|
||||
try {
|
||||
// look for local value
|
||||
// look for local value
|
||||
value = uint32_values_->getParam(name);
|
||||
} catch (const DhcpConfigError &) {
|
||||
try {
|
||||
// no local, use global value
|
||||
// no local, use global value
|
||||
value = global_context_->uint32_values_->getParam(name);
|
||||
} catch (const DhcpConfigError &) {
|
||||
isc_throw(DhcpConfigError, "Mandatory parameter " << name
|
||||
|
@ -47,8 +47,8 @@ typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
|
||||
/// @brief A template class that stores named elements of a given data type.
|
||||
///
|
||||
/// This template class is provides data value storage for configuration parameters
|
||||
/// of a given data type. The values are stored by parameter name and as instances
|
||||
/// of type "ValueType".
|
||||
/// of a given data type. The values are stored by parameter name and as instances
|
||||
/// of type "ValueType".
|
||||
///
|
||||
/// @param ValueType is the data type of the elements to store.
|
||||
template<typename ValueType>
|
||||
@ -57,7 +57,7 @@ class ValueStorage {
|
||||
/// @brief Stores the the parameter and its value in the store.
|
||||
///
|
||||
/// If the parameter does not exist in the store, then it will be added,
|
||||
/// otherwise its data value will be updated with the given value.
|
||||
/// otherwise its data value will be updated with the given value.
|
||||
///
|
||||
/// @param name is the name of the paramater to store.
|
||||
/// @param value is the data value to store.
|
||||
@ -71,10 +71,10 @@ class ValueStorage {
|
||||
/// @param name is the name of the parameter for which the data
|
||||
/// value is desired.
|
||||
///
|
||||
/// @return The paramater's data value of type <ValueType>.
|
||||
/// @return The paramater's data value of type @c ValueType.
|
||||
/// @throw DhcpConfigError if the parameter is not found.
|
||||
ValueType getParam(const std::string& name) const {
|
||||
typename std::map<std::string, ValueType>::const_iterator param
|
||||
typename std::map<std::string, ValueType>::const_iterator param
|
||||
= values_.find(name);
|
||||
|
||||
if (param == values_.end()) {
|
||||
@ -87,8 +87,8 @@ class ValueStorage {
|
||||
|
||||
/// @brief Remove the parameter from the store.
|
||||
///
|
||||
/// Deletes the entry for the given parameter from the store if it
|
||||
/// exists.
|
||||
/// Deletes the entry for the given parameter from the store if it
|
||||
/// exists.
|
||||
///
|
||||
/// @param name is the name of the paramater to delete.
|
||||
void delParam(const std::string& name) {
|
||||
@ -108,7 +108,7 @@ class ValueStorage {
|
||||
};
|
||||
|
||||
|
||||
/// @brief a collection of elements that store uint32 values
|
||||
/// @brief a collection of elements that store uint32 values
|
||||
typedef ValueStorage<uint32_t> Uint32Storage;
|
||||
typedef boost::shared_ptr<Uint32Storage> Uint32StoragePtr;
|
||||
|
||||
@ -128,9 +128,9 @@ typedef boost::shared_ptr<BooleanStorage> BooleanStoragePtr;
|
||||
class ParserContext {
|
||||
public:
|
||||
/// @brief Constructor
|
||||
///
|
||||
///
|
||||
/// @param universe is the Option::Universe value of this
|
||||
/// context.
|
||||
/// context.
|
||||
ParserContext(Option::Universe universe);
|
||||
|
||||
/// @brief Copy constructor
|
||||
@ -161,12 +161,12 @@ public:
|
||||
/// @brief Pointer to various parser context.
|
||||
typedef boost::shared_ptr<ParserContext> ParserContextPtr;
|
||||
|
||||
/// @brief Simple data-type parser template class
|
||||
/// @brief Simple data-type parser template class
|
||||
///
|
||||
/// This is the template class for simple data-type parsers. It supports
|
||||
/// parsing a configuration parameter with specific data-type for its
|
||||
/// possible values. It provides a common constructor, commit, and templated
|
||||
/// data storage. The "build" method implementation must be provided by a
|
||||
/// parsing a configuration parameter with specific data-type for its
|
||||
/// possible values. It provides a common constructor, commit, and templated
|
||||
/// data storage. The "build" method implementation must be provided by a
|
||||
/// declaring type.
|
||||
/// @param ValueType is the data type of the configuration paramater value
|
||||
/// the parser should handle.
|
||||
@ -182,7 +182,7 @@ public:
|
||||
/// @throw isc::dhcp::DhcpConfigError if a provided parameter's
|
||||
/// name is empty.
|
||||
/// @throw isc::dhcp::DhcpConfigError if storage is null.
|
||||
ValueParser(const std::string& param_name,
|
||||
ValueParser(const std::string& param_name,
|
||||
boost::shared_ptr<ValueStorage<ValueType> > storage)
|
||||
: storage_(storage), param_name_(param_name), value_() {
|
||||
// Empty parameter name is invalid.
|
||||
@ -199,12 +199,12 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/// @brief Parse a given element into a value of type <ValueType>
|
||||
/// @brief Parse a given element into a value of type @c ValueType
|
||||
///
|
||||
/// @param value a value to be parsed.
|
||||
///
|
||||
/// @throw isc::BadValue Typically the implementing type will throw
|
||||
/// a BadValue exception when given an invalid Element to parse.
|
||||
/// a BadValue exception when given an invalid Element to parse.
|
||||
void build(isc::data::ConstElementPtr value);
|
||||
|
||||
/// @brief Put a parsed value to the storage.
|
||||
@ -213,7 +213,7 @@ public:
|
||||
// its value. If it doesn't we insert a new element.
|
||||
storage_->setParam(param_name_, value_);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/// Pointer to the storage where committed value is stored.
|
||||
boost::shared_ptr<ValueStorage<ValueType> > storage_;
|
||||
@ -302,8 +302,23 @@ public:
|
||||
virtual void commit();
|
||||
|
||||
private:
|
||||
/// @brief Check that specified interface exists in
|
||||
/// @c InterfaceListConfigParser::interfaces_.
|
||||
///
|
||||
/// @param iface A name of the interface.
|
||||
///
|
||||
/// @return true if specified interface name was found.
|
||||
bool isIfaceAdded(const std::string& iface) const;
|
||||
|
||||
/// contains list of network interfaces
|
||||
std::vector<std::string> interfaces_;
|
||||
typedef std::list<std::string> IfaceListStorage;
|
||||
IfaceListStorage interfaces_;
|
||||
|
||||
// Should server listen on all interfaces.
|
||||
bool activate_all_;
|
||||
|
||||
// Parsed parameter name
|
||||
std::string param_name_;
|
||||
};
|
||||
|
||||
|
||||
@ -332,11 +347,11 @@ public:
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param options is the option storage in which to store the parsed option
|
||||
/// upon "commit".
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
/// upon "commit".
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
/// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
|
||||
OptionDataParser(const std::string&, OptionStoragePtr options,
|
||||
OptionDataParser(const std::string& dummy, OptionStoragePtr options,
|
||||
ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parses the single option data.
|
||||
@ -356,31 +371,31 @@ public:
|
||||
|
||||
/// @brief Commits option value.
|
||||
///
|
||||
/// This function adds a new option to the storage or replaces an existing
|
||||
/// This function adds a new option to the storage or replaces an existing
|
||||
/// option with the same code.
|
||||
///
|
||||
/// @throw isc::InvalidOperation if failed to set pointer to storage or
|
||||
/// @throw isc::InvalidOperation if failed to set pointer to storage or
|
||||
/// failed
|
||||
/// to call build() prior to commit. If that happens data in the storage
|
||||
/// remain un-modified.
|
||||
virtual void commit();
|
||||
|
||||
/// @brief virtual destructor to ensure orderly destruction of derivations.
|
||||
/// @brief virtual destructor to ensure orderly destruction of derivations.
|
||||
virtual ~OptionDataParser(){};
|
||||
|
||||
protected:
|
||||
/// @brief Finds an option definition within the server's option space
|
||||
///
|
||||
/// Given an option space and an option code, find the correpsonding
|
||||
///
|
||||
/// Given an option space and an option code, find the correpsonding
|
||||
/// option defintion within the server's option defintion storage. This
|
||||
/// method is pure virtual requiring derivations to manage which option
|
||||
/// space(s) is valid for search.
|
||||
///
|
||||
/// @param option_space name of the parameter option space
|
||||
/// @param option_code numeric value of the parameter to find
|
||||
/// @return OptionDefintionPtr of the option defintion or an
|
||||
/// @param option_space name of the parameter option space
|
||||
/// @param option_code numeric value of the parameter to find
|
||||
/// @return OptionDefintionPtr of the option defintion or an
|
||||
/// empty OptionDefinitionPtr if not found.
|
||||
/// @throw DhcpConfigError if the option space requested is not valid
|
||||
/// @throw DhcpConfigError if the option space requested is not valid
|
||||
/// for this server.
|
||||
virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
|
||||
std::string& option_space, uint32_t option_code) = 0;
|
||||
@ -420,13 +435,13 @@ private:
|
||||
/// Option space name where the option belongs to.
|
||||
std::string option_space_;
|
||||
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
};
|
||||
|
||||
///@brief Function pointer for OptionDataParser factory methods
|
||||
typedef OptionDataParser *OptionDataParserFactory(const std::string&,
|
||||
typedef OptionDataParser *OptionDataParserFactory(const std::string&,
|
||||
OptionStoragePtr options, ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parser for option data values within a subnet.
|
||||
@ -439,15 +454,15 @@ class OptionDataListParser : public DhcpConfigParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param string& nominally would be param name, this is always ignored.
|
||||
/// @param dummy nominally would be param name, this is always ignored.
|
||||
/// @param options parsed option storage for options in this list
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
/// @param optionDataParserFactory factory method for creating individual
|
||||
/// option parsers
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
/// @param optionDataParserFactory factory method for creating individual
|
||||
/// option parsers
|
||||
/// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
|
||||
OptionDataListParser(const std::string&, OptionStoragePtr options,
|
||||
ParserContextPtr global_context,
|
||||
OptionDataListParser(const std::string& dummy, OptionStoragePtr options,
|
||||
ParserContextPtr global_context,
|
||||
OptionDataParserFactory *optionDataParserFactory);
|
||||
|
||||
/// @brief Parses entries that define options' data for a subnet.
|
||||
@ -477,7 +492,7 @@ private:
|
||||
/// Collection of parsers;
|
||||
ParserCollection parsers_;
|
||||
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
|
||||
@ -495,10 +510,10 @@ public:
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param storage is the definition storage in which to store the parsed
|
||||
/// definition upon "commit".
|
||||
/// @param storage is the definition storage in which to store the parsed
|
||||
/// definition upon "commit".
|
||||
/// @throw isc::dhcp::DhcpConfigError if storage is null.
|
||||
OptionDefParser(const std::string&, OptionDefStoragePtr storage);
|
||||
OptionDefParser(const std::string& dummy, OptionDefStoragePtr storage);
|
||||
|
||||
/// @brief Parses an entry that describes single option definition.
|
||||
///
|
||||
@ -546,10 +561,10 @@ public:
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param storage is the definition storage in which to store the parsed
|
||||
/// definitions in this list
|
||||
/// @param storage is the definition storage in which to store the parsed
|
||||
/// definitions in this list
|
||||
/// @throw isc::dhcp::DhcpConfigError if storage is null.
|
||||
OptionDefListParser(const std::string&, OptionDefStoragePtr storage);
|
||||
OptionDefListParser(const std::string& dummy, OptionDefStoragePtr storage);
|
||||
|
||||
/// @brief Parse configuration entries.
|
||||
///
|
||||
@ -566,7 +581,7 @@ public:
|
||||
|
||||
private:
|
||||
/// @brief storage for option definitions.
|
||||
OptionDefStoragePtr storage_;
|
||||
OptionDefStoragePtr storage_;
|
||||
};
|
||||
|
||||
/// @brief a collection of pools
|
||||
@ -587,14 +602,13 @@ class PoolParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor.
|
||||
|
||||
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param pools is the storage in which to store the parsed pool
|
||||
/// upon "commit".
|
||||
/// @param pools is the storage in which to store the parsed pool
|
||||
/// upon "commit".
|
||||
/// @throw isc::dhcp::DhcpConfigError if storage is null.
|
||||
PoolParser(const std::string&, PoolStoragePtr pools);
|
||||
PoolParser(const std::string& dummy, PoolStoragePtr pools);
|
||||
|
||||
/// @brief parses the actual list
|
||||
///
|
||||
@ -614,9 +628,9 @@ protected:
|
||||
///
|
||||
/// @param addr is the IP prefix of the pool.
|
||||
/// @param len is the prefix length.
|
||||
/// @param ignored dummy parameter to provide symmetry between
|
||||
/// @return returns a PoolPtr to the new Pool object.
|
||||
virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len,
|
||||
/// @param ptype is the type of pool to create.
|
||||
/// @return returns a PoolPtr to the new Pool object.
|
||||
virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len,
|
||||
int32_t ptype=0) = 0;
|
||||
|
||||
/// @brief Creates a Pool object given starting and ending IP addresses.
|
||||
@ -625,7 +639,7 @@ protected:
|
||||
/// @param max is the last IP address in the pool.
|
||||
/// @param ptype is the type of pool to create (not used by all derivations)
|
||||
/// @return returns a PoolPtr to the new Pool object.
|
||||
virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min,
|
||||
virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min,
|
||||
isc::asiolink::IOAddress &max, int32_t ptype=0) = 0;
|
||||
|
||||
/// @brief pointer to the actual Pools storage
|
||||
@ -654,7 +668,7 @@ public:
|
||||
/// @param subnet pointer to the content of subnet definition
|
||||
///
|
||||
/// @throw isc::DhcpConfigError if subnet configuration parsing failed.
|
||||
virtual void build(isc::data::ConstElementPtr subnet);
|
||||
virtual void build(isc::data::ConstElementPtr subnet);
|
||||
|
||||
/// @brief Adds the created subnet to a server's configuration.
|
||||
virtual void commit() = 0;
|
||||
@ -671,7 +685,7 @@ protected:
|
||||
const std::string& config_id) = 0;
|
||||
|
||||
/// @brief Determines if the given option space name and code describe
|
||||
/// a standard option for the server.
|
||||
/// a standard option for the server.
|
||||
///
|
||||
/// @param option_space is the name of the option space to consider
|
||||
/// @param code is the numeric option code to consider
|
||||
@ -687,20 +701,20 @@ protected:
|
||||
uint32_t code) = 0;
|
||||
|
||||
/// @brief Issues a server specific warning regarding duplicate subnet
|
||||
/// options.
|
||||
///
|
||||
/// options.
|
||||
///
|
||||
/// @param code is the numeric option code of the duplicate option
|
||||
/// @param addr is the subnet address
|
||||
/// @param addr is the subnet address
|
||||
/// @todo a means to know the correct logger and perhaps a common
|
||||
/// message would allow this method to be emitted by the base class.
|
||||
virtual void duplicate_option_warning(uint32_t code,
|
||||
virtual void duplicate_option_warning(uint32_t code,
|
||||
isc::asiolink::IOAddress& addr) = 0;
|
||||
|
||||
/// @brief Instantiates the subnet based on a given IP prefix and prefix
|
||||
/// length.
|
||||
///
|
||||
/// @brief Instantiates the subnet based on a given IP prefix and prefix
|
||||
/// length.
|
||||
///
|
||||
/// @param addr is the IP prefix of the subnet.
|
||||
/// @param len is the prefix length
|
||||
/// @param len is the prefix length
|
||||
virtual void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) = 0;
|
||||
|
||||
/// @brief Returns value for a given parameter (after using inheritance)
|
||||
@ -724,7 +738,7 @@ private:
|
||||
|
||||
/// @brief Create a new subnet using a data from child parsers.
|
||||
///
|
||||
/// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing
|
||||
/// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing
|
||||
/// failed.
|
||||
void createSubnet();
|
||||
|
||||
@ -748,7 +762,7 @@ protected:
|
||||
/// Pointer to the created subnet object.
|
||||
isc::dhcp::SubnetPtr subnet_;
|
||||
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
};
|
||||
|
@ -54,6 +54,10 @@ consider reducing the lease lifetime. In this way, addresses allocated
|
||||
to clients that are no longer active on the network will become available
|
||||
available sooner.
|
||||
|
||||
% DHCPSRV_CFGMGR_ADD_IFACE adding listening interface %1
|
||||
A debug message issued when new interface is being added to the collection of
|
||||
interfaces on which server listens to DHCP messages.
|
||||
|
||||
% DHCPSRV_CFGMGR_ADD_SUBNET4 adding subnet %1
|
||||
A debug message reported when the DHCP configuration manager is adding the
|
||||
specified IPv4 subnet to its database.
|
||||
@ -62,6 +66,16 @@ specified IPv4 subnet to its database.
|
||||
A debug message reported when the DHCP configuration manager is adding the
|
||||
specified IPv6 subnet to its database.
|
||||
|
||||
% DHCPSRV_CFGMGR_ALL_IFACES_ACTIVE enabling listening on all interfaces
|
||||
A debug message issued when server is being configured to listen on all
|
||||
interfaces.
|
||||
|
||||
% DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES stop listening on all interfaces
|
||||
A debug message issued when configuration manager clears the internal list
|
||||
of active interfaces. This doesn't prevent the server from listening to
|
||||
the DHCP traffic through open sockets, but will rather be used by Interface
|
||||
Manager to select active interfaces when sockets are re-opened.
|
||||
|
||||
% DHCPSRV_CFGMGR_DELETE_SUBNET4 deleting all IPv4 subnets
|
||||
A debug message noting that the DHCP configuration manager has deleted all IPv4
|
||||
subnets in its database.
|
||||
|
@ -165,6 +165,7 @@ public:
|
||||
CfgMgr::instance().deleteSubnets4();
|
||||
CfgMgr::instance().deleteSubnets6();
|
||||
CfgMgr::instance().deleteOptionDefs();
|
||||
CfgMgr::instance().deleteActiveIfaces();
|
||||
}
|
||||
|
||||
/// @brief generates interface-id option based on provided text
|
||||
@ -573,6 +574,50 @@ TEST_F(CfgMgrTest, optionSpace6) {
|
||||
// @todo decide if a duplicate vendor space is allowed.
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to specify interfaces that server
|
||||
// should listen on.
|
||||
TEST_F(CfgMgrTest, addActiveIface) {
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
|
||||
cfg_mgr.addActiveIface("eth0");
|
||||
cfg_mgr.addActiveIface("eth1");
|
||||
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
|
||||
|
||||
cfg_mgr.deleteActiveIfaces();
|
||||
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth0"));
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth1"));
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to set the flag which configures the
|
||||
// server to listen on all interfaces.
|
||||
TEST_F(CfgMgrTest, activateAllIfaces) {
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
|
||||
cfg_mgr.addActiveIface("eth0");
|
||||
cfg_mgr.addActiveIface("eth1");
|
||||
|
||||
ASSERT_TRUE(cfg_mgr.isActiveIface("eth0"));
|
||||
ASSERT_TRUE(cfg_mgr.isActiveIface("eth1"));
|
||||
ASSERT_FALSE(cfg_mgr.isActiveIface("eth2"));
|
||||
|
||||
cfg_mgr.activateAllIfaces();
|
||||
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
|
||||
|
||||
cfg_mgr.deleteActiveIfaces();
|
||||
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth0"));
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth1"));
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
// No specific tests for getSubnet6. That method (2 overloaded versions) is tested
|
||||
// in Dhcpv6SrvTest.selectSubnetAddr and Dhcpv6SrvTest.selectSubnetIface
|
||||
// (see src/bin/dhcp6/tests/dhcp6_srv_unittest.cc)
|
||||
|
@ -42,12 +42,13 @@ public:
|
||||
/// @brief Constructor
|
||||
///
|
||||
DhcpParserTest() {
|
||||
CfgMgr::instance().deleteActiveIfaces();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// @brief Check BooleanParser basic functionality.
|
||||
///
|
||||
///
|
||||
/// Verifies that the parser:
|
||||
/// 1. Does not allow empty for storage.
|
||||
/// 2. Rejects a non-boolean element.
|
||||
@ -94,7 +95,7 @@ TEST_F(DhcpParserTest, booleanParserTest) {
|
||||
}
|
||||
|
||||
/// @brief Check StringParser basic functionality
|
||||
///
|
||||
///
|
||||
/// Verifies that the parser:
|
||||
/// 1. Does not allow empty for storage.
|
||||
/// 2. Builds with a nont string value.
|
||||
@ -134,7 +135,7 @@ TEST_F(DhcpParserTest, stringParserTest) {
|
||||
}
|
||||
|
||||
/// @brief Check Uint32Parser basic functionality
|
||||
///
|
||||
///
|
||||
/// Verifies that the parser:
|
||||
/// 1. Does not allow empty for storage.
|
||||
/// 2. Rejects a non-integer element.
|
||||
@ -163,8 +164,8 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
|
||||
ElementPtr int_element = Element::create(-1);
|
||||
EXPECT_THROW(parser.build(int_element), isc::BadValue);
|
||||
|
||||
// Verify that parser with rejects too large a value provided we are on
|
||||
// 64-bit platform.
|
||||
// Verify that parser with rejects too large a value provided we are on
|
||||
// 64-bit platform.
|
||||
if (sizeof(long) > sizeof(uint32_t)) {
|
||||
long max = (long)(std::numeric_limits<uint32_t>::max()) + 1;
|
||||
int_element->setValue(max);
|
||||
@ -197,30 +198,61 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
|
||||
///
|
||||
/// Verifies that the parser:
|
||||
/// 1. Does not allow empty for storage.
|
||||
/// 2. Does not allow name other than "interface"
|
||||
///
|
||||
/// InterfaceListParser doesn't do very much, this test will need to
|
||||
/// expand once it does.
|
||||
/// 2. Does not allow name other than "interfaces"
|
||||
/// 3. Parses list of interfaces and adds them to CfgMgr
|
||||
/// 4. Parses wildcard interface name and sets a CfgMgr flag which indicates
|
||||
/// that server will listen on all interfaces.
|
||||
TEST_F(DhcpParserTest, interfaceListParserTest) {
|
||||
|
||||
const std::string name = "interface";
|
||||
const std::string name = "interfaces";
|
||||
|
||||
// Verify that parser constructor fails if parameter name isn't "interface"
|
||||
EXPECT_THROW(InterfaceListConfigParser("bogus_name"), isc::BadValue);
|
||||
|
||||
InterfaceListConfigParser parser(name);
|
||||
boost::scoped_ptr<InterfaceListConfigParser>
|
||||
parser(new InterfaceListConfigParser(name));
|
||||
ElementPtr list_element = Element::createList();
|
||||
list_element->add(Element::create("eth0"));
|
||||
list_element->add(Element::create("eth1"));
|
||||
|
||||
// Make sure there are no interfaces added yet.
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth0"));
|
||||
ASSERT_FALSE(CfgMgr::instance().isActiveIface("eth1"));
|
||||
|
||||
// This should parse the configuration and add eth0 and eth1 to the list
|
||||
// of interfaces that server should listen on.
|
||||
parser->build(list_element);
|
||||
parser->commit();
|
||||
|
||||
// Use CfgMgr instance to check if eth0 and eth1 was added, and that
|
||||
// eth2 was not added.
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
|
||||
EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
|
||||
|
||||
// Add keyword all to the configuration. This should activate all
|
||||
// interfaces, including eth2, even though it has not been explicitly
|
||||
// added.
|
||||
list_element->add(Element::create("*"));
|
||||
|
||||
// Reset parser's state.
|
||||
parser.reset(new InterfaceListConfigParser(name));
|
||||
parser->build(list_element);
|
||||
parser->commit();
|
||||
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth0"));
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth1"));
|
||||
EXPECT_TRUE(cfg_mgr.isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
/// @brief Test Implementation of abstract OptionDataParser class. Allows
|
||||
/// testing basic option parsing.
|
||||
/// @brief Test Implementation of abstract OptionDataParser class. Allows
|
||||
/// testing basic option parsing.
|
||||
class UtestOptionDataParser : public OptionDataParser {
|
||||
public:
|
||||
|
||||
UtestOptionDataParser(const std::string&,
|
||||
OptionStoragePtr options, ParserContextPtr global_context)
|
||||
UtestOptionDataParser(const std::string&,
|
||||
OptionStoragePtr options, ParserContextPtr global_context)
|
||||
:OptionDataParser("", options, global_context) {
|
||||
}
|
||||
|
||||
@ -234,12 +266,12 @@ protected:
|
||||
virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
|
||||
std::string&, uint32_t) {
|
||||
OptionDefinitionPtr def;
|
||||
// always return empty
|
||||
// always return empty
|
||||
return (def);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Test Fixture class which provides basic structure for testing
|
||||
/// @brief Test Fixture class which provides basic structure for testing
|
||||
/// configuration parsing. This is essentially the same structure provided
|
||||
/// by dhcp servers.
|
||||
class ParseConfigTest : public ::testing::Test {
|
||||
@ -253,15 +285,15 @@ public:
|
||||
reset_context();
|
||||
}
|
||||
|
||||
/// @brief Parses a configuration.
|
||||
/// @brief Parses a configuration.
|
||||
///
|
||||
/// Parse the given configuration, populating the context storage with
|
||||
/// the parsed elements.
|
||||
///
|
||||
/// the parsed elements.
|
||||
///
|
||||
/// @param config_set is the set of elements to parse.
|
||||
/// @return returns an ConstElementPtr containing the numeric result
|
||||
/// code and outcome comment.
|
||||
isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
|
||||
isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
|
||||
config_set) {
|
||||
// Answer will hold the result.
|
||||
ConstElementPtr answer;
|
||||
@ -293,7 +325,7 @@ public:
|
||||
}
|
||||
|
||||
// The option values parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator
|
||||
std::map<std::string, ConstElementPtr>::const_iterator
|
||||
option_config = values_map.find("option-data");
|
||||
if (option_config != values_map.end()) {
|
||||
option_parser->build(option_config->second);
|
||||
@ -316,21 +348,21 @@ public:
|
||||
|
||||
/// @brief Create an element parser based on the element name.
|
||||
///
|
||||
/// Note that currently it only supports option-defs and option-data,
|
||||
///
|
||||
/// @param config_id is the name of the configuration element.
|
||||
/// Note that currently it only supports option-defs and option-data,
|
||||
///
|
||||
/// @param config_id is the name of the configuration element.
|
||||
/// @return returns a raw pointer to DhcpConfigParser. Note caller is
|
||||
/// responsible for deleting it once no longer needed.
|
||||
/// @throw throws NotImplemented if element name isn't supported.
|
||||
DhcpConfigParser* createConfigParser(const std::string& config_id) {
|
||||
DhcpConfigParser* parser = NULL;
|
||||
if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id,
|
||||
parser_context_->options_,
|
||||
parser = new OptionDataListParser(config_id,
|
||||
parser_context_->options_,
|
||||
parser_context_,
|
||||
UtestOptionDataParser::factory);
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser = new OptionDefListParser(config_id,
|
||||
parser = new OptionDefListParser(config_id,
|
||||
parser_context_->option_defs_);
|
||||
} else {
|
||||
isc_throw(NotImplemented,
|
||||
@ -341,15 +373,15 @@ public:
|
||||
return (parser);
|
||||
}
|
||||
|
||||
/// @brief Convenicee method for parsing a configuration
|
||||
///
|
||||
/// @brief Convenicee method for parsing a configuration
|
||||
///
|
||||
/// Given a configuration string, convert it into Elements
|
||||
/// and parse them.
|
||||
/// and parse them.
|
||||
/// @param config is the configuration string to parse
|
||||
///
|
||||
/// @return retuns 0 if the configuration parsed successfully,
|
||||
/// @return retuns 0 if the configuration parsed successfully,
|
||||
/// non-zero otherwise failure.
|
||||
int parseConfiguration (std::string &config) {
|
||||
int parseConfiguration (std::string &config) {
|
||||
int rcode_ = 1;
|
||||
// Turn config into elements.
|
||||
// Test json just to make sure its valid.
|
||||
@ -363,17 +395,17 @@ public:
|
||||
return (rcode_);
|
||||
}
|
||||
|
||||
/// @brief Find an option definition for a given space and code within
|
||||
/// @brief Find an option definition for a given space and code within
|
||||
/// the parser context.
|
||||
/// @param space is the space name of the desired option.
|
||||
/// @param code is the numeric "type" of the desired option.
|
||||
/// @return returns an OptionDefinitionPtr which points to the found
|
||||
/// definition or is empty.
|
||||
/// ASSERT_ tests don't work inside functions that return values
|
||||
/// ASSERT_ tests don't work inside functions that return values
|
||||
OptionDefinitionPtr getOptionDef(std::string space, uint32_t code)
|
||||
{
|
||||
OptionDefinitionPtr def;
|
||||
OptionDefContainerPtr defs =
|
||||
OptionDefContainerPtr defs =
|
||||
parser_context_->option_defs_->getItems(space);
|
||||
// Should always be able to get definitions list even if it is empty.
|
||||
EXPECT_TRUE(defs);
|
||||
@ -387,41 +419,41 @@ public:
|
||||
def = *(idx.begin());
|
||||
}
|
||||
}
|
||||
return (def);
|
||||
return (def);
|
||||
}
|
||||
|
||||
/// @brief Find an option for a given space and code within the parser
|
||||
/// @brief Find an option for a given space and code within the parser
|
||||
/// context.
|
||||
/// @param space is the space name of the desired option.
|
||||
/// @param code is the numeric "type" of the desired option.
|
||||
/// @return returns an OptionPtr which points to the found
|
||||
/// option or is empty.
|
||||
/// ASSERT_ tests don't work inside functions that return values
|
||||
/// ASSERT_ tests don't work inside functions that return values
|
||||
OptionPtr getOptionPtr(std::string space, uint32_t code)
|
||||
{
|
||||
OptionPtr option_ptr;
|
||||
Subnet::OptionContainerPtr options =
|
||||
Subnet::OptionContainerPtr options =
|
||||
parser_context_->options_->getItems(space);
|
||||
// Should always be able to get options list even if it is empty.
|
||||
EXPECT_TRUE(options);
|
||||
if (options) {
|
||||
// Attempt to find desired option.
|
||||
const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
|
||||
const Subnet::OptionContainerTypeRange& range =
|
||||
const Subnet::OptionContainerTypeRange& range =
|
||||
idx.equal_range(code);
|
||||
int cnt = std::distance(range.first, range.second);
|
||||
EXPECT_EQ(1, cnt);
|
||||
if (cnt == 1) {
|
||||
Subnet::OptionDescriptor desc = *(idx.begin());
|
||||
option_ptr = desc.option;
|
||||
Subnet::OptionDescriptor desc = *(idx.begin());
|
||||
option_ptr = desc.option;
|
||||
EXPECT_TRUE(option_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return (option_ptr);
|
||||
return (option_ptr);
|
||||
}
|
||||
|
||||
/// @brief Wipes the contents of the context to allowing another parsing
|
||||
/// @brief Wipes the contents of the context to allowing another parsing
|
||||
/// during a given test if needed.
|
||||
void reset_context(){
|
||||
// Note set context universe to V6 as it has to be something.
|
||||
@ -436,7 +468,7 @@ public:
|
||||
};
|
||||
|
||||
/// @brief Check Basic parsing of option definitions.
|
||||
///
|
||||
///
|
||||
/// Note that this tests basic operation of the OptionDefinitionListParser and
|
||||
/// OptionDefinitionParser. It uses a simple configuration consisting of one
|
||||
/// one definition and verifies that it is parsed and committed to storage
|
||||
@ -461,7 +493,7 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
|
||||
ASSERT_TRUE(rcode == 0);
|
||||
|
||||
// Verify that the option definition can be retrieved.
|
||||
OptionDefinitionPtr def = getOptionDef("isc", 100);
|
||||
OptionDefinitionPtr def = getOptionDef("isc", 100);
|
||||
ASSERT_TRUE(def);
|
||||
|
||||
// Verify that the option definition is correct.
|
||||
@ -473,7 +505,7 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
|
||||
}
|
||||
|
||||
/// @brief Check Basic parsing of options.
|
||||
///
|
||||
///
|
||||
/// Note that this tests basic operation of the OptionDataListParser and
|
||||
/// OptionDataParser. It uses a simple configuration consisting of one
|
||||
/// one definition and matching option data. It verifies that the option
|
||||
|
Loading…
x
Reference in New Issue
Block a user