1#!/usr/bin/env python3 2# 3# Copyright (c) 2018, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29 30import copy 31import unittest 32 33import command 34import config 35import mle 36import thread_cert 37from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV 38from pktverify.packet_verifier import PacketVerifier 39from pktverify.null_field import nullField 40 41DUT_LEADER = 1 42DUT_ROUTER1 = 2 43 44# Test Purpose and Description: 45# ----------------------------- 46# The purpose of this test case is to show that when the Leader is rebooted for a time period shorter than the leader timeout, it does not trigger network partitioning and remains the leader when it reattaches to the network. 47# 48# Test Topology: 49# ------------- 50# Leader 51# | 52# Router 53# 54# DUT Types: 55# ---------- 56# Leader 57# Router 58 59 60class Cert_5_5_1_LeaderReboot(thread_cert.TestCase): 61 #USE_MESSAGE_FACTORY = False 62 63 TOPOLOGY = { 64 DUT_LEADER: { 65 'name': 'LEADER', 66 'mode': 'rdn', 67 'allowlist': [DUT_ROUTER1] 68 }, 69 DUT_ROUTER1: { 70 'name': 'ROUTER', 71 'mode': 'rdn', 72 'allowlist': [DUT_LEADER] 73 }, 74 } 75 76 def _setUpLeader(self): 77 self.nodes[DUT_LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64()) 78 self.nodes[DUT_LEADER].enable_allowlist() 79 80 def test(self): 81 self.nodes[DUT_LEADER].start() 82 self.simulator.go(config.LEADER_STARTUP_DELAY) 83 self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader') 84 85 self.nodes[DUT_ROUTER1].start() 86 self.simulator.go(config.ROUTER_STARTUP_DELAY) 87 self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router') 88 89 leader_rloc = self.nodes[DUT_LEADER].get_ip6_address(config.ADDRESS_TYPE.RLOC) 90 91 leader_rloc16 = self.nodes[DUT_LEADER].get_addr16() 92 self.nodes[DUT_LEADER].reset() 93 self.assertFalse(self.nodes[DUT_ROUTER1].ping(leader_rloc)) 94 self._setUpLeader() 95 96 self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) 97 98 self.nodes[DUT_LEADER].start() 99 100 self.simulator.go(config.LEADER_STARTUP_DELAY) 101 self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader') 102 self.assertEqual(self.nodes[DUT_LEADER].get_addr16(), leader_rloc16) 103 104 router1_link_local_address = self.nodes[DUT_ROUTER1].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL) 105 self.assertTrue(self.nodes[DUT_LEADER].ping(router1_link_local_address)) 106 107 def verify(self, pv): 108 pkts = pv.pkts 109 pv.summary.show() 110 111 LEADER = pv.vars['LEADER'] 112 ROUTER = pv.vars['ROUTER'] 113 114 # Step 1: Verify topology is formed correctly. 115 pv.verify_attached('ROUTER', 'LEADER') 116 117 # Step 2: The DUT MUST send properly formatted MLE Advertisements with 118 # an IP Hop Limit of 255 to the Link-Local All Nodes multicast 119 # address (FF02::1). 120 # The following TLVs MUST be present in the MLE Advertisements: 121 # - Leader Data TLV 122 # - Route64 TLV 123 # - Source Address TLV 124 with pkts.save_index(): 125 pkts.filter_wpan_src64(LEADER).\ 126 filter_mle_advertisement('Leader').\ 127 must_next() 128 pkts.filter_wpan_src64(ROUTER).\ 129 filter_mle_advertisement('Router').\ 130 must_next() 131 132 pkts.filter_ping_request().\ 133 filter_wpan_src64(ROUTER).\ 134 must_next() 135 lstart = pkts.index 136 137 # Step 4: The Leader MUST send a multicast Link Request 138 # The following TLVs MUST be present in the Link Request: 139 # - Challenge TLV 140 # - Version TLV 141 # - TLV Request TLV: Address16 TLV, Route64 TLV 142 pkts.filter_wpan_src64(LEADER).\ 143 filter_LLARMA().\ 144 filter_mle_cmd(MLE_LINK_REQUEST).\ 145 filter(lambda p: { 146 CHALLENGE_TLV, 147 VERSION_TLV, 148 TLV_REQUEST_TLV, 149 ADDRESS16_TLV, 150 ROUTE64_TLV 151 } <= set(p.mle.tlv.type) and\ 152 p.mle.tlv.addr16 is nullField and\ 153 p.mle.tlv.route64.id_mask is nullField 154 ).\ 155 must_next() 156 lend = pkts.index 157 158 # Step 3: Reset Leader. The Leader MUST stop sending MLE advertisements. 159 # The Leader reboot time MUST be less than Leader Timeout value 160 pkts.range(lstart, lend).\ 161 filter_wpan_src64(LEADER).\ 162 filter_mle_advertisement('Leader').\ 163 must_not_next() 164 165 # Step 5: Router MUST reply with a Link Accept 166 # The following TLVs MUST be present in the Link Accept: 167 # - Leader Data TLV 168 # - Link-layer Frame Counter TLV 169 # - Response TLV 170 # - Source Address TLV 171 # - Address16 TLV 172 # - Route64 TLV 173 # - Version TLV 174 # - Challenge TLV (situational) 175 # - MLE Frame Counter TLV (optional) 176 # The Challenge TLV MUST be included 177 # if the response is an Accept and Request message. 178 _pkt = pkts.filter_wpan_src64(ROUTER).\ 179 filter_wpan_dst64(LEADER).\ 180 filter_mle_cmd2(MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST).\ 181 filter(lambda p: { 182 LEADER_DATA_TLV, 183 LINK_LAYER_FRAME_COUNTER_TLV, 184 RESPONSE_TLV, 185 SOURCE_ADDRESS_TLV, 186 ADDRESS16_TLV, 187 ROUTE64_TLV, 188 VERSION_TLV 189 } <= set(p.mle.tlv.type) and\ 190 p.mle.tlv.addr16 is not nullField and\ 191 p.mle.tlv.route64.id_mask is not nullField 192 ).\ 193 must_next() 194 if _pkt.mle.cmd == MLE_LINK_ACCEPT_AND_REQUEST: 195 _pkt.must_verify(lambda p: {CHALLENGE_TLV} <= set(p.mle.tlv.type)) 196 197 # Step 7: Router_1 MUST respond with an ICMPv6 Echo Reply 198 _pkt = pkts.filter_ping_request().\ 199 filter_wpan_src64(LEADER).\ 200 filter_wpan_dst64(ROUTER).\ 201 must_next() 202 lend2 = pkts.index 203 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 204 filter_wpan_src64(ROUTER).\ 205 filter_wpan_dst64(LEADER).\ 206 must_next() 207 208 # Step 6: The Leader MUST NOT send a Parent Request after it is re-enabled. 209 pkts.range(lend, lend2).\ 210 filter_wpan_src64(LEADER).\ 211 filter_mle_cmd(MLE_PARENT_REQUEST).\ 212 must_not_next() 213 214 215if __name__ == '__main__': 216 unittest.main() 217