/* * Copyright (c) 2017, 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 #include "common/code_utils.hpp" #include "common/instance.hpp" #include "thread/network_data_leader.hpp" #include "thread/network_data_local.hpp" #include "thread/network_data_service.hpp" #include "test_platform.h" #include "test_util.hpp" namespace ot { namespace NetworkData { void PrintExternalRouteConfig(const ExternalRouteConfig &aConfig) { printf("\nprefix:"); for (uint8_t b : aConfig.mPrefix.mPrefix.mFields.m8) { printf("%02x", b); } printf(", length:%d, rloc16:%04x, preference:%d, nat64:%d, stable:%d, nexthop:%d", aConfig.mPrefix.mLength, aConfig.mRloc16, aConfig.mPreference, aConfig.mNat64, aConfig.mStable, aConfig.mNextHopIsThisDevice); } // Returns true if the two given ExternalRouteConfig match (intentionally ignoring mNextHopIsThisDevice). bool CompareExternalRouteConfig(const otExternalRouteConfig &aConfig1, const otExternalRouteConfig &aConfig2) { return (memcmp(aConfig1.mPrefix.mPrefix.mFields.m8, aConfig2.mPrefix.mPrefix.mFields.m8, sizeof(aConfig1.mPrefix.mPrefix)) == 0) && (aConfig1.mPrefix.mLength == aConfig2.mPrefix.mLength) && (aConfig1.mRloc16 == aConfig2.mRloc16) && (aConfig1.mPreference == aConfig2.mPreference) && (aConfig1.mStable == aConfig2.mStable); } void TestNetworkDataIterator(void) { class TestNetworkData : public NetworkData { public: TestNetworkData(ot::Instance *aInstance, const uint8_t *aTlvs, uint8_t aTlvsLength) : NetworkData(*aInstance, kTypeLeader) { memcpy(mTlvs, aTlvs, aTlvsLength); mLength = aTlvsLength; } }; ot::Instance * instance; Iterator iter = kIteratorInit; ExternalRouteConfig config; instance = testInitInstance(); VerifyOrQuit(instance != nullptr); { const uint8_t kNetworkData[] = { 0x08, 0x04, 0x0B, 0x02, 0x00, 0x00, 0x03, 0x14, 0x00, 0x40, 0xFD, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC8, 0x00, 0x40, 0x01, 0x03, 0x54, 0x00, 0x00, }; otExternalRouteConfig routes[] = { { { {{{0xfd, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 64, }, 0xc800, // mRloc16 1, // mPreference false, // mNat64 false, // mStable false, // mNextHopIsThisDevice }, { { {{{0xfd, 0x00, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 64, }, 0x5400, // mRloc16 0, // mPreference false, // mNat64 true, // mStable false, // mNextHopIsThisDevice }, }; TestNetworkData netData(instance, kNetworkData, sizeof(kNetworkData)); iter = OT_NETWORK_DATA_ITERATOR_INIT; printf("\nTest #1: Network data 1"); printf("\n-------------------------------------------------"); for (const auto &route : routes) { SuccessOrQuit(netData.GetNextExternalRoute(iter, config)); PrintExternalRouteConfig(config); VerifyOrQuit(CompareExternalRouteConfig(config, route)); } } { const uint8_t kNetworkData[] = { 0x08, 0x04, 0x0B, 0x02, 0x00, 0x00, 0x03, 0x1E, 0x00, 0x40, 0xFD, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x07, 0x02, 0x11, 0x40, 0x00, 0x03, 0x10, 0x00, 0x40, 0x01, 0x03, 0x54, 0x00, 0x00, 0x05, 0x04, 0x54, 0x00, 0x31, 0x00, 0x02, 0x0F, 0x00, 0x40, 0xFD, 0x00, 0xAB, 0xBA, 0xCD, 0xDC, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x20, 0x03, 0x0E, 0x00, 0x20, 0xFD, 0x00, 0xAB, 0xBA, 0x01, 0x06, 0x54, 0x00, 0x00, 0x04, 0x00, 0x00, }; otExternalRouteConfig routes[] = { { { {{{0xfd, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 64, }, 0x1000, // mRloc16 1, // mPreference false, // mNat64 false, // mStable false, // mNextHopIsThisDevice }, { { {{{0xfd, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 64, }, 0x5400, // mRloc16 0, // mPreference false, // mNat64 true, // mStable false, // mNextHopIsThisDevice }, { { {{{0xfd, 0x00, 0xab, 0xba, 0xcd, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 64, }, 0x1000, // mRloc16 0, // mPreference true, // mNat64 false, // mStable false, // mNextHopIsThisDevice }, { { {{{0xfd, 0x00, 0xab, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 32, }, 0x5400, // mRloc16 0, // mPreference false, // mNat64 true, // mStable false, // mNextHopIsThisDevice }, { { {{{0xfd, 0x00, 0xab, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, 32, }, 0x0400, // mRloc16 0, // mPreference false, // mNat64 true, // mStable false, // mNextHopIsThisDevice }, }; TestNetworkData netData(instance, kNetworkData, sizeof(kNetworkData)); iter = OT_NETWORK_DATA_ITERATOR_INIT; printf("\nTest #2: Network data 2"); printf("\n-------------------------------------------------"); for (const auto &route : routes) { SuccessOrQuit(netData.GetNextExternalRoute(iter, config)); PrintExternalRouteConfig(config); VerifyOrQuit(CompareExternalRouteConfig(config, route)); } } testFreeInstance(instance); } #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE class TestNetworkData : public Local { public: explicit TestNetworkData(ot::Instance &aInstance) : Local(aInstance) { } template Error AddService(const uint8_t (&aServiceData)[kSize]) { return Local::AddService(ServiceTlv::kThreadEnterpriseNumber, aServiceData, kSize, true, nullptr, 0); } template Error ValidateServiceData(const ServiceTlv *aServiceTlv, const uint8_t (&aServiceData)[kSize]) const { return ((aServiceTlv != nullptr) && (aServiceTlv->GetServiceDataLength() == kSize) && (memcmp(aServiceTlv->GetServiceData(), aServiceData, kSize) == 0)) ? kErrorNone : kErrorFailed; } void Test(void) { const uint8_t kServiceData1[] = {0x02}; const uint8_t kServiceData2[] = {0xab}; const uint8_t kServiceData3[] = {0xab, 0x00}; const uint8_t kServiceData4[] = {0x02, 0xab, 0xcd, 0xef}; const uint8_t kServiceData5[] = {0x02, 0xab, 0xcd}; const ServiceTlv *tlv; SuccessOrQuit(AddService(kServiceData1)); SuccessOrQuit(AddService(kServiceData2)); SuccessOrQuit(AddService(kServiceData3)); SuccessOrQuit(AddService(kServiceData4)); SuccessOrQuit(AddService(kServiceData5)); DumpBuffer("netdata", mTlvs, mLength); // Iterate through all entries that start with { 0x02 } (kServiceData1) tlv = nullptr; tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData1, sizeof(kServiceData1), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData1)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData1, sizeof(kServiceData1), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData4)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData1, sizeof(kServiceData1), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData5)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData1, sizeof(kServiceData1), kServicePrefixMatch); VerifyOrQuit(tlv == nullptr, "FindNextService() returned extra TLV"); // Iterate through all entries that start with { 0xab } (kServiceData2) tlv = nullptr; tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData2, sizeof(kServiceData2), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData2)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData2, sizeof(kServiceData2), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData3)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData2, sizeof(kServiceData2), kServicePrefixMatch); VerifyOrQuit(tlv == nullptr, "FindNextService() returned extra TLV"); // Iterate through all entries that start with kServiceData5 tlv = nullptr; tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData5, sizeof(kServiceData5), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData4)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData5, sizeof(kServiceData5), kServicePrefixMatch); SuccessOrQuit(ValidateServiceData(tlv, kServiceData5)); tlv = FindNextService(tlv, ServiceTlv::kThreadEnterpriseNumber, kServiceData5, sizeof(kServiceData5), kServicePrefixMatch); VerifyOrQuit(tlv == nullptr, "FindNextService() returned extra TLV"); } }; void TestNetworkDataFindNextService(void) { ot::Instance *instance; printf("\n\n-------------------------------------------------"); printf("\nTestNetworkDataFindNextService()\n"); instance = testInitInstance(); VerifyOrQuit(instance != nullptr); { TestNetworkData netData(*instance); netData.Test(); } } #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE void TestNetworkDataDsnSrpServices(void) { class TestLeader : public Leader { public: void Populate(const uint8_t *aTlvs, uint8_t aTlvsLength) { memcpy(mTlvs, aTlvs, aTlvsLength); mLength = aTlvsLength; } }; ot::Instance *instance; printf("\n\n-------------------------------------------------"); printf("\nTestNetworkDataDsnSrpServices()\n"); instance = testInitInstance(); VerifyOrQuit(instance != nullptr); { struct AnycastEntry { uint16_t mAloc16; uint8_t mSequenceNumber; bool Matches(Service::DnsSrpAnycast::Info aInfo) const { VerifyOrQuit(aInfo.mAnycastAddress.GetIid().IsAnycastServiceLocator()); return (aInfo.mAnycastAddress.GetIid().GetLocator() == mAloc16) && (aInfo.mSequenceNumber == mSequenceNumber); } }; struct UnicastEntry { const char *mAddress; uint16_t mPort; bool Matches(Service::DnsSrpUnicast::Info aInfo) const { Ip6::SockAddr sockAddr; SuccessOrQuit(sockAddr.GetAddress().FromString(mAddress)); sockAddr.SetPort(mPort); return (aInfo.mSockAddr == sockAddr); } }; const uint8_t kNetworkData[] = { 0x0b, 0x08, 0x80, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x28, 0x00, 0x0b, 0x08, 0x81, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x6c, 0x00, 0x0b, 0x09, 0x82, 0x02, 0x5c, 0x03, 0x0d, 0x03, 0x4c, 0x00, 0xaa, 0x0b, 0x35, 0x83, 0x13, 0x5d, 0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00, 0x2d, 0x0e, 0xc6, 0x27, 0x55, 0x56, 0x18, 0xd9, 0x12, 0x34, 0x0d, 0x02, 0x00, 0x00, 0x0d, 0x14, 0x6c, 0x00, 0xfd, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xab, 0xcd, 0x0d, 0x04, 0x28, 0x00, 0x56, 0x78, 0x0b, 0x23, 0x84, 0x01, 0x5d, 0x0d, 0x02, 0x00, 0x00, 0x0d, 0x14, 0x4c, 0x00, 0xfd, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0x00, 0x0e, 0x0d, 0x04, 0x6c, 0x00, 0xcd, 0x12, }; const AnycastEntry kAnycastEntries[] = { {0xfc10, 0x02}, {0xfc11, 0xff}, {0xfc12, 0x03}, }; const UnicastEntry kUnicastEntries[] = { {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234}, {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd}, {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678}, {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e}, {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12}, }; const uint8_t kPreferredAnycastEntryIndex = 2; Service::Manager & manager = instance->Get(); Service::Manager::Iterator iterator; Service::DnsSrpAnycast::Info anycastInfo; Service::DnsSrpUnicast::Info unicastInfo; reinterpret_cast(instance->Get()).Populate(kNetworkData, sizeof(kNetworkData)); DumpBuffer("netdata", kNetworkData, sizeof(kNetworkData)); // Verify all the "DNS/SRP Anycast Service" entries in Network Data printf("\n- - - - - - - - - - - - - - - - - - - -"); printf("\nDNS/SRP Anycast Service entries\n"); for (const AnycastEntry &entry : kAnycastEntries) { SuccessOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo)); printf("\nanycastInfo { %s, seq:%d }", anycastInfo.mAnycastAddress.ToString().AsCString(), anycastInfo.mSequenceNumber); VerifyOrQuit(entry.Matches(anycastInfo), "GetNextDnsSrpAnycastInfo() returned incorrect info"); } VerifyOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNotFound, "GetNextDnsSrpAnycastInfo() returned unexpected extra entry"); // Find the preferred "DNS/SRP Anycast Service" entries in Network Data SuccessOrQuit(manager.FindPreferredDnsSrpAnycastInfo(anycastInfo)); printf("\n\nPreferred anycastInfo { %s, seq:%d }", anycastInfo.mAnycastAddress.ToString().AsCString(), anycastInfo.mSequenceNumber); VerifyOrQuit(kAnycastEntries[kPreferredAnycastEntryIndex].Matches(anycastInfo), "FindPreferredDnsSrpAnycastInfo() returned invalid info"); printf("\n\n- - - - - - - - - - - - - - - - - - - -"); printf("\nDNS/SRP Unicast Service entries\n"); iterator.Clear(); for (const UnicastEntry &entry : kUnicastEntries) { SuccessOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, unicastInfo)); printf("\nunicastInfo %s", unicastInfo.mSockAddr.ToString().AsCString()); VerifyOrQuit(entry.Matches(unicastInfo), "GetNextDnsSrpUnicastInfo() returned incorrect info"); } VerifyOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNotFound, "GetNextDnsSrpUnicastInfo() returned unexpected extra entry"); printf("\n"); } testFreeInstance(instance); } } // namespace NetworkData } // namespace ot int main(void) { ot::NetworkData::TestNetworkDataIterator(); #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE ot::NetworkData::TestNetworkDataFindNextService(); #endif ot::NetworkData::TestNetworkDataDsnSrpServices(); printf("\nAll tests passed\n"); return 0; }