mirror of
https://github.com/espressif/openthread.git
synced 2026-09-21 08:27:33 +00:00
[srp-client] add counters for performance tracking (#13137)
Adds OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE config, APIs and a CLI subcommand to expose SRP client metrics for on-device diagnosis. Tracks wire-level (Tx, bytes, timeouts) and transaction-level (attempts, success, RCODE-bucketed rejections) outcomes, registration drivers (host-address changes, auto-start server changes), and time accumulators for registered / anycast-available / unicast-available state. Defaults to enabled when the SRP client is enabled, so behavior is unchanged for existing builds.
This commit is contained in:
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (615)
|
||||
#define OPENTHREAD_API_VERSION (616)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -119,6 +119,33 @@ typedef struct otSrpClientService
|
||||
uint32_t mKeyLease; ///< Desired key lease interval in sec - zero to use default.
|
||||
} otSrpClientService;
|
||||
|
||||
/**
|
||||
* Represents the SRP client counters.
|
||||
*/
|
||||
typedef struct otSrpClientCounters
|
||||
{
|
||||
uint64_t mRegisteredTime; ///< Cumulative number of milliseconds spent in the registered state.
|
||||
uint64_t mAnycastAvailableTime; ///< Subset of `mRegisteredTime` while registered with an anycast server.
|
||||
uint64_t mUnicastAvailableTime; ///< Subset of `mRegisteredTime` while registered with a unicast server.
|
||||
uint64_t mTrackedTime; ///< Cumulative milliseconds since last counter reset, across all client states (including
|
||||
///< stopped/paused).
|
||||
uint32_t mTxUpdates; ///< Number of SRP update transmissions (wire-level; includes retransmissions).
|
||||
uint32_t mUpdateAttempts; ///< Number of fresh SRP update transactions started.
|
||||
uint32_t mSuccess; ///< Number of transactions completed with a successful server response.
|
||||
uint32_t mRejectedDuplicate; ///< Number of transactions completed with a name/record conflict response.
|
||||
uint32_t mRejectedSecurity; ///< Number of transactions completed with a security/policy/algorithm response.
|
||||
uint32_t mRejectedOther; ///< Number of transactions completed with any other server error response.
|
||||
uint32_t mTimeouts; ///< Number of retransmission timers that expired before a response arrived.
|
||||
uint32_t mHostAddressChanges; ///< Number of host-address change events that triggered an SRP re-registration.
|
||||
uint32_t mServerChanges; ///< Number of auto-start server (re-)selections that triggered an SRP update.
|
||||
uint32_t mServiceAdds; ///< Number of times a service was successfully added.
|
||||
uint32_t mServiceRemoves; ///< Number of times a service was removed (sends an unregister to server).
|
||||
uint32_t mServiceClears; ///< Number of times a service was cleared (local-only, no server message).
|
||||
uint32_t mHostAndServicesRemoves; ///< Number of times the host and all services were removed (notifies server).
|
||||
uint32_t mHostAndServicesClears; ///< Number of times the host and all services were cleared (local-only).
|
||||
uint32_t mTxTotalBytes; ///< Cumulative UDP payload bytes of transmitted SRP messages.
|
||||
} otSrpClientCounters;
|
||||
|
||||
/**
|
||||
* Pointer type defines the callback used by SRP client to notify user of changes/events/errors.
|
||||
*
|
||||
@@ -675,6 +702,28 @@ void otSrpClientSetServiceKeyRecordEnabled(otInstance *aInstance, bool aEnabled)
|
||||
*/
|
||||
bool otSrpClientIsServiceKeyRecordEnabled(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Gets the SRP client counters.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE` to be enabled.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
* @returns A pointer to the SRP client counters.
|
||||
*/
|
||||
const otSrpClientCounters *otSrpClientGetCounters(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Resets the SRP client counters.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE` to be enabled.
|
||||
*
|
||||
* All event counters are cleared and the time-tracking accumulators are restarted.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*/
|
||||
void otSrpClientResetCounters(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ Usage : `srp client [command] ...`
|
||||
- [help](#help)
|
||||
- [autostart](#autostart)
|
||||
- [callback](#callback)
|
||||
- [counters](#counters)
|
||||
- [host](#host)
|
||||
- [keyleaseinterval](#keyleaseinterval)
|
||||
- [leaseinterval](#leaseinterval)
|
||||
@@ -127,6 +128,43 @@ Removed service list:
|
||||
instance:"ins1", name:"_test1._udp", state:Removed, port:777, priority:0, weight:0
|
||||
```
|
||||
|
||||
### counters
|
||||
|
||||
Usage: `srp client counters [reset]`
|
||||
|
||||
Get the SRP client counters. Requires `OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE` to be enabled.
|
||||
|
||||
```bash
|
||||
> srp client counters
|
||||
Tx Updates: 12
|
||||
Update Attempts: 11
|
||||
Success: 11
|
||||
Rejected Duplicate: 0
|
||||
Rejected Security: 0
|
||||
Rejected Other: 0
|
||||
Timeouts: 1
|
||||
Host Address Changes: 0
|
||||
Server Changes: 1
|
||||
Service Adds: 1
|
||||
Service Removes: 0
|
||||
Service Clears: 0
|
||||
Host And Services Removes: 0
|
||||
Host And Services Clears: 0
|
||||
Tx Total Bytes: 4380
|
||||
Registered Time Milli: 845321
|
||||
Anycast Available Time Milli: 0
|
||||
Unicast Available Time Milli: 901002
|
||||
Tracked Time Milli: 901002
|
||||
Done
|
||||
```
|
||||
|
||||
Reset the SRP client counters.
|
||||
|
||||
```bash
|
||||
> srp client counters reset
|
||||
Done
|
||||
```
|
||||
|
||||
### host
|
||||
|
||||
Usage: `srp client host`
|
||||
|
||||
+112
-3
@@ -179,6 +179,109 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
/**
|
||||
* @cli srp client counters
|
||||
* @code
|
||||
* srp client counters
|
||||
* Tx Updates: 12
|
||||
* Update Attempts: 11
|
||||
* Success: 11
|
||||
* Rejected Duplicate: 0
|
||||
* Rejected Security: 0
|
||||
* Rejected Other: 0
|
||||
* Timeouts: 1
|
||||
* Host Address Changes: 0
|
||||
* Server Changes: 1
|
||||
* Service Adds: 1
|
||||
* Service Removes: 0
|
||||
* Service Clears: 0
|
||||
* Host And Services Removes: 0
|
||||
* Host And Services Clears: 0
|
||||
* Tx Total Bytes: 4380
|
||||
* Registered Time Milli: 845321
|
||||
* Anycast Available Time Milli: 0
|
||||
* Unicast Available Time Milli: 901002
|
||||
* Tracked Time Milli: 901002
|
||||
* Done
|
||||
* @endcode
|
||||
* @par api_copy
|
||||
* #otSrpClientGetCounters
|
||||
*/
|
||||
template <> otError SrpClient::Process<Cmd("counters")>(Arg aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
if (aArgs[0].IsEmpty())
|
||||
{
|
||||
struct CounterEntry
|
||||
{
|
||||
const uint32_t otSrpClientCounters::*mValuePtr;
|
||||
const char *mName;
|
||||
};
|
||||
|
||||
struct TimeCounterEntry
|
||||
{
|
||||
const uint64_t otSrpClientCounters::*mValuePtr;
|
||||
const char *mName;
|
||||
};
|
||||
|
||||
static const CounterEntry kCounters[] = {
|
||||
{&otSrpClientCounters::mTxUpdates, "Tx Updates"},
|
||||
{&otSrpClientCounters::mUpdateAttempts, "Update Attempts"},
|
||||
{&otSrpClientCounters::mSuccess, "Success"},
|
||||
{&otSrpClientCounters::mRejectedDuplicate, "Rejected Duplicate"},
|
||||
{&otSrpClientCounters::mRejectedSecurity, "Rejected Security"},
|
||||
{&otSrpClientCounters::mRejectedOther, "Rejected Other"},
|
||||
{&otSrpClientCounters::mTimeouts, "Timeouts"},
|
||||
{&otSrpClientCounters::mHostAddressChanges, "Host Address Changes"},
|
||||
{&otSrpClientCounters::mServerChanges, "Server Changes"},
|
||||
{&otSrpClientCounters::mServiceAdds, "Service Adds"},
|
||||
{&otSrpClientCounters::mServiceRemoves, "Service Removes"},
|
||||
{&otSrpClientCounters::mServiceClears, "Service Clears"},
|
||||
{&otSrpClientCounters::mHostAndServicesRemoves, "Host And Services Removes"},
|
||||
{&otSrpClientCounters::mHostAndServicesClears, "Host And Services Clears"},
|
||||
{&otSrpClientCounters::mTxTotalBytes, "Tx Total Bytes"},
|
||||
};
|
||||
|
||||
static const TimeCounterEntry kTimeCounters[] = {
|
||||
{&otSrpClientCounters::mRegisteredTime, "Registered Time Milli"},
|
||||
{&otSrpClientCounters::mAnycastAvailableTime, "Anycast Available Time Milli"},
|
||||
{&otSrpClientCounters::mUnicastAvailableTime, "Unicast Available Time Milli"},
|
||||
{&otSrpClientCounters::mTrackedTime, "Tracked Time Milli"},
|
||||
};
|
||||
|
||||
const otSrpClientCounters *counters = otSrpClientGetCounters(GetInstancePtr());
|
||||
|
||||
for (const CounterEntry &entry : kCounters)
|
||||
{
|
||||
OutputLine("%s: %lu", entry.mName, ToUlong(counters->*entry.mValuePtr));
|
||||
}
|
||||
|
||||
for (const TimeCounterEntry &entry : kTimeCounters)
|
||||
{
|
||||
OutputFormat("%s: ", entry.mName);
|
||||
OutputUint64Line(counters->*entry.mValuePtr);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @cli srp client counters reset
|
||||
* @par api_copy
|
||||
* #otSrpClientResetCounters
|
||||
*/
|
||||
else if ((aArgs[0] == "reset") && aArgs[1].IsEmpty())
|
||||
{
|
||||
otSrpClientResetCounters(GetInstancePtr());
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
|
||||
template <> otError SrpClient::Process<Cmd("host")>(Arg aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
@@ -979,9 +1082,15 @@ otError SrpClient::Process(Arg aArgs[])
|
||||
#define CmdEntry(aCommandString) {aCommandString, &SrpClient::Process<Cmd(aCommandString)>}
|
||||
|
||||
static constexpr Command kCommands[] = {
|
||||
CmdEntry("autostart"), CmdEntry("callback"), CmdEntry("host"), CmdEntry("keyleaseinterval"),
|
||||
CmdEntry("leaseinterval"), CmdEntry("server"), CmdEntry("service"), CmdEntry("start"),
|
||||
CmdEntry("state"), CmdEntry("stop"), CmdEntry("ttl"),
|
||||
CmdEntry("autostart"), CmdEntry("callback"),
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
CmdEntry("counters"),
|
||||
#endif
|
||||
CmdEntry("host"), CmdEntry("keyleaseinterval"),
|
||||
CmdEntry("leaseinterval"), CmdEntry("server"),
|
||||
CmdEntry("service"), CmdEntry("start"),
|
||||
CmdEntry("state"), CmdEntry("stop"),
|
||||
CmdEntry("ttl"),
|
||||
};
|
||||
|
||||
static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
|
||||
|
||||
@@ -183,4 +183,13 @@ bool otSrpClientIsServiceKeyRecordEnabled(otInstance *aInstance)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
const otSrpClientCounters *otSrpClientGetCounters(otInstance *aInstance)
|
||||
{
|
||||
return &AsCoreType(aInstance).Get<Srp::Client>().GetCounters();
|
||||
}
|
||||
|
||||
void otSrpClientResetCounters(otInstance *aInstance) { AsCoreType(aInstance).Get<Srp::Client>().ResetCounters(); }
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
|
||||
|
||||
@@ -54,6 +54,20 @@
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
*
|
||||
* Define to 1 to enable SRP Client counters APIs (`otSrpClientGetCounters` and `otSrpClientResetCounters`).
|
||||
*
|
||||
* Defaults to enabled when both `OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE` and `OPENTHREAD_CONFIG_UPTIME_ENABLE` are
|
||||
* enabled, and disabled otherwise. Requires `OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE` and
|
||||
* `OPENTHREAD_CONFIG_UPTIME_ENABLE`.
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE \
|
||||
(OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && OPENTHREAD_CONFIG_UPTIME_ENABLE)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
|
||||
*
|
||||
|
||||
+126
-3
@@ -260,10 +260,14 @@ const char *Client::TxJitter::ReasonToString(Reason aReason)
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
|
||||
|
||||
Client::AutoStart::AutoStart(void)
|
||||
Client::AutoStart::AutoStart(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mState(kDefaultMode ? kFirstTimeSelecting : kDisabled)
|
||||
, mAnycastSeqNum(0)
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
|
||||
, mTimeoutFailureCount(0)
|
||||
#endif
|
||||
{
|
||||
Clear();
|
||||
mState = kDefaultMode ? kFirstTimeSelecting : kDisabled;
|
||||
}
|
||||
|
||||
bool Client::AutoStart::HasSelectedServer(void) const
|
||||
@@ -291,6 +295,9 @@ void Client::AutoStart::SetState(State aState)
|
||||
{
|
||||
if (mState != aState)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
Get<Client>().UpdateTimeCounters();
|
||||
#endif
|
||||
LogInfo("AutoStartState %s -> %s", StateToString(mState), StateToString(aState));
|
||||
mState = aState;
|
||||
}
|
||||
@@ -348,6 +355,7 @@ Client::Client(Instance &aInstance)
|
||||
, mTimer(aInstance)
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
|
||||
, mGuardTimer(aInstance)
|
||||
, mAutoStart(aInstance)
|
||||
#endif
|
||||
{
|
||||
// The `Client` implementation uses different constant array of
|
||||
@@ -370,6 +378,11 @@ Client::Client(Instance &aInstance)
|
||||
};
|
||||
|
||||
mHostInfo.Init();
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
ClearAllBytes(mCounters);
|
||||
mLastUpdatedTimestamp = Get<UptimeTracker>().GetUptime();
|
||||
#endif
|
||||
}
|
||||
|
||||
Error Client::Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester)
|
||||
@@ -735,6 +748,10 @@ Error Client::UpdateHostInfoStateOnAddressChange(void)
|
||||
VerifyOrExit((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving),
|
||||
error = kErrorInvalidState);
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mHostAddressChanges++;
|
||||
#endif
|
||||
|
||||
if (mHostInfo.GetState() == kRemoved)
|
||||
{
|
||||
mHostInfo.SetState(kToAdd);
|
||||
@@ -757,6 +774,10 @@ Error Client::AddService(Service &aService)
|
||||
SuccessOrExit(error = aService.Init());
|
||||
mServices.Push(aService);
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mServiceAdds++;
|
||||
#endif
|
||||
|
||||
aService.SetState(kToAdd);
|
||||
UpdateState();
|
||||
|
||||
@@ -771,6 +792,10 @@ Error Client::RemoveService(Service &aService)
|
||||
|
||||
VerifyOrExit(mServices.Contains(aService), error = kErrorNotFound);
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mServiceRemoves++;
|
||||
#endif
|
||||
|
||||
UpdateServiceStateToRemove(aService);
|
||||
UpdateState();
|
||||
|
||||
@@ -791,6 +816,11 @@ Error Client::ClearService(Service &aService)
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = mServices.Remove(aService));
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mServiceClears++;
|
||||
#endif
|
||||
|
||||
aService.SetNext(nullptr);
|
||||
aService.SetState(kRemoved);
|
||||
UpdateState();
|
||||
@@ -830,6 +860,10 @@ Error Client::RemoveHostAndServices(bool aShouldRemoveKeyLease, bool aSendUnregT
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mHostAndServicesRemoves++;
|
||||
#endif
|
||||
|
||||
mHostInfo.SetState(kToRemove);
|
||||
UpdateState();
|
||||
|
||||
@@ -841,6 +875,10 @@ void Client::ClearHostAndServices(void)
|
||||
{
|
||||
LogInfo("Clear host & services");
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mHostAndServicesClears++;
|
||||
#endif
|
||||
|
||||
switch (GetState())
|
||||
{
|
||||
case kStateStopped:
|
||||
@@ -867,6 +905,11 @@ void Client::SetState(State aState)
|
||||
VerifyOrExit(aState != mState);
|
||||
|
||||
LogInfo("State %s -> %s", StateToString(mState), StateToString(aState));
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
UpdateTimeCounters();
|
||||
#endif
|
||||
|
||||
mState = aState;
|
||||
|
||||
switch (mState)
|
||||
@@ -892,6 +935,45 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
|
||||
const Client::Counters &Client::GetCounters(void)
|
||||
{
|
||||
UpdateTimeCounters();
|
||||
return mCounters;
|
||||
}
|
||||
|
||||
void Client::ResetCounters(void)
|
||||
{
|
||||
ClearAllBytes(mCounters);
|
||||
mLastUpdatedTimestamp = Get<UptimeTracker>().GetUptime();
|
||||
}
|
||||
|
||||
void Client::UpdateTimeCounters(void)
|
||||
{
|
||||
UptimeMsec now = Get<UptimeTracker>().GetUptime();
|
||||
UptimeMsec duration = now - mLastUpdatedTimestamp;
|
||||
|
||||
mLastUpdatedTimestamp = now;
|
||||
mCounters.mTrackedTime += duration;
|
||||
|
||||
if (mState == kStateUpdated)
|
||||
{
|
||||
mCounters.mRegisteredTime += duration;
|
||||
|
||||
if (GetServerAddress().GetAddress().GetIid().IsAnycastServiceLocator())
|
||||
{
|
||||
mCounters.mAnycastAvailableTime += duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCounters.mUnicastAvailableTime += duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
|
||||
bool Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode)
|
||||
{
|
||||
bool anyChanged;
|
||||
@@ -976,6 +1058,9 @@ void Client::SendUpdate(void)
|
||||
MsgInfo info;
|
||||
uint32_t length;
|
||||
bool anyChanged;
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
uint16_t txPayloadLength;
|
||||
#endif
|
||||
|
||||
info.mMessage.Reset(mSocket.NewMessage());
|
||||
VerifyOrExit(info.mMessage != nullptr, error = kErrorNoBufs);
|
||||
@@ -1012,12 +1097,24 @@ void Client::SendUpdate(void)
|
||||
if (anyChanged)
|
||||
{
|
||||
SelectNewMessageId();
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mUpdateAttempts++;
|
||||
#endif
|
||||
}
|
||||
|
||||
SuccessOrExit(error = UpdateIdAndSignatureInUpdateMessage(info));
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
txPayloadLength = info.mMessage->GetLength();
|
||||
#endif
|
||||
|
||||
SuccessOrExit(error = mSocket.SendTo(*info.mMessage, Ip6::MessageInfo()));
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mTxUpdates++;
|
||||
mCounters.mTxTotalBytes += txPayloadLength;
|
||||
#endif
|
||||
|
||||
// Ownership of the message is transferred to the socket upon a
|
||||
// successful `SendTo()` call.
|
||||
|
||||
@@ -1820,6 +1917,21 @@ void Client::ProcessResponse(Message &aMessage)
|
||||
{
|
||||
LogInfo("Server rejected %s code:%d", ErrorToString(error), header.GetResponseCode());
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
switch (error)
|
||||
{
|
||||
case kErrorDuplicated:
|
||||
mCounters.mRejectedDuplicate++;
|
||||
break;
|
||||
case kErrorSecurity:
|
||||
mCounters.mRejectedSecurity++;
|
||||
break;
|
||||
default:
|
||||
mCounters.mRejectedOther++;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mHostInfo.GetState() == kAdding)
|
||||
{
|
||||
// Since server rejected the update message, we go back to
|
||||
@@ -1929,6 +2041,10 @@ void Client::ProcessResponse(Message &aMessage)
|
||||
|
||||
ChangeHostAndServiceStates(kNewStateOnUpdateDone, kForServicesAppendedInMessage);
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mSuccess++;
|
||||
#endif
|
||||
|
||||
HandleUpdateDone();
|
||||
UpdateState();
|
||||
|
||||
@@ -2197,6 +2313,9 @@ void Client::HandleTimer(void)
|
||||
case kStateUpdating:
|
||||
LogRetryWaitInterval();
|
||||
LogInfo("Timed out, no response");
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mTimeouts++;
|
||||
#endif
|
||||
GrowRetryWaitInterval();
|
||||
SetState(kStateToUpdate);
|
||||
InvokeCallback(kErrorResponseTimeout);
|
||||
@@ -2394,6 +2513,10 @@ void Client::ProcessAutoStart(void)
|
||||
break;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
mCounters.mServerChanges++;
|
||||
#endif
|
||||
|
||||
IgnoreError(Start(serverSockAddr, kRequesterAuto));
|
||||
|
||||
exit:
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "common/owned_ptr.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "common/uptime.hpp"
|
||||
#include "crypto/ecdsa.hpp"
|
||||
#include "net/dns_types.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
@@ -66,6 +67,14 @@ namespace Srp {
|
||||
#error "SRP Client feature requires ECDSA support (OPENTHREAD_CONFIG_ECDSA_ENABLE)."
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE && !OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
|
||||
#error "OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE requires OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE."
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE && !OPENTHREAD_CONFIG_UPTIME_ENABLE
|
||||
#error "OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE requires OPENTHREAD_CONFIG_UPTIME_ENABLE."
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implements SRP client.
|
||||
*/
|
||||
@@ -757,6 +766,25 @@ public:
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
/**
|
||||
* Represents the SRP client counters (matches `otSrpClientCounters`).
|
||||
*/
|
||||
typedef otSrpClientCounters Counters;
|
||||
|
||||
/**
|
||||
* Gets the SRP client counters.
|
||||
*
|
||||
* @returns A reference to the SRP client counters.
|
||||
*/
|
||||
const Counters &GetCounters(void);
|
||||
|
||||
/**
|
||||
* Resets the SRP client counters.
|
||||
*/
|
||||
void ResetCounters(void);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Number of fast data polls after SRP Update tx (11x 188ms = ~2 seconds)
|
||||
static constexpr uint8_t kFastPollsAfterUpdateTx = 11;
|
||||
@@ -960,7 +988,7 @@ private:
|
||||
};
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
|
||||
class AutoStart : public Clearable<AutoStart>
|
||||
class AutoStart : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
enum State : uint8_t
|
||||
@@ -973,7 +1001,7 @@ private:
|
||||
kSelectedUnicast, // Has selected a unicast entry (address in server data).
|
||||
};
|
||||
|
||||
AutoStart(void);
|
||||
explicit AutoStart(Instance &aInstance);
|
||||
bool HasSelectedServer(void) const;
|
||||
State GetState(void) const { return mState; }
|
||||
void SetState(State aState);
|
||||
@@ -1077,6 +1105,10 @@ private:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
void UpdateTimeCounters(void);
|
||||
#endif
|
||||
|
||||
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
|
||||
static const char *StateToString(State aState);
|
||||
void LogRetryWaitInterval(void) const;
|
||||
@@ -1127,6 +1159,10 @@ private:
|
||||
GuardTimer mGuardTimer;
|
||||
AutoStart mAutoStart;
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
Counters mCounters;
|
||||
UptimeMsec mLastUpdatedTimestamp;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Srp
|
||||
|
||||
@@ -448,6 +448,7 @@ ot_nexus_test(router_reboot_multiple_link_request "core;nexus")
|
||||
ot_nexus_test(service "core;nexus")
|
||||
ot_nexus_test(srp_auto_start "core;nexus")
|
||||
ot_nexus_test(srp_client_change_lease "core;nexus")
|
||||
ot_nexus_test(srp_client_counters "core;nexus")
|
||||
ot_nexus_test(srp_client_remove_host "core;nexus")
|
||||
ot_nexus_test(srp_client_save_server_info "core;nexus")
|
||||
ot_nexus_test(srp_lease "core;nexus")
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (c) 2026, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "platform/nexus_core.hpp"
|
||||
#include "platform/nexus_node.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
static constexpr uint32_t kFormNetworkTime = 13 * 1000;
|
||||
static constexpr uint32_t kJoinNetworkTime = 10 * 1000;
|
||||
static constexpr uint32_t kRegistrationTime = 5 * 1000;
|
||||
|
||||
static constexpr uint32_t kLeaseTime = 60;
|
||||
static constexpr uint32_t kKeyLeaseTime = 240;
|
||||
|
||||
static const char kSrpServiceType[] = "_ipps._tcp";
|
||||
static const char kSrpInstanceName[] = "my-service";
|
||||
static const char kSrpInstanceName2[] = "my-service-2";
|
||||
static const char kSrpHostName[] = "my-host";
|
||||
static const char kSrpHostAddress[] = "2001::1";
|
||||
static constexpr uint16_t kSrpServicePort = 12345;
|
||||
static constexpr uint16_t kSrpServicePort2 = 12346;
|
||||
|
||||
void TestSrpClientCounters(void)
|
||||
{
|
||||
Core nexus;
|
||||
Ip6::Address srpHostAddress;
|
||||
Srp::Client::Service srpService;
|
||||
Srp::Client::Service srpService2;
|
||||
|
||||
Node &server = nexus.CreateNode();
|
||||
Node &client = nexus.CreateNode();
|
||||
|
||||
server.SetName("SRP_SERVER");
|
||||
client.SetName("SRP_CLIENT");
|
||||
|
||||
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
|
||||
|
||||
Log("Step 0: Form network and bring up server.");
|
||||
|
||||
server.Form();
|
||||
nexus.AdvanceTime(kFormNetworkTime);
|
||||
|
||||
{
|
||||
Srp::Server::LeaseConfig leaseConfig;
|
||||
leaseConfig.mMinLease = kLeaseTime;
|
||||
leaseConfig.mMaxLease = kLeaseTime;
|
||||
leaseConfig.mMinKeyLease = kKeyLeaseTime;
|
||||
leaseConfig.mMaxKeyLease = kKeyLeaseTime;
|
||||
SuccessOrQuit(server.Get<Srp::Server>().SetLeaseConfig(leaseConfig));
|
||||
}
|
||||
server.Get<Srp::Server>().SetEnabled(true);
|
||||
nexus.AdvanceTime(5 * 1000);
|
||||
|
||||
client.Join(server, Node::kAsFed);
|
||||
nexus.AdvanceTime(kJoinNetworkTime);
|
||||
|
||||
Log("Step 1: Verify counters start at zero.");
|
||||
{
|
||||
const otSrpClientCounters &counters = client.Get<Srp::Client>().GetCounters();
|
||||
|
||||
VerifyOrQuit(counters.mTxUpdates == 0);
|
||||
VerifyOrQuit(counters.mUpdateAttempts == 0);
|
||||
VerifyOrQuit(counters.mSuccess == 0);
|
||||
VerifyOrQuit(counters.mRejectedDuplicate == 0);
|
||||
VerifyOrQuit(counters.mRejectedSecurity == 0);
|
||||
VerifyOrQuit(counters.mRejectedOther == 0);
|
||||
VerifyOrQuit(counters.mTimeouts == 0);
|
||||
VerifyOrQuit(counters.mHostAddressChanges == 0);
|
||||
VerifyOrQuit(counters.mServerChanges == 0);
|
||||
VerifyOrQuit(counters.mServiceAdds == 0);
|
||||
VerifyOrQuit(counters.mServiceRemoves == 0);
|
||||
VerifyOrQuit(counters.mServiceClears == 0);
|
||||
VerifyOrQuit(counters.mHostAndServicesRemoves == 0);
|
||||
VerifyOrQuit(counters.mHostAndServicesClears == 0);
|
||||
VerifyOrQuit(counters.mTxTotalBytes == 0);
|
||||
VerifyOrQuit(counters.mRegisteredTime == 0);
|
||||
VerifyOrQuit(counters.mAnycastAvailableTime == 0);
|
||||
VerifyOrQuit(counters.mUnicastAvailableTime == 0);
|
||||
}
|
||||
|
||||
Log("Step 2: Register a service and verify Tx/Attempts/Success/ServerChanges/UnicastAvailable counters advance.");
|
||||
|
||||
client.Get<Srp::Client>().EnableAutoStartMode(nullptr, nullptr);
|
||||
SuccessOrQuit(client.Get<Srp::Client>().SetHostName(kSrpHostName));
|
||||
SuccessOrQuit(srpHostAddress.FromString(kSrpHostAddress));
|
||||
SuccessOrQuit(client.Get<Srp::Client>().SetHostAddresses(&srpHostAddress, 1));
|
||||
|
||||
ClearAllBytes(srpService);
|
||||
srpService.mName = kSrpServiceType;
|
||||
srpService.mInstanceName = kSrpInstanceName;
|
||||
srpService.mPort = kSrpServicePort;
|
||||
SuccessOrQuit(client.Get<Srp::Client>().AddService(srpService));
|
||||
|
||||
nexus.AdvanceTime(kRegistrationTime);
|
||||
|
||||
VerifyOrQuit(client.Get<Srp::Client>().GetHostInfo().GetState() == Srp::Client::kRegistered);
|
||||
|
||||
{
|
||||
const otSrpClientCounters &counters = client.Get<Srp::Client>().GetCounters();
|
||||
|
||||
VerifyOrQuit(counters.mTxUpdates >= 1);
|
||||
VerifyOrQuit(counters.mUpdateAttempts >= 1);
|
||||
VerifyOrQuit(counters.mSuccess >= 1);
|
||||
VerifyOrQuit(counters.mServerChanges >= 1);
|
||||
VerifyOrQuit(counters.mServiceAdds == 1);
|
||||
VerifyOrQuit(counters.mHostAddressChanges >= 1);
|
||||
VerifyOrQuit(counters.mTxTotalBytes > 0);
|
||||
VerifyOrQuit(counters.mTrackedTime > 0);
|
||||
VerifyOrQuit(counters.mRegisteredTime <= counters.mTrackedTime);
|
||||
VerifyOrQuit(counters.mUnicastAvailableTime > 0);
|
||||
VerifyOrQuit(counters.mUnicastAvailableTime <= counters.mRegisteredTime);
|
||||
VerifyOrQuit(counters.mAnycastAvailableTime == 0);
|
||||
|
||||
// Wire-level transmissions are at least as numerous as logical transactions.
|
||||
VerifyOrQuit(counters.mTxUpdates >= counters.mUpdateAttempts);
|
||||
|
||||
// Every completed transaction was attempted.
|
||||
VerifyOrQuit(counters.mUpdateAttempts >= counters.mSuccess + counters.mRejectedDuplicate +
|
||||
counters.mRejectedSecurity + counters.mRejectedOther);
|
||||
}
|
||||
|
||||
Log("Step 3: Wait for a refresh cycle and verify counters keep advancing.");
|
||||
{
|
||||
const otSrpClientCounters &before = client.Get<Srp::Client>().GetCounters();
|
||||
uint32_t txBefore = before.mTxUpdates;
|
||||
uint32_t attemptsBefore = before.mUpdateAttempts;
|
||||
uint32_t successBefore = before.mSuccess;
|
||||
uint64_t unicastAvailableBefore = before.mUnicastAvailableTime;
|
||||
|
||||
nexus.AdvanceTime(kLeaseTime * 1000);
|
||||
|
||||
const otSrpClientCounters &after = client.Get<Srp::Client>().GetCounters();
|
||||
|
||||
VerifyOrQuit(after.mTxUpdates > txBefore);
|
||||
VerifyOrQuit(after.mUpdateAttempts > attemptsBefore);
|
||||
VerifyOrQuit(after.mSuccess > successBefore);
|
||||
VerifyOrQuit(after.mRegisteredTime > 0);
|
||||
VerifyOrQuit(after.mUnicastAvailableTime > unicastAvailableBefore);
|
||||
}
|
||||
|
||||
Log("Step 4: Add a second service, remove the first, clear the second; verify Service{Adds,Removes,Clears}.");
|
||||
{
|
||||
ClearAllBytes(srpService2);
|
||||
srpService2.mName = kSrpServiceType;
|
||||
srpService2.mInstanceName = kSrpInstanceName2;
|
||||
srpService2.mPort = kSrpServicePort2;
|
||||
SuccessOrQuit(client.Get<Srp::Client>().AddService(srpService2));
|
||||
|
||||
nexus.AdvanceTime(kRegistrationTime);
|
||||
|
||||
VerifyOrQuit(client.Get<Srp::Client>().GetCounters().mServiceAdds == 2);
|
||||
|
||||
SuccessOrQuit(client.Get<Srp::Client>().RemoveService(srpService));
|
||||
|
||||
nexus.AdvanceTime(kRegistrationTime);
|
||||
|
||||
VerifyOrQuit(client.Get<Srp::Client>().GetCounters().mServiceRemoves == 1);
|
||||
|
||||
SuccessOrQuit(client.Get<Srp::Client>().ClearService(srpService2));
|
||||
|
||||
VerifyOrQuit(client.Get<Srp::Client>().GetCounters().mServiceClears == 1);
|
||||
}
|
||||
|
||||
Log("Step 5: Remove host & services, then clear; verify HostAndServices{Removes,Clears}.");
|
||||
{
|
||||
SuccessOrQuit(client.Get<Srp::Client>().RemoveHostAndServices(/* aShouldRemoveKeyLease */ false,
|
||||
/* aSendUnregToServer */ false));
|
||||
|
||||
nexus.AdvanceTime(kRegistrationTime);
|
||||
|
||||
VerifyOrQuit(client.Get<Srp::Client>().GetCounters().mHostAndServicesRemoves == 1);
|
||||
|
||||
client.Get<Srp::Client>().ClearHostAndServices();
|
||||
|
||||
VerifyOrQuit(client.Get<Srp::Client>().GetCounters().mHostAndServicesClears == 1);
|
||||
}
|
||||
|
||||
Log("Step 6: Reset counters and verify all event counters return to zero.");
|
||||
{
|
||||
client.Get<Srp::Client>().ResetCounters();
|
||||
|
||||
const otSrpClientCounters &counters = client.Get<Srp::Client>().GetCounters();
|
||||
|
||||
VerifyOrQuit(counters.mTxUpdates == 0);
|
||||
VerifyOrQuit(counters.mUpdateAttempts == 0);
|
||||
VerifyOrQuit(counters.mSuccess == 0);
|
||||
VerifyOrQuit(counters.mRejectedDuplicate == 0);
|
||||
VerifyOrQuit(counters.mRejectedSecurity == 0);
|
||||
VerifyOrQuit(counters.mRejectedOther == 0);
|
||||
VerifyOrQuit(counters.mTimeouts == 0);
|
||||
VerifyOrQuit(counters.mHostAddressChanges == 0);
|
||||
VerifyOrQuit(counters.mServerChanges == 0);
|
||||
VerifyOrQuit(counters.mServiceAdds == 0);
|
||||
VerifyOrQuit(counters.mServiceRemoves == 0);
|
||||
VerifyOrQuit(counters.mServiceClears == 0);
|
||||
VerifyOrQuit(counters.mHostAndServicesRemoves == 0);
|
||||
VerifyOrQuit(counters.mHostAndServicesClears == 0);
|
||||
VerifyOrQuit(counters.mTxTotalBytes == 0);
|
||||
VerifyOrQuit(counters.mRegisteredTime == 0);
|
||||
VerifyOrQuit(counters.mAnycastAvailableTime == 0);
|
||||
VerifyOrQuit(counters.mUnicastAvailableTime == 0);
|
||||
VerifyOrQuit(counters.mTrackedTime == 0);
|
||||
}
|
||||
|
||||
Log("Step 7: Verify time tracking restarts cleanly after reset.");
|
||||
{
|
||||
nexus.AdvanceTime(kRegistrationTime);
|
||||
|
||||
const otSrpClientCounters &counters = client.Get<Srp::Client>().GetCounters();
|
||||
|
||||
VerifyOrQuit(counters.mTrackedTime > 0);
|
||||
VerifyOrQuit(counters.mRegisteredTime <= counters.mTrackedTime);
|
||||
}
|
||||
|
||||
// Note: mTimeouts, mRejected{Duplicate,Security,Other}, and mAnycastAvailableTime are
|
||||
// not exercised by this nexus test — those paths require packet manipulation or an
|
||||
// anycast-published SRP server. Their increment sites are simple single-line bumps
|
||||
// verified by code review.
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::Nexus::TestSrpClientCounters();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE is disabled, skipping test\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE
|
||||
@@ -99,4 +99,6 @@
|
||||
|
||||
#define OPENTHREAD_CONFIG_HISTORY_TRACKER_CLIENT_ENABLE 0
|
||||
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE 0
|
||||
|
||||
#endif // OT_TORANJ_OPENTHREAD_CORE_TORANJ_CONFIG_POSIX_H_
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
|
||||
#define OPENTHREAD_CONFIG_SRP_SERVER_FAST_START_MODE_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_SRP_CLIENT_COUNTERS_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_COAP_API_ENABLE 1
|
||||
|
||||
#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 1
|
||||
|
||||
Reference in New Issue
Block a user