Commit 80f01bc6 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2902] Implemented selective closure of sockets (e.g. only v4 sockets).

parent 1ca6a3ac
......@@ -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;
......
......@@ -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
......
......@@ -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<NakedIfaceMgr> 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
......
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