summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcin Siodelski <marcin@isc.org>2015-02-09 21:09:22 +0100
committerMarcin Siodelski <marcin@isc.org>2015-02-09 21:09:22 +0100
commit543c155050c88e0607e96b6a52867bd71b2dd66a (patch)
treed7f438f49b1c959e1366b1097d18a23cea1a8dab
parent[3604] Use new interface configuration format where applicable. (diff)
downloadkea-543c155050c88e0607e96b6a52867bd71b2dd66a.tar.xz
kea-543c155050c88e0607e96b6a52867bd71b2dd66a.zip
[3604] Allow for configuration of the socket type for DHCPv4.
-rw-r--r--src/bin/dhcp4/dhcp4.spec32
-rw-r--r--src/lib/dhcpsrv/cfg_iface.cc55
-rw-r--r--src/lib/dhcpsrv/cfg_iface.h67
-rw-r--r--src/lib/dhcpsrv/dhcpsrv_messages.mes12
-rw-r--r--src/lib/dhcpsrv/parsers/ifaces_config_parser.cc4
-rw-r--r--src/lib/dhcpsrv/tests/cfg_iface_unittest.cc51
-rw-r--r--src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc75
7 files changed, 271 insertions, 25 deletions
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index 8c99576a87..dc60e82322 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -17,19 +17,33 @@
}
},
- { "item_name": "interfaces",
- "item_type": "list",
+ { "item_name": "interfaces-config",
+ "item_type": "map",
"item_optional": false,
- "item_default": [ "*" ],
- "list_item_spec":
+ "item_default": {},
+ "map_item_spec": [
{
- "item_name": "interface_name",
+ "item_name": "interfaces",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [ "*" ],
+ "list_item_spec":
+ {
+ "item_name": "interface_name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "*"
+ }
+ },
+
+ { "item_name": "socket-type",
"item_type": "string",
- "item_optional": false,
- "item_default": "*"
+ "item_optional": true,
+ "item_default": ""
}
- } ,
-
+ ]
+ },
+
{ "item_name": "renew-timer",
"item_type": "integer",
"item_optional": true,
diff --git a/src/lib/dhcpsrv/cfg_iface.cc b/src/lib/dhcpsrv/cfg_iface.cc
index 3c29ee4437..b90289d0da 100644
--- a/src/lib/dhcpsrv/cfg_iface.cc
+++ b/src/lib/dhcpsrv/cfg_iface.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 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
@@ -26,7 +26,7 @@ namespace dhcp {
const char* CfgIface::ALL_IFACES_KEYWORD = "*";
CfgIface::CfgIface()
- : wildcard_used_(false) {
+ : wildcard_used_(false), socket_type_(SOCKET_DEFAULT) {
}
void
@@ -38,7 +38,8 @@ bool
CfgIface::equals(const CfgIface& other) const {
return (iface_set_ == other.iface_set_ &&
address_map_ == other.address_map_ &&
- wildcard_used_ == other.wildcard_used_);
+ wildcard_used_ == other.wildcard_used_ &&
+ socket_type_ == other.socket_type_);
}
void
@@ -50,8 +51,22 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port,
// specified, mark all interfaces active. In all cases, mark loopback
// inactive.
setState(family, !wildcard_used_, true);
+ IfaceMgr& iface_mgr = IfaceMgr::instance();
// Remove selection of unicast addresses from all interfaces.
- IfaceMgr::instance().clearUnicasts();
+ iface_mgr.clearUnicasts();
+ // For the DHCPv4 server, if the user has selected that raw sockets
+ // should be used, we will try to configure the Interface Manager to
+ // support the direct responses to the clients that don't have the
+ // IP address. This should effectively turn on the use of raw
+ // sockets. However, this may be unsupported on some operating
+ // systems, so there is no guarantee.
+ if ((family == AF_INET) && (socket_type_ != SOCKET_DEFAULT)) {
+ iface_mgr.setMatchingPacketFilter(socket_type_ == SOCKET_RAW);
+ if ((socket_type_ == SOCKET_RAW) &&
+ !iface_mgr.isDirectResponseSupported()) {
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED);
+ }
+ }
// If there is no wildcard interface specified, we will have to iterate
// over the names specified by the caller and enable them.
if (!wildcard_used_) {
@@ -136,6 +151,7 @@ CfgIface::reset() {
wildcard_used_ = false;
iface_set_.clear();
address_map_.clear();
+ socket_type_ = SOCKET_DEFAULT;
}
void
@@ -316,5 +332,36 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) {
}
}
+void
+CfgIface::useSocketType(const uint16_t family,
+ const SocketType& socket_type) {
+ if (family != AF_INET) {
+ isc_throw(InvalidSocketType, "socket type must not be specified for"
+ " the DHCPv6 server");
+ } else if (socket_type == SOCKET_DEFAULT) {
+ isc_throw(InvalidSocketType, "invalid value SOCKET_DEFAULT"
+ " used to specify the socket type to be used by"
+ " the DHCPv4 server");
+ }
+ socket_type_ = socket_type;
+}
+
+void
+CfgIface::useSocketType(const uint16_t family,
+ const std::string& socket_type_name) {
+ SocketType socket_type;
+ if (socket_type_name == "datagram") {
+ socket_type = SOCKET_DGRAM;
+
+ } else if (socket_type_name == "raw") {
+ socket_type = SOCKET_RAW;
+
+ } else {
+ isc_throw(InvalidSocketType, "unsupported socket type '"
+ << socket_type_name << "'");
+ }
+ useSocketType(family, socket_type);
+}
+
} // end of isc::dhcp namespace
} // end of isc namespace
diff --git a/src/lib/dhcpsrv/cfg_iface.h b/src/lib/dhcpsrv/cfg_iface.h
index bf3113b008..72b450aff5 100644
--- a/src/lib/dhcpsrv/cfg_iface.h
+++ b/src/lib/dhcpsrv/cfg_iface.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 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
@@ -51,6 +51,14 @@ public:
isc::Exception(file, line, what) { };
};
+/// @brief Exception thrown when invalid socket type has been specified
+/// for the given family.
+class InvalidSocketType : public Exception {
+public:
+ InvalidSocketType(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
/// @brief Represents selection of interfaces for DHCP server.
///
/// This class manages selection of interfaces on which the DHCP server is
@@ -63,7 +71,22 @@ public:
/// instructs the server to listen on all available interfaces.
///
/// Once interfaces have been specified the sockets (either IPv4 or IPv6)
-/// can be opened by calling @c CfgIface::openSockets function.
+/// can be opened by calling @c CfgIface::openSockets function. Kea
+/// offers configuration parameters to control the types of sockets to be
+/// opened by the DHCPv4 server. In small deployments it is requires that
+/// the server can handle messages from the directly connected clients
+/// which don't have an address yet. Unicasting the response to such
+/// client is possible by the use of raw sockets. In larger deployments
+/// it is often the case that whole traffic is received via relays, and
+/// in such case the use of datagram sockets is preferred. The type of the
+/// sockets to be opened is specified using one of the
+/// @c CfgIface::useSocketType method variants. The @c CfgIface::SocketType
+/// enumeration specifies the possible values. The @c CfgIface::SOCKET_DEFAULT
+/// is a default setting of the @c CfgIface and it indicates that the
+/// @c IfaceMgr should continue using the currently used sockets' type.
+/// This is mostly used for unit testing to avoid modifying fake
+/// configurations of the @c IfaceMgr. In the real case, one of the
+/// remaining values should be used.
///
/// @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
@@ -71,6 +94,17 @@ public:
/// guarantee that the address family value is correct.
class CfgIface {
public:
+
+ /// @brief Socket type used by the DHCPv4 server.
+ enum SocketType {
+ /// Default socket type, mainly used for testing.
+ SOCKET_DEFAULT,
+ /// Raw socket, used for direct DHCPv4 traffic.
+ SOCKET_RAW,
+ /// Datagram socket, i.e. IP/UDP socket.
+ SOCKET_DGRAM
+ };
+
/// @brief Keyword used to enable all interfaces.
///
/// This keyword can be used instead of the interface name to specify
@@ -143,6 +177,32 @@ public:
/// @c CfgIface::use has been already called for this interface.
void use(const uint16_t family, const std::string& iface_name);
+ /// @brief Sets the specified socket type to be used by the server.
+ ///
+ /// @param family Address family (AF_INET or AF_INET6).
+ /// @param socket_type Socket type.
+ ///
+ /// @throw InvalidSocketType if the unsupported socket type has been
+ /// specified for the address family. Currently, the socket type
+ /// can only be selected for the AF_INET family.
+ void useSocketType(const uint16_t family, const SocketType& socket_type);
+
+ /// @brief Sets the specified socket type specified in textual format.
+ ///
+ /// The following names of the socket types are currently supported, and
+ /// can be passed in the @c socket_type parameter:
+ /// - raw - for raw sockets,
+ /// - datagram - for the datagram sockets,
+ ///
+ /// @param family Address family (AF_INET or AF_INET6)
+ /// @param socket_type_name Socket type in the textual format.
+ ///
+ /// @throw InvalidSocketType if the unsupported socket type has been
+ /// specified for the address family. Currently, the socket type
+ /// can only be selected for the AF_INET family.
+ void useSocketType(const uint16_t family,
+ const std::string& socket_type_name);
+
/// @brief Equality operator.
///
/// @param other Object to be compared with this object.
@@ -204,6 +264,9 @@ private:
/// @brief A booolean value which indicates that the wildcard interface name
/// has been specified (*).
bool wildcard_used_;
+
+ /// @brief A type of the sockets used by the DHCP server.
+ SocketType socket_type_;
};
}
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 8eac7c5941..9bf0222099 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -99,6 +99,18 @@ This is a debug message reporting that the DHCP configuration manager has
returned the specified IPv6 subnet when given the address hint specified
because it is the only subnet defined.
+% DHCPSRV_CFGMGR_SOCKET_RAW_UNSUPPORTED use of raw sockets is unsupported on this OS, datagram sockets will be used
+This warning message is logged when the user specified that the
+DHCPv4 server should use the raw sockets to receive the DHCP
+messages and respond to the clients, but the use of raw sockets
+is not supported on the particular environment. The raw sockets
+are useful when the server must respond to the directly connected
+clients which don't have an address yet. If the raw sockets are
+not supported by Kea on the particular platform, Kea will fall
+back to use of the datagram IP/UDP sockets. The responses to
+the directly connected clients will be broadcast. The responses
+to relayed clients will be unicast as usual.
+
% DHCPSRV_CFGMGR_SUBNET4 retrieved subnet %1 for address hint %2
This is a debug message reporting that the DHCP configuration manager has
returned the specified IPv4 subnet when given the address hint specified
diff --git a/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc b/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc
index 5c9fa34b9a..5166164fe5 100644
--- a/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc
+++ b/src/lib/dhcpsrv/parsers/ifaces_config_parser.cc
@@ -89,7 +89,9 @@ IfacesConfigParser4::build(isc::data::ConstElementPtr ifaces_config) {
BOOST_FOREACH(ConfigPair element, ifaces_config->mapValue()) {
try {
if (element.first == "socket-type") {
- /// @todo set socket-type
+ CfgIface cfg = CfgMgr::instance().getStagingCfg()->getCfgIface();
+ cfg.useSocketType(AF_INET, element.second->stringValue());
+ CfgMgr::instance().getStagingCfg()->setCfgIface(cfg);
} else if (!isGenericParameter(element.first)) {
isc_throw(DhcpConfigError, "usupported parameter '"
diff --git a/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc b/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc
index a0f62833fa..99518d73a4 100644
--- a/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 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
@@ -209,7 +209,6 @@ TEST_F(CfgIfaceTest, explicitNamesV6) {
EXPECT_FALSE(socketOpen("eth0", AF_INET6));
EXPECT_TRUE(socketOpen("eth1", AF_INET6));
EXPECT_FALSE(socketOpen("lo", AF_INET6));
-
}
// This test checks that the wildcard interface name can be specified to
@@ -338,6 +337,54 @@ TEST_F(CfgIfaceTest, equality) {
cfg2.use(AF_INET, "*");
EXPECT_TRUE(cfg1 == cfg2);
EXPECT_FALSE(cfg1 != cfg2);
+
+ // Differ by socket type.
+ cfg1.useSocketType(AF_INET, "raw");
+ EXPECT_FALSE(cfg1 == cfg2);
+ EXPECT_TRUE(cfg1 != cfg2);
+
+ // Now, both should use the same socket type.
+ cfg2.useSocketType(AF_INET, "raw");
+ EXPECT_TRUE(cfg1 == cfg2);
+ EXPECT_FALSE(cfg1 != cfg2);
}
+// This test verifies that it is possible to specify the socket
+// type to be used by the DHCPv4 server.
+// This test is enabled on LINUX and BSD only, because the
+// direct traffic is only supported on those systems.
+#if defined OS_LINUX || defined OS_BSD
+TEST(CfgIfaceNoStubTest, useSocketType) {
+ CfgIface cfg;
+ // Select datagram sockets.
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, "datagram"));
+ ASSERT_NO_THROW(cfg.openSockets(AF_INET, 10067, true));
+ // For datagram sockets, the direct traffic is not supported.
+ ASSERT_TRUE(!IfaceMgr::instance().isDirectResponseSupported());
+
+ // Select raw sockets.
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, "raw"));
+ ASSERT_NO_THROW(cfg.openSockets(AF_INET, 10067, true));
+ // For raw sockets, the direct traffic is supported.
+ ASSERT_TRUE(IfaceMgr::instance().isDirectResponseSupported());
+
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_DGRAM));
+ ASSERT_NO_THROW(cfg.openSockets(AF_INET, 10067, true));
+ ASSERT_TRUE(!IfaceMgr::instance().isDirectResponseSupported());
+
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_RAW));
+ ASSERT_NO_THROW(cfg.openSockets(AF_INET, 10067, true));
+ ASSERT_TRUE(IfaceMgr::instance().isDirectResponseSupported());
+
+ // Test invalid values.
+ EXPECT_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_DEFAULT),
+ InvalidSocketType);
+ EXPECT_THROW(cfg.useSocketType(AF_INET, "default"),
+ InvalidSocketType);
+ EXPECT_THROW(cfg.useSocketType(AF_INET6, "datagram"),
+ InvalidSocketType);
+}
+#endif
+
+
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc b/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc
index d3ad960364..8ef72c3375 100644
--- a/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc
+++ b/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc
@@ -59,9 +59,7 @@ TEST_F(IfacesConfigParserTest, interfaces) {
IfaceMgrTestConfig test_config(true);
// Configuration with one interface.
- std::string config = "{"
- "\"interfaces\": [ \"eth0\" ],"
- "\"socket-type\": \"raw\" }";
+ std::string config = "{ ""\"interfaces\": [ \"eth0\" ] }";
ElementPtr config_element = Element::fromJSON(config);
@@ -84,10 +82,7 @@ TEST_F(IfacesConfigParserTest, interfaces) {
// Try similar configuration but this time add a wildcard interface
// to see if sockets will open on all interfaces.
- config = "{"
- "\"interfaces\": [ \"eth0\", \"*\" ],"
- "\"socket-type\": \"raw\" }";
-
+ config = "{ \"interfaces\": [ \"eth0\", \"*\" ] }";
config_element = Element::fromJSON(config);
ASSERT_NO_THROW(parser.build(config_element));
@@ -99,5 +94,71 @@ TEST_F(IfacesConfigParserTest, interfaces) {
EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));
}
+// This test verifies that it is possible to select the raw socket
+// use in the configuration for interfaces.
+TEST_F(IfacesConfigParserTest, socketTypeRaw) {
+ // Create the reference configuration, which we will compare
+ // the parsed configuration to.
+ CfgIface cfg_ref;
+
+ // Configuration with a raw socket selected.
+ std::string config = "{ ""\"interfaces\": [ ],"
+ " \"socket-type\": \"raw\" }";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse the configuration.
+ IfacesConfigParser4 parser;
+ ASSERT_NO_THROW(parser.build(config_element));
+
+ // Compare the resulting configuration with a reference
+ // configuration using the raw socket.
+ SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
+ ASSERT_TRUE(cfg);
+ cfg_ref.useSocketType(AF_INET, CfgIface::SOCKET_RAW);
+ EXPECT_TRUE(cfg->getCfgIface() == cfg_ref);
+}
+
+// This test verifies that it is possible to select the datagram socket
+// use in the configuration for interfaces.
+TEST_F(IfacesConfigParserTest, socketTypeDatagram) {
+ // Create the reference configuration, which we will compare
+ // the parsed configuration to.
+ CfgIface cfg_ref;
+
+ // Configuration with a datagram socket selected.
+ std::string config = "{ ""\"interfaces\": [ ],"
+ " \"socket-type\": \"datagram\" }";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse the configuration.
+ IfacesConfigParser4 parser;
+ ASSERT_NO_THROW(parser.build(config_element));
+
+ // Compare the resulting configuration with a reference
+ // configuration using the raw socket.
+ SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
+ ASSERT_TRUE(cfg);
+ cfg_ref.useSocketType(AF_INET, CfgIface::SOCKET_DGRAM);
+ EXPECT_TRUE(cfg->getCfgIface() == cfg_ref);
+}
+
+// Test that the configuration rejects the invalid socket type.
+TEST_F(IfacesConfigParserTest, socketTypeInvalid) {
+ // For DHCPv4 we only accept the raw socket or datagram socket.
+ IfacesConfigParser4 parser4;
+ std::string config = "{ \"interfaces\": [ ],"
+ "\"socket-type\": \"default\" }";
+ ElementPtr config_element = Element::fromJSON(config);
+ ASSERT_THROW(parser4.build(config_element), DhcpConfigError);
+
+ // For DHCPv6 we don't accept any socket type.
+ IfacesConfigParser6 parser6;
+ config = "{ \"interfaces\": [ ],"
+ " \"socket-type\": \"datagram\" }";
+ config_element = Element::fromJSON(config);
+ ASSERT_THROW(parser6.build(config_element), DhcpConfigError);
+}
} // end of anonymous namespace