Files
openthread/tests/unit/test_srp_adv_proxy.cpp
T
Abtin Keshavarzian d87be657c6 [srp-client] enhance selection of TX jitter based on trigger reason (#10357)
This commit improves the `Srp::Client` mechanism for applying random
jitter delays before sending update messages to the server. It now
supports different jitter ranges based on specific triggers. Since
trigger events are often network-wide, potentially causing
simultaneous SRP re-registration from many nodes, longer jitter
intervals are used to distribute the resulting SRP update
transmissions and avoid congestion.

The following triggers are covered:

- Server switch: Client switching to a new server while already
  connected to a discovered server. This occurs when a new server
  entry appears in Network Data which is preferred over the current
  selection.

- Server restart: Client was previously connected to a server that
  disappeared from Network Data. Later, the same or a new server is
  discovered in Network Data.

- SLAAC address add or remove: This is generally triggered by updates
  to SLAAC prefixes in Network Data (e.g., OMR prefix changes).

- First registration after attach:

  - If the device is attaching to an established Thread mesh
    (e.g., after a reboot or initial pairing), the Network Data it
    receives should already include a server entry, leading to a
    quick server selection after attach. If server selection occurs
    within a short window, a shorter TX jitter is used, allowing the
    device to register quickly and become discoverable.

  - If server discovery takes longer, a longer TX jitter is used. This
    situation can indicate a server/BR starting up or a network-wide
    restart of many nodes (e.g., due to a power outage).

This commit introduces `TxJitter` class to manage the requested TX
jitter based on a trigger reason. It tracks the time of the event
that triggered a longer jitter request. If the update message is sent
immediately after the trigger event, the requested maximum jitter is
applied. However, if the update message is sent later, the maximum
jitter is adjusted proportionally to the time elapsed since the
trigger event. If the elapsed time exceeds the requested maximum
jitter interval, the default short jitter is applied to avoid
unnecessary registration delay.
2024-07-02 08:52:28 -07:00

2786 lines
96 KiB
C++

/*
* Copyright (c) 2023, 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 <openthread/config.h>
#include "test_platform.h"
#include "test_util.hpp"
#include <openthread/dataset_ftd.h>
#include <openthread/srp_client.h>
#include <openthread/srp_server.h>
#include <openthread/thread.h>
#include "common/arg_macros.hpp"
#include "common/array.hpp"
#include "common/string.hpp"
#include "common/time.hpp"
#include "instance/instance.hpp"
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \
OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE && !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && \
!OPENTHREAD_PLATFORM_POSIX && OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION
#define ENABLE_ADV_PROXY_TEST 1
#else
#define ENABLE_ADV_PROXY_TEST 0
#endif
#if ENABLE_ADV_PROXY_TEST
using namespace ot;
// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
#define Log(...) \
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \
(sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
static constexpr uint16_t kMaxRaSize = 800;
static ot::Instance *sInstance;
static uint32_t sNow = 0;
static uint32_t sAlarmTime;
static bool sAlarmOn = false;
static otRadioFrame sRadioTxFrame;
static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE];
static bool sRadioTxOngoing = false;
//----------------------------------------------------------------------------------------------------------------------
// Function prototypes
void ProcessRadioTxAndTasklets(void);
void AdvanceTime(uint32_t aDuration);
//----------------------------------------------------------------------------------------------------------------------
// `otPlatRadio`
extern "C" {
otRadioCaps otPlatRadioGetCaps(otInstance *) { return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF; }
otError otPlatRadioTransmit(otInstance *, otRadioFrame *)
{
sRadioTxOngoing = true;
return OT_ERROR_NONE;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; }
//----------------------------------------------------------------------------------------------------------------------
// `otPlatAlarm`
void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
{
sAlarmOn = true;
sAlarmTime = aT0 + aDt;
}
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
//----------------------------------------------------------------------------------------------------------------------
// `otPlatDnssd`
static constexpr uint16_t kDnssdArraySize = 128;
struct DnssdRequest
{
DnssdRequest(void) = default;
DnssdRequest(otPlatDnssdRequestId aId, otPlatDnssdRegisterCallback aCallback)
: mId(aId)
, mCallback(aCallback)
{
}
otPlatDnssdRequestId mId;
otPlatDnssdRegisterCallback mCallback;
};
static Array<DnssdRequest, kDnssdArraySize> sDnssdRegHostRequests;
static Array<DnssdRequest, kDnssdArraySize> sDnssdUnregHostRequests;
static Array<DnssdRequest, kDnssdArraySize> sDnssdRegServiceRequests;
static Array<DnssdRequest, kDnssdArraySize> sDnssdUnregServiceRequests;
static Array<DnssdRequest, kDnssdArraySize> sDnssdRegKeyRequests;
static Array<DnssdRequest, kDnssdArraySize> sDnssdUnregKeyRequests;
static bool sDnssdShouldCheckWithClient = true;
static Error sDnssdCallbackError = kErrorPending;
static otPlatDnssdState sDnssdState = OT_PLAT_DNSSD_READY;
static uint16_t sDnssdNumHostAddresses = 0;
constexpr uint32_t kInfraIfIndex = 1;
otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sDnssdState;
}
void otPlatDnssdRegisterService(otInstance *aInstance,
const otPlatDnssdService *aService,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback)
{
Log("otPlatDnssdRegisterService(aRequestId: %lu)", ToUlong(aRequestId));
Log(" hostName : %s", aService->mHostName);
Log(" serviceInstance: %s", aService->mServiceInstance);
Log(" serviceType : %s", aService->mServiceType);
Log(" num sub-types : %u", aService->mSubTypeLabelsLength);
for (uint16_t index = 0; index < aService->mSubTypeLabelsLength; index++)
{
Log(" sub-type %-4u : %s", index, aService->mSubTypeLabels[index]);
}
Log(" TXT data len : %u", aService->mTxtDataLength);
Log(" port : %u", aService->mPort);
Log(" priority : %u", aService->mPriority);
Log(" weight : %u", aService->mWeight);
Log(" TTL : %u", aService->mTtl);
Log(" Infra-if index : %u", aService->mInfraIfIndex);
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aService->mInfraIfIndex == kInfraIfIndex);
if (sDnssdShouldCheckWithClient)
{
Srp::Client &srpClient = AsCoreType(aInstance).Get<Srp::Client>();
bool didFind = false;
VerifyOrQuit(StringMatch(srpClient.GetHostInfo().GetName(), aService->mHostName));
didFind = false;
for (const Srp::Client::Service &service : srpClient.GetServices())
{
if (StringMatch(service.GetInstanceName(), aService->mServiceInstance))
{
didFind = true;
VerifyOrQuit(StringMatch(service.GetName(), aService->mServiceType));
VerifyOrQuit(service.GetPort() == aService->mPort);
VerifyOrQuit(service.GetWeight() == aService->mWeight);
VerifyOrQuit(service.GetPriority() == aService->mPriority);
VerifyOrQuit(service.HasSubType() == (aService->mSubTypeLabelsLength != 0));
}
}
VerifyOrQuit(didFind);
}
SuccessOrQuit(sDnssdRegServiceRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
{
aCallback(aInstance, aRequestId, sDnssdCallbackError);
}
}
void otPlatDnssdUnregisterService(otInstance *aInstance,
const otPlatDnssdService *aService,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback)
{
Log("otPlatDnssdUnregisterService(aRequestId: %lu)", ToUlong(aRequestId));
Log(" hostName : %s", aService->mHostName);
Log(" serviceInstance: %s", aService->mServiceInstance);
Log(" serviceName : %s", aService->mServiceType);
Log(" Infra-if index : %u", aService->mInfraIfIndex);
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aService->mInfraIfIndex == kInfraIfIndex);
if (sDnssdShouldCheckWithClient)
{
// Validate the received service info matches one of the services
// on SRP client.
Srp::Client &srpClient = AsCoreType(aInstance).Get<Srp::Client>();
bool didFind = false;
VerifyOrQuit(StringMatch(srpClient.GetHostInfo().GetName(), aService->mHostName));
for (const Srp::Client::Service &service : srpClient.GetServices())
{
if (StringMatch(service.GetInstanceName(), aService->mServiceInstance))
{
didFind = true;
VerifyOrQuit(StringMatch(service.GetName(), aService->mServiceType));
}
}
VerifyOrQuit(didFind);
}
SuccessOrQuit(sDnssdUnregServiceRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
{
aCallback(aInstance, aRequestId, sDnssdCallbackError);
}
}
void otPlatDnssdRegisterHost(otInstance *aInstance,
const otPlatDnssdHost *aHost,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback)
{
Log("otPlatDnssdRegisterHost(aRequestId: %lu)", ToUlong(aRequestId));
Log(" hostName : %s", aHost->mHostName);
Log(" numAddresses : %u", aHost->mAddressesLength);
for (uint16_t index = 0; index < aHost->mAddressesLength; index++)
{
Log(" Address %-4u : %s", index, AsCoreType(&aHost->mAddresses[index]).ToString().AsCString());
}
Log(" TTL : %u", aHost->mTtl);
Log(" Infra-if index : %u", aHost->mInfraIfIndex);
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aHost->mInfraIfIndex == kInfraIfIndex);
sDnssdNumHostAddresses = aHost->mAddressesLength;
if (sDnssdShouldCheckWithClient)
{
VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aHost->mHostName));
}
SuccessOrQuit(sDnssdRegHostRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
{
aCallback(aInstance, aRequestId, sDnssdCallbackError);
}
}
void otPlatDnssdUnregisterHost(otInstance *aInstance,
const otPlatDnssdHost *aHost,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback)
{
Log("otPlatDnssdUnregisterHost(aRequestId: %lu)", ToUlong(aRequestId));
Log(" hostName : %s", aHost->mHostName);
Log(" Infra-if index : %u", aHost->mInfraIfIndex);
VerifyOrQuit(sInstance == aInstance);
VerifyOrQuit(aHost->mInfraIfIndex == kInfraIfIndex);
if (sDnssdShouldCheckWithClient)
{
VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aHost->mHostName));
}
SuccessOrQuit(sDnssdUnregHostRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
{
aCallback(aInstance, aRequestId, sDnssdCallbackError);
}
}
void otPlatDnssdRegisterKey(otInstance *aInstance,
const otPlatDnssdKey *aKey,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback)
{
Log("otPlatDnssdRegisterKey(aRequestId: %lu)", ToUlong(aRequestId));
Log(" name : %s", aKey->mName);
Log(" serviceType : %s", aKey->mServiceType == nullptr ? "(null)" : aKey->mServiceType);
Log(" key data-len : %u", aKey->mKeyDataLength);
Log(" TTL : %u", aKey->mTtl);
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aKey->mInfraIfIndex == kInfraIfIndex);
if (sDnssdShouldCheckWithClient)
{
if (aKey->mServiceType == nullptr)
{
VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aKey->mName));
}
else
{
bool didFind = false;
for (const Srp::Client::Service &service : AsCoreType(aInstance).Get<Srp::Client>().GetServices())
{
if (StringMatch(service.GetInstanceName(), aKey->mName))
{
didFind = true;
VerifyOrQuit(StringMatch(service.GetName(), aKey->mServiceType));
}
}
VerifyOrQuit(didFind);
}
}
SuccessOrQuit(sDnssdRegKeyRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
{
aCallback(aInstance, aRequestId, sDnssdCallbackError);
}
}
void otPlatDnssdUnregisterKey(otInstance *aInstance,
const otPlatDnssdKey *aKey,
otPlatDnssdRequestId aRequestId,
otPlatDnssdRegisterCallback aCallback)
{
Log("otPlatDnssdUnregisterKey(aRequestId: %lu)", ToUlong(aRequestId));
Log(" name : %s", aKey->mName);
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aKey->mInfraIfIndex == kInfraIfIndex);
if (sDnssdShouldCheckWithClient)
{
if (aKey->mServiceType == nullptr)
{
VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aKey->mName));
}
else
{
bool didFind = false;
for (const Srp::Client::Service &service : AsCoreType(aInstance).Get<Srp::Client>().GetServices())
{
if (StringMatch(service.GetInstanceName(), aKey->mName))
{
didFind = true;
VerifyOrQuit(StringMatch(service.GetName(), aKey->mServiceType));
}
}
VerifyOrQuit(didFind);
}
}
SuccessOrQuit(sDnssdUnregKeyRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
{
aCallback(aInstance, aRequestId, sDnssdCallbackError);
}
}
// Number of times we expect each `otPlatDnssd` request to be called
struct DnssdRequestCounts : public Clearable<DnssdRequestCounts>
{
DnssdRequestCounts(void) { Clear(); }
uint16_t mKeyReg;
uint16_t mHostReg;
uint16_t mServiceReg;
uint16_t mKeyUnreg;
uint16_t mHostUnreg;
uint16_t mServiceUnreg;
};
void VerifyDnnsdRequests(const DnssdRequestCounts &aRequestCounts, bool aAllowMoreUnregs = false)
{
VerifyOrQuit(sDnssdRegKeyRequests.GetLength() == aRequestCounts.mKeyReg);
VerifyOrQuit(sDnssdRegHostRequests.GetLength() == aRequestCounts.mHostReg);
VerifyOrQuit(sDnssdRegServiceRequests.GetLength() == aRequestCounts.mServiceReg);
if (aAllowMoreUnregs)
{
VerifyOrQuit(sDnssdUnregKeyRequests.GetLength() >= aRequestCounts.mKeyUnreg);
VerifyOrQuit(sDnssdUnregHostRequests.GetLength() >= aRequestCounts.mHostUnreg);
VerifyOrQuit(sDnssdUnregServiceRequests.GetLength() >= aRequestCounts.mServiceUnreg);
}
else
{
VerifyOrQuit(sDnssdUnregKeyRequests.GetLength() == aRequestCounts.mKeyUnreg);
VerifyOrQuit(sDnssdUnregHostRequests.GetLength() == aRequestCounts.mHostUnreg);
VerifyOrQuit(sDnssdUnregServiceRequests.GetLength() == aRequestCounts.mServiceUnreg);
}
}
//----------------------------------------------------------------------------------------------------------------------
Array<void *, 500> sHeapAllocatedPtrs;
#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
void *otPlatCAlloc(size_t aNum, size_t aSize)
{
void *ptr = calloc(aNum, aSize);
SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr));
return ptr;
}
void otPlatFree(void *aPtr)
{
if (aPtr != nullptr)
{
void **entry = sHeapAllocatedPtrs.Find(aPtr);
VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice");
sHeapAllocatedPtrs.Remove(*entry);
}
free(aPtr);
}
#endif
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
va_list args;
printf(" ");
va_start(args, aFormat);
vprintf(aFormat, args);
va_end(args);
printf("\n");
}
#endif
} // extern "C"
//---------------------------------------------------------------------------------------------------------------------
void ProcessRadioTxAndTasklets(void)
{
do
{
if (sRadioTxOngoing)
{
sRadioTxOngoing = false;
otPlatRadioTxStarted(sInstance, &sRadioTxFrame);
otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE);
}
otTaskletsProcess(sInstance);
} while (otTaskletsArePending(sInstance));
}
void AdvanceTime(uint32_t aDuration)
{
uint32_t time = sNow + aDuration;
Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000);
while (TimeMilli(sAlarmTime) <= TimeMilli(time))
{
ProcessRadioTxAndTasklets();
sNow = sAlarmTime;
otPlatAlarmMilliFired(sInstance);
}
ProcessRadioTxAndTasklets();
sNow = time;
}
void InitTest(void)
{
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialize OT instance.
sNow = 0;
sAlarmOn = false;
sInstance = static_cast<Instance *>(testInitInstance());
memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame));
sRadioTxFrame.mPsdu = sRadioTxFramePsdu;
sRadioTxOngoing = false;
sDnssdShouldCheckWithClient = true;
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdCallbackError = kErrorPending;
sDnssdRegHostRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregServiceRequests.Clear();
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialize Border Router and start Thread operation.
otOperationalDataset dataset;
otOperationalDatasetTlvs datasetTlvs;
SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset));
otDatasetConvertToTlvs(&dataset, &datasetTlvs);
SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
SuccessOrQuit(otIp6SetEnabled(sInstance, true));
SuccessOrQuit(otThreadSetEnabled(sInstance, true));
// Configure the `Dnssd` module to use `otPlatDnssd` APIs.
sInstance->Get<Dnssd>().SetUseNativeMdns(false);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ensure device starts as leader.
AdvanceTime(10000);
VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER);
}
void FinalizeTest(void)
{
SuccessOrQuit(otIp6SetEnabled(sInstance, false));
SuccessOrQuit(otThreadSetEnabled(sInstance, false));
SuccessOrQuit(otInstanceErasePersistentInfo(sInstance));
testFreeInstance(sInstance);
}
//---------------------------------------------------------------------------------------------------------------------
// SRP Client callback
static bool sProcessedClientCallback = false;
static Error sLastClientCallbackError = kErrorNone;
void HandleSrpClientCallback(otError aError,
const otSrpClientHostInfo *aHostInfo,
const otSrpClientService *aServices,
const otSrpClientService *aRemovedServices,
void *aContext)
{
Log("HandleSrpClientCallback() called with error %s", ErrorToString(aError));
VerifyOrQuit(aContext == sInstance);
sProcessedClientCallback = true;
sLastClientCallbackError = aError;
OT_UNUSED_VARIABLE(aHostInfo);
OT_UNUSED_VARIABLE(aServices);
OT_UNUSED_VARIABLE(aRemovedServices);
}
static const char kHostName[] = "awesomehost";
void PrepareService1(Srp::Client::Service &aService)
{
static const char kServiceName[] = "_srv1._udp";
static const char kInstanceLabel[] = "service1";
static const char kSub1[] = "_sub1";
static const char kSub2[] = "_sub2";
static const char kSub3[] = "_sub3";
static const char *kSubLabels[] = {kSub1, kSub2, kSub3, nullptr};
static const char kTxtKey1[] = "ABCD";
static const uint8_t kTxtValue1[] = {'a', '0'};
static const char kTxtKey2[] = "Z0";
static const uint8_t kTxtValue2[] = {'1', '2', '3'};
static const char kTxtKey3[] = "D";
static const uint8_t kTxtValue3[] = {0};
static const otDnsTxtEntry kTxtEntries[] = {
{kTxtKey1, kTxtValue1, sizeof(kTxtValue1)},
{kTxtKey2, kTxtValue2, sizeof(kTxtValue2)},
{kTxtKey3, kTxtValue3, sizeof(kTxtValue3)},
};
memset(&aService, 0, sizeof(aService));
aService.mName = kServiceName;
aService.mInstanceName = kInstanceLabel;
aService.mSubTypeLabels = kSubLabels;
aService.mTxtEntries = kTxtEntries;
aService.mNumTxtEntries = 3;
aService.mPort = 777;
aService.mWeight = 1;
aService.mPriority = 2;
}
void PrepareService2(Srp::Client::Service &aService)
{
static const char kService2Name[] = "_matter._udp";
static const char kInstance2Label[] = "service2";
static const char kSub4[] = "_44444444";
static const char *kSubLabels2[] = {kSub4, nullptr};
memset(&aService, 0, sizeof(aService));
aService.mName = kService2Name;
aService.mInstanceName = kInstance2Label;
aService.mSubTypeLabels = kSubLabels2;
aService.mTxtEntries = nullptr;
aService.mNumTxtEntries = 0;
aService.mPort = 555;
aService.mWeight = 0;
aService.mPriority = 3;
}
//----------------------------------------------------------------------------------------------------------------------
typedef Dnssd::RequestId RequestId;
typedef Dnssd::RequestIdRange RequestIdRange;
void ValidateRequestIdRange(const RequestIdRange &aIdRange, RequestId aStart, RequestId aEnd)
{
RequestId maxId = NumericLimits<RequestId>::kMax;
bool shouldContain = false;
VerifyOrQuit(!aIdRange.IsEmpty());
for (RequestId id = aStart - 5; id != aEnd + 6; id++)
{
// `idRange` should contain IDs within range `[aStart, aEnd]`
if (id == aStart)
{
shouldContain = true;
}
if (id == aEnd + 1)
{
shouldContain = false;
}
VerifyOrQuit(aIdRange.Contains(id) == shouldContain);
}
// Test values that half the range apart
for (RequestId id = aStart + maxId / 2 - 10; id != aEnd + maxId / 2 + 10; id++)
{
VerifyOrQuit(!aIdRange.Contains(id));
}
}
void TestDnssdRequestIdRange(void)
{
RequestId maxId = NumericLimits<RequestId>::kMax;
RequestIdRange idRange;
Log("--------------------------------------------------------------------------------------------");
Log("TestDnssdRequestIdRange");
VerifyOrQuit(idRange.IsEmpty());
idRange.Add(5);
ValidateRequestIdRange(idRange, 5, 5);
idRange.Remove(4);
ValidateRequestIdRange(idRange, 5, 5);
idRange.Remove(6);
ValidateRequestIdRange(idRange, 5, 5);
idRange.Remove(5);
VerifyOrQuit(idRange.IsEmpty());
VerifyOrQuit(!idRange.Contains(5));
// Adding and removing multiple IDs
idRange.Add(10);
idRange.Add(15);
ValidateRequestIdRange(idRange, 10, 15);
idRange.Add(12);
ValidateRequestIdRange(idRange, 10, 15);
idRange.Add(15);
ValidateRequestIdRange(idRange, 10, 15);
idRange.Add(10);
ValidateRequestIdRange(idRange, 10, 15);
idRange.Add(9);
ValidateRequestIdRange(idRange, 9, 15);
idRange.Add(16);
ValidateRequestIdRange(idRange, 9, 16);
idRange.Remove(10);
ValidateRequestIdRange(idRange, 9, 16);
idRange.Remove(15);
ValidateRequestIdRange(idRange, 9, 16);
idRange.Remove(8);
ValidateRequestIdRange(idRange, 9, 16);
idRange.Remove(17);
ValidateRequestIdRange(idRange, 9, 16);
idRange.Remove(9);
ValidateRequestIdRange(idRange, 10, 16);
idRange.Remove(16);
ValidateRequestIdRange(idRange, 10, 15);
idRange.Clear();
VerifyOrQuit(idRange.IsEmpty());
VerifyOrQuit(!idRange.Contains(10));
// Ranges close to roll-over max value
idRange.Add(maxId);
ValidateRequestIdRange(idRange, maxId, maxId);
idRange.Remove(0);
ValidateRequestIdRange(idRange, maxId, maxId);
idRange.Remove(maxId - 1);
ValidateRequestIdRange(idRange, maxId, maxId);
idRange.Add(0);
ValidateRequestIdRange(idRange, maxId, 0);
idRange.Add(maxId - 2);
ValidateRequestIdRange(idRange, maxId - 2, 0);
idRange.Add(3);
ValidateRequestIdRange(idRange, maxId - 2, 3);
idRange.Add(3);
ValidateRequestIdRange(idRange, maxId - 2, 3);
idRange.Remove(4);
ValidateRequestIdRange(idRange, maxId - 2, 3);
idRange.Remove(maxId - 3);
ValidateRequestIdRange(idRange, maxId - 2, 3);
idRange.Remove(3);
ValidateRequestIdRange(idRange, maxId - 2, 2);
idRange.Remove(maxId - 2);
ValidateRequestIdRange(idRange, maxId - 1, 2);
Log("End of TestDnssdRequestIdRange");
}
void TestSrpAdvProxy(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxy");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add an on-mesh prefix (with SLAAC) to network data");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorNone; // Invoke callback directly from dnssd APIs
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->SetLeaseInterval(180);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a service");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
dnssdCounts.mKeyReg += 2;
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a second service");
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
// This time we should only see the new service and its key being
// registered as the host is same as before and already registered
dnssdCounts.mKeyReg++;
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait for longer than lease interval for client to refresh");
sProcessedClientCallback = false;
AdvanceTime(181 * 1000);
VerifyOrQuit(sProcessedClientCallback);
// Validate that adv-proxy does not update any of registration on
// DNS-SD platform since there is no change.
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal >= 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == advProxy->GetCounters().mAdvTotal);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add a new on-mesh prefix so to get a new host address");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:abba::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
sProcessedClientCallback = false;
AdvanceTime(15 * 1000);
// This time we should only see new host registration
// since that's the only thing that changes
dnssdCounts.mHostReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove the first service on client");
SuccessOrQuit(srpClient->RemoveService(service1));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
// We should see the service being unregistered
// by advertising proxy on DNS-SD platform but its key
// remains registered.
dnssdCounts.mServiceUnreg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
// Wait for more than lease interval again and make sure
// there is no change in DNS-SD platform API calls.
sProcessedClientCallback = false;
AdvanceTime(181 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Change service 2 on client, remove its sub-type");
SuccessOrQuit(srpClient->ClearService(service2));
PrepareService2(service2);
service2.mSubTypeLabels = nullptr;
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
// Since the service is now changed, advertising proxy
// should update it (re-register it) on DNS-SD APIs.
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove the host on client");
SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ false));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
// We should see the host and service being unregistered
// on DNS-SD APIs but keys remain unchanged.
dnssdCounts.mHostUnreg++;
dnssdCounts.mServiceUnreg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
VerifyOrQuit(service2.GetState() == Srp::Client::kRemoved);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove the host on client again and force an update to be sent to server");
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ false, /* aSendUnregToServer */ true));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
// We should see no changes (no calls) to DNS-SD APIs.
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Re-add service 1 on client and register with server");
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
PrepareService1(service1);
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
// We should see one host register and one service register
// on DNS-SD APIs. Keys are already registered.
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
// Wait for more than lease interval again and make sure
// there is no change in DNS-SD platform API calls.
sProcessedClientCallback = false;
AdvanceTime(181 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP client and wait for lease time to expire");
srpClient->ClearHostAndServices(); // does not signal removal to server
// Since we clear everything on SRP client, we disable
// matching the services with client from `otPlatDnssd`
// APIs.
sDnssdShouldCheckWithClient = false;
AdvanceTime(181 * 1000);
// Make sure host and service are unregistered.
dnssdCounts.mHostUnreg++;
dnssdCounts.mServiceUnreg++;
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(!advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == advProxy->GetCounters().mAdvTotal);
VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvSkipped == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
dnssdCounts.mKeyUnreg += 3;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxy");
}
void TestSrpAdvProxyDnssdStateChange(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxyDnssdStateChange");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add an on-mesh prefix (with SLAAC) to network data");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_STOPPED;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorNone; // Invoke callback directly
VerifyOrQuit(!advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(!advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->SetLeaseInterval(180);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a services");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a second service");
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
// None of the DNS-SD APIs should be called since its state
// `OT_PLAT_DNSSD_STOPPED` (`dnssdCounts` is all zeros).
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Update DNS-SD state and signal that state is changed");
sDnssdState = OT_PLAT_DNSSD_READY;
otPlatDnssdStateHandleStateChange(sInstance);
AdvanceTime(5);
VerifyOrQuit(advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mStateChanges == 1);
// Now the host and two services should be registered on
// DNS-SD platform
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg += 2;
dnssdCounts.mKeyReg += 3;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait for longer than lease interval for client to refresh");
sProcessedClientCallback = false;
AdvanceTime(181 * 1000);
VerifyOrQuit(sProcessedClientCallback);
// Validate that adv-proxy does not update any of registration on
// DNS-SD platform since there is no change.
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Update DNS-SD state to `STOPPED` and signal its change");
sDnssdState = OT_PLAT_DNSSD_STOPPED;
otPlatDnssdStateHandleStateChange(sInstance);
AdvanceTime(5);
VerifyOrQuit(!advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mStateChanges == 2);
// Since DNS-SD platform signal that it is stopped,
// there should be no calls to any of APIs.
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait for longer than lease interval for client to refresh");
sProcessedClientCallback = false;
AdvanceTime(181 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
// The DNS-SD API counters should remain unchanged
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Update DNS-SD state to `READY` and signal its change");
sDnssdState = OT_PLAT_DNSSD_READY;
otPlatDnssdStateHandleStateChange(sInstance);
AdvanceTime(5);
VerifyOrQuit(advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mStateChanges == 3);
// Check that the host and two services are again registered
// on DNS-SD platform by advertising proxy.
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg += 2;
dnssdCounts.mKeyReg += 3;
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Update DNS-SD state to `STOPPED` and signal its change");
sDnssdState = OT_PLAT_DNSSD_STOPPED;
otPlatDnssdStateHandleStateChange(sInstance);
AdvanceTime(5);
VerifyOrQuit(!advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mStateChanges == 4);
// Since DNS-SD platform signal that it is stopped,
// there should be no calls to any of APIs.
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove the first service on client");
SuccessOrQuit(srpClient->RemoveService(service1));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
// No changes to DNS-SD API counters (since it is stopped)
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Update DNS-SD state to `READY` and signal its change #2");
// Since the already removed `service1` is no longer available
// on SRP client, we disable checking the services with client
// from `otPlatDnssd` APIs.
sDnssdShouldCheckWithClient = false;
sDnssdState = OT_PLAT_DNSSD_READY;
otPlatDnssdStateHandleStateChange(sInstance);
AdvanceTime(5);
VerifyOrQuit(advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mStateChanges == 5);
// We should see the host and `service2` registered again.
// And all 3 keys (even for removed `service1`) to be
// registered.
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg += 3;
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(!advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mStateChanges == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvSkipped > 0);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal ==
(advProxy->GetCounters().mAdvSuccessful + advProxy->GetCounters().mAdvSkipped));
VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
dnssdCounts.mHostUnreg++;
dnssdCounts.mServiceUnreg++;
dnssdCounts.mKeyUnreg += 3;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxyDnssdStateChange");
}
void TestSrpAdvProxyDelayedCallback(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
const DnssdRequest *request;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxyDelayedCallback");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add an on-mesh prefix (with SLAAC) to network data");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorPending; // Do not call the callbacks directly
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->SetLeaseInterval(180);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a service, invoke the registration callback after some delay");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(1000);
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg += 2;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(!sProcessedClientCallback);
VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr);
// Invoke the service and key callbacks first
request = &sDnssdRegServiceRequests[0];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
request = &sDnssdRegKeyRequests[0];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
request = &sDnssdRegKeyRequests[1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(10);
VerifyOrQuit(!sProcessedClientCallback);
VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr);
// Invoke the host registration callback next
request = &sDnssdRegHostRequests[0];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(10);
VerifyOrQuit(srpServer->GetNextHost(nullptr) != nullptr);
AdvanceTime(100);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a second service, invoke registration callback with `kErrorDuplicated`");
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1000);
VerifyOrQuit(!sProcessedClientCallback);
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
// Invoke the service callback with kErrorDuplicated
request = &sDnssdRegServiceRequests[1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorDuplicated);
AdvanceTime(100);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorDuplicated);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Try registering service again from client, invoke callback with success");
SuccessOrQuit(srpClient->ClearService(service2));
PrepareService2(service2);
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1000);
VerifyOrQuit(!sProcessedClientCallback);
// We should see a new service registration request.
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 1);
// Invoked the service and key callback with success.
request = &sDnssdRegKeyRequests[sDnssdRegKeyRequests.GetLength() - 1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
request = &sDnssdRegServiceRequests[sDnssdRegServiceRequests.GetLength() - 1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(100);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Change the service and register again, but ignore the registration callback");
SuccessOrQuit(srpClient->ClearService(service2));
PrepareService2(service2);
service2.mSubTypeLabels = nullptr;
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1000);
VerifyOrQuit(!sProcessedClientCallback);
// We should see a new service registration request.
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
// Wait for advertising proxy timeout (there will be no callback from
// platform) so validate that registration failure is reported to
// the SRP client.
AdvanceTime(2 * 1000);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError != kErrorNone);
VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 1);
// Wait for longer than client retry time.
AdvanceTime(3 * 1000);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
// Make sure the host and two services are unregistered
// (even though the second service was not successfully
// registered yet).
VerifyOrQuit(sDnssdRegHostRequests.GetLength() == 1);
VerifyOrQuit(sDnssdRegServiceRequests.GetLength() >= 4);
VerifyOrQuit(sDnssdRegKeyRequests.GetLength() >= 3);
VerifyOrQuit(sDnssdUnregHostRequests.GetLength() == 1);
VerifyOrQuit(sDnssdUnregServiceRequests.GetLength() == 2);
VerifyOrQuit(sDnssdUnregKeyRequests.GetLength() == 3);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxyDelayedCallback");
}
void TestSrpAdvProxyReplacedEntries(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
const DnssdRequest *request;
uint16_t numServices;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxyReplacedEntries");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add an on-mesh prefix (with SLAAC) to network data");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorPending; // Do not call the callbacks directly
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set AdvTimeout to 5 minutes on AdvProxy");
// Change the timeout on AvdertisingProxy to 5 minutes
// so that we can send multiple SRP updates and create
// situations where previous advertisement are replaced.
advProxy->SetAdvTimeout(5 * 60 * 1000);
VerifyOrQuit(advProxy->GetAdvTimeout() == 5 * 60 * 1000);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a service and do not invoke the registration request callbacks");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(1200);
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg += 2;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
VerifyOrQuit(!sProcessedClientCallback);
VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr);
// SRP client min retry is 1800 msec, we wait for longer
// to make sure client retries.
AdvanceTime(2000);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
// We should see no new service or host registrations on
// DNS-SD platform APIs as the requests should be same
// and fully matching the outstanding ones.
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke the DNS-SD API callbacks");
request = &sDnssdRegServiceRequests[0];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
request = &sDnssdRegHostRequests[0];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
for (uint16_t index = 0; index < 2; index++)
{
request = &sDnssdRegKeyRequests[index];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
}
AdvanceTime(100);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(srpServer->GetNextHost(nullptr) != nullptr);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Check outstanding Adv being replaced");
// Change service 1
SuccessOrQuit(srpClient->ClearService(service1));
PrepareService1(service1);
service1.mSubTypeLabels = nullptr; // No sub-types
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(1200);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
// We should see the changed service registered on DNS-SD
// platform APIs.
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
// Change service 1 again (add sub-types back).
SuccessOrQuit(srpClient->ClearService(service1));
PrepareService1(service1);
SuccessOrQuit(srpClient->AddService(service1));
AdvanceTime(1200);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
// We should see the changed service registered on DNS-SD
// platform APIs again.
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke the replaced entry DNS-SD API callback");
request = &sDnssdRegServiceRequests[1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(100);
// Since adv is replaced invoking the old registration callback
// should not complete it.
VerifyOrQuit(!sProcessedClientCallback);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke the new entry DNS-SD API callback");
request = &sDnssdRegServiceRequests[2];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(100);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
// Make sure the service entry on the SRP server is the
// last (most recent) request with three sub-types
VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead() != nullptr);
VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead()->GetNumberOfSubTypes() == 3);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Check replacing Adv being blocked till old Adv is completed");
// Change service 1 and add service 2
SuccessOrQuit(srpClient->ClearService(service1));
PrepareService1(service1);
service1.mSubTypeLabels = nullptr; // No sub-types
SuccessOrQuit(srpClient->AddService(service1));
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1200);
// We should see a new Adv with two new service registrations
// on DNS-SD APIs.
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 5);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
dnssdCounts.mServiceReg += 2;
dnssdCounts.mKeyReg++;
VerifyDnnsdRequests(dnssdCounts);
// Invoke the key registration callback
request = &sDnssdRegKeyRequests[sDnssdRegKeyRequests.GetLength() - 1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
// Now have SRP client send a new SRP update message
// just changing `service2`. We clear `servcie1` on client
// so it is not included in new SRP update message.
SuccessOrQuit(srpClient->ClearService(service1));
SuccessOrQuit(srpClient->ClearService(service2));
PrepareService2(service2);
service2.mPort = 2222; // Use a different port number
SuccessOrQuit(srpClient->AddService(service2));
AdvanceTime(1200);
// We should see the new Adv (total increasing) and
// also replacing the outstanding one
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
// We should see new registration for the changed `service2`
dnssdCounts.mServiceReg++;
VerifyDnnsdRequests(dnssdCounts);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke the callback for new registration replacing old one first");
request = &sDnssdRegServiceRequests[5];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(100);
// This should not change anything, since the new Avd should
// be still blocked by the earlier Adv that it replaced.
VerifyOrQuit(!sProcessedClientCallback);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke the callback for replaced Adv services");
request = &sDnssdRegServiceRequests[4];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
request = &sDnssdRegServiceRequests[3];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(100);
// This should trigger both Adv to complete.
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
// Make sure the `service2` entry on the SRP server is the
// last (most recent) request with new port number.
VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead() != nullptr);
numServices = 0;
for (const Srp::Server::Service &service : srpServer->GetNextHost(nullptr)->GetServices())
{
numServices++;
if (StringMatch(service.GetInstanceLabel(), service2.GetInstanceName(), kStringCaseInsensitiveMatch))
{
VerifyOrQuit(service.GetPort() == service2.GetPort());
}
else if (StringMatch(service.GetInstanceLabel(), service1.GetInstanceName(), kStringCaseInsensitiveMatch))
{
// Service 1 was changed to have no sub-types
VerifyOrQuit(service.GetPort() == service1.GetPort());
VerifyOrQuit(service.GetNumberOfSubTypes() == 0);
}
else
{
VerifyOrQuit(false); // Unexpected extra service on SRP server.
}
}
VerifyOrQuit(numServices == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Check replacing Adv being blocked till old Adv is completed when removing services");
// Change and re-add both services so they are both
// included in a new SRP update message from client.
SuccessOrQuit(srpClient->ClearService(service2));
PrepareService1(service1);
PrepareService2(service2);
SuccessOrQuit(srpClient->AddService(service1));
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1200);
// We should see a new Adv with two new service registrations
// on DNS-SD APIs.
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 7);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
dnssdCounts.mServiceReg += 2;
VerifyDnnsdRequests(dnssdCounts);
// Now have SRP client send a new SRP update message
// just removing `service1`. We clear `servcie2` on client
// so it is not included in new SRP update message.
SuccessOrQuit(srpClient->RemoveService(service1));
SuccessOrQuit(srpClient->ClearService(service2));
AdvanceTime(1200);
// We should see a new Adv added replacing the outstanding
// one.
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 8);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 6);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 3);
dnssdCounts.mServiceUnreg++;
VerifyDnnsdRequests(dnssdCounts);
// Even though the new SRP update which removed `servcie2`
// is already unregistered, it should be blocked by the
// earlier Adv.
VerifyOrQuit(!sProcessedClientCallback);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke the callback for replaced Adv services");
request = &sDnssdRegServiceRequests[6];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
request = &sDnssdRegServiceRequests[7];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(100);
// This should trigger both Adv to complete, and first one
// should be committed before the second one removing the
// `service2`.
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 8);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 8);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 3);
VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
// Check services on server and make sure `service2`
// is marked as deleted.
VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead() != nullptr);
numServices = 0;
for (const Srp::Server::Service &service : srpServer->GetNextHost(nullptr)->GetServices())
{
numServices++;
if (StringMatch(service.GetInstanceLabel(), service1.GetInstanceName(), kStringCaseInsensitiveMatch))
{
VerifyOrQuit(service.IsDeleted());
}
else if (StringMatch(service.GetInstanceLabel(), service2.GetInstanceName(), kStringCaseInsensitiveMatch))
{
VerifyOrQuit(!service.IsDeleted());
}
else
{
VerifyOrQuit(false); // Unexpected extra service on SRP server.
}
}
VerifyOrQuit(numServices == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
sDnssdShouldCheckWithClient = false;
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxyReplacedEntries");
}
void TestSrpAdvProxyHostWithOffMeshRoutableAddress(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
const DnssdRequest *request;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxyHostWithOffMeshRoutableAddress");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorNone; // Invoke callback directly from dnssd APIs
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->SetLeaseInterval(400);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a service");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg += 2;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sDnssdNumHostAddresses == 0);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register a second service");
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(2 * 1000);
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(!advProxy->IsRunning());
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == advProxy->GetCounters().mAdvTotal);
VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvSkipped == 0);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxyHostWithOffMeshRoutableAddress");
}
void TestSrpAdvProxyRemoveBeforeCommitted(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
const DnssdRequest *request;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxyRemoveBeforeCommitted");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add an on-mesh prefix (with SLAAC) to network data");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorNone; // Do not call the callbacks directly
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register host and one service");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(2000);
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg += 2;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set AdvTimeout to 5 minutes on AdvProxy");
// Change the timeout on AvdertisingProxy to 5 minutes
// so that we can send multiple SRP updates and create
// situations where previous advertisement are replaced.
advProxy->SetAdvTimeout(5 * 60 * 1000);
VerifyOrQuit(advProxy->GetAdvTimeout() == 5 * 60 * 1000);
sDnssdCallbackError = kErrorPending; // Do not invoke callback
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove service1 while adding a new service2 and do not invoke callback from DNSSD plat");
SuccessOrQuit(srpClient->RemoveService(service1));
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1000);
dnssdCounts.mServiceReg++;
dnssdCounts.mServiceUnreg++;
dnssdCounts.mKeyReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
VerifyOrQuit(!sProcessedClientCallback);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove host and its services without removing key-lease");
SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ false));
AdvanceTime(1000);
// Proxy will unregister both services again
// (to be safe).
dnssdCounts.mHostUnreg++;
dnssdCounts.mServiceUnreg++;
VerifyDnnsdRequests(dnssdCounts, /* aAllowMoreUnregs */ true);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
VerifyOrQuit(!sProcessedClientCallback);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Invoke callback for last key registration");
// This should be enough for all `AdvInfo` entries to be finished.
request = &sDnssdRegKeyRequests[sDnssdRegKeyRequests.GetLength() - 1];
VerifyOrQuit(request->mCallback != nullptr);
request->mCallback(sInstance, request->mId, kErrorNone);
AdvanceTime(50);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
VerifyOrQuit(sProcessedClientCallback);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
sDnssdShouldCheckWithClient = false;
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxyRemoveBeforeCommitted");
}
void TestSrpAdvProxyFullyRemoveBeforeCommitted(void)
{
NetworkData::OnMeshPrefixConfig prefixConfig;
Srp::Server *srpServer;
Srp::Client *srpClient;
Srp::AdvertisingProxy *advProxy;
Srp::Client::Service service1;
Srp::Client::Service service2;
DnssdRequestCounts dnssdCounts;
uint16_t heapAllocations;
const DnssdRequest *request;
Log("--------------------------------------------------------------------------------------------");
Log("TestSrpAdvProxyFullyRemoveBeforeCommitted");
InitTest();
srpServer = &sInstance->Get<Srp::Server>();
srpClient = &sInstance->Get<Srp::Client>();
advProxy = &sInstance->Get<Srp::AdvertisingProxy>();
heapAllocations = sHeapAllocatedPtrs.GetLength();
PrepareService1(service1);
PrepareService2(service2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Add an on-mesh prefix (with SLAAC) to network data");
prefixConfig.Clear();
SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
prefixConfig.mPrefix.mLength = 64;
prefixConfig.mStable = true;
prefixConfig.mSlaac = true;
prefixConfig.mPreferred = true;
prefixConfig.mOnMesh = true;
prefixConfig.mDefaultRoute = false;
prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium;
SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
SuccessOrQuit(otBorderRouterRegister(sInstance));
// Configure Dnssd platform API behavior
sDnssdRegHostRequests.Clear();
sDnssdRegServiceRequests.Clear();
sDnssdUnregHostRequests.Clear();
sDnssdUnregServiceRequests.Clear();
sDnssdRegKeyRequests.Clear();
sDnssdUnregKeyRequests.Clear();
sDnssdState = OT_PLAT_DNSSD_READY;
sDnssdShouldCheckWithClient = true;
sDnssdCallbackError = kErrorNone; // Do not call the callbacks directly
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP server");
SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
srpServer->SetServiceHandler(nullptr, sInstance);
srpServer->SetEnabled(true);
VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
AdvanceTime(10000);
VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
VerifyOrQuit(advProxy->IsRunning());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start SRP client");
srpClient->SetCallback(HandleSrpClientCallback, sInstance);
srpClient->EnableAutoStartMode(nullptr, nullptr);
VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
AdvanceTime(2000);
VerifyOrQuit(srpClient->IsRunning());
SuccessOrQuit(srpClient->SetHostName(kHostName));
SuccessOrQuit(srpClient->EnableAutoHostAddress());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Register host and one service");
SuccessOrQuit(srpClient->AddService(service1));
sProcessedClientCallback = false;
AdvanceTime(2000);
dnssdCounts.mHostReg++;
dnssdCounts.mServiceReg++;
dnssdCounts.mKeyReg += 2;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
VerifyOrQuit(sProcessedClientCallback);
VerifyOrQuit(sLastClientCallbackError == kErrorNone);
VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Set AdvTimeout to 5 minutes on AdvProxy");
// Change the timeout on AvdertisingProxy to 5 minutes
// so that we can send multiple SRP updates and create
// situations where previous advertisement are replaced.
advProxy->SetAdvTimeout(5 * 60 * 1000);
VerifyOrQuit(advProxy->GetAdvTimeout() == 5 * 60 * 1000);
sDnssdCallbackError = kErrorPending; // Do not invoke callback
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove service1 while adding a new service2 and do not invoke callback from DNSSD plat");
SuccessOrQuit(srpClient->RemoveService(service1));
SuccessOrQuit(srpClient->AddService(service2));
sProcessedClientCallback = false;
AdvanceTime(1000);
dnssdCounts.mServiceReg++;
dnssdCounts.mServiceUnreg++;
dnssdCounts.mKeyReg++;
VerifyDnnsdRequests(dnssdCounts);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
VerifyOrQuit(!sProcessedClientCallback);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Remove host and its services and remove key-lease");
SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ true));
AdvanceTime(1000);
// Proxy should unregister everything.
// Keys may be unregistered multiple times.
dnssdCounts.mHostUnreg++;
dnssdCounts.mServiceUnreg++;
dnssdCounts.mKeyUnreg += 3;
VerifyDnnsdRequests(dnssdCounts, /* aAllowMoreUnregs */ true);
VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 3);
VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
VerifyOrQuit(sProcessedClientCallback);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Disable SRP server");
sDnssdShouldCheckWithClient = false;
// Verify that all heap allocations by SRP server
// and Advertising Proxy are freed.
srpServer->SetEnabled(false);
AdvanceTime(100);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finalize OT instance and validate all heap allocations are freed");
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
Log("End of TestSrpAdvProxyFullyRemoveBeforeCommitted");
}
#endif // ENABLE_ADV_PROXY_TEST
int main(void)
{
#if ENABLE_ADV_PROXY_TEST
TestDnssdRequestIdRange();
TestSrpAdvProxy();
TestSrpAdvProxyDnssdStateChange();
TestSrpAdvProxyDelayedCallback();
TestSrpAdvProxyReplacedEntries();
TestSrpAdvProxyHostWithOffMeshRoutableAddress();
TestSrpAdvProxyRemoveBeforeCommitted();
TestSrpAdvProxyFullyRemoveBeforeCommitted();
printf("All tests passed\n");
#else
printf("SRP_ADV_PROXY feature is not enabled\n");
#endif
return 0;
}