Skip to content

Commit eaf095f

Browse files
Stop treating BUSY response as fatal when CASE re-attempts are enabled.
1. We now communicate the requested delay to the session establishment delegate when we get a BUSY response. 2. OperationalSessionSetup, when it has CASE re-attempts enabled, treats a BUSY response as a trigger to reattempt, not a fatal error. 3. In CASEServer, set the requested delay to a better value if we have sent Sigma2 and are waiting for a response to that. Fixes project-chip#28288
1 parent 28da08f commit eaf095f

9 files changed

+97
-15
lines changed

src/app/OperationalSessionSetup.cpp

+38-5
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ void OperationalSessionSetup::OnSessionEstablishmentError(CHIP_ERROR error, Sess
430430
// member instead of having a boolean
431431
// mTryingNextResultDueToSessionEstablishmentError, so we can recover the
432432
// error in UpdateDeviceData.
433-
if (CHIP_ERROR_TIMEOUT == error)
433+
if (CHIP_ERROR_TIMEOUT == error || CHIP_ERROR_BUSY == error)
434434
{
435435
#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
436436
// Make a copy of the ReliableMessageProtocolConfig, since our
@@ -480,6 +480,15 @@ void OperationalSessionSetup::OnSessionEstablishmentError(CHIP_ERROR error, Sess
480480
// Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
481481
}
482482

483+
void OperationalSessionSetup::OnResponderBusy(System::Clock::Milliseconds16 requestedDelay)
484+
{
485+
#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
486+
// Store the requested delay, so that we can use it for scheduling our
487+
// retry.
488+
mRequestedBusyDelay = requestedDelay;
489+
#endif
490+
}
491+
483492
void OperationalSessionSetup::OnSessionEstablished(const SessionHandle & session)
484493
{
485494
VerifyOrReturn(mState == State::Connecting,
@@ -705,9 +714,22 @@ CHIP_ERROR OperationalSessionSetup::ScheduleSessionSetupReattempt(System::Clock:
705714
static_assert(UINT16_MAX / CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_INITIAL_DELAY_SECONDS >=
706715
(1 << CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_MAX_BACKOFF),
707716
"Our backoff calculation will overflow.");
708-
timerDelay = System::Clock::Seconds16(
717+
System::Clock::Timeout actualTimerDelay = System::Clock::Seconds16(
709718
static_cast<uint16_t>(CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_INITIAL_DELAY_SECONDS
710719
<< min((mAttemptsDone - 1), CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_MAX_BACKOFF)));
720+
const bool responseWasBusy = mRequestedBusyDelay != System::Clock::kZero;
721+
if (responseWasBusy)
722+
{
723+
if (mRequestedBusyDelay > actualTimerDelay)
724+
{
725+
actualTimerDelay = mRequestedBusyDelay;
726+
}
727+
728+
// Reset mRequestedBusyDelay now that we have consumed it, so it does
729+
// not affect future reattempts not triggered by a busy response.
730+
mRequestedBusyDelay = System::Clock::kZero;
731+
}
732+
711733
if (mAttemptsDone % 2 == 0)
712734
{
713735
// It's possible that the other side received one of our Sigma1 messages
@@ -716,11 +738,22 @@ CHIP_ERROR OperationalSessionSetup::ScheduleSessionSetupReattempt(System::Clock:
716738
// listening for Sigma1 messages again.
717739
//
718740
// To handle that, on every other retry, add the amount of time it would
719-
// take the other side to time out.
741+
// take the other side to time out. It would be nice if we could rely
742+
// on the delay reported in a BUSY response to just tell us that value,
743+
// but in practice for old devices BUSY often sends some hardcoded value
744+
// that tells us nothing about when the other side will decide it has
745+
// timed out.
720746
auto additionalTimeout = CASESession::ComputeSigma2ResponseTimeout(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig()));
721-
timerDelay += std::chrono::duration_cast<System::Clock::Seconds16>(additionalTimeout);
747+
actualTimerDelay += additionalTimeout;
722748
}
723-
CHIP_ERROR err = mInitParams.exchangeMgr->GetSessionManager()->SystemLayer()->StartTimer(timerDelay, TrySetupAgain, this);
749+
timerDelay = std::chrono::duration_cast<System::Clock::Seconds16>(actualTimerDelay);
750+
751+
CHIP_ERROR err = mInitParams.exchangeMgr->GetSessionManager()->SystemLayer()->StartTimer(actualTimerDelay, TrySetupAgain, this);
752+
753+
// TODO: If responseWasBusy, should we increment, mRemainingAttempts and
754+
// mResolveAttemptsAllowed, since we were explicitly told to retry? Hard to
755+
// tell what consumers expect out of a capped retry count here.
756+
724757
// The cast on count() is needed because the type count() returns might not
725758
// actually be uint16_t; on some platforms it's int.
726759
ChipLogProgress(Discovery,

src/app/OperationalSessionSetup.h

+3
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate,
227227
//////////// SessionEstablishmentDelegate Implementation ///////////////
228228
void OnSessionEstablished(const SessionHandle & session) override;
229229
void OnSessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage) override;
230+
void OnResponderBusy(System::Clock::Milliseconds16 requestedDelay) override;
230231

231232
ScopedNodeId GetPeerId() const { return mPeerId; }
232233

@@ -319,6 +320,8 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate,
319320

320321
uint8_t mResolveAttemptsAllowed = 0;
321322

323+
System::Clock::Milliseconds16 mRequestedBusyDelay = System::Clock::kZero;
324+
322325
Callback::CallbackDeque mConnectionRetry;
323326
#endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
324327

src/protocols/secure_channel/CASEServer.cpp

+25-3
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,31 @@ CHIP_ERROR CASEServer::OnMessageReceived(Messaging::ExchangeContext * ec, const
9090
// Handshake wasn't stuck, send the busy status report and let the existing handshake continue.
9191

9292
// A successful CASE handshake can take several seconds and some may time out (30 seconds or more).
93-
// TODO: Come up with better estimate: https://github.com/project-chip/connectedhomeip/issues/28288
94-
// For now, setting minimum wait time to 5000 milliseconds.
95-
CHIP_ERROR err = SendBusyStatusReport(ec, System::Clock::Milliseconds16(5000));
93+
94+
System::Clock::Milliseconds16 delay = System::Clock::kZero;
95+
if (GetSession().GetState() == CASESession::State::kSentSigma2)
96+
{
97+
// The delay should be however long we think it will take for
98+
// that to time out.
99+
auto sigma2Timeout = CASESession::ComputeSigma2ResponseTimeout(GetSession().GetRemoteMRPConfig());
100+
if (sigma2Timeout < System::Clock::Milliseconds16::max())
101+
{
102+
delay = std::chrono::duration_cast<System::Clock::Milliseconds16>(sigma2Timeout);
103+
}
104+
else
105+
{
106+
// Avoid overflow issues, just wait for as long as we can to
107+
// get close to our expected Sigma2 timeout.
108+
delay = System::Clock::Milliseconds16::max();
109+
}
110+
}
111+
else
112+
{
113+
// For now, setting minimum wait time to 5000 milliseconds if we
114+
// have no other information.
115+
delay = System::Clock::Milliseconds16(5000);
116+
}
117+
CHIP_ERROR err = SendBusyStatusReport(ec, delay);
96118
if (err != CHIP_NO_ERROR)
97119
{
98120
ChipLogError(Inet, "Failed to send the busy status report, err:%" CHIP_ERROR_FORMAT, err.Format());

src/protocols/secure_channel/CASESession.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -1966,7 +1966,8 @@ void CASESession::OnSuccessStatusReport()
19661966
Finish();
19671967
}
19681968

1969-
CHIP_ERROR CASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode)
1969+
CHIP_ERROR CASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
1970+
Optional<uintptr_t> protocolData)
19701971
{
19711972
CHIP_ERROR err = CHIP_NO_ERROR;
19721973
switch (protocolCode)
@@ -1981,6 +1982,10 @@ CHIP_ERROR CASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralS
19811982

19821983
case kProtocolCodeBusy:
19831984
err = CHIP_ERROR_BUSY;
1985+
if (protocolData.HasValue())
1986+
{
1987+
mDelegate->OnResponderBusy(System::Clock::Milliseconds16(static_cast<uint16_t>(protocolData.Value())));
1988+
}
19841989
break;
19851990

19861991
default:

src/protocols/secure_channel/CASESession.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler,
272272
const ByteSpan & skInfo, const ByteSpan & nonce);
273273

274274
void OnSuccessStatusReport() override;
275-
CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode) override;
275+
CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
276+
Optional<uintptr_t> protocolData) override;
276277

277278
void AbortPendingEstablish(CHIP_ERROR err);
278279

src/protocols/secure_channel/PASESession.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,8 @@ void PASESession::OnSuccessStatusReport()
759759
Finish();
760760
}
761761

762-
CHIP_ERROR PASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode)
762+
CHIP_ERROR PASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
763+
Optional<uintptr_t> protocolData)
763764
{
764765
CHIP_ERROR err = CHIP_NO_ERROR;
765766
switch (protocolCode)

src/protocols/secure_channel/PASESession.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ class DLL_EXPORT PASESession : public Messaging::UnsolicitedMessageHandler,
203203
CHIP_ERROR HandleMsg3(System::PacketBufferHandle && msg);
204204

205205
void OnSuccessStatusReport() override;
206-
CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode) override;
206+
CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
207+
Optional<uintptr_t> protocolData) override;
207208

