[netdata] update srp/dns anycast entry seq num selection (#7488)

This commit enhances the selection algorithm for the preferred entry
when there are multiple "DNS/SRP Service Anycast" entries in the
Thread Network Data. The sequence number (which is `uint8_t`) is used
for selecting the preferred entry and the larger value is preferred.
The seq numbers are compared using "serial number arithmetic"
(RFC-1982) but if we have 3 or more entries the notion of a largest
seq number may not be well-defined under the "serial number
arithmetic" comparison (e.g., for `{10, 130, 250}` there is no
largest seq number since `130 > 10`, `250 > 130` and `10 > 250`). In
such a case the algorithm selects the entry with largest seq number
in normal sense.

This commit updates `FindPreferredDnsSrpAnycastInfo()` to implement
the new algorithm. It also updates `test_network_data` unit test to
add many test cases related to the selection algorithm.
This commit is contained in:
Abtin Keshavarzian
2022-04-15 10:08:42 -07:00
committed by GitHub
parent a08e627419
commit 758f342a29
3 changed files with 234 additions and 23 deletions
+52 -4
View File
@@ -195,20 +195,68 @@ exit:
Error Manager::FindPreferredDnsSrpAnycastInfo(DnsSrpAnycast::Info &aInfo) const
{
Error error = kErrorNotFound;
Iterator iterator;
DnsSrpAnycast::Info info;
bool found = false;
DnsSrpAnycast::Info maxNumericalSeqNumInfo;
// Determine the entry with largest seq number in two ways:
// `aInfo` will track the largest using serial number arithmetic
// comparison, while `maxNumericalSeqNumInfo` tracks the largest
// using normal numerical comparison.
while (GetNextDnsSrpAnycastInfo(iterator, info) == kErrorNone)
{
if (!found || info.IsSequenceNumberAheadOf(aInfo))
if (error == kErrorNotFound)
{
aInfo = info;
maxNumericalSeqNumInfo = info;
error = kErrorNone;
continue;
}
if (SerialNumber::IsGreater(info.mSequenceNumber, aInfo.mSequenceNumber))
{
aInfo = info;
found = true;
}
if (info.mSequenceNumber > maxNumericalSeqNumInfo.mSequenceNumber)
{
maxNumericalSeqNumInfo = info;
}
}
return found ? kErrorNone : kErrorNotFound;
SuccessOrExit(error);
// Check that the largest seq number using serial number arithmetic is
// well-defined (i.e., the seq number is larger than all other seq
// numbers values). If it is not, we prefer `maxNumericalSeqNumInfo`.
iterator.Reset();
while (GetNextDnsSrpAnycastInfo(iterator, info) == kErrorNone)
{
constexpr uint8_t kMidValue = (NumericLimits<uint8_t>::kMax / 2) + 1;
uint8_t seqNumber = info.mSequenceNumber;
uint8_t diff;
if (seqNumber == aInfo.mSequenceNumber)
{
continue;
}
diff = seqNumber - aInfo.mSequenceNumber;
if ((diff == kMidValue) || !SerialNumber::IsGreater(aInfo.mSequenceNumber, seqNumber))
{
aInfo = maxNumericalSeqNumInfo;
break;
}
}
exit:
return error;
}
Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info &aInfo) const
-19
View File
@@ -174,25 +174,6 @@ public:
*/
struct Info
{
/**
* This method indicates whether or not the sequence number from the current `Info` is ahead of (more recent
* than) the sequence number from another `Info`.
*
* The sequence numbers comparison follows the Serial Number Arithmetic logic from RFC-1982. It is semantically
* equivalent to `GetSequenceNumber() > aOther.GetSequenceNumber()` while handling roll-over of the `uint8_t`
* values.
*
* @param[in] aOther The other `Info` to compare with.
*
* @retval TRUE The `Info` is ahead of @p aOther.
* @retval FALSE The `Info` is not ahead of @p aOther.
*
*/
bool IsSequenceNumberAheadOf(const Info &aOther) const
{
return SerialNumber::IsGreater(mSequenceNumber, aOther.mSequenceNumber);
}
Ip6::Address mAnycastAddress; ///< The anycast address associated with the DNS/SRP servers.
uint8_t mSequenceNumber; ///< Sequence number used to notify SRP client if they need to re-register.
};
+182
View File
@@ -707,6 +707,187 @@ void TestNetworkDataDsnSrpServices(void)
testFreeInstance(instance);
}
void TestNetworkDataDsnSrpAnycastSeqNumSelection(void)
{
class TestLeader : public Leader
{
public:
void Populate(const uint8_t *aTlvs, uint8_t aTlvsLength)
{
memcpy(GetBytes(), aTlvs, aTlvsLength);
SetLength(aTlvsLength);
}
};
struct TestInfo
{
const uint8_t *mNetworkData;
uint8_t mNetworkDataLength;
const uint8_t *mSeqNumbers;
uint8_t mSeqNumbersLength;
uint8_t mPreferredSeqNum;
};
ot::Instance *instance;
printf("\n\n-------------------------------------------------");
printf("\nTestNetworkDataDsnSrpAnycastSeqNumSelection()\n");
instance = testInitInstance();
VerifyOrQuit(instance != nullptr);
const uint8_t kNetworkData1[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x81, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
};
const uint8_t kSeqNumbers1[] = {1, 129};
const uint8_t kPreferredSeqNum1 = 129;
const uint8_t kNetworkData2[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x85, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x05, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
};
const uint8_t kSeqNumbers2[] = {133, 5};
const uint8_t kPreferredSeqNum2 = 133;
const uint8_t kNetworkData3[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
};
const uint8_t kSeqNumbers3[] = {1, 2, 255};
const uint8_t kPreferredSeqNum3 = 2;
const uint8_t kNetworkData4[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x82, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
};
const uint8_t kSeqNumbers4[] = {10, 130, 250};
const uint8_t kPreferredSeqNum4 = 250;
const uint8_t kNetworkData5[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x82, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
};
const uint8_t kSeqNumbers5[] = {130, 250, 10};
const uint8_t kPreferredSeqNum5 = 250;
const uint8_t kNetworkData6[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0x82, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
};
const uint8_t kSeqNumbers6[] = {250, 10, 130};
const uint8_t kPreferredSeqNum6 = 250;
const uint8_t kNetworkData7[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0x8A, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
};
const uint8_t kSeqNumbers7[] = {250, 10, 138};
const uint8_t kPreferredSeqNum7 = 250;
const uint8_t kNetworkData8[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
0x0b, 0x08, 0x83, 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
};
const uint8_t kSeqNumbers8[] = {1, 2, 255, 254};
const uint8_t kPreferredSeqNum8 = 2;
const uint8_t kNetworkData9[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
0x0b, 0x08, 0x83, 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
};
const uint8_t kSeqNumbers9[] = {1, 2, 255, 254};
const uint8_t kPreferredSeqNum9 = 2;
const uint8_t kNetworkData10[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0x78, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
0x0b, 0x08, 0x83, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
};
const uint8_t kSeqNumbers10[] = {254, 2, 120, 1};
const uint8_t kPreferredSeqNum10 = 120;
const uint8_t kNetworkData11[] = {
0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0, // Service TLV
0x0b, 0x08, 0x80, 0x02, 0x5c, 0xf0, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
0x0b, 0x08, 0x82, 0x02, 0x5c, 0x78, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
0x0b, 0x08, 0x83, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
};
const uint8_t kSeqNumbers11[] = {240, 2, 120, 1};
const uint8_t kPreferredSeqNum11 = 240;
const TestInfo kTests[] = {
{kNetworkData1, sizeof(kNetworkData1), kSeqNumbers1, sizeof(kSeqNumbers1), kPreferredSeqNum1},
{kNetworkData2, sizeof(kNetworkData2), kSeqNumbers2, sizeof(kSeqNumbers2), kPreferredSeqNum2},
{kNetworkData3, sizeof(kNetworkData3), kSeqNumbers3, sizeof(kSeqNumbers3), kPreferredSeqNum3},
{kNetworkData4, sizeof(kNetworkData4), kSeqNumbers4, sizeof(kSeqNumbers4), kPreferredSeqNum4},
{kNetworkData5, sizeof(kNetworkData5), kSeqNumbers5, sizeof(kSeqNumbers5), kPreferredSeqNum5},
{kNetworkData6, sizeof(kNetworkData6), kSeqNumbers6, sizeof(kSeqNumbers6), kPreferredSeqNum6},
{kNetworkData7, sizeof(kNetworkData7), kSeqNumbers7, sizeof(kSeqNumbers7), kPreferredSeqNum7},
{kNetworkData8, sizeof(kNetworkData8), kSeqNumbers8, sizeof(kSeqNumbers8), kPreferredSeqNum8},
{kNetworkData9, sizeof(kNetworkData9), kSeqNumbers9, sizeof(kSeqNumbers9), kPreferredSeqNum9},
{kNetworkData10, sizeof(kNetworkData10), kSeqNumbers10, sizeof(kSeqNumbers10), kPreferredSeqNum10},
{kNetworkData11, sizeof(kNetworkData11), kSeqNumbers11, sizeof(kSeqNumbers11), kPreferredSeqNum11},
};
Service::Manager &manager = instance->Get<Service::Manager>();
uint8_t testIndex = 0;
for (const TestInfo &test : kTests)
{
Service::Manager::Iterator iterator;
Service::DnsSrpAnycast::Info anycastInfo;
reinterpret_cast<TestLeader &>(instance->Get<Leader>()).Populate(test.mNetworkData, test.mNetworkDataLength);
printf("\n- - - - - - - - - - - - - - - - - - - -");
printf("\nDNS/SRP Anycast Service entries for test %d", ++testIndex);
for (uint8_t index = 0; index < test.mSeqNumbersLength; index++)
{
SuccessOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo));
printf("\n { %s, seq:%d }", anycastInfo.mAnycastAddress.ToString().AsCString(),
anycastInfo.mSequenceNumber);
VerifyOrQuit(anycastInfo.mSequenceNumber == test.mSeqNumbers[index]);
}
VerifyOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNotFound);
SuccessOrQuit(manager.FindPreferredDnsSrpAnycastInfo(anycastInfo));
printf("\n preferred -> seq:%d ", anycastInfo.mSequenceNumber);
VerifyOrQuit(anycastInfo.mSequenceNumber == test.mPreferredSeqNum);
}
testFreeInstance(instance);
}
} // namespace NetworkData
} // namespace ot
@@ -717,6 +898,7 @@ int main(void)
ot::NetworkData::TestNetworkDataFindNextService();
#endif
ot::NetworkData::TestNetworkDataDsnSrpServices();
ot::NetworkData::TestNetworkDataDsnSrpAnycastSeqNumSelection();
printf("\nAll tests passed\n");
return 0;