From e6134cb828255809b436b71365ce60fbcc2f87d8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 6 May 2026 22:29:13 -0700 Subject: [PATCH] [ip6] enforce filter rules when Thread role is disabled (#13050) This commit updates `Ip6::Filter::Apply()` to remove the exception that allowed all unsecure link-local IPv6 datagrams to pass through when the Thread role was disabled (e.g., when the interface is up but Thread has not yet started). By removing this check, the device now consistently enforces strict port filtering at all times. Only explicitly allowed traffic, such as MLE messages, commissioner traffic, or user-configured unsecure ports, will be permitted, improving the overall security posture regardless of the current Thread role state. For testing and backward compatibility on reference devices, the `mAllowUnsecureWhenDisabled` flag is introduced (available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled). This allows the legacy behavior to be restored via the new public APIs `otIp6SetAllowUnsecureWhenDisabled()`. The new APIs are also provided in CLI under `unsecureport allwhendisabled` command. --- include/openthread/instance.h | 2 +- include/openthread/ip6.h | 23 +++++++++++++++++++++++ src/cli/cli.cpp | 26 ++++++++++++++++++++++++++ src/core/api/ip6_api.cpp | 12 ++++++++++++ src/core/net/ip6_filter.cpp | 5 +++-- src/core/net/ip6_filter.hpp | 23 +++++++++++++++++++++++ 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 0a7f44190..0101f39e8 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (594) +#define OPENTHREAD_API_VERSION (595) /** * @addtogroup api-instance diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index 0d9be5f3c..3d6acb10a 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -605,6 +605,29 @@ void otIp6RemoveAllUnsecurePorts(otInstance *aInstance); */ const uint16_t *otIp6GetUnsecurePorts(otInstance *aInstance, uint8_t *aNumEntries); +/** + * Sets whether to allow link-local unsecure IPv6 datagrams when the Thread role is disabled. + * + * Available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. This is intended for testing. By default, + * this is disabled (i.e., unsecure traffic is always dropped regardless of the device's role). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aAllow TRUE to allow, FALSE otherwise. + */ +void otIp6SetAllowUnsecureWhenDisabled(otInstance *aInstance, bool aAllow); + +/** + * Indicates whether allowing link-local unsecure IPv6 datagrams when the Thread role is disabled is enabled. + * + * Available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE Does allow unsecure IPv6 datagrams when the Thread role is disabled. + * @retval FALSE Does not allow unsecure IPv6 datagrams when the Thread role is disabled. + */ +bool otIp6IsUnsecureAllowedWhenDisabled(otInstance *aInstance); + /** * Test if two IPv6 addresses are the same. * diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 64cc8de97..a7b5a14a0 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -7085,6 +7085,32 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputNewLine(); } +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + /** + * @cli unsecureport allwhendisabled + * @code + * unsecureport allwhendisabled + * Disabled + * Done + * @endcode + * @par api_copy + * #otIp6IsUnsecureAllowedWhenDisabled + */ + else if (aArgs[0] == "allwhendisabled") + { + /** + * @cli unsecureport allwhendisabled (enable, disable) + * @code + * unsecureport allwhendisabled enable + * Done + * @endcode + * @cparam unsecureport allwhendisabled @ca{enable|disable} + * @par api_copy + * #otIp6SetAllowUnsecureWhenDisabled + */ + error = ProcessEnableDisable(aArgs + 1, otIp6IsUnsecureAllowedWhenDisabled, otIp6SetAllowUnsecureWhenDisabled); + } +#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE else { error = OT_ERROR_INVALID_COMMAND; diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index 6b2932b0a..9becd1c88 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -312,3 +312,15 @@ void otIp6ResetBorderRoutingCounters(otInstance *aInstance) AsCoreType(aInstance).Get().ResetBorderRoutingCounters(); } #endif + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +void otIp6SetAllowUnsecureWhenDisabled(otInstance *aInstance, bool aAllow) +{ + AsCoreType(aInstance).Get().SetAllowUnsecureWhenDisabled(aAllow); +} + +bool otIp6IsUnsecureAllowedWhenDisabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsUnsecureAllowedWhenDisabled(); +} +#endif diff --git a/src/core/net/ip6_filter.cpp b/src/core/net/ip6_filter.cpp index c4f518b43..5fc5cee7f 100644 --- a/src/core/net/ip6_filter.cpp +++ b/src/core/net/ip6_filter.cpp @@ -56,11 +56,12 @@ Error Filter::Apply(const Message &aMessage) const VerifyOrExit(headers.GetDestinationAddress().IsLinkLocalUnicastOrMulticast()); - // Allow all link-local IPv6 datagrams when Thread is not enabled - if (Get().GetRole() == Mle::kRoleDisabled) +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + if (mAllowUnsecureWhenDisabled && Get().IsDisabled()) { ExitNow(error = kErrorNone); } +#endif dstPort = headers.GetDestinationPort(); diff --git a/src/core/net/ip6_filter.hpp b/src/core/net/ip6_filter.hpp index 60fc63d47..a1bc668ec 100644 --- a/src/core/net/ip6_filter.hpp +++ b/src/core/net/ip6_filter.hpp @@ -66,6 +66,9 @@ public: */ explicit Filter(Instance &aInstance) : InstanceLocator(aInstance) +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + , mAllowUnsecureWhenDisabled(false) +#endif { } @@ -131,6 +134,23 @@ public: return &mUnsecurePorts[0]; } +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + /** + * Sets whether to allow link-local unsecure IPv6 datagrams when the Thread role is disabled. + * + * @param[in] aAllow TRUE to allow, FALSE otherwise. + */ + void SetAllowUnsecureWhenDisabled(bool aAllow) { mAllowUnsecureWhenDisabled = aAllow; } + + /** + * Indicates whether allowing link-local unsecure IPv6 datagrams when the Thread role is disabled is enabled. + * + * @retval TRUE Does allow unsecure IPv6 datagrams when the Thread role is disabled. + * @retval FALSE Does not allow unsecure IPv6 datagrams when the Thread role is disabled. + */ + bool IsUnsecureAllowedWhenDisabled(void) const { return mAllowUnsecureWhenDisabled; } +#endif + private: static constexpr uint16_t kMaxUnsecurePorts = 2; @@ -143,6 +163,9 @@ private: Error UpdateUnsecurePorts(Action aAction, uint16_t aPort); Array mUnsecurePorts; +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + bool mAllowUnsecureWhenDisabled; +#endif }; } // namespace Ip6