// is no need to rollback configuration if socket fails to open on any
// of the interfaces.
CfgMgr::instance().getStagingCfg()->
- getCfgIface().openSockets(CfgIface::V4, srv->getPort(),
+ getCfgIface().openSockets(AF_INET, srv->getPort(),
getInstance()->useBroadcast());
return (answer);
checkResult(status, 0);
CfgMgr::instance().getStagingCfg()->
- getCfgIface().openSockets(CfgIface::V4, 10000);
+ getCfgIface().openSockets(AF_INET, 10000);
// eth0 and eth1 were explicitly selected. eth2 was not.
EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
checkResult(status, 0);
CfgMgr::instance().getStagingCfg()->
- getCfgIface().openSockets(CfgIface::V4, 10000);
+ getCfgIface().openSockets(AF_INET, 10000);
// All interfaces should be now active.
ASSERT_TRUE(test_config.socketOpen("eth0", AF_INET));
// is no need to rollback configuration if socket fails to open on any
// of the interfaces.
CfgMgr::instance().getStagingCfg()->
- getCfgIface().openSockets(CfgIface::V6, srv->getPort());
+ getCfgIface().openSockets(AF_INET6, srv->getPort());
return (answer);
}
build, the configuration is then applied ("committed") and commit() method is
called.
+ \note With the implementation of the Kea ticket #3534 we're moving away from
+ the concept of commit being called for individual parsers. When this ticket
+ and a couple of followup tickets are implemented, the commit will be a
+ single operation executed for the whole configuration received from many
+ parsers.
+
All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them
are generic (e.g. Uint32Parser that is able to handle any
unsigned 32 bit integer), but some are very specialized (e.g.
checkResult(status, 0);
CfgMgr::instance().getStagingCfg()->
- getCfgIface().openSockets(CfgIface::V6, 10000);
+ getCfgIface().openSockets(AF_INET6, 10000);
// eth0 and eth1 were explicitly selected. eth2 was not.
EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
checkResult(status, 0);
CfgMgr::instance().getStagingCfg()->
- getCfgIface().openSockets(CfgIface::V6, 10000);
+ getCfgIface().openSockets(AF_INET6, 10000);
// All interfaces should be now active.
EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
endif
kea.conf: kea.conf.pre
- $(top_srcdir)/tools/path_replacer.sh $(builddir)/kea.conf.pre $@
+ $(top_srcdir)/tools/path_replacer.sh $(top_srcdir)/src/bin/keactrl/kea.conf.pre $@
if INSTALL_CONFIGURATIONS
}
void
-CfgIface::openSockets(const Family& family, const uint16_t port,
+CfgIface::openSockets(const uint16_t family, const uint16_t port,
const bool use_bcast) const {
// If wildcard interface '*' was not specified, set all interfaces to
// inactive state. We will later enable them selectively using the
<< *iface_name << "' as this interface doesn't"
" exist");
- } else if (family == V4) {
+ } else if (family == AF_INET) {
iface->inactive4_ = false;
} else {
}
// Select unicast sockets. It works only for V6. Ignore for V4.
- if (family == V6) {
+ if (family == AF_INET6) {
for (UnicastMap::const_iterator unicast = unicast_map_.begin();
unicast != unicast_map_.end(); ++unicast) {
Iface* iface = IfaceMgr::instance().getIface(unicast->first);
IfaceMgrErrorMsgCallback error_callback =
boost::bind(&CfgIface::socketOpenErrorHandler, _1);
bool sopen;
- if (family == V4) {
+ if (family == AF_INET) {
sopen = IfaceMgr::instance().openSockets4(port, use_bcast,
error_callback);
} else {
}
void
-CfgIface::setState(const Family& family, const bool inactive,
+CfgIface::setState(const uint16_t family, const bool inactive,
const bool loopback_inactive) const {
IfaceMgr::IfaceCollection ifaces = IfaceMgr::instance().getIfaces();
for (IfaceMgr::IfaceCollection::iterator iface = ifaces.begin();
iface != ifaces.end(); ++iface) {
Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
- if (family == V4) {
+ if (family == AF_INET) {
iface_ptr->inactive4_ = iface_ptr->flag_loopback_ ?
loopback_inactive : inactive;
} else {
}
void
-CfgIface::use(const Family& family, const std::string& iface_name) {
+CfgIface::use(const uint16_t family, const std::string& iface_name) {
// The interface name specified may have two formats, e.g.:
// - eth0
// - eth0/2001:db8:1::1.
}
- } else if (family == V4) {
+ } else if (family == AF_INET) {
isc_throw(InvalidIfaceName, "unicast addresses in the format of: "
"iface-name/unicast-addr_stress can only be specified for"
" IPv6 addr_stress family");
///
/// Once interfaces have been specified the sockets (either IPv4 or IPv6)
/// can be opened by calling @c CfgIface::openSockets function.
+///
+/// @warning This class makes use of the AF_INET and AF_INET6 family literals,
+/// but it doesn't verify that the address family value passed as @c uint16_t
+/// parameter is equal to one of them. It is a callers responsibility to
+/// guarantee that the address family value is correct.
class CfgIface {
public:
/// @brief Keyword used to enable all interfaces.
/// that DHCP server should listen on all interfaces.
static const char* ALL_IFACES_KEYWORD;
- /// @brief Protocol family: IPv4 or IPv6.
- ///
- /// Depending on the family specified, the IPv4 or IPv6 sockets are
- /// opened.
- enum Family {
- V4, V6
- };
-
/// @brief Constructor.
CfgIface();
/// documentation for details how to specify interfaces and unicast
/// addresses to bind the sockets to.
///
- /// @param family Address family (v4 or v6).
+ /// @param family Address family (AF_INET or AF_INET6).
/// @param port Port number to be used to bind sockets to.
/// @param use_bcast A boolean flag which indicates if the broadcast
/// traffic should be received through the socket. This parameter is
/// ignored for IPv6.
- void openSockets(const Family& family, const uint16_t port,
+ void openSockets(const uint16_t family, const uint16_t port,
const bool use_bcast = true) const;
/// @brief Puts the interface configuration into default state.
/// not allowed when specifying a unicast address. For example:
/// */2001:db8:1::1 is not allowed.
///
- /// @param family Address family (v4 or v6).
+ /// @param family Address family (AF_INET or AF_INET6).
/// @param iface_name Explicit interface name, a wildcard name (*) of
/// the interface(s) or the pair of iterface/unicast-address to be used
/// to receive DHCP traffic.
/// @throw DuplicateIfaceName If the interface is already selected, i.e.
/// @throw IOError when specified unicast address is invalid.
/// @c CfgIface::use has been already called for this interface.
- void use(const Family& family, const std::string& iface_name);
+ void use(const uint16_t family, const std::string& iface_name);
/// @brief Equality operator.
///
/// This function selects all interfaces to receive DHCP traffic or
/// deselects all interfaces so as none of them receives a DHCP traffic.
///
- /// @param family Address family (v4 or v6).
+ /// @param family Address family (AF_INET or AF_INET6).
/// @param inactive A boolean value which indicates if all interfaces
/// (except loopback) should be selected or deselected.
/// @param loopback_inactive A boolean value which indicates if loopback
/// interface should be selected or deselected.
/// should be deselected/inactive (true) or selected/active (false).
- void setState(const Family& family, const bool inactive,
+ void setState(const uint16_t family, const bool inactive,
const bool loopback_inactive) const;
/// @brief Error handler for executed when opening a socket fail.
/// @brief Commits the staging configuration.
///
/// The staging configuration becomes current configuration when this
- /// function is called. It removes the oldest configurations held in the
- /// history so as the size of the list of configuration does not excide
+ /// function is called. It removes the oldest configuration held in the
+ /// history so as the size of the list of configuration does not exceed
/// the @c CONFIG_LIST_SIZE.
///
/// This function is exception safe.
/// @warning Revert operation will rollback any changes to the staging
/// configuration (if it exists).
///
+ /// @param index A distance from the current configuration to the
+ /// past configuration to be reverted. The minimal value is 1 which points
+ /// to the nearest configuration.
+ ///
/// @throw isc::OutOfRange if the specified index is out of range.
void revert(const size_t index);
private:
- /// @brief Checks if current configuration is created and creates if needed.
+ /// @brief Checks if current configuration is created and creates it if needed.
///
/// This private method is called to ensure that the current configuration
/// is created. If current configuration is not set, it creates the
void
Configuration::applyLoggingCfg() const {
/// @todo Remove the hardcoded location.
- setenv("B10_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
+ setenv("KEA_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
std::list<LoggerSpecification> specs;
for (LoggingInfoStorage::const_iterator it = logging_info_.begin();
std::string iface_name = iface->stringValue();
try {
cfg_iface.use(global_context_->universe_ == Option::V4 ?
- CfgIface::V4 : CfgIface::V6, iface_name);
+ AF_INET : AF_INET6, iface_name);
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, "Failed to select interface: "
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012, 2014 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
@section cfgmgr Configuration Manager
-Configuration Manager (\ref isc::dhcp::CfgMgr) stores configuration information
-necessary for DHCPv4 and DHCPv6 server operation. In particular, it stores
-subnets (\ref isc::dhcp::Subnet4 and \ref isc::dhcp::Subnet6) together with
-their pools (\ref isc::dhcp::Pool4 and \ref isc::dhcp::Pool6), options and
-other information specified by the user in the Kea configuration.
+Configuration Manager (\ref isc::dhcp::CfgMgr) is a singleton object which
+holds configuration information necessary for the operation of Kea daemons.
+A complete collection of information for the daemon is stored in the
+\ref isc::dhcp::Configuration object. Internally, the Configuration Manager
+holds a list of \ref isc::dhcp::Configuration objects, from which one
+is marked as "current configuration".
+
+When the server starts up or is being reconfigured a new
+\ref isc::dhcp::Configuration object, referred to as "staging configuration",
+is created. The staging configuration is held at the tip of the list of
+configurations. The object can be accessed by calling the
+\ref isc::dhcp::CfgMgr::getStagingCfg. This object can be accessed
+from different stages of the configuration parsing and modified as needed.
+Modifications of to the staging configuration do not affect the current
+configuration. The staging configuration is unused until the
+\ref isc::dhcp::CfgMgr::commit function is called. This exception safe method
+marks the staging object as "current configuration". The const pointer to the
+current configuration can be accessed by calling a
+\ref isc::dhcp::CfgMgr::getCurrentCfg.
+
+The staging configuration can be discarded at any time before it is committed
+by calling the \ref isc::dhcp::CfgMgr::rollback. This removes the
+\ref isc::dhcp::Configuration object from the Configuration Manager. When
+the \ref isc::dhcp::CfgMgr::getStagingCfg is called again a fresh/default
+\ref isc::dhcp::Configuration object is returned.
+
+The Configuration Manager stores previous configurations, i.e. configurations
+which occurred prior to the most current configuration. This is currently
+unused (except for unit tests) by the deamons, but in the future this
+mechanism can be used to trigger a rollover of the server configuration
+to a last good configuration that the administrator prefers.
+
+The previous configurations are identified by the value which specifies a
+distance between the current configuration and the previous
+configuration. For example: the value of 1 identifies an immediate
+predecessor of the current configuration, the value of 2 identifies the
+one that occurred before it etc.
+
+@todo Currently, only a subset of configuration information is stored in
+the \ref isc::dhcp::Configuration object. Kea developers are actively working
+on migrating the other configuration parameters to it.
@section allocengine Allocation Engine
TEST_F(CfgIfaceTest, explicitNamesV4) {
CfgIface cfg;
// Specify valid interface names. There should be no error.
- ASSERT_NO_THROW(cfg.use(CfgIface::V4, "eth0"));
- ASSERT_NO_THROW(cfg.use(CfgIface::V4, "eth1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "eth0"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "eth1"));
// Open sockets on specified interfaces.
- cfg.openSockets(CfgIface::V4, DHCP4_SERVER_PORT);
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// Sockets should be now open on eth0 and eth1, but not on loopback.
EXPECT_TRUE(socketOpen("eth0", AF_INET));
// Reset configuration and select only one interface this time.
cfg.reset();
- ASSERT_NO_THROW(cfg.use(CfgIface::V4, "eth1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "eth1"));
- cfg.openSockets(CfgIface::V4, DHCP4_SERVER_PORT);
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// Socket should be open on eth1 only.
EXPECT_FALSE(socketOpen("eth0", AF_INET));
TEST_F(CfgIfaceTest, explicitNamesV6) {
CfgIface cfg;
// Specify valid interface names. There should be no error.
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "eth0"));
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "eth1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth1"));
// Open sockets on specified interfaces.
- cfg.openSockets(CfgIface::V6, DHCP6_SERVER_PORT);
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
// Sockets should be now open on eth0 and eth1, but not on loopback.
EXPECT_TRUE(socketOpen("eth0", AF_INET6));
// Reset configuration and select only one interface this time.
cfg.reset();
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "eth1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth1"));
- cfg.openSockets(CfgIface::V6, DHCP6_SERVER_PORT);
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
// Socket should be open on eth1 only.
EXPECT_FALSE(socketOpen("eth0", AF_INET6));
// select all interfaces to open IPv4 sockets.
TEST_F(CfgIfaceTest, wildcardV4) {
CfgIface cfg;
- ASSERT_NO_THROW(cfg.use(CfgIface::V4, "*"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "*"));
- cfg.openSockets(CfgIface::V4, DHCP4_SERVER_PORT);
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
// Sockets should be now open on eth0 and eth1, but not on loopback.
EXPECT_TRUE(socketOpen("eth0", AF_INET));
// select all interfaces to open IPv6 sockets.
TEST_F(CfgIfaceTest, wildcardV6) {
CfgIface cfg;
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "*"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "*"));
- cfg.openSockets(CfgIface::V6, DHCP6_SERVER_PORT);
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
// Sockets should be now open on eth0 and eth1, but not on loopback.
EXPECT_TRUE(socketOpen("eth0", AF_INET6));
// One socket will be opened on link-local address, one on unicast but
// on the same interface.
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "eth0"));
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "eth0/2001:db8:1::1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0/2001:db8:1::1"));
- cfg.openSockets(CfgIface::V6, DHCP6_SERVER_PORT);
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
EXPECT_TRUE(socketOpen("eth0", AF_INET6));
EXPECT_TRUE(unicastOpen("eth0"));
// Test that when invalid interface names are specified an exception is thrown.
TEST_F(CfgIfaceTest, invalidValues) {
CfgIface cfg;
- ASSERT_THROW(cfg.use(CfgIface::V4, ""), InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V4, " "), InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V4, "bogus"), NoSuchIface);
+ ASSERT_THROW(cfg.use(AF_INET, ""), InvalidIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET, " "), InvalidIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET, "bogus"), NoSuchIface);
- ASSERT_NO_THROW(cfg.use(CfgIface::V4, "eth0"));
- ASSERT_THROW(cfg.use(CfgIface::V4, "eth0"), DuplicateIfaceName);
+ ASSERT_NO_THROW(cfg.use(AF_INET, "eth0"));
+ ASSERT_THROW(cfg.use(AF_INET, "eth0"), DuplicateIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V4, "eth0/2001:db8:1::1"), InvalidIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET, "eth0/2001:db8:1::1"), InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V6, "eth0/"), InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V6, "/2001:db8:1::1"), InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V6, "*/2001:db8:1::1"), InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V6, "bogus/2001:db8:1::1"), NoSuchIface);
- ASSERT_THROW(cfg.use(CfgIface::V6, "eth0/fe80::3a60:77ff:fed5:cdef"),
+ ASSERT_THROW(cfg.use(AF_INET6, "eth0/"), InvalidIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET6, "/2001:db8:1::1"), InvalidIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET6, "*/2001:db8:1::1"), InvalidIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET6, "bogus/2001:db8:1::1"), NoSuchIface);
+ ASSERT_THROW(cfg.use(AF_INET6, "eth0/fe80::3a60:77ff:fed5:cdef"),
InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V6, "eth0/fe80::3a60:77ff:fed5:cdef"),
+ ASSERT_THROW(cfg.use(AF_INET6, "eth0/fe80::3a60:77ff:fed5:cdef"),
InvalidIfaceName);
- ASSERT_THROW(cfg.use(CfgIface::V6, "eth0/2001:db8:1::2"), NoSuchAddress);
- ASSERT_NO_THROW(cfg.use(CfgIface::V6, "*"));
- ASSERT_THROW(cfg.use(CfgIface::V6, "*"), DuplicateIfaceName);
+ ASSERT_THROW(cfg.use(AF_INET6, "eth0/2001:db8:1::2"), NoSuchAddress);
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "*"));
+ ASSERT_THROW(cfg.use(AF_INET6, "*"), DuplicateIfaceName);
}
// Test that the equality and inequality operators work fine for CfgIface.
EXPECT_FALSE(cfg1 != cfg2);
// Differ by one interface.
- cfg1.use(CfgIface::V4, "eth0");
+ cfg1.use(AF_INET, "eth0");
EXPECT_FALSE(cfg1 == cfg2);
EXPECT_TRUE(cfg1 != cfg2);
// Now interfaces should be equal.
- cfg2.use(CfgIface::V4, "eth0");
+ cfg2.use(AF_INET, "eth0");
EXPECT_TRUE(cfg1 == cfg2);
EXPECT_FALSE(cfg1 != cfg2);
// Differ by unicast address.
- cfg1.use(CfgIface::V6, "eth0/2001:db8:1::1");
+ cfg1.use(AF_INET6, "eth0/2001:db8:1::1");
EXPECT_FALSE(cfg1 == cfg2);
EXPECT_TRUE(cfg1 != cfg2);
// Differ by unicast address and one interface.
- cfg2.use(CfgIface::V6, "eth1");
+ cfg2.use(AF_INET6, "eth1");
EXPECT_FALSE(cfg1 == cfg2);
EXPECT_TRUE(cfg1 != cfg2);
// Now, the unicast addresses are equal but still differ by one interface.
- cfg2.use(CfgIface::V6, "eth0/2001:db8:1::1");
+ cfg2.use(AF_INET6, "eth0/2001:db8:1::1");
EXPECT_FALSE(cfg1 == cfg2);
EXPECT_TRUE(cfg1 != cfg2);
// They should be now back to equal.
- cfg1.use(CfgIface::V6, "eth1");
+ cfg1.use(AF_INET6, "eth1");
EXPECT_TRUE(cfg1 == cfg2);
EXPECT_FALSE(cfg1 != cfg2);
// Even though the wildcard doesn't change anything because all interfaces
// are already in use, the fact that the wildcard is specified should
// cause them to be not equal.
- cfg1.use(CfgIface::V6, "*");
+ cfg1.use(AF_INET6, "*");
EXPECT_FALSE(cfg1 == cfg2);
EXPECT_TRUE(cfg1 != cfg2);
// Finally, both are equal as they use wildacard.
- cfg2.use(CfgIface::V4, "*");
+ cfg2.use(AF_INET, "*");
EXPECT_TRUE(cfg1 == cfg2);
EXPECT_FALSE(cfg1 != cfg2);
}
cfg_mgr.commit();
}
- // Now we have 5 configurations with:
+ // Now we have 6 configurations with:
// - debuglevel = 99 (a default one)
// - debuglevel = 10
// - debuglevel = 11
// And this configuration should be now the current one and the debuglevel
// of this configuration is 10.
EXPECT_EQ(10, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
+ EXPECT_NE(cfg_mgr.getCurrentCfg()->getSequence(), 1);
// The new set of configuration is now as follows:
// - debuglevel = 99
// Set interface configuration for conf1.
CfgIface cfg_iface;
- cfg_iface.use(CfgIface::V4, "eth0");
+ cfg_iface.use(AF_INET, "eth0");
conf1.addLoggingInfo(info);
conf1.setCfgIface(cfg_iface);
CfgIface cfg_iface1;
CfgIface cfg_iface2;
- cfg_iface1.use(CfgIface::V4, "eth0");
+ cfg_iface1.use(AF_INET, "eth0");
conf1.setCfgIface(cfg_iface1);
EXPECT_FALSE(conf1 == conf2);
EXPECT_TRUE(conf1 != conf2);
- cfg_iface2.use(CfgIface::V4, "eth0");
+ cfg_iface2.use(AF_INET, "eth0");
conf2.setCfgIface(cfg_iface2);
EXPECT_TRUE(conf1 == conf2);
// eth2 was not added.
ConfigurationPtr cfg = CfgMgr::instance().getStagingCfg();
ASSERT_TRUE(cfg);
- ASSERT_NO_THROW(cfg->getCfgIface().openSockets(CfgIface::V4, 10000));
+ ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
EXPECT_FALSE(test_config.socketOpen("eth1", AF_INET));
parser->commit();
cfg = CfgMgr::instance().getStagingCfg();
- ASSERT_NO_THROW(cfg->getCfgIface().openSockets(CfgIface::V4, 10000));
+ ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));