diff --git a/src/cpp/rtps/transport/TCPTransportInterface.cpp b/src/cpp/rtps/transport/TCPTransportInterface.cpp index a2912dc11f8..aff82c3fd95 100644 --- a/src/cpp/rtps/transport/TCPTransportInterface.cpp +++ b/src/cpp/rtps/transport/TCPTransportInterface.cpp @@ -705,10 +705,31 @@ bool TCPTransportInterface::OpenOutputChannel( listening_port = config->listening_ports.front(); } + bool local_lower_interface = false; + if (IPLocator::getPhysicalPort(physical_locator) == listening_port) + { + std::vector list; + std::vector local_interfaces; + get_ips(local_interfaces); + for (const auto& interface_it : local_interfaces) + { + Locator interface_loc(interface_it.locator); + interface_loc.port = physical_locator.port; + if (is_interface_allowed(interface_loc)) + { + list.push_back(interface_loc); + } + } + if (!list.empty() && (list.front() < physical_locator)) + { + local_lower_interface = true; + } + } + // If the remote physical port is higher than our listening port, a new CONNECT channel needs to be created and connected // and the locator added to the send_resource_list. // If the remote physical port is lower than our listening port, only the locator needs to be added to the send_resource_list. - if (IPLocator::getPhysicalPort(physical_locator) > listening_port) + if (IPLocator::getPhysicalPort(physical_locator) > listening_port || local_lower_interface) { // Client side (either Server-Client or LARGE_DATA) EPROSIMA_LOG_INFO(OpenOutputChannel, "OpenOutputChannel: [CONNECT] (physical: " diff --git a/test/unittest/transport/TCPv4Tests.cpp b/test/unittest/transport/TCPv4Tests.cpp index ceff1931f5f..800a4c218bb 100644 --- a/test/unittest/transport/TCPv4Tests.cpp +++ b/test/unittest/transport/TCPv4Tests.cpp @@ -2045,6 +2045,34 @@ TEST_F(TCPv4Tests, reconnect_after_open_port_failure) client_resource_list.clear(); } +// This test verifies that OpenOutputChannel correctly handles a remote locator with +// same physical port as the local listening port. +TEST_F(TCPv4Tests, opening_output_channel_with_same_locator_as_local_listening_port) +{ + TCPv4Transport transportUnderTest(descriptor); + transportUnderTest.init(); + + // Two locators with the same port as the local listening port, but different addresses + Locator_t lowerOutputChannelLocator; + lowerOutputChannelLocator.kind = LOCATOR_KIND_TCPv4; + lowerOutputChannelLocator.port = g_default_port; + IPLocator::setLogicalPort(lowerOutputChannelLocator, g_default_port); + Locator_t higherOutputChannelLocator = lowerOutputChannelLocator; + IPLocator::setIPv4(lowerOutputChannelLocator, 1, 1, 1, 1); + IPLocator::setIPv4(higherOutputChannelLocator, 255, 255, 255, 255); + + SendResourceList send_resource_list; + + // If the remote address is lower than the local one, no channel must be created but it must be added to the send_resource_list + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, lowerOutputChannelLocator)); + ASSERT_FALSE(transportUnderTest.is_output_channel_open_for(lowerOutputChannelLocator)); + ASSERT_EQ(send_resource_list.size(), 1); + // If the remote address is higher than the local one, a CONNECT channel must be created and added to the send_resource_list + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, higherOutputChannelLocator)); + ASSERT_TRUE(transportUnderTest.is_output_channel_open_for(higherOutputChannelLocator)); + ASSERT_EQ(send_resource_list.size(), 2); +} + void TCPv4Tests::HELPER_SetDescriptorDefaults() { descriptor.add_listener_port(g_default_port); diff --git a/test/unittest/transport/TCPv6Tests.cpp b/test/unittest/transport/TCPv6Tests.cpp index bb554e6bd36..14793895937 100644 --- a/test/unittest/transport/TCPv6Tests.cpp +++ b/test/unittest/transport/TCPv6Tests.cpp @@ -414,6 +414,32 @@ TEST_F(TCPv6Tests, reconnect_after_open_port_failure) client_resource_list.clear(); } +TEST_F(TCPv6Tests, opening_output_channel_with_same_locator_as_local_listening_port) +{ + descriptor.add_listener_port(g_default_port); + TCPv6Transport transportUnderTest(descriptor); + transportUnderTest.init(); + + // Two locators with the same port as the local listening port, but different addresses + Locator_t lowerOutputChannelLocator; + lowerOutputChannelLocator.kind = LOCATOR_KIND_TCPv6; + lowerOutputChannelLocator.port = g_default_port; + IPLocator::setLogicalPort(lowerOutputChannelLocator, g_default_port); + Locator_t higherOutputChannelLocator = lowerOutputChannelLocator; + IPLocator::setIPv6(lowerOutputChannelLocator, "::"); + IPLocator::setIPv6(higherOutputChannelLocator, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + + SendResourceList send_resource_list; + + // If the remote address is lower than the local one, no channel must be created but it must be added to the send_resource_list + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, lowerOutputChannelLocator)); + ASSERT_FALSE(transportUnderTest.is_output_channel_open_for(lowerOutputChannelLocator)); + ASSERT_EQ(send_resource_list.size(), 1); + // If the remote address is higher than the local one, a CONNECT channel must be created and added to the send_resource_list + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, higherOutputChannelLocator)); + ASSERT_TRUE(transportUnderTest.is_output_channel_open_for(higherOutputChannelLocator)); + ASSERT_EQ(send_resource_list.size(), 2); +} /* TEST_F(TCPv6Tests, send_and_receive_between_both_secure_ports)