Commit 0f151975 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[5377] outbound-interface fixed, unit-tests added.

parent 7ffa4c68
......@@ -720,6 +720,30 @@ temporarily override a list of interface names and listen on all interfaces.
fall back to use IP/UDP sockets.</para>
</note>
<para>In typical environment the DHCP server is expected to send back its
responses on the same network interface as the query packets come in. This is
the default behavior. However, in some deployments it is expected the outbound
(response) packets will be sent as regular traffic and the outbound interface
will be determined by the routing tables. This kind of asymetric traffic
is uncommon, but valid. Kea now supports a parameter called
<command>outbound-interface</command> that control this behavior. It supports
two values. The first one, <userinput>same-as-inbound</userinput>, tells Kea
to send back the response on the same inteface the query packet came in. This
is the default behavior. The second one, <userinput>use-routing</userinput>
tells Kea to send regular UDP packets and let the kernel's routing table to
determine most appropriate interface. This only works when dhcp-socket-type is
set to udp. An example configuration looks as follows:
<screen>
"Dhcp4": {
"interfaces-config": {
"interfaces": [ "eth1", "eth3" ],
"dhcp-socket-type": "udp",
<userinput>"outbound-interface": "use-routing"</userinput>
},
...
}</screen>
</para>
<para>Interfaces are re-detected at each reconfiguration. This behavior
can be disabled by setting <command>re-detect</command> value to
<userinput>false</userinput>, for instance:
......
......@@ -227,6 +227,11 @@ CfgIface::textToSocketType(const std::string& socket_type_name) const {
}
}
CfgIface::OutboundIface
CfgIface::getOutboundIface() const {
return (outbound_iface_);
}
std::string
CfgIface::outboundTypeToText() const {
switch (outbound_iface_) {
......@@ -246,7 +251,7 @@ CfgIface::textToOutboundIface(const std::string& txt) {
return (USE_ROUTING);
} else {
isc_throw(InvalidSocketType, "unsupported socket type '"
isc_throw(BadValue, "unsupported outbound interface type '"
<< txt << "'");
}
}
......@@ -455,6 +460,10 @@ CfgIface::toElement() const {
result->set("dhcp-socket-type", Element::create(std::string("udp")));
}
if (outbound_iface_ != SAME_AS_INBOUND) {
result->set("outbound-interface", Element::create(outboundTypeToText()));
}
// Set re-detect
result->set("re-detect", Element::create(re_detect_));
......
......@@ -233,12 +233,24 @@ public:
/// @brief Returns the socket type in the textual format.
std::string socketTypeToText() const;
/// @brief Sets outbound interface type
///
/// @param traffic_type sets the type of traffic
void setOutboundIface(const OutboundIface& traffic_type);
/// @brief Returns outbound interface traffic type
///
/// @return type of traffic (use-routing or same-as-inbound)
OutboundIface getOutboundIface() const;
/// @brief Returns outbound type as string
///
/// @return text representation of the outbound type
std::string outboundTypeToText() const;
/// @brief Converts text to outbound interface
/// @param txt either 'same-as-inbound' or 'use-routing'
/// @return converted value
static OutboundIface textToOutboundIface(const std::string& txt);
/// @brief Converts the socket type in the textual format to the type
......
......@@ -98,6 +98,33 @@ TEST_F(IfacesConfigParserTest, interfaces) {
EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));
}
// This test checks that the parsed structure can be converted back to Element
// tree.
TEST_F(IfacesConfigParserTest, toElement) {
// Creates fake interfaces with fake addresses.
IfaceMgrTestConfig test_config(true);
// Configuration with one interface.
std::string config =
"{ \"interfaces\": [ \"eth0\" ], "
" \"dhcp-socket-type\": \"udp\","
" \"outbound-interface\": \"use-routing\", "
" \"re-detect\": false }";
ElementPtr config_element = Element::fromJSON(config);
// Parse the configuration.
IfacesConfigParser parser(AF_INET);
CfgIfacePtr cfg_iface = CfgMgr::instance().getStagingCfg()->getCfgIface();
ASSERT_TRUE(cfg_iface);
ASSERT_NO_THROW(parser.parse(cfg_iface, config_element));
// Check it can be unparsed.
runToElementTest<CfgIface>(config, *cfg_iface);
}
// This test verifies that it is possible to select the raw socket
// use in the configuration for interfaces.
TEST_F(IfacesConfigParserTest, socketTypeRaw) {
......@@ -177,4 +204,44 @@ TEST_F(IfacesConfigParserTest, socketTypeInvalid) {
ASSERT_THROW(parser6.parse(cfg_iface, config_element), DhcpConfigError);
}
// Tests that outbound-interface is parsed properly.
TEST_F(IfacesConfigParserTest, outboundInterface) {
// For DHCPv4 we accept 'use-routing' or 'same-as-inbound'.
IfacesConfigParser parser4(AF_INET);
// For DHCPv6 we don't accept this at all.
IfacesConfigParser parser6(AF_INET6);
CfgIfacePtr cfg_iface = CfgMgr::instance().getStagingCfg()->getCfgIface();
// The default should be to use the same as client's query packet.
EXPECT_EQ(CfgIface::SAME_AS_INBOUND, cfg_iface->getOutboundIface());
// Value 1: use-routing
std::string config = "{ \"interfaces\": [ ],"
"\"outbound-interface\": \"use-routing\","
" \"re-detect\": false }";
ElementPtr config_element = Element::fromJSON(config);
ASSERT_NO_THROW(parser4.parse(cfg_iface, config_element));
EXPECT_EQ(CfgIface::USE_ROUTING, cfg_iface->getOutboundIface());
EXPECT_THROW(parser6.parse(cfg_iface, config_element), DhcpConfigError);
// Value 2: same-as-inbound
config = "{ \"interfaces\": [ ],"
"\"outbound-interface\": \"same-as-inbound\","
" \"re-detect\": false }";
config_element = Element::fromJSON(config);
ASSERT_NO_THROW(parser4.parse(cfg_iface, config_element));
EXPECT_EQ(CfgIface::SAME_AS_INBOUND, cfg_iface->getOutboundIface());
EXPECT_THROW(parser6.parse(cfg_iface, config_element), DhcpConfigError);
// Other values are not supported.
config = "{ \"interfaces\": [ ],"
"\"outbound-interface\": \"default\","
" \"re-detect\": false }";
config_element = Element::fromJSON(config);
EXPECT_THROW(parser4.parse(cfg_iface, config_element), DhcpConfigError);
EXPECT_THROW(parser6.parse(cfg_iface, config_element), DhcpConfigError);
}
} // end of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment