From ae940e6df81788a4fbb590d89b5f70d6341e2fa7 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 30 Apr 2025 07:45:21 -0700 Subject: [PATCH] [border-agent] update "ConnectionMode" in state bitmap when stopped (#11462) This commit updates how the "ConnectionMode" field is set in the Border Agent State Bitmap, which is advertised as the value of the `sb` TXT key. In particular, when the Border Agent service is stopped and therefore not accepting any connections, the value of this field is now set to `kConnectionModeDisabled` to indicate this. This commit also updates and enhances `test_border_agent` to validate the State Bitmap entry in the TXT data, covering cases where the device role changes or ePSKc support is enabled/disabled. --- src/core/meshcop/border_agent.cpp | 2 +- tests/nexus/test_border_agent.cpp | 67 ++++++++++++++++++- .../test_publish_meshcop_service.py | 5 +- tests/scripts/thread-cert/node.py | 5 ++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index b771988e6..a4bf663ba 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -499,7 +499,7 @@ BorderAgent::TxtEncoder::StateBitmap BorderAgent::TxtEncoder::GetStateBitmap(voi { StateBitmap state; - state.mConnectionMode = kConnectionModePskc; + state.mConnectionMode = Get().IsRunning() ? kConnectionModePskc : kConnectionModeDisabled; state.mAvailability = kAvailabilityHigh; switch (Get().GetRole()) diff --git a/tests/nexus/test_border_agent.cpp b/tests/nexus/test_border_agent.cpp index ac90898d4..cb95616ec 100644 --- a/tests/nexus/test_border_agent.cpp +++ b/tests/nexus/test_border_agent.cpp @@ -817,10 +817,26 @@ template <> bool CheckObjectSameAsTxtEntryData(const TxtEntry &aTxtEnt void TestBorderAgentTxtDataCallback(void) { + // State bitmap masks and field values + static constexpr uint32_t kMaskConnectionMode = 7 << 0; + static constexpr uint32_t kConnectionModeDisabled = 0 << 0; + static constexpr uint32_t kConnectionModePskc = 1 << 0; + static constexpr uint32_t kMaskThreadIfStatus = 3 << 3; + static constexpr uint32_t kThreadIfStatusNotInitialized = 0 << 3; + static constexpr uint32_t kThreadIfStatusInitialized = 1 << 3; + static constexpr uint32_t kThreadIfStatusActive = 2 << 3; + static constexpr uint32_t kMaskThreadRole = 3 << 9; + static constexpr uint32_t kThreadRoleDisabledOrDetached = 0 << 9; + static constexpr uint32_t kThreadRoleChild = 1 << 9; + static constexpr uint32_t kThreadRoleRouter = 2 << 9; + static constexpr uint32_t kThreadRoleLeader = 3 << 9; + static constexpr uint32_t kFlagEpskcSupported = 1 << 11; + Core nexus; Node &node0 = nexus.CreateNode(); TxtDataTester txtDataTester(node0.Get()); TxtEntry txtEntry; + uint32_t stateBitmap; #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE BorderAgent::Id id; BorderAgent::Id newId; @@ -852,7 +868,14 @@ void TestBorderAgentTxtDataCallback(void) VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, NameData(kThreadVersionString, strlen(kThreadVersionString)))); VerifyOrQuit(txtDataTester.FindTxtEntry("xa", txtEntry)); VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get().GetExtAddress())); + VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry)); + VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t)); + stateBitmap = BigEndian::ReadUint32(txtEntry.mValue); + VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModeDisabled); + VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusNotInitialized); + VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleDisabledOrDetached); + VerifyOrQuit(stateBitmap & kFlagEpskcSupported); VerifyOrQuit(txtDataTester.FindTxtEntry("pt", txtEntry) == false); VerifyOrQuit(txtDataTester.FindTxtEntry("at", txtEntry) == false); @@ -883,13 +906,20 @@ void TestBorderAgentTxtDataCallback(void) VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, NameData(kThreadVersionString, strlen(kThreadVersionString)))); VerifyOrQuit(txtDataTester.FindTxtEntry("xa", txtEntry)); VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get().GetExtAddress())); - VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry)); VerifyOrQuit(txtDataTester.FindTxtEntry("pt", txtEntry)); VerifyOrQuit(CheckObjectSameAsTxtEntryData( txtEntry, BigEndian::HostSwap32(node0.Get().GetLeaderData().GetPartitionId()))); VerifyOrQuit(txtDataTester.FindTxtEntry("at", txtEntry)); VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get().GetTimestamp())); + VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry)); + VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t)); + stateBitmap = BigEndian::ReadUint32(txtEntry.mValue); + VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModePskc); + VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusActive); + VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleLeader); + VerifyOrQuit(stateBitmap & kFlagEpskcSupported); + // 2.2 Check the Border Agent state VerifyOrQuit(txtDataTester.mIsRunning == true); VerifyOrQuit(txtDataTester.mUdpPort != 0); @@ -920,6 +950,41 @@ void TestBorderAgentTxtDataCallback(void) VerifyOrQuit(!txtDataTester.mCallbackInvoked); #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Disable EphemeralKeyManager and validate that TXT data state bitmap indicates this"); + + txtDataTester.mCallbackInvoked = false; + node0.Get().SetEnabled(false); + VerifyOrQuit(node0.Get().GetState() == EphemeralKeyManager::kStateDisabled); + + nexus.AdvanceTime(1); + VerifyOrQuit(txtDataTester.mCallbackInvoked); + + VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry)); + VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t)); + stateBitmap = BigEndian::ReadUint32(txtEntry.mValue); + VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModePskc); + VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusActive); + VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleLeader); + VerifyOrQuit(!(stateBitmap & kFlagEpskcSupported)); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Disable the MLE operation and validate the TXT data state bitmap"); + + txtDataTester.mCallbackInvoked = false; + SuccessOrQuit(node0.Get().Disable()); + + nexus.AdvanceTime(1); + VerifyOrQuit(txtDataTester.mCallbackInvoked); + + VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry)); + VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t)); + stateBitmap = BigEndian::ReadUint32(txtEntry.mValue); + VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModeDisabled); + VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusNotInitialized); + VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleDisabledOrDetached); + VerifyOrQuit(!(stateBitmap & kFlagEpskcSupported)); } } // namespace Nexus diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 7b5c8277a..daf3cbf25 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -209,7 +209,10 @@ class PublishMeshCopService(thread_cert.TestCase): sb_data = service_data['txt']['sb'].encode('raw_unicode_escape') state_bitmap = int.from_bytes(sb_data, byteorder='big') logging.info(bin(state_bitmap)) - self.assertEqual((state_bitmap & 7), 1) # connection mode = PskC + if br.get_ba_state() == 'Active': + self.assertEqual((state_bitmap & 7), 1) # connection mode = PskC + else: + self.assertEqual((state_bitmap & 7), 0) # connection mode = Disabled sb_thread_interface_status = state_bitmap >> 3 & 3 sb_thread_role = state_bitmap >> 9 & 3 device_role = br.get_state() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index cce49e15c..868fa7418 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1913,6 +1913,11 @@ class NodeImpl: self.send_command(cmd) self._expect_done() + def get_ba_state(self): + states = [r'Disabled', r'Inactive', r'Active'] + self.send_command('ba state') + return self._expect_result(states) + def get_ephemeral_key_state(self): cmd = 'ba ephemeralkey' states = [r'Disabled', r'Stopped', r'Started', r'Connected', r'Accepted']