[tests] add test case Cert_9_2_02 (#3589)

1. Add new test case **Cert_9_2_02**. Since the test case contains 2
topologies, added 2 new files:
- `Cert_9_2_02A_MGMTCommissionerSet.py`,
- `Cert_9_2_02B_MGMTCommissionerSet.py`.

2. Add mesh_cop tlvs parsing. The corresponding file is
`mesh_cop.py`. The mesh_cop tlvs are used in many cases. For example,
`CommissionerSessionId` is used in both the parsing of **Commissioning
Data**(in mle) and the parsing of **CoAP**. Collected all mesh_cop
tlvs refered in Thread into this file with some unused tlvs
unimplemented.

3. Reuse `SubTlvsFactory` in `network_data.py`. `SubTlvsFactory` are
used to choose the corresponding factory of specific tlv and do the
parsing of it. The `NetworkLayerTlvsFactory` in `network_layer.py` had
exactly the same function with it (almost the same code). Moved the
`SubTlvsFactory` into a new file `tlvs_parsing.py` and change the
usage in `network_data.py` and `network_layer.py`. This helps us
remove some redundant code.

4. Some format issues. Remove some trailing spaces.
This commit is contained in:
Irving-cl
2019-03-19 21:17:15 -07:00
committed by Jonathan Hui
parent a3f398b30f
commit a637cdccf4
15 changed files with 1146 additions and 442 deletions
@@ -83,8 +83,8 @@ class Cert_6_1_2_REEDAttach_MED(unittest.TestCase):
self.assertEqual(self.nodes[REED].get_state(), 'child')
self.nodes[MED].start()
self.simulator.go(5)
self.simulator.go(5)
self.assertEqual(self.nodes[MED].get_state(), 'child')
self.assertEqual(self.nodes[REED].get_state(), 'router')
med_messages = self.simulator.get_messages_sent_by(MED)
@@ -106,7 +106,7 @@ class Cert_6_1_2_REEDAttach_MED(unittest.TestCase):
# Wait additional DEFAULT_CHILD_TIMEOUT to ensure the keep-alive message (child update request from MED) happens.
self.simulator.go(config.DEFAULT_CHILD_TIMEOUT)
med_messages = self.simulator.get_messages_sent_by(MED)
# Step 8 - DUT sends Child Update messages
msg = med_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST)
check_child_update_request_from_child(msg, source_address=CheckType.CONTAIN, leader_data=CheckType.CONTAIN)
@@ -31,12 +31,11 @@ import functools
import time
import unittest
from command import check_child_id_response
from command import check_child_update_response
from command import check_child_update_request_from_child
from command import check_data_response
from command import check_child_id_response, check_child_update_response, check_child_update_request_from_child, check_data_response
from command import CheckType
from command import CommissioningDataCheck, NetworkDataCheck, PrefixesCheck, SinglePrefixCheck
from command import NetworkDataCheckType
import config
import mle
import network_data
@@ -135,22 +134,38 @@ class Cert_7_1_1_BorderRouterAsLeader(unittest.TestCase):
# Step 2 - DUT creates network data
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
check_data_response(msg, network_data_check=(NetworkDataCheckType.PREFIX_CONTENT,
[{network_data.TlvType.PREFIX:b'2001000200000001'}, {network_data.TlvType.PREFIX:b'2001000200000002'}]))
check_data_response(msg,
network_data_check=NetworkDataCheck(
prefixes_check=PrefixesCheck(prefix_check_list=[ SinglePrefixCheck(prefix=b'2001000200000001'), SinglePrefixCheck(prefix=b'2001000200000002') ])
)
)
# Step 4 - DUT sends a MLE Child ID Response to Router1
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
check_child_id_response(msg, network_data_check=(NetworkDataCheckType.PREFIX_CNT, 2))
check_child_id_response(msg,
network_data_check=NetworkDataCheck(
prefixes_check=PrefixesCheck(prefix_cnt=2)
)
)
# Step 6 - DUT sends a MLE Child ID Response to SED1
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
check_child_id_response(msg, network_data_check=(NetworkDataCheckType.PREFIX_CONTENT, [{network_data.TlvType.BORDER_ROUTER:0xFFFE}]))
check_child_id_response(msg,
network_data_check=NetworkDataCheck(
prefixes_check=PrefixesCheck(prefix_check_list=[ SinglePrefixCheck(border_router_16=0xFFFE) ])
)
)
# For Step 10
msg_chd_upd_res_to_sed = leader_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_RESPONSE)
# Step 8 - DUT sends a MLE Child ID Response to MED1
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
check_child_id_response(msg, network_data_check=(NetworkDataCheckType.PREFIX_CNT, 2))
check_child_id_response(msg,
network_data_check=NetworkDataCheck(
prefixes_check=PrefixesCheck(prefix_cnt=2)
)
)
# Step 10 - DUT sends Child Update Response
msg_chd_upd_res_to_med = leader_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_RESPONSE)
@@ -30,10 +30,12 @@
import time
import unittest
from command import check_child_update_request_from_child, check_child_update_request_from_parent, check_child_update_response, check_data_response
from command import CheckType
from command import NetworkDataCheck, PrefixesCheck, SinglePrefixCheck
from command import NetworkDataCheckType
import config
import command
import mle
import network_data
import node
@@ -136,37 +138,40 @@ class Cert_7_1_3_BorderRouterAsLeader(unittest.TestCase):
# 3 - Leader
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
command.check_data_response(msg, network_data_check=(NetworkDataCheckType.PREFIX_CONTENT,
[{network_data.TlvType.PREFIX:b'2001000200000001'}, {network_data.TlvType.PREFIX:b'2001000200000002'}]))
check_data_response(msg,
network_data_check=NetworkDataCheck(
prefixes_check=PrefixesCheck(prefix_check_list=[ SinglePrefixCheck(b'2001000200000001'), SinglePrefixCheck(b'2001000200000002')])
)
)
# 4 - N/A
# Get addresses registered by MED1
msg = med1_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST)
command.check_child_update_request_from_child(msg, address_registration=CheckType.CONTAIN, CIDs=[0, 1, 2])
check_child_update_request_from_child(msg, address_registration=CheckType.CONTAIN, CIDs=[0, 1, 2])
# 5 - Leader
# Make a copy of leader's messages to ensure that we don't miss messages to SED1
leader_messages_copy = leader_messages.clone()
msg = leader_messages_copy.next_mle_message(mle.CommandType.CHILD_UPDATE_RESPONSE, sent_to_node=self.nodes[MED1])
command.check_child_update_response(msg, address_registration=CheckType.CONTAIN, CIDs=[1, 2])
check_child_update_response(msg, address_registration=CheckType.CONTAIN, CIDs=[1, 2])
# 6A & 6B - Leader
if config.LEADER_NOTIFY_SED_BY_CHILD_UPDATE_REQUEST:
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST, sent_to_node=self.nodes[SED1])
command.check_child_update_request_from_parent(msg,
check_child_update_request_from_parent(msg,
leader_data=CheckType.CONTAIN, network_data=CheckType.CONTAIN, active_timestamp=CheckType.CONTAIN)
else:
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE, sent_to_node=self.nodes[SED1])
command.check_data_response(msg, network_data=CheckType.CONTAIN, active_timestamp=CheckType.CONTAIN)
check_data_response(msg, network_data_check=command.NetworkDataCheck())
# 7 - N/A
# Get addresses registered by SED1
msg = sed1_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST)
command.check_child_update_request_from_child(msg, address_registration=CheckType.CONTAIN, CIDs=[0, 1])
check_child_update_request_from_child(msg, address_registration=CheckType.CONTAIN, CIDs=[0, 1])
# 8 - Leader
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_RESPONSE, sent_to_node=self.nodes[SED1])
command.check_child_update_response(msg, address_registration=CheckType.CONTAIN, CIDs=[1])
check_child_update_response(msg, address_registration=CheckType.CONTAIN, CIDs=[1])
if __name__ == '__main__':
@@ -0,0 +1,172 @@
#!/usr/bin/env python
#
# Copyright (c) 2019, 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.
#
from ipaddress import ip_address
import unittest
from mle import NetworkData
from network_data import CommissioningData
import command
import config
import mesh_cop
import mle
import node
COMMISSIONER = 1
LEADER = 2
class Cert_9_2_02_MGMTCommissionerSet(unittest.TestCase):
def setUp(self):
self.simulator = config.create_default_simulator()
self.nodes = {}
for i in range(1,3):
self.nodes[i] = node.Node(i, simulator=self.simulator)
self.nodes[COMMISSIONER].set_panid(0xface)
self.nodes[COMMISSIONER].set_mode('rsdn')
self.nodes[COMMISSIONER].add_whitelist(self.nodes[LEADER].get_addr64())
self.nodes[COMMISSIONER].enable_whitelist()
self.nodes[COMMISSIONER].set_router_selection_jitter(1)
self.nodes[LEADER].set_panid(0xface)
self.nodes[LEADER].set_mode('rsdn')
self.nodes[LEADER].add_whitelist(self.nodes[COMMISSIONER].get_addr64())
self.nodes[LEADER].enable_whitelist()
self.nodes[LEADER].set_router_selection_jitter(1)
def tearDown(self):
for node in list(self.nodes.values()):
node.stop()
node.destroy()
self.simulator.stop()
def test(self):
self.nodes[LEADER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[COMMISSIONER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router')
# Skip all other Coaps sent by Leader
self.simulator.get_messages_sent_by(COMMISSIONER)
self.simulator.get_messages_sent_by(LEADER)
# Commissioner start
self.nodes[COMMISSIONER].commissioner_start()
self.simulator.go(3)
self.simulator.get_messages_sent_by(COMMISSIONER) # Skip LEAD_PET.req
# Get CommissionerSesssionId from LEAD_PET.rsp
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04', assert_enabled=True)
commissioner_session_id_tlv = command.get_sub_tlv(msg.coap.payload, mesh_cop.CommissionerSessionId)
# Step 2 - Harness instructs commissioner to send MGMT_COMMISSIONER_SET.req to Leader
steering_data_tlv = mesh_cop.SteeringData(bytes([0xFF]))
self.nodes[COMMISSIONER].commissioner_mgmtset_with_tlvs([steering_data_tlv])
self.simulator.go(5)
# Step 3 - Leader responds to MGMT_COMMISSIONER_SET.req with MGMT_COMMISSIONER_SET.rsp
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04')
command.check_coap_message(msg, [mesh_cop.State(mesh_cop.MeshCopState.REJECT)]) # (mesh_cop.State(mesh_cop.MeshCopState.REJECT),) <- this a tuple, don't delete the comma
self.simulator.get_messages_sent_by(COMMISSIONER) # Skip LEAD_PET.req
# Step 4 - Harness instructs commissioner to send MGMT_COMMISSIONER_SET.req to Leader
self.nodes[COMMISSIONER].commissioner_mgmtset_with_tlvs([steering_data_tlv, commissioner_session_id_tlv])
self.simulator.go(5)
commissioner_messages = self.simulator.get_messages_sent_by(COMMISSIONER)
msg = commissioner_messages.next_coap_message('0.02', uri_path='/c/cs')
rloc = ip_address(self.nodes[LEADER].get_addr_rloc())
leader_aloc = ip_address(self.nodes[LEADER].get_addr_leader_aloc())
command.check_coap_message(msg, [steering_data_tlv, commissioner_session_id_tlv], dest_addrs=[rloc, leader_aloc])
# Step 5 - Leader sends MGMT_COMMISSIONER_SET.rsp to commissioner
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04')
command.check_coap_message(msg, [mesh_cop.State(mesh_cop.MeshCopState.ACCEPT)])
# Step 6 - Leader sends a multicast MLE Data Response
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
command.check_data_response(msg, command.NetworkDataCheck(
commissioning_data_check=command.CommissioningDataCheck(stable=0, sub_tlv_type_list=[mesh_cop.CommissionerSessionId, mesh_cop.SteeringData, mesh_cop.BorderAgentLocator])))
# Step 7 - Harness instructs commissioner to send MGMT_COMMISSIONER_SET.req to Leader
border_agent_locator_tlv = mesh_cop.BorderAgentLocator(0x0400)
self.nodes[COMMISSIONER].commissioner_mgmtset_with_tlvs(
[commissioner_session_id_tlv, border_agent_locator_tlv])
self.simulator.go(5)
# Step 8 - Leader responds to MGMT_COMMISSIONER_SET.req with MGMT_COMMISSIONER_SET.rsp
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04')
command.check_coap_message(msg, [mesh_cop.State(mesh_cop.MeshCopState.REJECT)])
# Step 9 - Harness instructs commissioner to send MGMT_COMMISSIONER_SET.req to Leader
self.nodes[COMMISSIONER].commissioner_mgmtset_with_tlvs(
[steering_data_tlv, commissioner_session_id_tlv, border_agent_locator_tlv])
self.simulator.go(5)
# Step 10 - Leader responds to MGMT_COMMISSIONER_SET.req with MGMT_COMMISSIONER_SET.rsp
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04')
command.check_coap_message(msg, [mesh_cop.State(mesh_cop.MeshCopState.REJECT)])
# Step 11 - Harness instructs commissioner to send MGMT_COMMISSIONER_SET.req to Leader
self.nodes[COMMISSIONER].commissioner_mgmtset_with_tlvs(
[mesh_cop.CommissionerSessionId(0xFFFF), steering_data_tlv])
self.simulator.go(5)
# Step 12 - Leader responds to MGMT_COMMISSIONER_SET.req with MGMT_COMMISSIONER_SET.rsp
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04')
command.check_coap_message(msg, [mesh_cop.State(mesh_cop.MeshCopState.REJECT)])
# Step 13 - Harness instructs commissioner to send MGMT_COMMISSIONER_SET.req to Leader
self.nodes[COMMISSIONER].commissioner_mgmtset_with_tlvs(
[commissioner_session_id_tlv, steering_data_tlv, mesh_cop.Channel(0x0, 0x0)])
self.simulator.go(5)
# Step 14 - Leader responds to MGMT_COMMISSIONER_SET.req with MGMT_COMMISSIONER_SET.rsp
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_coap_message('2.04')
command.check_coap_message(msg, [mesh_cop.State(mesh_cop.MeshCopState.ACCEPT)])
# Step 15 - Send ICMPv6 Echo Request to Leader
leader_rloc = self.nodes[LEADER].get_addr_rloc()
self.assertTrue(self.nodes[COMMISSIONER].ping(leader_rloc))
if __name__ == '__main__':
unittest.main()
+4
View File
@@ -108,6 +108,7 @@ EXTRA_DIST = \
Cert_8_1_02_Commissioning.py \
Cert_8_2_01_JoinerRouter.py \
Cert_8_2_02_JoinerRouter.py \
Cert_9_2_02_MGMTCommissionerSet.py \
Cert_9_2_04_ActiveDataset.py \
Cert_9_2_07_DelayTimer.py \
Cert_9_2_08_PersistentDatasets.py \
@@ -153,6 +154,7 @@ EXTRA_DIST = \
test_service.py \
test_network_data.py \
test_network_layer.py \
tlvs_parsing.py \
$(NULL)
check_PROGRAMS = \
@@ -247,6 +249,7 @@ check_SCRIPTS = \
Cert_8_1_02_Commissioning.py \
Cert_8_2_01_JoinerRouter.py \
Cert_8_2_02_JoinerRouter.py \
Cert_9_2_02_MGMTCommissionerSet.py \
Cert_9_2_04_ActiveDataset.py \
Cert_9_2_07_DelayTimer.py \
Cert_9_2_08_PersistentDatasets.py \
@@ -288,6 +291,7 @@ XFAIL_NCP_TESTS = \
Cert_8_1_02_Commissioning.py \
Cert_8_2_01_JoinerRouter.py \
Cert_8_2_02_JoinerRouter.py \
Cert_9_2_02_MGMTCommissionerSet.py \
Cert_9_2_04_ActiveDataset.py \
Cert_9_2_07_DelayTimer.py \
Cert_9_2_08_PersistentDatasets.py \
+3 -3
View File
@@ -257,10 +257,10 @@ class CoapMessage(object):
class CoapMessageProxy(object):
""" Proxy class of CoAP message.
""" Proxy class of CoAP message.
The main idea behind this class is to delay parsing payload. Due to architecture of the existing solution
it is possible to process confirmation message before a request message. In such case it is not possible
The main idea behind this class is to delay parsing payload. Due to architecture of the existing solution
it is possible to process confirmation message before a request message. In such case it is not possible
to get URI path to get proper payload parser.
"""
+96 -48
View File
@@ -273,7 +273,6 @@ def check_parent_request(command_msg, is_first_request):
elif not scan_mask.end_device:
raise ValueError("Second parent request without E bit set")
def check_parent_response(command_msg, mle_frame_counter = CheckType.OPTIONAL):
"""Verify a properly formatted Parent Response command message.
"""
@@ -313,41 +312,6 @@ def check_child_id_request(command_msg, tlv_request = CheckType.OPTIONAL, \
check_tlv_request_tlv(command_msg, CheckType.CONTAIN, mle.TlvType.ADDRESS16)
check_tlv_request_tlv(command_msg, CheckType.CONTAIN, mle.TlvType.NETWORK_DATA)
def find_prefix_tlv(tlvs, cond_map):
"""Find a prefix tlv in tlvs which matchs some conditions specified by cond_map
"""
for tlv in tlvs:
if network_data.TlvType.PREFIX in cond_map:
if binascii.hexlify(tlv.prefix) != cond_map[network_data.TlvType.PREFIX]:
continue
if network_data.TlvType.BORDER_ROUTER in cond_map:
border_router_tlv = get_sub_tlv(tlv.sub_tlvs, network_data.BorderRouter)
if border_router_tlv.border_router_16 != cond_map[network_data.TlvType.BORDER_ROUTER]:
continue
return tlv
return None
def check_network_data(data, check_detail):
check_type = check_detail[0]
prefixes = [tlv for tlv in data.tlvs if isinstance(tlv, network_data.Prefix)]
if check_type == NetworkDataCheckType.PREFIX_CNT:
# check_detail[1] should be a integer number representing the minimum count of prefixes should be
min_cnt = check_detail[1]
assert len(prefixes) >= min_cnt, 'Network data should contain at least {} prefixes'.format(mn_cnt)
for prefix in prefixes:
check_prefix(prefix)
elif check_type == NetworkDataCheckType.PREFIX_CONTENT:
# check_detail[1] should be a list of dictionary(like
# [{network_data.TlvType.PREFIX='...', network_data.TlvType.BORDER_ROUTER='...'}])
# each entry of the dictionary represents one thing to check of the prefix tlv
assert len(prefixes) >= len(check_detail[1]), 'Network data seems to have less prefixes than expected'
# basic check of prefixes
for prefix in prefixes:
check_prefix(prefix)
for cond_map in check_detail[1]:
tlv = find_prefix_tlv(prefixes, cond_map)
assert tlv is not None, 'Some prefix sub-tlv is not found:{}'.format(cond_map)
def check_child_id_response(command_msg, route64 = CheckType.OPTIONAL, network_data = CheckType.OPTIONAL, \
address_registration = CheckType.OPTIONAL, active_timestamp = CheckType.OPTIONAL, \
pending_timestamp = CheckType.OPTIONAL, active_operational_dataset = CheckType.OPTIONAL, \
@@ -367,9 +331,9 @@ def check_child_id_response(command_msg, route64 = CheckType.OPTIONAL, network_d
check_mle_optional_tlv(command_msg, active_operational_dataset, mle.ActiveOperationalDataset)
check_mle_optional_tlv(command_msg, pending_operational_dataset, mle.PendingOperationalDataset)
if network_data_check != None:
if network_data_check is not None:
network_data_tlv = command_msg.assertMleMessageContainsTlv(mle.NetworkData)
check_network_data(network_data_tlv, network_data_check)
network_data_check.check(network_data_tlv)
def check_prefix(prefix):
"""Verify if a prefix contains 6loWPAN sub-TLV and border router sub-TLV
@@ -418,27 +382,27 @@ def contains_tlv(sub_tlvs, tlv_type):
"""
return any(isinstance(sub_tlv, tlv_type) for sub_tlv in sub_tlvs)
def contains_tlvs(sub_tlvs, tlv_types):
"""Verify if all types of tlv in a list are included in a sub-tlv list.
"""
return all((any(isinstance(sub_tlv, tlv_type) for sub_tlv in sub_tlvs)) for tlv_type in tlv_types)
def check_secure_mle_key_id_mode(command_msg, key_id_mode):
"""Verify if the mle command message sets the right key id mode.
"""
assert isinstance(command_msg.mle, mle.MleMessageSecured)
assert command_msg.mle.aux_sec_hdr.key_id_mode == key_id_mode
def check_data_response(command_msg, network_data_opt=CheckType.OPTIONAL,
active_timestamp=CheckType.OPTIONAL,
network_data_check=None):
def check_data_response(command_msg, network_data_check=None, active_timestamp=CheckType.OPTIONAL):
"""Verify a properly formatted Data Response command message.
"""
check_secure_mle_key_id_mode(command_msg, 0x02)
command_msg.assertMleMessageContainsTlv(mle.SourceAddress)
command_msg.assertMleMessageContainsTlv(mle.LeaderData)
check_mle_optional_tlv(command_msg, network_data_opt, mle.NetworkData)
check_mle_optional_tlv(command_msg, active_timestamp, mle.ActiveTimestamp)
if network_data_check != None:
if network_data_check is not None:
network_data_tlv = command_msg.assertMleMessageContainsTlv(mle.NetworkData)
check_network_data(network_data_tlv, network_data_check)
network_data_check.check(network_data_tlv)
def check_child_update_request_from_parent(command_msg, leader_data=CheckType.OPTIONAL,
network_data=CheckType.OPTIONAL, challenge=CheckType.OPTIONAL,
@@ -532,11 +496,11 @@ def check_discovery_response(command_msg, request_src_addr, steering_data=CheckT
assert response.version == config.PROTOCOL_VERSION
assert_contains_tlv(tlvs, CheckType.CONTAIN, mesh_cop.ExtendedPanid)
assert_contains_tlv(tlvs, CheckType.CONTAIN, mesh_cop.NetworkName)
assert_contains_tlv(tlvs, steering_data, network_data.SteeringData)
assert_contains_tlv(tlvs, steering_data, mesh_cop.SteeringData)
assert_contains_tlv(tlvs, steering_data, mesh_cop.JoinerUdpPort)
check_type = CheckType.CONTAIN if response.native_flag else CheckType.OPTIONAL
assert_contains_tlv(tlvs, check_type, network_data.CommissionerUdpPort)
assert_contains_tlv(tlvs, check_type, mesh_cop.CommissionerUdpPort)
def get_joiner_udp_port_in_discovery_response(command_msg):
"""Get the udp port specified in a DISCOVERY RESPONSE message
@@ -565,3 +529,87 @@ def check_joiner_router_commissioning_messages(commissioning_messages):
"""Verify COAP messages sent by joiner router while commissioning process.
"""
assert any(msg.type == mesh_cop.MeshCopMessageType.JOIN_ENT_NTF for msg in commissioning_messages)
return None
def check_payload_same(tp1, tp2):
"""Verfiy two payloads are totally the same.
A payload is a tuple of tlvs.
"""
assert len(tp1) == len(tp2)
for tlv in tp2:
peer_tlv = get_sub_tlv(tp1, type(tlv))
assert peer_tlv is not None and peer_tlv == tlv, 'peer_tlv:{}, tlv:{} type:{}'.format(peer_tlv, tlv, type(tlv))
def check_coap_message(msg, payloads, dest_addrs=None):
if dest_addrs is not None:
found = False
for dest in dest_addrs:
if msg.ipv6_packet.ipv6_header.destination_address == dest:
found = True
break
assert found, 'Destination address incorrect'
check_payload_same(msg.coap.payload, payloads)
class SinglePrefixCheck:
def __init__(self, prefix=None, border_router_16=None):
self._prefix = prefix
self._border_router_16 = border_router_16
def check(self, prefix_tlv):
border_router_tlv = assert_contains_tlv(prefix_tlv.sub_tlvs, CheckType.CONTAIN, network_data.BorderRouter)
lowpan_id_tlv = assert_contains_tlv(prefix_tlv.sub_tlvs, CheckType.CONTAIN, network_data.LowpanId)
result = True
if self._prefix is not None:
result &= (self._prefix == binascii.hexlify(prefix_tlv.prefix))
if self._border_router_16 is not None:
result &= (self._border_router_16 == border_router_tlv.border_router_16)
return result
class PrefixesCheck:
def __init__(self, prefix_cnt=0, prefix_check_list=[]):
self._prefix_cnt = prefix_cnt
self._prefix_check_list = prefix_check_list
def check(self, prefix_tlvs):
# if prefix_cnt is given, then check count only
if self._prefix_cnt > 0:
assert len(prefix_tlvs) >= self._prefix_cnt, 'prefix count is less than expected'
else:
for prefix_check in self._prefix_check_list:
found = False
for prefix_tlv in prefix_tlvs:
if prefix_check.check(prefix_tlv):
found = True
break
assert found, 'Some prefix is absent: {}'.format(prefix_check)
class CommissioningDataCheck:
def __init__(self, stable=None, sub_tlv_type_list=[]):
self._stable = stable
self._sub_tlv_type_list = sub_tlv_type_list
def check(self, commissioning_data_tlv):
if self._stable is not None:
assert self._stable == commissioning_data_tlv.stable, 'Commissioning Data stable flag is not correct'
assert contains_tlvs(commissioning_data_tlv.sub_tlvs, self._sub_tlv_type_list), 'Some sub tlvs are missing in Commissioning Data'
class NetworkDataCheck:
def __init__(self, prefixes_check=None, commissioning_data_check=None):
self._prefixes_check = prefixes_check
self._commissioning_data_check = commissioning_data_check
def check(self, network_data_tlv):
if self._prefixes_check is not None:
prefix_tlvs = [tlv for tlv in network_data_tlv.tlvs if isinstance(tlv, network_data.Prefix)]
self._prefixes_check.check(prefix_tlvs)
if self._commissioning_data_check is not None:
commissioning_data_tlv = assert_contains_tlv(network_data_tlv.tlvs, CheckType.CONTAIN, network_data.CommissioningData)
self._commissioning_data_check.check(commissioning_data_tlv)
+57 -11
View File
@@ -26,9 +26,10 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import os
from enum import Enum
import os
from tlvs_parsing import SubTlvsFactory
import coap
import dtls
import ipv6
@@ -108,10 +109,11 @@ def create_default_network_data_service_sub_tlvs_factory():
def create_default_network_data_commissioning_data_sub_tlvs_factories():
return {
network_data.MeshcopTlvType.STEERING_DATA: network_data.SteeringDataFactory(),
network_data.MeshcopTlvType.BORDER_AGENT_LOCATOR: network_data.BorderAgentLocatorFactory(),
network_data.MeshcopTlvType.COMMISSIONER_SESSION_ID: network_data.CommissionerSessionIdFactory(),
network_data.MeshcopTlvType.COMMISSIONER_UDP_PORT: network_data.CommissionerUdpPortFactory(),
mesh_cop.TlvType.CHANNEL: mesh_cop.ChannelFactory(),
mesh_cop.TlvType.STEERING_DATA: mesh_cop.SteeringDataFactory(),
mesh_cop.TlvType.BORDER_AGENT_LOCATOR: mesh_cop.BorderAgentLocatorFactory(),
mesh_cop.TlvType.COMMISSIONER_SESSION_ID: mesh_cop.CommissionerSessionIdFactory(),
mesh_cop.TlvType.COMMISSIONER_UDP_PORT: mesh_cop.CommissionerUdpPortFactory(),
}
@@ -168,9 +170,9 @@ def create_default_thread_discovery_sub_tlvs_factories():
mesh_cop.TlvType.DISCOVERY_RESPONSE: mesh_cop.DiscoveryResponseFactory(),
mesh_cop.TlvType.EXTENDED_PANID: mesh_cop.ExtendedPanidFactory(),
mesh_cop.TlvType.NETWORK_NAME: mesh_cop.NetworkNameFactory(),
mesh_cop.TlvType.STEERING_DATA: network_data.SteeringDataFactory(),
mesh_cop.TlvType.STEERING_DATA: mesh_cop.SteeringDataFactory(),
mesh_cop.TlvType.JOINER_UDP_PORT: mesh_cop.JoinerUdpPortFactory(),
mesh_cop.TlvType.COMMISSIONER_UDP_PORT: network_data.CommissionerUdpPortFactory()
mesh_cop.TlvType.COMMISSIONER_UDP_PORT: mesh_cop.CommissionerUdpPortFactory()
}
def create_default_mle_tlvs_factories():
@@ -238,20 +240,64 @@ def create_deafult_network_tlvs_factories():
def create_default_network_tlvs_factory():
return network_layer.NetworkLayerTlvsFactory(
tlvs_factories=create_deafult_network_tlvs_factories())
return SubTlvsFactory(sub_tlvs_factories=create_deafult_network_tlvs_factories())
def create_default_mesh_cop_tlvs_factories():
return {
mesh_cop.TlvType.CHANNEL: mesh_cop.ChannelFactory(),
mesh_cop.TlvType.PAN_ID: mesh_cop.PanidFactory(),
mesh_cop.TlvType.EXTENDED_PANID: mesh_cop.ExtendedPanidFactory(),
mesh_cop.TlvType.NETWORK_NAME: mesh_cop.NetworkNameFactory(),
mesh_cop.TlvType.PSKC: mesh_cop.PSKcFactory(),
mesh_cop.TlvType.NETWORK_MASTER_KEY: mesh_cop.NetworkMasterKeyFactory(),
mesh_cop.TlvType.NETWORK_KEY_SEQUENCE_COUNTER: mesh_cop.NetworkKeySequenceCounterFactory(),
mesh_cop.TlvType.NETWORK_MESH_LOCAL_PREFIX: mesh_cop.NetworkMeshLocalPrefixFactory(),
mesh_cop.TlvType.STEERING_DATA: mesh_cop.SteeringDataFactory(),
mesh_cop.TlvType.BORDER_AGENT_LOCATOR: mesh_cop.BorderAgentLocatorFactory(),
mesh_cop.TlvType.COMMISSIONER_ID: mesh_cop.CommissionerIdFactory(),
mesh_cop.TlvType.COMMISSIONER_SESSION_ID: mesh_cop.CommissionerSessionIdFactory(),
mesh_cop.TlvType.SECURITY_POLICY: mesh_cop.SecurityPolicyFactory(),
mesh_cop.TlvType.GET: mesh_cop.GetFactory(),
mesh_cop.TlvType.ACTIVE_TIMESTAMP: mesh_cop.ActiveTimestampFactory(),
mesh_cop.TlvType.COMMISSIONER_UDP_PORT: mesh_cop.CommissionerUdpPortFactory(),
mesh_cop.TlvType.STATE: mesh_cop.StateFactory(),
mesh_cop.TlvType.JOINER_DTLS_ENCAPSULATION: mesh_cop.JoinerDtlsEncapsulationFactory(),
mesh_cop.TlvType.JOINER_UDP_PORT: mesh_cop.JoinerUdpPortFactory(),
mesh_cop.TlvType.JOINER_IID: mesh_cop.JoinerIIDFactory(),
mesh_cop.TlvType.JOINER_ROUTER_LOCATOR: mesh_cop.JoinerRouterLocatorFactory(),
mesh_cop.TlvType.JOINER_ROUTER_KEK: mesh_cop.JoinerRouterKEKFactory(),
mesh_cop.TlvType.PROVISIONING_URL: mesh_cop.ProvisioningUrlFactory(),
mesh_cop.TlvType.VENDOR_NAME: mesh_cop.VendorNameFactory(),
mesh_cop.TlvType.VENDOR_MODEL: mesh_cop.VendorModelFactory(),
mesh_cop.TlvType.VENDOR_SW_VERSION: mesh_cop.VendorSWVersionFactory(),
mesh_cop.TlvType.VENDOR_DATA: mesh_cop.VendorDataFactory(),
mesh_cop.TlvType.VENDOR_STACK_VERSION: mesh_cop.VendorStackVersionFactory(),
mesh_cop.TlvType.UDP_ENCAPSULATION: mesh_cop.UdpEncapsulationFactory(),
mesh_cop.TlvType.IPV6_ADDRESS: mesh_cop.Ipv6AddressFactory(),
mesh_cop.TlvType.PENDING_TIMESTAMP: mesh_cop.PendingTimestampFactory(),
mesh_cop.TlvType.DELAY_TIMER: mesh_cop.DelayTimerFactory(),
mesh_cop.TlvType.CHANNEL_MASK: mesh_cop.ChannelMaskFactory(),
mesh_cop.TlvType.COUNT: mesh_cop.CountFactory(),
mesh_cop.TlvType.PERIOD: mesh_cop.PeriodFactory(),
mesh_cop.TlvType.SCAN_DURATION: mesh_cop.ScanDurationFactory(),
mesh_cop.TlvType.ENERGY_LIST: mesh_cop.EnergyListFactory()
}
def create_default_mesh_cop_tlvs_factory():
return SubTlvsFactory(sub_tlvs_factories=create_default_mesh_cop_tlvs_factories())
def create_default_uri_path_based_payload_factories():
network_layer_tlvs_factory = create_default_network_tlvs_factory()
mesh_cop_tlvs_factory = create_default_mesh_cop_tlvs_factory()
return {
"/a/as": network_layer_tlvs_factory,
"/a/aq": network_layer_tlvs_factory,
"/a/ar": network_layer_tlvs_factory,
"/a/ae": network_layer_tlvs_factory,
"/a/an": network_layer_tlvs_factory,
"/a/sd": network_layer_tlvs_factory
"/a/sd": network_layer_tlvs_factory,
"/c/lp": mesh_cop_tlvs_factory,
"/c/cs": mesh_cop_tlvs_factory
}
+670 -183
View File
@@ -27,33 +27,65 @@
# POSSIBILITY OF SUCH DAMAGE.
#
from binascii import hexlify
from enum import IntEnum
import io
import struct
from network_data import SubTlvsFactory
import common
class TlvType(IntEnum):
CHANNEL = 0
PAN_ID = 1
EXTENDED_PANID = 2
NETWORK_NAME = 3
PSKC = 4
NETWORK_MASTER_KEY = 5
NETWORK_KEY_SEQUENCE_COUNTER = 6
NETWORK_MESH_LOCAL_PREFIX = 7
STEERING_DATA = 8
BORDER_AGENT_LOCATOR = 9
COMMISSIONER_ID = 10
COMMISSIONER_SESSION_ID = 11
SECURITY_POLICY = 12
GET = 13
ACTIVE_TIMESTAMP = 14
COMMISSIONER_UDP_PORT = 15
STATE = 16
JOINER_DTLS_ENCAPSULATION = 17
JOINER_UDP_PORT = 18
JOINER_IID = 19
JOINER_ROUTER_LOCATOR = 20
JOINER_ROUTER_KEK = 21
PROVISIONING_URL = 32
VENDOR_NAME = 33
VENDOR_MODEL = 34
VENDOR_SW_VERSION = 35
VENDOR_DATA = 36
VENDOR_STACK_VERSION = 37
UDP_ENCAPSULATION = 48
IPV6_ADDRESS = 49
PENDING_TIMESTAMP = 51
DELAY_TIMER = 52
CHANNEL_MASK = 53
COUNT = 54
PERIOD = 55
SCAN_DURATION = 56
ENERGY_LIST = 57
DISCOVERY_REQUEST = 128
DISCOVERY_RESPONSE = 129
class MeshCopState(IntEnum):
ACCEPT = 0x1
REJECT = 0xFF
class MeshCopMessageType(IntEnum):
JOIN_FIN_REQ = 1
JOIN_FIN_RSP = 2
JOIN_ENT_NTF = 3
JOIN_FIN_REQ = 1,
JOIN_FIN_RSP = 2,
JOIN_ENT_NTF = 3,
JOIN_ENT_RSP = 4
@@ -64,6 +96,335 @@ def create_mesh_cop_message_type_set():
MeshCopMessageType.JOIN_ENT_RSP ]
# Channel TLV (0)
class Channel(object):
def __init__(self, channel_page, channel):
self._channel_page = channel_page
self._channel = channel
@property
def channel_page(self):
return self._channel_page
@property
def channel(self):
return self._channel
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._channel_page == other._channel_page and self._channel == other.__channel
def __repr__(self):
return 'Channel(channel_page={},channel={})'.format(self._channel_page, self._channel)
def to_hex(self):
return struct.pack('>BBBH', TlvType.CHANNEL, 3, self.channel_page, self.channel)
class ChannelFactory(object):
def parse(self, data, message_info):
data_tp = struct.unpack('>BH', data.read(3))
channel_page = data_tp[0]
channel = data_tp[1]
return Channel(channel_page, channel)
# PanId TLV (1)
class Panid(object):
# TODO: Not implemented yet
pass
class PanidFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# ExtendedPanid TLV (2)
class ExtendedPanid(object):
def __init__(self, extended_panid):
self._extended_panid = extended_panid
@property
def extended_panid(self):
return self._extended_panid
def __eq__(self, other):
return (type(self) is type(other)
and self.extended_panid == other.extended_panid)
def __repr__(self):
return "ExtendedPanid(extended_panid={})".format(self.extended_panid)
class ExtendedPanidFactory(object):
def parse(self, data, message_info):
extended_panid = struct.unpack(">Q", data.read(8))[0]
return ExtendedPanid(extended_panid)
# NetworkName TLV (3)
class NetworkName(object):
def __init__(self, network_name):
self._network_name = network_name
@property
def network_name(self):
return self._network_name
def __eq__(self, other):
return (type(self) is type(other)
and self.network_name == other.network_name)
def __repr__(self):
return "NetworkName(network_name={})".format(self.network_name)
class NetworkNameFactory(object):
def parse(self, data, message_info):
len = message_info.length
network_name = struct.unpack("{}s".format(10), data.read(len))[0]
return NetworkName(network_name)
# PSKc TLV (4)
class PSKc(object):
# TODO: Not implemented yet
pass
class PSKcFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# NetworkMasterKey TLV (5)
class NetworkMasterKey(object):
# TODO: Not implemented yet
pass
class NetworkMasterKeyFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# NetworkKeySequenceCounter TLV (6)
class NetworkKeySequenceCounter(object):
# TODO: Not implemented yet
pass
class NetworkKeySequenceCounterFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# NetworkMeshLocalPrefix TLV (7)
class NetworkMeshLocalPrefix(object):
# TODO: Not implemented yet
pass
class NetworkMeshLocalPrefixFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Steering Data TLV (8)
class SteeringData(object):
def __init__(self, bloom_filter):
self._bloom_filter = bloom_filter
@property
def bloom_filter(self):
return self._bloom_filter
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._bloom_filter == other._bloom_filter
def __repr__(self):
return "SteeringData(bloom_filter={})".format(hexlify(self._bloom_filter))
def to_hex(self):
bloom_filter_len = len(self.bloom_filter)
return struct.pack('>BB', TlvType.STEERING_DATA, bloom_filter_len) + self.bloom_filter
class SteeringDataFactory:
def parse(self, data, message_info):
bloom_filter = data.read(message_info.length)
return SteeringData(bloom_filter)
# Border Agent Locator TLV (9)
class BorderAgentLocator(object):
def __init__(self, address):
self._border_agent_locator = address
@property
def border_agent_locator(self):
return self._border_agent_locator
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._border_agent_locator == other._border_agent_locator
def __repr__(self):
return "BorderAgentLocator(rloc16={})".format(hex(self._border_agent_locator))
def to_hex(self):
return struct.pack('>BBH', TlvType.BORDER_AGENT_LOCATOR, 2, self.border_agent_locator)
class BorderAgentLocatorFactory:
def parse(self, data, message_info):
border_agent_locator = struct.unpack(">H", data.read(2))[0]
return BorderAgentLocator(border_agent_locator)
# CommissionerId TLV (10)
class CommissionerId(object):
def __init__(self, commissioner_id):
self._commissioner_id = commissioner_id
@property
def commissioner_id(self):
return self._commissioner_id
def __eq__(self, other):
return self.commissioner_id == other.commissioner_id
def __repr__(self):
return "CommissionerId(commissioner_id={})".format(self.commissioner_id)
class CommissionerIdFactory(object):
def parse(self, data, message_info):
commissioner_id = data.getvalue().decode('utf-8')
return CommissionerId(commissioner_id)
# Commissioner Session ID TLV (11)
class CommissionerSessionId(object):
def __init__(self, commissioner_session_id):
self._commissioner_session_id = commissioner_session_id
@property
def commissioner_session_id(self):
return self._commissioner_session_id
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._commissioner_session_id == other._commissioner_session_id
def __repr__(self):
return "CommissionerSessionId(commissioner_session_id={})".format(self._commissioner_session_id)
def to_hex(self):
return struct.pack('>BBH', TlvType.COMMISSIONER_SESSION_ID, 2, self.commissioner_session_id)
class CommissionerSessionIdFactory:
def parse(self, data, message_info):
session_id = struct.unpack(">H", data.read(2))[0]
return CommissionerSessionId(session_id)
# SecurityPolicy TLV (12)
class SecurityPolicy(object):
# TODO: Not implemented yet
pass
class SecurityPolicyFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Get TLV (13)
class Get(object):
# TODO: Not implemented yet
pass
class GetFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# ActiveTimestamp TLV (14)
class ActiveTimestamp(object):
# TODO: Not implemented yet
pass
class ActiveTimestampFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Commissioner UDP Port TLV (15)
class CommissionerUdpPort(object):
def __init__(self, udp_port):
self._udp_port = udp_port
@property
def udp_port(self):
return self._udp_port
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._udp_port == other._udp_port
def __repr__(self):
return "CommissionerUdpPort(udp_port={})".format(self._udp_port)
class CommissionerUdpPortFactory:
def parse(self, data, message_info):
udp_port = struct.unpack(">H", data.read(2))[0]
return CommissionerUdpPort(udp_port)
# State TLV (16)
class State(object):
def __init__(self, state):
@@ -82,11 +443,109 @@ class State(object):
class StateFactory:
def parse(self, data):
def parse(self, data, message_info):
state = ord(data.read(1))
return State(state)
# JoinerDtlsEncapsulation TLV (17)
class JoinerDtlsEncapsulation(object):
# TODO: Not implemented yet
pass
class JoinerDtlsEncapsulationFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# JoinerUdpPort TLV (18)
class JoinerUdpPort(object):
def __init__(self, udp_port):
self._udp_port = udp_port
@property
def udp_port(self):
return self._udp_port
def __eq__(self, other):
return type(self) is type(other) and self.udp_port == other.udp_port
def __repr__(self):
return "JoinerUdpPort(udp_port={})".format(self.udp_port)
class JoinerUdpPortFactory(object):
def parse(self, data, message_info):
udp_port = struct.unpack(">H", data.read(2))[0]
return JoinerUdpPort(udp_port)
# JoinerIID TLV (19)
class JoinerIID(object):
# TODO: Not implemented yet
pass
class JoinerIIDFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# JoinerRouterLocator TLV (20)
class JoinerRouterLocator(object):
# TODO: Not implemented yet
pass
class JoinerRouterLocatorFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# JoinerRouterKEK TLV (21)
class JoinerRouterKEK(object):
# TODO: Not implemented yet
pass
class JoinerRouterKEKFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# ProvisioningURL TLV (32)
class ProvisioningUrl(object):
def __init__(self, url):
self._url = url
@property
def url(self):
return self._url
def __repr__(self):
return "ProvisioningUrl(url={})".format(self.url)
class ProvisioningUrlFactory:
def parse(self, data, message_info):
url = data.decode('utf-8')
return ProvisioningUrl(url)
# VendorName TLV (33)
class VendorName(object):
def __init__(self, vendor_name):
@@ -102,14 +561,14 @@ class VendorName(object):
def __repr__(self):
return "VendorName(vendor_name={})".format(self.vendor_name)
class VendorNameFactory:
def parse(self, data):
def parse(self, data, message_info):
vendor_name = data.getvalue().decode('utf-8')
return VendorName(vendor_name)
# VendorModel TLV (34)
class VendorModel(object):
def __init__(self, vendor_model):
@@ -128,10 +587,12 @@ class VendorModel(object):
class VendorModelFactory:
def parse(self, data):
def parse(self, data, message_info):
vendor_model = data.getvalue().decode('utf-8')
return VendorModel(vendor_model)
# VendorSWVersion TLV (35)
class VendorSWVersion(object):
def __init__(self, vendor_sw_version):
@@ -150,11 +611,31 @@ class VendorSWVersion(object):
class VendorSWVersionFactory:
def parse(self, data):
def parse(self, data, message_info):
vendor_sw_version = data.getvalue()
return VendorSWVersion(vendor_sw_version)
# VendorData TLV (36)
class VendorData(object):
def __init__(self, data):
self._vendor_data = data
@property
def vendor_data(self):
return self._vendor_data
def __repr__(self):
return "Vendor(url={})".format(self.vendor_data)
class VendorDataFactory(object):
def parse(self, data, message_info):
return VendorData(data)
# VendorStackVersion TLV (37)
class VendorStackVersion(object):
@@ -189,10 +670,9 @@ class VendorStackVersion(object):
def __repr__(self):
return "VendorStackVersion(vendor_stack_version={}, build={}, rev={}, minor={}, major={})".format(self.stack_vendor_oui, self.build, self.rev, self.minor, self.major)
class VendorStackVersionFactory:
def parse(self, data):
def parse(self, data, message_info):
stack_vendor_oui = struct.unpack(">H", data.read(2))[0]
rest = struct.unpack(">BBBB", data.read(4))
build = rest[1] << 4 | (0xf0 & rest[2])
@@ -202,42 +682,191 @@ class VendorStackVersionFactory:
return VendorStackVersion(stack_vendor_oui, build, rev, minor, major)
class ProvisioningUrl(object):
# UdpEncapsulation TLV (48)
class UdpEncapsulation(object):
# TODO: Not implemented yet
pass
def __init__(self, url):
self._url = url
class UdpEncapsulationFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Ipv6Address TLV (49)
class Ipv6Address(object):
# TODO: Not implemented yet
pass
class Ipv6AddressFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# PendingTimestamp TLV (51)
class PendingTimestamp(object):
# TODO: Not implemented yet
pass
class PendingTimestampFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# DelayTimer TLV (52)
class DelayTimer(object):
# TODO: Not implemented yet
pass
class DelayTimerFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# ChannelMask TLV (53)
class ChannelMask(object):
# TODO: Not implemented yet
pass
class ChannelMaskFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Count TLV (54)
class Count(object):
# TODO: Not implemented yet
pass
class CountFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Period TLV (55)
class Period(object):
# TODO: Not implemented yet
pass
class PeriodFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# ScanDuration TLV (56)
class ScanDuration(object):
# TODO: Not implemented yet
pass
class ScanDurationFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# EnergyList TLV (57)
class EnergyList(object):
# TODO: Not implemented yet
pass
class EnergyListFactory(object):
# TODO: Not implemented yet
def parse(self, data, message_info):
raise NotImplementedError("TODO: Not implemented yet")
# Discovery Request TLV (128)
class DiscoveryRequest(object):
def __init__(self, version, joiner_flag):
self._version = version
self._joiner_flag = joiner_flag
@property
def url(self):
return self._url
def version(self):
return self._version
def __repr__(self):
return "ProvisioningUrl(url={})".format(self.url)
class ProvisioningUrlFactory:
def parse(self, data):
url = data.decode('utf-8')
return ProvisioningUrl(url)
class VendorData(object):
def __init__(self, data):
self._vendor_data = data
@property
def vendor_data(self):
return self._vendor_data
def joiner_flag(self):
return self._joiner_flag
def __eq__(self, other):
return (type(self) is type(other)
and self.version == other.version
and self.joiner_flag == other.joiner_flag)
def __repr__(self):
return "Vendor(url={})".format(self.vendor_data)
return "DiscoveryRequest(version={}, joiner_flag={})".format(
self.version, self.joiner_flag)
class VendorDataFactory(object):
class DiscoveryRequestFactory(object):
def parse(self, data):
return VendorData(data)
def parse(self, data, message_info):
data_byte = struct.unpack(">B", data.read(1))[0]
version = (data_byte & 0xf0) >> 4
joiner_flag = (data_byte & 0x08) >> 3
return DiscoveryRequest(version, joiner_flag)
# Discovery Response TLV (128)
class DiscoveryResponse(object):
def __init__(self, version, native_flag):
self._version = version
self._native_flag = native_flag
@property
def version(self):
return self._version
@property
def native_flag(self):
return self._native_flag
def __eq__(self, other):
return (type(self) is type(other)
and self.version == other.version
and self.native_flag == other.native_flag)
def __repr__(self):
return "DiscoveryResponse(version={}, native_flag={})".format(
self.version, self.native_flag)
class DiscoveryResponseFactory(object):
def parse(self, data, message_info):
data_byte = struct.unpack(">B", data.read(1))[0]
version = (data_byte & 0xf0) >> 4
native_flag = (data_byte & 0x08) >> 3
return DiscoveryResponse(version, native_flag)
class MeshCopCommand(object):
@@ -288,15 +917,13 @@ class MeshCopCommandFactory:
length = self._get_length(data)
value = data.read(length)
factory = self._get_tlv_factory(_type)
if factory == None:
return None
return factory.parse(io.BytesIO(value))
return factory.parse(io.BytesIO(value), None) # message_info not needed here
def _get_mesh_cop_msg_type(self, msg_type_str):
tp = self._mesh_cop_msg_type_map[msg_type_str]
if tp == None:
raise RuntimeError('Mesh cop message type not found: {}'.format(msg_type_str))
return tp
try:
return self._mesh_cop_msg_type_map[msg_type_str]
except KeyError:
raise KeyError('Mesh cop message type not found: {}'.format(msg_type_str))
def parse(self, cmd_type_str, data):
cmd_type = self._get_mesh_cop_msg_type(cmd_type_str)
@@ -325,143 +952,3 @@ class ThreadDiscoveryTlvsFactory(SubTlvsFactory):
def __init__(self, sub_tlvs_factories):
super(ThreadDiscoveryTlvsFactory, self).__init__(sub_tlvs_factories)
class DiscoveryRequest(object):
def __init__(self, version, joiner_flag):
self._version = version
self._joiner_flag = joiner_flag
@property
def version(self):
return self._version
@property
def joiner_flag(self):
return self._joiner_flag
def __eq__(self, other):
return (type(self) is type(other)
and self.version == other.version
and self.joiner_flag == other.joiner_flag)
def __repr__(self):
return "DiscoveryRequest(version={}, joiner_flag={})".format(
self.version, self.joiner_flag)
class DiscoveryRequestFactory(object):
def parse(self, data, message_info):
data_byte = struct.unpack(">B", data.read(1))[0]
version = (data_byte & 0xf0) >> 4
joiner_flag = (data_byte & 0x08) >> 3
return DiscoveryRequest(version, joiner_flag)
class DiscoveryResponse(object):
def __init__(self, version, native_flag):
self._version = version
self._native_flag = native_flag
@property
def version(self):
return self._version
@property
def native_flag(self):
return self._native_flag
def __eq__(self, other):
return (type(self) is type(other)
and self.version == other.version
and self.native_flag == other.native_flag)
def __repr__(self):
return "DiscoveryResponse(version={}, native_flag={})".format(
self.version, self.native_flag)
class DiscoveryResponseFactory(object):
def parse(self, data, message_info):
data_byte = struct.unpack(">B", data.read(1))[0]
version = (data_byte & 0xf0) >> 4
native_flag = (data_byte & 0x08) >> 3
return DiscoveryResponse(version, native_flag)
class ExtendedPanid(object):
def __init__(self, extended_panid):
self._extended_panid = extended_panid
@property
def extended_panid(self):
return self._extended_panid
def __eq__(self, other):
return (type(self) is type(other)
and self.extended_panid == other.extended_panid)
def __repr__(self):
return "ExtendedPanid(extended_panid={})".format(self.extended_panid)
class ExtendedPanidFactory(object):
def parse(self, data, message_info):
extended_panid = struct.unpack(">Q", data.read(8))[0]
return ExtendedPanid(extended_panid)
class NetworkName(object):
def __init__(self, network_name):
self._network_name = network_name
@property
def network_name(self):
return self._network_name
def __eq__(self, other):
return (type(self) is type(other)
and self.network_name == other.network_name)
def __repr__(self):
return "NetworkName(network_name={})".format(self.network_name)
class NetworkNameFactory(object):
def parse(self, data, message_info):
len = message_info.length
network_name = struct.unpack("{}s".format(10), data.read(len))[0]
return NetworkName(network_name)
class JoinerUdpPort(object):
def __init__(self, udp_port):
self._udp_port = udp_port
@property
def udp_port(self):
return self._udp_port
def __eq__(self, other):
return type(self) is type(other) and self.udp_port == other.udp_port
def __repr__(self):
return "JoinerUdpPort(udp_port={})".format(self.udp_port)
class JoinerUdpPortFactory(object):
def parse(self, data, message_info):
udp_port = struct.unpack(">H", data.read(2))[0]
return JoinerUdpPort(udp_port)
+1 -138
View File
@@ -33,6 +33,7 @@ import struct
from binascii import hexlify
from enum import IntEnum
from tlvs_parsing import SubTlvsFactory
import common
@@ -47,13 +48,6 @@ class TlvType(IntEnum):
SERVER = 6
class MeshcopTlvType(IntEnum):
STEERING_DATA = 8
BORDER_AGENT_LOCATOR = 9
COMMISSIONER_SESSION_ID = 11
COMMISSIONER_UDP_PORT = 15
class NetworkData(object):
def __init__(self, stable):
@@ -64,36 +58,6 @@ class NetworkData(object):
return self._stable
class SubTlvsFactory(object):
def __init__(self, sub_tlvs_factories):
self._sub_tlvs_factories = sub_tlvs_factories
def _get_factory(self, _type):
try:
return self._sub_tlvs_factories[_type]
except KeyError:
raise RuntimeError("Could not find factory. Factory type = {}.".format(_type))
def parse(self, data, message_info):
sub_tlvs = []
while data.tell() < len(data.getvalue()):
_type = ord(data.read(1))
length = ord(data.read(1))
value = data.read(length)
factory = self._get_factory(_type)
message_info.length = length
tlv = factory.parse(io.BytesIO(value), message_info)
sub_tlvs.append(tlv)
return sub_tlvs
class NetworkDataSubTlvsFactory(SubTlvsFactory):
def parse(self, data, message_info):
@@ -434,107 +398,6 @@ class CommissioningDataFactory(object):
return CommissioningData(sub_tlvs, message_info.stable)
class SteeringData(object):
def __init__(self, bloom_filter):
self._bloom_filter = bloom_filter
@property
def bloom_filter(self):
return self._bloom_filter
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._bloom_filter == other._bloom_filter
def __repr__(self):
return "SteeringData(bloom_filter={})".format(hexlify(self._bloom_filter))
class SteeringDataFactory:
def parse(self, data, message_info):
bloom_filter = data.read(message_info.length)
return SteeringData(bloom_filter)
class BorderAgentLocator(object):
def __init__(self, address):
self._udp_port = address
@property
def udp_port(self):
return self._udp_port
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._udp_port == other._udp_port
def __repr__(self):
return "BorderAgentLocator(rloc16={})".format(hex(self._udp_port))
class BorderAgentLocatorFactory:
def parse(self, data, message_info):
border_agent_locator = struct.unpack(">H", data.read(2))[0]
return BorderAgentLocator(border_agent_locator)
class CommissionerSessionId(object):
def __init__(self, commissioner_session_id):
self._udp_port = commissioner_session_id
@property
def udp_port(self):
return self._udp_port
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._udp_port == other._udp_port
def __repr__(self):
return "CommissionerSessionId(id={})".format(hex(self._udp_port))
class CommissionerSessionIdFactory:
def parse(self, data, message_info):
session_id = struct.unpack(">H", data.read(2))[0]
return CommissionerSessionId(session_id)
class CommissionerUdpPort(object):
def __init__(self, udp_port):
self._udp_port = udp_port
@property
def udp_port(self):
return self._udp_port
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self._udp_port == other._udp_port
def __repr__(self):
return "CommissionerUdpPort(udp_port={})".format(self._udp_port)
class CommissionerUdpPortFactory:
def parse(self, data, message_info):
udp_port = struct.unpack(">H", data.read(2))[0]
return CommissionerUdpPort(udp_port)
class Service(NetworkData):
def __init__(self, t, _id, enterprise_number, service_data_length, service_data, sub_tlvs, stable):
@@ -314,22 +314,3 @@ class ThreadNetworkDataFactory(object):
tlvs = self._network_data_tlvs_factory.parse(data, message_info)
return ThreadNetworkData(tlvs)
class NetworkLayerTlvsFactory(object):
def __init__(self, tlvs_factories):
self._tlvs_factories = tlvs_factories
def parse(self, data, message_info):
tlvs = []
while data.tell() < len(data.getvalue()):
_type = ord(data.read(1))
length = ord(data.read(1))
factory = self._tlvs_factories[_type]
tlv = factory.parse(io.BytesIO(data.read(length)), message_info)
tlvs.append(tlv)
return tlvs
+12
View File
@@ -205,6 +205,12 @@ class Node:
def get_addr(self, prefix):
return self.interface.get_addr(prefix)
def get_addr_rloc(self):
return self.interface.get_addr_rloc()
def get_addr_leader_aloc(self):
return self.interface.get_addr_leader_aloc()
def get_eidcaches(self):
return self.interface.get_eidcaches()
@@ -296,5 +302,11 @@ class Node:
def coaps_get(self):
self.interface.coaps_get()
def commissioner_mgmtset(self, tlvs_binary):
self.interface.commissioner_mgmtset(tlvs_binary)
def commissioner_mgmtset_with_tlvs(self, tlvs):
self.interface.commissioner_mgmtset_with_tlvs(tlvs)
if __name__ == '__main__':
unittest.main()
+31
View File
@@ -511,6 +511,22 @@ class otCli:
return None
def get_addr_rloc(self):
addrs = self.get_addrs()
for addr in addrs:
segs = addr.split(':')
if segs[4] == '0' and segs[5] == 'ff' and segs[6] == 'fe00' and segs[7] != 'fc00':
return addr
return None
def get_addr_leader_aloc(self):
addrs = self.get_addrs()
for addr in addrs:
segs = addr.split(':')
if segs[4] == '0' and segs[5] == 'ff' and segs[6] == 'fe00' and segs[7] == 'fc00':
return addr
return None
def get_eidcaches(self):
eidcaches = []
self.send_command('eidcache')
@@ -901,3 +917,18 @@ class otCli:
timeout = 5
self._expect('Received coap secure response', timeout=timeout)
def commissioner_mgmtset(self, tlvs_binary):
cmd = 'commissioner mgmtset binary ' + tlvs_binary
self.send_command(cmd)
self._expect('Done')
def bytes_to_hex_str(self, src):
return ''.join(format(x, '02x') for x in src)
def commissioner_mgmtset_with_tlvs(self, tlvs):
payload = bytearray()
for tlv in tlvs:
payload += tlv.to_hex()
self.commissioner_mgmtset(self.bytes_to_hex_str(payload))
@@ -481,24 +481,5 @@ class TestThreadNetworkDataFactory(unittest.TestCase):
self.assertEqual(tlvs, thread_network_data.tlvs)
class TestNetworkLayerTlvsFactory(unittest.TestCase):
def test_should_create_tlv_when_parse_method_is_called(self):
# GIVEN
class DummyNetworkDataTlvsFactory:
def parse(self, data, message_info):
return bytearray(data.read())
tlv = any_tlv_data()
factory = network_layer.NetworkLayerTlvsFactory({tlv[0]: DummyNetworkDataTlvsFactory()})
# WHEN
actual_tlvs = factory.parse(io.BytesIO(tlv), None)
# THEN
self.assertEqual(tlv[2:], actual_tlvs[0])
if __name__ == "__main__":
unittest.main()
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python
#
# Copyright (c) 2019, 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 io
class SubTlvsFactory(object):
def __init__(self, sub_tlvs_factories):
self._sub_tlvs_factories = sub_tlvs_factories
def _get_factory(self, _type):
try:
return self._sub_tlvs_factories[_type]
except KeyError:
raise RuntimeError("Could not find factory. Factory type = {}.".format(_type))
def parse(self, data, message_info):
sub_tlvs = []
while data.tell() < len(data.getvalue()):
_type = ord(data.read(1))
length = ord(data.read(1))
value = data.read(length)
factory = self._get_factory(_type)
message_info.length = length
tlv = factory.parse(io.BytesIO(value), message_info)
sub_tlvs.append(tlv)
return sub_tlvs