| /* |
| * Copyright (c) 2020, 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 definitions for the RA-based routing management. |
| * |
| */ |
| |
| #ifndef ROUTING_MANAGER_HPP_ |
| #define ROUTING_MANAGER_HPP_ |
| |
| #include "openthread-core-config.h" |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE |
| |
| #if !OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE |
| #error "OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE." |
| #endif |
| |
| #if !OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE |
| #error "OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE." |
| #endif |
| |
| #include <openthread/border_routing.h> |
| #include <openthread/nat64.h> |
| #include <openthread/netdata.h> |
| |
| #include "border_router/infra_if.hpp" |
| #include "common/array.hpp" |
| #include "common/callback.hpp" |
| #include "common/error.hpp" |
| #include "common/heap_allocatable.hpp" |
| #include "common/heap_array.hpp" |
| #include "common/heap_data.hpp" |
| #include "common/linked_list.hpp" |
| #include "common/locator.hpp" |
| #include "common/message.hpp" |
| #include "common/notifier.hpp" |
| #include "common/pool.hpp" |
| #include "common/string.hpp" |
| #include "common/timer.hpp" |
| #include "crypto/sha256.hpp" |
| #include "net/ip6.hpp" |
| #include "net/nat64_translator.hpp" |
| #include "net/nd6.hpp" |
| #include "thread/network_data.hpp" |
| |
| namespace ot { |
| |
| namespace BorderRouter { |
| |
| /** |
| * Implements bi-directional routing between Thread and Infrastructure networks. |
| * |
| * The Border Routing manager works on both Thread interface and infrastructure interface. |
| * All ICMPv6 messages are sent/received on the infrastructure interface. |
| * |
| */ |
| class RoutingManager : public InstanceLocator |
| { |
| friend class ot::Notifier; |
| friend class ot::Instance; |
| |
| public: |
| typedef NetworkData::RoutePreference RoutePreference; ///< Route preference (high, medium, low). |
| typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator. |
| typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry. |
| typedef otBorderRoutingRouterEntry RouterEntry; ///< Router Entry. |
| typedef otPdProcessedRaInfo PdProcessedRaInfo; ///< Data of PdProcessedRaInfo. |
| typedef otBorderRoutingRequestDhcp6PdCallback PdCallback; ///< DHCPv6 PD callback. |
| |
| /** |
| * This constant specifies the maximum number of route prefixes that may be published by `RoutingManager` |
| * in Thread Network Data. |
| * |
| * This is used by `NetworkData::Publisher` to reserve entries for use by `RoutingManager`. |
| * |
| * The number of published entries accounts for: |
| * - Route prefix `fc00::/7` or `::/0` |
| * - One entry for NAT64 published prefix. |
| * - One extra entry for transitions. |
| * |
| */ |
| static constexpr uint16_t kMaxPublishedPrefixes = 3; |
| |
| /** |
| * Represents the states of `RoutingManager`. |
| * |
| */ |
| enum State : uint8_t |
| { |
| kStateUninitialized = OT_BORDER_ROUTING_STATE_UNINITIALIZED, ///< Uninitialized. |
| kStateDisabled = OT_BORDER_ROUTING_STATE_DISABLED, ///< Initialized but disabled. |
| kStateStopped = OT_BORDER_ROUTING_STATE_STOPPED, ///< Initialized & enabled, but currently stopped. |
| kStateRunning = OT_BORDER_ROUTING_STATE_RUNNING, ///< Initialized, enabled, and running. |
| }; |
| |
| /** |
| * This enumeration represents the states of DHCPv6 PD in `RoutingManager`. |
| * |
| */ |
| enum Dhcp6PdState : uint8_t |
| { |
| kDhcp6PdStateDisabled = OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED, ///< Disabled. |
| kDhcp6PdStateStopped = OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED, ///< Enabled, but currently stopped. |
| kDhcp6PdStateRunning = OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING, ///< Enabled, and running. |
| }; |
| |
| /** |
| * Initializes the routing manager. |
| * |
| * @param[in] aInstance A OpenThread instance. |
| * |
| */ |
| explicit RoutingManager(Instance &aInstance); |
| |
| /** |
| * Initializes the routing manager on given infrastructure interface. |
| * |
| * @param[in] aInfraIfIndex An infrastructure network interface index. |
| * @param[in] aInfraIfIsRunning A boolean that indicates whether the infrastructure |
| * interface is running. |
| * |
| * @retval kErrorNone Successfully started the routing manager. |
| * @retval kErrorInvalidArgs The index of the infra interface is not valid. |
| * |
| */ |
| Error Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning); |
| |
| /** |
| * Enables/disables the Border Routing Manager. |
| * |
| * @note The Border Routing Manager is enabled by default. |
| * |
| * @param[in] aEnabled A boolean to enable/disable the Border Routing Manager. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * @retval kErrorNone Successfully enabled/disabled the Border Routing Manager. |
| * |
| */ |
| Error SetEnabled(bool aEnabled); |
| |
| /** |
| * Indicates whether or not it is currently running. |
| * |
| * In order for the `RoutingManager` to be running it needs to be initialized and enabled, and device being |
| * attached. |
| * |
| * @retval TRUE The RoutingManager is currently running. |
| * @retval FALSE The RoutingManager is not running. |
| * |
| */ |
| bool IsRunning(void) const { return mIsRunning; } |
| |
| /** |
| * Gets the state of `RoutingManager`. |
| * |
| * @returns The current state of `RoutingManager`. |
| * |
| */ |
| State GetState(void) const; |
| |
| /** |
| * Requests the Border Routing Manager to stop. |
| * |
| * If Border Routing Manager is running, calling this method immediately stops it and triggers the preparation |
| * and sending of a final Router Advertisement (RA) message on infrastructure interface which deprecates and/or |
| * removes any previously advertised PIO/RIO prefixes. If Routing Manager is not running (or not enabled), no |
| * action is taken. |
| * |
| * Note that this method does not change whether the Routing Manager is enabled or disabled (see `SetEnabled()`). |
| * It stops the Routing Manager temporarily. After calling this method if the device role gets changes (device |
| * gets attached) and/or the infra interface state gets changed, the Routing Manager may be started again. |
| * |
| */ |
| void RequestStop(void) { Stop(); } |
| |
| /** |
| * Gets the current preference used when advertising Route Info Options (RIO) in Router Advertisement |
| * messages sent over the infrastructure link. |
| * |
| * The RIO preference is determined as follows: |
| * |
| * - If explicitly set by user by calling `SetRouteInfoOptionPreference()`, the given preference is used. |
| * - Otherwise, it is determined based on device's role: Medium preference when in router/leader role and low |
| * preference when in child role. |
| * |
| * @returns The current Route Info Option preference. |
| * |
| */ |
| RoutePreference GetRouteInfoOptionPreference(void) const { return mRioAdvertiser.GetPreference(); } |
| |
| /** |
| * Explicitly sets the preference to use when advertising Route Info Options (RIO) in Router |
| * Advertisement messages sent over the infrastructure link. |
| * |
| * After a call to this method, BR will use the given preference for all its advertised RIOs. The preference can be |
| * cleared by calling `ClearRouteInfoOptionPreference`()`. |
| * |
| * @param[in] aPreference The route preference to use. |
| * |
| */ |
| void SetRouteInfoOptionPreference(RoutePreference aPreference) { mRioAdvertiser.SetPreference(aPreference); } |
| |
| /** |
| * Clears a previously set preference value for advertised Route Info Options. |
| * |
| * After a call to this method, BR will use device role to determine the RIO preference: Medium preference when |
| * in router/leader role and low preference when in child role. |
| * |
| */ |
| void ClearRouteInfoOptionPreference(void) { mRioAdvertiser.ClearPreference(); } |
| |
| /** |
| * Sets additional options to append at the end of emitted Router Advertisement (RA) messages. |
| * |
| * The content of @p aOptions is copied internally, so can be a temporary stack variable. |
| * |
| * Subsequent calls to this method will overwrite the previously set value. |
| * |
| * @param[in] aOptions A pointer to the encoded options. Can be `nullptr` to clear. |
| * @param[in] aLength Number of bytes in @p aOptions. |
| * |
| * @retval kErrorNone Successfully set the extra option bytes. |
| * @retval kErrorNoBufs Could not allocate buffer to save the buffer. |
| * |
| */ |
| Error SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength); |
| |
| /** |
| * Gets the current preference used for published routes in Network Data. |
| * |
| * The preference is determined as follows: |
| * |
| * - If explicitly set by user by calling `SetRoutePreference()`, the given preference is used. |
| * - Otherwise, it is determined automatically by `RoutingManager` based on the device's role and link quality. |
| * |
| * @returns The current published route preference. |
| * |
| */ |
| RoutePreference GetRoutePreference(void) const { return mRoutePublisher.GetPreference(); } |
| |
| /** |
| * Explicitly sets the preference of published routes in Network Data. |
| * |
| * After a call to this method, BR will use the given preference. The preference can be cleared by calling |
| * `ClearRoutePreference`()`. |
| * |
| * @param[in] aPreference The route preference to use. |
| * |
| */ |
| void SetRoutePreference(RoutePreference aPreference) { mRoutePublisher.SetPreference(aPreference); } |
| |
| /** |
| * Clears a previously set preference value for published routes in Network Data. |
| * |
| * After a call to this method, BR will determine the preference automatically based on the device's role and |
| * link quality (to the parent when acting as end-device). |
| * |
| */ |
| void ClearRoutePreference(void) { mRoutePublisher.ClearPreference(); } |
| |
| /** |
| * Returns the local generated off-mesh-routable (OMR) prefix. |
| * |
| * The randomly generated 64-bit prefix will be added to the Thread Network Data if there isn't already an OMR |
| * prefix. |
| * |
| * @param[out] aPrefix A reference to where the prefix will be output to. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * @retval kErrorNone Successfully retrieved the OMR prefix. |
| * |
| */ |
| Error GetOmrPrefix(Ip6::Prefix &aPrefix) const; |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| /** |
| * Returns the platform provided off-mesh-routable (OMR) prefix. |
| * |
| * The prefix is extracted from the platform generated RA messages handled by `ProcessPlatformGeneratedNd()`. |
| * |
| * @param[out] aPrefixInfo A reference to where the prefix info will be output to. |
| * |
| * @retval kErrorNone Successfully retrieved the OMR prefix. |
| * @retval kErrorNotFound There are no valid PD prefix on this BR. |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * |
| */ |
| Error GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const; |
| |
| /** |
| * Returns platform generated RA message processed information. |
| * |
| * @param[out] aPdProcessedRaInfo A reference to where the PD processed RA info will be output to. |
| * |
| * @retval kErrorNone Successfully retrieved the Info. |
| * @retval kErrorNotFound There are no valid RA process info on this BR. |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * |
| */ |
| Error GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo); |
| #endif |
| |
| /** |
| * Returns the currently favored off-mesh-routable (OMR) prefix. |
| * |
| * The favored OMR prefix can be discovered from Network Data or can be our local OMR prefix. |
| * |
| * An OMR prefix with higher preference is favored. If the preference is the same, then the smaller prefix (in the |
| * sense defined by `Ip6::Prefix`) is favored. |
| * |
| * @param[out] aPrefix A reference to output the favored prefix. |
| * @param[out] aPreference A reference to output the preference associated with the favored OMR prefix. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not running yet. |
| * @retval kErrorNone Successfully retrieved the OMR prefix. |
| * |
| */ |
| Error GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const; |
| |
| /** |
| * Returns the on-link prefix for the adjacent infrastructure link. |
| * |
| * The randomly generated 64-bit prefix will be advertised |
| * on the infrastructure link if there isn't already a usable |
| * on-link prefix being advertised on the link. |
| * |
| * @param[out] aPrefix A reference to where the prefix will be output to. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * @retval kErrorNone Successfully retrieved the local on-link prefix. |
| * |
| */ |
| Error GetOnLinkPrefix(Ip6::Prefix &aPrefix) const; |
| |
| /** |
| * Returns the favored on-link prefix for the adjacent infrastructure link. |
| * |
| * The favored prefix is either a discovered prefix on the infrastructure link or the local on-link prefix. |
| * |
| * @param[out] aPrefix A reference to where the prefix will be output to. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * @retval kErrorNone Successfully retrieved the favored on-link prefix. |
| * |
| */ |
| Error GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const; |
| |
| #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE |
| /** |
| * Gets the state of NAT64 prefix publishing. |
| * |
| * @retval kStateDisabled NAT64 is disabled. |
| * @retval kStateNotRunning NAT64 is enabled, but is not running since routing manager is not running. |
| * @retval kStateIdle NAT64 is enabled, but the border router is not publishing a NAT64 prefix. Usually |
| * when there is another border router publishing a NAT64 prefix with higher |
| * priority. |
| * @retval kStateActive The Border router is publishing a NAT64 prefix. |
| * |
| */ |
| Nat64::State GetNat64PrefixManagerState(void) const { return mNat64PrefixManager.GetState(); } |
| |
| /** |
| * Enable or disable NAT64 prefix publishing. |
| * |
| * @param[in] aEnabled A boolean to enable/disable NAT64 prefix publishing. |
| * |
| */ |
| void SetNat64PrefixManagerEnabled(bool aEnabled); |
| |
| /** |
| * Returns the local NAT64 prefix. |
| * |
| * @param[out] aPrefix A reference to where the prefix will be output to. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * @retval kErrorNone Successfully retrieved the NAT64 prefix. |
| * |
| */ |
| Error GetNat64Prefix(Ip6::Prefix &aPrefix); |
| |
| /** |
| * Returns the currently favored NAT64 prefix. |
| * |
| * The favored NAT64 prefix can be discovered from infrastructure link or can be the local NAT64 prefix. |
| * |
| * @param[out] aPrefix A reference to output the favored prefix. |
| * @param[out] aRoutePreference A reference to output the preference associated with the favored prefix. |
| * |
| * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. |
| * @retval kErrorNone Successfully retrieved the NAT64 prefix. |
| * |
| */ |
| Error GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference); |
| |
| /** |
| * Informs `RoutingManager` of the result of the discovery request of NAT64 prefix on infrastructure |
| * interface (`InfraIf::DiscoverNat64Prefix()`). |
| * |
| * @param[in] aPrefix The discovered NAT64 prefix on `InfraIf`. |
| * |
| */ |
| void HandleDiscoverNat64PrefixDone(const Ip6::Prefix &aPrefix) { mNat64PrefixManager.HandleDiscoverDone(aPrefix); } |
| |
| #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE |
| |
| /** |
| * Processes a received ICMPv6 message from the infrastructure interface. |
| * |
| * Malformed or undesired messages are dropped silently. |
| * |
| * @param[in] aPacket The received ICMPv6 packet. |
| * @param[in] aSrcAddress The source address this message is sent from. |
| * |
| */ |
| void HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); |
| |
| /** |
| * Handles infrastructure interface state changes. |
| * |
| */ |
| void HandleInfraIfStateChanged(void) { EvaluateState(); } |
| |
| /** |
| * Checks whether the on-mesh prefix configuration is a valid OMR prefix. |
| * |
| * @param[in] aOnMeshPrefixConfig The on-mesh prefix configuration to check. |
| * |
| * @retval TRUE The prefix is a valid OMR prefix. |
| * @retval FALSE The prefix is not a valid OMR prefix. |
| * |
| */ |
| static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); |
| |
| /** |
| * Checks whether a given prefix is a valid OMR prefix. |
| * |
| * @param[in] aPrefix The prefix to check. |
| * |
| * @retval TRUE The prefix is a valid OMR prefix. |
| * @retval FALSE The prefix is not a valid OMR prefix. |
| * |
| */ |
| static bool IsValidOmrPrefix(const Ip6::Prefix &aPrefix); |
| |
| /** |
| * Initializes a `PrefixTableIterator`. |
| * |
| * An iterator can be initialized again to start from the beginning of the table. |
| * |
| * When iterating over entries in the table, to ensure the entry update times are consistent, they are given |
| * relative to the time the iterator was initialized. |
| * |
| * @param[out] aIterator The iterator to initialize. |
| * |
| */ |
| void InitPrefixTableIterator(PrefixTableIterator &aIterator) const |
| { |
| mDiscoveredPrefixTable.InitIterator(aIterator); |
| } |
| |
| /** |
| * Iterates over entries in the discovered prefix table. |
| * |
| * @param[in,out] aIterator An iterator. |
| * @param[out] aEntry A reference to the entry to populate. |
| * |
| * @retval kErrorNone Got the next entry, @p aEntry is updated and @p aIterator is advanced. |
| * @retval kErrorNotFound No more entries in the table. |
| * |
| */ |
| Error GetNextPrefixTableEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const |
| { |
| return mDiscoveredPrefixTable.GetNextEntry(aIterator, aEntry); |
| } |
| |
| /** |
| * Iterates over discovered router entries on infrastructure link. |
| * |
| * @param[in,out] aIterator An iterator. |
| * @param[out] aEntry A reference to the entry to populate. |
| * |
| * @retval kErrorNone Got the next router info, @p aEntry is updated and @p aIterator is advanced. |
| * @retval kErrorNotFound No more routers. |
| * |
| */ |
| Error GetNextRouterEntry(PrefixTableIterator &aIterator, RouterEntry &aEntry) const |
| { |
| return mDiscoveredPrefixTable.GetNextRouter(aIterator, aEntry); |
| } |
| |
| #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE |
| /** |
| * Determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server. |
| * |
| * This should be called from `Srp::Server` when auto-enable mode is changed. |
| * |
| */ |
| void HandleSrpServerAutoEnableMode(void); |
| #endif |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| /** |
| * Handles a router advertisement message received on platform's Thread interface. |
| * |
| * Note: This method is a part of DHCPv6 PD support on Thread border routers. The message should be generated by the |
| * software like dnamasq, radvd, systemd-networkd on the platform as a part of the DHCPv6 prefix delegation process |
| * for distributing the prefix to the interfaces (links). |
| * |
| * @param[in] aRouterAdvert A pointer to the buffer of the router advertisement message. |
| * @param[in] aLength The length of the router advertisement message. |
| * |
| */ |
| void ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength) |
| { |
| mPdPrefixManager.ProcessPlatformGeneratedRa(aRouterAdvert, aLength); |
| } |
| |
| /** |
| * Handles a prefix delegated from a DHCPv6 PD server. The prefix is received on the DHCPv6 PD client callback and |
| * then this method can be used to configure the prefix in the Routing Manager module. |
| * |
| * Note: This method is a part of DHCPv6 PD support on Thread border routers. For platforms where it doesn't make |
| * sense to generate a RA to set a DHCPv6 PD prefix this method can be used to set the prefix directly. The lifetime |
| * of the prefix can be updated by calling the function again with updated values. |
| * |
| * @param[in] aPrefixInfo Prefix information structure received from the DHCPv6 PD server. |
| * |
| */ |
| void ProcessDhcpPdPrefix(const PrefixTableEntry &aPrefixInfo) { mPdPrefixManager.ProcessDhcpPdPrefix(aPrefixInfo); } |
| |
| /** |
| * Enables / Disables the functions for DHCPv6 PD. |
| * |
| * @param[in] aEnabled Whether to accept platform generated RA messages. |
| * |
| */ |
| void SetDhcp6PdEnabled(bool aEnabled) { return mPdPrefixManager.SetEnabled(aEnabled); } |
| |
| /** |
| * Returns the state of accpeting RouterAdvertisement messages on platform interface. |
| * |
| * @retval kDhcp6PdStateRunning DHCPv6 PD should be enabled and running on this border router. |
| * @retval kDhcp6PdStateDisabled DHCPv6 PD should be disabled on this border router.. |
| * |
| */ |
| Dhcp6PdState GetDhcp6PdState(void) const { return mPdPrefixManager.GetState(); } |
| |
| /** |
| * Sets the callback whenever the state of a prefix request or release, via the DHCPv6 Prefix Delegation (PD), |
| * changes on the Thread interface. |
| * |
| * @param[in] aCallback A pointer to a function that is called whenever the state of a prefix request or release |
| * changes. |
| * @param[in] aContext A pointer to arbitrary context information. |
| * |
| */ |
| void SetRequestDhcp6PdCallback(PdCallback aCallback, void *aContext) |
| { |
| mPdPrefixManager.SetRequestDhcp6PdCallback(aCallback, aContext); |
| } |
| |
| #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| |
| private: |
| static constexpr uint8_t kMaxOnMeshPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES; |
| |
| static constexpr uint8_t kOmrPrefixLength = OT_IP6_PREFIX_BITSIZE; // The length of an OMR prefix. In bits. |
| static constexpr uint8_t kOnLinkPrefixLength = OT_IP6_PREFIX_BITSIZE; // The length of an On-link prefix. In bits. |
| static constexpr uint8_t kBrUlaPrefixLength = 48; // The length of a BR ULA prefix. In bits. |
| static constexpr uint8_t kNat64PrefixLength = 96; // The length of a NAT64 prefix. In bits. |
| |
| static constexpr uint16_t kOmrPrefixSubnetId = 1; // The subnet ID of an OMR prefix within a BR ULA prefix. |
| static constexpr uint16_t kNat64PrefixSubnetId = 2; // The subnet ID of a NAT64 prefix within a BR ULA prefix. |
| |
| // The maximum number of initial Router Advertisements. |
| static constexpr uint32_t kMaxInitRtrAdvertisements = 3; |
| |
| static constexpr uint32_t kDefaultOmrPrefixLifetime = 1800; // The default OMR prefix valid lifetime. In sec. |
| static constexpr uint32_t kDefaultOnLinkPrefixLifetime = 1800; // The default on-link prefix valid lifetime. In sec. |
| static constexpr uint32_t kDefaultNat64PrefixLifetime = 300; // The default NAT64 prefix valid lifetime. In sec. |
| static constexpr uint32_t kMaxRtrAdvInterval = 600; // Max Router Advertisement Interval. In sec. |
| static constexpr uint32_t kMinRtrAdvInterval = kMaxRtrAdvInterval / 3; // Min RA Interval. In sec. |
| static constexpr uint32_t kMaxInitRtrAdvInterval = 16; // Max Initial RA Interval. In sec. |
| static constexpr uint32_t kRaReplyJitter = 500; // Jitter for sending RA after rx RS. In msec. |
| static constexpr uint32_t kPolicyEvaluationMinDelay = 2000; // Min delay for policy evaluation. In msec. |
| static constexpr uint32_t kPolicyEvaluationMaxDelay = 4000; // Max delay for policy evaluation. In msec. |
| static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. |
| |
| // The STALE_RA_TIME in seconds. The Routing Manager will consider the prefixes |
| // and learned RA parameters STALE when they are not refreshed in STALE_RA_TIME |
| // seconds. The Routing Manager will then start Router Solicitation to verify |
| // that the STALE prefix is not being advertised anymore and remove the STALE |
| // prefix. |
| // The value is chosen in range of [`kMaxRtrAdvInterval` upper bound (1800s), `kDefaultOnLinkPrefixLifetime`]. |
| static constexpr uint32_t kRtrAdvStaleTime = 1800; |
| |
| static_assert(kMinRtrAdvInterval <= 3 * kMaxRtrAdvInterval / 4, "invalid RA intervals"); |
| static_assert(kDefaultOmrPrefixLifetime >= kMaxRtrAdvInterval, "invalid default OMR prefix lifetime"); |
| static_assert(kDefaultOnLinkPrefixLifetime >= kMaxRtrAdvInterval, "invalid default on-link prefix lifetime"); |
| static_assert(kRtrAdvStaleTime >= 1800 && kRtrAdvStaleTime <= kDefaultOnLinkPrefixLifetime, |
| "invalid RA STALE time"); |
| static_assert(kPolicyEvaluationMaxDelay > kPolicyEvaluationMinDelay, |
| "kPolicyEvaluationMaxDelay must be larger than kPolicyEvaluationMinDelay"); |
| |
| using Option = Ip6::Nd::Option; |
| using PrefixInfoOption = Ip6::Nd::PrefixInfoOption; |
| using RouteInfoOption = Ip6::Nd::RouteInfoOption; |
| using RaFlagsExtOption = Ip6::Nd::RaFlagsExtOption; |
| using RouterAdvert = Ip6::Nd::RouterAdvert; |
| using NeighborAdvertMessage = Ip6::Nd::NeighborAdvertMessage; |
| using NeighborSolicitMessage = Ip6::Nd::NeighborSolicitMessage; |
| using RouterSolicitMessage = Ip6::Nd::RouterSolicitMessage; |
| |
| enum RouterAdvTxMode : uint8_t // Used in `SendRouterAdvertisement()` |
| { |
| kInvalidateAllPrevPrefixes, |
| kAdvPrefixesFromNetData, |
| }; |
| |
| enum ScheduleMode : uint8_t // Used in `ScheduleRoutingPolicyEvaluation()` |
| { |
| kImmediately, |
| kForNextRa, |
| kAfterRandomDelay, |
| kToReplyToRs, |
| }; |
| |
| void HandleDiscoveredPrefixTableChanged(void); // Declare early so we can use in `mSignalTask` |
| void HandleDiscoveredPrefixTableEntryTimer(void) { mDiscoveredPrefixTable.HandleEntryTimer(); } |
| void HandleDiscoveredPrefixTableRouterTimer(void) { mDiscoveredPrefixTable.HandleRouterTimer(); } |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| class PdPrefixManager; // For DiscoveredPrefixTable::Entry |
| #endif |
| |
| class DiscoveredPrefixTable : public InstanceLocator |
| { |
| // This class maintains the discovered on-link and route prefixes |
| // from the received RA messages by processing PIO and RIO options |
| // from the message. It takes care of processing the RA message but |
| // delegates the decision whether to include or exclude a prefix to |
| // `RoutingManager` by calling its `ShouldProcessPrefixInfoOption()` |
| // and `ShouldProcessRouteInfoOption()` methods. |
| // |
| // It manages the lifetime of the discovered entries and publishes |
| // and unpublishes the prefixes in the Network Data (as external |
| // route) as they are added or removed. |
| // |
| // When there is any change in the table (an entry is added, removed, |
| // or modified), it signals the change to `RoutingManager` by calling |
| // `HandleDiscoveredPrefixTableChanged()` callback. A `Tasklet` is |
| // used for signalling which ensures that if there are multiple |
| // changes within the same flow of execution, the callback is |
| // invoked after all the changes are processed. |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| friend class PdPrefixManager; // For DiscoveredPrefixTable::Entry |
| #endif |
| |
| public: |
| explicit DiscoveredPrefixTable(Instance &aInstance); |
| |
| void ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage, const Ip6::Address &aSrcAddress); |
| void ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage); |
| |
| bool ContainsDefaultOrNonUlaRoutePrefix(void) const; |
| bool ContainsNonUlaOnLinkPrefix(void) const; |
| bool ContainsUlaOnLinkPrefix(void) const; |
| |
| void FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const; |
| |
| void RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix); |
| void RemoveRoutePrefix(const Ip6::Prefix &aPrefix); |
| |
| void RemoveAllEntries(void); |
| void RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold); |
| |
| TimeMilli CalculateNextStaleTime(TimeMilli aNow) const; |
| |
| void DetermineAndSetFlags(RouterAdvert::Header &aHeader) const; |
| |
| void InitIterator(PrefixTableIterator &aIterator) const; |
| Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const; |
| Error GetNextRouter(PrefixTableIterator &aIterator, RouterEntry &aEntry) const; |
| |
| void HandleEntryTimer(void); |
| void HandleRouterTimer(void); |
| |
| private: |
| #if !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE |
| static constexpr uint16_t kMaxRouters = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS; |
| static constexpr uint16_t kMaxEntries = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES; |
| #endif |
| |
| class Entry : public LinkedListEntry<Entry>, |
| public Unequatable<Entry>, |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE |
| public Heap::Allocatable<Entry>, |
| #endif |
| private Clearable<Entry> |
| { |
| friend class LinkedListEntry<Entry>; |
| friend class Clearable<Entry>; |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| friend class PdPrefixManager; |
| #endif |
| |
| public: |
| enum Type : uint8_t |
| { |
| kTypeOnLink, |
| kTypeRoute, |
| }; |
| |
| struct Matcher |
| { |
| Matcher(const Ip6::Prefix &aPrefix, Type aType) |
| : mPrefix(aPrefix) |
| , mType(aType) |
| { |
| } |
| |
| const Ip6::Prefix &mPrefix; |
| Type mType; |
| }; |
| |
| struct Checker |
| { |
| enum Mode : uint8_t |
| { |
| kIsUla, |
| kIsNotUla, |
| }; |
| |
| Checker(Mode aMode, Type aType) |
| : mMode(aMode) |
| , mType(aType) |
| |
| { |
| } |
| |
| Mode mMode; |
| Type mType; |
| }; |
| |
| struct ExpirationChecker |
| { |
| explicit ExpirationChecker(TimeMilli aNow) |
| : mNow(aNow) |
| { |
| } |
| |
| TimeMilli mNow; |
| }; |
| |
| void SetFrom(const RouterAdvert::Header &aRaHeader); |
| void SetFrom(const PrefixInfoOption &aPio); |
| void SetFrom(const RouteInfoOption &aRio); |
| void SetFrom(const PrefixTableEntry &aPrefixTableEntry); |
| Type GetType(void) const { return mType; } |
| bool IsOnLinkPrefix(void) const { return (mType == kTypeOnLink); } |
| bool IsRoutePrefix(void) const { return (mType == kTypeRoute); } |
| const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } |
| const TimeMilli &GetLastUpdateTime(void) const { return mLastUpdateTime; } |
| uint32_t GetValidLifetime(void) const { return mValidLifetime; } |
| void ClearValidLifetime(void) { mValidLifetime = 0; } |
| TimeMilli GetExpireTime(void) const; |
| TimeMilli GetStaleTime(void) const; |
| TimeMilli GetStaleTimeFromPreferredLifetime(void) const; |
| RoutePreference GetPreference(void) const; |
| bool operator==(const Entry &aOther) const; |
| bool Matches(const Matcher &aMatcher) const; |
| bool Matches(const Checker &aChecker) const; |
| bool Matches(const ExpirationChecker &aChecker) const; |
| |
| // Methods to use when `IsOnLinkPrefix()` |
| uint32_t GetPreferredLifetime(void) const { return mShared.mPreferredLifetime; } |
| void ClearPreferredLifetime(void) { mShared.mPreferredLifetime = 0; } |
| bool IsDeprecated(void) const; |
| void AdoptValidAndPreferredLifetimesFrom(const Entry &aEntry); |
| |
| // Method to use when `!IsOnlinkPrefix()` |
| RoutePreference GetRoutePreference(void) const { return mShared.mRoutePreference; } |
| |
| private: |
| static uint32_t CalculateExpireDelay(uint32_t aValidLifetime); |
| |
| Entry *mNext; |
| Ip6::Prefix mPrefix; |
| Type mType; |
| TimeMilli mLastUpdateTime; |
| uint32_t mValidLifetime; |
| union |
| { |
| uint32_t mPreferredLifetime; // Applicable when prefix is on-link. |
| RoutePreference mRoutePreference; // Applicable when prefix is not on-link |
| } mShared; |
| }; |
| |
| struct Router : public LinkedListEntry<Router>, |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE |
| public Heap::Allocatable<Router>, |
| #endif |
| public Clearable<Router> |
| { |
| // The timeout (in msec) for router staying in active state |
| // before starting the Neighbor Solicitation (NS) probes. |
| static constexpr uint32_t kActiveTimeout = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; |
| |
| static constexpr uint8_t kMaxNsProbes = 5; // Max number of NS probe attempts. |
| static constexpr uint32_t kNsProbeRetryInterval = 1000; // In msec. Time between NS probe attempts. |
| static constexpr uint32_t kNsProbeTimeout = 2000; // In msec. Max Wait time after last NS probe. |
| static constexpr uint32_t kJitter = 2000; // In msec. Jitter to randomize probe starts. |
| |
| static_assert(kMaxNsProbes < 255, "kMaxNsProbes MUST not be 255"); |
| |
| enum EmptyChecker : uint8_t |
| { |
| kContainsNoEntriesOrFlags |
| }; |
| |
| bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } |
| bool Matches(EmptyChecker aChecker) const; |
| void CopyInfoTo(RouterEntry &aEntry) const; |
| |
| Router *mNext; |
| Ip6::Address mAddress; |
| LinkedList<Entry> mEntries; |
| TimeMilli mTimeout; |
| uint8_t mNsProbeCount; |
| bool mManagedAddressConfigFlag : 1; |
| bool mOtherConfigFlag : 1; |
| bool mStubRouterFlag : 1; |
| }; |
| |
| class Iterator : public PrefixTableIterator |
| { |
| public: |
| enum AdvanceMode : uint8_t |
| { |
| kToNextEntry, |
| kToNextRouter, |
| }; |
| |
| void Init(const LinkedList<Router> &aRouters); |
| void Advance(AdvanceMode aMode); |
| |
| const Router *GetRouter(void) const { return static_cast<const Router *>(mPtr1); } |
| const Entry *GetEntry(void) const { return static_cast<const Entry *>(mPtr2); } |
| TimeMilli GetInitTime(void) const { return TimeMilli(mData32); } |
| |
| private: |
| void SetRouter(const Router *aRouter) { mPtr1 = aRouter; } |
| void SetEntry(const Entry *aEntry) { mPtr2 = aEntry; } |
| void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } |
| }; |
| |
| void ProcessRaHeader(const RouterAdvert::Header &aRaHeader, Router &aRouter); |
| void ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter); |
| void ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter); |
| void ProcessRaFlagsExtOption(const RaFlagsExtOption &aFlagsOption, Router &aRouter); |
| bool Contains(const Entry::Checker &aChecker) const; |
| void RemovePrefix(const Entry::Matcher &aMatcher); |
| void RemoveOrDeprecateEntriesFromInactiveRouters(void); |
| void RemoveRoutersWithNoEntriesOrFlags(void); |
| void FreeRouters(LinkedList<Router> &aRouters); |
| void FreeEntries(LinkedList<Entry> &aEntries); |
| void UpdateNetworkDataOnChangeTo(Entry &aEntry); |
| const Entry *FindFavoredEntryToPublish(const Ip6::Prefix &aPrefix) const; |
| void RemoveExpiredEntries(void); |
| void SignalTableChanged(void); |
| void UpdateRouterOnRx(Router &aRouter); |
| void SendNeighborSolicitToRouter(const Router &aRouter); |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE |
| Router *AllocateRouter(void) { return Router::Allocate(); } |
| Entry *AllocateEntry(void) { return Entry::Allocate(); } |
| void FreeRouter(Router &aRouter) { aRouter.Free(); } |
| void FreeEntry(Entry &aEntry) { aEntry.Free(); } |
| #else |
| Router *AllocateRouter(void) { return mRouterPool.Allocate(); } |
| Entry *AllocateEntry(void) { return mEntryPool.Allocate(); } |
| void FreeRouter(Router &aRouter) { mRouterPool.Free(aRouter); } |
| void FreeEntry(Entry &aEntry) { mEntryPool.Free(aEntry); } |
| #endif |
| |
| using SignalTask = TaskletIn<RoutingManager, &RoutingManager::HandleDiscoveredPrefixTableChanged>; |
| using EntryTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleDiscoveredPrefixTableEntryTimer>; |
| using RouterTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleDiscoveredPrefixTableRouterTimer>; |
| |
| LinkedList<Router> mRouters; |
| EntryTimer mEntryTimer; |
| RouterTimer mRouterTimer; |
| SignalTask mSignalTask; |
| #if !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE |
| Pool<Entry, kMaxEntries> mEntryPool; |
| Pool<Router, kMaxRouters> mRouterPool; |
| #endif |
| }; |
| |
| class OmrPrefixManager; |
| |
| class OmrPrefix : public Clearable<OmrPrefix> |
| { |
| friend class OmrPrefixManager; |
| |
| public: |
| OmrPrefix(void) { Clear(); } |
| |
| bool IsEmpty(void) const { return (mPrefix.GetLength() == 0); } |
| const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } |
| RoutePreference GetPreference(void) const { return mPreference; } |
| bool IsDomainPrefix(void) const { return mIsDomainPrefix; } |
| |
| protected: |
| Ip6::Prefix mPrefix; |
| RoutePreference mPreference; |
| bool mIsDomainPrefix; |
| }; |
| |
| class FavoredOmrPrefix : public OmrPrefix |
| { |
| friend class OmrPrefixManager; |
| |
| public: |
| bool IsInfrastructureDerived(void) const; |
| |
| private: |
| void SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); |
| void SetFrom(const OmrPrefix &aOmrPrefix); |
| bool IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const; |
| }; |
| |
| class OmrPrefixManager : public InstanceLocator |
| { |
| public: |
| explicit OmrPrefixManager(Instance &aInstance); |
| |
| void Init(const Ip6::Prefix &aBrUlaPrefix); |
| void Start(void); |
| void Stop(void); |
| void Evaluate(void); |
| void UpdateDefaultRouteFlag(bool aDefaultRoute); |
| bool ShouldAdvertiseLocalAsRio(void) const; |
| const Ip6::Prefix &GetGeneratedPrefix(void) const { return mGeneratedPrefix; } |
| const OmrPrefix &GetLocalPrefix(void) const { return mLocalPrefix; } |
| const FavoredOmrPrefix &GetFavoredPrefix(void) const { return mFavoredPrefix; } |
| |
| private: |
| static constexpr uint16_t kInfoStringSize = 85; |
| |
| typedef String<kInfoStringSize> InfoString; |
| |
| void DetermineFavoredPrefix(void); |
| Error AddLocalToNetData(void); |
| Error AddOrUpdateLocalInNetData(void); |
| void RemoveLocalFromNetData(void); |
| InfoString LocalToString(void) const; |
| |
| OmrPrefix mLocalPrefix; |
| Ip6::Prefix mGeneratedPrefix; |
| FavoredOmrPrefix mFavoredPrefix; |
| bool mIsLocalAddedInNetData; |
| bool mDefaultRoute; |
| }; |
| |
| void HandleOnLinkPrefixManagerTimer(void) { mOnLinkPrefixManager.HandleTimer(); } |
| |
| class OnLinkPrefixManager : public InstanceLocator |
| { |
| public: |
| explicit OnLinkPrefixManager(Instance &aInstance); |
| |
| // Max number of old on-link prefixes to retain to deprecate. |
| static constexpr uint16_t kMaxOldPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES; |
| |
| void Init(void); |
| void Start(void); |
| void Stop(void); |
| void Evaluate(void); |
| const Ip6::Prefix &GetLocalPrefix(void) const { return mLocalPrefix; } |
| const Ip6::Prefix &GetFavoredDiscoveredPrefix(void) const { return mFavoredDiscoveredPrefix; } |
| bool IsInitalEvaluationDone(void) const; |
| void HandleDiscoveredPrefixTableChanged(void); |
| bool ShouldPublishUlaRoute(void) const; |
| Error AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage); |
| bool IsPublishingOrAdvertising(void) const; |
| void HandleNetDataChange(void); |
| void HandleExtPanIdChange(void); |
| void HandleTimer(void); |
| |
| private: |
| enum State : uint8_t // State of `mLocalPrefix` |
| { |
| kIdle, |
| kPublishing, |
| kAdvertising, |
| kDeprecating, |
| }; |
| |
| struct OldPrefix |
| { |
| bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } |
| |
| Ip6::Prefix mPrefix; |
| TimeMilli mExpireTime; |
| }; |
| |
| State GetState(void) const { return mState; } |
| void SetState(State aState); |
| void GenerateLocalPrefix(void); |
| void PublishAndAdvertise(void); |
| void Deprecate(void); |
| void ResetExpireTime(TimeMilli aNow); |
| Error AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage); |
| Error AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage); |
| void DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime); |
| void SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime); |
| |
| static const char *StateToString(State aState); |
| |
| using ExpireTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleOnLinkPrefixManagerTimer>; |
| |
| Ip6::Prefix mLocalPrefix; |
| State mState; |
| TimeMilli mExpireTime; |
| Ip6::Prefix mFavoredDiscoveredPrefix; |
| Array<OldPrefix, kMaxOldPrefixes> mOldLocalPrefixes; |
| ExpireTimer mTimer; |
| }; |
| |
| void HandleRioAdvertiserimer(void) { mRioAdvertiser.HandleTimer(); } |
| |
| class RioAdvertiser : public InstanceLocator |
| { |
| // Manages the list of prefixes advertised as RIO in emitted |
| // RA. The RIO prefixes are discovered from on-mesh prefixes in |
| // network data including OMR prefix from `OmrPrefixManager`. |
| // It also handles deprecating removed prefixes. |
| |
| public: |
| explicit RioAdvertiser(Instance &aInstance); |
| |
| RoutePreference GetPreference(void) const { return mPreference; } |
| void SetPreference(RoutePreference aPreference); |
| void ClearPreference(void); |
| void HandleRoleChanged(void); |
| Error AppendRios(RouterAdvert::TxMessage &aRaMessage); |
| Error InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage); |
| bool HasAdvertised(const Ip6::Prefix &aPrefix) const { return mPrefixes.ContainsMatching(aPrefix); } |
| uint16_t GetAdvertisedRioCount(void) const { return mPrefixes.GetLength(); } |
| void HandleTimer(void); |
| |
| private: |
| static constexpr uint32_t kDeprecationTime = TimeMilli::SecToMsec(300); |
| |
| struct RioPrefix : public Clearable<RioPrefix> |
| { |
| bool Matches(const Ip6::Prefix &aPrefix) const { return (mPrefix == aPrefix); } |
| |
| Ip6::Prefix mPrefix; |
| bool mIsDeprecating; |
| TimeMilli mExpirationTime; |
| }; |
| |
| struct RioPrefixArray : |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE |
| public Heap::Array<RioPrefix> |
| #else |
| public Array<RioPrefix, 2 * kMaxOnMeshPrefixes> |
| #endif |
| { |
| void Add(const Ip6::Prefix &aPrefix); |
| }; |
| |
| void SetPreferenceBasedOnRole(void); |
| void UpdatePreference(RoutePreference aPreference); |
| Error AppendRio(const Ip6::Prefix &aPrefix, uint32_t aRouteLifetime, RouterAdvert::TxMessage &aRaMessage); |
| |
| using RioTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleRioAdvertiserimer>; |
| |
| RioPrefixArray mPrefixes; |
| RioTimer mTimer; |
| RoutePreference mPreference; |
| bool mUserSetPreference; |
| }; |
| |
| #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE |
| void HandleNat64PrefixManagerTimer(void) { mNat64PrefixManager.HandleTimer(); } |
| |
| class Nat64PrefixManager : public InstanceLocator |
| { |
| public: |
| // This class manages the NAT64 related functions including |
| // generation of local NAT64 prefix, discovery of infra |
| // interface prefix, maintaining the discovered prefix |
| // lifetime, and selection of the NAT64 prefix to publish in |
| // Network Data. |
| // |
| // Calling methods except GenerateLocalPrefix and SetEnabled |
| // when disabled becomes no-op. |
| |
| explicit Nat64PrefixManager(Instance &aInstance); |
| |
| void SetEnabled(bool aEnabled); |
| Nat64::State GetState(void) const; |
| |
| void Start(void); |
| void Stop(void); |
| |
| void GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix); |
| const Ip6::Prefix &GetLocalPrefix(void) const { return mLocalPrefix; } |
| const Ip6::Prefix &GetFavoredPrefix(RoutePreference &aPreference) const; |
| void Evaluate(void); |
| void HandleDiscoverDone(const Ip6::Prefix &aPrefix); |
| void HandleTimer(void); |
| |
| private: |
| void Discover(void); |
| void Publish(void); |
| |
| using Nat64Timer = TimerMilliIn<RoutingManager, &RoutingManager::HandleNat64PrefixManagerTimer>; |
| |
| bool mEnabled; |
| |
| Ip6::Prefix mInfraIfPrefix; // The latest NAT64 prefix discovered on the infrastructure interface. |
| Ip6::Prefix mLocalPrefix; // The local prefix (from BR ULA prefix). |
| Ip6::Prefix mPublishedPrefix; // The prefix to publish in Net Data (empty or local or from infra-if). |
| RoutePreference mPublishedPreference; // The published prefix preference. |
| Nat64Timer mTimer; |
| }; |
| #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE |
| |
| void HandleRoutePublisherTimer(void) { mRoutePublisher.HandleTimer(); } |
| |
| class RoutePublisher : public InstanceLocator // Manages the routes that are published in net data |
| { |
| public: |
| explicit RoutePublisher(Instance &aInstance); |
| |
| void Start(void) { Evaluate(); } |
| void Stop(void) { Unpublish(); } |
| void Evaluate(void); |
| |
| void UpdateAdvPioFlags(bool aAdvPioFlag); |
| |
| RoutePreference GetPreference(void) const { return mPreference; } |
| void SetPreference(RoutePreference aPreference); |
| void ClearPreference(void); |
| |
| void HandleNotifierEvents(Events aEvents); |
| void HandleTimer(void); |
| |
| static const Ip6::Prefix &GetUlaPrefix(void) { return AsCoreType(&kUlaPrefix); } |
| |
| private: |
| static constexpr uint32_t kDelayBeforePrfUpdateOnLinkQuality3 = TimeMilli::SecToMsec(5 * 60); |
| |
| static const otIp6Prefix kUlaPrefix; |
| |
| enum State : uint8_t |
| { |
| kDoNotPublish, // Do not publish any routes in network data. |
| kPublishDefault, // Publish "::/0" route in network data. |
| kPublishUla, // Publish "fc00::/7" route in network data. |
| }; |
| |
| void DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const; |
| void UpdatePublishedRoute(State aNewState); |
| void Unpublish(void); |
| void SetPreferenceBasedOnRole(void); |
| void UpdatePreference(RoutePreference aPreference); |
| |
| static const char *StateToString(State aState); |
| |
| using DelayTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleRoutePublisherTimer>; |
| |
| State mState; |
| RoutePreference mPreference; |
| bool mUserSetPreference; |
| bool mAdvPioFlag; |
| DelayTimer mTimer; |
| }; |
| |
| struct RaInfo |
| { |
| // Tracks info about emitted RA messages: |
| // |
| // - Number of RAs sent |
| // - Last RA TX time |
| // - Hashes of last TX RAs (to tell if a received RA is from |
| // `RoutingManager` itself) |
| // - RA header to use, and |
| // - Whether the RA header is discovered from receiving RAs |
| // from the host itself. |
| // |
| // This ensures that if an entity on host is advertising certain |
| // info in its RA header (e.g., a default route), the RAs we |
| // emit from `RoutingManager` also include the same header. |
| |
| typedef Crypto::Sha256::Hash Hash; |
| |
| static constexpr uint16_t kNumHashEntries = 5; |
| |
| RaInfo(void) |
| : mHeaderUpdateTime(TimerMilli::GetNow()) |
| , mIsHeaderFromHost(false) |
| , mTxCount(0) |
| , mLastTxTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs) |
| , mLastHashIndex(0) |
| { |
| } |
| |
| void IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage); |
| bool IsRaFromManager(const Ip6::Nd::RouterAdvert::RxMessage &aRaMessage) const; |
| static void CalculateHash(const InfraIf::Icmp6Packet &aRaMessage, Hash &aHash); |
| |
| RouterAdvert::Header mHeader; |
| TimeMilli mHeaderUpdateTime; |
| bool mIsHeaderFromHost; |
| uint32_t mTxCount; |
| TimeMilli mLastTxTime; |
| Hash mHashes[kNumHashEntries]; |
| uint16_t mLastHashIndex; |
| }; |
| |
| void HandleRsSenderTimer(void) { mRsSender.HandleTimer(); } |
| |
| class RsSender : public InstanceLocator |
| { |
| public: |
| // This class implements tx of Router Solicitation (RS) |
| // messages to discover other routers. `Start()` schedules |
| // a cycle of RS transmissions of `kMaxTxCount` separated |
| // by `kTxInterval`. At the end of cycle the callback |
| // `HandleRsSenderFinished()` is invoked to inform end of |
| // the cycle to `RoutingManager`. |
| |
| explicit RsSender(Instance &aInstance); |
| |
| bool IsInProgress(void) const { return mTimer.IsRunning(); } |
| void Start(void); |
| void Stop(void); |
| void HandleTimer(void); |
| |
| private: |
| // All time intervals are in msec. |
| static constexpr uint32_t kMaxStartDelay = 1000; // Max random delay to send the first RS. |
| static constexpr uint32_t kTxInterval = 4000; // Interval between RS tx. |
| static constexpr uint32_t kRetryDelay = kTxInterval; // Interval to wait to retry a failed RS tx. |
| static constexpr uint32_t kWaitOnLastAttempt = 1000; // Wait interval after last RS tx. |
| static constexpr uint8_t kMaxTxCount = 3; // Number of RS tx in one cycle. |
| |
| Error SendRs(void); |
| |
| using RsTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleRsSenderTimer>; |
| |
| uint8_t mTxCount; |
| RsTimer mTimer; |
| TimeMilli mStartTime; |
| }; |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| void HandlePdPrefixManagerTimer(void) { mPdPrefixManager.HandleTimer(); } |
| |
| class PdPrefixManager : public InstanceLocator |
| { |
| public: |
| // This class implements handling (including management of the lifetime) of the prefix obtained from platform's |
| // DHCPv6 PD client. We expect the platform will send ICMP6 RA messages to the platform's interface for the |
| // information of the prefix. |
| // This class manages the state of the PD prefix in OmrPrefixManager |
| |
| explicit PdPrefixManager(Instance &aInstance); |
| |
| void SetEnabled(bool aEnabled); |
| void Start(void) { StartStop(/* aStart= */ true); } |
| void Stop(void) { StartStop(/* aStart= */ false); } |
| bool IsRunning(void) const { return GetState() == Dhcp6PdState::kDhcp6PdStateRunning; } |
| bool HasPrefix(void) const { return IsValidOmrPrefix(mPrefix.GetPrefix()); } |
| const Ip6::Prefix &GetPrefix(void) const { return mPrefix.GetPrefix(); } |
| Dhcp6PdState GetState(void) const; |
| |
| void ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength); |
| void ProcessDhcpPdPrefix(const PrefixTableEntry &aPrefixTableEntry); |
| Error GetPrefixInfo(PrefixTableEntry &aInfo) const; |
| Error GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const; |
| void HandleTimer(void) { WithdrawPrefix(); } |
| void SetRequestDhcp6PdCallback(PdCallback aCallback, void *aContext) |
| { |
| mExternalCallback.Set(aCallback, aContext); |
| } |
| |
| static const char *StateToString(Dhcp6PdState aState); |
| |
| static bool IsValidPdPrefix(const Ip6::Prefix &aPrefix) |
| { |
| // We should accept ULA prefix since it could be used by the internet infrastructure like NAT64. |
| return aPrefix.GetLength() != 0 && aPrefix.GetLength() <= kOmrPrefixLength && !aPrefix.IsLinkLocal() && |
| !aPrefix.IsMulticast(); |
| } |
| |
| private: |
| Error Process(const RouterAdvert::RxMessage *aMessage, const PrefixTableEntry *aPrefixTableEntry); |
| bool ProcessPrefixEntry(DiscoveredPrefixTable::Entry &aEntry, DiscoveredPrefixTable::Entry &aFavoredEntry); |
| void EvaluateStateChange(Dhcp6PdState aOldState); |
| void WithdrawPrefix(void); |
| void StartStop(bool aStart); |
| |
| using PlatformOmrPrefixTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandlePdPrefixManagerTimer>; |
| using ExternalCallback = Callback<PdCallback>; |
| |
| bool mEnabled; |
| bool mIsRunning; |
| uint32_t mNumPlatformPioProcessed; |
| uint32_t mNumPlatformRaReceived; |
| TimeMilli mLastPlatformRaTime; |
| ExternalCallback mExternalCallback; |
| PlatformOmrPrefixTimer mTimer; |
| DiscoveredPrefixTable::Entry mPrefix; |
| }; |
| |
| #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| |
| void EvaluateState(void); |
| void Start(void); |
| void Stop(void); |
| void HandleNotifierEvents(Events aEvents); |
| bool IsInitialized(void) const { return mInfraIf.IsInitialized(); } |
| bool IsEnabled(void) const { return mIsEnabled; } |
| Error LoadOrGenerateRandomBrUlaPrefix(void); |
| |
| void EvaluateRoutingPolicy(void); |
| bool IsInitalPolicyEvaluationDone(void) const; |
| void ScheduleRoutingPolicyEvaluation(ScheduleMode aMode); |
| void HandleRsSenderFinished(TimeMilli aStartTime); |
| void SendRouterAdvertisement(RouterAdvTxMode aRaTxMode); |
| |
| void HandleDiscoveredPrefixStaleTimer(void); |
| |
| void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); |
| void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); |
| void HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket); |
| bool ShouldProcessPrefixInfoOption(const PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix); |
| bool ShouldProcessRouteInfoOption(const RouteInfoOption &aRio, const Ip6::Prefix &aPrefix); |
| void UpdateDiscoveredPrefixTableOnNetDataChange(void); |
| bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; |
| bool NetworkDataContainsUlaRoute(void) const; |
| void UpdateRouterAdvertHeader(const RouterAdvert::RxMessage *aRouterAdvertMessage); |
| void ResetDiscoveredPrefixStaleTimer(void); |
| |
| static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix); |
| static bool IsValidOnLinkPrefix(const PrefixInfoOption &aPio); |
| static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix); |
| |
| static void LogPrefixInfoOption(const Ip6::Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime); |
| static void LogRouteInfoOption(const Ip6::Prefix &aPrefix, uint32_t aLifetime, RoutePreference aPreference); |
| |
| using RoutingPolicyTimer = TimerMilliIn<RoutingManager, &RoutingManager::EvaluateRoutingPolicy>; |
| using DiscoveredPrefixStaleTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleDiscoveredPrefixStaleTimer>; |
| |
| // Indicates whether the Routing Manager is running (started). |
| bool mIsRunning; |
| |
| // Indicates whether the Routing manager is enabled. The Routing |
| // Manager will be stopped if we are disabled. |
| bool mIsEnabled; |
| |
| InfraIf mInfraIf; |
| |
| // The /48 BR ULA prefix loaded from local persistent storage or |
| // randomly generated if none is found in persistent storage. |
| Ip6::Prefix mBrUlaPrefix; |
| |
| OmrPrefixManager mOmrPrefixManager; |
| |
| RioAdvertiser mRioAdvertiser; |
| RoutePreference mRioPreference; |
| bool mUserSetRioPreference; |
| |
| OnLinkPrefixManager mOnLinkPrefixManager; |
| |
| DiscoveredPrefixTable mDiscoveredPrefixTable; |
| |
| RoutePublisher mRoutePublisher; |
| |
| #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE |
| Nat64PrefixManager mNat64PrefixManager; |
| #endif |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE |
| PdPrefixManager mPdPrefixManager; |
| #endif |
| |
| RaInfo mRaInfo; |
| RsSender mRsSender; |
| Heap::Data mExtraRaOptions; |
| |
| DiscoveredPrefixStaleTimer mDiscoveredPrefixStaleTimer; |
| RoutingPolicyTimer mRoutingPolicyTimer; |
| }; |
| |
| } // namespace BorderRouter |
| |
| DefineMapEnum(otBorderRoutingState, BorderRouter::RoutingManager::State); |
| |
| } // namespace ot |
| |
| #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE |
| |
| #endif // ROUTING_MANAGER_HPP_ |