208209
void Finish();
209210

src/protocols/secure_channel/PairingSession.h

+9-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#pragma once
2727

2828
#include <lib/core/CHIPError.h>
29+
#include <lib/core/Optional.h>
2930
#include <lib/core/TLV.h>
3031
#include <messaging/ExchangeContext.h>
3132
#include <messaging/SessionParameters.h>
@@ -129,7 +130,11 @@ class DLL_EXPORT PairingSession : public SessionDelegate
129130

130131
void SetPeerSessionId(uint16_t id) { mPeerSessionId.SetValue(id); }
131132
virtual void OnSuccessStatusReport() {}
132-
virtual CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode)
133+
134+
// Handle a failure StatusReport message from the server. protocolData will
135+
// depend on exactly what the generalCode/protocolCode are.
136+
virtual CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
137+
Optional<uintptr_t> protocolData)
133138
{
134139
return CHIP_ERROR_INTERNAL;
135140
}
@@ -174,6 +179,7 @@ class DLL_EXPORT PairingSession : public SessionDelegate
174179
return CHIP_NO_ERROR;
175180
}
176181

182+
Optional<uintptr_t> protocolData;
177183
if (report.GetGeneralCode() == Protocols::SecureChannel::GeneralStatusCode::kBusy &&
178184
report.GetProtocolCode() == Protocols::SecureChannel::kProtocolCodeBusy)
179185
{
@@ -189,15 +195,15 @@ class DLL_EXPORT PairingSession : public SessionDelegate
189195
}
190196
else
191197
{
192-
// TODO: CASE: Notify minimum wait time to clients on receiving busy status report #28290
193198
ChipLogProgress(SecureChannel, "Received busy status report with minimum wait time: %u ms", minimumWaitTime);
199+
protocolData.Emplace(minimumWaitTime);
194200
}
195201
}
196202
}
197203

