1#!/usr/bin/env python3 2# 3# Copyright (c) 2016, 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 unittest 31 32import config 33import thread_cert 34from pktverify.consts import MLE_ADVERTISEMENT, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE 35from pktverify.packet_verifier import PacketVerifier 36 37DUT_LEADER = 1 38ROUTER1 = 2 39ROUTER2 = 3 40 41# Test Purpose and Description: 42# ----------------------------- 43# The purpose of this test case is to verify that the router ID mask is managed 44# correctly, as the connectivity to a router or group of routers is lost and / or 45# a new router is added to network. 46# 47# Test Topology: 48# ------------- 49# Leader(DUT) 50# | 51# Router_1 52# | 53# Router_2 54# 55# DUT Types: 56# ---------- 57# Leader 58 59 60class Cert_5_3_6_RouterIdMask(thread_cert.TestCase): 61 USE_MESSAGE_FACTORY = False 62 63 TOPOLOGY = { 64 DUT_LEADER: { 65 'name': 'LEADER', 66 'mode': 'rdn', 67 'allowlist': [ROUTER1] 68 }, 69 ROUTER1: { 70 'name': 'ROUTER_1', 71 'mode': 'rdn', 72 'allowlist': [DUT_LEADER, ROUTER2] 73 }, 74 ROUTER2: { 75 'name': 'ROUTER_2', 76 'mode': 'rdn', 77 'allowlist': [ROUTER1] 78 }, 79 } 80 81 def _setUpRouter2(self): 82 self.nodes[ROUTER2].add_allowlist(self.nodes[ROUTER1].get_addr64()) 83 self.nodes[ROUTER2].enable_allowlist() 84 self.nodes[ROUTER2].set_router_selection_jitter(1) 85 86 def test(self): 87 # 1 88 self.nodes[DUT_LEADER].start() 89 self.simulator.go(config.LEADER_STARTUP_DELAY) 90 self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader') 91 92 self.nodes[ROUTER1].start() 93 self.simulator.go(config.ROUTER_STARTUP_DELAY) 94 self.assertEqual(self.nodes[ROUTER1].get_state(), 'router') 95 96 self.nodes[ROUTER2].start() 97 self.simulator.go(config.ROUTER_STARTUP_DELAY) 98 self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') 99 100 self.collect_rloc16s() 101 102 # Wait DUT_LEADER to establish routing to ROUTER2 via ROUTER1's MLE 103 # advertisement. 104 self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) 105 106 # 2 107 self.nodes[ROUTER2].reset() 108 self._setUpRouter2() 109 110 # 3 & 4 111 112 self.simulator.go(720) 113 114 # 5 115 116 self.nodes[ROUTER2].start() 117 self.simulator.go(config.ROUTER_STARTUP_DELAY) 118 self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') 119 120 self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) 121 122 # 6 123 self.nodes[ROUTER1].reset() 124 self.nodes[ROUTER2].reset() 125 126 self.simulator.go(720) 127 128 def verify(self, pv): 129 pkts = pv.pkts 130 pv.summary.show() 131 132 LEADER = pv.vars['LEADER'] 133 ROUTER_1 = pv.vars['ROUTER_1'] 134 ROUTER_2 = pv.vars['ROUTER_2'] 135 136 leader_rid = pv.vars['LEADER_RLOC16'] >> 10 137 router_1_rid = pv.vars['ROUTER_1_RLOC16'] >> 10 138 router_2_rid = pv.vars['ROUTER_2_RLOC16'] >> 10 139 140 # Step 1: Ensure topology is formed correctly 141 pv.verify_attached('ROUTER_1', 'LEADER') 142 pv.verify_attached('ROUTER_2', 'ROUTER_1') 143 144 pkts.filter_wpan_src64(LEADER).\ 145 filter_LLANMA().\ 146 filter_mle_cmd(MLE_ADVERTISEMENT).\ 147 filter(lambda p: 148 {1,2,1} == set(p.mle.tlv.route64.cost) and\ 149 {leader_rid, router_1_rid, router_2_rid} == 150 p.mle.tlv.route64.id_mask 151 ).\ 152 must_next() 153 154 # Step 4: The DUT’s routing cost to Router_2 MUST count to infinity 155 # The DUT MUST remove Router_2 ID from its ID set 156 # Verify route data has settled 157 _pkt = pkts.filter_wpan_src64(LEADER).\ 158 filter_LLANMA().\ 159 filter_mle_cmd(MLE_ADVERTISEMENT).\ 160 filter(lambda p: {1,0,1} == set(p.mle.tlv.route64.cost)).\ 161 must_next() 162 pkts.filter_wpan_src64(LEADER).\ 163 filter_LLANMA().\ 164 filter_mle_cmd(MLE_ADVERTISEMENT).\ 165 filter(lambda p: 166 {1,1} == set(p.mle.tlv.route64.cost) and\ 167 {leader_rid, router_1_rid} == 168 p.mle.tlv.route64.id_mask 169 ).\ 170 must_next() 171 172 # Step 5: Re-attach Router_2 to Router_1. 173 # The DUT MUST reset the MLE Advertisement trickle timer and 174 # send an Advertisement 175 pv.verify_attached('ROUTER_2', 'ROUTER_1') 176 # check trickle timer between the successive advertisements 177 with pkts.save_index(): 178 _pkt = pkts.filter_wpan_src64(LEADER).\ 179 filter_LLANMA().\ 180 filter_mle_cmd(MLE_ADVERTISEMENT).\ 181 must_next() 182 # Time between MLE Advertisements after reset can be up to 3.5 seconds 183 # 0.0s: Reset Interval, reset interval to 1 seconds, random interval at 0.5 second 184 # 0.5s: Transmit MLE Advertisement 185 # 1.0s: Set interval to 2 seconds, random interval at 2 seconds 186 # 3.0s: Receive MLE Advertisement, reset interval to 1 second, random interval at 1.0 second 187 # 4.0s: Transmit MLE Advertisement 188 pkts.filter_wpan_src64(LEADER).\ 189 filter_LLANMA().\ 190 filter_mle_cmd(MLE_ADVERTISEMENT).\ 191 filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\ 192 must_next() 193 # check router cost before and after the re-attach 194 pkts.filter_wpan_src64(LEADER).\ 195 filter_LLANMA().\ 196 filter_mle_cmd(MLE_ADVERTISEMENT).\ 197 filter(lambda p: {1,0,1} == set(p.mle.tlv.route64.cost)).\ 198 must_next() 199 pkts.filter_wpan_src64(LEADER).\ 200 filter_LLANMA().\ 201 filter_mle_cmd(MLE_ADVERTISEMENT).\ 202 filter(lambda p: {1,2,1} == set(p.mle.tlv.route64.cost) and\ 203 {leader_rid, router_1_rid, router_2_rid} == 204 p.mle.tlv.route64.id_mask 205 ).\ 206 must_next() 207 208 # Step 6: The DUT’s routing cost to Router_1 MUST go directly to 209 # infinity as there is no multi-hop cost for Router_1 210 # The DUT MUST remove Router_1 & Router_2 IDs from its ID set 211 pkts.filter_wpan_src64(LEADER).\ 212 filter_LLANMA().\ 213 filter_mle_cmd(MLE_ADVERTISEMENT).\ 214 filter(lambda p: {0, 0, 1} == set(p.mle.tlv.route64.cost)).\ 215 must_next() 216 pkts.filter_wpan_src64(LEADER).\ 217 filter_LLANMA().\ 218 filter_mle_cmd(MLE_ADVERTISEMENT).\ 219 filter(lambda p: {0, 1} == set(p.mle.tlv.route64.cost)).\ 220 must_next() 221 pkts.filter_wpan_src64(LEADER).\ 222 filter_LLANMA().\ 223 filter_mle_cmd(MLE_ADVERTISEMENT).\ 224 filter(lambda p: 225 [1] == p.mle.tlv.route64.cost and\ 226 {leader_rid} == 227 p.mle.tlv.route64.id_mask 228 ).\ 229 must_next() 230 231 232if __name__ == '__main__': 233 unittest.main() 234