Files
openthread/tests/scripts/thread-cert/mesh_cop.py
T
Abtin Keshavarzian 71e00eb20f [test] add missing tests in the Makefile.am & update file access mode (#6825)
This commits updates `thread-cert/Makefile.am` adding some existing
tests that were not included in the Makefile when the tests were
added. It also sorts the entries to be in alphabetical order.

This commit also updates the access control mode for all script files
to be executable by all (`chmod 755` or "-rwxr-xr-x").
2021-07-19 14:35:50 -07:00

965 lines
24 KiB
Python
Executable File

#!/usr/bin/env python3
#
# 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 binascii import hexlify
from enum import IntEnum
import io
import logging
import struct
from network_data import SubTlvsFactory
from tlvs_parsing import UnknownTlvFactory
import common
class TlvType(IntEnum):
CHANNEL = 0
PAN_ID = 1
EXTENDED_PANID = 2
NETWORK_NAME = 3
PSKC = 4
NETWORK_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
CSL_SYNCHRONIZED_TIMEOUT = 85
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_ENT_RSP = 4
def create_mesh_cop_message_type_set():
return [
MeshCopMessageType.JOIN_FIN_REQ,
MeshCopMessageType.JOIN_FIN_RSP,
MeshCopMessageType.JOIN_ENT_NTF,
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 (isinstance(self, 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 (isinstance(self, 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")
# NetworkKey TLV (5)
class NetworkKey(object):
# TODO: Not implemented yet
pass
class NetworkKeyFactory(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):
self._state = state
@property
def state(self):
return self._state
def __eq__(self, other):
return self.state == other.state
def __repr__(self):
return "State(state={})".format(self.state)
class StateFactory:
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 (isinstance(self, 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.getvalue().decode('utf-8')
return ProvisioningUrl(url)
# VendorName TLV (33)
class VendorName(object):
def __init__(self, vendor_name):
self._vendor_name = vendor_name
@property
def vendor_name(self):
return self._vendor_name
def __eq__(self, other):
return self.vendor_name == other.vendor_name
def __repr__(self):
return "VendorName(vendor_name={})".format(self.vendor_name)
class VendorNameFactory:
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):
self._vendor_model = vendor_model
@property
def vendor_model(self):
return self._vendor_model
def __eq__(self, other):
return self.vendor_model == other.vendor_model
def __repr__(self):
return "VendorModel(vendor_model={})".format(self.vendor_model)
class VendorModelFactory:
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):
self._vendor_sw_version = vendor_sw_version
@property
def vendor_sw_version(self):
return self._vendor_sw_version
def __eq__(self, other):
return self.vendor_sw_version == other.vendor_sw_version
def __repr__(self):
return "VendorName(vendor_sw_version={})".format(self.vendor_sw_version)
class VendorSWVersionFactory:
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):
def __init__(self, stack_vendor_oui, build, rev, minor, major):
self._stack_vendor_oui = stack_vendor_oui
self._build = build
self._rev = rev
self._minor = minor
self._major = major
return
@property
def stack_vendor_oui(self):
return self._stack_vendor_oui
@property
def build(self):
return self._build
@property
def rev(self):
return self._rev
@property
def minor(self):
return self._minor
@property
def major(self):
return self._major
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, 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])
rev = 0xF & rest[2]
minor = rest[3] & 0xf0
major = rest[3] & 0xF
return VendorStackVersion(stack_vendor_oui, build, rev, minor, major)
# UdpEncapsulation TLV (48)
class UdpEncapsulation(object):
# TODO: Not implemented yet
pass
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 version(self):
return self._version
@property
def joiner_flag(self):
return self._joiner_flag
def __eq__(self, other):
return (isinstance(self, 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)
# 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 (isinstance(self, 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):
def __init__(self, _type, tlvs):
self._type = _type
self._tlvs = tlvs
@property
def type(self):
return self._type
@property
def tlvs(self):
return self._tlvs
def __repr__(self):
tlvs_str = ", ".join(["{}".format(tlv) for tlv in self.tlvs])
return "MeshCopCommand(type={}, tlvs=[{}])".format(self.type, tlvs_str)
def create_deault_mesh_cop_msg_type_map():
return {
'JOIN_FIN.req': MeshCopMessageType.JOIN_FIN_REQ,
'JOIN_FIN.rsp': MeshCopMessageType.JOIN_FIN_RSP,
'JOIN_ENT.ntf': MeshCopMessageType.JOIN_ENT_NTF,
'JOIN_ENT.rsp': MeshCopMessageType.JOIN_ENT_RSP,
}
class MeshCopCommandFactory:
def __init__(self, tlvs_factories):
self._tlvs_factories = tlvs_factories
self._mesh_cop_msg_type_map = create_deault_mesh_cop_msg_type_map()
def _get_length(self, data):
return ord(data.read(1))
def _get_tlv_factory(self, _type):
try:
return self._tlvs_factories[_type]
except KeyError:
logging.error('Could not find TLV factory. Unsupported TLV type: {}'.format(_type))
return UnknownTlvFactory(_type)
def _parse_tlv(self, data):
_type = TlvType(ord(data.read(1)))
length = self._get_length(data)
value = data.read(length)
factory = self._get_tlv_factory(_type)
return factory.parse(io.BytesIO(value), None) # message_info not needed here
def _get_mesh_cop_msg_type(self, msg_type_str):
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)
tlvs = []
while data.tell() < len(data.getvalue()):
tlv = self._parse_tlv(data)
tlvs.append(tlv)
return MeshCopCommand(cmd_type, tlvs)
def create_default_mesh_cop_tlv_factories():
return {
TlvType.STATE: StateFactory(),
TlvType.PROVISIONING_URL: ProvisioningUrlFactory(),
TlvType.VENDOR_NAME: VendorNameFactory(),
TlvType.VENDOR_MODEL: VendorModelFactory(),
TlvType.VENDOR_SW_VERSION: VendorSWVersionFactory(),
TlvType.VENDOR_DATA: VendorDataFactory(),
TlvType.VENDOR_STACK_VERSION: VendorStackVersionFactory(),
}
class ThreadDiscoveryTlvsFactory(SubTlvsFactory):
def __init__(self, sub_tlvs_factories):
super(ThreadDiscoveryTlvsFactory, self).__init__(sub_tlvs_factories)