mirror of
https://github.com/espressif/openthread.git
synced 2026-08-29 05:19:54 +00:00
[mdns] implement response aggregation (#10881)
This commit implements response aggregation (RFC 6762 section 6.4) in the mDNS module. If multiple responses are scheduled to be sent, each delayed by a different interval, earlier responses are further delayed to allow aggregation with other responses scheduled to go out a little later. Before preparing a response, we determine the next multicast transmission time that is explicitly after the current time. This is used for response aggregation. As the response is being prepared, different entries can decide whether to extend their answer delay duration if allowed (e.g., probe question answer delay cannot be extended) and possible (not extending the delay beyond the maximum allowed delay). To realize this, the way we track the `AnswerTime` of records is changed. It is now tracked by two variables: `mQueryRxTime` (the receive time of the question triggering the answer) and `mAnswerDelay`. This commit also adds `TestResponseAggregation()` to the `test_mdns` unit test to validate the response aggregation behavior.
This commit is contained in:
@@ -53,6 +53,14 @@ NextFireTime::NextFireTime(Time aNow)
|
||||
|
||||
void NextFireTime::UpdateIfEarlier(Time aTime) { mNextTime = Min(mNextTime, Max(mNow, aTime)); }
|
||||
|
||||
void NextFireTime::UpdateIfEarlierAndInFuture(Time aTime)
|
||||
{
|
||||
if (aTime > mNow)
|
||||
{
|
||||
mNextTime = Min(mNextTime, aTime);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// `Timer`
|
||||
|
||||
|
||||
@@ -95,6 +95,21 @@ public:
|
||||
*/
|
||||
void UpdateIfEarlier(Time aTime);
|
||||
|
||||
/**
|
||||
* Updates the tracked next fire time with a new given time, but only if it is earlier than the current
|
||||
* fire time and in the future relative to `GetNow()`.
|
||||
*
|
||||
* If the given @p aTime is not in the future relative to `GetNow()`, it is ignored. This is unlike
|
||||
* `UpdateIfEarlier()`, which allows all `aTime` values, including ones that are in the past (where it uses
|
||||
* `GetNow()`).
|
||||
*
|
||||
* This method can be used to track the next fire time among non-expired times, ensuring the tracked next fire time
|
||||
* will be in the future relative to `GetNow()`.
|
||||
*
|
||||
* @param[in] aTime The new time.
|
||||
*/
|
||||
void UpdateIfEarlierAndInFuture(Time aTime);
|
||||
|
||||
/**
|
||||
* Indicates whether or not next fire time is set.
|
||||
*
|
||||
|
||||
+182
-25
@@ -303,6 +303,31 @@ exit:
|
||||
void Core::HandleEntryTimer(void)
|
||||
{
|
||||
EntryContext context(GetInstance(), TxMessage::kMulticastResponse);
|
||||
NextFireTime nextAggrTxTime(context.GetNow());
|
||||
|
||||
// Determine the next multicast transmission time that is explicitly
|
||||
// after `GetNow()` to set `mNextAggrTxTime`. This is used for
|
||||
// response aggregation. As `HandleTimer()` is called on different
|
||||
// entries, they can decide to extend their answer delay to the
|
||||
// determined `mNextAggrTxTime` so that all answers are included in
|
||||
// the same response message.
|
||||
|
||||
for (HostEntry &entry : mHostEntries)
|
||||
{
|
||||
entry.DetermineNextAggrTxTime(nextAggrTxTime);
|
||||
}
|
||||
|
||||
for (ServiceEntry &entry : mServiceEntries)
|
||||
{
|
||||
entry.DetermineNextAggrTxTime(nextAggrTxTime);
|
||||
}
|
||||
|
||||
for (ServiceType &serviceType : mServiceTypes)
|
||||
{
|
||||
serviceType.DetermineNextAggrTxTime(nextAggrTxTime);
|
||||
}
|
||||
|
||||
context.mNextAggrTxTime = nextAggrTxTime.GetNextTime();
|
||||
|
||||
// We process host entries before service entries. This order
|
||||
// ensures we can determine whether host addresses have already
|
||||
@@ -613,22 +638,35 @@ void Core::RecordInfo::ScheduleAnswer(const AnswerInfo &aInfo)
|
||||
// that did not receive and cache the previous transmission will
|
||||
// retry its request.
|
||||
|
||||
VerifyOrExit(GetDurationSinceLastMulticast(aInfo.mAnswerTime) >= kMinIntervalBetweenMulticast);
|
||||
VerifyOrExit(GetDurationSinceLastMulticast(aInfo.GetAnswerTime()) >= kMinIntervalBetweenMulticast);
|
||||
}
|
||||
|
||||
if (mMulticastAnswerPending)
|
||||
{
|
||||
VerifyOrExit(aInfo.mAnswerTime < mAnswerTime);
|
||||
}
|
||||
TimeMilli targetAnswerTime;
|
||||
|
||||
mMulticastAnswerPending = true;
|
||||
mAnswerTime = aInfo.mAnswerTime;
|
||||
if (mCanExtendAnswerDelay && aInfo.mIsProbe)
|
||||
{
|
||||
mCanExtendAnswerDelay = false;
|
||||
}
|
||||
|
||||
targetAnswerTime = Min(aInfo.GetAnswerTime(), GetAnswerTime());
|
||||
mQueryRxTime = Min(aInfo.mQueryRxTime, mQueryRxTime);
|
||||
mAnswerDelay = targetAnswerTime - mQueryRxTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMulticastAnswerPending = true;
|
||||
mCanExtendAnswerDelay = !aInfo.mIsProbe;
|
||||
mQueryRxTime = aInfo.mQueryRxTime;
|
||||
mAnswerDelay = aInfo.mAnswerDelay;
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool Core::RecordInfo::ShouldAppendTo(EntryContext &aContext) const
|
||||
bool Core::RecordInfo::ShouldAppendTo(EntryContext &aContext)
|
||||
{
|
||||
bool shouldAppend = false;
|
||||
|
||||
@@ -644,7 +682,20 @@ bool Core::RecordInfo::ShouldAppendTo(EntryContext &aContext) const
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
shouldAppend = mMulticastAnswerPending && (mAnswerTime <= aContext.GetNow());
|
||||
if (mMulticastAnswerPending && (GetAnswerTime() <= aContext.GetNow()))
|
||||
{
|
||||
// Check if we can delay the answer further so that it can
|
||||
// be aggregated with other responses scheduled to go out a
|
||||
// little later.
|
||||
|
||||
if (ExtendAnswerDelay(aContext) == kErrorNone)
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
shouldAppend = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TxMessage::kUnicastResponse:
|
||||
@@ -660,6 +711,36 @@ exit:
|
||||
return shouldAppend;
|
||||
}
|
||||
|
||||
Error Core::RecordInfo::ExtendAnswerDelay(EntryContext &aContext)
|
||||
{
|
||||
Error error = kErrorFailed;
|
||||
|
||||
// Extend the answer delay for response aggregation when possible.
|
||||
//
|
||||
// This method is called when we have a pending multicast answer
|
||||
// (`mMulticastAnswerPending`) and the answer time has already
|
||||
// expired. We first check if the answer can be delayed (e.g., it
|
||||
// is not allowed for probe responses) and that there is an
|
||||
// upcoming `mNextAggrTxTime` within a short window of time from
|
||||
// `GetNow()`, before extending the delay. We ensure that the
|
||||
// overall answer delay does not exceed
|
||||
// `kResponseAggregationMaxDelay`.
|
||||
|
||||
VerifyOrExit(mCanExtendAnswerDelay);
|
||||
|
||||
VerifyOrExit(aContext.mNextAggrTxTime != aContext.GetNow().GetDistantFuture());
|
||||
VerifyOrExit(aContext.mNextAggrTxTime - aContext.GetNow() < kResponseAggregationMaxDelay);
|
||||
|
||||
VerifyOrExit(aContext.mNextAggrTxTime - mQueryRxTime < kResponseAggregationMaxDelay);
|
||||
|
||||
mAnswerDelay = aContext.mNextAggrTxTime - mQueryRxTime;
|
||||
|
||||
error = kErrorNone;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void Core::RecordInfo::UpdateStateAfterAnswer(const TxMessage &aResponse)
|
||||
{
|
||||
// Updates the state after a unicast or multicast response is
|
||||
@@ -720,7 +801,7 @@ void Core::RecordInfo::UpdateFireTimeOn(FireTime &aFireTime)
|
||||
|
||||
if (mMulticastAnswerPending)
|
||||
{
|
||||
aFireTime.SetFireTime(mAnswerTime);
|
||||
aFireTime.SetFireTime(GetAnswerTime());
|
||||
}
|
||||
|
||||
if (mIsLastMulticastValid)
|
||||
@@ -748,6 +829,24 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::RecordInfo::DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const
|
||||
{
|
||||
VerifyOrExit(mIsPresent);
|
||||
|
||||
if (mAnnounceCounter < kNumberOfAnnounces)
|
||||
{
|
||||
aNextAggrTxTime.UpdateIfEarlierAndInFuture(mAnnounceTime);
|
||||
}
|
||||
|
||||
if (mMulticastAnswerPending)
|
||||
{
|
||||
aNextAggrTxTime.UpdateIfEarlierAndInFuture(GetAnswerTime());
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::RecordInfo::MarkAsAppended(TxMessage &aTxMessage, Section aSection)
|
||||
{
|
||||
mAppendSection = aSection;
|
||||
@@ -1070,18 +1169,27 @@ void Core::Entry::ScheduleNsecAnswer(const AnswerInfo &aInfo)
|
||||
{
|
||||
if (mMulticastNsecPending)
|
||||
{
|
||||
VerifyOrExit(aInfo.mAnswerTime < mNsecAnswerTime);
|
||||
}
|
||||
TimeMilli targetAnswerTime = Min(aInfo.GetAnswerTime(), GetNsecAnswerTime());
|
||||
|
||||
mMulticastNsecPending = true;
|
||||
mNsecAnswerTime = aInfo.mAnswerTime;
|
||||
mNsecQueryRxTime = Min(aInfo.mQueryRxTime, mNsecQueryRxTime);
|
||||
mNsecAnswerDelay = targetAnswerTime - mNsecQueryRxTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMulticastNsecPending = true;
|
||||
mNsecQueryRxTime = aInfo.mQueryRxTime;
|
||||
mNsecAnswerDelay = aInfo.mAnswerDelay;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool Core::Entry::ShouldAnswerNsec(TimeMilli aNow) const { return mMulticastNsecPending && (mNsecAnswerTime <= aNow); }
|
||||
bool Core::Entry::ShouldAnswerNsec(TimeMilli aNow) const
|
||||
{
|
||||
return mMulticastNsecPending && (GetNsecAnswerTime() <= aNow);
|
||||
}
|
||||
|
||||
void Core::Entry::AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength)
|
||||
{
|
||||
@@ -1127,7 +1235,7 @@ void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords,
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
AnswerInfo info = aInfo;
|
||||
|
||||
info.mAnswerTime = now;
|
||||
info.mAnswerDelay = 0;
|
||||
|
||||
OT_ASSERT(info.mIsProbe);
|
||||
|
||||
@@ -1157,7 +1265,8 @@ void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords,
|
||||
}
|
||||
else if (record.GetLastMulticastTime(lastMulticastTime) == kErrorNone)
|
||||
{
|
||||
info.mAnswerTime = Max(info.mAnswerTime, lastMulticastTime + kMinIntervalProbeResponse);
|
||||
info.mAnswerDelay =
|
||||
Max(info.GetAnswerTime(), lastMulticastTime + kMinIntervalProbeResponse) - info.mQueryRxTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1175,7 +1284,7 @@ void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords,
|
||||
|
||||
if (!shouldDelay)
|
||||
{
|
||||
info.mAnswerTime = now;
|
||||
info.mAnswerDelay = 0;
|
||||
}
|
||||
|
||||
for (uint16_t index = 0; index < aRecordsLength; index++)
|
||||
@@ -1193,7 +1302,17 @@ void Core::Entry::DetermineNextFireTime(void)
|
||||
|
||||
if (mMulticastNsecPending)
|
||||
{
|
||||
SetFireTime(mNsecAnswerTime);
|
||||
SetFireTime(GetNsecAnswerTime());
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Entry::DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const
|
||||
{
|
||||
mKeyRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
|
||||
if (mMulticastNsecPending)
|
||||
{
|
||||
aNextAggrTxTime.UpdateIfEarlierAndInFuture(GetNsecAnswerTime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,6 +1352,7 @@ template <typename EntryType> void Core::Entry::HandleTimer(EntryContext &aConte
|
||||
case kRemoving:
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
thisAsEntryType->DetermineNextFireTime();
|
||||
|
||||
exit:
|
||||
@@ -1610,6 +1730,17 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::HostEntry::DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const
|
||||
{
|
||||
VerifyOrExit(GetState() == kRegistered);
|
||||
|
||||
Entry::DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
mAddrRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSection)
|
||||
{
|
||||
Message *message;
|
||||
@@ -2265,6 +2396,25 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::ServiceEntry::DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const
|
||||
{
|
||||
VerifyOrExit(GetState() == kRegistered);
|
||||
|
||||
Entry::DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
|
||||
mPtrRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
mSrvRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
mTxtRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
|
||||
for (const SubType &subType : mSubTypes)
|
||||
{
|
||||
subType.mPtrRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::ServiceEntry::DiscoverOffsetsAndHost(HostEntry *&aHostEntry)
|
||||
{
|
||||
// Discovers the `HostEntry` associated with this `ServiceEntry`
|
||||
@@ -2824,6 +2974,11 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::ServiceType::DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const
|
||||
{
|
||||
mServicesPtr.DetermineNextAggrTxTime(aNextAggrTxTime);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Core::TxMessage
|
||||
|
||||
@@ -3304,6 +3459,7 @@ Core::EntryContext::EntryContext(Instance &aInstance, TxMessage::Type aResponseT
|
||||
: mProbeMessage(aInstance, TxMessage::kMulticastProbe)
|
||||
, mResponseMessage(aInstance, aResponseType)
|
||||
{
|
||||
mNextAggrTxTime = mNextFireTime.GetNow().GetDistantFuture();
|
||||
}
|
||||
|
||||
Core::EntryContext::EntryContext(Instance &aInstance,
|
||||
@@ -3313,6 +3469,7 @@ Core::EntryContext::EntryContext(Instance &aInstance,
|
||||
: mProbeMessage(aInstance, TxMessage::kMulticastProbe)
|
||||
, mResponseMessage(aInstance, aResponseType, aDest, aQueryId)
|
||||
{
|
||||
mNextAggrTxTime = mNextFireTime.GetNow().GetDistantFuture();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -3332,7 +3489,8 @@ Error Core::RxMessage::Init(Instance &aInstance,
|
||||
|
||||
InstanceLocatorInit::Init(aInstance);
|
||||
|
||||
mNext = nullptr;
|
||||
mNext = nullptr;
|
||||
mRxTime = TimerMilli::GetNow();
|
||||
|
||||
VerifyOrExit(!aMessagePtr.IsNull(), error = kErrorInvalidArgs);
|
||||
|
||||
@@ -3464,7 +3622,7 @@ Core::RxMessage::ProcessOutcome Core::RxMessage::ProcessQuery(bool aShouldProces
|
||||
bool shouldDelay = false;
|
||||
bool canAnswer = false;
|
||||
bool needUnicastResponse = false;
|
||||
TimeMilli answerTime;
|
||||
uint16_t delay = 0;
|
||||
|
||||
for (Question &question : mQuestions)
|
||||
{
|
||||
@@ -3506,16 +3664,14 @@ Core::RxMessage::ProcessOutcome Core::RxMessage::ProcessQuery(bool aShouldProces
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
answerTime = TimerMilli::GetNow();
|
||||
|
||||
if (shouldDelay)
|
||||
{
|
||||
answerTime += Random::NonCrypto::GetUint32InRange(kMinResponseDelay, kMaxResponseDelay);
|
||||
delay = Random::NonCrypto::GetUint32InRange(kMinResponseDelay, kMaxResponseDelay);
|
||||
}
|
||||
|
||||
for (const Question &question : mQuestions)
|
||||
{
|
||||
AnswerQuestion(question, answerTime);
|
||||
AnswerQuestion(question, delay);
|
||||
}
|
||||
|
||||
if (needUnicastResponse)
|
||||
@@ -3625,7 +3781,7 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Core::RxMessage::AnswerQuestion(const Question &aQuestion, TimeMilli aAnswerTime)
|
||||
void Core::RxMessage::AnswerQuestion(const Question &aQuestion, uint16_t aDelay)
|
||||
{
|
||||
HostEntry *hostEntry;
|
||||
ServiceEntry *serviceEntry;
|
||||
@@ -3634,7 +3790,8 @@ void Core::RxMessage::AnswerQuestion(const Question &aQuestion, TimeMilli aAnswe
|
||||
VerifyOrExit(aQuestion.mCanAnswer);
|
||||
|
||||
answerInfo.mQuestionRrType = aQuestion.mRrType;
|
||||
answerInfo.mAnswerTime = aAnswerTime;
|
||||
answerInfo.mAnswerDelay = aDelay;
|
||||
answerInfo.mQueryRxTime = mRxTime;
|
||||
answerInfo.mIsProbe = aQuestion.mIsProbe;
|
||||
answerInfo.mUnicastResponse = aQuestion.mUnicastResponse;
|
||||
answerInfo.mLegacyUnicastResponse = mIsLegacyUnicast;
|
||||
|
||||
+30
-12
@@ -719,6 +719,10 @@ private:
|
||||
static constexpr uint32_t kMaxInitialQueryDelay = 120; // msec
|
||||
static constexpr uint32_t kRandomDelayReuseInterval = 2; // msec
|
||||
|
||||
static constexpr uint32_t kMinResponseDelay = 20; // msec
|
||||
static constexpr uint32_t kMaxResponseDelay = 120; // msec
|
||||
static constexpr uint32_t kResponseAggregationMaxDelay = 500; // msec
|
||||
|
||||
static constexpr uint32_t kUnspecifiedTtl = 0;
|
||||
static constexpr uint32_t kDefaultTtl = 120;
|
||||
static constexpr uint32_t kDefaultKeyTtl = kDefaultTtl;
|
||||
@@ -815,8 +819,11 @@ private:
|
||||
|
||||
struct AnswerInfo
|
||||
{
|
||||
TimeMilli GetAnswerTime(void) const { return (mQueryRxTime + mAnswerDelay); }
|
||||
|
||||
uint16_t mQuestionRrType;
|
||||
TimeMilli mAnswerTime;
|
||||
uint16_t mAnswerDelay;
|
||||
TimeMilli mQueryRxTime;
|
||||
bool mIsProbe;
|
||||
bool mUnicastResponse;
|
||||
bool mLegacyUnicastResponse;
|
||||
@@ -873,11 +880,13 @@ private:
|
||||
void UpdateTtl(uint32_t aTtl);
|
||||
|
||||
void StartAnnouncing(void);
|
||||
bool ShouldAppendTo(EntryContext &aContext) const;
|
||||
bool ShouldAppendTo(EntryContext &aContext);
|
||||
bool CanAnswer(void) const;
|
||||
void ScheduleAnswer(const AnswerInfo &aInfo);
|
||||
Error ExtendAnswerDelay(EntryContext &aContext);
|
||||
void UpdateStateAfterAnswer(const TxMessage &aResponse);
|
||||
void UpdateFireTimeOn(FireTime &aFireTime);
|
||||
void DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const;
|
||||
uint32_t GetDurationSinceLastMulticast(TimeMilli aTime) const;
|
||||
Error GetLastMulticastTime(TimeMilli &aLastMulticastTime) const;
|
||||
|
||||
@@ -901,6 +910,8 @@ private:
|
||||
kAppendedInUnicastMsg,
|
||||
};
|
||||
|
||||
TimeMilli GetAnswerTime(void) const { return mQueryRxTime + mAnswerDelay; }
|
||||
|
||||
static constexpr uint32_t kMinIntervalBetweenMulticast = 1000; // msec
|
||||
static constexpr uint32_t kLastMulticastTimeAge = 10 * Time::kOneHourInMsec;
|
||||
|
||||
@@ -910,12 +921,14 @@ private:
|
||||
bool mMulticastAnswerPending : 1;
|
||||
bool mUnicastAnswerPending : 1;
|
||||
bool mIsLastMulticastValid : 1;
|
||||
bool mCanExtendAnswerDelay : 1;
|
||||
uint8_t mAnnounceCounter;
|
||||
AppendState mAppendState;
|
||||
Section mAppendSection;
|
||||
uint16_t mAnswerDelay;
|
||||
uint32_t mTtl;
|
||||
TimeMilli mAnnounceTime;
|
||||
TimeMilli mAnswerTime;
|
||||
TimeMilli mQueryRxTime;
|
||||
TimeMilli mLastMulticastTime;
|
||||
};
|
||||
|
||||
@@ -978,6 +991,7 @@ private:
|
||||
NameAppender aNameAppender);
|
||||
bool ShouldAnswerNsec(TimeMilli aNow) const;
|
||||
void DetermineNextFireTime(void);
|
||||
void DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const;
|
||||
void ScheduleTimer(void);
|
||||
void AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength);
|
||||
void AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength);
|
||||
@@ -988,10 +1002,11 @@ private:
|
||||
RecordInfo mKeyRecord;
|
||||
|
||||
private:
|
||||
void SetState(State aState);
|
||||
void ClearKey(void);
|
||||
void ScheduleCallbackTask(void);
|
||||
void CheckMessageSizeLimitToPrepareAgain(TxMessage &aTxMessage, bool &aPrepareAgain);
|
||||
void SetState(State aState);
|
||||
void ClearKey(void);
|
||||
void ScheduleCallbackTask(void);
|
||||
void CheckMessageSizeLimitToPrepareAgain(TxMessage &aTxMessage, bool &aPrepareAgain);
|
||||
TimeMilli GetNsecAnswerTime(void) const { return mNsecQueryRxTime + mNsecAnswerDelay; }
|
||||
|
||||
State mState;
|
||||
uint8_t mProbeCount;
|
||||
@@ -999,7 +1014,8 @@ private:
|
||||
bool mUnicastNsecPending : 1;
|
||||
bool mAppendedNsec : 1;
|
||||
bool mBypassCallbackStateCheck : 1;
|
||||
TimeMilli mNsecAnswerTime;
|
||||
uint16_t mNsecAnswerDelay;
|
||||
TimeMilli mNsecQueryRxTime;
|
||||
Heap::Data mKeyData;
|
||||
Callback mCallback;
|
||||
Callback mKeyCallback;
|
||||
@@ -1033,6 +1049,7 @@ private:
|
||||
void ClearAppendState(void);
|
||||
void PrepareResponse(EntryContext &aContext);
|
||||
void HandleConflict(void);
|
||||
void DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const;
|
||||
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
|
||||
Error CopyInfoTo(Host &aHost, EntryState &aState) const;
|
||||
Error CopyInfoTo(Key &aKey, EntryState &aState) const;
|
||||
@@ -1092,6 +1109,7 @@ private:
|
||||
void ClearAppendState(void);
|
||||
void PrepareResponse(EntryContext &aContext);
|
||||
void HandleConflict(void);
|
||||
void DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const;
|
||||
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
|
||||
Error CopyInfoTo(Service &aService, EntryState &aState, EntryIterator &aIterator) const;
|
||||
Error CopyInfoTo(Key &aKey, EntryState &aState) const;
|
||||
@@ -1182,6 +1200,7 @@ private:
|
||||
bool ShouldSuppressKnownAnswer(uint32_t aTtl) const;
|
||||
void HandleTimer(EntryContext &aContext);
|
||||
void PrepareResponse(EntryContext &aContext);
|
||||
void DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const;
|
||||
|
||||
private:
|
||||
void PrepareResponseRecords(EntryContext &aContext);
|
||||
@@ -1263,6 +1282,7 @@ private:
|
||||
NextFireTime mNextFireTime;
|
||||
TxMessage mProbeMessage;
|
||||
TxMessage mResponseMessage;
|
||||
TimeMilli mNextAggrTxTime;
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -1317,11 +1337,8 @@ private:
|
||||
bool mIsForAllServicesDnssd : 1; // Is for "_services._dns-sd._udp" (all service types).
|
||||
};
|
||||
|
||||
static constexpr uint32_t kMinResponseDelay = 20; // msec
|
||||
static constexpr uint32_t kMaxResponseDelay = 120; // msec
|
||||
|
||||
void ProcessQuestion(Question &aQuestion);
|
||||
void AnswerQuestion(const Question &aQuestion, TimeMilli aAnswerTime);
|
||||
void AnswerQuestion(const Question &aQuestion, uint16_t aDelay);
|
||||
void AnswerServiceTypeQuestion(const Question &aQuestion, const AnswerInfo &aInfo, ServiceEntry &aFirstEntry);
|
||||
bool ShouldSuppressKnownAnswer(const Name &aServiceType,
|
||||
const char *aSubLabel,
|
||||
@@ -1341,6 +1358,7 @@ private:
|
||||
void ProcessARecord(const Name &aName, const ResourceRecord &aRecord, uint16_t aRecordOffset);
|
||||
|
||||
RxMessage *mNext;
|
||||
TimeMilli mRxTime;
|
||||
OwnedPtr<Message> mMessagePtr;
|
||||
Heap::Array<Question> mQuestions;
|
||||
AddressInfo mSenderAddress;
|
||||
|
||||
@@ -4048,6 +4048,158 @@ void TestMultiPacket(void)
|
||||
testFreeInstance(sInstance);
|
||||
}
|
||||
|
||||
void TestResponseAggregation(void)
|
||||
{
|
||||
Core *mdns = InitTest();
|
||||
Core::Service tcpService;
|
||||
Core::Service udpService;
|
||||
const DnsMessage *dnsMsg;
|
||||
uint16_t heapAllocations;
|
||||
DnsNameString fullTcpServiceName;
|
||||
DnsNameString fullTcpServiceType;
|
||||
DnsNameString fullUdpServiceName;
|
||||
DnsNameString fullUdpServiceType;
|
||||
|
||||
Log("-------------------------------------------------------------------------------------------");
|
||||
Log("TestResponseAggregation");
|
||||
|
||||
AdvanceTime(1);
|
||||
|
||||
heapAllocations = sHeapAllocatedPtrs.GetLength();
|
||||
SuccessOrQuit(mdns->SetEnabled(true, kInfraIfIndex));
|
||||
|
||||
tcpService.mHostName = "host";
|
||||
tcpService.mServiceInstance = "srv1";
|
||||
tcpService.mServiceType = "_matter._tcp";
|
||||
tcpService.mSubTypeLabels = nullptr;
|
||||
tcpService.mSubTypeLabelsLength = 0;
|
||||
tcpService.mTxtData = kTxtData1;
|
||||
tcpService.mTxtDataLength = sizeof(kTxtData1);
|
||||
tcpService.mPort = 1111;
|
||||
tcpService.mPriority = 1;
|
||||
tcpService.mWeight = 2;
|
||||
tcpService.mTtl = 4500;
|
||||
|
||||
udpService.mHostName = "host";
|
||||
udpService.mServiceInstance = "srv2";
|
||||
udpService.mServiceType = "_srv._udp";
|
||||
udpService.mSubTypeLabels = nullptr;
|
||||
udpService.mSubTypeLabelsLength = 0;
|
||||
udpService.mTxtData = kTxtData2;
|
||||
udpService.mTxtDataLength = sizeof(kTxtData2);
|
||||
udpService.mPort = 2222;
|
||||
udpService.mPriority = 6;
|
||||
udpService.mWeight = 2;
|
||||
udpService.mTtl = 4500;
|
||||
|
||||
fullTcpServiceName.Append("%s.%s.local.", tcpService.mServiceInstance, tcpService.mServiceType);
|
||||
fullTcpServiceType.Append("%s.local.", tcpService.mServiceType);
|
||||
|
||||
fullUdpServiceName.Append("%s.%s.local.", udpService.mServiceInstance, udpService.mServiceType);
|
||||
fullUdpServiceType.Append("%s.local.", udpService.mServiceType);
|
||||
|
||||
Log("-------------------------------------------------------------------------------------------");
|
||||
Log("Register a first `ServiceEntry`, check probes and announcements");
|
||||
|
||||
sDnsMessages.Clear();
|
||||
|
||||
sRegCallbacks[0].Reset();
|
||||
SuccessOrQuit(mdns->RegisterService(tcpService, 0, HandleSuccessCallback));
|
||||
|
||||
for (uint8_t probeCount = 0; probeCount < 3; probeCount++)
|
||||
{
|
||||
sDnsMessages.Clear();
|
||||
|
||||
VerifyOrQuit(!sRegCallbacks[0].mWasCalled);
|
||||
AdvanceTime(250);
|
||||
|
||||
VerifyOrQuit(!sDnsMessages.IsEmpty());
|
||||
dnsMsg = sDnsMessages.GetHead();
|
||||
dnsMsg->ValidateHeader(kMulticastQuery, /* Q */ 1, /* Ans */ 0, /* Auth */ 2, /* Addnl */ 0);
|
||||
dnsMsg->ValidateAsProbeFor(tcpService, /* aUnicastRequest */ (probeCount == 0));
|
||||
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
|
||||
}
|
||||
|
||||
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
|
||||
{
|
||||
sDnsMessages.Clear();
|
||||
|
||||
AdvanceTime((anncCount == 0) ? 250 : (1U << (anncCount - 1)) * 1000);
|
||||
VerifyOrQuit(sRegCallbacks[0].mWasCalled);
|
||||
|
||||
VerifyOrQuit(!sDnsMessages.IsEmpty());
|
||||
dnsMsg = sDnsMessages.GetHead();
|
||||
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 4, /* Auth */ 0, /* Addnl */ 1);
|
||||
dnsMsg->Validate(tcpService, kInAnswerSection, kCheckSrv | kCheckTxt | kCheckPtr | kCheckServicesPtr);
|
||||
|
||||
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
|
||||
}
|
||||
|
||||
Log("-------------------------------------------------------------------------------------------");
|
||||
Log("Register a second `ServiceEntry`, check probes and announcements");
|
||||
|
||||
sDnsMessages.Clear();
|
||||
|
||||
sRegCallbacks[0].Reset();
|
||||
SuccessOrQuit(mdns->RegisterService(udpService, 0, HandleSuccessCallback));
|
||||
|
||||
for (uint8_t probeCount = 0; probeCount < 3; probeCount++)
|
||||
{
|
||||
sDnsMessages.Clear();
|
||||
|
||||
VerifyOrQuit(!sRegCallbacks[0].mWasCalled);
|
||||
AdvanceTime(250);
|
||||
|
||||
VerifyOrQuit(!sDnsMessages.IsEmpty());
|
||||
dnsMsg = sDnsMessages.GetHead();
|
||||
dnsMsg->ValidateHeader(kMulticastQuery, /* Q */ 1, /* Ans */ 0, /* Auth */ 2, /* Addnl */ 0);
|
||||
dnsMsg->ValidateAsProbeFor(udpService, /* aUnicastRequest */ (probeCount == 0));
|
||||
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
|
||||
}
|
||||
|
||||
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
|
||||
{
|
||||
sDnsMessages.Clear();
|
||||
|
||||
AdvanceTime((anncCount == 0) ? 250 : (1U << (anncCount - 1)) * 1000);
|
||||
VerifyOrQuit(sRegCallbacks[0].mWasCalled);
|
||||
|
||||
VerifyOrQuit(!sDnsMessages.IsEmpty());
|
||||
dnsMsg = sDnsMessages.GetHead();
|
||||
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 4, /* Auth */ 0, /* Addnl */ 1);
|
||||
dnsMsg->Validate(udpService, kInAnswerSection, kCheckSrv | kCheckTxt | kCheckPtr | kCheckServicesPtr);
|
||||
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
|
||||
}
|
||||
|
||||
Log("-------------------------------------------------------------------------------------------");
|
||||
Log("Send two PTR queries back to back and validate the response is aggregated");
|
||||
|
||||
AdvanceTime(2000);
|
||||
|
||||
sDnsMessages.Clear();
|
||||
SendQuery(fullTcpServiceType.AsCString(), ResourceRecord::kTypePtr);
|
||||
AdvanceTime(5);
|
||||
SendQuery(fullUdpServiceType.AsCString(), ResourceRecord::kTypePtr);
|
||||
|
||||
AdvanceTime(1000);
|
||||
|
||||
dnsMsg = sDnsMessages.GetHead();
|
||||
VerifyOrQuit(dnsMsg != nullptr);
|
||||
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 4);
|
||||
dnsMsg->Validate(tcpService, kInAnswerSection, kCheckPtr);
|
||||
dnsMsg->Validate(tcpService, kInAdditionalSection, kCheckSrv | kCheckTxt);
|
||||
dnsMsg->Validate(udpService, kInAnswerSection, kCheckPtr);
|
||||
dnsMsg->Validate(udpService, kInAdditionalSection, kCheckSrv | kCheckTxt);
|
||||
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
|
||||
|
||||
SuccessOrQuit(mdns->SetEnabled(false, kInfraIfIndex));
|
||||
VerifyOrQuit(sHeapAllocatedPtrs.GetLength() <= heapAllocations);
|
||||
|
||||
Log("End of test");
|
||||
|
||||
testFreeInstance(sInstance);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void TestQuestionUnicastDisallowed(void)
|
||||
@@ -7124,6 +7276,7 @@ int main(void)
|
||||
ot::Dns::Multicast::TestHostOrServiceAndKeyReg();
|
||||
ot::Dns::Multicast::TestQuery();
|
||||
ot::Dns::Multicast::TestMultiPacket();
|
||||
ot::Dns::Multicast::TestResponseAggregation();
|
||||
ot::Dns::Multicast::TestQuestionUnicastDisallowed();
|
||||
ot::Dns::Multicast::TestTxMessageSizeLimit();
|
||||
ot::Dns::Multicast::TestHostConflict();
|
||||
|
||||
Reference in New Issue
Block a user