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:
/// @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
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) {
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