Skip to content

Commit 58264e2

Browse files
committed
Fix JointFabricDatastore ownership, lifetime, and sync edge cases
1 parent fdb31db commit 58264e2

File tree

15 files changed

+1426
-685
lines changed

15 files changed

+1426
-685
lines changed

docs/guides/joint_fabric_guide.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ $ ./chip-lighting-app --capabilities 0x4 --passcode 11022044 --KVS light_a_kvs
136136
- Commission lighting-app
137137

138138
```
139-
>>> pairing onnetwork 2 11022044
139+
>>> pairing onnetwork 2 11022044 --regular 1
140140
```
141141

142142
Check that a Fabric having `AdminVendorID` set to 0xFFF1 has been installed:
@@ -235,7 +235,7 @@ $ ./chip-lighting-app --capabilities 0x4 --passcode 11022066 --KVS light_b_kvs
235235
- Commission lighting-app
236236

237237
```
238-
>>> pairing onnetwork 22 11022066
238+
>>> pairing onnetwork 22 11022066 --regular 1
239239
```
240240

241241
Check that a Fabric having `AdminVendorID` set to 0xFFF2 has been installed:

examples/jf-admin-app/linux/JFADatastoreSync.cpp

Lines changed: 764 additions & 170 deletions
Large diffs are not rendered by default.

examples/jf-admin-app/linux/JFAManager.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
#include <app/ConcreteAttributePath.h>
2525
#include <app/server/Server.h>
2626

27+
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
28+
#include <CommissionerMain.h>
29+
#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
30+
2731
#include <controller/CHIPCluster.h>
2832
#include <lib/support/logging/CHIPLogging.h>
2933

@@ -197,6 +201,23 @@ void JFAManager::HandleCommissioningCompleteEvent()
197201

198202
LogErrorOnFailure(Server::GetInstance().GetJointFabricDatastore().AddAdmin(newAdmin));
199203

204+
if (!mCommissionerInitialized)
205+
{
206+
CHIP_ERROR err = InitCommissioner(LinuxDeviceOptions::GetInstance().securedCommissionerPort,
207+
LinuxDeviceOptions::GetInstance().unsecuredCommissionerPort, fb.GetFabricId(),
208+
fabricIndex);
209+
if (err == CHIP_NO_ERROR)
210+
{
211+
mCommissionerInitialized = true;
212+
ChipLogProgress(JointFabric, "Commissioner mode initialized on commissioned fabric index %u",
213+
static_cast<unsigned>(fabricIndex));
214+
}
215+
else
216+
{
217+
ChipLogError(JointFabric, "Failed to initialize commissioner mode: %" CHIP_ERROR_FORMAT, err.Format());
218+
}
219+
}
220+
200221
ChipLogProgress(JointFabric, "Joint Fabric Administrator commissioned on fabric index %d", fabricIndex);
201222
}
202223
}

examples/jf-admin-app/linux/include/CHIPProjectAppConfig.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@
4747
#define CHIP_DEVICE_CONFIG_DEVICE_NAME "JF Admin"
4848

4949
#define CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE 1
50+
51+
#define CHIP_LINUX_APP_START_COMMISSIONER_AT_BOOT 0

examples/jf-admin-app/linux/include/JFADatastoreSync.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <app/server/JointFabricDatastore.h>
2222
#include <app/server/Server.h>
2323
#include <lib/core/CHIPError.h>
24+
#include <map>
25+
#include <memory>
2426

