Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[20238] Enabling multiple interfaces through whitelist in TCP servers (backport #4297) #4412

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/cpp/rtps/transport/TCPTransportInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,20 @@ uint16_t TCPTransportInterface::create_acceptor_socket(
std::vector<std::string> vInterfaces = get_binding_interfaces_list();
for (std::string& sInterface : vInterfaces)
{
Locator loc = locator;
if (loc.kind == LOCATOR_KIND_TCPv4)
{
IPLocator::setIPv4(loc, sInterface);
}
else if (loc.kind == LOCATOR_KIND_TCPv6)
{
IPLocator::setIPv6(loc, sInterface);
}
#if TLS_FOUND
if (configuration()->apply_security)
{
std::shared_ptr<TCPAcceptorSecure> acceptor =
std::make_shared<TCPAcceptorSecure>(io_service_, sInterface, locator);
std::make_shared<TCPAcceptorSecure>(io_service_, sInterface, loc);
acceptors_[acceptor->locator()] = acceptor;
acceptor->accept(this, ssl_context_);
final_port = static_cast<uint16_t>(acceptor->locator().port);
Expand All @@ -312,7 +321,7 @@ uint16_t TCPTransportInterface::create_acceptor_socket(
#endif // if TLS_FOUND
{
std::shared_ptr<TCPAcceptorBasic> acceptor =
std::make_shared<TCPAcceptorBasic>(io_service_, sInterface, locator);
std::make_shared<TCPAcceptorBasic>(io_service_, sInterface, loc);
acceptors_[acceptor->locator()] = acceptor;
acceptor->accept(this);
final_port = static_cast<uint16_t>(acceptor->locator().port);
Expand Down
24 changes: 22 additions & 2 deletions src/cpp/rtps/transport/TCPv6Transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,29 @@ TCPv6Transport::TCPv6Transport(
: TCPTransportInterface(LOCATOR_KIND_TCPv6)
, configuration_(descriptor)
{
for (const auto& interface : descriptor.interfaceWhiteList)
if (!descriptor.interfaceWhiteList.empty())
{
interface_whitelist_.emplace_back(ip::address_v6::from_string(interface));
const auto white_begin = descriptor.interfaceWhiteList.begin();
const auto white_end = descriptor.interfaceWhiteList.end();

std::vector<IPFinder::info_IP> local_interfaces;
get_ipv6s(local_interfaces, true);
for (const IPFinder::info_IP& infoIP : local_interfaces)
{
if (std::find_if(white_begin, white_end, [this, infoIP](const std::string& white_list_element)
{
return compare_ips(white_list_element, infoIP.name);
}) != white_end )
{
interface_whitelist_.emplace_back(ip::address_v6::from_string(infoIP.name));
}
}

if (interface_whitelist_.empty())
{
EPROSIMA_LOG_ERROR(TRANSPORT, "All whitelist interfaces were filtered out");
interface_whitelist_.emplace_back(ip::address_v6::from_string("2001:db8::"));
}
}

for (uint16_t& port : configuration_.listening_ports)
Expand Down
58 changes: 58 additions & 0 deletions test/unittest/transport/TCPv4Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,64 @@ TEST_F(TCPv4Tests, send_and_receive_between_allowed_interfaces_ports)
}
}

static void GetIP4s(
std::vector<IPFinder::info_IP>& interfaces)
{
IPFinder::getIPs(&interfaces, false);
auto new_end = remove_if(interfaces.begin(),
interfaces.end(),
[](IPFinder::info_IP ip)
{
return ip.type != IPFinder::IP4 && ip.type != IPFinder::IP4_LOCAL;
});
interfaces.erase(new_end, interfaces.end());
std::for_each(interfaces.begin(), interfaces.end(), [](IPFinder::info_IP& loc)
{
loc.locator.kind = LOCATOR_KIND_TCPv4;
});
}

TEST_F(TCPv4Tests, check_TCPv4_interface_whitelist_initialization)
{
std::vector<IPFinder::info_IP> interfaces;

GetIP4s(interfaces);

std::vector<std::string> mock_interfaces;
for (auto& ip : interfaces)
{
mock_interfaces.push_back(ip.name);
}
// Add manually localhost to test adding multiple interfaces
mock_interfaces.push_back("127.0.0.1");

for (auto& ip : mock_interfaces)
{
descriptor.interfaceWhiteList.emplace_back(ip);
}
MockTCPv4Transport transportUnderTest(descriptor);
transportUnderTest.init();

// Check that the transport whitelist and the acceptors map is the same size as the mock_interfaces
ASSERT_EQ(transportUnderTest.get_interface_whitelist().size(), descriptor.interfaceWhiteList.size());
ASSERT_EQ(transportUnderTest.get_acceptors_map().size(), descriptor.interfaceWhiteList.size());

// Check that every interface is in the whitelist
auto check_whitelist = transportUnderTest.get_interface_whitelist();
for (auto& ip : mock_interfaces)
{
ASSERT_NE(std::find(check_whitelist.begin(), check_whitelist.end(), asio::ip::address_v4::from_string(
ip)), check_whitelist.end());
}

// Check that every interface is in the acceptors map
for (const auto& test : transportUnderTest.get_acceptors_map())
{
ASSERT_NE(std::find(mock_interfaces.begin(), mock_interfaces.end(), IPLocator::toIPv4string(
test.first)), mock_interfaces.end());
}
}

#if TLS_FOUND
TEST_F(TCPv4Tests, send_and_receive_between_secure_ports_client_verifies)
{
Expand Down
64 changes: 64 additions & 0 deletions test/unittest/transport/TCPv6Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,70 @@ TEST_F(TCPv6Tests, autofill_port)
EXPECT_TRUE(transportUnderTest_multiple_autofill.configuration()->listening_ports.size() == 3);
}

static void GetIP6s(
std::vector<IPFinder::info_IP>& interfaces)
{
IPFinder::getIPs(&interfaces, false);
auto new_end = remove_if(interfaces.begin(),
interfaces.end(),
[](IPFinder::info_IP ip)
{
return ip.type != IPFinder::IP6 && ip.type != IPFinder::IP6_LOCAL;
});
interfaces.erase(new_end, interfaces.end());
std::for_each(interfaces.begin(), interfaces.end(), [](IPFinder::info_IP& loc)
{
loc.locator.kind = LOCATOR_KIND_TCPv6;
});
}

TEST_F(TCPv6Tests, check_TCPv6_interface_whitelist_initialization)
{
std::vector<IPFinder::info_IP> interfaces;

GetIP6s(interfaces);

// asio::ip::addres_v6 appends the interface name to the IP address, but the locator does not
// Create two different vectors to compare them
std::vector<std::string> asio_interfaces;
std::vector<std::string> locator_interfaces;
for (auto& ip : interfaces)
{
asio_interfaces.push_back(ip.name);
locator_interfaces.push_back(IPLocator::toIPv6string(ip.locator));
}
// Add manually localhost to test adding multiple interfaces
asio_interfaces.push_back("::1");
locator_interfaces.push_back("::1");

for (auto& ip : locator_interfaces)
{
descriptor.interfaceWhiteList.emplace_back(ip);
}
descriptor.add_listener_port(g_default_port);
MockTCPv6Transport transportUnderTest(descriptor);
transportUnderTest.init();

// Check that the transport whitelist and the acceptors map is the same size as the locator_interfaces
ASSERT_EQ(transportUnderTest.get_interface_whitelist().size(), descriptor.interfaceWhiteList.size());
ASSERT_EQ(transportUnderTest.get_acceptors_map().size(), descriptor.interfaceWhiteList.size());

// Check that every interface is in the whitelist
auto check_whitelist = transportUnderTest.get_interface_whitelist();
for (auto& ip : asio_interfaces)
{
ASSERT_NE(std::find(check_whitelist.begin(), check_whitelist.end(), asio::ip::address_v6::from_string(
ip)), check_whitelist.end());
}

// Check that every interface is in the acceptors map
for (const auto& test : transportUnderTest.get_acceptors_map())
{
ASSERT_NE(std::find(locator_interfaces.begin(), locator_interfaces.end(), IPLocator::toIPv6string(
test.first)), locator_interfaces.end());
}
}

// This test verifies server's channel resources mapping keys uniqueness, where keys are clients locators.
// Clients typically communicated its PID as its locator port. When having several clients in the same
// process this lead to overwriting server's channel resources map elements.
Expand Down
10 changes: 10 additions & 0 deletions test/unittest/transport/mock/MockTCPv4Transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ class MockTCPv4Transport : public TCPv4Transport
return unbound_channel_resources_;
}

const std::vector<asio::ip::address_v4>& get_interface_whitelist() const
{
return interface_whitelist_;
}

const std::map<Locator_t, std::shared_ptr<fastdds::rtps::TCPAcceptor>>& get_acceptors_map() const
{
return acceptors_;
}

};

} // namespace rtps
Expand Down
10 changes: 10 additions & 0 deletions test/unittest/transport/mock/MockTCPv6Transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ class MockTCPv6Transport : public TCPv6Transport
return unbound_channel_resources_;
}

const std::vector<asio::ip::address_v6>& get_interface_whitelist() const
{
return interface_whitelist_;
}

const std::map<Locator_t, std::shared_ptr<fastdds::rtps::TCPAcceptor>>& get_acceptors_map() const
{
return acceptors_;
}

};

} // namespace rtps
Expand Down
Loading