1#!/usr/bin/env python3 2# 3# Copyright (c) 2020, 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_DATA_RESPONSE, LEAD_PET_URI, LEAD_KA_URI, MGMT_COMMISSIONER_SET_URI, NM_COMMISSIONER_ID_TLV, NM_COMMISSIONER_SESSION_ID_TLV, NM_STATE_TLV, NM_STEERING_DATA_TLV, NM_BORDER_AGENT_LOCATOR_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, ACTIVE_TIMESTAMP_TLV, SOURCE_ADDRESS_TLV, NWD_COMMISSIONING_DATA_TLV, MESHCOP_ACCEPT, MESHCOP_REJECT 35from pktverify.packet_verifier import PacketVerifier 36 37LEADER = 1 38COMMISSIONER = 2 39FAKE_JOINER = '166e0a0000000999' 40 41# Test Purpose and Description: 42# ----------------------------- 43# The purpose of this test case is to verify that a Commissioner Candidate is able 44# to register itself to the network as a Commissioner, send periodic Commissioner 45# keep-alive messages, update steering data and unregister itself as a Commissioner 46# Leader accepts the Commissioner Candidate as a Commissioner in the network, responds 47# to periodic Commissioner keep-alive messages, propagates Thread Network Data correctly 48# in the network and unregisters the Commissioner on its request 49# 50# Test Topology: 51# ------------- 52# Commissioner 53# | 54# Leader 55# 56# DUT Types: 57# ---------- 58# Leader 59# Commissioner 60 61 62class Cert_8_3_01_CommissionerPetition(thread_cert.TestCase): 63 USE_MESSAGE_FACTORY = False 64 SUPPORT_NCP = False 65 66 TOPOLOGY = { 67 LEADER: { 68 'name': 'LEADER', 69 'networkkey': '00112233445566778899aabbccddeeff', 70 'mode': 'rdn', 71 }, 72 COMMISSIONER: { 73 'name': 'COMMISSIONER', 74 'networkkey': '00112233445566778899aabbccddeeff', 75 'mode': 'rdn', 76 }, 77 } 78 79 def test(self): 80 self.nodes[LEADER].start() 81 self.simulator.go(config.LEADER_STARTUP_DELAY) 82 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 83 self.nodes[COMMISSIONER].start() 84 self.simulator.go(config.ROUTER_STARTUP_DELAY) 85 self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router') 86 87 self.collect_rlocs() 88 self.collect_rloc16s() 89 self.collect_leader_aloc(LEADER) 90 91 self.nodes[COMMISSIONER].commissioner_start() 92 self.simulator.go(60) 93 94 self.nodes[COMMISSIONER].commissioner_add_joiner(FAKE_JOINER, 'PSKD01') 95 self.simulator.go(5) 96 97 self.nodes[COMMISSIONER].commissioner_stop() 98 self.simulator.go(5) 99 self.nodes[COMMISSIONER].commissioner_start() 100 self.simulator.go(5) 101 102 def verify(self, pv): 103 pkts = pv.pkts 104 pv.summary.show() 105 106 LEADER = pv.vars['LEADER'] 107 LEADER_ALOC = pv.vars['LEADER_ALOC'] 108 LEADER_RLOC = pv.vars['LEADER_RLOC'] 109 LEADER_RLOC16 = pv.vars['LEADER_RLOC16'] 110 COMMISSIONER = pv.vars['COMMISSIONER'] 111 COMMISSIONER_RLOC = pv.vars['COMMISSIONER_RLOC'] 112 113 # Step 2: Commissioner sends a petition request (LEAD_PET.req) to Leader: 114 # CoAP Request URI 115 # CON POST coap://<L>:MM/c/lp 116 # CoAP Payload 117 # Commissioner ID TLV 118 pv.verify_attached('COMMISSIONER', 'LEADER') 119 _pkt = pkts.last() 120 _leader_pet_pkt = pkts.filter_wpan_src64(COMMISSIONER).\ 121 filter_wpan_dst16(LEADER_RLOC16).\ 122 filter_coap_request(LEAD_PET_URI).\ 123 filter(lambda p: { 124 NM_COMMISSIONER_ID_TLV 125 } <= set(p.coap.tlv.type)\ 126 ).\ 127 must_next() 128 129 # Step 3: Leader accepts Commissioner to the network, sends a Leader Petition 130 # Response (LEAD_PET.rsp) to Commissioner: 131 # CoAP Response Code 132 # 2.04 Changed 133 # CoAP Payload 134 # State TLV (value = Accept) 135 # Commissioner ID TLV 136 # Commissioner Session ID TLV 137 # 138 # Leader sends a MLE Data Response to the network with the 139 # following TLVs: 140 # - Active Timestamp TLV 141 # - Leader Data TLV 142 # - Network Data TLV 143 # - Source Address TLV 144 pkts.filter_ipv6_src_dst(_leader_pet_pkt.ipv6.dst, COMMISSIONER_RLOC).\ 145 filter_coap_ack(LEAD_PET_URI).\ 146 filter(lambda p: { 147 NM_STATE_TLV, 148 NM_COMMISSIONER_ID_TLV, 149 NM_COMMISSIONER_SESSION_ID_TLV 150 } <= set(p.coap.tlv.type) and\ 151 p.thread_meshcop.tlv.state == MESHCOP_ACCEPT 152 ).\ 153 must_next() 154 _dr_pkt = pkts.filter_wpan_src64(LEADER).\ 155 filter_LLANMA().\ 156 filter_mle_cmd(MLE_DATA_RESPONSE).\ 157 filter(lambda p: { 158 NETWORK_DATA_TLV, 159 SOURCE_ADDRESS_TLV, 160 ACTIVE_TIMESTAMP_TLV, 161 LEADER_DATA_TLV 162 } <= set(p.mle.tlv.type) and\ 163 { 164 NWD_COMMISSIONING_DATA_TLV 165 } <= set(p.thread_nwd.tlv.type) and\ 166 { 167 NM_BORDER_AGENT_LOCATOR_TLV, 168 NM_COMMISSIONER_SESSION_ID_TLV, 169 NM_STEERING_DATA_TLV 170 } <= set(p.thread_meshcop.tlv.type) and\ 171 p.mle.tlv.leader_data.data_version == 172 (_pkt.mle.tlv.leader_data.data_version + 1) % 256 and\ 173 p.thread_nwd.tlv.stable == [0] 174 ).\ 175 must_next() 176 177 # Step 4: Commissioner sends a commissioner keep-alive request (LEAD_KA.req) to Leader: 178 # CoAP Request URI 179 # CON POST coap://<L>:MM/c/la 180 # CoAP Payload 181 # State TLV (value = Accept) 182 # Commissioner Session ID TLV 183 _leader_ka_pkt = pkts.filter_wpan_src64(COMMISSIONER).\ 184 filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ 185 filter_coap_request(LEAD_KA_URI).\ 186 filter(lambda p: { 187 NM_STATE_TLV, 188 NM_COMMISSIONER_SESSION_ID_TLV 189 } <= set(p.coap.tlv.type) and\ 190 p.thread_meshcop.tlv.state == MESHCOP_ACCEPT 191 ).\ 192 must_next() 193 194 # Step 5: Leader sends a commissioner keep-alive response (LEAD_KA.rsp) to Commissioner: 195 # CoAP Response Code 196 # 2.04 Changed 197 # CoAP Payload 198 # State TLV (value = Accept) 199 pkts.filter_ipv6_src_dst(_leader_ka_pkt.ipv6.dst, COMMISSIONER_RLOC).\ 200 filter_coap_ack(LEAD_KA_URI).\ 201 filter(lambda p: { 202 NM_STATE_TLV 203 } <= set(p.coap.tlv.type) and\ 204 p.thread_meshcop.tlv.state == MESHCOP_ACCEPT 205 ).\ 206 must_next() 207 208 # Step 6: Commissioner sends a Set Commissioner Dataset Request (MGMT_COMMISSIONER_SET.req) 209 # to Leader ALOC 0xFC00 or Leader RLOC: 210 # CoAP Request URI 211 # CON POST coap://<L>:MM/c/cs 212 # CoAP Payload 213 # Commissioner Session ID TLV 214 # Steering Data TLV 215 _mgmt_set_pkt = pkts.filter_wpan_src64(COMMISSIONER).\ 216 filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ 217 filter_coap_request(MGMT_COMMISSIONER_SET_URI).\ 218 filter(lambda p: { 219 NM_STEERING_DATA_TLV, 220 NM_COMMISSIONER_SESSION_ID_TLV 221 } <= set(p.coap.tlv.type) 222 ).\ 223 must_next() 224 225 # Step 7: Leader sends a Set Commissioner Dataset Response (MGMT_COMMISSIONER_SET.rsp) to 226 # Commissioner: 227 # CoAP Response Code 228 # 2.04 Changed 229 # CoAP Payload 230 # State TLV (value = Accept) 231 # 232 # Leader sends a MLE Data Response to the network with the 233 # following TLVs: 234 # - Active Timestamp TLV 235 # - Leader Data TLV 236 # - Network Data TLV 237 # - Source Address TLV 238 pkts.filter_ipv6_src_dst(_mgmt_set_pkt.ipv6.dst, COMMISSIONER_RLOC).\ 239 filter_coap_ack(MGMT_COMMISSIONER_SET_URI).\ 240 filter(lambda p: { 241 NM_STATE_TLV 242 } <= set(p.coap.tlv.type) and\ 243 p.thread_meshcop.tlv.state == MESHCOP_ACCEPT 244 ).\ 245 must_next() 246 247 _dr_pkt2 = pkts.filter_wpan_src64(LEADER).\ 248 filter_LLANMA().\ 249 filter_mle_cmd(MLE_DATA_RESPONSE).\ 250 filter(lambda p: { 251 NETWORK_DATA_TLV, 252 SOURCE_ADDRESS_TLV, 253 ACTIVE_TIMESTAMP_TLV, 254 LEADER_DATA_TLV 255 } <= set(p.mle.tlv.type) and\ 256 { 257 NWD_COMMISSIONING_DATA_TLV 258 } <= set(p.thread_nwd.tlv.type) and\ 259 { 260 NM_BORDER_AGENT_LOCATOR_TLV, 261 NM_COMMISSIONER_SESSION_ID_TLV, 262 NM_STEERING_DATA_TLV 263 } <= set(p.thread_meshcop.tlv.type) and\ 264 p.mle.tlv.leader_data.data_version == 265 (_dr_pkt.mle.tlv.leader_data.data_version + 1) % 256 and\ 266 p.thread_nwd.tlv.stable == [0] 267 ).\ 268 must_next() 269 270 # Step 8: Commissioner sends a resign request via commissioner keep-alive request 271 # (LEAD_KA.req) to Leader: 272 # CoAP Request URI 273 # CON POST coap://<L>:MM/c/la 274 # CoAP Payload 275 # State TLV (value = Reject) 276 # Commissioner Session ID TLV 277 _leader_ka_pkt = pkts.filter_wpan_src64(COMMISSIONER).\ 278 filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ 279 filter_coap_request(LEAD_KA_URI).\ 280 filter(lambda p: { 281 NM_STATE_TLV, 282 NM_COMMISSIONER_SESSION_ID_TLV 283 } <= set(p.coap.tlv.type) and\ 284 p.thread_meshcop.tlv.state == MESHCOP_REJECT 285 ).\ 286 must_next() 287 288 # Step 9: Leader accepts the resignation by responding with ‘Reject’ in a commissioner keep-alive 289 # response (LEAD_KA.rsp) to Commissioner: 290 # CoAP Response Code 291 # 2.04 Changed 292 # CoAP Payload 293 # State TLV (value = Reject) 294 # 295 # Leader sends a MLE Data Response to the network with the 296 # following TLVs: 297 # - Active Timestamp TLV 298 # - Leader Data TLV 299 # - Network Data TLV 300 # - Source Address TLV 301 pkts.filter_ipv6_src_dst(_leader_ka_pkt.ipv6.dst, COMMISSIONER_RLOC).\ 302 filter_coap_ack(LEAD_KA_URI).\ 303 filter(lambda p: { 304 NM_STATE_TLV 305 } <= set(p.coap.tlv.type) and\ 306 p.thread_meshcop.tlv.state == MESHCOP_REJECT 307 ).\ 308 must_next() 309 _dr_pkt3 = pkts.filter_wpan_src64(LEADER).\ 310 filter_LLANMA().\ 311 filter_mle_cmd(MLE_DATA_RESPONSE).\ 312 filter(lambda p: { 313 NETWORK_DATA_TLV, 314 SOURCE_ADDRESS_TLV, 315 ACTIVE_TIMESTAMP_TLV, 316 LEADER_DATA_TLV 317 } <= set(p.mle.tlv.type) and\ 318 { 319 NWD_COMMISSIONING_DATA_TLV 320 } <= set(p.thread_nwd.tlv.type) and\ 321 { 322 NM_COMMISSIONER_SESSION_ID_TLV 323 } <= set(p.thread_meshcop.tlv.type) and\ 324 (p.mle.tlv.leader_data.data_version - 325 _dr_pkt2.mle.tlv.leader_data.data_version) % 256 <= 127 and\ 326 p.thread_nwd.tlv.stable == [0] 327 ).\ 328 must_next() 329 330 # Step 10: Commissioner sends a petition request (LEAD_PET.req) to Leader: 331 # CoAP Request URI 332 # CON POST coap://<L>:MM/c/lp 333 # CoAP Payload 334 # Commissioner ID TLV 335 _leader_pet_pkt = pkts.filter_wpan_src64(COMMISSIONER).\ 336 filter_ipv6_2dsts(LEADER_ALOC, LEADER_RLOC).\ 337 filter_coap_request(LEAD_PET_URI).\ 338 filter(lambda p: { 339 NM_COMMISSIONER_ID_TLV 340 } <= set(p.coap.tlv.type)\ 341 ).\ 342 must_next() 343 344 # Step 11: Leader accepts Commissioner to the network, sends a Leader Petition 345 # Response (LEAD_PET.rsp) to Commissioner: 346 # CoAP Response Code 347 # 2.04 Changed 348 # CoAP Payload 349 # State TLV (value = Accept) 350 # Commissioner ID TLV 351 # Commissioner Session ID TLV (contains higher Session ID number 352 # than in Step 9) 353 pkts.filter_ipv6_src_dst(_leader_pet_pkt.ipv6.dst, COMMISSIONER_RLOC).\ 354 filter_coap_ack(LEAD_PET_URI).\ 355 filter(lambda p: { 356 NM_STATE_TLV, 357 NM_COMMISSIONER_ID_TLV, 358 NM_COMMISSIONER_SESSION_ID_TLV 359 } <= set(p.coap.tlv.type) and\ 360 p.thread_meshcop.tlv.state == MESHCOP_ACCEPT and\ 361 p.thread_meshcop.tlv.commissioner_sess_id == 362 (_dr_pkt3.thread_meshcop.tlv.commissioner_sess_id + 1) % 65536 363 ).\ 364 must_next() 365 366 367if __name__ == '__main__': 368 unittest.main() 369