[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:
Abtin Keshavarzian
2024-11-14 08:29:16 -08:00
committed by GitHub
parent cc8f078586
commit de746129e9
5 changed files with 388 additions and 37 deletions
+153
View File
@@ -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();