2527
namespace chip {
2628

@@ -73,10 +75,11 @@ class JFADatastoreSync : public app::JointFabricDatastore::Delegate
7375
void(CHIP_ERROR,
7476
const std::vector<app::Clusters::JointFabricDatastore::Structs::DatastoreEndpointBindingEntryStruct::Type> &)>
7577
onSuccess) override;
76-
CHIP_ERROR FetchGroupKeySetList(
77-
NodeId nodeId,
78-
std::function<void(CHIP_ERROR,
79-
const std::vector<app::Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type> &)>
78+
CHIP_ERROR FetchGroupKeySetList(NodeId nodeId,
79+
std::function<void(CHIP_ERROR, const std::vector<uint16_t> &)> onSuccess) override;
80+
CHIP_ERROR FetchGroupKeySet(
81+
NodeId nodeId, uint16_t groupKeySetID,
82+
std::function<void(CHIP_ERROR, const app::Clusters::JointFabricDatastore::Structs::DatastoreGroupKeySetStruct::Type &)>
8083
onSuccess) override;
8184
CHIP_ERROR FetchACLList(
8285
NodeId nodeId,
@@ -90,6 +93,23 @@ class JFADatastoreSync : public app::JointFabricDatastore::Delegate
9093
static JFADatastoreSync sJFDS;
9194

9295
Server * mServer = nullptr;
96+
97+
// Map to hold in-flight device pairing commands, keyed by unique command token.
98+
// This allows multiple concurrent commands for the same node to coexist safely.
99+
std::map<uint64_t, std::shared_ptr<void>> mInFlightCommands;
100+
uint64_t mNextInFlightCommandToken = 1;
101+
102+
uint64_t AllocateInFlightCommandToken() { return mNextInFlightCommandToken++; }
103+
104+
// Helper to store command and ensure it stays alive until callbacks complete.
105+
template <typename T>
106+
void StoreInFlightCommand(uint64_t token, std::shared_ptr<T> command)
107+
{
108+
mInFlightCommands[token] = std::static_pointer_cast<void>(command);
109+
}
110+
111+
// Helper to remove command after callbacks complete.
112+
void RemoveInFlightCommand(uint64_t token) { mInFlightCommands.erase(token); }
93113
};
94114

95115
inline JFADatastoreSync & JFADSync(void)

examples/jf-admin-app/linux/include/JFAManager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ class JFAManager : public app::JointFabricAdministrator::Delegate
8181
EndpointId peerAdminJFAdminClusterEndpointId = kInvalidEndpointId;
8282
Crypto::P256PublicKey peerAdminICACPubKey;
8383
uint8_t mICACBuffer[Credentials::kMaxDERCertLength];
84-
size_t mICACBufferLen = 0;
84+
size_t mICACBufferLen = 0;
85+
bool mCommissionerInitialized = false;
8586

8687
void ConnectToNode(ScopedNodeId scopedNodeId, OnConnectedAction onConnectedAction);
8788
CHIP_ERROR SendCommissioningComplete();

examples/jf-control-app/commands/pairing/PairingCommand.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ CHIP_ERROR PairingCommand::RunCommand()
122122
ChipLogValueX64(anchorNodeId));
123123
return CHIP_ERROR_BAD_REQUEST;
124124
}
125-
else
125+
else if (!mRegularDevice.ValueOr(false))
126126
{
127127
// Skip commissioning complete for JCM and other device commissioning methods but not Anchor Administrator commissioning.
128128
mSkipCommissioningComplete = MakeOptional(true);
@@ -682,7 +682,11 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
682682
{
683683
if (err == CHIP_NO_ERROR)
684684
{
685-
if (!mSkipCommissioningComplete.ValueOr(false))
685+
if (mRegularDevice.ValueOr(false))
686+
{
687+
ChipLogProgress(JointFabric, "Device (nodeId=%ld) commissioned with success", nodeId);
688+
}
689+
else if (!mSkipCommissioningComplete.ValueOr(false))
686690
{
687691
ChipLogProgress(JointFabric, "Anchor Administrator (nodeId=%ld) commissioned with success", nodeId);
688692
TEMPORARY_RETURN_IGNORED SetAnchorNodeId(nodeId);

examples/jf-control-app/commands/pairing/PairingCommand.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class PairingCommand : public CHIPCommand,
103103
"If set, a LIT ICD that is commissioned will be requested to stay active for this many milliseconds");
104104
AddArgument("anchor", 0, 1, &mAnchor, "If set to true then a NOC with Anchor and Administrator CAT is issued");
105105
AddArgument("jcm", 0, 1, &mJCM, "Set it to true in order to commission a Joint Fabric Administrator");
106+
AddArgument("regular", 0, 1, &mRegularDevice, "Set it to true to commission a regular device");
106107
switch (networkType)
107108
{
108109
case PairingNetworkType::None:
@@ -296,6 +297,7 @@ class PairingCommand : public CHIPCommand,
296297
chip::Optional<bool> mSkipCommissioningComplete;
297298
chip::Optional<bool> mAnchor;
298299
chip::Optional<bool> mJCM;
300+
chip::Optional<bool> mRegularDevice;
299301
chip::Optional<bool> mBypassAttestationVerifier;
300302
chip::Optional<std::vector<uint32_t>> mCASEAuthTags;
301303
chip::Optional<char *> mCountryCode;

examples/platform/linux/AppMain.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
#include <ControllerShellCommands.h>
6060
#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
6161

62+
#ifndef CHIP_LINUX_APP_START_COMMISSIONER_AT_BOOT
63+
#define CHIP_LINUX_APP_START_COMMISSIONER_AT_BOOT 1
64+
#endif
65+
6266
#if defined(ENABLE_CHIP_SHELL)
6367
#include <CommissioneeShellCommands.h>
6468
#include <lib/shell/Engine.h> // nogncheck
@@ -1049,11 +1053,13 @@ void ChipLinuxAppMainLoop(chip::ServerInitParams & initParams, AppMainLoopImplem
10491053
PrintOnboardingCodes(LinuxDeviceOptions::GetInstance().payload);
10501054

10511055
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
1056+
#if CHIP_LINUX_APP_START_COMMISSIONER_AT_BOOT
10521057
ChipLogProgress(AppServer, "Starting commissioner");
10531058
VerifyOrReturn(InitCommissioner(LinuxDeviceOptions::GetInstance().securedCommissionerPort,
10541059
LinuxDeviceOptions::GetInstance().unsecuredCommissionerPort,
10551060
LinuxDeviceOptions::GetInstance().commissionerFabricId) == CHIP_NO_ERROR);
10561061
ChipLogProgress(AppServer, "Started commissioner");
1062+
#endif // CHIP_LINUX_APP_START_COMMISSIONER_AT_BOOT
10571063
#if defined(ENABLE_CHIP_SHELL)
10581064
Shell::RegisterControllerCommands();
10591065
#endif // defined(ENABLE_CHIP_SHELL)

examples/platform/linux/CommissionerMain.cpp

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ NodeId gLocalId = kMaxOperationalNodeId;
123123
Credentials::GroupDataProviderImpl gGroupDataProvider;
124124
Crypto::RawKeySessionKeystore gSessionKeystore;
125125

126-
CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, FabricId fabricId)
126+
CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, FabricId fabricId,
127+
FabricIndex commissionerFabricIndex)
127128
{
128129
Controller::FactoryInitParams factoryParams;
129130
Controller::SetupParams params;
@@ -149,9 +150,17 @@ CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, F
149150
params.controllerVendorId = static_cast<VendorId>(vendorId);
150151

151152
ReturnErrorOnFailure(gOpCredsIssuer.Initialize(gServerStorage));
152-
if (fabricId != kUndefinedFabricId)
153+
154+
if (commissionerFabricIndex == kUndefinedFabricIndex && fabricId != kUndefinedFabricId)
153155
{
154-
gOpCredsIssuer.SetFabricIdForNextNOCRequest(fabricId);
156+
for (const auto & fb : Server::GetInstance().GetFabricTable())
157+
{
158+
if (fb.GetFabricId() == fabricId)
159+
{
160+
commissionerFabricIndex = fb.GetFabricIndex();
161+
break;
162+
}
163+
}
155164
}
156165

157166
// No need to explicitly set the UDC port since we will use default
@@ -179,17 +188,34 @@ CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, F
179188
Platform::ScopedMemoryBuffer<uint8_t> rcac;
180189
VerifyOrReturnError(rcac.Alloc(Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
181190
MutableByteSpan rcacSpan(rcac.Get(), Controller::kMaxCHIPDERCertLength);
182-
183191
Crypto::P256Keypair ephemeralKey;
184-
ReturnErrorOnFailure(ephemeralKey.Initialize(Crypto::ECPKeyTarget::ECDSA));
185192

186-
ReturnErrorOnFailure(gOpCredsIssuer.GenerateNOCChainAfterValidation(gLocalId, /* fabricId = */ 1, chip::kUndefinedCATs,
187-
ephemeralKey.Pubkey(), rcacSpan, icacSpan, nocSpan));
193+
if (commissionerFabricIndex != kUndefinedFabricIndex)
194+
{
195+
params.fabricIndex.SetValue(commissionerFabricIndex);
196+
params.removeFromFabricTableOnShutdown = false;
197+
198+
ChipLogProgress(Support, " ----- Commissioner reusing existing fabric index %u",
199+
static_cast<unsigned>(commissionerFabricIndex));
200+
}
201+
else
202+
{
203+
const FabricId commissionerFabricId = (fabricId == kUndefinedFabricId) ? static_cast<FabricId>(1) : fabricId;
204+
if (commissionerFabricId != kUndefinedFabricId)
205+
{
206+
gOpCredsIssuer.SetFabricIdForNextNOCRequest(commissionerFabricId);
207+
}
208+
209+
ReturnErrorOnFailure(ephemeralKey.Initialize(Crypto::ECPKeyTarget::ECDSA));
210+
211+
ReturnErrorOnFailure(gOpCredsIssuer.GenerateNOCChainAfterValidation(gLocalId, commissionerFabricId, chip::kUndefinedCATs,
212+
ephemeralKey.Pubkey(), rcacSpan, icacSpan, nocSpan));
188213

189-
params.operationalKeypair = &ephemeralKey;
190-
params.controllerRCAC = rcacSpan;
191-
params.controllerICAC = icacSpan;
192-
params.controllerNOC = nocSpan;
214+
params.operationalKeypair = &ephemeralKey;
215+
params.controllerRCAC = rcacSpan;
216+
params.controllerICAC = icacSpan;
217+
params.controllerNOC = nocSpan;
218+
}
193219

194220
params.defaultCommissioner = &gAutoCommissioner;
195221
params.enableServerInteractions = true;
@@ -227,7 +253,8 @@ CHIP_ERROR InitCommissioner(uint16_t commissionerPort, uint16_t udcListenPort, F
227253

228254
ChipLogProgress(Support,
229255
"InitCommissioner nodeId=0x" ChipLogFormatX64 " fabric.fabricId=0x" ChipLogFormatX64 " fabricIndex=0x%x",
230-
ChipLogValueX64(gCommissioner.GetNodeId()), ChipLogValueX64(fabricId), static_cast<unsigned>(fabricIndex));
256+
ChipLogValueX64(gCommissioner.GetNodeId()), ChipLogValueX64(gCommissioner.GetFabricId()),
257+
static_cast<unsigned>(fabricIndex));
231258

232259
return CHIP_NO_ERROR;
233260
}

0 commit comments

Comments
 (0)