blob: 8c50884a6e45d7c54b048fa2b4064ee75f4f57be [file]
/*
* Copyright (c) 2026 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
#pragma once
#include <cstdint>
#include <cstring>
#include <inet/IPAddress.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/Optional.h>
#include <lib/support/Span.h>
namespace chip {
namespace Groupcast {
/**
* @brief Class to store GroupcastTesting event data
*
* This class stores all fields from the GroupcastTesting event,
* including IP addresses as byte arrays for proper storage.
*/
class Testing
{
public:
class Delegate
{
public:
virtual ~Delegate() = default;
virtual void FlushGroupcastTestingEvent() = 0;
};
/**
* @brief Test result enum matching GroupcastTestResultEnum from the Groupcast cluster.
* This cannot point to the raw code-generated type due to build deps, so it is copied for convenience.
*/
enum Result : uint8_t
{
kSuccess = 0x00,
kGeneralError = 0x01,
kMessageReplay = 0x02,
kFailedAuth = 0x03,
kNoAvailableKey = 0x04,
kSendFailure = 0x05,
};
static constexpr size_t kIPv6AddressLength = 16; // IPv6 address is 16 bytes
Testing() = default;
~Testing() = default;
void SetDelegate(Delegate * delegate) { mDelegate = delegate; }
void NotifyDelegate()
{
if (mDelegate != nullptr)
{
mDelegate->FlushGroupcastTestingEvent();
}
}
// Getters for optional fields
const chip::Optional<chip::GroupId> & GetGroupID() const { return mGroupID; }
const chip::Optional<chip::EndpointId> & GetEndpointID() const { return mEndpointID; }
const chip::Optional<chip::ClusterId> & GetClusterID() const { return mClusterID; }
const chip::Optional<uint32_t> & GetElementID() const { return mElementID; }
const chip::Optional<bool> & GetAccessAllowed() const { return mAccessAllowed; }
// Setters for optional fields
void SetGroupID(const chip::Optional<chip::GroupId> & groupID) { mGroupID = groupID; }
void SetGroupID(chip::GroupId groupID) { mGroupID = MakeOptional(groupID); }
void ClearGroupID() { mGroupID = chip::Optional<chip::GroupId>(); }
void SetEndpointID(const chip::Optional<chip::EndpointId> & endpointID) { mEndpointID = endpointID; }
void SetEndpointID(chip::EndpointId endpointID) { mEndpointID = MakeOptional(endpointID); }
void ClearEndpointID() { mEndpointID = chip::Optional<chip::EndpointId>(); }
void SetClusterID(const chip::Optional<chip::ClusterId> & clusterID) { mClusterID = clusterID; }
void SetClusterID(chip::ClusterId clusterID) { mClusterID = MakeOptional(clusterID); }
void ClearClusterID() { mClusterID = chip::Optional<chip::ClusterId>(); }
void SetElementID(const chip::Optional<uint32_t> & elementID) { mElementID = elementID; }
void SetElementID(uint32_t elementID) { mElementID = MakeOptional(elementID); }
void ClearElementID() { mElementID = chip::Optional<uint32_t>(); }
void SetAccessAllowed(const chip::Optional<bool> & accessAllowed) { mAccessAllowed = accessAllowed; }
void SetAccessAllowed(bool accessAllowed) { mAccessAllowed = MakeOptional(accessAllowed); }
void ClearAccessAllowed() { mAccessAllowed = chip::Optional<bool>(); }
// Getters for required fields
bool IsEnabled() const { return mEnabled; }
uint8_t GetTestResult() const { return mTestResult; }
Result GetTestResultEnum() const { return static_cast<Result>(mTestResult); }
chip::FabricIndex GetFabricIndex() const { return mFabricIndex; }
bool IsFabricUnderTest(chip::FabricIndex fabricIndex) { return mFabricIndex == fabricIndex; }
// Setters for required fields
void SetEnabled(bool enabled) { mEnabled = enabled; }
void SetTestResult(uint8_t result) { mTestResult = result; }
void SetTestResult(Result result) { mTestResult = static_cast<uint8_t>(result); }
void SetFabricIndex(chip::FabricIndex fabricIndex) { mFabricIndex = fabricIndex; }
// IP address getters
chip::ByteSpan GetSourceIpAddress() const { return chip::ByteSpan(mSourceIpAddress, mSourceIpAddressLength); }
chip::ByteSpan GetDestinationIpAddress() const { return chip::ByteSpan(mDestinationIpAddress, mDestinationIpAddressLength); }
bool HasSourceIpAddress() const { return mSourceIpAddressLength > 0; }
bool HasDestinationIpAddress() const { return mDestinationIpAddressLength > 0; }
/**
* @brief Set source IP address from Inet::IPAddress.
* @param ipAddress IPAddress containing the IPv6 address
*/
void SetSourceIpAddress(const Inet::IPAddress & ipAddress)
{
if (ipAddress.IsIPv6())
{
uint8_t * p = mSourceIpAddress;
ipAddress.WriteAddress(p);
mSourceIpAddressLength = kIPv6AddressLength;
}
}
/**
* @brief Set destination IP address from Inet::IPAddress.
* @param ipAddress IPAddress containing the IPv6 address
*/
void SetDestinationIpAddress(const Inet::IPAddress & ipAddress)
{
if (ipAddress.IsIPv6())
{
uint8_t * p = mDestinationIpAddress;
ipAddress.WriteAddress(p);
mDestinationIpAddressLength = kIPv6AddressLength;
}
}
/**
* @brief Clear source IP address
*/
void ClearSourceIpAddress() { mSourceIpAddressLength = 0; }
/**
* @brief Clear destination IP address
*/
void ClearDestinationIpAddress() { mDestinationIpAddressLength = 0; }
/**
* @brief Template method to convert stored data to event type
* @tparam EventType The event type structure (e.g., Groupcast::Events::GroupcastTesting::Type)
* @param event Output event structure to populate
*/
template <typename EventType>
void ToEventType(EventType & event) const
{
event.groupcastTestResult = static_cast<decltype(event.groupcastTestResult)>(mTestResult);
event.fabricIndex = mFabricIndex;
event.groupID = mGroupID;
event.endpointID = mEndpointID;
event.clusterID = mClusterID;
event.elementID = mElementID;
event.accessAllowed = mAccessAllowed;
if (mSourceIpAddressLength > 0)
{
event.sourceIpAddress = MakeOptional(chip::ByteSpan(mSourceIpAddress, mSourceIpAddressLength));
}
else
{
event.sourceIpAddress = chip::Optional<chip::ByteSpan>();
}
if (mDestinationIpAddressLength > 0)
{
event.destinationIpAddress = MakeOptional(chip::ByteSpan(mDestinationIpAddress, mDestinationIpAddressLength));
}
else
{
event.destinationIpAddress = chip::Optional<chip::ByteSpan>();
}
}
/**
* @brief Clear all stored data
*/
void Clear()
{
mSourceIpAddressLength = 0;
mDestinationIpAddressLength = 0;
mGroupID = chip::Optional<chip::GroupId>();
mEndpointID = chip::Optional<chip::EndpointId>();
mClusterID = chip::Optional<chip::ClusterId>();
mElementID = chip::Optional<uint32_t>();
mAccessAllowed = chip::Optional<bool>();
mTestResult = 0;
}
private:
Delegate * mDelegate = nullptr;
bool mEnabled = false;
// IP addresses stored as IPv6 address bytes (16 bytes) in network byte order (RFC 4291)
uint8_t mSourceIpAddress[kIPv6AddressLength];
uint8_t mDestinationIpAddress[kIPv6AddressLength];
size_t mSourceIpAddressLength = 0;
size_t mDestinationIpAddressLength = 0;
// Optional fields
chip::Optional<chip::GroupId> mGroupID;
chip::Optional<chip::EndpointId> mEndpointID;
chip::Optional<chip::ClusterId> mClusterID;
chip::Optional<uint32_t> mElementID;
chip::Optional<bool> mAccessAllowed;
// Required fields
uint8_t mTestResult = 0;
chip::FabricIndex mFabricIndex = kUndefinedFabricIndex;
};
/**
* @brief Get the singleton Testing instance.
*
* Callers must externally synchronize usage of this function.
*
* @return Reference to the global GroupcastTesting instance
*/
Testing & GetTesting();
} // namespace Groupcast
} // namespace chip