198204
// It's very important that we propagate the return value from
199205
// OnFailureStatusReport out to the caller. Make sure we return it directly.
200-
return OnFailureStatusReport(report.GetGeneralCode(), report.GetProtocolCode());
206+
return OnFailureStatusReport(report.GetGeneralCode(), report.GetProtocolCode(), protocolData);
201207
}
202208

203209
/**

src/protocols/secure_channel/SessionEstablishmentDelegate.h

+10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#pragma once
2727

28+
#include <system/SystemClock.h>
2829
#include <system/SystemPacketBuffer.h>
2930
#include <transport/Session.h>
3031
#include <transport/raw/MessageHeader.h>
@@ -80,6 +81,15 @@ class DLL_EXPORT SessionEstablishmentDelegate
8081
*/
8182
virtual void OnSessionEstablished(const SessionHandle & session) {}
8283

84+
/**
85+
* Called when the responder has responded with a "busy" status code and
86+
* provided a requested delay.
87+
*
88+
* This call will be followed by an OnSessionEstablishmentError with
89+
* CHIP_ERROR_BUSY as the error.
90+
*/
91+
virtual void OnResponderBusy(System::Clock::Milliseconds16 requestedDelay) {}
92+
8393
virtual ~SessionEstablishmentDelegate() {}
8494
};
8595

0 commit comments

Comments
 (0)