From: Marcin Siodelski Date: Fri, 26 Apr 2013 12:36:56 +0000 (+0200) Subject: [2902] Implemented selective closure of sockets (e.g. only v4 sockets). X-Git-Tag: bind10-1.2.0beta1-release~440^2~22 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=80f01bc608cbd779c76a6e5d5ca8dade8b37dbdd;p=thirdparty%2Fkea.git [2902] Implemented selective closure of sockets (e.g. only v4 sockets). --- diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index ded2639c7a..b5c155be9e 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -58,11 +58,44 @@ Iface::Iface(const std::string& name, int ifindex) void Iface::closeSockets() { - for (SocketCollection::iterator sock = sockets_.begin(); - sock != sockets_.end(); ++sock) { - close(sock->sockfd_); + // Close IPv4 sockets. + closeSockets(AF_INET); + // Close IPv6 sockets. + closeSockets(AF_INET6); +} + +void +Iface::closeSockets(const uint16_t family) { + // Check that the correect 'family' value has been specified. + // The possible values are AF_INET or AF_INET6. Note that, in + // the current code they are used to differentiate that the + // socket is used to transmit IPv4 or IPv6 traffic. However, + // the actual family types of the sockets may be different, + // e.g. for LPF we are using raw sockets of AF_PACKET family. + // + // @todo Consider replacing the AF_INET and AF_INET6 with some + // enum which will not be confused with the actual socket type. + if ((family != AF_INET) && (family != AF_INET6)) { + isc_throw(BadValue, "Invalid socket family " << family + << " specified when requested to close all sockets" + << " which belong to this family"); + } + // Search for the socket of the specific type. + SocketCollection::iterator sock = sockets_.begin(); + while (sock != sockets_.end()) { + if (sock->family_ == family) { + // Close and delete the socket and move to the + // next one. + close(sock->sockfd_); + sockets_.erase(sock++); + + } else { + // Different type of socket. Let's move + // to the next one. + ++sock; + + } } - sockets_.clear(); } std::string @@ -150,6 +183,14 @@ void IfaceMgr::closeSockets() { } } +void +IfaceMgr::closeSockets(const uint16_t family) { + for (IfaceCollection::iterator iface = ifaces_.begin(); + iface != ifaces_.end(); ++iface) { + iface->closeSockets(family); + } +} + IfaceMgr::~IfaceMgr() { // control_buf_ is deleted automatically (scoped_ptr) control_buf_len_ = 0; diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 5c40feadc3..7dd8bac96f 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -110,6 +110,27 @@ public: /// @brief Closes all open sockets on interface. void closeSockets(); + /// @brief Closes all IPv4 or IPv6 sockets. + /// + /// This function closes sockets of the specific 'type' and closes them. + /// The 'type' of the socket indicates whether it is used to send IPv4 + /// or IPv6 packets. The allowed values of the parameter are AF_INET and + /// AF_INET6 for IPv4 and IPv6 packets respectively. It is important + /// to realize that the actual types of sockets may be different than + /// AF_INET for IPv4 packets. This is because, historically the IfaceMgr + /// always used AF_INET sockets for IPv4 traffic. This is no longer the + /// case when the Direct IPv4 traffic must be supported. In order to support + /// direct traffic, the IfaceMgr operates on raw sockets, e.g. AF_PACKET + /// family sockets on Linux. + /// + /// @todo Replace the AF_INET and AF_INET6 values with an enum + /// which will not be confused with the actual socket type. + /// + /// @param family type of the sockets to be closed (AF_INET or AF_INET6) + /// + /// @throw BadValue if family value is different than AF_INET or AF_INET6. + void closeSockets(const uint16_t family); + /// @brief Returns full interface name as "ifname/ifindex" string. /// /// @return string with interface name @@ -268,7 +289,7 @@ public: /// flag specifies if selected interface is multicast capable bool flag_multicast_; - /// flag specifies if selected interface is broadcast capable + /// flag specifies if selected interface is broadcast capable bool flag_broadcast_; /// interface flags (this value is as is returned by OS, @@ -538,6 +559,27 @@ public: /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes. void closeSockets(); + /// @brief Closes all IPv4 or IPv6 sockets. + /// + /// This function closes sockets of the specific 'type' and closes them. + /// The 'type' of the socket indicates whether it is used to send IPv4 + /// or IPv6 packets. The allowed values of the parameter are AF_INET and + /// AF_INET6 for IPv4 and IPv6 packets respectively. It is important + /// to realize that the actual types of sockets may be different than + /// AF_INET for IPv4 packets. This is because, historically the IfaceMgr + /// always used AF_INET sockets for IPv4 traffic. This is no longer the + /// case when the Direct IPv4 traffic must be supported. In order to support + /// direct traffic, the IfaceMgr operates on raw sockets, e.g. AF_PACKET + /// family sockets on Linux. + /// + /// @todo Replace the AF_INET and AF_INET6 values with an enum + /// which will not be confused with the actual socket type. + /// + /// @param family type of the sockets to be closed (AF_INET or AF_INET6) + /// + /// @throw BadValue if family value is different than AF_INET or AF_INET6. + void closeSockets(const uint16_t family); + /// @brief returns number of detected interfaces /// /// @return number of detected interfaces diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index ea14311a64..e2e51911b9 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -122,6 +122,24 @@ public: ~IfaceMgrTest() { } + // Get ther number of IPv4 or IPv6 sockets on the loopback interface + int getOpenSocketsCount(const Iface& iface, uint16_t family) const { + // Get all sockets. + Iface::SocketCollection sockets = iface.getSockets(); + + // Loop through sockets and try to find the ones which match the + // specified type. + int sockets_count = 0; + for (Iface::SocketCollection::const_iterator sock = sockets.begin(); + sock != sockets.end(); ++sock) { + // Match found, increase the counter. + if (sock->family_ == family) { + ++sockets_count; + } + } + return (sockets_count); + } + }; // We need some known interface to work reliably. Loopback interface @@ -216,6 +234,66 @@ TEST_F(IfaceMgrTest, basic) { ASSERT_TRUE(&ifacemgr != 0); } + +// This test verifies that sockets can be closed selectively, i.e. all +// IPv4 sockets can be closed first and all IPv6 sockets remain open. +TEST_F(IfaceMgrTest, closeSockets) { + // Will be using local loopback addresses for this test. + IOAddress loaddr("127.0.0.1"); + IOAddress loaddr6("::1"); + + // Create instance of IfaceMgr. + boost::scoped_ptr iface_mgr(new NakedIfaceMgr()); + ASSERT_TRUE(iface_mgr); + + // Out constructor does not detect interfaces by itself. We need + // to create one and add. + int ifindex = if_nametoindex(LOOPBACK); + ASSERT_GT(ifindex, 0); + Iface lo_iface(LOOPBACK, ifindex); + iface_mgr->getIfacesLst().push_back(lo_iface); + + // Create set of V4 and V6 sockets on the loopback interface. + // They must differ by a port they are bound to. + for (int i = 0; i < 6; ++i) { + // Every other socket will be IPv4. + if (i % 2) { + ASSERT_NO_THROW( + iface_mgr->openSocket(LOOPBACK, loaddr, 10000 + i) + ); + } else { + ASSERT_NO_THROW( + iface_mgr->openSocket(LOOPBACK, loaddr6, 10000 + i) + ); + } + } + + // At the end we should have 3 IPv4 and 3 IPv6 sockets open. + Iface* iface = iface_mgr->getIface(LOOPBACK); + ASSERT_TRUE(iface != NULL); + + int v4_sockets_count = getOpenSocketsCount(*iface, AF_INET); + ASSERT_EQ(3, v4_sockets_count); + int v6_sockets_count = getOpenSocketsCount(*iface, AF_INET6); + ASSERT_EQ(3, v6_sockets_count); + + // Let's try to close only IPv4 sockets. + ASSERT_NO_THROW(iface_mgr->closeSockets(AF_INET)); + v4_sockets_count = getOpenSocketsCount(*iface, AF_INET); + EXPECT_EQ(0, v4_sockets_count); + // The IPv6 sockets should remain open. + v6_sockets_count = getOpenSocketsCount(*iface, AF_INET6); + EXPECT_EQ(3, v6_sockets_count); + + // Let's try to close IPv6 sockets. + ASSERT_NO_THROW(iface_mgr->closeSockets(AF_INET6)); + v4_sockets_count = getOpenSocketsCount(*iface, AF_INET); + EXPECT_EQ(0, v4_sockets_count); + // They should have been closed now. + v6_sockets_count = getOpenSocketsCount(*iface, AF_INET6); + EXPECT_EQ(0, v6_sockets_count); +} + TEST_F(IfaceMgrTest, ifaceClass) { // basic tests for Iface inner class