[mle] 1.2 MED registers MA (scope > 3) to its parent via AR TLV (#4837)

This commit is contained in:
Rongli Sun
2020-04-24 10:17:55 -07:00
committed by GitHub
parent a13d8b958f
commit 60be2be68a
5 changed files with 367 additions and 11 deletions
+31 -7
View File
@@ -1392,6 +1392,8 @@ otError Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMod
uint8_t length = 0;
uint8_t counter = 0;
uint16_t startOffset = aMessage.GetLength();
uint8_t iterator = 0; // used to iterate external multicast addresses.
Ip6::Address address;
tlv.SetType(Tlv::kAddressRegistration);
SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv)));
@@ -1434,15 +1436,28 @@ otError Mle::AppendAddressRegistration(Message &aMessage, AddressRegistrationMod
VerifyOrExit(counter < OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER, OT_NOOP);
}
// For sleepy end device, register external multicast addresses to the parent for indirect transmission
if (!IsRxOnWhenIdle())
// Append external multicast addresses. For sleepy end device,
// register all external multicast addresses with the parent for
// indirect transmission. Since Thread 1.2, non-sleepy MED should
// also register external multicast addresses of scope larger than
// realm with a 1.2 or higher parent.
if (!IsRxOnWhenIdle()
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|| !GetParent().IsThreadVersion1p1()
#endif
)
{
uint8_t iterator = 0;
Ip6::Address address;
// append external multicast address
while (Get<ThreadNetif>().GetNextExternalMulticast(iterator, address) == OT_ERROR_NONE)
{
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
// For Thread 1.2 MED, skip multicast address with scope not
// larger than realm local when registering.
if (IsRxOnWhenIdle() && !address.IsMulticastLargerThanRealmLocal())
{
continue;
}
#endif
entry.SetUncompressed();
entry.SetIp6Address(address);
SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength()));
@@ -1571,7 +1586,16 @@ void Mle::HandleStateChanged(otChangedFlags aFlags)
if ((aFlags & (OT_CHANGED_IP6_MULTICAST_SUBSCRIBED | OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED)) != 0)
{
if (IsChild() && !IsFullThreadDevice() && !IsRxOnWhenIdle())
// When multicast subscription changes, SED always notifies its parent as it depends on its
// parent for indirect transmission. Since Thread 1.2, MED MAY also notify its parent of 1.2
// or higher version as it could depend on its parent to perform Multicast Listener Report.
if (IsChild() && !IsFullThreadDevice() &&
(!IsRxOnWhenIdle()
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|| !GetParent().IsThreadVersion1p1()
#endif
))
{
mChildUpdateRequestState = kChildUpdateRequestPending;
ScheduleMessageTransmissionTimer();
+8
View File
@@ -351,6 +351,14 @@ public:
*/
void SetRloc16(uint16_t aRloc16) { mRloc16 = aRloc16; }
/**
* This method indicates whether or not it is a valid Thread 1.1 neighbor.
*
* @returns TRUE if it is a valid Thread 1.1 neighbor, FALSE otherwise.
*
*/
bool IsThreadVersion1p1(void) const { return mState != kStateInvalid && mVersion == OT_THREAD_VERSION_1_1; }
/**
* This method indicates whether Enhanced Keep-Alive is supported or not.
*
+18 -4
View File
@@ -29,6 +29,7 @@
import binascii
import ipaddress
import ipv6
import network_data
import network_layer
@@ -586,11 +587,24 @@ def get_sub_tlv(tlvs, tlv_type):
return sub_tlv
def check_address_registration_tlv(addr_reg_tlv, address_set):
"""Verify all addresses contained in address_set are contained in add_reg_tlv
def check_address_registration_tlv(
command_msg,
full_address,
):
"""Check whether or not a full IPv6 address in AddressRegistrationTlv.
"""
assert all(addr in addr_reg_tlv.addresses for addr in address_set
), 'Some addresses are not included in AddressRegistration TLV'
found = False
addr = ipaddress.ip_address(full_address)
addresses = command_msg.assertMleMessageContainsTlv(
mle.AddressRegistration).addresses
for item in addresses:
if isinstance(item, mle.AddressFull) and ipaddress.ip_address(
item.ipv6_address) == addr:
found = True
break
return found
def assert_contains_tlv(tlvs, check_type, tlv_type):
+5
View File
@@ -704,6 +704,11 @@ class Node:
self.send_command(cmd)
self._expect('Done')
def add_ipmaddr(self, ipmaddr):
cmd = 'ipmaddr add %s' % ipmaddr
self.send_command(cmd)
self._expect('Done')
def get_addrs(self):
self.send_command('ipaddr')
@@ -0,0 +1,305 @@
#!/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 mle
import thread_cert
LEADER_1_2 = 1
MED_1_2 = 2
SED_1_2 = 3
MED_1_1 = 4
SED_1_1 = 5
ROUTER_1_1 = 6
MED_1_2_2 = 7
SED_1_2_2 = 8
WAIT_ATTACH = 5
WAIT_REDUNDANCE = 3
ROUTER_SELECTION_JITTER = 1
MA1_LINKLOCAL = 'ff02::1:2:3:4'
MA2_ADMINSCOPE = 'ff04::1:2:3:4'
"""
Topology
SED_1_2
|
|
MED_1_2 --- LEADER_1_2 --- MED_1_1
| \
| \
SED_1_1 ROUTER_1_1 --- MED_1_2_2
|
|
SED_1_2_2
1) Bring up Leader_1_2.
2) Bring up MED_1_2, which attaches to Thread 1.2 parent, only register MA with scope larger than realm local.
a) add MA1_LINKLOCAL which would NOT be registered in AddressRegistrationTLV of Child Update Request.
b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
3) Bring up SED_1_2, which attaches to Thread 1.2 parent, register any external MA for indirect transmission.
a) add MA1_LINKLOCAL which would be registered in AddressRegistrationTLV of Child Update Request
b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
4) Bring up MED_1_1, which attaches to Thread 1.2 parent, not register any external MA.
a) add MA1_LINKLOCAL which would NOT be registered in AddressRegistrationTLV of Child Update Request.
b) add MA2_ADMINSCOPE which would NOT be registered in AddressRegistrationTLV of Child Update Request.
5) Bring up SED_1_1, which attaches to Thread 1.2 parent, register any external MA for indirect transmission.
a) add MA1_LINKLOCAL which would be registered in AddressRegistrationTLV of Child Update Request
b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
6) Bring up ROUTER_1_1.
7) Bring up MED_1_2_2 which attaches to Thread 1.1 parent, not register any external MA.
a) add MA1_LINKLOCAL which would NOT be registered in AddressRegistrationTLV of Child Update Request
b) add MA2_ADMINSCOPE which would NOT be registered in AddressRegistrationTLV of Child Update Request.
8) Bring up SED_1_2_2 which attaches to Thread 1.1 parent, register any external MA for indirect transmission.
a) add MA1_LINKLOCAL which would be registered in AddressRegistrationTLV of Child Update Request
b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
"""
class TestMulticastRegistration(thread_cert.TestCase):
topology = {
LEADER_1_2: {
'version': '1.2',
'whitelist': [MED_1_2, SED_1_2, MED_1_1, SED_1_1, ROUTER_1_1],
},
MED_1_2: {
'mode': 'rsn',
'version': '1.2',
'whitelist': [LEADER_1_2],
},
SED_1_2: {
'mode': 'sn',
'version': '1.2',
'whitelist': [LEADER_1_2],
},
MED_1_1: {
'mode': 'rsn',
'version': '1.1',
'whitelist': [LEADER_1_2],
},
SED_1_1: {
'mode': 'sn',
'version': '1.1',
'whitelist': [LEADER_1_2],
},
ROUTER_1_1: {
'version': '1.1',
'whitelist': [LEADER_1_2, MED_1_2_2, SED_1_2_2],
},
MED_1_2_2: {
'mode': 'rsn',
'version': '1.2',
'whitelist': [ROUTER_1_1],
},
SED_1_2_2: {
'mode': 'sn',
'version': '1.2',
'whitelist': [ROUTER_1_1],
},
}
"""All nodes are created with default configurations"""
def __check_multicast_registration(self,
node,
multicast_address,
child_update_request_assert=True,
in_address_registration=True):
''' Check whether or not the addition of the multicast address on the specific node
would trigger Child Update Request for multicast address registration via Address
Registraion TLV.
Args:
node (int) : The device id
multicast_address (string): The multicast address
child_update_request_assert (bool): whether or not the addition should trigger Child Update Request
in_address_registration (bool): Whether or not the multicast_address should be registered
'''
# Flush relative message queues.
self.flush_nodes([node])
self.nodes[node].add_ipmaddr(multicast_address)
WAIT_TIME = WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
messages = self.simulator.get_messages_sent_by(node)
msg = messages.next_mle_message(
mle.CommandType.CHILD_UPDATE_REQUEST,
assert_enabled=child_update_request_assert)
if msg:
is_in = command.check_address_registration_tlv(
msg, multicast_address)
if in_address_registration:
assert is_in, 'Error: %s %s in AddressRegistrationTLV %s'.format(
'Expected', multicast_address, ' not found')
else:
assert not is_in, 'Error: %s %s in AddressRegistrationTLV %s'.format(
'Unexpected', multicast_address, '')
def test(self):
# 1) Bring up Leader_1_2.
self.nodes[LEADER_1_2].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[LEADER_1_2].get_state(), 'leader')
# 2) Bring up MED_1_2, which attaches to Thread 1.2 parent, only register MA with scope larger than realm local.
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')
# 2a) add MA1_LINKLOCAL which would NOT be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(MED_1_2,
MA1_LINKLOCAL,
child_update_request_assert=False,
in_address_registration=False)
# 2b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(MED_1_2,
MA2_ADMINSCOPE,
child_update_request_assert=True,
in_address_registration=True)
# 3) Bring up SED_1_2, which attaches to Thread 1.2 parent, register any external MA for indirect transmission.
self.nodes[SED_1_2].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[SED_1_2].get_state(), 'child')
# 3a) add MA1_LINKLOCAL which would be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(SED_1_2,
MA1_LINKLOCAL,
child_update_request_assert=True,
in_address_registration=True)
# 3b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(SED_1_2,
MA2_ADMINSCOPE,
child_update_request_assert=True,
in_address_registration=True)
# 4) Bring up MED_1_1, which attaches to Thread 1.2 parent, not register any external MA.
self.nodes[MED_1_1].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[MED_1_1].get_state(), 'child')
# 4a) add MA1_LINKLOCAL which would NOT be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(MED_1_1,
MA1_LINKLOCAL,
child_update_request_assert=False,
in_address_registration=False)
# 4b) add MA2_ADMINSCOPE which would NOT be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(MED_1_1,
MA2_ADMINSCOPE,
child_update_request_assert=False,
in_address_registration=False)
# 5) Bring up SED_1_1, which attaches to Thread 1.2 parent, register any external MA for indirect transmission.
self.nodes[SED_1_1].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[SED_1_1].get_state(), 'child')
# 5a) add MA1_LINKLOCAL which would be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(SED_1_1,
MA1_LINKLOCAL,
child_update_request_assert=True,
in_address_registration=True)
# 5b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(SED_1_1,
MA2_ADMINSCOPE,
child_update_request_assert=True,
in_address_registration=True)
#6) Bring up ROUTER_1_1.
self.nodes[ROUTER_1_1].set_router_selection_jitter(
ROUTER_SELECTION_JITTER)
self.nodes[ROUTER_1_1].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
# 7) Bring up MED_1_2_2 which attaches to Thread 1.1 parent, not register any external MA.
self.nodes[MED_1_2_2].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[MED_1_2_2].get_state(), 'child')
# 7a) add MA1_LINKLOCAL which would NOT be registered in AddressRegistrationTLV of Child Update Request
self.__check_multicast_registration(MED_1_2_2,
MA1_LINKLOCAL,
child_update_request_assert=False,
in_address_registration=False)
# 7b) add MA2_ADMINSCOPE which would NOT be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(MED_1_2_2,
MA2_ADMINSCOPE,
child_update_request_assert=False,
in_address_registration=False)
# 8) Bring up SED_1_2_2 which attaches to Thread 1.1 parent, register any external MA for indirect transmission.
self.nodes[SED_1_2_2].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[SED_1_2_2].get_state(), 'child')
# 8a) add MA1_LINKLOCAL which would be registered in AddressRegistrationTLV of Child Update Request
self.__check_multicast_registration(SED_1_2_2,
MA1_LINKLOCAL,
child_update_request_assert=True,
in_address_registration=True)
# 8b) add MA2_ADMINSCOPE which would be registered in AddressRegistrationTLV of Child Update Request.
self.__check_multicast_registration(SED_1_2_2,
MA2_ADMINSCOPE,
child_update_request_assert=True,
in_address_registration=True)
if __name__ == '__main__':
unittest.main()