Add a FabricTable API to get the next available fabric index. (#28391)
* Add a FabricTable API to get the next available fabric index.
This can be used during controller startup to map the fabric index (which is
allocated to the controller partway through startup and then used immediately
for storage keys) to the controller instance being started.
* Address review comments.
diff --git a/src/credentials/FabricTable.cpp b/src/credentials/FabricTable.cpp
index 6d9f478..eeb5932 100644
--- a/src/credentials/FabricTable.cpp
+++ b/src/credentials/FabricTable.cpp
@@ -2080,4 +2080,19 @@
return CHIP_NO_ERROR;
}
+CHIP_ERROR FabricTable::PeekFabricIndexForNextAddition(FabricIndex & outIndex)
+{
+ EnsureNextAvailableFabricIndexUpdated();
+ if (!mNextAvailableFabricIndex.HasValue())
+ {
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ FabricIndex index = mNextAvailableFabricIndex.Value();
+ VerifyOrReturnError(IsValidFabricIndex(index), CHIP_ERROR_INVALID_FABRIC_INDEX);
+
+ outIndex = index;
+ return CHIP_NO_ERROR;
+}
+
} // namespace chip
diff --git a/src/credentials/FabricTable.h b/src/credentials/FabricTable.h
index 82a8252..60cd54d 100644
--- a/src/credentials/FabricTable.h
+++ b/src/credentials/FabricTable.h
@@ -971,6 +971,14 @@
#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
}
+ /**
+ * Get the fabric index that will be used for the next fabric that will be
+ * added. Returns error if no more fabrics can be added, otherwise writes
+ * the fabric index that will be used for the next addition into the
+ * outparam.
+ */
+ CHIP_ERROR PeekFabricIndexForNextAddition(FabricIndex & outIndex);
+
private:
enum class StateFlags : uint16_t
{
diff --git a/src/credentials/tests/TestFabricTable.cpp b/src/credentials/tests/TestFabricTable.cpp
index c6e7998..83866b3 100644
--- a/src/credentials/tests/TestFabricTable.cpp
+++ b/src/credentials/tests/TestFabricTable.cpp
@@ -523,6 +523,12 @@
NL_TEST_ASSERT_EQUALS(inSuite, fabricTable.FabricCount(), 0);
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 1);
+ }
+
size_t numFabricsIterated = 0;
size_t numStorageKeysAtStart = storage.GetNumKeys();
@@ -588,6 +594,13 @@
// No storage yet
NL_TEST_ASSERT(inSuite, storage.GetNumKeys() == numStorageKeysAtStart);
+ // Next fabric index has not been updated yet.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 1);
+ }
+
// Validate iterator sees pending
{
numFabricsIterated = 0;
@@ -612,6 +625,13 @@
NL_TEST_ASSERT(inSuite, storage.GetNumKeys() == (numStorageKeysAtStart + 4)); // 2 opcerts + fabric metadata + index
+ // Next fabric index has been updated.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 2);
+ }
+
// Validate contents
const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
NL_TEST_ASSERT(inSuite, fabricInfo != nullptr);
@@ -679,6 +699,14 @@
ByteSpan noc = fabric44CertAuthority.GetNoc();
NL_TEST_ASSERT_EQUALS(inSuite, fabricTable.FabricCount(), 1);
+
+ // Next fabric index should still be the same as before.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 2);
+ }
+
NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.AddNewPendingTrustedRootCert(rcac));
FabricIndex newFabricIndex = kUndefinedFabricIndex;
@@ -689,6 +717,12 @@
NL_TEST_ASSERT(inSuite, newFabricIndex == 2);
// No storage yet
NL_TEST_ASSERT(inSuite, storage.GetNumKeys() == numStorageAfterFirstAdd);
+ // Next fabric index has not been updated yet.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 2);
+ }
// Commit, now storage should have keys
NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.CommitPendingFabricData());
@@ -697,6 +731,13 @@
NL_TEST_ASSERT_EQUALS(inSuite, storage.GetNumKeys(),
(numStorageAfterFirstAdd + 5)); // 3 opcerts + fabric metadata + 1 operational key
+ // Next fabric index has been updated.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 3);
+ }
+
// Validate contents
const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
NL_TEST_ASSERT(inSuite, fabricInfo != nullptr);
@@ -879,6 +920,13 @@
NL_TEST_ASSERT(inSuite, saw1 == true);
NL_TEST_ASSERT(inSuite, saw2 == true);
}
+
+ // Next fabric index has stayed the same.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 3);
+ }
}
size_t numStorageAfterUpdate = storage.GetNumKeys();
@@ -904,6 +952,13 @@
NL_TEST_ASSERT(inSuite, fabricInfo->GetFabricLabel().data_equal(CharSpan{ "roboto", strlen("roboto") }));
}
}
+
+ // Next fabric index has stayed the same.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 3);
+ }
}
// Sequence 5: Remove FabricIndex 1 (FabricId 11, NodeId 55), make sure FabricIndex 2 (FabricId 44, NodeId 1000) still exists
@@ -917,6 +972,13 @@
NL_TEST_ASSERT_EQUALS(inSuite, storage.GetNumKeys(), (numStorageAfterUpdate - 3)); // Deleted NOC, RCAC, Metadata
}
+ // Next fabric index has stayed the same.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 3);
+ }
+
// Validate contents of Fabric Index 2 is still OK
const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
NL_TEST_ASSERT(inSuite, fabricInfo != nullptr);
@@ -1374,6 +1436,13 @@
NL_TEST_ASSERT(inSuite, saw1 == true);
NL_TEST_ASSERT(inSuite, saw2 == true);
}
+
+ // Next fabric index should now be 3, since we added 1 and 2 above.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 3);
+ }
}
// Global: Last known good time + fabric index = 2
@@ -1501,6 +1570,13 @@
CHIP_ERROR_INVALID_SIGNATURE);
}
}
+
+ // Validate that next fabric index is still 3;
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 3);
+ }
}
}
@@ -1544,6 +1620,12 @@
NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.AddNewPendingTrustedRootCert(rcac));
FabricIndex newFabricIndex = kUndefinedFabricIndex;
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 1);
+ }
+
NL_TEST_ASSERT_EQUALS(inSuite, fabricTable.FabricCount(), 0);
NL_TEST_ASSERT_SUCCESS(inSuite,
fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex));
@@ -1596,6 +1678,13 @@
NL_TEST_ASSERT(inSuite, numFabricsIterated == 0);
NL_TEST_ASSERT(inSuite, saw1 == false);
}
+
+ // Validate next fabric index has not changed.
+ {
+ FabricIndex nextFabricIndex = kUndefinedFabricIndex;
+ NL_TEST_ASSERT(inSuite, fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex) == CHIP_NO_ERROR);
+ NL_TEST_ASSERT_EQUALS(inSuite, nextFabricIndex, 1);
+ }
}
size_t numStorageAfterRevert = storage.GetNumKeys();