[slaac] enhance and simplify SLAAC support (#3621)

This commit enhances the SLAAC support in OpenThread core. It contains
the following changes and features:

The `Utils::Slaac` class is updated to include all the related code
for managing SLAAC addresses (e.g., `Slaac` class maintains the
address buffer and directly subscribes to `Notifier` to listen for
Network Data changes to update SLAAC addresses).

The SLAAC address module is changed to support and use semantically
opaque IID generation algorithm (RFC 7217) instead of random IID
generation. This ensures that SLAAC addresses are random but stable
(i.e., the same SLAAC address is added on a device for the same
prefix) and aligns the implementation with Thread specification
requirement.

The semantically opaque IID generation logic is updated to follow RFC
7217 with SHA-256 as the pseudo-random function, and a 256 bit secret
key (generated once using true random number generator and saved in
non-volatile settings).

A new feature is added to allow SLAAC support to be enabled or
disabled during network operation. When enabled, SLAAC addresses are
generated and added to the interface. When disabled, any previously
added SLAAC address is removed.

This commit also adds "SLAAC prefix filter" feature which allows
OpenThread users to register a filter handler with SLAAC module. The
handler is invoked by SLAAC module when it is about to add a SLAAC
address based on a prefix. The returned boolean value from the handler
determines whether the address should be filtered or not.
This commit is contained in:
Abtin Keshavarzian
2019-03-11 09:35:07 -07:00
committed by Jonathan Hui
parent 6453c9f033
commit 58f144a7f7
11 changed files with 459 additions and 318 deletions
+48 -55
View File
@@ -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);
/**
* @}
*
+28 -21
View File
@@ -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<Instance *>(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<Utils::SemanticallyOpaqueIidGenerator *>(aContext)->CreateIid(aInstance, aAddress);
}
void otIp6SetReceiveCallback(otInstance *aInstance, otIp6ReceiveCallback aCallback, void *aCallbackContext)
{
Instance &instance = *static_cast<Instance *>(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<Instance *>(aInstance);
Utils::Slaac &slaac = instance.Get<Utils::Slaac>();
if (aEnabled)
{
slaac.Enable();
}
else
{
slaac.Disable();
}
}
void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFilter)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<Utils::Slaac>().SetFilter(aFilter);
}
#endif // OPENTHREAD_CONFIG_ENABLE_SLAAC
+7
View File
@@ -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)
{
+52 -6
View File
@@ -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.
*
+1 -1
View File
@@ -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));
}
+1 -1
View File
@@ -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.
-4
View File
@@ -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
+3 -14
View File
@@ -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
+15 -9
View File
@@ -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;
+240 -130
View File
@@ -35,197 +35,307 @@
#include "utils/wrap_string.h"
#include <openthread/ip6.h>
#include <openthread/netdata.h>
#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<Slaac>().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<NetworkData::Leader>();
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<Instance *>(aInstance)->GetThreadNetif().RemoveUnicastAddress(
*static_cast<Ip6::NetifUnicastAddress *>(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<Instance *>(aInstance)->GetThreadNetif().AddUnicastAddress(
*static_cast<Ip6::NetifUnicastAddress *>(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<Ip6::Address *>(&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<uint8_t *>(&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<uint8_t *>(&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
+64 -77
View File
@@ -36,7 +36,9 @@
#include "openthread-core-config.h"
#include <openthread/ip6.h>
#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];
};
/**