Skip to content

Commit 910ac99

Browse files
MiguelCompanyjuanlofer-eprosimaDanipiza
authored
Assert liveliness with periodic heartbeats in secure participants (#6411)
* Refs #24372. Assert liveliness with periodic heartbeats in secure participants. Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com> * Refs #24372. Added regression test. Signed-off-by: danipiza <dpizarrogallego@gmail.com> * Refs #24372. Revert change in returned value. Signed-off-by: Miguel Company <miguelcompany@eprosima.com> * Refs #24372. Improve null pointer checks. Signed-off-by: Miguel Company <miguelcompany@eprosima.com> * Refs #24372. Uncrustify. Signed-off-by: Miguel Company <miguelcompany@eprosima.com> --------- Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com> Signed-off-by: danipiza <dpizarrogallego@gmail.com> Signed-off-by: Miguel Company <miguelcompany@eprosima.com> Co-authored-by: Juan Lopez Fernandez <juanlopez@eprosima.com> Co-authored-by: danipiza <dpizarrogallego@gmail.com>
1 parent 42e0d08 commit 910ac99

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

src/cpp/rtps/builtin/discovery/participant/PDPSimple.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ void PDPSimple::announceParticipantState(
291291

292292
auto endpoints = dynamic_cast<fastdds::rtps::SimplePDPEndpoints*>(builtin_endpoints_.get());
293293
assert(endpoints && endpoints->writer.history_);
294-
295294
if (!endpoints || !endpoints->writer.history_)
296295
{
297296
FASTDDS_UNREACHABLE(); // “cannot happen” invariant
@@ -303,6 +302,22 @@ void PDPSimple::announceParticipantState(
303302
if (!(dispose || new_change))
304303
{
305304
endpoints->writer.writer_->send_periodic_announcement();
305+
306+
#if HAVE_SECURITY
307+
if (mp_RTPSParticipant->is_secure())
308+
{
309+
// PDP non-secure endpoints are unmatched after participant authentication succeeds (and secure PDP
310+
// endpoints are matched), and since the secure ones are TRANSIENT_LOCAL, we send periodic heartbeats
311+
// to assert liveliness on remote participants
312+
auto secure = dynamic_cast<fastdds::rtps::SimplePDPEndpointsSecure*>(builtin_endpoints_.get());
313+
assert(secure && secure->secure_writer.writer_);
314+
if (!secure || !secure->secure_writer.writer_)
315+
{
316+
FASTDDS_UNREACHABLE(); // “cannot happen” invariant
317+
}
318+
secure->secure_writer.writer_->send_periodic_heartbeat(true, true);
319+
}
320+
#endif // HAVE_SECURITY
306321
}
307322
}
308323
}

test/blackbox/common/BlackboxTestsSecurity.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,98 @@ TEST_P(Security, RemoveParticipantProxyDataonSecurityManagerLeaseExpired_validat
13151315

13161316
}
13171317

1318+
// Regression test for secure PDP liveliness maintenance without user traffic.
1319+
TEST(Security, SecureParticipantsDoNotLoseDiscoveryWithoutUserTraffic)
1320+
{
1321+
PubSubReader<HelloWorldPubSubType> reader("HelloWorldTopic_secure_participant_liveliness");
1322+
PubSubWriter<HelloWorldPubSubType> writer("HelloWorldTopic_secure_participant_liveliness");
1323+
const auto idle_timeout = std::chrono::seconds(6);
1324+
const auto data_timeout = std::chrono::seconds(10);
1325+
// Keep the lease short so loss of secure PDP liveliness shows up quickly
1326+
const auto lease_duration = eprosima::fastdds::dds::Duration_t(3, 0);
1327+
const auto announcement_period = eprosima::fastdds::dds::Duration_t(1, 0);
1328+
// Use UDP transport to later block the fallback participant DATA(P) traffic
1329+
auto reader_transport = std::make_shared<test_UDPv4TransportDescriptor>();
1330+
auto writer_transport = std::make_shared<test_UDPv4TransportDescriptor>();
1331+
1332+
const std::string governance_file("governance_helloworld_all_enable.smime");
1333+
const std::string permissions_file("permissions_helloworld.smime");
1334+
1335+
CommonPermissionsConfigure(reader, writer, governance_file, permissions_file);
1336+
// Force the test through UDP to avoid local shortcuts masking the secure discovery behavior
1337+
reader.disable_builtin_transport().add_user_transport_to_pparams(reader_transport);
1338+
writer.disable_builtin_transport().add_user_transport_to_pparams(writer_transport);
1339+
1340+
// Two secure participants that authenticate, discover each other, and exchange user data
1341+
reader.lease_duration(lease_duration, announcement_period)
1342+
.history_depth(10)
1343+
.reliability(eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS)
1344+
.init();
1345+
ASSERT_TRUE(reader.isInitialized());
1346+
1347+
writer.lease_duration(lease_duration, announcement_period)
1348+
.history_depth(10)
1349+
.reliability(eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS)
1350+
.init();
1351+
ASSERT_TRUE(writer.isInitialized());
1352+
1353+
reader.wait_authorized();
1354+
writer.wait_authorized();
1355+
1356+
// Wait until both endpoints are matched
1357+
reader.wait_discovery();
1358+
writer.wait_discovery();
1359+
1360+
// Verify discovery state stayed stable
1361+
auto assert_still_discovered = [&reader, &writer]()
1362+
{
1363+
ASSERT_TRUE(reader.is_matched());
1364+
ASSERT_TRUE(writer.is_matched());
1365+
ASSERT_EQ(reader.get_matched(), 1u);
1366+
ASSERT_EQ(writer.get_matched(), 1u);
1367+
1368+
const auto reader_status = reader.get_subscription_matched_status();
1369+
const auto writer_status = writer.get_publication_matched_status();
1370+
ASSERT_EQ(reader_status.total_count, 1);
1371+
ASSERT_EQ(writer_status.total_count, 1);
1372+
};
1373+
1374+
// Check the secure participants must not be undiscovered just because user traffic stops
1375+
auto assert_idle_period_keeps_discovery = [&reader, &writer, &idle_timeout, &assert_still_discovered]()
1376+
{
1377+
ASSERT_FALSE(reader.wait_participant_undiscovery(idle_timeout));
1378+
ASSERT_FALSE(writer.wait_participant_undiscovery(idle_timeout));
1379+
1380+
assert_still_discovered();
1381+
};
1382+
1383+
// Discovery is only useful if data can still flow after the idle window
1384+
auto assert_data_flow = [&reader, &writer, &data_timeout, &assert_still_discovered]()
1385+
{
1386+
auto data = default_helloworld_data_generator(2);
1387+
1388+
reader.startReception(data);
1389+
writer.send(data);
1390+
1391+
ASSERT_TRUE(data.empty());
1392+
ASSERT_EQ(reader.block_for_all(data_timeout), 2u);
1393+
assert_still_discovered();
1394+
};
1395+
1396+
ASSERT_NO_FATAL_FAILURE(assert_still_discovered());
1397+
1398+
// After the secure PDP endpoints are established, stop the unprotected participant DATA(P)
1399+
// traffic, to depend on the secure liveliness maintenance path
1400+
reader_transport->test_transport_options->always_drop_participant_builtin_topic_data = true;
1401+
writer_transport->test_transport_options->always_drop_participant_builtin_topic_data = true;
1402+
1403+
// Exercise the idle scenario twice to catch both immediate loss and unstable recovery/rematch behavior
1404+
ASSERT_NO_FATAL_FAILURE(assert_idle_period_keeps_discovery());
1405+
ASSERT_NO_FATAL_FAILURE(assert_data_flow());
1406+
ASSERT_NO_FATAL_FAILURE(assert_idle_period_keeps_discovery());
1407+
ASSERT_NO_FATAL_FAILURE(assert_data_flow());
1408+
}
1409+
13181410
TEST(Security, AllowUnauthenticatedParticipants_EntityCreationFailsIfRTPSProtectionIsNotNONE)
13191411
{
13201412
PubSubReader<HelloWorldPubSubType> reader("HelloWorldTopic");

0 commit comments

Comments
 (0)