From e4c3f3ec1c96fa1b3277e6f13984434e9c45650c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ferreira=20Gonz=C3=A1lez?= Date: Wed, 21 Feb 2024 07:44:44 +0100 Subject: [PATCH] Enabling multiple interfaces through whitelist in TCP servers (#4297) * Refs #20238: Add whitelist test in TCPv4 Signed-off-by: cferreiragonz * Refs #20238: Add whitelist test in TCPv6 Signed-off-by: cferreiragonz * Refs #20238: Adding interface to locator if whitelist is not empty Signed-off-by: cferreiragonz * Refs #20238: Uncrustify Signed-off-by: cferreiragonz * Refs #20238: Fix build in Windows Signed-off-by: cferreiragonz * Refs #20238: Apply suggestions Signed-off-by: cferreiragonz * Refs #20238: Remove repeated addition of listener port (added by default) Signed-off-by: cferreiragonz --------- Signed-off-by: cferreiragonz (cherry picked from commit 5f65a65196dff2453ff86df711db018fefed1e54) --- .../rtps/transport/TCPTransportInterface.cpp | 13 +- test/unittest/transport/TCPv4Tests.cpp | 150 ++++++++++++++++++ test/unittest/transport/TCPv6Tests.cpp | 64 ++++++++ .../transport/mock/MockTCPv4Transport.h | 10 ++ .../transport/mock/MockTCPv6Transport.h | 10 ++ 5 files changed, 245 insertions(+), 2 deletions(-) diff --git a/src/cpp/rtps/transport/TCPTransportInterface.cpp b/src/cpp/rtps/transport/TCPTransportInterface.cpp index 5c6cd4d4611..a2912dc11f8 100644 --- a/src/cpp/rtps/transport/TCPTransportInterface.cpp +++ b/src/cpp/rtps/transport/TCPTransportInterface.cpp @@ -299,11 +299,20 @@ uint16_t TCPTransportInterface::create_acceptor_socket( std::vector 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 acceptor = - std::make_shared(io_service_, sInterface, locator); + std::make_shared(io_service_, sInterface, loc); acceptors_[acceptor->locator()] = acceptor; acceptor->accept(this, ssl_context_); final_port = static_cast(acceptor->locator().port); @@ -312,7 +321,7 @@ uint16_t TCPTransportInterface::create_acceptor_socket( #endif // if TLS_FOUND { std::shared_ptr acceptor = - std::make_shared(io_service_, sInterface, locator); + std::make_shared(io_service_, sInterface, loc); acceptors_[acceptor->locator()] = acceptor; acceptor->accept(this); final_port = static_cast(acceptor->locator().port); diff --git a/test/unittest/transport/TCPv4Tests.cpp b/test/unittest/transport/TCPv4Tests.cpp index 8ec3f8748c4..19103d71b8c 100644 --- a/test/unittest/transport/TCPv4Tests.cpp +++ b/test/unittest/transport/TCPv4Tests.cpp @@ -463,6 +463,156 @@ TEST_F(TCPv4Tests, send_and_receive_between_allowed_interfaces_ports) } } +<<<<<<< HEAD +======= +static void GetIP4s( + std::vector& 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; + }); +} + +// Send and receive between allowed interfaces (the first available except the local) added by name +TEST_F(TCPv4Tests, send_and_receive_between_allowed_interfaces_ports_by_name) +{ + std::vector interfaces; + + GetIP4s(interfaces); + + eprosima::fastdds::dds::Log::SetVerbosity(eprosima::fastdds::dds::Log::Kind::Info); + std::regex filter("RTCP(?!_SEQ)"); + eprosima::fastdds::dds::Log::SetCategoryFilter(filter); + TCPv4TransportDescriptor recvDescriptor; + std::cout << "Adding to whitelist: " << interfaces[0].dev << " " << interfaces[0].name << " " << + interfaces[0].locator << std::endl; + recvDescriptor.interfaceWhiteList.emplace_back(interfaces[0].dev); + + recvDescriptor.add_listener_port(g_default_port); + TCPv4Transport receiveTransportUnderTest(recvDescriptor); + receiveTransportUnderTest.init(); + + TCPv4TransportDescriptor sendDescriptor; + sendDescriptor.interfaceWhiteList.emplace_back(interfaces[0].dev); + + TCPv4Transport sendTransportUnderTest(sendDescriptor); + sendTransportUnderTest.init(); + + Locator_t inputLocator; + inputLocator.kind = LOCATOR_KIND_TCPv4; + inputLocator.port = g_default_port; + inputLocator.set_address(interfaces[0].locator); + IPLocator::setLogicalPort(inputLocator, 7410); + + LocatorList_t locator_list; + locator_list.push_back(inputLocator); + + Locator_t outputLocator; + outputLocator.kind = LOCATOR_KIND_TCPv4; + outputLocator.set_address(interfaces[0].locator); + outputLocator.port = g_default_port; + IPLocator::setLogicalPort(outputLocator, 7410); + + { + MockReceiverResource receiver(receiveTransportUnderTest, inputLocator); + MockMessageReceiver* msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + ASSERT_TRUE(receiveTransportUnderTest.IsInputChannelOpen(inputLocator)); + + SendResourceList send_resource_list; + ASSERT_TRUE(sendTransportUnderTest.OpenOutputChannel(send_resource_list, outputLocator)); + ASSERT_FALSE(send_resource_list.empty()); + octet message[5] = { 'H', 'e', 'l', 'l', 'o' }; + bool bOk = false; + std::function recCallback = [&]() + { + EXPECT_EQ(memcmp(message, msg_recv->data, 5), 0); + bOk = true; + }; + + msg_recv->setCallback(recCallback); + + bool bFinish(false); + auto sendThreadFunction = [&]() + { + Locators input_begin(locator_list.begin()); + Locators input_end(locator_list.end()); + + bool sent = + send_resource_list.at(0)->send(message, 5, &input_begin, &input_end, + (std::chrono::steady_clock::now() + std::chrono::microseconds(100))); + while (!bFinish && !sent) + { + Locators input_begin2(locator_list.begin()); + Locators input_end2(locator_list.end()); + + sent = + send_resource_list.at(0)->send(message, 5, &input_begin2, &input_end2, + (std::chrono::steady_clock::now() + std::chrono::microseconds(100))); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + EXPECT_TRUE(sent); + //EXPECT_TRUE(transportUnderTest.send(message, 5, outputLocator, inputLocator)); + }; + + senderThread.reset(new std::thread(sendThreadFunction)); + std::this_thread::sleep_for(std::chrono::seconds(10)); + bFinish = true; + senderThread->join(); + ASSERT_TRUE(bOk); + } +} + +TEST_F(TCPv4Tests, check_TCPv4_interface_whitelist_initialization) +{ + std::vector interfaces; + + GetIP4s(interfaces); + + std::vector 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()); + } +} + +>>>>>>> 5f65a6519 (Enabling multiple interfaces through whitelist in TCP servers (#4297)) #if TLS_FOUND TEST_F(TCPv4Tests, send_and_receive_between_secure_ports_client_verifies) { diff --git a/test/unittest/transport/TCPv6Tests.cpp b/test/unittest/transport/TCPv6Tests.cpp index d0c77c98277..f35293c74f3 100644 --- a/test/unittest/transport/TCPv6Tests.cpp +++ b/test/unittest/transport/TCPv6Tests.cpp @@ -207,6 +207,70 @@ TEST_F(TCPv6Tests, autofill_port) EXPECT_TRUE(transportUnderTest_multiple_autofill.configuration()->listening_ports.size() == 3); } +static void GetIP6s( + std::vector& 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 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 asio_interfaces; + std::vector 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. diff --git a/test/unittest/transport/mock/MockTCPv4Transport.h b/test/unittest/transport/mock/MockTCPv4Transport.h index 6a1e7300b86..f5aff8b452b 100644 --- a/test/unittest/transport/mock/MockTCPv4Transport.h +++ b/test/unittest/transport/mock/MockTCPv4Transport.h @@ -46,6 +46,16 @@ class MockTCPv4Transport : public TCPv4Transport return unbound_channel_resources_; } + const std::vector& get_interface_whitelist() const + { + return interface_whitelist_; + } + + const std::map>& get_acceptors_map() const + { + return acceptors_; + } + }; } // namespace rtps diff --git a/test/unittest/transport/mock/MockTCPv6Transport.h b/test/unittest/transport/mock/MockTCPv6Transport.h index 37b8d7c02d3..33f9155e2dc 100644 --- a/test/unittest/transport/mock/MockTCPv6Transport.h +++ b/test/unittest/transport/mock/MockTCPv6Transport.h @@ -46,6 +46,16 @@ class MockTCPv6Transport : public TCPv6Transport return unbound_channel_resources_; } + const std::vector& get_interface_whitelist() const + { + return interface_whitelist_; + } + + const std::map>& get_acceptors_map() const + { + return acceptors_; + } + }; } // namespace rtps