diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index afd2d7e5f..1e5ff6144 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -239,6 +239,22 @@ void otBorderRoutingSetRouteInfoOptionPreference(otInstance *aInstance, otRouteP */ void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance); +/** + * Sets additional options to append at the end of emitted Router Advertisement (RA) messages. + * + * The content of @p aOptions is copied internally, so it can be a temporary buffer (e.g., a stack allocated array). + * + * Subsequent calls to this function overwrite the previously set value. + * + * @param[in] aOptions A pointer to the encoded options. Can be `NULL` to clear. + * @param[in] aLength Number of bytes in @p aOptions. + * + * @retval OT_ERROR_NONE Successfully set the extra option bytes. + * @retval OT_ERROR_NO_BUFS Could not allocate buffer to save the buffer. + * + */ +otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength); + /** * Gets the current preference used for published routes in Network Data. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 84641d422..1ce766c02 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (401) +#define OPENTHREAD_API_VERSION (402) /** * @addtogroup api-instance diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index 189de8a6f..8376290d6 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -36,6 +36,7 @@ omrprefix onlinkprefix pd prefixtable +raoptions rioprf routeprf routers @@ -235,6 +236,28 @@ prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, pre Done ``` +### raoptions + +Usage: `br raoptions ` + +Sets additional options to append at the end of emitted Router Advertisement (RA) messages. `` provided as hex bytes. + +```bash +> br raoptions 0400ff00020001 +Done +``` + +### raoptions clear + +Usage: `br raoptions clear` + +Clear any previously set additional options to append at the end of emitted Router Advertisement (RA) messages. + +```bash +> br raoptions clear +Done +``` + ### rioprf Usage: `br rioprf` diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index 85590b925..53f2b3274 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -538,6 +538,46 @@ void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry) aEntry.mStubRouterFlag); } +template <> otError Br::Process(Arg aArgs[]) +{ + static constexpr uint16_t kMaxExtraOptions = 800; + + otError error = OT_ERROR_NONE; + uint8_t options[kMaxExtraOptions]; + uint16_t length; + + /** + * @cli br raoptions (set,clear) + * @code + * br raoptions 0400ff00020001 + * Done + * @endcode + * @code + * br raoptions clear + * Done + * @endcode + * @cparam br raoptions @ca{options|clear} + * `br raoptions clear` passes a `nullptr` to #otBorderRoutingSetExtraRouterAdvertOptions. + * Otherwise, you can pass the `options` byte as hex data. + * @par api_copy + * #otBorderRoutingSetExtraRouterAdvertOptions + */ + if (aArgs[0] == "clear") + { + length = 0; + } + else + { + length = sizeof(options); + SuccessOrExit(error = aArgs[0].ParseAsHexString(length, options)); + } + + error = otBorderRoutingSetExtraRouterAdvertOptions(GetInstancePtr(), length > 0 ? options : nullptr, length); + +exit: + return error; +} + template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -702,6 +742,7 @@ otError Br::Process(Arg aArgs[]) CmdEntry("pd"), #endif CmdEntry("prefixtable"), + CmdEntry("raoptions"), CmdEntry("rioprf"), CmdEntry("routeprf"), CmdEntry("routers"), diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index b62ee7041..7c8002dea 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -75,6 +75,11 @@ void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance) AsCoreType(aInstance).Get().ClearRouteInfoOptionPreference(); } +otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength) +{ + return AsCoreType(aInstance).Get().SetExtraRouterAdvertOptions(aOptions, aLength); +} + otRoutePreference otBorderRoutingGetRoutePreference(otInstance *aInstance) { return static_cast( diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index b4c1b0ae5..bebd795c8 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -363,6 +363,22 @@ exit: return; } +Error RoutingManager::SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength) +{ + Error error = kErrorNone; + + if (aOptions == nullptr) + { + mExtraRaOptions.Free(); + } + else + { + error = mExtraRaOptions.SetFrom(aOptions, aLength); + } + + return error; +} + #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE void RoutingManager::HandleSrpServerAutoEnableMode(void) { @@ -607,6 +623,11 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) SuccessOrExit(error = mRioAdvertiser.AppendRios(raMsg)); } + if (mExtraRaOptions.GetLength() > 0) + { + SuccessOrExit(error = raMsg.AppendBytes(mExtraRaOptions.GetBytes(), mExtraRaOptions.GetLength())); + } + VerifyOrExit(raMsg.ContainsAnyOptions()); destAddress.SetToLinkLocalAllNodesMulticast(); diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index bb376eedc..37de6eca3 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -55,6 +55,7 @@ #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" @@ -233,6 +234,22 @@ public: */ 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. * @@ -1360,8 +1377,9 @@ private: PdPrefixManager mPdPrefixManager; #endif - RaInfo mRaInfo; - RsSender mRsSender; + RaInfo mRaInfo; + RsSender mRsSender; + Heap::Data mExtraRaOptions; DiscoveredPrefixStaleTimer mDiscoveredPrefixStaleTimer; RoutingPolicyTimer mRoutingPolicyTimer; diff --git a/src/core/net/nd6.hpp b/src/core/net/nd6.hpp index 260606b30..4178f181f 100644 --- a/src/core/net/nd6.hpp +++ b/src/core/net/nd6.hpp @@ -808,6 +808,18 @@ public: */ Error AppendFlagsExtensionOption(bool aStubRouterFlag); + /** + * Appends bytes from a given buffer to the RA message. + * + * @param[in] aBytes A pointer to the buffer containing the bytes to append. + * @param[in] aLength The buffer length. + * + * @retval kErrorNone Bytes are appended successfully. + * @retval kErrorNoBufs Insufficient available buffers to grow the message. + * + */ + Error AppendBytes(const uint8_t *aBytes, uint16_t aLength); + /** * Indicates whether or not the received RA message contains any options. * @@ -820,7 +832,6 @@ public: private: static constexpr uint16_t kCapacityIncrement = 256; - Error AppendBytes(const uint8_t *aBytes, uint16_t aLength); Option *AppendOption(uint16_t aOptionSize); Heap::Array mArray;