blob: 15dbf90670ac342dab010dfd50aeac1c7f5c2689 [file] [log] [blame]
/*
*
* Copyright (c) 2024 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/InetInterface.h>
#include <lib/core/CHIPError.h>
#include <lib/core/Optional.h>
#include <lib/core/PeerId.h>
#include <lib/dnssd/Constants.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ReliableMessageProtocolConfig.h>
namespace chip {
namespace Dnssd {
enum class DiscoveryFilterType : uint8_t
{
kNone,
kShortDiscriminator,
kLongDiscriminator,
kVendorId,
kDeviceType,
kCommissioningMode,
kInstanceName,
kCommissioner,
kCompressedFabricId,
};
struct DiscoveryFilter
{
DiscoveryFilterType type;
uint64_t code = 0;
const char * instanceName = nullptr;
DiscoveryFilter() : type(DiscoveryFilterType::kNone), code(0) {}
DiscoveryFilter(const DiscoveryFilterType newType) : type(newType) {}
DiscoveryFilter(const DiscoveryFilterType newType, uint64_t newCode) : type(newType), code(newCode) {}
DiscoveryFilter(const DiscoveryFilterType newType, const char * newInstanceName) : type(newType), instanceName(newInstanceName)
{}
bool operator==(const DiscoveryFilter & other) const
{
if (type != other.type)
{
return false;
}
if (type == DiscoveryFilterType::kInstanceName)
{
return (instanceName != nullptr) && (other.instanceName != nullptr) && (strcmp(instanceName, other.instanceName) == 0);
}
return code == other.code;
}
};
enum class DiscoveryType
{
kUnknown,
kOperational,
kCommissionableNode,
kCommissionerNode
};
struct CommonResolutionData
{
static constexpr unsigned kMaxIPAddresses = CHIP_DEVICE_CONFIG_MAX_DISCOVERED_IP_ADDRESSES;
Inet::InterfaceId interfaceId;
size_t numIPs = 0; // number of valid IP addresses
Inet::IPAddress ipAddress[kMaxIPAddresses];
uint16_t port = 0;
char hostName[kHostNameMaxLength + 1] = {};
bool supportsTcp = false;
Optional<bool> isICDOperatingAsLIT;
Optional<System::Clock::Milliseconds32> mrpRetryIntervalIdle;
Optional<System::Clock::Milliseconds32> mrpRetryIntervalActive;
Optional<System::Clock::Milliseconds16> mrpRetryActiveThreshold;
CommonResolutionData() { Reset(); }
bool IsValid() const { return !IsHost("") && (numIPs > 0) && (ipAddress[0] != chip::Inet::IPAddress::Any); }
ReliableMessageProtocolConfig GetRemoteMRPConfig() const
{
const ReliableMessageProtocolConfig defaultConfig = GetDefaultMRPConfig();
return ReliableMessageProtocolConfig(GetMrpRetryIntervalIdle().ValueOr(defaultConfig.mIdleRetransTimeout),
GetMrpRetryIntervalActive().ValueOr(defaultConfig.mActiveRetransTimeout),
GetMrpRetryActiveThreshold().ValueOr(defaultConfig.mActiveThresholdTime));
}
Optional<System::Clock::Milliseconds32> GetMrpRetryIntervalIdle() const { return mrpRetryIntervalIdle; }
Optional<System::Clock::Milliseconds32> GetMrpRetryIntervalActive() const { return mrpRetryIntervalActive; }
Optional<System::Clock::Milliseconds16> GetMrpRetryActiveThreshold() const { return mrpRetryActiveThreshold; }
bool IsDeviceTreatedAsSleepy(const ReliableMessageProtocolConfig * defaultMRPConfig) const
{
// If either session interval (Idle - SII, Active - SAI) has a value and that value is greater
// than the value passed to this function, then the peer device will be treated as if it is
// a Sleepy End Device (SED)
return (mrpRetryIntervalIdle.HasValue() && (mrpRetryIntervalIdle.Value() > defaultMRPConfig->mIdleRetransTimeout)) ||
(mrpRetryIntervalActive.HasValue() && (mrpRetryIntervalActive.Value() > defaultMRPConfig->mActiveRetransTimeout));
}
bool IsHost(const char * host) const { return strcmp(host, hostName) == 0; }
void Reset()
{
memset(hostName, 0, sizeof(hostName));
mrpRetryIntervalIdle = NullOptional;
mrpRetryIntervalActive = NullOptional;
mrpRetryActiveThreshold = NullOptional;
isICDOperatingAsLIT = NullOptional;
numIPs = 0;
port = 0;
supportsTcp = false;
interfaceId = Inet::InterfaceId::Null();
for (auto & addr : ipAddress)
{
addr = chip::Inet::IPAddress::Any;
}
}
void LogDetail() const
{
if (!IsHost(""))
{
ChipLogDetail(Discovery, "\tHostname: %s", hostName);
}
#if CHIP_DETAIL_LOGGING
for (unsigned j = 0; j < numIPs; j++)
{
char buf[Inet::IPAddress::kMaxStringLength];
char * ipAddressOut = ipAddress[j].ToString(buf);
ChipLogDetail(Discovery, "\tIP Address #%d: %s", j + 1, ipAddressOut);
}
#endif // CHIP_DETAIL_LOGGING
if (port > 0)
{
ChipLogDetail(Discovery, "\tPort: %u", port);
}
if (mrpRetryIntervalIdle.HasValue())
{
ChipLogDetail(Discovery, "\tMrp Interval idle: %" PRIu32 " ms", mrpRetryIntervalIdle.Value().count());
}
else
{
ChipLogDetail(Discovery, "\tMrp Interval idle: not present");
}
if (mrpRetryIntervalActive.HasValue())
{
ChipLogDetail(Discovery, "\tMrp Interval active: %" PRIu32 " ms", mrpRetryIntervalActive.Value().count());
}
else
{
ChipLogDetail(Discovery, "\tMrp Interval active: not present");
}
if (mrpRetryActiveThreshold.HasValue())
{
ChipLogDetail(Discovery, "\tMrp Active Threshold: %u ms", mrpRetryActiveThreshold.Value().count());
}
else
{
ChipLogDetail(Discovery, "\tMrp Active Threshold: not present");
}
ChipLogDetail(Discovery, "\tTCP Supported: %d", supportsTcp);
if (isICDOperatingAsLIT.HasValue())
{
ChipLogDetail(Discovery, "\tThe ICD operates in %s", isICDOperatingAsLIT.Value() ? "LIT" : "SIT");
}
else
{
ChipLogDetail(Discovery, "\tICD: not present");
}
}
};
/// Data that is specific to Operational Discovery of nodes
struct OperationalNodeData
{
PeerId peerId;
void Reset() { peerId = PeerId(); }
};
inline constexpr size_t kMaxDeviceNameLen = 32;
inline constexpr size_t kMaxRotatingIdLen = 50;
inline constexpr size_t kMaxPairingInstructionLen = 128;
/// Data that is specific to commisionable/commissioning node discovery
struct DnssdNodeData
{
size_t rotatingIdLen = 0;
uint32_t deviceType = 0;
uint16_t longDiscriminator = 0;
uint16_t vendorId = 0;
uint16_t productId = 0;
uint16_t pairingHint = 0;
uint8_t commissioningMode = 0;
uint8_t commissionerPasscode = 0;
uint8_t rotatingId[kMaxRotatingIdLen] = {};
char instanceName[Commission::kInstanceNameMaxLength + 1] = {};
char deviceName[kMaxDeviceNameLen + 1] = {};
char pairingInstruction[kMaxPairingInstructionLen + 1] = {};
DnssdNodeData() {}
void Reset()
{
// Let constructor clear things as default
this->~DnssdNodeData();
new (this) DnssdNodeData();
}
bool IsInstanceName(const char * instance) const { return strcmp(instance, instanceName) == 0; }
void LogDetail() const
{
if (rotatingIdLen > 0)
{
char rotatingIdString[chip::Dnssd::kMaxRotatingIdLen * 2 + 1] = "";
Encoding::BytesToUppercaseHexString(rotatingId, rotatingIdLen, rotatingIdString, sizeof(rotatingIdString));
ChipLogDetail(Discovery, "\tRotating ID: %s", rotatingIdString);
}
if (strlen(deviceName) != 0)
{
ChipLogDetail(Discovery, "\tDevice Name: %s", deviceName);
}
if (vendorId > 0)
{
ChipLogDetail(Discovery, "\tVendor ID: %u", vendorId);
}
if (productId > 0)
{
ChipLogDetail(Discovery, "\tProduct ID: %u", productId);
}
if (deviceType > 0)
{
ChipLogDetail(Discovery, "\tDevice Type: %" PRIu32, deviceType);
}
if (longDiscriminator > 0)
{
ChipLogDetail(Discovery, "\tLong Discriminator: %u", longDiscriminator);
}
if (strlen(pairingInstruction) != 0)
{
ChipLogDetail(Discovery, "\tPairing Instruction: %s", pairingInstruction);
}
if (pairingHint > 0)
{
ChipLogDetail(Discovery, "\tPairing Hint: %u", pairingHint);
}
if (!IsInstanceName(""))
{
ChipLogDetail(Discovery, "\tInstance Name: %s", instanceName);
}
ChipLogDetail(Discovery, "\tCommissioning Mode: %u", commissioningMode);
if (commissionerPasscode > 0)
{
ChipLogDetail(Discovery, "\tCommissioner Passcode: %u", commissionerPasscode);
}
}
};
struct ResolvedNodeData
{
CommonResolutionData resolutionData;
OperationalNodeData operationalData;
void LogNodeIdResolved() const
{
#if CHIP_PROGRESS_LOGGING
// Would be nice to log the interface id, but sorting out how to do so
// across our different InterfaceId implementations is a pain.
ChipLogProgress(Discovery, "Node ID resolved for " ChipLogFormatPeerId, ChipLogValuePeerId(operationalData.peerId));
resolutionData.LogDetail();
#endif // CHIP_PROGRESS_LOGGING
}
};
struct DiscoveredNodeData
{
CommonResolutionData resolutionData;
DnssdNodeData nodeData;
DiscoveryType nodeType;
void Reset()
{
resolutionData.Reset();
nodeData.Reset();
nodeType = DiscoveryType::kUnknown;
}
DiscoveredNodeData() { Reset(); }
void LogDetail() const
{
ChipLogDetail(Discovery, "Discovered node:");
resolutionData.LogDetail();
nodeData.LogDetail();
}
};
/// Callbacks for discovering nodes advertising non-operational status:
/// - Commissioners
/// - Nodes in commissioning modes over IP (e.g. ethernet devices, devices already
/// connected to thread/wifi or devices with a commissioning window open)
class DiscoverNodeDelegate
{
public:
virtual ~DiscoverNodeDelegate() = default;
/// Called within the CHIP event loop once a node is discovered.
///
/// May be called multiple times as more nodes send their answer to a
/// multicast discovery query
virtual void OnNodeDiscovered(const DiscoveredNodeData & nodeData) = 0;
};
} // namespace Dnssd
} // namespace chip