mirror of
https://github.com/espressif/openthread.git
synced 2026-09-09 18:50:07 +00:00
[dua] support to specify iid for domain unicast address (#4900)
This commit is contained in:
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (2)
|
||||
#define OPENTHREAD_API_VERSION (3)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -57,6 +57,24 @@ extern "C" {
|
||||
#define OT_IP6_IID_SIZE 8 ///< Size of an IPv6 Interface Identifier (bytes)
|
||||
#define OT_IP6_ADDRESS_SIZE 16 ///< Size of an IPv6 address (bytes)
|
||||
|
||||
/**
|
||||
* @struct otIp6InterfaceIdentifier
|
||||
*
|
||||
* This structure represents the Interface Identifier of an IPv6 address.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
struct otIp6InterfaceIdentifier
|
||||
{
|
||||
uint8_t m8[OT_IP6_IID_SIZE]; ///< The Interface Identifier of an IPv6 address.
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
* This structure represents the Interface Identifier of an IPv6 address.
|
||||
*
|
||||
*/
|
||||
typedef struct otIp6InterfaceIdentifier otIp6InterfaceIdentifier;
|
||||
|
||||
/**
|
||||
* @struct otIp6Address
|
||||
*
|
||||
|
||||
@@ -477,7 +477,7 @@ otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName);
|
||||
/**
|
||||
* Get the Thread Domain Name.
|
||||
*
|
||||
* This function is only availble since Thread 1.2.
|
||||
* This function is only available since Thread 1.2.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
@@ -491,7 +491,7 @@ const char *otThreadGetDomainName(otInstance *aInstance);
|
||||
/**
|
||||
* Set the Thread Domain Name.
|
||||
*
|
||||
* This function is only availble since Thread 1.2.
|
||||
* This function is only available since Thread 1.2.
|
||||
* This function succeeds only when Thread protocols are disabled.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
@@ -505,6 +505,35 @@ const char *otThreadGetDomainName(otInstance *aInstance);
|
||||
*/
|
||||
otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName);
|
||||
|
||||
/**
|
||||
* Set/Clear the Interface Identifier manually specified for the Thread Domain Unicast Address.
|
||||
*
|
||||
* This function is only available since Thread 1.2 when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aIid A pointer to the Interface Identifier to set or NULL to clear.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully set/cleared the Interface Identifier.
|
||||
* @retval OT_ERROR_INVALID_ARGS The specified Interface Identifier is reserved.
|
||||
*
|
||||
* @sa otThreadGetFixedDuaInterfaceIdentifier
|
||||
*/
|
||||
otError otThreadSetFixedDuaInterfaceIdentifier(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid);
|
||||
|
||||
/**
|
||||
* Get the Interface Identifier manually specified for the Thread Domain Unicast Address.
|
||||
*
|
||||
* This function is only available since Thread 1.2 when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
* @returns A pointer to the Interface Identifier which was set manually, or NULL if none was set.
|
||||
*
|
||||
* @sa otThreadSetFixedDuaInterfaceIdentifier
|
||||
*
|
||||
*/
|
||||
const otIp6InterfaceIdentifier *otThreadGetFixedDuaInterfaceIdentifier(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Get the thrKeySequenceCounter.
|
||||
*
|
||||
|
||||
@@ -39,6 +39,7 @@ Done
|
||||
- [discover](#discover-channel)
|
||||
- [dns](#dns-resolve-hostname-dns-server-ip-dns-server-port)
|
||||
- [domainname](#domainname)
|
||||
- [dua](#dua-iid)
|
||||
- [eidcache](#eidcache)
|
||||
- [eui64](#eui64)
|
||||
- [extaddr](#extaddr)
|
||||
@@ -562,6 +563,34 @@ Set the Thread Domain Name for Thread 1.2 device.
|
||||
Done
|
||||
```
|
||||
|
||||
### dua iid
|
||||
|
||||
Get the Interface Identifier mannually specified for Thread Domain Unicast Address on Thread 1.2 device.
|
||||
|
||||
```bash
|
||||
> dua iid
|
||||
0004000300020001
|
||||
Done
|
||||
```
|
||||
|
||||
### dua iid \<iid\>
|
||||
|
||||
Set the Interface Identifier mannually specified for Thread Domain Unicast Address on Thread 1.2 device.
|
||||
|
||||
```bash
|
||||
> dua iid 0004000300020001
|
||||
Done
|
||||
```
|
||||
|
||||
### dua iid clear
|
||||
|
||||
Clear the Interface Identifier mannually specified for Thread Domain Unicast Address on Thread 1.2 device.
|
||||
|
||||
```bash
|
||||
> dua iid clear
|
||||
Done
|
||||
```
|
||||
|
||||
### eidcache
|
||||
|
||||
Print the EID-to-RLOC cache entries.
|
||||
|
||||
@@ -147,6 +147,9 @@ const struct Command Interpreter::sCommands[] = {
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
{"domainname", &Interpreter::ProcessDomainName},
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
{"dua", &Interpreter::ProcessDua},
|
||||
#endif
|
||||
#if OPENTHREAD_FTD
|
||||
{"eidcache", &Interpreter::ProcessEidCache},
|
||||
#endif
|
||||
@@ -609,6 +612,51 @@ exit:
|
||||
AppendResult(error);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
void Interpreter::ProcessDua(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aArgsLength >= 1 && strcmp(aArgs[0], "iid") == 0, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
switch (aArgsLength)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(mInstance);
|
||||
|
||||
if (iid != NULL)
|
||||
{
|
||||
OutputBytes(iid->m8, sizeof(otIp6InterfaceIdentifier));
|
||||
mServer->OutputFormat("\r\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
if (strcmp(aArgs[1], "clear") == 0)
|
||||
{
|
||||
SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
otIp6InterfaceIdentifier iid;
|
||||
|
||||
VerifyOrExit(Hex2Bin(aArgs[1], iid.m8, sizeof(otIp6InterfaceIdentifier)) ==
|
||||
sizeof(otIp6InterfaceIdentifier),
|
||||
error = OT_ERROR_INVALID_ARGS);
|
||||
SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, &iid));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
AppendResult(error);
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
|
||||
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
|
||||
void Interpreter::ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[])
|
||||
|
||||
@@ -211,6 +211,11 @@ private:
|
||||
#endif
|
||||
|
||||
void ProcessDomainName(uint8_t aArgsLength, char *aArgs[]);
|
||||
|
||||
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
void ProcessDua(uint8_t aArgsLength, char *aArgs[]);
|
||||
#endif
|
||||
|
||||
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
@@ -220,6 +220,40 @@ otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName)
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
otError otThreadSetFixedDuaInterfaceIdentifier(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid)
|
||||
{
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
if (aIid)
|
||||
{
|
||||
error = instance.Get<DuaManager>().SetFixedDuaInterfaceIdentifier(
|
||||
*static_cast<const Ip6::InterfaceIdentifier *>(aIid));
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.Get<DuaManager>().ClearFixedDuaInterfaceIdentifier();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
const otIp6InterfaceIdentifier *otThreadGetFixedDuaInterfaceIdentifier(otInstance *aInstance)
|
||||
{
|
||||
Instance & instance = *static_cast<Instance *>(aInstance);
|
||||
const otIp6InterfaceIdentifier *iid = NULL;
|
||||
|
||||
if (instance.Get<DuaManager>().IsFixedDuaInterfaceIdentifierSet())
|
||||
{
|
||||
iid = &instance.Get<DuaManager>().GetFixedDuaInterfaceIdentifier();
|
||||
}
|
||||
|
||||
return iid;
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
|
||||
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
|
||||
uint32_t otThreadGetKeySequenceCounter(otInstance *aInstance)
|
||||
|
||||
@@ -45,6 +45,25 @@ using ot::Encoding::BigEndian::HostSwap32;
|
||||
namespace ot {
|
||||
namespace Ip6 {
|
||||
|
||||
bool InterfaceIdentifier::IsUnspecified(void) const
|
||||
{
|
||||
return (m8[0] == 0 && m8[1] == 0 && m8[2] == 0 && m8[3] == 0 && m8[4] == 0 && m8[5] == 0 && m8[6] == 0 &&
|
||||
m8[7] == 0);
|
||||
}
|
||||
|
||||
bool InterfaceIdentifier::IsReserved(void) const
|
||||
{
|
||||
Address addr;
|
||||
|
||||
addr.SetIid(this->m8);
|
||||
return addr.IsIidReserved();
|
||||
}
|
||||
|
||||
InterfaceIdentifier::InfoString InterfaceIdentifier::ToString(void) const
|
||||
{
|
||||
return InfoString("%02x%02x%02x%02x%02x%02x%02x%02x", m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
|
||||
}
|
||||
|
||||
bool Address::IsUnspecified(void) const
|
||||
{
|
||||
return (mFields.m32[0] == 0 && mFields.m32[1] == 0 && mFields.m32[2] == 0 && mFields.m32[3] == 0);
|
||||
@@ -213,6 +232,11 @@ void Address::SetMulticastNetworkPrefix(const uint8_t *aPrefix, uint8_t aPrefixL
|
||||
mFields.m8[kMulticastNetworkPrefixLengthOffset] = aPrefixLength;
|
||||
}
|
||||
|
||||
bool Address::HasIid(const uint8_t *aIid) const
|
||||
{
|
||||
return (memcmp(mFields.m8 + kInterfaceIdentifierOffset, aIid, kInterfaceIdentifierSize) == 0);
|
||||
}
|
||||
|
||||
void Address::SetIid(const uint8_t *aIid)
|
||||
{
|
||||
memcpy(mFields.m8 + kInterfaceIdentifierOffset, aIid, kInterfaceIdentifierSize);
|
||||
|
||||
@@ -57,6 +57,54 @@ namespace Ip6 {
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class represents the Interface Identifier of an IPv6 address.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class InterfaceIdentifier : public otIp6InterfaceIdentifier,
|
||||
public Equatable<InterfaceIdentifier>,
|
||||
public Clearable<InterfaceIdentifier>
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
kInfoStringSize = 17, // Max chars for the info string (`ToString()`).
|
||||
};
|
||||
|
||||
/**
|
||||
* This type defines the fixed-length `String` object returned from `ToString()`.
|
||||
*
|
||||
*/
|
||||
typedef String<kInfoStringSize> InfoString;
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the Interface Identifier is unspecified.
|
||||
*
|
||||
* @retval true If the Interface Identifier is unspecified.
|
||||
* @retval false If the Interface Identifier is not unspecified.
|
||||
*
|
||||
*/
|
||||
bool IsUnspecified(void) const;
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the Interface Identifier is reserved (RFC 5453).
|
||||
*
|
||||
* @retval true If the Interface Identifier is reserved.
|
||||
* @retval false If the Interface Identifier is not reserved.
|
||||
*
|
||||
*/
|
||||
bool IsReserved(void) const;
|
||||
|
||||
/**
|
||||
* This method converts an Interface Identifier to a string.
|
||||
*
|
||||
* @returns An `InfoString` containing the string representation of the Interface Identifier.
|
||||
*
|
||||
*/
|
||||
InfoString ToString(void) const;
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
* This class implements an IPv6 address object.
|
||||
*
|
||||
@@ -81,7 +129,7 @@ public:
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kInterfaceIdentifierSize = 8, ///< Interface Identifier size in bytes.
|
||||
kInterfaceIdentifierSize = OT_IP6_IID_SIZE, ///< Interface Identifier size in bytes.
|
||||
kIp6AddressStringSize = 40, ///< Max buffer size in bytes to store an IPv6 address in string format.
|
||||
};
|
||||
|
||||
@@ -451,13 +499,43 @@ public:
|
||||
*/
|
||||
uint8_t *GetIid(void) { return mFields.m8 + kInterfaceIdentifierOffset; }
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the IPv6 address has the specified Interface Identifier.
|
||||
*
|
||||
* @param[in] aIid A pointer to the Interface Identifier.
|
||||
*
|
||||
* @retval true If the IPv6 address has the specified Interface Identifier.
|
||||
* @retval false If the IPv6 address doesn't have the specified Interface Identifier.
|
||||
*
|
||||
*/
|
||||
bool HasIid(const uint8_t *aIid) const;
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the IPv6 address has the specified Interface Identifier.
|
||||
*
|
||||
* @param[in] aIid A reference to the Interface Identifier.
|
||||
*
|
||||
* @retval true If the IPv6 address has the specified Interface Identifier.
|
||||
* @retval false If the IPv6 address doesn't have the specified Interface Identifier.
|
||||
*
|
||||
*/
|
||||
bool HasIid(const InterfaceIdentifier &aIid) const { return HasIid(aIid.m8); }
|
||||
|
||||
/**
|
||||
* This method sets the Interface Identifier.
|
||||
*
|
||||
* @param[in] aIid A pointer to the Interface Identifier.
|
||||
*
|
||||
*/
|
||||
void SetIid(const uint8_t *aIid);
|
||||
|
||||
/**
|
||||
* This method sets the Interface Identifier.
|
||||
*
|
||||
* @param[in] aIid A reference to the Interface Identifier.
|
||||
*
|
||||
*/
|
||||
void SetIid(const uint8_t *aIid);
|
||||
void SetIid(const InterfaceIdentifier &aIid) { SetIid(aIid.m8); }
|
||||
|
||||
/**
|
||||
* This method sets the Interface Identifier.
|
||||
|
||||
@@ -54,6 +54,8 @@ DuaManager::DuaManager(Instance &aInstance)
|
||||
mDomainUnicastAddress.mValid = true;
|
||||
mDomainUnicastAddress.mScopeOverride = Ip6::Address::kGlobalScope;
|
||||
mDomainUnicastAddress.mScopeOverrideValid = true;
|
||||
|
||||
mFixedDuaInterfaceIdentifier.Clear();
|
||||
}
|
||||
|
||||
void DuaManager::UpdateDomainUnicastAddress(BackboneRouter::Leader::DomainPrefixState aState)
|
||||
@@ -64,7 +66,6 @@ void DuaManager::UpdateDomainUnicastAddress(BackboneRouter::Leader::DomainPrefix
|
||||
(aState == BackboneRouter::Leader::kDomainPrefixRefreshed))
|
||||
{
|
||||
Get<ThreadNetif>().RemoveUnicastAddress(mDomainUnicastAddress);
|
||||
mDomainUnicastAddress.GetAddress().Clear();
|
||||
}
|
||||
|
||||
VerifyOrExit((aState == BackboneRouter::Leader::kDomainPrefixAdded) ||
|
||||
@@ -75,19 +76,82 @@ void DuaManager::UpdateDomainUnicastAddress(BackboneRouter::Leader::DomainPrefix
|
||||
|
||||
OT_ASSERT(prefix != NULL);
|
||||
|
||||
mDomainUnicastAddress.GetAddress().SetPrefix(prefix->mPrefix.mFields.m8, prefix->mLength);
|
||||
mDomainUnicastAddress.mPrefixLength = prefix->mLength;
|
||||
mDomainUnicastAddress.GetAddress().Clear();
|
||||
mDomainUnicastAddress.GetAddress().SetPrefix(prefix->mPrefix.mFields.m8, prefix->mLength);
|
||||
|
||||
if (Get<Utils::Slaac>().GenerateIid(mDomainUnicastAddress, NULL, 0, &mDadCounter) == OT_ERROR_NONE)
|
||||
// Apply cached DUA Interface Identifier manually specified.
|
||||
if (IsFixedDuaInterfaceIdentifierSet())
|
||||
{
|
||||
Get<ThreadNetif>().AddUnicastAddress(mDomainUnicastAddress);
|
||||
mDomainUnicastAddress.GetAddress().SetIid(mFixedDuaInterfaceIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
mDomainUnicastAddress.GetAddress().Clear();
|
||||
otLogWarnCore("Failed to generate valid DUA");
|
||||
SuccessOrExit(GenerateDomainUnicastAddressIid());
|
||||
}
|
||||
|
||||
Get<ThreadNetif>().AddUnicastAddress(mDomainUnicastAddress);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
otError DuaManager::GenerateDomainUnicastAddressIid(void)
|
||||
{
|
||||
otError error;
|
||||
|
||||
if ((error = Get<Utils::Slaac>().GenerateIid(mDomainUnicastAddress, NULL, 0, &mDadCounter)) == OT_ERROR_NONE)
|
||||
{
|
||||
otLogInfoIp6("Generated DUA: %s", mDomainUnicastAddress.GetAddress().ToString().AsCString());
|
||||
}
|
||||
else
|
||||
{
|
||||
otLogWarnIp6("Generate DUA: %s", otThreadErrorToString(error));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
otError DuaManager::SetFixedDuaInterfaceIdentifier(const Ip6::InterfaceIdentifier &aIid)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(!aIid.IsReserved(), error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(mFixedDuaInterfaceIdentifier.IsUnspecified() || mFixedDuaInterfaceIdentifier != aIid, OT_NOOP);
|
||||
|
||||
mFixedDuaInterfaceIdentifier = aIid;
|
||||
otLogInfoIp6("Set DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
|
||||
|
||||
if (Get<ThreadNetif>().IsUnicastAddress(GetDomainUnicastAddress()))
|
||||
{
|
||||
Get<ThreadNetif>().RemoveUnicastAddress(mDomainUnicastAddress);
|
||||
mDomainUnicastAddress.GetAddress().SetIid(mFixedDuaInterfaceIdentifier);
|
||||
Get<ThreadNetif>().AddUnicastAddress(mDomainUnicastAddress);
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void DuaManager::ClearFixedDuaInterfaceIdentifier(void)
|
||||
{
|
||||
// Nothing to clear.
|
||||
VerifyOrExit(IsFixedDuaInterfaceIdentifierSet(), OT_NOOP);
|
||||
|
||||
if (GetDomainUnicastAddress().HasIid(mFixedDuaInterfaceIdentifier) &&
|
||||
Get<ThreadNetif>().IsUnicastAddress(GetDomainUnicastAddress()))
|
||||
{
|
||||
Get<ThreadNetif>().RemoveUnicastAddress(mDomainUnicastAddress);
|
||||
|
||||
if (GenerateDomainUnicastAddressIid() == OT_ERROR_NONE)
|
||||
{
|
||||
Get<ThreadNetif>().AddUnicastAddress(mDomainUnicastAddress);
|
||||
}
|
||||
}
|
||||
|
||||
otLogInfoIp6("Cleared DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
|
||||
mFixedDuaInterfaceIdentifier.Clear();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,45 @@ public:
|
||||
*/
|
||||
const Ip6::Address &GetDomainUnicastAddress(void) const { return mDomainUnicastAddress.GetAddress(); }
|
||||
|
||||
/**
|
||||
* This method sets the Interface Identifier manually specified for the Thread Domain Unicast Address.
|
||||
*
|
||||
* @param[in] aIid A reference to the Interface Identifier to set.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully set the Interface Identifier.
|
||||
* @retval OT_ERROR_INVALID_ARGS The specified Interface Identifier is reserved.
|
||||
*
|
||||
*/
|
||||
otError SetFixedDuaInterfaceIdentifier(const Ip6::InterfaceIdentifier &aIid);
|
||||
|
||||
/**
|
||||
* This method clears the Interface Identifier manually specified for the Thread Domain Unicast Address.
|
||||
*
|
||||
*/
|
||||
void ClearFixedDuaInterfaceIdentifier(void);
|
||||
|
||||
/**
|
||||
* This method indicates whether or not there is Interface Identifier manually specified for the Thread
|
||||
* Domain Unicast Address.
|
||||
*
|
||||
* @retval true If there is Interface Identifier manually specified.
|
||||
* @retval false If there is no Interface Identifier manually specified.
|
||||
*
|
||||
*/
|
||||
bool IsFixedDuaInterfaceIdentifierSet(void) { return !mFixedDuaInterfaceIdentifier.IsUnspecified(); }
|
||||
|
||||
/**
|
||||
* This method gets the Interface Identifier for the Thread Domain Unicast Address if manually specified.
|
||||
*
|
||||
* @returns A reference to the Interface Identifier.
|
||||
*
|
||||
*/
|
||||
const Ip6::InterfaceIdentifier &GetFixedDuaInterfaceIdentifier(void) const { return mFixedDuaInterfaceIdentifier; }
|
||||
|
||||
private:
|
||||
otError GenerateDomainUnicastAddressIid(void);
|
||||
|
||||
Ip6::InterfaceIdentifier mFixedDuaInterfaceIdentifier;
|
||||
Ip6::NetifUnicastAddress mDomainUnicastAddress;
|
||||
uint8_t mDadCounter;
|
||||
};
|
||||
|
||||
@@ -607,6 +607,41 @@ def check_address_registration_tlv(
|
||||
return found
|
||||
|
||||
|
||||
def check_compressed_address_registration_tlv(command_msg,
|
||||
cid,
|
||||
iid,
|
||||
cid_present_once=False):
|
||||
'''Check whether or not a compressed IPv6 address in AddressRegistrationTlv.
|
||||
note: only compare the iid part of the address.
|
||||
|
||||
Args:
|
||||
command_msg (MleMessage) : The Mle message to check.
|
||||
cid (int): The context id of the domain prefix.
|
||||
iid (string): The Interface Identifier.
|
||||
cid_present_once(boolean): True if cid entry should apprear only once in AR Tlv.
|
||||
False otherwise.
|
||||
'''
|
||||
found = False
|
||||
cid_cnt = 0
|
||||
|
||||
addresses = command_msg.assertMleMessageContainsTlv(
|
||||
mle.AddressRegistration).addresses
|
||||
|
||||
for item in addresses:
|
||||
if isinstance(item, mle.AddressCompressed):
|
||||
if cid == item.cid:
|
||||
cid_cnt = cid_cnt + 1
|
||||
if iid == item.iid.hex():
|
||||
found = True
|
||||
break
|
||||
assert found, 'Error: Expected (cid, iid):({},{}) Not Found'.format(
|
||||
cid, iid)
|
||||
|
||||
assert cid_present_once == (
|
||||
cid_cnt == 1), 'Error: Expected cid present {} but present {}'.format(
|
||||
'once' if cid_present_once else '', cid_cnt)
|
||||
|
||||
|
||||
def assert_contains_tlv(tlvs, check_type, tlv_type):
|
||||
"""Assert a tlv list contains specific tlv and return the first qualified.
|
||||
"""
|
||||
|
||||
@@ -60,9 +60,13 @@ LINK_LOCAL_ALL_NODES_ADDRESS = 'ff02::1'
|
||||
LINK_LOCAL_ALL_ROUTERS_ADDRESS = 'ff02::2'
|
||||
|
||||
DOMAIN_PREFIX = 'fd00:7d03:7d03:7d03::/64'
|
||||
ALL_DOMAIN_BBRS_ADDRESS = 'ff32:40:fd00:7d03:7d03:7d03:0:3'
|
||||
DOMAIN_PREFIX_ALTER = 'fd00:7d04:7d04:7d04::/64'
|
||||
|
||||
ALL_NETWORK_BBRS_ADDRESS = 'ff32:40:fd00:db8:0:0:0:3'
|
||||
|
||||
ALL_DOMAIN_BBRS_ADDRESS = 'ff32:40:fd00:7d03:7d03:7d03:0:3'
|
||||
ALL_DOMAIN_BBRS_ADDRESS_ALTER = 'ff32:40:fd00:7d04:7d04:7d04:0:3'
|
||||
|
||||
DEFAULT_MASTER_KEY = bytearray([
|
||||
0x00,
|
||||
0x11,
|
||||
|
||||
@@ -514,8 +514,7 @@ class Node:
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def set_domain_prefix(self, prefix):
|
||||
flags = 'prosD'
|
||||
def set_domain_prefix(self, prefix, flags='prosD'):
|
||||
self.add_prefix(prefix, flags)
|
||||
self.register_netdata()
|
||||
|
||||
@@ -523,6 +522,16 @@ class Node:
|
||||
self.remove_prefix(prefix)
|
||||
self.register_netdata()
|
||||
|
||||
def set_dua_iid(self, iid):
|
||||
cmd = 'dua iid {}'.format(iid)
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def clear_dua_iid(self):
|
||||
cmd = 'dua iid clear'
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def set_link_quality(self, addr, lqi):
|
||||
cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi)
|
||||
self.send_command(cmd)
|
||||
@@ -748,6 +757,16 @@ class Node:
|
||||
|
||||
return None
|
||||
|
||||
def has_ipaddr(self, address):
|
||||
ipaddr = ipaddress.ip_address(address)
|
||||
ipaddrs = self.get_addrs()
|
||||
for addr in ipaddrs:
|
||||
if isinstance(addr, bytearray):
|
||||
addr = bytes(addr)
|
||||
if ipaddress.ip_address(addr) == ipaddr:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_ipmaddrs(self):
|
||||
self.send_command('ipmaddr')
|
||||
return self._expect_results(r'\S+(:\S*)+')
|
||||
@@ -880,7 +899,7 @@ class Node:
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def add_prefix(self, prefix, flags, prf='med'):
|
||||
def add_prefix(self, prefix, flags='paosr', prf='med'):
|
||||
cmd = 'prefix add %s %s %s' % (prefix, flags, prf)
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
import command
|
||||
import config
|
||||
import ipaddress
|
||||
import mle
|
||||
import thread_cert
|
||||
|
||||
BBR_1 = 1 # Collapsed with Leader Role
|
||||
ROUTER_1_1 = 2
|
||||
ROUTER_1_2 = 3
|
||||
MED_1_2 = 4
|
||||
SED_1_2 = 5
|
||||
|
||||
WAIT_ATTACH = 5
|
||||
WAIT_REDUNDANCE = 3
|
||||
ROUTER_SELECTION_JITTER = 1
|
||||
BBR_REGISTRATION_JITTER = 5
|
||||
SED_POLL_PERIOD = 2000 # 2s
|
||||
MED_TIMEOUT = 20 # 20s
|
||||
|
||||
DUA_IID_MANUAL1 = '4444333322221111'
|
||||
DUA_IID_MANUAL2 = '5555444433332222'
|
||||
|
||||
TEST_PREFIX1 = '2001:0:0:1::/64'
|
||||
TEST_PREFIX2 = '2001:0:0:2::/64'
|
||||
TEST_PREFIX3 = '2001:0:0:3::/64'
|
||||
"""
|
||||
Topology
|
||||
|
||||
|
||||
SED_1_2
|
||||
|
|
||||
|
|
||||
ROUTER_1_1 MED_1_2
|
||||
| |
|
||||
| |
|
||||
BBR_1 (LEADER) --- ROUTER_1_2
|
||||
|
||||
|
||||
1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
|
||||
Prefix without `P_slaac`.
|
||||
|
||||
2) Bring up ROUTER_1_1, no DUA was added due to that `P_slaac` flag is not set.
|
||||
|
||||
3) Bring up ROUTER_1_2, verify that it has DUA generated.
|
||||
|
||||
4) Bring up MED_1_2 with DUA_IID_MANUAL1 set in advance, verify
|
||||
a) DUA_IID_MANUAL1 is registered in Address Registration TLV via Child Update Request.
|
||||
b) Remove DUA_IID_MANUAL1, a new DUA generated via SLAAC would be registered in Address
|
||||
Registration TLV via Child Update Request.
|
||||
c) Set DUA_IID_MANUAL2 which should override the generated one and be registered in Address
|
||||
Registration TLV via Child Update Request.
|
||||
d) Remove DUA_IID_MANUAL2, a new DUA generated via SLAAC, the same as in above b) would
|
||||
be registered in Address Registration TLV via Child Update Request.
|
||||
|
||||
5) Change BBR_1 from config.DOMAIN_PREFIX to config.DOMAIN_PRFIX_ALTER. Verify that MED_1_2
|
||||
generate a new Interface Identifier different from the one generated in 4d) due to the
|
||||
Domain Prefix change.
|
||||
|
||||
6) Recover config.Domain_Prefix on BBR_1. Verify that MED_1_2 generates and registers the same
|
||||
DUA as in step 4b).
|
||||
|
||||
7) Configure ROUTER_1_1 as Border Router with 3 SLAAC prefixes, verify MED_1_2 would register
|
||||
its DUA in Address Registration TLV.
|
||||
|
||||
8) Bring up SED_1_2, verify it generates one DUA, and registers it to its parent, though the parent
|
||||
is a Thread 1.1 device.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class TestDomainUnicastAddress(thread_cert.TestCase):
|
||||
topology = {
|
||||
BBR_1: {
|
||||
'version': '1.2',
|
||||
'whitelist': [ROUTER_1_1, ROUTER_1_2],
|
||||
'is_bbr': True
|
||||
},
|
||||
ROUTER_1_1: {
|
||||
'version': '1.1',
|
||||
'whitelist': [BBR_1, SED_1_2]
|
||||
},
|
||||
ROUTER_1_2: {
|
||||
'version': '1.2',
|
||||
'whitelist': [BBR_1, MED_1_2]
|
||||
},
|
||||
MED_1_2: {
|
||||
'mode': 'rsn',
|
||||
'version': '1.2',
|
||||
'whitelist': [ROUTER_1_2],
|
||||
},
|
||||
SED_1_2: {
|
||||
'mode': 'sn',
|
||||
'version': '1.2',
|
||||
'whitelist': [ROUTER_1_1],
|
||||
},
|
||||
}
|
||||
"""All nodes are created with default configurations"""
|
||||
|
||||
def __get_iid(self, address):
|
||||
''' Get the interface identifier of an IPv6 address.
|
||||
|
||||
Args:
|
||||
address (string): An IPv6 address;
|
||||
'''
|
||||
return ''.join(ipaddress.ip_address(address).exploded.split(':')[4:])
|
||||
|
||||
def __check_dua_registration(self, node, iid, dp_cid):
|
||||
''' Check whether or not the specified domain unicast address is registered in Address
|
||||
Registraion TLV.
|
||||
|
||||
Args:
|
||||
node (int) : The device id
|
||||
iid (string): The Interface Identifier
|
||||
dp_cid (int): The context id of the domain prefix.
|
||||
'''
|
||||
|
||||
messages = self.simulator.get_messages_sent_by(node)
|
||||
msg = messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST)
|
||||
command.check_compressed_address_registration_tlv(msg,
|
||||
dp_cid,
|
||||
iid,
|
||||
cid_present_once=True)
|
||||
|
||||
def test(self):
|
||||
# starting context id
|
||||
context_id = 1
|
||||
|
||||
# 1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
|
||||
# Prefix without `P_slaac`.
|
||||
self.nodes[BBR_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
|
||||
self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
|
||||
self.nodes[BBR_1].set_backbone_router(seqno=1)
|
||||
self.nodes[BBR_1].start()
|
||||
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
|
||||
self.simulator.go(WAIT_TIME)
|
||||
self.assertEqual(self.nodes[BBR_1].get_state(), 'leader')
|
||||
self.nodes[BBR_1].enable_backbone_router()
|
||||
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(),
|
||||
'Primary')
|
||||
assert self.nodes[BBR_1].has_ipmaddr(config.ALL_NETWORK_BBRS_ADDRESS)
|
||||
assert not self.nodes[BBR_1].has_ipmaddr(config.ALL_DOMAIN_BBRS_ADDRESS)
|
||||
|
||||
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD')
|
||||
WAIT_TIME = WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
assert self.nodes[BBR_1].has_ipmaddr(config.ALL_DOMAIN_BBRS_ADDRESS)
|
||||
|
||||
self.simulator.set_lowpan_context(context_id, config.DOMAIN_PREFIX)
|
||||
domain_prefix_cid = context_id
|
||||
|
||||
# 2) Bring up ROUTER_1_1, no DUA was added due to that `P_slaac` flag is not set.
|
||||
self.nodes[ROUTER_1_1].set_router_selection_jitter(
|
||||
ROUTER_SELECTION_JITTER)
|
||||
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
|
||||
self.nodes[ROUTER_1_1].start()
|
||||
self.simulator.go(WAIT_TIME)
|
||||
self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
|
||||
dua = self.nodes[ROUTER_1_1].get_addr(config.DOMAIN_PREFIX)
|
||||
assert not dua, 'Error: Unexpected DUA ({})'.format(dua)
|
||||
|
||||
# 3) Bring up ROUTER_1_2, verify that it has DUA generated.
|
||||
self.nodes[ROUTER_1_2].set_router_selection_jitter(
|
||||
ROUTER_SELECTION_JITTER)
|
||||
self.nodes[ROUTER_1_2].start()
|
||||
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
|
||||
self.simulator.go(WAIT_TIME)
|
||||
self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
|
||||
dua = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
assert dua, 'Error: Expected DUA not found'
|
||||
self.assertTrue(self.nodes[BBR_1].ping(dua))
|
||||
|
||||
# 4) Bring up MED_1_2 with DUA_IID_MANUAL1 set in advance
|
||||
self.nodes[MED_1_2].set_dua_iid(DUA_IID_MANUAL1)
|
||||
self.nodes[MED_1_2].set_timeout(MED_TIMEOUT)
|
||||
self.nodes[MED_1_2].start()
|
||||
WAIT_TIME = WAIT_ATTACH
|
||||
self.simulator.go(WAIT_TIME)
|
||||
self.assertEqual(self.nodes[MED_1_2].get_state(), 'child')
|
||||
|
||||
# 4a) DUA_IID_MANUAL1 is registered in Address Registration TLV via Child Update Request.
|
||||
self.__check_dua_registration(MED_1_2, DUA_IID_MANUAL1,
|
||||
domain_prefix_cid)
|
||||
|
||||
# 4b) Remove DUA_IID_MANUAL1, a new DUA generated via SLAAC would be registered in Address
|
||||
# Registration TLV via Child Update Request.
|
||||
|
||||
# Flush relative message queues.
|
||||
messages = self.simulator.get_messages_sent_by(MED_1_2)
|
||||
|
||||
self.nodes[MED_1_2].clear_dua_iid()
|
||||
WAIT_TIME = MED_TIMEOUT + WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
|
||||
med_1_2_dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
assert med_1_2_dua, 'Error: Expected DUA not found'
|
||||
|
||||
med_1_2_dua_iid = self.__get_iid(med_1_2_dua)
|
||||
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid,
|
||||
domain_prefix_cid)
|
||||
|
||||
# 4c) Set DUA_IID_MANUAL2 which should override the generated one and be registered in Address
|
||||
# Registration TLV via Child Update Request.
|
||||
|
||||
# Flush relative message queues.
|
||||
messages = self.simulator.get_messages_sent_by(MED_1_2)
|
||||
self.nodes[MED_1_2].set_dua_iid(DUA_IID_MANUAL2)
|
||||
WAIT_TIME = WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
|
||||
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
|
||||
self.__check_dua_registration(MED_1_2, DUA_IID_MANUAL2,
|
||||
domain_prefix_cid)
|
||||
|
||||
# 4d) Remove DUA_IID_MANUAL2, a new DUA generated via SLAAC, the same as in above b) would
|
||||
# be registered in Address Registration TLV via Child Update Request.
|
||||
|
||||
# Flush relative message queues.
|
||||
messages = self.simulator.get_messages_sent_by(MED_1_2)
|
||||
self.nodes[MED_1_2].clear_dua_iid()
|
||||
WAIT_TIME = WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
assert ipaddress.ip_address(dua) == ipaddress.ip_address(
|
||||
med_1_2_dua), 'Error: Expected SLAAC DUA not generated'
|
||||
assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
|
||||
dua), 'Error: Expected same SLAAC DUA not generated'
|
||||
|
||||
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid,
|
||||
domain_prefix_cid)
|
||||
|
||||
# 5) Change BBR_1 from config.DOMAIN_PREFIX to config.DOMAIN_PRFIX_ALTER. Verify that MED_1_2
|
||||
# generates a new Interface Identifier different from the one generated in 4d) due to the
|
||||
# Domain Prefix change.
|
||||
context_id += 1
|
||||
self.simulator.set_lowpan_context(context_id,
|
||||
config.DOMAIN_PREFIX_ALTER)
|
||||
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX_ALTER)
|
||||
WAIT_TIME = WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
|
||||
med_1_2_dua2 = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX_ALTER)
|
||||
med_1_2_dua2_iid = self.__get_iid(med_1_2_dua2)
|
||||
self.__check_dua_registration(MED_1_2, med_1_2_dua2_iid, context_id)
|
||||
|
||||
#6) Recover config.Domain_Prefix on BBR_1. Verify that MED_1_2 generates and registers the same
|
||||
# DUA as in step 4b).
|
||||
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX)
|
||||
WAIT_TIME = WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
assert dua, 'Error: Expected DUA not found'
|
||||
assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
|
||||
dua), 'Error: Expected same SLAAC DUA not generated'
|
||||
|
||||
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid,
|
||||
domain_prefix_cid)
|
||||
|
||||
#7) Configure ROUTER_1_1 as Border Router with 3 SLAAC prefixes, verify MED_1_2 would register
|
||||
# its DUA in Address Registration TLV.
|
||||
|
||||
# Flush relative message queues.
|
||||
messages = self.simulator.get_messages_sent_by(MED_1_2)
|
||||
|
||||
context_id += 1
|
||||
self.simulator.set_lowpan_context(context_id, TEST_PREFIX1)
|
||||
self.nodes[ROUTER_1_1].add_prefix(TEST_PREFIX1)
|
||||
|
||||
context_id += 1
|
||||
self.simulator.set_lowpan_context(context_id, TEST_PREFIX2)
|
||||
self.nodes[ROUTER_1_1].add_prefix(TEST_PREFIX2)
|
||||
context_id += 1
|
||||
self.simulator.set_lowpan_context(context_id, TEST_PREFIX3)
|
||||
self.nodes[ROUTER_1_1].add_prefix(TEST_PREFIX3)
|
||||
self.nodes[ROUTER_1_1].register_netdata()
|
||||
|
||||
WAIT_TIME = WAIT_REDUNDANCE
|
||||
self.simulator.go(WAIT_TIME)
|
||||
|
||||
WAIT_TIME = MED_TIMEOUT
|
||||
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
assert dua, 'Error: Expected DUA not found'
|
||||
assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
|
||||
dua), 'Error: Expected same SLAAC DUA not generated'
|
||||
|
||||
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid,
|
||||
domain_prefix_cid)
|
||||
|
||||
#8) Bring up SED_1_2, verify that it generates one DUA, and registers it to its parent, though the parent
|
||||
# is a Thread 1.1 device.
|
||||
self.nodes[SED_1_2].set_pollperiod(SED_POLL_PERIOD)
|
||||
self.nodes[SED_1_2].start()
|
||||
WAIT_TIME = WAIT_ATTACH
|
||||
self.simulator.go(WAIT_TIME)
|
||||
|
||||
dua = self.nodes[SED_1_2].get_addr(config.DOMAIN_PREFIX)
|
||||
assert dua, 'Error: Expected DUA not found'
|
||||
|
||||
dua_iid = self.__get_iid(dua)
|
||||
self.__check_dua_registration(SED_1_2, dua_iid, domain_prefix_cid)
|
||||
|
||||
self.assertTrue(self.nodes[BBR_1].ping(dua))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user