[tests] support testing commissioning (#3441)

Add support for testing commissioning process, including:

1. Add DTLS, Thread Discovery and commissioning messages(JOIN_FIN.req etc) parsing;
2. Support parsing log from UART response, and construct decrypted messages;
3. Common commands for commissioning related verification;
This commit is contained in:
wgtdkp
2019-02-26 15:49:57 -08:00
committed by Jonathan Hui
parent cc37bd1dfb
commit dc947f0152
13 changed files with 1420 additions and 47 deletions
+72 -5
View File
@@ -34,6 +34,7 @@ import sys
import coap
import common
import dtls
import ipv6
import lowpan
import mac802154
@@ -49,6 +50,7 @@ class MessageType(IntEnum):
BEACON = 4
DATA = 5
COMMAND = 6
DTLS = 7
class Message(object):
@@ -61,6 +63,7 @@ class Message(object):
self._coap = None
self._mle = None
self._icmp = None
self._dtls = None
def _extract_udp_datagram(self, udp_datagram):
if isinstance(udp_datagram.payload, mle.MleMessage):
@@ -71,6 +74,11 @@ class Message(object):
self._type = MessageType.COAP
self._coap = udp_datagram.payload
# DTLS message factory returns a list of messages
elif isinstance(udp_datagram.payload, list):
self._type = MessageType.DTLS
self._dtls = udp_datagram.payload
def _extract_upper_layer_protocol(self, upper_layer_protocol):
if isinstance(upper_layer_protocol, ipv6.ICMPv6):
self._type = MessageType.ICMP
@@ -79,6 +87,32 @@ class Message(object):
elif isinstance(upper_layer_protocol, ipv6.UDPDatagram):
self._extract_udp_datagram(upper_layer_protocol)
def try_extract_dtls_messages(self):
"""Extract multiple dtls messages that are sent in a single UDP datagram
"""
if self.type != MessageType.DTLS:
return [self.clone()]
assert isinstance(self.dtls, list)
ret = []
for dtls in self.dtls:
msg = self.clone()
msg._dtls = dtls
ret.append(msg)
return ret
def clone(self):
msg = Message()
msg._type = self.type
msg._channel = self.channel
msg._mac_header = self.mac_header
msg._ipv6_packet = self.ipv6_packet
msg._coap = self.coap
msg._mle = self.mle
msg._icmp = self.icmp
msg._dtls = self.dtls
return msg
@property
def type(self):
return self._type
@@ -141,6 +175,10 @@ class Message(object):
def icmp(self, value):
self._icmp = value
@property
def dtls(self):
return self._dtls
def get_mle_message_tlv(self, tlv_class_type):
if self.type != MessageType.MLE:
raise ValueError("Invalid message type. Expected MLE message.")
@@ -306,19 +344,30 @@ class Message(object):
def isMacAddressTypeLong(self):
return self.mac_header.dest_address.type == common.MacAddressType.LONG
def get_dst_udp_port(self):
assert isinstance(self.ipv6_packet.upper_layer_protocol, ipv6.UDPDatagram)
return self.ipv6_packet.upper_layer_protocol.header.dst_port
def __repr__(self):
if self.type == MessageType.DTLS and self.dtls.content_type == dtls.ContentType.HANDSHAKE:
return "Message(type={})".format(str(self.dtls.handshake_type))
return "Message(type={})".format(MessageType(self.type).name)
class MessagesSet(object):
def __init__(self, messages):
def __init__(self, messages, commissioning_messages=[]):
self._messages = messages
self._commissioning_messages = commissioning_messages
@property
def messages(self):
return self._messages
@property
def commissioning_messages(self):
return self._commissioning_messages
def next_coap_message(self, code, uri_path=None, assert_enabled=True):
message = None
@@ -433,6 +482,21 @@ class MessagesSet(object):
def next_command_message(self):
return self.next_message_of(MessageType.COMMAND)
def next_dtls_message(self, content_type, handshake_type=None):
while self.messages:
msg = self.messages.pop(0)
if msg.type != MessageType.DTLS:
continue
if msg.dtls.content_type != content_type:
continue
if (content_type == dtls.ContentType.HANDSHAKE and
msg.dtls.handshake_type != handshake_type):
continue
return msg
t = handshake_type if content_type == dtls.ContentType.HANDSHAKE else content_type
raise ValueError("Could not find DTLS message of type: {}".format(str(t)))
def contains_icmp_message(self):
for m in self.messages:
if m.type == MessageType.ICMP:
@@ -472,7 +536,10 @@ class MessagesSet(object):
def clone(self):
"""Make a copy of current MessageSet.
"""
return MessagesSet(self.messages[:])
return MessagesSet(self.messages[:], self.commissioning_messages[:])
def __repr__(self):
return str(self.messages)
class MessageFactory:
@@ -506,7 +573,7 @@ class MessageFactory:
message.mac_header = mac_frame.header
if message.mac_header.frame_type != mac802154.MacHeader.FrameType.DATA:
return message
return [message]
message_info = common.MessageInfo()
message_info.source_mac_address = message.mac_header.src_address
@@ -517,11 +584,11 @@ class MessageFactory:
ipv6_packet = self._lowpan_parser.parse(lowpan_payload, message_info)
if ipv6_packet is None:
return message
return [message]
message.ipv6_packet = ipv6_packet
if message.type == MessageType.MLE:
self._add_device_descriptors(message)
return message
return message.try_extract_dtls_messages()