mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[border-agent] improve DTLS session resource management (#13078)
Every DTLS ClientHello from an unseen port previously allocated a dynamic CoapDtlsSession on the heap before DTLS cookie verification. This allowed multiple connection attempts to leave allocated sessions active indefinitely, leading to high memory utilization. To resolve this: - Enforce a 15-second handshake timeout on newly allocated sessions. Connecting sessions that do not successfully finish the handshake within 15 seconds are cleanly disconnected and freed. - Enforce a session limit cap of 16 concurrent secure sessions on the Border Agent. Reaching this limit immediately rejects new session connection requests before triggering heap allocation. - Implement Nexus test case TestBorderAgentSessionsLimit to robustly verify both session limit rejection and handshake timeout behavior.
This commit is contained in:
@@ -2574,6 +2574,137 @@ void TestBorderAgentServiceRegistrationRename(void)
|
||||
node1.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
||||
}
|
||||
|
||||
void TestBorderAgentSessionsLimit(void)
|
||||
{
|
||||
Core nexus;
|
||||
Node &node0 = nexus.CreateNode();
|
||||
Node *nodes[20];
|
||||
Ip6::SockAddr sockAddr;
|
||||
Pskc pskc;
|
||||
Manager::SessionIterator iter;
|
||||
Manager::SessionInfo sessionInfo;
|
||||
uint8_t sessionCount;
|
||||
|
||||
Log("------------------------------------------------------------------------------------------------------");
|
||||
Log("TestBorderAgentSessionsLimit");
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
SuccessOrQuit(node0.SetLogLevel(kLogLevelInfo));
|
||||
|
||||
node0.Form();
|
||||
|
||||
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
||||
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
for (uint8_t i = 0; i < 20; i++)
|
||||
{
|
||||
nodes[i] = &nexus.CreateNode();
|
||||
SuccessOrQuit(nodes[i]->Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
|
||||
nodes[i]->Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
|
||||
nodes[i]->Get<ThreadNetif>().Up();
|
||||
}
|
||||
|
||||
VerifyOrQuit(node0.Get<Manager>().IsEnabled());
|
||||
VerifyOrQuit(node0.Get<Manager>().IsRunning());
|
||||
|
||||
SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(node0.Get<Manager>().GetUdpPort()));
|
||||
|
||||
sockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
|
||||
sockAddr.SetPort(node0.Get<Manager>().GetUdpPort());
|
||||
node0.Get<KeyManager>().GetPskc(pskc);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Establish 16 concurrent secure sessions (the limit)");
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
SuccessOrQuit(nodes[i]->Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
||||
SuccessOrQuit(nodes[i]->Get<Tmf::SecureAgent>().Open(0));
|
||||
SuccessOrQuit(nodes[i]->Get<Tmf::SecureAgent>().Connect(sockAddr));
|
||||
}
|
||||
|
||||
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
VerifyOrQuit(nodes[i]->Get<Tmf::SecureAgent>().IsConnected());
|
||||
}
|
||||
|
||||
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 16);
|
||||
|
||||
// Verify exactly 16 sessions are connected
|
||||
sessionCount = 0;
|
||||
iter.Init(node0.GetInstance());
|
||||
while (iter.GetNextSessionInfo(sessionInfo) == kErrorNone)
|
||||
{
|
||||
VerifyOrQuit(sessionInfo.mIsConnected);
|
||||
sessionCount++;
|
||||
}
|
||||
VerifyOrQuit(sessionCount == 16);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Try to establish a 17th secure session, should be rejected");
|
||||
|
||||
SuccessOrQuit(nodes[16]->Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
||||
SuccessOrQuit(nodes[16]->Get<Tmf::SecureAgent>().Open(0));
|
||||
SuccessOrQuit(nodes[16]->Get<Tmf::SecureAgent>().Connect(sockAddr));
|
||||
|
||||
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
||||
|
||||
VerifyOrQuit(!nodes[16]->Get<Tmf::SecureAgent>().IsConnected());
|
||||
VerifyOrQuit(node0.Get<Manager>().GetCounters().mPskcSecureSessionSuccesses == 16);
|
||||
|
||||
// Verify count remains 16
|
||||
sessionCount = 0;
|
||||
iter.Init(node0.GetInstance());
|
||||
while (iter.GetNextSessionInfo(sessionInfo) == kErrorNone)
|
||||
{
|
||||
sessionCount++;
|
||||
}
|
||||
VerifyOrQuit(sessionCount == 16);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Close all sessions to clean up");
|
||||
|
||||
for (uint8_t i = 0; i < 17; i++)
|
||||
{
|
||||
nodes[i]->Get<Tmf::SecureAgent>().Close();
|
||||
}
|
||||
|
||||
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
|
||||
|
||||
iter.Init(node0.GetInstance());
|
||||
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Verify Handshake Timeout: start a session but block the client right after first packet");
|
||||
|
||||
SuccessOrQuit(nodes[0]->Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
|
||||
SuccessOrQuit(nodes[0]->Get<Tmf::SecureAgent>().Open(0));
|
||||
SuccessOrQuit(nodes[0]->Get<Tmf::SecureAgent>().Connect(sockAddr));
|
||||
|
||||
// Drop client's interface immediately so it never receives HelloVerifyRequest
|
||||
nodes[0]->Get<ThreadNetif>().Down();
|
||||
|
||||
// Wait for 14 seconds (handshake timeout is 15 seconds)
|
||||
nexus.AdvanceTime(14 * Time::kOneSecondInMsec);
|
||||
|
||||
// The session should still be in connecting state on node0
|
||||
iter.Init(node0.GetInstance());
|
||||
SuccessOrQuit(iter.GetNextSessionInfo(sessionInfo));
|
||||
VerifyOrQuit(!sessionInfo.mIsConnected);
|
||||
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
||||
|
||||
// Wait for additional 4 seconds (total 18 seconds, past the 15-second timeout + 2-second guard time)
|
||||
nexus.AdvanceTime(4 * Time::kOneSecondInMsec);
|
||||
|
||||
// The session should have timed out and been removed
|
||||
iter.Init(node0.GetInstance());
|
||||
VerifyOrQuit(iter.GetNextSessionInfo(sessionInfo) == kErrorNotFound);
|
||||
|
||||
Log("TestBorderAgentSessionsLimit passed successfully!");
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
@@ -2586,6 +2717,7 @@ int main(void)
|
||||
ot::Nexus::TestBorderAgentTxtDataCallback();
|
||||
ot::Nexus::TestBorderAgentServiceRegistration();
|
||||
ot::Nexus::TestBorderAgentServiceRegistrationRename();
|
||||
ot::Nexus::TestBorderAgentSessionsLimit();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user