blob: a072fc256b9a0f79becf2e1ab3323b8b3739e065 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <inttypes.h>
#include <app/server/Server.h>
#include <app/InteractionModelEngine.h>
#include <app/server/EchoHandler.h>
#include <app/server/RendezvousServer.h>
#include <app/server/StorablePeerConnection.h>
#include <app/util/DataModelHandler.h>
#include <ble/BLEEndPoint.h>
#include <core/CHIPPersistentStorageDelegate.h>
#include <inet/IPAddress.h>
#include <inet/InetError.h>
#include <inet/InetLayer.h>
#include <mdns/ServiceNaming.h>
#include <messaging/ExchangeMgr.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/KeyValueStoreManager.h>
#include <protocols/secure_channel/CASEServer.h>
#include <protocols/secure_channel/MessageCounterManager.h>
#include <setup_payload/SetupPayload.h>
#include <support/CodeUtils.h>
#include <support/ErrorStr.h>
#include <support/logging/CHIPLogging.h>
#include <sys/param.h>
#include <system/SystemPacketBuffer.h>
#include <system/TLVPacketBufferBackingStore.h>
#include <transport/FabricTable.h>
#include <transport/SecureSessionMgr.h>
#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT || CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
#include <protocols/user_directed_commissioning/UserDirectedCommissioning.h>
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT || CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
#if CHIP_DEVICE_CONFIG_ENABLE_MDNS
#include <app/server/Mdns.h>
#endif
using namespace ::chip;
using namespace ::chip::Inet;
using namespace ::chip::Transport;
using namespace ::chip::DeviceLayer;
using namespace ::chip::Messaging;
namespace {
constexpr bool isRendezvousBypassed()
{
#if defined(CHIP_BYPASS_RENDEZVOUS) && CHIP_BYPASS_RENDEZVOUS
return true;
#elif defined(CONFIG_RENDEZVOUS_MODE)
return static_cast<RendezvousInformationFlag>(CONFIG_RENDEZVOUS_MODE) == RendezvousInformationFlag::kNone;
#else
return false;
#endif
}
constexpr bool useTestPairing()
{
// Use the test pairing whenever rendezvous is bypassed. Otherwise, there wouldn't be
// any way to communicate with the device using CHIP protocol.
// This is used to bypass BLE in the cirque test.
// Only in the cirque test this is enabled with --args='bypass_rendezvous=true'.
return isRendezvousBypassed();
}
class ServerStorageDelegate : public PersistentStorageDelegate
{
CHIP_ERROR SyncGetKeyValue(const char * key, void * buffer, uint16_t & size) override
{
ChipLogProgress(AppServer, "Retrieved value from server storage.");
return PersistedStorage::KeyValueStoreMgr().Get(key, buffer, size);
}
CHIP_ERROR SyncSetKeyValue(const char * key, const void * value, uint16_t size) override
{
ChipLogProgress(AppServer, "Stored value in server storage");
return PersistedStorage::KeyValueStoreMgr().Put(key, value, size);
}
CHIP_ERROR SyncDeleteKeyValue(const char * key) override
{
ChipLogProgress(AppServer, "Delete value in server storage");
return PersistedStorage::KeyValueStoreMgr().Delete(key);
}
};
ServerStorageDelegate gServerStorage;
SessionIDAllocator gSessionIDAllocator;
CHIP_ERROR PersistFabricToKVS(FabricInfo * fabric, FabricIndex nextAvailableId)
{
ReturnErrorCodeIf(fabric == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
ChipLogProgress(AppServer, "Persisting fabric ID %d, next available %d", fabric->GetFabricIndex(), nextAvailableId);
ReturnErrorOnFailure(GetGlobalFabricTable().Store(fabric->GetFabricIndex()));
ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kFabricTableCountKey, &nextAvailableId, sizeof(nextAvailableId)));
ChipLogProgress(AppServer, "Persisting fabric ID successfully");
return CHIP_NO_ERROR;
}
CHIP_ERROR RestoreAllFabricsFromKVS(FabricTable & fabrics, FabricIndex & nextAvailableId)
{
// It's not an error if the key doesn't exist. Just return right away.
VerifyOrReturnError(PersistedStorage::KeyValueStoreMgr().Get(kFabricTableCountKey, &nextAvailableId) == CHIP_NO_ERROR,
CHIP_NO_ERROR);
ChipLogProgress(AppServer, "Next available fabric ID is %d", nextAvailableId);
// TODO: The fabric ID space allocation should be re-evaluated. With the current approach, the space could be
// exhausted while IDs are still available (e.g. if the fabric IDs are allocated and freed over a period of time).
// Also, the current approach can make ID lookup slower as more IDs are allocated and freed.
for (FabricIndex id = 0; id < nextAvailableId; id++)
{
// Recreate the binding if one exists in persistent storage. Else skip to the next ID
if (fabrics.LoadFromStorage(id) == CHIP_NO_ERROR)
{
FabricInfo * fabric = fabrics.FindFabricWithIndex(id);
if (fabric != nullptr)
{
ChipLogProgress(AppServer, "Found fabric pairing for %d, node ID 0x" ChipLogFormatX64, fabric->GetFabricIndex(),
ChipLogValueX64(fabric->GetNodeId()));
}
}
}
ChipLogProgress(AppServer, "Restored all fabric pairings from KVS.");
return CHIP_NO_ERROR;
}
void EraseAllFabricsUpTo(FabricIndex nextAvailableId)
{
PersistedStorage::KeyValueStoreMgr().Delete(kFabricTableCountKey);
for (FabricIndex id = 0; id < nextAvailableId; id++)
{
GetGlobalFabricTable().Delete(id);
}
}
static CHIP_ERROR RestoreAllSessionsFromKVS(SecureSessionMgr & sessionMgr)
{
uint16_t nextSessionKeyId = 0;
// It's not an error if the key doesn't exist. Just return right away.
VerifyOrReturnError(PersistedStorage::KeyValueStoreMgr().Get(kStorablePeerConnectionCountKey, &nextSessionKeyId) ==
CHIP_NO_ERROR,
CHIP_NO_ERROR);
ChipLogProgress(AppServer, "Found %d stored connections", nextSessionKeyId);
PASESession * session = chip::Platform::New<PASESession>();
VerifyOrReturnError(session != nullptr, CHIP_ERROR_NO_MEMORY);
for (uint16_t keyId = 0; keyId < nextSessionKeyId; keyId++)
{
StorablePeerConnection connection;
if (CHIP_NO_ERROR == connection.FetchFromKVS(gServerStorage, keyId))
{
connection.GetPASESession(session);
ChipLogProgress(AppServer, "Fetched the session information: from 0x" ChipLogFormatX64,
ChipLogValueX64(session->PeerConnection().GetPeerNodeId()));
if (gSessionIDAllocator.Reserve(keyId) == CHIP_NO_ERROR)
{
sessionMgr.NewPairing(Optional<Transport::PeerAddress>::Value(session->PeerConnection().GetPeerAddress()),
session->PeerConnection().GetPeerNodeId(), session, SecureSession::SessionRole::kResponder,
connection.GetFabricIndex());
}
else
{
ChipLogProgress(AppServer, "Session Key ID %" PRIu16 " cannot be used. Skipping over this session", keyId);
}
session->Clear();
}
}
chip::Platform::Delete(session);
return CHIP_NO_ERROR;
}
void EraseAllSessionsUpTo(uint16_t nextSessionKeyId)
{
PersistedStorage::KeyValueStoreMgr().Delete(kStorablePeerConnectionCountKey);
for (uint16_t keyId = 0; keyId < nextSessionKeyId; keyId++)
{
gSessionIDAllocator.Free(keyId);
StorablePeerConnection::DeleteFromKVS(gServerStorage, keyId);
}
}
// TODO: The following class is setting the discriminator in Persistent Storage. This is
// is needed since BLE reads the discriminator using ConfigurationMgr APIs. The
// better solution will be to pass the discriminator to BLE without changing it
// in the persistent storage.
// https://github.com/project-chip/connectedhomeip/issues/4767
class DeviceDiscriminatorCache
{
public:
CHIP_ERROR UpdateDiscriminator(uint16_t discriminator)
{
if (!mOriginalDiscriminatorCached)
{
// Cache the original discriminator
ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSetupDiscriminator(mOriginalDiscriminator));
mOriginalDiscriminatorCached = true;
}
return DeviceLayer::ConfigurationMgr().StoreSetupDiscriminator(discriminator);
}
CHIP_ERROR RestoreDiscriminator()
{
if (mOriginalDiscriminatorCached)
{
// Restore the original discriminator
ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().StoreSetupDiscriminator(mOriginalDiscriminator));
mOriginalDiscriminatorCached = false;
}
return CHIP_NO_ERROR;
}
private:
bool mOriginalDiscriminatorCached = false;
uint16_t mOriginalDiscriminator = 0;
};
DeviceDiscriminatorCache gDeviceDiscriminatorCache;
FabricTable gFabrics;
FabricIndex gNextAvailableFabricIndex = 0;
bool gPairingWindowOpen = false;
class ServerRendezvousAdvertisementDelegate : public RendezvousAdvertisementDelegate
{
public:
CHIP_ERROR StartAdvertisement() const override
{
if (isBLE)
{
ReturnErrorOnFailure(chip::DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(true));
}
if (mDelegate != nullptr)
{
mDelegate->OnPairingWindowOpened();
}
gPairingWindowOpen = true;
return CHIP_NO_ERROR;
}
CHIP_ERROR StopAdvertisement() const override
{
gDeviceDiscriminatorCache.RestoreDiscriminator();
gPairingWindowOpen = false;
if (isBLE)
{
ReturnErrorOnFailure(chip::DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(false));
}
if (mDelegate != nullptr)
{
mDelegate->OnPairingWindowClosed();
}
FabricInfo * fabric = gFabrics.FindFabricWithIndex(mFabric);
if (fabric != nullptr)
{
ReturnErrorOnFailure(PersistFabricToKVS(fabric, gNextAvailableFabricIndex));
}
return CHIP_NO_ERROR;
}
void SetDelegate(AppDelegate * delegate) { mDelegate = delegate; }
void SetBLE(bool ble) { isBLE = ble; }
void SetFabricIndex(FabricIndex id) { mFabric = id; }
private:
AppDelegate * mDelegate = nullptr;
FabricIndex mFabric;
bool isBLE = true;
};
DemoTransportMgr gTransports;
SecureSessionMgr gSessions;
RendezvousServer gRendezvousServer;
CASEServer gCASEServer;
Messaging::ExchangeManager gExchangeMgr;
ServerRendezvousAdvertisementDelegate gAdvDelegate;
class ServerCallback : public ExchangeDelegate
{
public:
CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader,
const PayloadHeader & payloadHeader, System::PacketBufferHandle && buffer) override
{
CHIP_ERROR err = CHIP_NO_ERROR;
// as soon as a client connects, assume it is connected
VerifyOrExit(!buffer.IsNull(), ChipLogError(AppServer, "Received data but couldn't process it..."));
VerifyOrExit(packetHeader.GetSourceNodeId().HasValue(), ChipLogError(AppServer, "Unknown source for received message"));
VerifyOrExit(mSessionMgr != nullptr, ChipLogError(AppServer, "SecureSessionMgr is not initilized yet"));
VerifyOrExit(packetHeader.GetSourceNodeId().Value() != kUndefinedNodeId,
ChipLogError(AppServer, "Unknown source for received message"));
ChipLogProgress(AppServer, "Packet received from Node 0x" ChipLogFormatX64 ": %u bytes",
ChipLogValueX64(packetHeader.GetSourceNodeId().Value()), buffer->DataLength());
HandleDataModelMessage(exchangeContext, std::move(buffer));
exit:
return err;
}
void OnResponseTimeout(ExchangeContext * ec) override
{
ChipLogProgress(AppServer, "Failed to receive response");
if (mDelegate != nullptr)
{
mDelegate->OnReceiveError();
}
}
void SetDelegate(AppDelegate * delegate) { mDelegate = delegate; }
void SetSessionMgr(SecureSessionMgr * mgr) { mSessionMgr = mgr; }
private:
AppDelegate * mDelegate = nullptr;
SecureSessionMgr * mSessionMgr = nullptr;
};
secure_channel::MessageCounterManager gMessageCounterManager;
ServerCallback gCallbacks;
SecurePairingUsingTestSecret gTestPairing;
#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
chip::Protocols::UserDirectedCommissioning::UserDirectedCommissioningClient gUDCClient;
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
void HandlePairingWindowTimeout(System::Layer * aSystemLayer, void * aAppState, CHIP_ERROR aError)
{
ClosePairingWindow();
}
} // namespace
CHIP_ERROR OpenDefaultPairingWindow(ResetFabrics resetFabrics, uint16_t commissioningTimeoutSeconds,
chip::PairingWindowAdvertisement advertisementMode)
{
// TODO(cecille): If this is re-called when the window is already open, what should happen?
gDeviceDiscriminatorCache.RestoreDiscriminator();
uint32_t pinCode;
ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSetupPinCode(pinCode));
RendezvousParameters params;
params.SetSetupPINCode(pinCode);
#if CONFIG_NETWORK_LAYER_BLE
gAdvDelegate.SetBLE(advertisementMode == chip::PairingWindowAdvertisement::kBle);
params.SetAdvertisementDelegate(&gAdvDelegate);
if (advertisementMode == chip::PairingWindowAdvertisement::kBle)
{
params.SetBleLayer(DeviceLayer::ConnectivityMgr().GetBleLayer()).SetPeerAddress(Transport::PeerAddress::BLE());
}
#endif // CONFIG_NETWORK_LAYER_BLE
if (resetFabrics == ResetFabrics::kYes)
{
EraseAllFabricsUpTo(gNextAvailableFabricIndex);
EraseAllSessionsUpTo(gSessionIDAllocator.Peek());
// Only resetting gNextAvailableFabricIndex at reboot otherwise previously paired device with fabricID 0
// can continue sending messages to accessory as next available fabric will also be 0.
// This logic is not up to spec, will be implemented up to spec once AddOptCert is implemented.
gFabrics.Reset();
}
FabricIndex fabricIndex = gNextAvailableFabricIndex;
FabricInfo * fabricInfo = gFabrics.AssignFabricIndex(fabricIndex);
VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_NO_MEMORY);
gNextAvailableFabricIndex++;
ReturnErrorOnFailure(gRendezvousServer.WaitForPairing(
std::move(params), kSpake2p_Iteration_Count,
ByteSpan(reinterpret_cast<const uint8_t *>(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt)), 0, &gExchangeMgr,
&gTransports, &gSessions, fabricInfo));
if (commissioningTimeoutSeconds != kNoCommissioningTimeout)
{
ReturnErrorOnFailure(
DeviceLayer::SystemLayer.StartTimer(commissioningTimeoutSeconds * 1000, HandlePairingWindowTimeout, nullptr));
}
return CHIP_NO_ERROR;
}
CHIP_ERROR OpenPairingWindowUsingVerifier(uint16_t commissioningTimeoutSeconds, uint16_t discriminator, PASEVerifier & verifier,
uint32_t iterations, ByteSpan salt, uint16_t passcodeID)
{
RendezvousParameters params;
ReturnErrorOnFailure(gDeviceDiscriminatorCache.UpdateDiscriminator(discriminator));
gAdvDelegate.SetBLE(false);
params.SetPASEVerifier(verifier).SetAdvertisementDelegate(&gAdvDelegate);
FabricIndex fabricIndex = gNextAvailableFabricIndex;
FabricInfo * fabricInfo = gFabrics.AssignFabricIndex(fabricIndex);
VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_NO_MEMORY);
gNextAvailableFabricIndex++;
ReturnErrorOnFailure(gRendezvousServer.WaitForPairing(std::move(params), iterations, salt, passcodeID, &gExchangeMgr,
&gTransports, &gSessions, fabricInfo));
if (commissioningTimeoutSeconds != kNoCommissioningTimeout)
{
ReturnErrorOnFailure(
DeviceLayer::SystemLayer.StartTimer(commissioningTimeoutSeconds * 1000, HandlePairingWindowTimeout, nullptr));
}
return CHIP_NO_ERROR;
}
void ClosePairingWindow()
{
if (gPairingWindowOpen)
{
ChipLogProgress(AppServer, "Closing pairing window");
gRendezvousServer.Cleanup();
}
}
bool IsPairingWindowOpen()
{
return gPairingWindowOpen;
}
// The function will initialize datamodel handler and then start the server
// The server assumes the platform's networking has been setup already
void InitServer(AppDelegate * delegate)
{
CHIP_ERROR err = CHIP_NO_ERROR;
chip::Platform::MemoryInit();
InitDataModelHandler(&gExchangeMgr);
gCallbacks.SetDelegate(delegate);
#if CHIP_DEVICE_LAYER_TARGET_DARWIN
err = PersistedStorage::KeyValueStoreMgrImpl().Init("chip.store");
SuccessOrExit(err);
#elif CHIP_DEVICE_LAYER_TARGET_LINUX
PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH);
#endif
err = gRendezvousServer.Init(delegate, &gServerStorage, &gSessionIDAllocator);
SuccessOrExit(err);
gAdvDelegate.SetDelegate(delegate);
err = gFabrics.Init(&gServerStorage);
SuccessOrExit(err);
// Init transport before operations with secure session mgr.
err = gTransports.Init(UdpListenParameters(&DeviceLayer::InetLayer).SetAddressType(kIPAddressType_IPv6)
#if INET_CONFIG_ENABLE_IPV4
,
UdpListenParameters(&DeviceLayer::InetLayer).SetAddressType(kIPAddressType_IPv4)
#endif
#if CONFIG_NETWORK_LAYER_BLE
,
BleListenParameters(DeviceLayer::ConnectivityMgr().GetBleLayer())
#endif
);
SuccessOrExit(err);
err = gSessions.Init(&DeviceLayer::SystemLayer, &gTransports, &gFabrics, &gMessageCounterManager);
SuccessOrExit(err);
err = gExchangeMgr.Init(&gSessions);
SuccessOrExit(err);
err = gMessageCounterManager.Init(&gExchangeMgr);
SuccessOrExit(err);
err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeMgr, nullptr);
SuccessOrExit(err);
#if defined(CHIP_APP_USE_ECHO)
err = InitEchoHandler(&gExchangeMgr);
SuccessOrExit(err);
#endif
if (useTestPairing())
{
ChipLogProgress(AppServer, "Rendezvous and secure pairing skipped");
SuccessOrExit(err = AddTestPairing());
}
else if (DeviceLayer::ConnectivityMgr().IsWiFiStationProvisioned() || DeviceLayer::ConnectivityMgr().IsThreadProvisioned())
{
// If the network is already provisioned, proactively disable BLE advertisement.
ChipLogProgress(AppServer, "Network already provisioned. Disabling BLE advertisement");
chip::DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(false);
// Restore any previous fabric pairings
VerifyOrExit(CHIP_NO_ERROR == RestoreAllFabricsFromKVS(gFabrics, gNextAvailableFabricIndex),
ChipLogError(AppServer, "Could not restore fabric table"));
VerifyOrExit(CHIP_NO_ERROR == RestoreAllSessionsFromKVS(gSessions),
ChipLogError(AppServer, "Could not restore previous sessions"));
}
else
{
#if CHIP_DEVICE_CONFIG_ENABLE_PAIRING_AUTOSTART
SuccessOrExit(err = OpenDefaultPairingWindow(ResetFabrics::kYes));
#endif
}
// ESP32 and Mbed OS examples have a custom logic for enabling DNS-SD
#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !CHIP_DEVICE_LAYER_TARGET_ESP32 && !CHIP_DEVICE_LAYER_TARGET_MBED
app::Mdns::StartServer();
#endif
gCallbacks.SetSessionMgr(&gSessions);
// Register to receive unsolicited legacy ZCL messages from the exchange manager.
err = gExchangeMgr.RegisterUnsolicitedMessageHandlerForProtocol(Protocols::TempZCL::Id, &gCallbacks);
SuccessOrExit(err);
// Register to receive unsolicited Service Provisioning messages from the exchange manager.
err = gExchangeMgr.RegisterUnsolicitedMessageHandlerForProtocol(Protocols::ServiceProvisioning::Id, &gCallbacks);
SuccessOrExit(err);
err = gCASEServer.ListenForSessionEstablishment(&gExchangeMgr, &gTransports, &gSessions, &GetGlobalFabricTable(),
&gSessionIDAllocator);
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "ERROR setting up transport: %s", ErrorStr(err));
}
else
{
ChipLogProgress(AppServer, "Server Listening...");
}
}
#if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
// NOTE: UDC client is located in Server.cpp because it really only makes sense
// to send UDC from a Matter device. The UDC message payload needs to include the device's
// randomly generated service name.
CHIP_ERROR SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner)
{
ChipLogDetail(AppServer, "SendUserDirectedCommissioningRequest2");
CHIP_ERROR err;
char nameBuffer[chip::Mdns::kMaxInstanceNameSize + 1];
err = app::Mdns::GetCommissionableInstanceName(nameBuffer, sizeof(nameBuffer));
if (err != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "Failed to get mdns instance name error: %s", ErrorStr(err));
return err;
}
ChipLogDetail(AppServer, "instanceName=%s", nameBuffer);
chip::System::PacketBufferHandle payloadBuf = chip::MessagePacketBuffer::NewWithData(nameBuffer, strlen(nameBuffer));
if (payloadBuf.IsNull())
{
ChipLogError(AppServer, "Unable to allocate packet buffer\n");
return CHIP_ERROR_NO_MEMORY;
}
err = gUDCClient.SendUDCMessage(&gTransports, std::move(payloadBuf), commissioner);
if (err == CHIP_NO_ERROR)
{
ChipLogDetail(AppServer, "Send UDC request success");
}
else
{
ChipLogError(AppServer, "Send UDC request failed, err: %s\n", chip::ErrorStr(err));
}
return err;
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
CHIP_ERROR AddTestPairing()
{
CHIP_ERROR err = CHIP_NO_ERROR;
FabricInfo * fabricInfo = nullptr;
PASESession * testSession = nullptr;
PASESessionSerializable serializedTestSession;
for (const FabricInfo & fabric : gFabrics)
if (fabric.IsInitialized() && fabric.GetNodeId() == chip::kTestDeviceNodeId)
ExitNow();
fabricInfo = gFabrics.AssignFabricIndex(gNextAvailableFabricIndex);
VerifyOrExit(fabricInfo != nullptr, err = CHIP_ERROR_NO_MEMORY);
fabricInfo->SetNodeId(chip::kTestDeviceNodeId);
gTestPairing.ToSerializable(serializedTestSession);
testSession = chip::Platform::New<PASESession>();
testSession->FromSerializable(serializedTestSession);
SuccessOrExit(err = gSessions.NewPairing(Optional<PeerAddress>{ PeerAddress::Uninitialized() }, chip::kTestControllerNodeId,
testSession, SecureSession::SessionRole::kResponder, gNextAvailableFabricIndex));
++gNextAvailableFabricIndex;
exit:
if (testSession)
{
testSession->Clear();
chip::Platform::Delete(testSession);
}
if (err != CHIP_NO_ERROR && fabricInfo != nullptr)
gFabrics.ReleaseFabricIndex(gNextAvailableFabricIndex);
return err;
}
FabricTable & GetGlobalFabricTable()
{
return gFabrics;
}