[mle] send MLE Avd on router promo & accept former child as router (#8307)

This commit updates `MleRouter to send an immediate MLE Advertisement
on promotion to `router` role and assignment of new Router ID. This
helps inform our former parent of our newly allocated Router ID and
cause it to reset it own advertisement trickle timer. This can help
speed up the dissemination of the new Router ID to other routers. It
can also help with quicker link establishment with our former parent
and other routers.

It also removes tx of multicast Link Request (including its delay
mechanism) upon promotion to `router` role.

It also updates `Mle` so if we receive an MLE advertisement from a
former child (which is recently get promoted to router) to accept it
immediately and copy the info from `Child` entry to `Router`.

Finally this commit updates the following test-cases which are
impacted by the changes above (e.g., skipping the checks for
multicast Link Request):

- `v1_2_router_5_1_1`
- `Cert_5_1_01_RouterAttach`
- `Cert_5_2_04_REEDUpgrade`
- `Cert_5_5_02_LeaderReboot`
- `Cert_5_5_05_SplitMergeREED`
This commit is contained in:
Abtin Keshavarzian
2023-03-06 21:15:32 -08:00
committed by GitHub
parent 6e975c5875
commit d2d9aff012
10 changed files with 41 additions and 172 deletions
+21 -33
View File
@@ -86,7 +86,6 @@ MleRouter::MleRouter(Instance &aInstance)
, mRouterSelectionJitter(kRouterSelectionJitter) , mRouterSelectionJitter(kRouterSelectionJitter)
, mRouterSelectionJitterTimeout(0) , mRouterSelectionJitterTimeout(0)
, mChildRouterLinks(kChildRouterLinks) , mChildRouterLinks(kChildRouterLinks)
, mLinkRequestDelay(0)
, mParentPriority(kParentPriorityUnspecified) , mParentPriority(kParentPriorityUnspecified)
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
, mBackboneRouterRegistrationDelay(0) , mBackboneRouterRegistrationDelay(0)
@@ -196,7 +195,6 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus)
Get<MeshForwarder>().SetRxOnWhenIdle(true); Get<MeshForwarder>().SetRxOnWhenIdle(true);
mRouterSelectionJitterTimeout = 0; mRouterSelectionJitterTimeout = 0;
mLinkRequestDelay = 0;
switch (mRole) switch (mRole)
{ {
@@ -290,7 +288,6 @@ void MleRouter::HandleDetachStart(void)
void MleRouter::HandleChildStart(AttachMode aMode) void MleRouter::HandleChildStart(AttachMode aMode)
{ {
mLinkRequestDelay = 0;
mAddressSolicitRejected = false; mAddressSolicitRejected = false;
mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
@@ -478,11 +475,6 @@ void MleRouter::SendAdvertisement(void)
// children to detach. // children to detach.
VerifyOrExit(!mAddressSolicitPending); VerifyOrExit(!mAddressSolicitPending);
// Suppress MLE Advertisements before sending multicast Link Request.
//
// Before sending the multicast Link Request message, no links have been established to neighboring routers.
VerifyOrExit(mLinkRequestDelay == 0);
VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs); VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendSourceAddressTlv());
SuccessOrExit(error = message->AppendLeaderDataTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv());
@@ -522,7 +514,7 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor)
TxMessage *message = nullptr; TxMessage *message = nullptr;
Ip6::Address destination; Ip6::Address destination;
VerifyOrExit(mLinkRequestDelay == 0 && mChallengeTimeout == 0); VerifyOrExit(mChallengeTimeout == 0);
destination.Clear(); destination.Clear();
@@ -1296,12 +1288,6 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
if (IsRouter()) if (IsRouter())
{ {
if (mLinkRequestDelay > 0 && routeTlv.IsRouterIdSet(mRouterId))
{
mLinkRequestDelay = 0;
IgnoreError(SendLinkRequest(nullptr));
}
if (ShouldDowngrade(routerId, routeTlv)) if (ShouldDowngrade(routerId, routeTlv))
{ {
mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
@@ -1311,6 +1297,19 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
router = mRouterTable.FindRouterById(routerId); router = mRouterTable.FindRouterById(routerId);
VerifyOrExit(router != nullptr); VerifyOrExit(router != nullptr);
if (!router->IsStateValid() && aRxInfo.IsNeighborStateValid() && Get<ChildTable>().Contains(*aRxInfo.mNeighbor))
{
// The Adv is from a former child that is now acting as a router,
// we copy the info from child entry and update the RLOC16.
*static_cast<Neighbor *>(router) = *aRxInfo.mNeighbor;
router->SetRloc16(Rloc16FromRouterId(routerId));
router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle |
DeviceMode::kModeFullNetworkData));
mNeighborTable.Signal(NeighborTable::kRouterAdded, *router);
}
// Send unicast link request if no link to router and no unicast/multicast link request in progress // Send unicast link request if no link to router and no unicast/multicast link request in progress
if (!router->IsStateValid() && !router->IsStateLinkRequest() && (mChallengeTimeout == 0) && if (!router->IsStateValid() && !router->IsStateLinkRequest() && (mChallengeTimeout == 0) &&
(linkMargin >= kLinkRequestMinMargin)) (linkMargin >= kLinkRequestMinMargin))
@@ -1484,11 +1483,6 @@ void MleRouter::HandleTimeTick(void)
VerifyOrExit(IsFullThreadDevice(), Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMleRouter)); VerifyOrExit(IsFullThreadDevice(), Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMleRouter));
if (mLinkRequestDelay > 0 && --mLinkRequestDelay == 0)
{
IgnoreError(SendLinkRequest(nullptr));
}
if (mChallengeTimeout > 0) if (mChallengeTimeout > 0)
{ {
mChallengeTimeout--; mChallengeTimeout--;
@@ -3427,7 +3421,6 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage,
ThreadRouterMaskTlv routerMaskTlv; ThreadRouterMaskTlv routerMaskTlv;
uint8_t routerId; uint8_t routerId;
Router *router; Router *router;
bool isRouterIdAllocated;
mAddressSolicitPending = false; mAddressSolicitPending = false;
@@ -3459,8 +3452,6 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage,
SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(*aMessage, rloc16)); SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(*aMessage, rloc16));
routerId = RouterIdFromRloc16(rloc16); routerId = RouterIdFromRloc16(rloc16);
isRouterIdAllocated = mRouterTable.IsAllocated(routerId);
SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv)); SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv));
VerifyOrExit(routerMaskTlv.IsValid()); VerifyOrExit(routerMaskTlv.IsValid());
@@ -3503,16 +3494,13 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage,
leader->SetNextHopAndCost(RouterIdFromRloc16(mParent.GetRloc16()), mParent.GetLeaderCost()); leader->SetNextHopAndCost(RouterIdFromRloc16(mParent.GetRloc16()), mParent.GetLeaderCost());
} }
if (isRouterIdAllocated) // We send an Advertisement to inform our former parent of our
{ // newly allocated Router ID. This will cause the parent to
// send Link Request immediately if Router ID was previously allocated // reset its advertisement trickle timer which can help speed
IgnoreError(SendLinkRequest(nullptr)); // up the dissemination of the new Router ID to other routers.
} // This can also help with quicker link establishment with our
else // former parent and other routers.
{ SendAdvertisement();
// wait to send Link Request until new Router ID has been disseminated from the Leader
mLinkRequestDelay = kMulticastLinkRequestDelay;
}
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateChildIdRequest)) for (Child &child : Get<ChildTable>().Iterate(Child::kInStateChildIdRequest))
{ {
-2
View File
@@ -668,8 +668,6 @@ private:
uint8_t mChildRouterLinks; uint8_t mChildRouterLinks;
uint8_t mLinkRequestDelay;
int8_t mParentPriority; ///< The assigned parent priority value, -2 means not assigned. int8_t mParentPriority; ///< The assigned parent priority value, -2 means not assigned.
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
uint8_t mBackboneRouterRegistrationDelay; ///< Delay before registering Backbone Router service. uint8_t mBackboneRouterRegistrationDelay; ///< Delay before registering Backbone Router service.
@@ -271,53 +271,8 @@ class Cert_5_1_01_RouterAttach(thread_cert.TestCase):
).\ ).\
must_next() must_next()
# Step 8: Router Sends a Link Request Message. # Steps 8 and 9 are skipped due to change the Link establishment
# The Link Request Message MUST be multicast and contain # process (no multicast MLE Link Request by new router).
# the following TLVs:
# - Challenge TLV
# - Leader Data TLV
# - Source Address TLV
# - Version TLV
# - TLV Request TLV: Link Margin
pkts.filter_wpan_src64(ROUTER).\
filter_LLARMA().\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
LEADER_DATA_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
LINK_MARGIN_TLV
} <= set(p.mle.tlv.type)\
).\
must_next()
# Step 9: Leader sends a Unicast Link Accept and Request Message.
# The Message MUST be unicast to Router
# The Message MUST contain the following TLVs:
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address TLV
# - Version TLV
# - Challenge TLV (optional)
# - MLE Frame Counter TLV (optional)
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_LINK_ACCEPT_AND_REQUEST).\
filter(lambda p: {
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)).\
must_next()
# Step 10: Router is sending properly formatted MLE Advertisements. # Step 10: Router is sending properly formatted MLE Advertisements.
# MLE Advertisements MUST be sent with an IP Hop Limit of # MLE Advertisements MUST be sent with an IP Hop Limit of
@@ -344,28 +344,8 @@ class Cert_5_2_4_REEDUpgrade(thread_cert.TestCase):
must_next() must_next()
# Step 10: REED Sends a Link Request Message. # Step 10: REED Sends a Link Request Message.
# The Link Request Message MUST be multicast and contain # This step is skipped due to change that new router no
# the following TLVs: # longer send multicast Link Request.
# - Challenge TLV
# - Leader Data TLV
# - Source Address TLV
# - TLV Request TLV: Link Margin
# - Version TLV
with pkts.save_index():
pkts.filter_wpan_src64(REED).\
filter_LLARMA().\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
LEADER_DATA_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
LINK_MARGIN_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
# Step 11: The REED MLE Child ID Response MUST be properly # Step 11: The REED MLE Child ID Response MUST be properly
# formatted with MED_1s new 16-bit address. # formatted with MED_1s new 16-bit address.
@@ -190,12 +190,8 @@ class Cert_5_3_6_RouterIdMask(thread_cert.TestCase):
filter_mle_cmd(MLE_ADVERTISEMENT).\ filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\ filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\
must_next() must_next()
# check router cost before and after the re-attach
pkts.filter_wpan_src64(LEADER).\ # check router cost after the re-attach
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {1,0,1} == set(p.mle.tlv.route64.cost)).\
must_next()
pkts.filter_wpan_src64(LEADER).\ pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\ filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\ filter_mle_cmd(MLE_ADVERTISEMENT).\
@@ -176,14 +176,9 @@ class Cert_5_5_2_LeaderReboot(thread_cert.TestCase):
thread_address.tlv.status == 0) thread_address.tlv.status == 0)
#Step 15: Leader Send a Multicast Link Request #Step 15: Leader Send a Multicast Link Request
_lpkts.filter_mle_cmd(MLE_LINK_REQUEST).must_next().must_verify(
lambda p: {VERSION_TLV, TLV_REQUEST_TLV, SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, CHALLENGE_TLV} < set(
p.mle.tlv.type))
#Step 16: Router_1 send a Unicast Link Accept #Step 16: Router_1 send a Unicast Link Accept
_rpkts.filter_mle_cmd(MLE_LINK_ACCEPT_AND_REQUEST).must_next().must_verify(lambda p: { # Steps 15 and 16 are skipped since new router no longer
VERSION_TLV, SOURCE_ADDRESS_TLV, RESPONSE_TLV, MLE_FRAME_COUNTER_TLV, LINK_MARGIN_TLV, LEADER_DATA_TLV # send multicast Link Request.
} < set(p.mle.tlv.type))
#Step 17: Router_1 MUST respond with an ICMPv6 Echo Reply #Step 17: Router_1 MUST respond with an ICMPv6 Echo Reply
_rpkts.filter_ping_request().filter_wpan_dst64(MED).must_next() _rpkts.filter_ping_request().filter_wpan_dst64(MED).must_next()
@@ -197,9 +197,8 @@ class Cert_5_5_5_SplitMergeREED(thread_cert.TestCase):
lambda p: {NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV} <= set(p.coap.tlv.type)) lambda p: {NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV} <= set(p.coap.tlv.type))
# Step 7: DUT send a Multicast Link Request # Step 7: DUT send a Multicast Link Request
pkts.filter_wpan_src64(REED).filter_mle_cmd(MLE_LINK_REQUEST).must_next().must_verify(lambda p: { # Step 7 is skipped since new router no longer sends
VERSION_TLV, TLV_REQUEST_TLV, SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, CHALLENGE_TLV, LINK_MARGIN_TLV # multicast Link Request.
} <= set(p.mle.tlv.type))
# Step 8: DUT send Child ID Response to Router_1 # Step 8: DUT send Child ID Response to Router_1
reed_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next().must_verify(lambda p: p.wpan.dst64 == ROUTER_1) reed_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next().must_verify(lambda p: p.wpan.dst64 == ROUTER_1)
@@ -158,7 +158,7 @@ class Cert_7_1_7_BorderRouterAsLeader(thread_cert.TestCase):
# Wait for Router_2 reattachment and network data propagation # Wait for Router_2 reattachment and network data propagation
# ADVERTISEMENT_I_MAX + DEFAULT_CHILD_TIMEOUT + ATTACH_DELAY + Extra # ADVERTISEMENT_I_MAX + DEFAULT_CHILD_TIMEOUT + ATTACH_DELAY + Extra
self.simulator.go(60) self.simulator.go(120)
self.assertEqual(self.nodes[ROUTER_2].get_state(), 'router') self.assertEqual(self.nodes[ROUTER_2].get_state(), 'router')
self.collect_ipaddrs() self.collect_ipaddrs()
self.collect_rloc16s() self.collect_rloc16s()
@@ -31,7 +31,7 @@ import unittest
import config import config
import thread_cert import thread_cert
from pktverify.consts import MLE_LINK_REQUEST, MLE_LINK_ACCEPT from pktverify.consts import MLE_LINK_REQUEST
from pktverify.packet_verifier import PacketVerifier from pktverify.packet_verifier import PacketVerifier
LEADER = 1 LEADER = 1
@@ -110,30 +110,12 @@ class TestRouterMulticastLinkRequest(thread_cert.TestCase):
self.assertEqual(self.nodes[REED].get_state(), 'router') self.assertEqual(self.nodes[REED].get_state(), 'router')
self.simulator.go(LINK_ESTABLISH_DELAY_THRESHOLD + 3) self.simulator.go(LINK_ESTABLISH_DELAY_THRESHOLD + 3)
def verify(self, pv: PacketVerifier): # Verify that REED has established link with all routers
pkts = pv.pkts reed_table = self.nodes[REED].router_table()
print(pv.vars) reed_id = self.nodes[REED].get_router_id()
pv.summary.show() for router in [self.nodes[ROUTER1], self.nodes[ROUTER2], self.nodes[ROUTER3]]:
self.assertEqual(reed_table[router.get_router_id()]['link'], 1)
REED = pv.vars['REED'] self.assertEqual(router.router_table()[reed_id]['link'], 1)
as_pkt = pkts.filter_wpan_src64(REED).filter_coap_request('/a/as', confirmable=True).must_next()
parent_rloc16 = as_pkt.wpan.dst16
as_ack_pkt = pkts.filter_wpan_src16(parent_rloc16).filter_coap_ack('/a/as').must_next()
become_router_timestamp = as_ack_pkt.sniff_timestamp
# REED has just received `/a/as` and become a Router
# REED should send Multicast Link Request after becoming Router
link_request_pkt = pkts.filter_wpan_src64(REED).filter_mle_cmd(MLE_LINK_REQUEST).must_next()
link_request_pkt.must_verify('ipv6.dst == "ff02::2"')
# REED should send Link Accept to the three Routers
for router in ('ROUTER1', 'ROUTER2', 'ROUTER3'):
with pkts.save_index():
pkt = pkts.filter_wpan_src64(REED).filter_wpan_dst64(
pv.vars[router]).filter_mle_cmd(MLE_LINK_ACCEPT).must_next()
link_establish_delay = pkt.sniff_timestamp - become_router_timestamp
logging.info("Link to %s established in %.3f seconds", router, link_establish_delay)
self.assertLess(link_establish_delay, LINK_ESTABLISH_DELAY_THRESHOLD)
if __name__ == '__main__': if __name__ == '__main__':
+2 -26
View File
@@ -136,31 +136,7 @@ class Router_5_1_01(thread_cert.TestCase):
status_tlv = msg.get_coap_message_tlv(network_layer.Status) status_tlv = msg.get_coap_message_tlv(network_layer.Status)
self.assertEqual(network_layer.StatusValues.SUCCESS, status_tlv.status) self.assertEqual(network_layer.StatusValues.SUCCESS, status_tlv.status)
# 8 - Router_1 sends a multicast Link Request Message # 8 - Router_1 Transmit MLE advertisements
msg = router_messages.next_mle_message(mle.CommandType.LINK_REQUEST)
msg.assertMleMessageContainsTlv(mle.SourceAddress)
msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.Challenge)
msg.assertMleMessageContainsTlv(mle.Version)
msg.assertMleMessageContainsTlv(mle.TlvRequest)
assert msg.get_mle_message_tlv(mle.Version).version >= 3
tlv_request = msg.get_mle_message_tlv(mle.TlvRequest)
self.assertIn(mle.TlvType.LINK_MARGIN, tlv_request.tlvs)
# 9 - Leader sends a Unicast Link Accept
msg = leader_messages.next_mle_message(mle.CommandType.LINK_ACCEPT_AND_REQUEST)
msg.assertMleMessageContainsTlv(mle.SourceAddress)
msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.Response)
msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter)
msg.assertMleMessageContainsTlv(mle.Version)
msg.assertMleMessageContainsTlv(mle.LinkMargin)
msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter)
msg.assertMleMessageContainsOptionalTlv(mle.Challenge)
assert msg.get_mle_message_tlv(mle.Version).version >= 3
# 10 - Router_1 Transmit MLE advertisements
msg = router_messages.next_mle_message(mle.CommandType.ADVERTISEMENT) msg = router_messages.next_mle_message(mle.CommandType.ADVERTISEMENT)
msg.assertSentWithHopLimit(255) msg.assertSentWithHopLimit(255)
msg.assertSentToDestinationAddress('ff02::1') msg.assertSentToDestinationAddress('ff02::1')
@@ -168,7 +144,7 @@ class Router_5_1_01(thread_cert.TestCase):
msg.assertMleMessageContainsTlv(mle.LeaderData) msg.assertMleMessageContainsTlv(mle.LeaderData)
msg.assertMleMessageContainsTlv(mle.Route64) msg.assertMleMessageContainsTlv(mle.Route64)
# 11 - Verify connectivity by sending an ICMPv6 Echo Request to the DUT link local address # 9 - Verify connectivity by sending an ICMPv6 Echo Request to the DUT link local address
self.assertTrue(self.nodes[LEADER].ping(self.nodes[ROUTER_1].get_linklocal())) self.assertTrue(self.nodes[LEADER].ping(self.nodes[ROUTER_1].get_linklocal()))
self.assertTrue(self.nodes[ROUTER_1].ping(self.nodes[LEADER].get_linklocal())) self.assertTrue(self.nodes[ROUTER_1].ping(self.nodes[LEADER].get_linklocal()))