1063 lines
31 KiB
C++
1063 lines
31 KiB
C++
/*
|
|
* Copyright (c) 2021, 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.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements the Network Data Publisher.
|
|
*
|
|
*/
|
|
|
|
#include "network_data_publisher.hpp"
|
|
|
|
#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
|
|
|
|
#include "common/array.hpp"
|
|
#include "common/code_utils.hpp"
|
|
#include "common/const_cast.hpp"
|
|
#include "common/instance.hpp"
|
|
#include "common/locator_getters.hpp"
|
|
#include "common/log.hpp"
|
|
#include "common/random.hpp"
|
|
#include "thread/network_data_local.hpp"
|
|
#include "thread/network_data_service.hpp"
|
|
|
|
namespace ot {
|
|
namespace NetworkData {
|
|
|
|
RegisterLogModule("NetDataPublshr");
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// Publisher
|
|
|
|
Publisher::Publisher(Instance &aInstance)
|
|
: InstanceLocator(aInstance)
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
, mDnsSrpServiceEntry(aInstance)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
, mPrefixCallback(nullptr)
|
|
, mPrefixCallbackContext(nullptr)
|
|
#endif
|
|
, mTimer(aInstance, Publisher::HandleTimer)
|
|
{
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
// Since the `PrefixEntry` type is used in an array,
|
|
// we cannot use a constructor with an argument (e.g.,
|
|
// we cannot use `InstacneLocator`) so we use
|
|
// `IntanceLocatorInit` and `Init()` the entries one
|
|
// by one.
|
|
|
|
for (PrefixEntry &entry : mPrefixEntries)
|
|
{
|
|
entry.Init(aInstance);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
|
|
void Publisher::SetPrefixCallback(PrefixCallback aCallback, void *aContext)
|
|
{
|
|
mPrefixCallback = aCallback;
|
|
mPrefixCallbackContext = aContext;
|
|
}
|
|
|
|
Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig)
|
|
{
|
|
Error error = kErrorNone;
|
|
PrefixEntry *entry;
|
|
|
|
VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
|
|
VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
|
|
|
|
entry = FindOrAllocatePrefixEntry(aConfig.GetPrefix());
|
|
VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
|
|
|
|
entry->Publish(aConfig);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig)
|
|
{
|
|
Error error = kErrorNone;
|
|
PrefixEntry *entry;
|
|
|
|
VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
|
|
VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
|
|
|
|
entry = FindOrAllocatePrefixEntry(aConfig.GetPrefix());
|
|
VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
|
|
|
|
entry->Publish(aConfig);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
bool Publisher::IsPrefixAdded(const Ip6::Prefix &aPrefix) const
|
|
{
|
|
bool isAdded = false;
|
|
const PrefixEntry *entry;
|
|
|
|
entry = FindMatchingPrefixEntry(aPrefix);
|
|
VerifyOrExit(entry != nullptr);
|
|
|
|
isAdded = entry->IsAdded();
|
|
|
|
exit:
|
|
return isAdded;
|
|
}
|
|
|
|
Error Publisher::UnpublishPrefix(const Ip6::Prefix &aPrefix)
|
|
{
|
|
Error error = kErrorNone;
|
|
PrefixEntry *entry;
|
|
|
|
entry = FindMatchingPrefixEntry(aPrefix);
|
|
VerifyOrExit(entry != nullptr, error = kErrorNotFound);
|
|
|
|
entry->Unpublish();
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Publisher::PrefixEntry *Publisher::FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix)
|
|
{
|
|
// Returns a matching prefix entry if found, otherwise tries
|
|
// to allocate a new entry.
|
|
|
|
PrefixEntry *prefixEntry = FindMatchingPrefixEntry(aPrefix);
|
|
|
|
VerifyOrExit(prefixEntry == nullptr);
|
|
|
|
for (PrefixEntry &entry : mPrefixEntries)
|
|
{
|
|
if (!entry.IsInUse())
|
|
{
|
|
prefixEntry = &entry;
|
|
ExitNow();
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return prefixEntry;
|
|
}
|
|
|
|
Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix)
|
|
{
|
|
return AsNonConst(AsConst(this)->FindMatchingPrefixEntry(aPrefix));
|
|
}
|
|
|
|
const Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix) const
|
|
{
|
|
const PrefixEntry *prefixEntry = nullptr;
|
|
|
|
for (const PrefixEntry &entry : mPrefixEntries)
|
|
{
|
|
if (entry.IsInUse() && entry.Matches(aPrefix))
|
|
{
|
|
prefixEntry = &entry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return prefixEntry;
|
|
}
|
|
|
|
bool Publisher::IsAPrefixEntry(const Entry &aEntry) const
|
|
{
|
|
return (&mPrefixEntries[0] <= &aEntry) && (&aEntry < GetArrayEnd(mPrefixEntries));
|
|
}
|
|
|
|
void Publisher::NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const
|
|
{
|
|
if (mPrefixCallback != nullptr)
|
|
{
|
|
mPrefixCallback(static_cast<otNetDataPublisherEvent>(aEvent), &aPrefix, mPrefixCallbackContext);
|
|
}
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
|
|
void Publisher::HandleNotifierEvents(Events aEvents)
|
|
{
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
mDnsSrpServiceEntry.HandleNotifierEvents(aEvents);
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
for (PrefixEntry &entry : mPrefixEntries)
|
|
{
|
|
entry.HandleNotifierEvents(aEvents);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Publisher::HandleTimer(Timer &aTimer)
|
|
{
|
|
aTimer.Get<Publisher>().HandleTimer();
|
|
}
|
|
|
|
void Publisher::HandleTimer(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
mDnsSrpServiceEntry.HandleTimer();
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
for (PrefixEntry &entry : mPrefixEntries)
|
|
{
|
|
entry.HandleTimer();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// Publisher::Entry
|
|
|
|
void Publisher::Entry::SetState(State aState)
|
|
{
|
|
VerifyOrExit(mState != aState);
|
|
|
|
LogInfo("%s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(), StateToString(mState),
|
|
StateToString(aState));
|
|
mState = aState;
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
bool Publisher::Entry::IsPreferred(uint16_t aRloc16) const
|
|
{
|
|
// Indicates whether or not an entry from `aRloc16` is preferred
|
|
// over our entry (based on our RLOC). We prefer an entry from a
|
|
// router over an entry from an end-device (e.g., a REED). If both
|
|
// are the same type, then the one with smaller RLOC16 is preferred.
|
|
|
|
bool isOtherRouter = Mle::Mle::IsActiveRouter(aRloc16);
|
|
|
|
return (Get<Mle::Mle>().IsRouterOrLeader() == isOtherRouter) ? (aRloc16 < Get<Mle::Mle>().GetRloc16())
|
|
: isOtherRouter;
|
|
}
|
|
|
|
void Publisher::Entry::UpdateState(uint8_t aNumEntries, uint8_t aNumPreferredEntries, uint8_t aDesiredNumEntries)
|
|
{
|
|
// This method uses the info about number existing entries (total
|
|
// and preferred) in Network Data along with the desired number of
|
|
// entries we aim to have in the Network Data to decide whether or
|
|
// not to take any action (add or remove our entry).
|
|
|
|
LogInfo("%s in netdata - total:%d, preferred:%d, desired:%d", ToString().AsCString(), aNumEntries,
|
|
aNumPreferredEntries, aDesiredNumEntries);
|
|
|
|
switch (GetState())
|
|
{
|
|
case kNoEntry:
|
|
break;
|
|
|
|
case kToAdd:
|
|
// Our entry is ready to be added. If there are too few existing
|
|
// entries, we start adding our entry (start the timer with a
|
|
// random delay before adding the entry).
|
|
|
|
if (aNumEntries < aDesiredNumEntries)
|
|
{
|
|
mUpdateTime = TimerMilli::GetNow() + Random::NonCrypto::GetUint32InRange(1, kMaxDelayToAdd);
|
|
SetState(kAdding);
|
|
Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
|
|
LogUpdateTime();
|
|
}
|
|
break;
|
|
|
|
case kAdding:
|
|
// Our entry is being added (waiting time before we add). If we
|
|
// now see that there are enough entries, we stop adding the
|
|
// entry.
|
|
|
|
if (aNumEntries >= aDesiredNumEntries)
|
|
{
|
|
SetState(kToAdd);
|
|
}
|
|
break;
|
|
|
|
case kAdded:
|
|
// Our entry is already added in the Network Data. If there are
|
|
// enough entries, do nothing and keep monitoring. If we see now
|
|
// that there are too many entries, we start removing our entry
|
|
// after a random delay time. If our entry itself is preferred
|
|
// over other entries (indicated by `aNumPreferredEntries <
|
|
// aDesiredNumEntries`) we add an extra delay before removing
|
|
// the entry. This gives higher chance for a non-preferred
|
|
// entry from another device to be removed before our entry.
|
|
|
|
if (aNumEntries > aDesiredNumEntries)
|
|
{
|
|
mUpdateTime = TimerMilli::GetNow() + Random::NonCrypto::GetUint32InRange(1, kMaxDelayToRemove);
|
|
|
|
if (aNumPreferredEntries < aDesiredNumEntries)
|
|
{
|
|
mUpdateTime += kExtraDelayToRemovePeferred;
|
|
}
|
|
|
|
SetState(kRemoving);
|
|
Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
|
|
LogUpdateTime();
|
|
}
|
|
break;
|
|
|
|
case kRemoving:
|
|
// Our entry is being removed (wait time before remove). If we
|
|
// now see that there are enough or too few entries, we stop
|
|
// removing our entry.
|
|
|
|
if (aNumEntries <= aDesiredNumEntries)
|
|
{
|
|
SetState(kAdded);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Publisher::Entry::HandleTimer(void)
|
|
{
|
|
// Timer is used to delay adding/removing the entry. If we have
|
|
// reached `mUpdateTime` add or remove the entry. Otherwise,
|
|
// restart the timer (note that timer can be shared between
|
|
// different published entries).
|
|
|
|
VerifyOrExit((GetState() == kAdding) || (GetState() == kRemoving));
|
|
|
|
if (mUpdateTime <= TimerMilli::GetNow())
|
|
{
|
|
if (GetState() == kAdding)
|
|
{
|
|
Add();
|
|
}
|
|
else
|
|
{
|
|
Remove(/* aNextState */ kToAdd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Publisher::Entry::Add(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
|
|
{
|
|
static_cast<DnsSrpServiceEntry *>(this)->Add();
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
if (Get<Publisher>().IsAPrefixEntry(*this))
|
|
{
|
|
static_cast<PrefixEntry *>(this)->Add();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Publisher::Entry::Remove(State aNextState)
|
|
{
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
|
|
{
|
|
static_cast<DnsSrpServiceEntry *>(this)->Remove(aNextState);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
if (Get<Publisher>().IsAPrefixEntry(*this))
|
|
{
|
|
static_cast<PrefixEntry *>(this)->Remove(aNextState);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Publisher::Entry::InfoString Publisher::Entry::ToString(bool aIncludeState) const
|
|
{
|
|
InfoString string;
|
|
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
|
|
{
|
|
string.Append("DNS/SRP service");
|
|
ExitNow();
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
if (Get<Publisher>().IsAPrefixEntry(*this))
|
|
{
|
|
const PrefixEntry &prefixEntry = *static_cast<const PrefixEntry *>(this);
|
|
|
|
switch (prefixEntry.mType)
|
|
{
|
|
case PrefixEntry::kTypeOnMeshPrefix:
|
|
string.Append("OnMeshPrefix ");
|
|
break;
|
|
|
|
case PrefixEntry::kTypeExternalRoute:
|
|
string.Append("ExternalRoute ");
|
|
break;
|
|
}
|
|
|
|
string.Append(prefixEntry.mPrefix.ToString().AsCString());
|
|
ExitNow();
|
|
}
|
|
#endif
|
|
|
|
exit:
|
|
if (aIncludeState)
|
|
{
|
|
string.Append(" (state:%s)", StateToString(GetState()));
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
void Publisher::Entry::LogUpdateTime(void) const
|
|
{
|
|
LogInfo("%s - update in %u msec", ToString().AsCString(), mUpdateTime - TimerMilli::GetNow());
|
|
}
|
|
|
|
const char *Publisher::Entry::StateToString(State aState)
|
|
{
|
|
static const char *const kStateStrings[] = {
|
|
"NoEntry", // (0) kNoEntry
|
|
"ToAdd", // (1) kToAdd
|
|
"Adding", // (2) kAdding
|
|
"Added", // (3) kAdded
|
|
"Removing", // (4) kRemoving
|
|
};
|
|
|
|
static_assert(0 == kNoEntry, "kNoEntry value is not correct");
|
|
static_assert(1 == kToAdd, "kToAdd value is not correct");
|
|
static_assert(2 == kAdding, "kAdding value is not correct");
|
|
static_assert(3 == kAdded, "kAdded value is not correct");
|
|
static_assert(4 == kRemoving, "kRemoving value is not correct");
|
|
|
|
return kStateStrings[aState];
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// Publisher::DnsSrpServiceEntry
|
|
|
|
Publisher::DnsSrpServiceEntry::DnsSrpServiceEntry(Instance &aInstance)
|
|
: mCallback(nullptr)
|
|
, mCallbackContext(nullptr)
|
|
{
|
|
Init(aInstance);
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::SetCallback(DnsSrpServiceCallback aCallback, void *aContext)
|
|
{
|
|
mCallback = aCallback;
|
|
mCallbackContext = aContext;
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::PublishAnycast(uint8_t aSequenceNumber)
|
|
{
|
|
LogInfo("Publishing DNS/SRP service anycast (seq-num:%d)", aSequenceNumber);
|
|
Publish(Info::InfoAnycast(aSequenceNumber));
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort)
|
|
{
|
|
LogInfo("Publishing DNS/SRP service unicast (%s, port:%d)", aAddress.ToString().AsCString(), aPort);
|
|
Publish(Info::InfoUnicast(kTypeUnicast, aAddress, aPort));
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::PublishUnicast(uint16_t aPort)
|
|
{
|
|
LogInfo("Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort);
|
|
Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get<Mle::Mle>().GetMeshLocal64(), aPort));
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::Publish(const Info &aInfo)
|
|
{
|
|
if (GetState() != kNoEntry)
|
|
{
|
|
if (aInfo == mInfo)
|
|
{
|
|
LogInfo("%s is already being published", ToString().AsCString());
|
|
ExitNow();
|
|
}
|
|
|
|
Remove(/* aNextState */ kNoEntry);
|
|
}
|
|
|
|
mInfo = aInfo;
|
|
SetState(kToAdd);
|
|
|
|
Process();
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::Unpublish(void)
|
|
{
|
|
LogInfo("Unpublishing DNS/SRP service");
|
|
|
|
Remove(/* aNextState */ kNoEntry);
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::HandleNotifierEvents(Events aEvents)
|
|
{
|
|
if ((GetType() == kTypeUnicastMeshLocalEid) && aEvents.Contains(kEventThreadMeshLocalAddrChanged))
|
|
{
|
|
mInfo.SetAddress(Get<Mle::Mle>().GetMeshLocal64());
|
|
|
|
if (GetState() == kAdded)
|
|
{
|
|
// If the entry is already added, we need to update it
|
|
// so we remove it and add it back immediately with
|
|
// the new mesh-local address.
|
|
|
|
Remove(/* aNextState */ kAdding);
|
|
Add();
|
|
Get<Notifier>().HandleServerDataUpdated();
|
|
}
|
|
}
|
|
|
|
if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
|
|
{
|
|
Process();
|
|
}
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::Add(void)
|
|
{
|
|
// Adds the service entry to the network data.
|
|
|
|
switch (GetType())
|
|
{
|
|
case kTypeAnycast:
|
|
SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpAnycast>(
|
|
Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber())));
|
|
break;
|
|
|
|
case kTypeUnicast:
|
|
SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpUnicast>(
|
|
Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort())));
|
|
break;
|
|
|
|
case kTypeUnicastMeshLocalEid:
|
|
SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpUnicast>(
|
|
Service::DnsSrpUnicast::ServerData(mInfo.GetAddress(), mInfo.GetPort())));
|
|
break;
|
|
}
|
|
|
|
Get<Notifier>().HandleServerDataUpdated();
|
|
SetState(kAdded);
|
|
Notify(kEventEntryAdded);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::Remove(State aNextState)
|
|
{
|
|
// Removes the service entry from network data (if it was added).
|
|
|
|
VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
|
|
|
|
switch (GetType())
|
|
{
|
|
case kTypeAnycast:
|
|
SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpAnycast>(
|
|
Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber())));
|
|
break;
|
|
|
|
case kTypeUnicast:
|
|
SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpUnicast>(
|
|
Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort())));
|
|
break;
|
|
|
|
case kTypeUnicastMeshLocalEid:
|
|
SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpUnicast>());
|
|
break;
|
|
}
|
|
|
|
Get<Notifier>().HandleServerDataUpdated();
|
|
Notify(kEventEntryRemoved);
|
|
|
|
exit:
|
|
SetState(aNextState);
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::Notify(Event aEvent) const
|
|
{
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
Get<Srp::Server>().HandleNetDataPublisherEvent(aEvent);
|
|
#endif
|
|
|
|
if (mCallback != nullptr)
|
|
{
|
|
mCallback(static_cast<otNetDataPublisherEvent>(aEvent), mCallbackContext);
|
|
}
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::Process(void)
|
|
{
|
|
// This method checks the entries currently present in Network Data
|
|
// based on which it then decides whether or not take action
|
|
// (add/remove or keep monitoring).
|
|
|
|
uint8_t numEntries = 0;
|
|
uint8_t numPreferredEntries = 0;
|
|
uint8_t desiredNumEntries = 0;
|
|
|
|
// Do not make any changes if device is not attached, and wait
|
|
// for role change event.
|
|
VerifyOrExit(Get<Mle::Mle>().IsAttached());
|
|
|
|
VerifyOrExit(GetState() != kNoEntry);
|
|
|
|
switch (GetType())
|
|
{
|
|
case kTypeAnycast:
|
|
CountAnycastEntries(numEntries, numPreferredEntries);
|
|
desiredNumEntries = kDesiredNumAnycast;
|
|
break;
|
|
|
|
case kTypeUnicast:
|
|
case kTypeUnicastMeshLocalEid:
|
|
CountUnicastEntries(numEntries, numPreferredEntries);
|
|
desiredNumEntries = kDesiredNumUnicast;
|
|
break;
|
|
}
|
|
|
|
UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
|
|
{
|
|
// Count the number of matching "DNS/SRP Anycast" service entries
|
|
// in the Network Data (the match requires the entry to use same
|
|
// "sequence number" value). We prefer the entries associated with
|
|
// smaller RLCO16.
|
|
|
|
Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber());
|
|
const ServiceTlv * serviceTlv = nullptr;
|
|
ServiceData data;
|
|
|
|
data.Init(&serviceData, serviceData.GetLength());
|
|
|
|
while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
|
|
nullptr)
|
|
{
|
|
TlvIterator subTlvIterator(*serviceTlv);
|
|
const ServerTlv *serverSubTlv;
|
|
|
|
while ((serverSubTlv = subTlvIterator.Iterate<ServerTlv>()) != nullptr)
|
|
{
|
|
aNumEntries++;
|
|
|
|
if (IsPreferred(serverSubTlv->GetServer16()))
|
|
{
|
|
aNumPreferredEntries++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
|
|
{
|
|
// Count the number of "DNS/SRP Unicast" service entries in
|
|
// the Network Data.
|
|
|
|
const ServiceTlv *serviceTlv = nullptr;
|
|
ServiceData data;
|
|
|
|
data.InitFrom(Service::DnsSrpUnicast::kServiceData);
|
|
|
|
while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
|
|
nullptr)
|
|
{
|
|
TlvIterator subTlvIterator(*serviceTlv);
|
|
const ServerTlv *serverSubTlv;
|
|
|
|
while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
|
|
{
|
|
if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
|
|
{
|
|
aNumEntries++;
|
|
|
|
// Generally, we prefer entries where the SRP/DNS server
|
|
// address/port info is included in the service TLV data
|
|
// over the ones where the info is included in the
|
|
// server TLV data (i.e., we prefer infra-provided
|
|
// SRP/DNS entry over a BR local one using ML-EID). If
|
|
// our entry itself uses the service TLV data, then we
|
|
// prefer based on the associated RLOC16.
|
|
|
|
if (GetType() == kTypeUnicast)
|
|
{
|
|
if (IsPreferred(serverSubTlv->GetServer16()))
|
|
{
|
|
aNumPreferredEntries++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aNumPreferredEntries++;
|
|
}
|
|
}
|
|
|
|
if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
|
|
{
|
|
aNumEntries++;
|
|
|
|
// If our entry also uses the server TLV data (with
|
|
// ML-EID address), then the we prefer based on the
|
|
// associated RLOC16.
|
|
|
|
if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
|
|
{
|
|
aNumPreferredEntries++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// Publisher::DnsSrpServiceEntry::Info
|
|
|
|
Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress)
|
|
: mPortOrSeqNumber(aPortOrSeqNumber)
|
|
, mType(aType)
|
|
{
|
|
// It is important to `Clear()` the object since we compare all
|
|
// bytes using overload of operator `==`.
|
|
|
|
Clear();
|
|
|
|
mType = aType;
|
|
mPortOrSeqNumber = aPortOrSeqNumber;
|
|
|
|
if (aAddress != nullptr)
|
|
{
|
|
mAddress = *aAddress;
|
|
}
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------
|
|
// Publisher::PrefixEntry
|
|
|
|
void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig)
|
|
{
|
|
LogInfo("Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
|
|
|
|
Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeOnMeshPrefix);
|
|
}
|
|
|
|
void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig)
|
|
{
|
|
LogInfo("Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
|
|
|
|
Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeExternalRoute);
|
|
}
|
|
|
|
void Publisher::PrefixEntry::Publish(const Ip6::Prefix &aPrefix, uint16_t aNewFlags, Type aNewType)
|
|
{
|
|
if (GetState() != kNoEntry)
|
|
{
|
|
// If this is an existing entry, first we check that there is
|
|
// a change in either type or flags. We remove the old entry
|
|
// from Network Data if it was added. If the only change is
|
|
// to flags (e.g., change to the preference level) and the
|
|
// entry was previously added in Network Data, we re-add it
|
|
// with the new flags. This ensures that changes to flags are
|
|
// immediately reflected in the Network Data.
|
|
|
|
State oldState = GetState();
|
|
|
|
VerifyOrExit((mType != aNewType) || (mFlags != aNewFlags));
|
|
|
|
Remove(/* aNextState */ kNoEntry);
|
|
|
|
if ((mType == aNewType) && ((oldState == kAdded) || (oldState == kRemoving)))
|
|
{
|
|
mFlags = aNewFlags;
|
|
Add();
|
|
}
|
|
}
|
|
|
|
VerifyOrExit(GetState() == kNoEntry);
|
|
|
|
mType = aNewType;
|
|
mPrefix = aPrefix;
|
|
mFlags = aNewFlags;
|
|
|
|
SetState(kToAdd);
|
|
|
|
exit:
|
|
Process();
|
|
}
|
|
|
|
void Publisher::PrefixEntry::Unpublish(void)
|
|
{
|
|
LogInfo("Unpublishing %s", mPrefix.ToString().AsCString());
|
|
|
|
Remove(/* aNextState */ kNoEntry);
|
|
}
|
|
|
|
void Publisher::PrefixEntry::HandleNotifierEvents(Events aEvents)
|
|
{
|
|
if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
|
|
{
|
|
Process();
|
|
}
|
|
}
|
|
|
|
void Publisher::PrefixEntry::Add(void)
|
|
{
|
|
// Adds the prefix entry to the network data.
|
|
|
|
switch (mType)
|
|
{
|
|
case kTypeOnMeshPrefix:
|
|
SuccessOrExit(AddOnMeshPrefix());
|
|
break;
|
|
|
|
case kTypeExternalRoute:
|
|
SuccessOrExit(AddExternalRoute());
|
|
break;
|
|
}
|
|
|
|
Get<Notifier>().HandleServerDataUpdated();
|
|
SetState(kAdded);
|
|
Get<Publisher>().NotifyPrefixEntryChange(kEventEntryAdded, mPrefix);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
Error Publisher::PrefixEntry::AddOnMeshPrefix(void)
|
|
{
|
|
OnMeshPrefixConfig config;
|
|
|
|
config.mPrefix = mPrefix;
|
|
config.mStable = true;
|
|
config.SetFromTlvFlags(mFlags);
|
|
|
|
return Get<Local>().AddOnMeshPrefix(config);
|
|
}
|
|
|
|
Error Publisher::PrefixEntry::AddExternalRoute(void)
|
|
{
|
|
ExternalRouteConfig config;
|
|
|
|
config.mPrefix = mPrefix;
|
|
config.mStable = true;
|
|
config.SetFromTlvFlags(static_cast<uint8_t>(mFlags));
|
|
|
|
return Get<Local>().AddHasRoutePrefix(config);
|
|
}
|
|
|
|
void Publisher::PrefixEntry::Remove(State aNextState)
|
|
{
|
|
// Remove the prefix entry from the network data.
|
|
|
|
VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
|
|
|
|
switch (mType)
|
|
{
|
|
case kTypeOnMeshPrefix:
|
|
IgnoreError(Get<Local>().RemoveOnMeshPrefix(mPrefix));
|
|
break;
|
|
|
|
case kTypeExternalRoute:
|
|
IgnoreError(Get<Local>().RemoveHasRoutePrefix(mPrefix));
|
|
break;
|
|
}
|
|
|
|
Get<Notifier>().HandleServerDataUpdated();
|
|
Get<Publisher>().NotifyPrefixEntryChange(kEventEntryRemoved, mPrefix);
|
|
|
|
exit:
|
|
SetState(aNextState);
|
|
}
|
|
|
|
void Publisher::PrefixEntry::Process(void)
|
|
{
|
|
// This method checks the entries currently present in Network Data
|
|
// based on which it then decides whether or not take action
|
|
// (add/remove or keep monitoring).
|
|
|
|
uint8_t numEntries = 0;
|
|
uint8_t numPreferredEntries = 0;
|
|
uint8_t desiredNumEntries = 0;
|
|
|
|
// Do not make any changes if device is not attached, and wait
|
|
// for role change event.
|
|
VerifyOrExit(Get<Mle::Mle>().IsAttached());
|
|
|
|
VerifyOrExit(GetState() != kNoEntry);
|
|
|
|
switch (mType)
|
|
{
|
|
case kTypeOnMeshPrefix:
|
|
CountOnMeshPrefixEntries(numEntries, numPreferredEntries);
|
|
desiredNumEntries = kDesiredNumOnMeshPrefix;
|
|
break;
|
|
case kTypeExternalRoute:
|
|
CountExternalRouteEntries(numEntries, numPreferredEntries);
|
|
desiredNumEntries = kDesiredNumExternalRoute;
|
|
break;
|
|
}
|
|
|
|
UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
|
|
{
|
|
const PrefixTlv * prefixTlv;
|
|
const BorderRouterTlv *brSubTlv;
|
|
int8_t preference = BorderRouterEntry::PreferenceFromFlags(mFlags);
|
|
uint16_t flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags);
|
|
|
|
prefixTlv = Get<Leader>().FindPrefix(mPrefix);
|
|
VerifyOrExit(prefixTlv != nullptr);
|
|
|
|
brSubTlv = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ true);
|
|
VerifyOrExit(brSubTlv != nullptr);
|
|
|
|
for (const BorderRouterEntry *entry = brSubTlv->GetFirstEntry(); entry <= brSubTlv->GetLastEntry();
|
|
entry = entry->GetNext())
|
|
{
|
|
uint16_t entryFlags = entry->GetFlags();
|
|
int8_t entryPreference = BorderRouterEntry::PreferenceFromFlags(entryFlags);
|
|
|
|
// Count an existing entry in the network data if its flags
|
|
// match ours and and its preference is same or higher than our
|
|
// preference. We do not count matching entries at a lower
|
|
// preference than ours. This ensures that a device with higher
|
|
// preference entry publishes its entry even when there are many
|
|
// lower preference similar entries in the network data
|
|
// (potentially causing a lower preference entry to be removed).
|
|
|
|
if ((BorderRouterEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
|
|
(entryPreference >= preference))
|
|
{
|
|
aNumEntries++;
|
|
|
|
// We prefer an entry if it has strictly higher preference
|
|
// than ours or if it has same preference we use the associated
|
|
// RLOC16.
|
|
|
|
if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
|
|
{
|
|
aNumPreferredEntries++;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
|
|
{
|
|
const PrefixTlv * prefixTlv;
|
|
const HasRouteTlv *hrSubTlv;
|
|
int8_t preference = HasRouteEntry::PreferenceFromFlags(static_cast<uint8_t>(mFlags));
|
|
uint8_t flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast<uint8_t>(mFlags));
|
|
|
|
prefixTlv = Get<Leader>().FindPrefix(mPrefix);
|
|
VerifyOrExit(prefixTlv != nullptr);
|
|
|
|
hrSubTlv = prefixTlv->FindSubTlv<HasRouteTlv>(/* aStable */ true);
|
|
VerifyOrExit(hrSubTlv != nullptr);
|
|
|
|
for (const HasRouteEntry *entry = hrSubTlv->GetFirstEntry(); entry <= hrSubTlv->GetLastEntry();
|
|
entry = entry->GetNext())
|
|
{
|
|
uint8_t entryFlags = entry->GetFlags();
|
|
int8_t entryPreference = HasRouteEntry::PreferenceFromFlags(entryFlags);
|
|
|
|
// Count an existing entry in the network data if its flags
|
|
// match ours and and its preference is same or higher than our
|
|
// preference. We do not count matching entries at a lower
|
|
// preference than ours. This ensures that a device with higher
|
|
// preference entry publishes its entry even when there are many
|
|
// lower preference similar entries in the network data
|
|
// (potentially causing a lower preference entry to be removed).
|
|
|
|
if ((HasRouteEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
|
|
(entryPreference >= preference))
|
|
{
|
|
aNumEntries++;
|
|
|
|
// We prefer an entry if it has strictly higher preference
|
|
// than ours or if it has same preference with a smaller
|
|
// RLOC16.
|
|
|
|
if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
|
|
{
|
|
aNumPreferredEntries++;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
|
|
} // namespace NetworkData
|
|
} // namespace ot
|
|
|
|
#endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
|