diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index a59294dbd..887773463 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -132,24 +132,6 @@ typedef enum otNetifInterfaceId OT_NETIF_INTERFACE_ID_THREAD = 1, ///< The Thread Network interface ID. } otNetifInterfaceId; -/** - * This structure represents data used by Semantically Opaque IID Generator. - * - */ -typedef struct -{ - uint8_t *mInterfaceId; ///< String of bytes representing interface ID. Like "eth0" or "wlan0". - uint8_t mInterfaceIdLength; ///< Length of interface ID string. - - uint8_t *mNetworkId; ///< Network ID (or name). Can be null if mNetworkIdLength is 0. - uint8_t mNetworkIdLength; ///< Length of Network ID string. - - uint8_t mDadCounter; ///< Duplicate address detection counter. - - uint8_t *mSecretKey; ///< Secret key used to create IID. Cannot be null. - uint16_t mSecretKeyLength; ///< Secret key length in bytes. Should be at least 16 bytes == 128 bits. -} otSemanticallyOpaqueIidGeneratorData; - /** * This structure represents an IPv6 socket address. * @@ -306,43 +288,6 @@ bool otIp6IsMulticastPromiscuousEnabled(otInstance *aInstance); */ void otIp6SetMulticastPromiscuousEnabled(otInstance *aInstance, bool aEnabled); -/** - * Create random IID for given IPv6 address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddresses A pointer to structure containing IPv6 address for which IID is being created. - * @param[in] aContext A pointer to unused data. - * - * @retval OT_ERROR_NONE Created valid IID for given IPv6 address. - * - */ -otError otIp6CreateRandomIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); - -/** - * Create IID for given IPv6 address using extended MAC address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddresses A pointer to structure containing IPv6 address for which IID is being created. - * @param[in] aContext A pointer to unused data. - * - * @retval OT_ERROR_NONE Created valid IID for given IPv6 address. - * - */ -otError otIp6CreateMacIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); - -/** - * Create semantically opaque IID for given IPv6 address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddresses A pointer to structure containing IPv6 address for which IID is being created. - * @param[inout] aContext A pointer to a otSemanticallyOpaqueIidGeneratorData structure. - * - * @retval OT_ERROR_NONE Created valid IID for given IPv6 address. - * @retval OT_ERROR_IP6_ADDRESS_CREATION_FAILURE Could not create valid IID for given IPv6 address. - * - */ -otError otIp6CreateSemanticallyOpaqueIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); - /** * Allocate a new message buffer for sending an IPv6 message. * @@ -577,6 +522,54 @@ bool otIp6IsAddressUnspecified(const otIp6Address *aAddress); */ otError otIp6SelectSourceAddress(otInstance *aInstance, otMessageInfo *aMessageInfo); +/** + * This function enables/disables the SLAAC module. + * + * This function requires the build-time feature `OPENTHREAD_CONFIG_ENABLE_SLAAC` to be enabled. + * + * When SLAAC module is enabled, SLAAC addresses (based on on-mesh prefixes in Network Data) are added to the interface. + * When SLAAC module is disabled any previously added SLAAC address is removed. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnabled TRUE to enable, FALSE to disable. + * + */ +void otIp6SetSlaacEnabled(otInstance *aInstance, bool aEnabled); + +/** + * This function pointer allows user to filter prefixes and not allow an SLAAC address based on a prefix to be added. + * + * `otIp6SetSlaacPrefixFilter()` can be used to set the filter handler. The filter handler is invoked by SLAAC module + * when it is about to add a SLAAC address based on a prefix. Its boolean return value determines whether the address + * is filtered (not added) or not. + * + * @param[in] aInstacne A pointer to an OpenThread instance. + * @param[in] aPrefix A pointer to prefix for which SLAAC address is about to be added. + * + * @retval TRUE Indicates that the SLAAC address based on the prefix should be filtered and NOT added. + * @retval FALSE Indicates that the SLAAC address based on the prefix should be added. + * + */ +typedef bool (*otIp6SlaacPrefixFilter)(otInstance *aInstance, const otIp6Prefix *aPrefix); + +/** + * This function sets the SLAAC module filter handler. + * + * This function requires the build-time feature `OPENTHREAD_CONFIG_ENABLE_SLAAC` to be enabled. + * + * The filter handler is called by SLAAC module when it is about to add a SLAAC address based on a prefix to decide + * whether the address should be added or not. + * + * A NULL filter handler disables filtering and allows all SLAAC addresses to be added. + * + * If this function is not called, the default filter used by SLAAC module will be NULL (filtering is disabled). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aFilter A pointer to SLAAC prefix filter handler, or NULL to disable filtering. + * + */ +void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFilter); + /** * @} * diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index 32a5e93f6..1a8a80f76 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -39,7 +39,9 @@ #include "common/instance.hpp" #include "common/logging.hpp" +#if OPENTHREAD_CONFIG_ENABLE_SLAAC #include "utils/slaac_address.hpp" +#endif using namespace ot; @@ -131,27 +133,6 @@ void otIp6SetMulticastPromiscuousEnabled(otInstance *aInstance, bool aEnabled) instance.GetThreadNetif().SetMulticastPromiscuous(aEnabled); } -otError otIp6CreateRandomIid(otInstance *aInstance, otNetifAddress *aAddress, void *aContext) -{ - return Utils::Slaac::CreateRandomIid(aInstance, aAddress, aContext); -} - -otError otIp6CreateMacIid(otInstance *aInstance, otNetifAddress *aAddress, void *) -{ - Instance &instance = *static_cast(aInstance); - - memcpy(&aAddress->mAddress.mFields.m8[OT_IP6_ADDRESS_SIZE - OT_IP6_IID_SIZE], - &instance.GetThreadNetif().GetMac().GetExtAddress(), OT_IP6_IID_SIZE); - aAddress->mAddress.mFields.m8[OT_IP6_ADDRESS_SIZE - OT_IP6_IID_SIZE] ^= 0x02; - - return OT_ERROR_NONE; -} - -otError otIp6CreateSemanticallyOpaqueIid(otInstance *aInstance, otNetifAddress *aAddress, void *aContext) -{ - return static_cast(aContext)->CreateIid(aInstance, aAddress); -} - void otIp6SetReceiveCallback(otInstance *aInstance, otIp6ReceiveCallback aCallback, void *aCallbackContext) { Instance &instance = *static_cast(aInstance); @@ -288,3 +269,29 @@ otError otIp6SelectSourceAddress(otInstance *aInstance, otMessageInfo *aMessageI exit: return error; } + +#if OPENTHREAD_CONFIG_ENABLE_SLAAC + +void otIp6SetSlaacEnabled(otInstance *aInstance, bool aEnabled) +{ + Instance & instance = *static_cast(aInstance); + Utils::Slaac &slaac = instance.Get(); + + if (aEnabled) + { + slaac.Enable(); + } + else + { + slaac.Disable(); + } +} + +void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFilter) +{ + Instance &instance = *static_cast(aInstance); + + instance.Get().SetFilter(aFilter); +} + +#endif // OPENTHREAD_CONFIG_ENABLE_SLAAC diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 1b67d9e9f..f2e0b0843 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -630,6 +630,13 @@ template <> inline Dhcp6::Dhcp6Client &Instance::Get(void) } #endif +#if OPENTHREAD_CONFIG_ENABLE_SLAAC +template <> inline Utils::Slaac &Instance::Get(void) +{ + return GetThreadNetif().GetSlaac(); +} +#endif + #if OPENTHREAD_ENABLE_JAM_DETECTION template <> inline Utils::JamDetector &Instance::Get(void) { diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp index a205713dd..561896ced 100644 --- a/src/core/common/settings.hpp +++ b/src/core/common/settings.hpp @@ -39,6 +39,9 @@ #include "common/locator.hpp" #include "mac/mac_frame.hpp" #include "thread/mle.hpp" +#if OPENTHREAD_CONFIG_ENABLE_SLAAC +#include "utils/slaac_address.hpp" +#endif namespace ot { @@ -122,12 +125,13 @@ protected: */ enum Key { - kKeyActiveDataset = 0x0001, ///< Active Operational Dataset - kKeyPendingDataset = 0x0002, ///< Pending Operational Dataset - kKeyNetworkInfo = 0x0003, ///< Thread network information - kKeyParentInfo = 0x0004, ///< Parent information - kKeyChildInfo = 0x0005, ///< Child information - kKeyThreadAutoStart = 0x0006, ///< Auto-start information + kKeyActiveDataset = 0x0001, ///< Active Operational Dataset + kKeyPendingDataset = 0x0002, ///< Pending Operational Dataset + kKeyNetworkInfo = 0x0003, ///< Thread network information + kKeyParentInfo = 0x0004, ///< Parent information + kKeyChildInfo = 0x0005, ///< Child information + kKeyThreadAutoStart = 0x0006, ///< Auto-start information + kKeySlaacIidSecretKey = 0x0007, ///< Secret key used by SLAAC module for generating semantically opaque IID }; explicit SettingsBase(Instance &aInstance) @@ -319,6 +323,48 @@ public: */ otError DeleteThreadAutoStart(void) { return Delete(kKeyThreadAutoStart); } +#if OPENTHREAD_CONFIG_ENABLE_SLAAC + + /** + * This method saves the SLAAC IID secret key. + * + * @param[in] aKey The SLAAC IID secret key. + * + * @retval OT_ERROR_NONE Successfully saved the value. + * @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality. + * + */ + otError SaveSlaacIidSecretKey(const Utils::Slaac::IidSecretKey &aKey) + { + return Save(kKeySlaacIidSecretKey, &aKey, sizeof(Utils::Slaac::IidSecretKey)); + } + + /** + * This method reads the SLAAC IID secret key. + * + * @param[out] aKey A reference to a SLAAC IID secret key to output the read value. + * + * @retval OT_ERROR_NONE Successfully read the value. + * @retval OT_ERROR_NOT_FOUND No corresponding value in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality. + * + */ + otError ReadSlaacIidSecretKey(Utils::Slaac::IidSecretKey &aKey) + { + return ReadFixedSize(kKeySlaacIidSecretKey, &aKey, sizeof(Utils::Slaac::IidSecretKey)); + } + + /** + * This method deletes the SLAAC IID secret key value from settings. + * + * @retval OT_ERROR_NONE Successfully deleted the value. + * @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality. + * + */ + otError DeleteSlaacIidSecretKey(void) { return Delete(kKeySlaacIidSecretKey); } + +#endif // OPENTHREAD_CONFIG_ENABLE_SLAAC + /** * This method adds a Child Info entry to settings. * diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 4975755c9..0e3eddcc3 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -242,7 +242,7 @@ uint8_t Address::PrefixMatch(const uint8_t *aPrefixA, const uint8_t *aPrefixB, u return rval; } -uint8_t Address::PrefixMatch(const Address &aOther) const +uint8_t Address::PrefixMatch(const otIp6Address &aOther) const { return PrefixMatch(mFields.m8, aOther.mFields.m8, sizeof(Address)); } diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index e5af2f5fb..34a069bf1 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -343,7 +343,7 @@ public: * @returns The number of IPv6 prefix bits that match. * */ - uint8_t PrefixMatch(const Address &aOther) const; + uint8_t PrefixMatch(const otIp6Address &aOther) const; /** * This method evaluates whether or not the IPv6 addresses match. diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 232b926cd..557ded179 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1506,10 +1506,6 @@ void Mle::HandleStateChanged(otChangedFlags aFlags) #endif #endif -#if OPENTHREAD_CONFIG_ENABLE_SLAAC - GetNetif().UpdateSlaac(); -#endif - #if OPENTHREAD_ENABLE_DHCP6_SERVER GetNetif().GetDhcp6Server().UpdateService(); #endif // OPENTHREAD_ENABLE_DHCP6_SERVER diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 295434b13..8e29e0a27 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -43,9 +43,6 @@ #include "thread/mle.hpp" #include "thread/thread_tlvs.hpp" #include "thread/thread_uri_paths.hpp" -#if OPENTHREAD_CONFIG_ENABLE_SLAAC -#include "utils/slaac_address.hpp" -#endif using ot::Encoding::BigEndian::HostSwap16; @@ -60,6 +57,9 @@ ThreadNetif::ThreadNetif(Instance &aInstance) #if OPENTHREAD_ENABLE_DHCP6_SERVER , mDhcp6Server(aInstance) #endif // OPENTHREAD_ENABLE_DHCP6_SERVER +#if OPENTHREAD_CONFIG_ENABLE_SLAAC + , mSlaac(aInstance) +#endif #if OPENTHREAD_ENABLE_DNS_CLIENT , mDnsClient(aInstance.GetThreadNetif()) #endif // OPENTHREAD_ENABLE_DNS_CLIENT @@ -111,9 +111,6 @@ ThreadNetif::ThreadNetif(Instance &aInstance) #endif { mCoap.SetInterceptor(&ThreadNetif::TmfFilter, this); -#if OPENTHREAD_CONFIG_ENABLE_SLAAC - memset(mSlaacAddresses, 0, sizeof(mSlaacAddresses)); -#endif } void ThreadNetif::Up(void) @@ -227,12 +224,4 @@ exit: return rval; } -#if OPENTHREAD_CONFIG_ENABLE_SLAAC -void ThreadNetif::UpdateSlaac(void) -{ - Utils::Slaac::UpdateAddresses(&GetInstance(), mSlaacAddresses, OT_ARRAY_LENGTH(mSlaacAddresses), - otIp6CreateRandomIid, NULL); -} -#endif - } // namespace ot diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 790d95927..3551dd1e5 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -75,6 +75,10 @@ #include "thread/time_sync_service.hpp" #include "utils/child_supervision.hpp" +#if OPENTHREAD_CONFIG_ENABLE_SLAAC +#include "utils/slaac_address.hpp" +#endif + #if OPENTHREAD_ENABLE_JAM_DETECTION #include "utils/jam_detector.hpp" #endif // OPENTHREAD_ENABLE_JAM_DETECTION @@ -183,6 +187,16 @@ public: Dhcp6::Dhcp6Server &GetDhcp6Server(void) { return mDhcp6Server; } #endif // OPENTHREAD_ENABLE_DHCP6_SERVER +#if OPENTHREAD_CONFIG_ENABLE_SLAAC + /** + * This method returns a reference to the SLAAC manager object. + * + * @returns A reference to the SLAAC manager object. + * + */ + Utils::Slaac &GetSlaac(void) { return mSlaac; } +#endif + #if OPENTHREAD_ENABLE_DNS_CLIENT /** * This method returns a reference to the dns client object. @@ -427,14 +441,6 @@ public: */ bool IsTmfMessage(const Ip6::MessageInfo &aMessageInfo); -#if OPENTHREAD_CONFIG_ENABLE_SLAAC - /** - * This method updates addresses that shall be automatically created using SLAAC. - * - */ - void UpdateSlaac(void); -#endif - private: static otError TmfFilter(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext); @@ -446,7 +452,7 @@ private: Dhcp6::Dhcp6Server mDhcp6Server; #endif // OPENTHREAD_ENABLE_DHCP6_SERVER #if OPENTHREAD_CONFIG_ENABLE_SLAAC - Ip6::NetifUnicastAddress mSlaacAddresses[OPENTHREAD_CONFIG_NUM_SLAAC_ADDRESSES]; + Utils::Slaac mSlaac; #endif #if OPENTHREAD_ENABLE_DNS_CLIENT Dns::Client mDnsClient; diff --git a/src/core/utils/slaac_address.cpp b/src/core/utils/slaac_address.cpp index b89f0ad43..d810d1968 100644 --- a/src/core/utils/slaac_address.cpp +++ b/src/core/utils/slaac_address.cpp @@ -35,197 +35,307 @@ #include "utils/wrap_string.h" -#include -#include - #include "common/code_utils.hpp" -#include "common/debug.hpp" #include "common/instance.hpp" +#include "common/logging.hpp" +#include "common/owner-locator.hpp" #include "common/random.hpp" +#include "common/settings.hpp" #include "crypto/sha256.hpp" -#include "mac/mac.hpp" #include "net/ip6_address.hpp" +#if OPENTHREAD_CONFIG_ENABLE_SLAAC + namespace ot { namespace Utils { -void Slaac::UpdateAddresses(otInstance * aInstance, - otNetifAddress *aAddresses, - uint32_t aNumAddresses, - IidCreator aIidCreator, - void * aContext) +Slaac::Slaac(Instance &aInstance) + : InstanceLocator(aInstance) + , mEnabled(true) + , mFilter(NULL) + , mNotifierCallback(aInstance, &Slaac::HandleStateChanged, this) { - otNetworkDataIterator iterator; - otBorderRouterConfig config; + memset(mAddresses, 0, sizeof(mAddresses)); +} - // remove addresses - for (size_t i = 0; i < aNumAddresses; i++) +void Slaac::Enable(void) +{ + VerifyOrExit(!mEnabled); + + otLogInfoUtil("SLAAC:: Enabling"); + mEnabled = true; + Update(kModeAdd); + +exit: + return; +} + +void Slaac::Disable(void) +{ + VerifyOrExit(mEnabled); + + otLogInfoUtil("SLAAC:: Disabling"); + mEnabled = false; + Update(kModeRemove); + +exit: + return; +} + +void Slaac::SetFilter(otIp6SlaacPrefixFilter aFilter) +{ + VerifyOrExit(aFilter != mFilter); + + mFilter = aFilter; + otLogInfoUtil("SLAAC: Filter %s", (mFilter != NULL) ? "updated" : "disabled"); + + VerifyOrExit(mEnabled); + Update(kModeAdd | kModeRemove); + +exit: + return; +} + +bool Slaac::ShouldFilter(const otIp6Prefix &aPrefix) const +{ + return (mFilter != NULL) && mFilter(&GetInstance(), &aPrefix); +} + +void Slaac::HandleStateChanged(Notifier::Callback &aCallback, otChangedFlags aFlags) +{ + aCallback.GetOwner().HandleStateChanged(aFlags); +} + +void Slaac::HandleStateChanged(otChangedFlags aFlags) +{ + UpdateMode mode = kModeNone; + + VerifyOrExit(mEnabled); + + if (aFlags & OT_CHANGED_THREAD_NETDATA) { - otNetifAddress *address = &aAddresses[i]; - bool found = false; + mode |= kModeAdd | kModeRemove; + } - if (!address->mValid) + if (aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED) + { + // When an IPv6 address is removed, we ensure to check if a SLAAC address + // needs to be added (replacing the removed address). + // + // Note that if an address matching a newly added on-mesh prefix (with + // SLAAC flag) is already present (e.g., user previously added an address + // with same prefix), the SLAAC module will not add a SLAAC address with same + // prefix. So on IPv6 address removal event, we check if SLAAC module need + // to add any addresses. + + mode |= kModeAdd; + } + + if (mode != kModeNone) + { + Update(mode); + } + +exit: + return; +} + +void Slaac::Update(UpdateMode aMode) +{ + ThreadNetif & netif = GetNetif(); + NetworkData::Leader & networkData = GetInstance().Get(); + otNetworkDataIterator iterator; + otBorderRouterConfig config; + Ip6::NetifUnicastAddress *slaacAddr; + bool found; + + if (aMode & kModeRemove) + { + // If enabled, remove any SLAAC addresses with no matching on-mesh prefix, + // otherwise (when disabled) remove all previously added SLAAC addresses. + + for (slaacAddr = &mAddresses[0]; slaacAddr < &mAddresses[OT_ARRAY_LENGTH(mAddresses)]; slaacAddr++) { - continue; - } - - iterator = OT_NETWORK_DATA_ITERATOR_INIT; - - while (otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config) == OT_ERROR_NONE) - { - if (config.mSlaac == false) + if (!slaacAddr->mValid) { continue; } - if (otIp6PrefixMatch(&config.mPrefix.mPrefix, &address->mAddress) >= config.mPrefix.mLength && - config.mPrefix.mLength == address->mPrefixLength) - { - found = true; - break; - } - } + found = false; - if (!found) - { - static_cast(aInstance)->GetThreadNetif().RemoveUnicastAddress( - *static_cast(address)); - address->mValid = false; + if (mEnabled) + { + iterator = OT_NETWORK_DATA_ITERATOR_INIT; + + while (networkData.GetNextOnMeshPrefix(&iterator, &config) == OT_ERROR_NONE) + { + otIp6Prefix &prefix = config.mPrefix; + + if (config.mSlaac && !ShouldFilter(prefix) && (prefix.mLength == slaacAddr->mPrefixLength) && + (slaacAddr->GetAddress().PrefixMatch(prefix.mPrefix) >= prefix.mLength)) + { + found = true; + break; + } + } + } + + if (!found) + { + otLogInfoUtil("SLAAC: Removing address %s", slaacAddr->GetAddress().ToString().AsCString()); + + netif.RemoveUnicastAddress(*slaacAddr); + slaacAddr->mValid = false; + } } } - // add addresses - iterator = OT_NETWORK_DATA_ITERATOR_INIT; - - while (otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config) == OT_ERROR_NONE) + if ((aMode & kModeAdd) && mEnabled) { - bool found = false; + // Generate and add SLAAC addresses for any newly added on-mesh prefixes. - if (config.mSlaac == false) - { - continue; - } + iterator = OT_NETWORK_DATA_ITERATOR_INIT; - for (const otNetifAddress *address = otIp6GetUnicastAddresses(aInstance); address != NULL; - address = address->mNext) + while (networkData.GetNextOnMeshPrefix(&iterator, &config) == OT_ERROR_NONE) { - if (otIp6PrefixMatch(&config.mPrefix.mPrefix, &address->mAddress) >= config.mPrefix.mLength && - config.mPrefix.mLength == address->mPrefixLength) + otIp6Prefix &prefix = config.mPrefix; + + if (!config.mSlaac || ShouldFilter(prefix)) { - found = true; - break; + continue; } - } - if (!found) - { - for (size_t i = 0; i < aNumAddresses; i++) + found = false; + + for (const Ip6::NetifUnicastAddress *netifAddr = netif.GetUnicastAddresses(); netifAddr != NULL; + netifAddr = netifAddr->GetNext()) { - otNetifAddress *address = &aAddresses[i]; - - if (address->mValid) + if ((netifAddr->mPrefixLength == prefix.mLength) && + (netifAddr->GetAddress().PrefixMatch(prefix.mPrefix) >= prefix.mLength)) { - continue; + found = true; + break; + } + } + + if (!found) + { + bool added = false; + + for (slaacAddr = &mAddresses[0]; slaacAddr < &mAddresses[OT_ARRAY_LENGTH(mAddresses)]; slaacAddr++) + { + if (slaacAddr->mValid) + { + continue; + } + + memset(slaacAddr, 0, sizeof(*slaacAddr)); + memcpy(&slaacAddr->mAddress, &prefix.mPrefix, BitVectorBytes(prefix.mLength)); + + slaacAddr->mPrefixLength = prefix.mLength; + slaacAddr->mPreferred = config.mPreferred; + slaacAddr->mValid = true; + + GenerateIid(*slaacAddr); + + otLogInfoUtil("SLAAC: Adding address %s", slaacAddr->GetAddress().ToString().AsCString()); + + netif.AddUnicastAddress(*slaacAddr); + + added = true; + break; } - memset(address, 0, sizeof(*address)); - memcpy(&address->mAddress, &config.mPrefix.mPrefix, 8); - - address->mPrefixLength = config.mPrefix.mLength; - address->mPreferred = config.mPreferred; - address->mValid = true; - - if (aIidCreator(aInstance, address, aContext) != OT_ERROR_NONE) + if (!added) { - CreateRandomIid(aInstance, address, aContext); + otLogWarnUtil("SLAAC: Failed to add - max %d addresses supported and already in use", + OT_ARRAY_LENGTH(mAddresses)); } - - static_cast(aInstance)->GetThreadNetif().AddUnicastAddress( - *static_cast(address)); - break; } } } } -otError Slaac::CreateRandomIid(otInstance *, otNetifAddress *aAddress, void *) +void Slaac::GenerateIid(Ip6::NetifUnicastAddress &aAddress) const { - Random::FillBuffer(aAddress->mAddress.mFields.m8 + OT_IP6_ADDRESS_SIZE - OT_IP6_IID_SIZE, OT_IP6_IID_SIZE); - return OT_ERROR_NONE; -} + /* + * This method generates a semantically opaque IID per RFC 7217. + * + * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) + * + * - RID is random (but stable) Identifier. + * - For pseudo-random function `F()` SHA-256 is used in this method. + * - `Net_Iface` is set to constant string "wpan". + * - `Network_ID` is not used (optional per RF-7217). + * - The `secret_key` is randomly generated on first use (using true + * random number generator) and saved in non-volatile settings for + * future use. + * + */ -otError SemanticallyOpaqueIidGenerator::CreateIid(otInstance *aInstance, otNetifAddress *aAddress) -{ - otError error = OT_ERROR_NONE; - - for (uint32_t i = 0; i <= kMaxRetries; i++) - { - error = CreateIidOnce(aInstance, aAddress); - VerifyOrExit(error == OT_ERROR_IP6_ADDRESS_CREATION_FAILURE); - - mDadCounter++; - } - -exit: - return error; -} - -otError SemanticallyOpaqueIidGenerator::CreateIidOnce(otInstance *aInstance, otNetifAddress *aAddress) -{ - otError error = OT_ERROR_NONE; + const uint8_t netIface[] = {'w', 'p', 'a', 'n'}; + uint16_t dadCounter; + IidSecretKey secretKey; Crypto::Sha256 sha256; uint8_t hash[Crypto::Sha256::kHashSize]; - Ip6::Address * address = static_cast(&aAddress->mAddress); - sha256.Start(); + OT_STATIC_ASSERT(sizeof(hash) >= Ip6::Address::kInterfaceIdentifierSize, + "SHA-256 hash size is too small to use as IPv6 address IID"); - sha256.Update(aAddress->mAddress.mFields.m8, aAddress->mPrefixLength / 8); + GetIidSecretKey(secretKey); - VerifyOrExit(mInterfaceId != NULL, error = OT_ERROR_INVALID_ARGS); - sha256.Update(mInterfaceId, mInterfaceIdLength); - - if (mNetworkIdLength) + for (dadCounter = 0; dadCounter < kMaxIidCreationAttempts; dadCounter++) { - VerifyOrExit(mNetworkId != NULL, error = OT_ERROR_INVALID_ARGS); - sha256.Update(mNetworkId, mNetworkIdLength); + sha256.Start(); + sha256.Update(aAddress.mAddress.mFields.m8, BitVectorBytes(aAddress.mPrefixLength)); + sha256.Update(netIface, sizeof(netIface)); + sha256.Update(reinterpret_cast(&dadCounter), sizeof(dadCounter)); + sha256.Update(secretKey.m8, sizeof(IidSecretKey)); + sha256.Finish(hash); + + aAddress.GetAddress().SetIid(&hash[0]); + + // Exit and return the address if the IID is not reserved, + // otherwise, try again with a new dadCounter + + VerifyOrExit(aAddress.GetAddress().IsIidReserved()); } - sha256.Update(static_cast(&mDadCounter), sizeof(mDadCounter)); - - VerifyOrExit(mSecretKey != NULL, error = OT_ERROR_INVALID_ARGS); - sha256.Update(mSecretKey, mSecretKeyLength); - - sha256.Finish(hash); - - memcpy(&aAddress->mAddress.mFields.m8[OT_IP6_ADDRESS_SIZE - OT_IP6_IID_SIZE], &hash[sizeof(hash) - OT_IP6_IID_SIZE], - OT_IP6_IID_SIZE); - - VerifyOrExit(!IsAddressRegistered(aInstance, aAddress), error = OT_ERROR_IP6_ADDRESS_CREATION_FAILURE); - VerifyOrExit(!address->IsIidReserved(), error = OT_ERROR_IP6_ADDRESS_CREATION_FAILURE); + otLogWarnUtil("SLAAC: Failed to generate a non-reserved IID after %d attempts", dadCounter); + Random::FillBuffer(hash, Ip6::Address::kInterfaceIdentifierSize); + aAddress.GetAddress().SetIid(&hash[0]); exit: - return error; + return; } -bool SemanticallyOpaqueIidGenerator::IsAddressRegistered(otInstance *aInstance, otNetifAddress *aCreatedAddress) +void Slaac::GetIidSecretKey(IidSecretKey &aKey) const { - bool result = false; - const otNetifAddress *address = otIp6GetUnicastAddresses(aInstance); + otError error; + Settings &settings = GetInstance().GetSettings(); - while (address != NULL) + error = settings.ReadSlaacIidSecretKey(aKey); + VerifyOrExit(error != OT_ERROR_NONE); + + // If there is no previously saved secret key, generate + // a random one and save it. + + error = otPlatRandomGetTrue(aKey.m8, sizeof(IidSecretKey)); + + if (error != OT_ERROR_NONE) { - if (0 == memcmp(aCreatedAddress->mAddress.mFields.m8, address->mAddress.mFields.m8, - sizeof(address->mAddress.mFields))) - { - ExitNow(result = true); - } - - address = address->mNext; + Random::FillBuffer(aKey.m8, sizeof(IidSecretKey)); } + settings.SaveSlaacIidSecretKey(aKey); + + otLogInfoUtil("SLAAC: Generated and saved secret key"); + exit: - return result; + return; } } // namespace Utils } // namespace ot + +#endif // OPENTHREAD_CONFIG_ENABLE_SLAAC \ No newline at end of file diff --git a/src/core/utils/slaac_address.hpp b/src/core/utils/slaac_address.hpp index 34a4df920..fe0fb8c16 100644 --- a/src/core/utils/slaac_address.hpp +++ b/src/core/utils/slaac_address.hpp @@ -36,7 +36,9 @@ #include "openthread-core-config.h" -#include +#include "common/locator.hpp" +#include "common/notifier.hpp" +#include "net/netif.hpp" namespace ot { namespace Utils { @@ -54,105 +56,90 @@ namespace Utils { * This class implements the SLAAC utility for Thread protocol. * */ -class Slaac +class Slaac : public InstanceLocator { public: - /** - * This function pointer is called when SLAAC creates global address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * @param[in] aContext A pointer to creator-specific context. - * - * @retval OT_ERROR_NONE if generated valid IID. - * @retval OT_ERROR_FAILED if creating IID failed. - * - */ - typedef otError (*IidCreator)(otInstance *aInstance, otNetifAddress *aAddress, void *aContext); + enum + { + kIidSecretKeySize = 32, ///< Number of bytes in secret key for generating semantically opaque IID. + }; /** - * This function update addresses that shall be automatically created using SLAAC. - * - * @param[in] aInstance A pointer to OpenThread instance. - * @param[inout] aAddresses A pointer to an array containing addresses created by this module. - * @param[in] aNumAddresses The number of elements in aAddresses array. - * @param[in] aIidCreator A pointer to function that will be used to create IID for IPv6 addresses. - * @param[in] aContext A pointer to IID creator-specific context data. + * This type represents the secret key used for generating semantically opaque IID (per RFC 7217). * */ - static void UpdateAddresses(otInstance * aInstance, - otNetifAddress *aAddresses, - uint32_t aNumAddresses, - IidCreator aIidCreator, - void * aContext); + struct IidSecretKey + { + uint8_t m8[kIidSecretKeySize]; + }; /** - * This function creates randomly generated IPv6 IID for given IPv6 address. + * This constructor initializes the SLAAC manager object. * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * @param[in] aContext Unused pointer. + * Note that SLAAC module starts enabled. + * + * @param[in] aInstance A reference to the OpenThread instance. * - * @retval OT_ERROR_NONE Generated valid IID. */ - static otError CreateRandomIid(otInstance *aInstance, otNetifAddress *aAddress, void *aContext); -}; + explicit Slaac(Instance &aInstance); -/** - * This class implements the Method for Generating Semantically Opaque IIDs with IPv6 SLAAC (RFC 7217). - * - */ -class SemanticallyOpaqueIidGenerator : public otSemanticallyOpaqueIidGeneratorData -{ -public: /** - * This method creates semantically opaque IID for given IPv6 address and context. + * This method enables the SLAAC module. * - * The generator starts with DAD counter provided as a class member field. The DAD counter is automatically - * incremented at most kMaxRetries times if creation of valid IPv6 address fails. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * - * @retval OT_ERROR_NONE Generated valid IID. - * @retval OT_ERROR_INVALID_ARGS Any given parameter is invalid. - * @retval OT_ERROR_IP6_ADDRESS_CREATION_FAILURE Could not generate IID due to RFC 7217 restrictions. + * When enabled, new SLAAC addresses are generated and added fron on-mesh prefixes in network data. * */ - otError CreateIid(otInstance *aInstance, otNetifAddress *aAddress); + void Enable(void); + + /** + * This method disables the SLAAC module. + * + * When disabled, any previously added SLAAC address by this module is removed. + * + */ + void Disable(void); + + /** + * This methods sets a SLAAC prefix filter handler. + * + * The handler is invoked by SLAAC module when it is about to add a SLAAC address based on a prefix. The return + * boolean value from handler determines whether the address is filtered or added (TRUE to filter the address, + * FALSE to add address). + * + * The filter can be set to `NULL` to disable filtering (i.e., allow SLAAC addresses for all prefixes). + * + */ + void SetFilter(otIp6SlaacPrefixFilter aFilter); private: enum { - kMaxRetries = 255, + kMaxIidCreationAttempts = 1024, // Maximum number of attempts when generating IID. }; - /** - * This method creates semantically opaque IID for given arguments. - * - * This function creates IID only for given DAD counter value. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * - * @retval OT_ERROR_NONE Generated valid IID. - * @retval OT_ERROR_INVALID_ARGS Any given parameter is invalid. - * @retval OT_ERROR_IP6_ADDRESS_CREATION_FAILURE Could not generate IID due to RFC 7217 restrictions. - * - */ - otError CreateIidOnce(otInstance *aInstance, otNetifAddress *aAddress); + // Values for `UpdateMode` input parameter in `Update()`. + enum + { + kModeNone = 0x0, // No action. + kModeAdd = 1 << 0, // Add new SLAAC addresses for new prefixes in network data. + kModeRemove = 1 << 1, // Remove SLAAC addresses. + // When SLAAC is enabled, remove addresses with no matching prefix in network data, + // When SLAAC is disabled, remove all previously added addresses. + }; - /** - * This method checks if created IPv6 address is already registered in the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aCreatedAddress A pointer to created IPv6 address. - * - * @retval true Given address is present in the address list. - * @retval false Given address is missing in the address list. - * - */ - bool IsAddressRegistered(otInstance *aInstance, otNetifAddress *aCreatedAddress); + typedef uint8_t UpdateMode; + + bool ShouldFilter(const otIp6Prefix &aPrefix) const; + void Update(UpdateMode aMode); + void GenerateIid(Ip6::NetifUnicastAddress &aAddress) const; + void GetIidSecretKey(IidSecretKey &aKey) const; + static void HandleStateChanged(Notifier::Callback &aCallback, otChangedFlags aFlags); + void HandleStateChanged(otChangedFlags aFlags); + + bool mEnabled; + otIp6SlaacPrefixFilter mFilter; + Notifier::Callback mNotifierCallback; + Ip6::NetifUnicastAddress mAddresses[OPENTHREAD_CONFIG_NUM_SLAAC_ADDRESSES]; }; /**