Note interfaces are not re-detected during <command>config-test</command>.
</para>
+ <para>Usually loopback interfaces (e.g. the "lo" or "lo0" interface)
+ may not configured but if only one interface is configured and
+ IP/UDP sockets are specified a loopback interface is accepted.
+ </para>
+
+ <para>It can be used for instance to run Kea in a FreeBSD jail having
+ only a loopback interface, servicing relayed DHCP request:
+
+ <screen>
+"Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ <userinput>"lo0"</userinput> ],
+ "dhcp-socket-type": "udp"
+ },
+ ...
+}</screen>
+ </para>
+
</section>
<section id="dhcpinform-unicast-issues">
}
</screen>
+ <para>Usually loopback interfaces (e.g. the "lo" or "lo0" interface)
+ may not configured but if only one interface is configured a loopback
+ interface is accepted. Note Kea requires link-local address which does
+ not exist on all systems, or a specified unicast address as in:
+ </para>
+
+ <screen>
+"Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ <userinput>"lo/::1"</userinput> ]
+ },
+ ...
+}
+ </screen>
+
</section>
<section id="ipv6-subnet-id">
control_buf_(new char[control_buf_len_]),
packet_filter_(new PktFilterInet()),
packet_filter6_(new PktFilterInet6()),
- test_mode_(false)
+ test_mode_(false),
+ allow_loopback_(false)
{
try {
// that the interface configuration is valid and that the interface
// is not a loopback interface. In both cases, we want to report
// that the socket will not be opened.
- if (iface->flag_loopback_) {
+ // Relax the check when the loopback interface was explicitely
+ // allowed
+ if (iface->flag_loopback_ && !allow_loopback_) {
IFACEMGR_ERROR(SocketConfigError, error_handler,
"must not open socket on the loopback"
" interface " << iface->getName());
// that the interface configuration is valid and that the interface
// is not a loopback interface. In both cases, we want to report
// that the socket will not be opened.
- if (iface->flag_loopback_) {
+ // Relax the check when the loopback interface was explicitely
+ // allowed
+ if (iface->flag_loopback_ && !allow_loopback_) {
IFACEMGR_ERROR(SocketConfigError, error_handler,
"must not open socket on the loopback"
" interface " << iface->getName());
-// Copyright (C) 2011-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
return (test_mode_);
}
+ /// @brief Allows or disallows the loopback interface
+ ///
+ /// By default the loopback interface is not considered when opening
+ /// sockets. This flag provides a way to relax this constraint.
+ ///
+ void setAllowLoopBack(const bool allow_loopback) {
+ allow_loopback_ = allow_loopback;
+ }
+
/// @brief Check if packet be sent directly to the client having no address.
///
/// Checks if IfaceMgr can send DHCPv4 packet to the client
///
/// This method opens sockets only on interfaces which have the
/// @c inactive6_ field set to false (are active). If the interface is active
- /// but it is not running, it is down, or is a loopback interface,
- /// an error is reported.
+ /// but it is not running, it is down, or is a loopback interface when
+ /// loopback is not allowed, an error is reported.
///
/// On the systems with multiple interfaces, it is often desired that the
/// failure to open a socket on a particular interface doesn't cause a
///
/// This method opens sockets only on interfaces which have the
/// @c inactive4_ field set to false (are active). If the interface is active
- /// but it is not running, it is down, or is a loopback interface,
- /// an error is reported.
+ /// but it is not running, it is down, or is a loopback interface when
+ /// oopback is not allowed, an error is reported.
///
/// The type of the socket being open depends on the selected Packet Filter
/// represented by a class derived from @c isc::dhcp::PktFilter abstract
/// @brief Indicates if the IfaceMgr is in the test mode.
bool test_mode_;
+
+ /// @brief Allows to use loopback
+ bool allow_loopback_;
};
}; // namespace isc::dhcp
-// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
}
+// This test verifies that IPv4 sockets are open on the loopback interface
+// when the loopback is active and allowed.
+TEST_F(IfaceMgrTest, openSockets4Loopback) {
+ NakedIfaceMgr ifacemgr;
+
+ // Remove all real interfaces and create a set of dummy interfaces.
+ ifacemgr.createIfaces();
+
+ // Allow the loopback interface.
+ ifacemgr.setAllowLoopBack(true);
+
+ // Make the loopback interface active.
+ ifacemgr.getIface("lo")->inactive4_ = false;
+
+ // Use the custom packet filter object. This object mimics the socket
+ // opening operation - the real socket is not open.
+ boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
+ ASSERT_TRUE(custom_packet_filter);
+ ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+
+ // Simulate opening sockets using the dummy packet filter.
+ ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, 0));
+
+ // Expect that the sockets are open on all interfaces.
+ EXPECT_EQ(1, ifacemgr.getIface("eth0")->getSockets().size());
+ EXPECT_EQ(1, ifacemgr.getIface("eth1")->getSockets().size());
+ EXPECT_EQ(1, ifacemgr.getIface("lo")->getSockets().size());
+}
+
// This test verifies that the socket is not open on the interface which is
// down, but sockets are open on all other non-loopback interfaces.
TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
#endif
}
+// This test checks that the sockets are open on the loopback interface
+// when the loopback is active and allowed.
+TEST_F(IfaceMgrTest, openSockets6Loopback) {
+ NakedIfaceMgr ifacemgr;
+
+ // Remove all real interfaces and create a set of dummy interfaces.
+ ifacemgr.createIfaces();
+
+ // Allow the loopback interface.
+ ifacemgr.setAllowLoopBack(true);
+
+ // Make the loopback interface active.
+ ifacemgr.getIface("lo")->inactive6_ = false;
+
+ // The loopback interface has no link-local (as for Linux but not BSD)
+ // so add one.
+ ifacemgr.getIface("lo")->addUnicast(IOAddress("::1"));
+
+ boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+ ASSERT_TRUE(filter);
+ ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+ // Simulate opening sockets using the dummy packet filter.
+ bool success = false;
+ ASSERT_NO_THROW(success = ifacemgr.openSockets6(DHCP6_SERVER_PORT));
+ EXPECT_TRUE(success);
+
+ // Check that the loopback interface has at least an open socket.
+ EXPECT_EQ(1, ifacemgr.getIface("lo")->getSockets().size());
+
+ // This socket should be bound to ::1
+ EXPECT_TRUE(ifacemgr.isBound("lo", "::1"));
+}
+
// This test checks that socket is not open on the interface which doesn't
// have a link-local address.
TEST_F(IfaceMgrTest, openSockets6NoLinkLocal) {
-// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// Close any open sockets because we're going to modify some properties
// of the IfaceMgr. Those modifications require that sockets are closed.
closeSockets();
+ // The loopback interface can be used only when:
+ // - wildcard is not used
+ // - UDP socket will be used, i.e. not IPv4 and RAW socket
+ // - there is one interface name only in the interface set
+ // and this interface is a loopback interface.
+ // - or the interface set is empty and all interfaces in the address
+ // map are the same and a loopback interface.
+ bool loopback_used_ = false;
+ if (!wildcard_used_ &&
+ ((family == AF_INET6) || (socket_type_ == SOCKET_UDP)) &&
+ (iface_set_.size() == 1) &&
+ (address_map_.empty())) {
+ // Get the first and only interface.
+ IfacePtr iface = IfaceMgr::instance().getIface(*iface_set_.begin());
+ if (iface && iface->flag_loopback_) {
+ loopback_used_ = true;
+ }
+ } else if (!wildcard_used_ &&
+ ((family == AF_INET6) || (socket_type_ == SOCKET_UDP)) &&
+ iface_set_.empty() &&
+ !address_map_.empty()) {
+ // Get the first interface
+ const std::string& name = address_map_.begin()->first;
+ bool same = true;
+ for (ExplicitAddressMap::const_iterator unicast = address_map_.begin();
+ unicast != address_map_.end(); ++unicast) {
+ if (unicast->first != name) {
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ IfacePtr iface = IfaceMgr::instance().getIface(name);
+ if (iface && iface->flag_loopback_) {
+ loopback_used_ = true;
+ }
+ }
+ }
// If wildcard interface '*' was not specified, set all interfaces to
// inactive state. We will later enable them selectively using the
// interface names specified by the user. If wildcard interface was
- // specified, mark all interfaces active. In all cases, mark loopback
- // inactive.
- setState(family, !wildcard_used_, true);
+ // specified, mark all interfaces active. Mark loopback inactive when
+ // not explicitely allowed.
+ setState(family, !wildcard_used_, loopback_used_);
IfaceMgr& iface_mgr = IfaceMgr::instance();
// Remove selection of unicast addresses from all interfaces.
iface_mgr.clearUnicasts();
+ // Allow the loopback interface when required.
+ iface_mgr.setAllowLoopBack(loopback_used_);
// 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
-// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
EXPECT_TRUE(socketOpen("eth1", "192.0.2.5"));
}
+// This test checks that it is possible to specify the loopback interface.
+TEST_F(CfgIfaceTest, explicitLoopbackV4) {
+ CfgIface cfg;
+ ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
+
+ // Use UDP sockets
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
+
+ // Open sockets on specified interfaces and addresses.
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
+
+ EXPECT_TRUE(socketOpen("lo", "127.0.0.1"));
+
+ // Close all sockets and make sure they are really closed.
+ cfg.closeSockets();
+ ASSERT_FALSE(socketOpen("lo", "127.0.0.1"));
+
+ // Reset configuration.
+ cfg.reset();
+
+ // Retry with wirdcard
+ ASSERT_NO_THROW(cfg.use(AF_INET, "*"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
+
+ // Retry without UDP sockets
+ cfg.reset();
+ ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
+
+ // Retry with a second interface
+ cfg.reset();
+ ASSERT_NO_THROW(cfg.use(AF_INET, "eth0"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
+
+ // Finally with a second interface and address
+ cfg.reset();
+ ASSERT_NO_THROW(cfg.use(AF_INET, "eth0/10.0.0.1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET, "lo"));
+ ASSERT_NO_THROW(cfg.useSocketType(AF_INET, CfgIface::SOCKET_UDP));
+ cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", "127.0.0.1"));
+}
+
// This test checks that the interface names can be explicitly selected
// by their names and IPv6 sockets are opened on these interfaces.
TEST_F(CfgIfaceTest, explicitNamesV6) {
ASSERT_THROW(cfg.use(AF_INET6, "*"), DuplicateIfaceName);
}
+// This test checks that it is possible to specify the loopback interface.
+// Note that without a link-local address an unicast address is required.
+TEST_F(CfgIfaceTest, explicitLoopbackV6) {
+ CfgIface cfg;
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
+
+ // Open sockets on specified interfaces and addresses.
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
+
+ EXPECT_TRUE(socketOpen("lo", AF_INET6));
+
+ // Close all sockets and make sure they are really closed.
+ cfg.closeSockets();
+ ASSERT_FALSE(socketOpen("lo", AF_INET6));
+
+ // Reset configuration.
+ cfg.reset();
+
+ // Retry with wirdcard
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "*"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", AF_INET6));
+
+ // Retry with a second interface
+ cfg.reset();
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", AF_INET6));
+
+ // Finally with a second interface and address
+ cfg.reset();
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0/2001:db8:1::1"));
+ ASSERT_NO_THROW(cfg.use(AF_INET6, "lo/::1"));
+ cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
+ // No loopback socket
+ EXPECT_FALSE(socketOpen("lo", AF_INET6));
+}
+
// Test that the equality and inequality operators work fine for CfgIface.
TEST_F(CfgIfaceTest, equality) {
CfgIface cfg1;