[dua] support to specify iid for domain unicast address (#4900)

This commit is contained in:
Rongli Sun
2020-06-09 10:38:13 -07:00
committed by GitHub
parent b28d7c4a45
commit b51d9100e6
15 changed files with 779 additions and 15 deletions
+1 -1
View File
@@ -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
+18
View File
@@ -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
*
+31 -2
View File
@@ -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.
*
+29
View File
@@ -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.
+48
View File
@@ -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[])
+5
View File
@@ -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
+34
View File
@@ -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)
+24
View File
@@ -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);
+80 -2
View File
@@ -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.
+70 -6
View File
@@ -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;
}
+38
View File
@@ -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;
};
+35
View File
@@ -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.
"""
+5 -1
View File
@@ -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,
+22 -3
View File
@@ -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()