openthread/src/core/thread/network_data_publisher.hpp

485 lines
21 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 includes definition of Network Data Publisher.
*/
#ifndef NETWORK_DATA_PUBLISHER_HPP_
#define NETWORK_DATA_PUBLISHER_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
#if !OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE && !OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
#error "OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE requires either OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE"\
"or OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE"
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && (OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES < \
(OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 4))
#error "OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES needs to support more entries when "\
"OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE is enabled to accommodate for max on-link prefixes"
#endif
#include <openthread/netdata_publisher.h>
#include "common/clearable.hpp"
#include "common/equatable.hpp"
#include "common/error.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/notifier.hpp"
#include "common/string.hpp"
#include "common/timer.hpp"
#include "net/ip6_address.hpp"
#include "thread/network_data_types.hpp"
namespace ot {
namespace NetworkData {
/**
* This class implements the Network Data Publisher.
*
* It provides mechanisms to limit the number of similar Service and/or Prefix (on-mesh prefix or external route)
* entries in the Thread Network Data by monitoring the Network Data and managing if or when to add or remove entries.
*
*/
class Publisher : public InstanceLocator, private NonCopyable
{
friend class ot::Notifier;
public:
/**
* This enumeration represents the events reported from the Publisher callbacks.
*
*/
enum Event : uint8_t
{
kEventEntryAdded = OT_NETDATA_PUBLISHER_EVENT_ENTRY_ADDED, ///< Entry is added to Network Data.
kEventEntryRemoved = OT_NETDATA_PUBLISHER_EVENT_ENTRY_REMOVED, ///< Entry is removed from Network Data.
};
/**
* This constructor initializes `Publisher` object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit Publisher(Instance &aInstance);
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
/**
* This type represents the callback function pointer used to notify when a "DNS/SRP Service" entry is added to or
* removed from the Thread Network Data.
*
* On remove the callback is invoked independent of whether the entry is removed by `Publisher` (e.g., when there
* are too many similar entries already present in the Network Data) or through an explicit call to unpublish the
* entry (i.e., a call to `UnpublishDnsSrpService()`).
*
*/
typedef otNetDataDnsSrpServicePublisherCallback DnsSrpServiceCallback;
/**
* This method sets a callback for notifying when a published "DNS/SRP Service" is actually added to or removed
* from the Thread Network Data.
*
* A subsequent call to this method replaces any previously set callback function.
*
* @param[in] aCallback The callback function pointer (can be NULL if not needed).
* @param[in] aContext A pointer to application-specific context (used when @p aCallback is invoked).
*
*/
void SetDnsSrpServiceCallback(DnsSrpServiceCallback aCallback, void *aContext)
{
mDnsSrpServiceEntry.SetCallback(aCallback, aContext);
}
/**
* This method requests "DNS/SRP Service Anycast Address" to be published in the Thread Network Data.
*
* A call to this method will remove and replace any previous "DNS/SRP Service" entry that was being published
* (from earlier call to any of `PublishDnsSrpService{Type}()` methods).
*
* @param[in] aSequenceNumber The sequence number of DNS/SRP Anycast Service.
*
*/
void PublishDnsSrpServiceAnycast(uint8_t aSequenceNumber) { mDnsSrpServiceEntry.PublishAnycast(aSequenceNumber); }
/**
* This method requests "DNS/SRP Service Unicast Address" to be published in the Thread Network Data.
*
* A call to this method will remove and replace any previous "DNS/SRP Service" entry that was being published
* (from earlier call to any of `PublishDnsSrpService{Type}()` methods).
*
* This method publishes the "DNS/SRP Service Unicast Address" by including the address and port info in the
* Service TLV data.
*
* @param[in] aAddress The DNS/SRP server address to publish.
* @param[in] aPort The SRP server port number to publish.
*
*/
void PublishDnsSrpServiceUnicast(const Ip6::Address &aAddress, uint16_t aPort)
{
mDnsSrpServiceEntry.PublishUnicast(aAddress, aPort);
}
/**
* This method requests "DNS/SRP Service Unicast Address" to be published in the Thread Network Data.
*
* A call to this method will remove and replace any previous "DNS/SRP Service" entry that was being published
* (from earlier call to any of `PublishDnsSrpService{Type}()` methods).
*
* Unlike the `PublishDnsSrpServiceUnicast(aAddress, aPort)` which requires the published address to be given and
* includes the info in the Service TLV data, this method uses the device's mesh-local EID and includes the info
* in the Server TLV data.
*
* @param[in] aPort The SRP server port number to publish.
*
*/
void PublishDnsSrpServiceUnicast(uint16_t aPort) { mDnsSrpServiceEntry.PublishUnicast(aPort); }
/**
* This method indicates whether or not currently the "DNS/SRP Service" entry is added to the Thread Network Data.
*
* @retval TRUE The published DNS/SRP Service entry is added to the Thread Network Data.
* @retval FALSE The entry is not added to Thread Network Data or there is no entry to publish.
*
*/
bool IsDnsSrpServiceAdded(void) const { return mDnsSrpServiceEntry.IsAdded(); }
/**
* This method unpublishes any previously added "DNS/SRP (Anycast or Unicast) Service" entry from the Thread
* Network Data.
*
*/
void UnpublishDnsSrpService(void) { mDnsSrpServiceEntry.Unpublish(); }
#endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
/**
* This type represents the callback function pointer used to notify when a prefix (on-mesh or external route)
* entry is added to or removed from the Thread Network Data.
*
* On remove the callback is invoked independent of whether the entry is removed by `Publisher` (e.g., when there
* are too many similar entries already present in the Network Data) or through an explicit call to unpublish the
* entry.
*
*/
typedef otNetDataPrefixPublisherCallback PrefixCallback;
/**
* This method sets a callback for notifying when a published prefix entry is actually added to or removed from
* the Thread Network Data.
*
* A subsequent call to this method replaces any previously set callback function.
*
* @param[in] aCallback The callback function pointer (can be NULL if not needed).
* @param[in] aContext A pointer to application-specific context (used when @p aCallback is invoked).
*
*/
void SetPrefixCallback(PrefixCallback aCallback, void *aContext);
/**
* This method requests an on-mesh prefix to be published in the Thread Network Data.
*
* Only stable entries can be published (i.e.,`aConfig.mStable` MUST be `true`).
*
* A subsequent call to this method will replace a previous request for the same prefix. In particular if the
* new call only changes the flags (e.g., preference level) and the prefix is already added in the Network Data,
* the change to flags is immediately reflected in the Network Data. This ensures that existing entries in the
* Network Data are not abruptly removed. Note that a change in the preference level can potentially later cause
* the entry to be removed from the Network Data after determining there are other nodes that are publishing the
* same prefix with the same or higher preference.
*
* @param[in] aConfig The on-mesh prefix config to publish.
*
* @retval kErrorNone The on-mesh prefix is published successfully.
* @retval kErrorInvalidArgs The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable).
* @retval kErrorAlready An entry with the same prefix is already in the published list.
* @retval kErrorNoBufs Could not allocate an entry for the new request. Publisher supports a limited number
* of entries (shared between on-mesh prefix and external route) determined by config
* `OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES`.
*
*
*/
Error PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig);
/**
* This method requests an external route prefix to be published in the Thread Network Data.
*
* Only stable entries can be published (i.e.,`aConfig.mStable` MUST be `true`).
*
* A subsequent call to this method will replace a previous request for the same prefix. In particular if the
* new call only changes the flags (e.g., preference level) and the prefix is already added in the Network Data,
* the change to flags is immediately reflected in the Network Data. This ensures that existing entries in the
* Network Data are not abruptly removed. Note that a change in the preference level can potentially later cause
* the entry to be removed from the Network Data after determining there are other nodes that are publishing the
* same prefix with the same or higher preference.
*
* @param[in] aConfig The external route config to publish.
*
* @retval kErrorNone The external route is published successfully.
* @retval kErrorInvalidArgs The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable).
* @retval kErrorNoBufs Could not allocate an entry for the new request. Publisher supports a limited number
* of entries (shared between on-mesh prefix and external route) determined by config
* `OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES`.
*
*
*/
Error PublishExternalRoute(const ExternalRouteConfig &aConfig);
/**
* This method indicates whether or not currently a published prefix entry (on-mesh or external route) is added to
* the Thread Network Data.
*
* @param[in] aPrefix The prefix to check.
*
* @retval TRUE The published prefix entry is added to the Thread Network Data.
* @retval FALSE The entry is not added to Thread Network Data or there is no matching entry to publish.
*
*/
bool IsPrefixAdded(const Ip6::Prefix &aPrefix) const;
/**
* This method unpublishes a previously published prefix (on-mesh or external route).
*
* @param[in] aPrefix The prefix to unpublish.
*
* @retval kErrorNone The prefix was unpublished successfully.
* @retval kErrorNotFound Could not find the prefix in the published list.
*
*/
Error UnpublishPrefix(const Ip6::Prefix &aPrefix);
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
private:
class Entry : public InstanceLocatorInit
{
protected:
enum State : uint8_t
{
kNoEntry, // Entry is unused (there is no entry).
kToAdd, // Entry is ready to be added, monitoring network data to decide if/when to add it.
kAdding, // Entry is being added in network data (random wait interval before add).
kAdded, // Entry is added in network data, monitoring to determine if/when to remove.
kRemoving, // Entry is being removed from network data (random wait interval before remove).
};
// All intervals are in milliseconds.
static constexpr uint32_t kMaxDelayToAdd = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_DELAY_TO_ADD;
static constexpr uint32_t kMaxDelayToRemove = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_DELAY_TO_REMOVE;
static constexpr uint32_t kExtraDelayToRemovePeferred =
OPENTHREAD_CONFIG_NETDATA_PUBLISHER_EXTRA_DELAY_TIME_TO_REMOVE_PREFERRED;
static constexpr uint16_t kInfoStringSize = 50;
typedef String<kInfoStringSize> InfoString;
Entry(void)
: mState(kNoEntry)
{
}
void Init(Instance &aInstance) { InstanceLocatorInit::Init(aInstance); }
State GetState(void) const { return mState; }
void SetState(State aState);
const TimeMilli &GetUpdateTime(void) const { return mUpdateTime; }
bool IsPreferred(uint16_t aRloc16) const;
void UpdateState(uint8_t aNumEntries, uint8_t aNumPreferredEntries, uint8_t aDesiredNumEntries);
void HandleTimer(void);
InfoString ToString(bool aIncludeState = true) const;
public:
bool IsAdded(void) const { return (mState == kAdded); }
private:
void Add(void);
void Remove(State aNextState);
void LogUpdateTime(void) const;
static const char *StateToString(State aState);
TimeMilli mUpdateTime;
State mState;
};
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
class DnsSrpServiceEntry : public Entry, private NonCopyable
{
friend class Entry;
public:
explicit DnsSrpServiceEntry(Instance &aInstance);
void SetCallback(DnsSrpServiceCallback aCallback, void *aContext);
void PublishAnycast(uint8_t aSequenceNumber);
void PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort);
void PublishUnicast(uint16_t aPort);
void Unpublish(void);
void HandleTimer(void) { Entry::HandleTimer(); }
void HandleNotifierEvents(Events aEvents);
private:
static constexpr uint8_t kDesiredNumAnycast =
OPENTHREAD_CONFIG_NETDATA_PUBLISHER_DESIRED_NUM_ANYCAST_DNS_SRP_SERVICE_ENTRIES;
static constexpr uint8_t kDesiredNumUnicast =
OPENTHREAD_CONFIG_NETDATA_PUBLISHER_DESIRED_NUM_UNICAST_DNS_SRP_SERVICE_ENTRIES;
enum Type : uint8_t
{
kTypeAnycast,
kTypeUnicast,
kTypeUnicastMeshLocalEid,
};
class Info : public Clearable<Info>, public Equatable<Info>
{
public:
Info(void) { Clear(); }
Type GetType(void) const { return mType; }
uint8_t GetSequenceNumber(void) const { return static_cast<uint8_t>(mPortOrSeqNumber); }
uint16_t GetPort(void) const { return mPortOrSeqNumber; }
const Ip6::Address &GetAddress(void) const { return mAddress; }
void SetAddress(const Ip6::Address &aAddress) { mAddress = aAddress; }
static Info InfoAnycast(uint8_t aSequenceNumber) { return Info(kTypeAnycast, aSequenceNumber); }
static Info InfoUnicast(Type aType, const Ip6::Address &aAddress, uint16_t aPort)
{
return Info(aType, aPort, &aAddress);
}
private:
Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress = nullptr);
Ip6::Address mAddress;
uint16_t mPortOrSeqNumber;
Type mType;
};
Type GetType(void) const { return mInfo.GetType(); }
void Publish(const Info &aInfo);
void Add(void);
void Remove(State aNextState);
void Notify(Event aEvent) const;
void Process(void);
void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
void CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
Info mInfo;
DnsSrpServiceCallback mCallback;
void * mCallbackContext;
};
#endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
// Max number of prefix (on-mesh or external route) entries.
static constexpr uint16_t kMaxPrefixEntries = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES;
class PrefixEntry : public Entry, private NonCopyable
{
friend class Entry;
public:
void Init(Instance &aInstance) { Entry::Init(aInstance); }
bool IsInUse(void) const { return GetState() != kNoEntry; }
bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; }
void Publish(const OnMeshPrefixConfig &aConfig);
void Publish(const ExternalRouteConfig &aConfig);
void Unpublish(void);
void HandleTimer(void) { Entry::HandleTimer(); }
void HandleNotifierEvents(Events aEvents);
private:
static constexpr uint8_t kDesiredNumOnMeshPrefix =
OPENTHREAD_CONFIG_NETDATA_PUBLISHER_DESIRED_NUM_ON_MESH_PREFIX_ENTRIES;
static constexpr uint8_t kDesiredNumExternalRoute =
OPENTHREAD_CONFIG_NETDATA_PUBLISHER_DESIRED_NUM_EXTERNAL_ROUTE_ENTRIES;
enum Type : uint8_t
{
kTypeOnMeshPrefix,
kTypeExternalRoute,
};
void Publish(const Ip6::Prefix &aPrefix, uint16_t aNewFlags, Type aNewType);
void Add(void);
Error AddOnMeshPrefix(void);
Error AddExternalRoute(void);
void Remove(State aNextState);
void Process(void);
void CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
void CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
Type mType;
Ip6::Prefix mPrefix;
uint16_t mFlags;
};
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
bool IsADnsSrpServiceEntry(const Entry &aEntry) const { return (&aEntry == &mDnsSrpServiceEntry); }
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
PrefixEntry * FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix);
PrefixEntry * FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix);
const PrefixEntry *FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix) const;
bool IsAPrefixEntry(const Entry &aEntry) const;
void NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const;
#endif
TimerMilli &GetTimer(void) { return mTimer; }
void HandleNotifierEvents(Events aEvents);
static void HandleTimer(Timer &aTimer);
void HandleTimer(void);
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
DnsSrpServiceEntry mDnsSrpServiceEntry;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
PrefixEntry mPrefixEntries[kMaxPrefixEntries];
PrefixCallback mPrefixCallback;
void * mPrefixCallbackContext;
#endif
TimerMilli mTimer;
};
} // namespace NetworkData
} // namespace ot
#endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
#endif // NETWORK_DATA_PUBLISHER_HPP_