1#!/usr/bin/env python3
2#
3#  Copyright (c) 2016, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30import io
31import logging
32import struct
33
34from binascii import hexlify
35
36import common
37
38from enum import IntEnum
39from tlvs_parsing import UnknownTlvFactory
40
41
42class CommandType(IntEnum):
43    LINK_REQUEST = 0
44    LINK_ACCEPT = 1
45    LINK_ACCEPT_AND_REQUEST = 2
46    LINK_REJECT = 3
47    ADVERTISEMENT = 4
48    UPDATE = 5
49    UPDATE_REQUEST = 6
50    DATA_REQUEST = 7
51    DATA_RESPONSE = 8
52    PARENT_REQUEST = 9
53    PARENT_RESPONSE = 10
54    CHILD_ID_REQUEST = 11
55    CHILD_ID_RESPONSE = 12
56    CHILD_UPDATE_REQUEST = 13
57    CHILD_UPDATE_RESPONSE = 14
58    ANNOUNCE = 15
59    DISCOVERY_REQUEST = 16
60    DISCOVERY_RESPONSE = 17
61    LINK_METRICS_MANAGEMENT_REQUEST = 18
62    LINK_METRICS_MANAGEMENT_RESPONSE = 19
63    LINK_PROBE = 20
64    TIME_SYNC = 99
65
66
67class TlvType(IntEnum):
68    SOURCE_ADDRESS = 0
69    MODE = 1
70    TIMEOUT = 2
71    CHALLENGE = 3
72    RESPONSE = 4
73    LINK_LAYER_FRAME_COUNTER = 5
74    MLE_FRAME_COUNTER = 8
75    ROUTE64 = 9
76    ADDRESS16 = 10
77    LEADER_DATA = 11
78    NETWORK_DATA = 12
79    TLV_REQUEST = 13
80    SCAN_MASK = 14
81    CONNECTIVITY = 15
82    LINK_MARGIN = 16
83    STATUS = 17
84    VERSION = 18
85    ADDRESS_REGISTRATION = 19
86    CHANNEL = 20
87    PANID = 21
88    ACTIVE_TIMESTAMP = 22
89    PENDING_TIMESTAMP = 23
90    ACTIVE_OPERATIONAL_DATASET = 24
91    PENDING_OPERATIONAL_DATASET = 25
92    THREAD_DISCOVERY = 26
93    CSL_CHANNEL = 80
94    CSL_SYNCHRONIZED_TIMEOUT = 85
95    CSL_CLOCK_ACCURACY = 86
96    LINK_METRICS_QUERY = 87
97    LINK_METRICS_MANAGEMENT = 88
98    LINK_METRICS_REPORT = 89
99    LINK_PROBE = 90
100    TIME_REQUEST = 252
101    TIME_PARAMETER = 253
102
103
104class LinkMetricsSubTlvType(IntEnum):
105    LINK_METRICS_REPORT = 0
106    LINK_METRICS_QUERY_ID = 1
107    LINK_METRICS_QUERY_OPTIONS = 2
108    FORWARD_PROBING_REGISTRATION = 3
109    LINK_METRICS_STATUS = 5
110    ENHANCED_ACK_LINK_METRICS_CONFIGURATION = 7
111
112
113class SourceAddress(object):
114
115    def __init__(self, address):
116        self._address = address
117
118    @property
119    def address(self):
120        return self._address
121
122    def __eq__(self, other):
123        common.expect_the_same_class(self, other)
124
125        return self.address == other.address
126
127    def __repr__(self):
128        return "SourceAddress(address={})".format(hex(self._address))
129
130
131class SourceAddressFactory:
132
133    def parse(self, data, message_info):
134        address = struct.unpack(">H", data.read(2))[0]
135        return SourceAddress(address)
136
137
138class Mode(object):
139
140    def __init__(self, receiver, secure, device_type, network_data):
141        self._receiver = receiver
142        self._secure = secure
143        self._device_type = device_type
144        self._network_data = network_data
145
146    @property
147    def receiver(self):
148        return self._receiver
149
150    @property
151    def secure(self):
152        return self._secure
153
154    @property
155    def device_type(self):
156        return self._device_type
157
158    @property
159    def network_data(self):
160        return self._network_data
161
162    def __eq__(self, other):
163        common.expect_the_same_class(self, other)
164
165        return (self.receiver == other.receiver and self.secure == other.secure and
166                self.device_type == other.device_type and self.network_data == other.network_data)
167
168    def __repr__(self):
169        return "Mode(receiver={}, secure={}, device_type={}, network_data={})".format(
170            self.receiver, self.secure, self.device_type, self.network_data)
171
172
173class ModeFactory:
174
175    def parse(self, data, message_info):
176        mode = ord(data.read(1))
177        receiver = (mode >> 3) & 0x01
178        secure = (mode >> 2) & 0x01
179        device_type = (mode >> 1) & 0x01
180        network_data = (mode >> 0) & 0x01
181        return Mode(receiver, secure, device_type, network_data)
182
183
184class Timeout(object):
185
186    def __init__(self, timeout):
187        self._timeout = timeout
188
189    @property
190    def timeout(self):
191        return self._timeout
192
193    def __eq__(self, other):
194        common.expect_the_same_class(self, other)
195
196        return self.timeout == other.timeout
197
198    def __repr__(self):
199        return "Timeout(timeout={})".format(self.timeout)
200
201
202class TimeoutFactory:
203
204    def parse(self, data, message_info):
205        timeout = struct.unpack(">I", data.read(4))[0]
206        return Timeout(timeout)
207
208
209class Challenge(object):
210
211    def __init__(self, challenge):
212        self._challenge = challenge
213
214    @property
215    def challenge(self):
216        return self._challenge
217
218    def __eq__(self, other):
219        common.expect_the_same_class(self, other)
220
221        return self.challenge == other.challenge
222
223    def __repr__(self):
224        return "Challenge(challenge={})".format(hexlify(self.challenge))
225
226
227class ChallengeFactory:
228
229    def parse(self, data, message_info):
230        challenge = data.read()
231        return Challenge(challenge)
232
233
234class Response(object):
235
236    def __init__(self, response):
237        self._response = response
238
239    @property
240    def response(self):
241        return self._response
242
243    def __eq__(self, other):
244        common.expect_the_same_class(self, other)
245
246        return self.response == other.response
247
248    def __repr__(self):
249        return "Response(response={})".format(hexlify(self.response))
250
251
252class ResponseFactory:
253
254    def parse(self, data, message_info):
255        response = data.read()
256        return Response(response)
257
258
259class LinkLayerFrameCounter(object):
260
261    def __init__(self, frame_counter):
262        self._frame_counter = frame_counter
263
264    @property
265    def frame_counter(self):
266        return self._frame_counter
267
268    def __eq__(self, other):
269        common.expect_the_same_class(self, other)
270
271        return self.frame_counter == other.frame_counter
272
273    def __repr__(self):
274        return "LinkLayerFrameCounter(frame_counter={})".format(self.frame_counter)
275
276
277class LinkLayerFrameCounterFactory:
278
279    def parse(self, data, message_info):
280        frame_counter = struct.unpack(">I", data.read(4))[0]
281        return LinkLayerFrameCounter(frame_counter)
282
283
284class MleFrameCounter(object):
285
286    def __init__(self, frame_counter):
287        self._frame_counter = frame_counter
288
289    @property
290    def frame_counter(self):
291        return self._frame_counter
292
293    def __eq__(self, other):
294        common.expect_the_same_class(self, other)
295
296        return self.frame_counter == other.frame_counter
297
298    def __repr__(self):
299        return "MleFrameCounter(frame_counter={})".format(self.frame_counter)
300
301
302class MleFrameCounterFactory:
303
304    def parse(self, data, message_info):
305        frame_counter = struct.unpack(">I", data.read(4))[0]
306        return MleFrameCounter(frame_counter)
307
308
309class LinkQualityAndRouteData(object):
310
311    def __init__(self, output, _input, route):
312        self._output = output
313        self._input = _input
314        self._route = route
315
316    @property
317    def output(self):
318        return self._output
319
320    @property
321    def input(self):
322        return self._input
323
324    @property
325    def route(self):
326        return self._route
327
328    def __eq__(self, other):
329        common.expect_the_same_class(self, other)
330
331        return (self.output == other.output and self.input == other.input and self.route == other.route)
332
333    def __repr__(self):
334        return "LinkQualityAndRouteData(ouput={}, input={}, route={})".format(self.output, self.input, self.route)
335
336
337class LinkQualityAndRouteDataFactory:
338
339    def parse(self, data, message_info):
340        lqrd = ord(data.read(1))
341        output = (lqrd >> 6) & 0x3
342        _input = (lqrd >> 4) & 0x3
343        route = lqrd & 0x0F
344        return LinkQualityAndRouteData(output, _input, route)
345
346
347class Route64(object):
348
349    def __init__(self, id_sequence, router_id_mask, link_quality_and_route_data):
350        self._id_sequence = id_sequence
351        self._router_id_mask = router_id_mask
352        self._link_quality_and_route_data = link_quality_and_route_data
353
354    @property
355    def id_sequence(self):
356        return self._id_sequence
357
358    @property
359    def router_id_mask(self):
360        return self._router_id_mask
361
362    @property
363    def link_quality_and_route_data(self):
364        return self._link_quality_and_route_data
365
366    def __eq__(self, other):
367        common.expect_the_same_class(self, other)
368
369        return (self.id_sequence == other.id_sequence and self.router_id_mask == other.router_id_mask and
370                self.link_quality_and_route_data == other.link_quality_and_route_data)
371
372    def __repr__(self):
373        lqrd_str = ", ".join(["{}".format(lqrd) for lqrd in self.link_quality_and_route_data])
374        return "Route64(id_sequence={}, router_id_mask={}, link_quality_and_route_data=[{}])".format(
375            self.id_sequence, hex(self.router_id_mask), lqrd_str)
376
377
378class Route64Factory:
379
380    def __init__(self, link_quality_and_route_data_factory):
381        self._lqrd_factory = link_quality_and_route_data_factory
382
383    def parse(self, data, message_info):
384        id_sequence = ord(data.read(1))
385        router_id_mask = struct.unpack(">Q", data.read(8))[0]
386
387        link_quality_and_route_data = []
388
389        while data.tell() < len(data.getvalue()):
390            link_quality_and_route_data.append(self._lqrd_factory.parse(data, message_info))
391
392        return Route64(id_sequence, router_id_mask, link_quality_and_route_data)
393
394
395class Address16(object):
396
397    def __init__(self, address):
398        self._address = address
399
400    @property
401    def address(self):
402        return self._address
403
404    def __eq__(self, other):
405        common.expect_the_same_class(self, other)
406
407        return self.address == other.address
408
409    def __repr__(self):
410        return "Address16(address={})".format(hex(self.address))
411
412
413class Address16Factory:
414
415    def parse(self, data, message_info):
416        address = struct.unpack(">H", data.read(2))[0]
417        return Address16(address)
418
419
420class LeaderData(object):
421
422    def __init__(
423        self,
424        partition_id,
425        weighting,
426        data_version,
427        stable_data_version,
428        leader_router_id,
429    ):
430        self._partition_id = partition_id
431        self._weighting = weighting
432        self._data_version = data_version
433        self._stable_data_version = stable_data_version
434        self._leader_router_id = leader_router_id
435
436    @property
437    def partition_id(self):
438        return self._partition_id
439
440    @property
441    def weighting(self):
442        return self._weighting
443
444    @property
445    def data_version(self):
446        return self._data_version
447
448    @property
449    def stable_data_version(self):
450        return self._stable_data_version
451
452    @property
453    def leader_router_id(self):
454        return self._leader_router_id
455
456    def __eq__(self, other):
457        common.expect_the_same_class(self, other)
458
459        return (self.partition_id == other.partition_id and self.weighting == other.weighting and
460                self.data_version == other.data_version and self.stable_data_version == other.stable_data_version and
461                self.leader_router_id == other.leader_router_id)
462
463    def __repr__(self):
464        return 'LeaderData(partition_id={}, weighting={}, data_version={}, stable_data_version={},leader_router_id={}'.format(
465            self.partition_id,
466            self.weighting,
467            self.data_version,
468            self.stable_data_version,
469            self.leader_router_id,
470        )
471
472
473class LeaderDataFactory:
474
475    def parse(self, data, message_info):
476        partition_id = struct.unpack(">I", data.read(4))[0]
477        weighting = ord(data.read(1))
478        data_version = ord(data.read(1))
479        stable_data_version = ord(data.read(1))
480        leader_router_id = ord(data.read(1))
481        return LeaderData(
482            partition_id,
483            weighting,
484            data_version,
485            stable_data_version,
486            leader_router_id,
487        )
488
489
490class NetworkData(object):
491
492    def __init__(self, tlvs):
493        self._tlvs = tlvs
494
495    @property
496    def tlvs(self):
497        return self._tlvs
498
499    def __eq__(self, other):
500        common.expect_the_same_class(self, other)
501
502        return self.tlvs == other.tlvs
503
504    def __repr__(self):
505        tlvs_str = ", ".join(["{}".format(tlv) for tlv in self.tlvs])
506        return "NetworkData(tlvs=[{}])".format(tlvs_str)
507
508
509class NetworkDataFactory:
510
511    def __init__(self, network_data_tlvs_factory):
512        self._tlvs_factory = network_data_tlvs_factory
513
514    def parse(self, data, message_info):
515        tlvs = self._tlvs_factory.parse(data, message_info)
516        return NetworkData(tlvs)
517
518
519class TlvRequest(object):
520
521    def __init__(self, tlvs):
522        self._tlvs = tlvs
523
524    @property
525    def tlvs(self):
526        return self._tlvs
527
528    def __eq__(self, other):
529        common.expect_the_same_class(self, other)
530
531        return self.tlvs == other.tlvs
532
533    def __repr__(self):
534        tlvs_str = ", ".join(["{}".format(tlv) for tlv in self.tlvs])
535        return "TlvRequest(tlvs=[{}])".format(tlvs_str)
536
537
538class TlvRequestFactory:
539
540    def parse(self, data, message_info):
541        tlvs = [b for b in bytearray(data.read())]
542        return TlvRequest(tlvs)
543
544
545class ScanMask(object):
546
547    def __init__(self, router, end_device):
548        self._router = router
549        self._end_device = end_device
550
551    @property
552    def router(self):
553        return self._router
554
555    @property
556    def end_device(self):
557        return self._end_device
558
559    def __eq__(self, other):
560        common.expect_the_same_class(self, other)
561
562        return (self.router == other.router and self.end_device == other.end_device)
563
564    def __repr__(self):
565        return "ScanMask(router={}, end_device={})".format(self.router, self.end_device)
566
567
568class ScanMaskFactory:
569
570    def parse(self, data, message_info):
571        scan_mask = ord(data.read(1))
572        router = (scan_mask >> 7) & 0x01
573        end_device = (scan_mask >> 6) & 0x01
574        return ScanMask(router, end_device)
575
576
577class Connectivity(object):
578
579    def __init__(
580        self,
581        pp_byte,
582        link_quality_3,
583        link_quality_2,
584        link_quality_1,
585        leader_cost,
586        id_sequence,
587        active_routers,
588        sed_buffer_size=None,
589        sed_datagram_count=None,
590    ):
591        self._pp_byte = pp_byte
592        self._link_quality_3 = link_quality_3
593        self._link_quality_2 = link_quality_2
594        self._link_quality_1 = link_quality_1
595        self._leader_cost = leader_cost
596        self._id_sequence = id_sequence
597        self._active_routers = active_routers
598        self._sed_buffer_size = sed_buffer_size
599        self._sed_datagram_count = sed_datagram_count
600
601    @property
602    def pp_byte(self):
603        return self._pp_byte
604
605    @property
606    def pp(self):
607        return common.map_pp(self._pp_byte)
608
609    @property
610    def link_quality_3(self):
611        return self._link_quality_3
612
613    @property
614    def link_quality_2(self):
615        return self._link_quality_2
616
617    @property
618    def link_quality_1(self):
619        return self._link_quality_1
620
621    @property
622    def leader_cost(self):
623        return self._leader_cost
624
625    @property
626    def id_sequence(self):
627        return self._id_sequence
628
629    @property
630    def active_routers(self):
631        return self._active_routers
632
633    @property
634    def sed_buffer_size(self):
635        return self._sed_buffer_size
636
637    @property
638    def sed_datagram_count(self):
639        return self._sed_datagram_count
640
641    def __eq__(self, other):
642        common.expect_the_same_class(self, other)
643
644        return (self.pp == other.pp and self.link_quality_3 == other.link_quality_3 and
645                self.link_quality_2 == other.link_quality_2 and self.link_quality_1 == other.link_quality_1 and
646                self.leader_cost == other.leader_cost and self.id_sequence == other.id_sequence and
647                self.active_routers == other.active_routers and self.sed_buffer_size == other.sed_buffer_size and
648                self.sed_datagram_count == other.sed_datagram_count)
649
650    def __repr__(self):
651        return r"Connectivity(pp={}, \
652                 link_quality_3={}, \
653                 link_quality_2={}, \
654                 link_quality_1={}, \
655                 leader_cost={}, \
656                 id_sequence={}, \
657                 active_routers={}, \
658                 sed_buffer_size={}, \
659                 sed_datagram_count={})".format(
660            self.pp,
661            self.link_quality_3,
662            self.link_quality_2,
663            self.link_quality_1,
664            self.leader_cost,
665            self.id_sequence,
666            self.active_routers,
667            self.sed_buffer_size,
668            self.sed_datagram_count,
669        )
670
671
672class ConnectivityFactory:
673
674    def parse(self, data, message_info):
675        pp_byte = ord(data.read(1))
676        link_quality_3 = ord(data.read(1))
677        link_quality_2 = ord(data.read(1))
678        link_quality_1 = ord(data.read(1))
679        leader_cost = ord(data.read(1))
680        id_sequence = ord(data.read(1))
681        active_routers = ord(data.read(1))
682
683        sed_data = io.BytesIO(data.read(3))
684
685        if len(sed_data.getvalue()) > 0:
686            sed_buffer_size = struct.unpack(">H", sed_data.read(2))[0]
687            sed_datagram_count = ord(sed_data.read(1))
688        else:
689            sed_buffer_size = None
690            sed_datagram_count = None
691
692        return Connectivity(
693            pp_byte,
694            link_quality_3,
695            link_quality_2,
696            link_quality_1,
697            leader_cost,
698            id_sequence,
699            active_routers,
700            sed_buffer_size,
701            sed_datagram_count,
702        )
703
704
705class LinkMargin(object):
706
707    def __init__(self, link_margin):
708        self._link_margin = link_margin
709
710    @property
711    def link_margin(self):
712        return self._link_margin
713
714    def __eq__(self, other):
715        common.expect_the_same_class(self, other)
716
717        return self.link_margin == other.link_margin
718
719    def __repr__(self):
720        return "LinkMargin(link_margin={})".format(self.link_margin)
721
722
723class LinkMarginFactory:
724
725    def parse(self, data, message_info):
726        link_margin = ord(data.read(1))
727        return LinkMargin(link_margin)
728
729
730class Status(object):
731
732    def __init__(self, status):
733        self._status = status
734
735    @property
736    def status(self):
737        return self._status
738
739    def __eq__(self, other):
740        common.expect_the_same_class(self, other)
741
742        return self.status == other.status
743
744    def __repr__(self):
745        return "Status(status={})".format(self.status)
746
747
748class StatusFactory:
749
750    def parse(self, data, message_info):
751        status = ord(data.read(1))
752        return Status(status)
753
754
755class Version(object):
756
757    def __init__(self, version):
758        self._version = version
759
760    @property
761    def version(self):
762        return self._version
763
764    def __eq__(self, other):
765        common.expect_the_same_class(self, other)
766
767        return self.version == other.version
768
769    def __repr__(self):
770        return "Version(version={})".format(self.version)
771
772
773class VersionFactory:
774
775    def parse(self, data, message_info):
776        version = struct.unpack(">H", data.read(2))[0]
777        return Version(version)
778
779
780class AddressFull(object):
781
782    def __init__(self, ipv6_address):
783        self._ipv6_address = ipv6_address
784
785    @property
786    def ipv6_address(self):
787        return self._ipv6_address
788
789    def __eq__(self, other):
790        common.expect_the_same_class(self, other)
791
792        return self.ipv6_address == other.ipv6_address
793
794    def __repr__(self):
795        return "AddressFull(ipv6_address={}')".format(hexlify(self.ipv6_address))
796
797
798class AddressFullFactory:
799
800    def parse(self, data, message_info):
801        data.read(1)  # first byte is ignored
802        ipv6_address = data.read(16)
803        return AddressFull(ipv6_address)
804
805
806class AddressCompressed(object):
807
808    def __init__(self, cid, iid):
809        self._cid = cid
810        self._iid = iid
811
812    @property
813    def cid(self):
814        return self._cid
815
816    @property
817    def iid(self):
818        return self._iid
819
820    def __eq__(self, other):
821        common.expect_the_same_class(self, other)
822
823        return self.cid == other.cid and self.iid == other.iid
824
825    def __repr__(self):
826        return "AddressCompressed(cid={}, iid={}')".format(self.cid, hexlify(self.iid))
827
828
829class AddressCompressedFactory:
830
831    def parse(self, data, message_info):
832        cid = ord(data.read(1)) & 0x0F
833        iid = bytearray(data.read(8))
834        return AddressCompressed(cid, iid)
835
836
837class AddressRegistration(object):
838
839    def __init__(self, addresses):
840        self._addresses = addresses
841
842    @property
843    def addresses(self):
844        return self._addresses
845
846    def __eq__(self, other):
847        common.expect_the_same_class(self, other)
848
849        return self.addresses == other.addresses
850
851    def __repr__(self):
852        addresses_str = ", ".join(["{}".format(address) for address in self.addresses])
853        return "AddressRegistration(addresses=[{}])".format(addresses_str)
854
855
856class AddressRegistrationFactory:
857
858    def __init__(self, addr_compressed_factory, addr_full_factory):
859        self._addr_compressed_factory = addr_compressed_factory
860        self._addr_full_factory = addr_full_factory
861
862    def parse(self, data, message_info):
863        addresses = []
864
865        while data.tell() < len(data.getvalue()):
866            compressed = (ord(data.read(1)) >> 7) & 0x01
867            data.seek(-1, io.SEEK_CUR)
868
869            if compressed:
870                addresses.append(self._addr_compressed_factory.parse(data, message_info))
871            else:
872                addresses.append(self._addr_full_factory.parse(data, message_info))
873
874        return AddressRegistration(addresses)
875
876
877class Channel(object):
878
879    def __init__(self, channel_page, channel):
880        self._channel_page = channel_page
881        self._channel = channel
882
883    @property
884    def channel_page(self):
885        return self._channel_page
886
887    @property
888    def channel(self):
889        return self._channel
890
891    def __eq__(self, other):
892        common.expect_the_same_class(self, other)
893
894        return (self.channel_page == other.channel_page and self.channel == other.channel)
895
896    def __repr__(self):
897        return "Channel(channel_page={}, channel={})".format(self.channel_page, self.channel)
898
899
900class ChannelFactory:
901
902    def parse(self, data, message_info):
903        channel_page = ord(data.read(1))
904        channel = struct.unpack(">H", data.read(2))[0]
905        return Channel(channel_page, channel)
906
907
908class PanId:
909
910    def __init__(self, pan_id):
911        self._pan_id = pan_id
912
913    @property
914    def pan_id(self):
915        return self._pan_id
916
917    def __eq__(self, other):
918        common.expect_the_same_class(self, other)
919
920        return self.pan_id == other.pan_id
921
922    def __repr__(self):
923        return "PanId(pan_id={})".format(self.pan_id)
924
925
926class PanIdFactory:
927
928    def parse(self, data, message_info):
929        pan_id = struct.unpack(">H", data.read(2))[0]
930        return PanId(pan_id)
931
932
933class ActiveTimestamp(object):
934
935    def __init__(self, timestamp_seconds, timestamp_ticks, u):
936        self._timestamp_seconds = timestamp_seconds
937        self._timestamp_ticks = timestamp_ticks
938        self._u = u
939
940    @property
941    def timestamp_seconds(self):
942        return self._timestamp_seconds
943
944    @property
945    def timestamp_ticks(self):
946        return self._timestamp_ticks
947
948    @property
949    def u(self):
950        return self._u
951
952    def __eq__(self, other):
953        common.expect_the_same_class(self, other)
954
955        return (self.timestamp_seconds == other.timestamp_seconds and self.timestamp_ticks == other.timestamp_ticks and
956                self.u == other.u)
957
958    def __repr__(self):
959        return "ActiveTimestamp(timestamp_seconds={}, timestamp_ticks={}, u={})".format(
960            self.timestamp_seconds, self.timestamp_ticks, self.u)
961
962
963class ActiveTimestampFactory:
964
965    def parse(self, data, message_info):
966        seconds = bytearray([0x00, 0x00]) + bytearray(data.read(6))
967        ticks = struct.unpack(">H", data.read(2))[0]
968
969        timestamp_seconds = struct.unpack(">Q", bytes(seconds))[0]
970        timestamp_ticks = ticks >> 1
971        u = ticks & 0x01
972        return ActiveTimestamp(timestamp_seconds, timestamp_ticks, u)
973
974
975class PendingTimestamp(object):
976
977    def __init__(self, timestamp_seconds, timestamp_ticks, u):
978        self._timestamp_seconds = timestamp_seconds
979        self._timestamp_ticks = timestamp_ticks
980        self._u = u
981
982    @property
983    def timestamp_seconds(self):
984        return self._timestamp_seconds
985
986    @property
987    def timestamp_ticks(self):
988        return self._timestamp_ticks
989
990    @property
991    def u(self):
992        return self._u
993
994    def __eq__(self, other):
995        common.expect_the_same_class(self, other)
996
997        return (self.timestamp_seconds == other.timestamp_seconds and self.timestamp_ticks == other.timestamp_ticks and
998                self.u == other.u)
999
1000    def __repr__(self):
1001        return "PendingTimestamp(timestamp_seconds={}, timestamp_ticks={}, u={})".format(
1002            self.timestamp_seconds, self.timestamp_ticks, self.u)
1003
1004
1005class PendingTimestampFactory:
1006
1007    def parse(self, data, message_info):
1008        seconds = bytearray([0x00, 0x00]) + bytearray(data.read(6))
1009        ticks = struct.unpack(">H", data.read(2))[0]
1010
1011        timestamp_seconds = struct.unpack(">Q", bytes(seconds))[0]
1012        timestamp_ticks = ticks >> 1
1013        u = ticks & 0x01
1014        return PendingTimestamp(timestamp_seconds, timestamp_ticks, u)
1015
1016
1017class ActiveOperationalDataset:
1018    # TODO: Not implemented yet
1019
1020    def __init__(self):
1021        print("ActiveOperationalDataset is not implemented yet.")
1022
1023
1024class ActiveOperationalDatasetFactory:
1025
1026    def parse(self, data, message_info):
1027        return ActiveOperationalDataset()
1028
1029
1030class PendingOperationalDataset:
1031    # TODO: Not implemented yet
1032
1033    def __init__(self):
1034        print("PendingOperationalDataset is not implemented yet.")
1035
1036
1037class PendingOperationalDatasetFactory:
1038
1039    def parse(self, data, message_info):
1040        return PendingOperationalDataset()
1041
1042
1043class ThreadDiscovery(object):
1044
1045    def __init__(self, tlvs):
1046        self._tlvs = tlvs
1047
1048    @property
1049    def tlvs(self):
1050        return self._tlvs
1051
1052    def __eq__(self, other):
1053        return self.tlvs == other.tlvs
1054
1055    def __repr__(self):
1056        return "ThreadDiscovery(tlvs={})".format(self.tlvs)
1057
1058
1059class ThreadDiscoveryFactory:
1060
1061    def __init__(self, thread_discovery_tlvs_factory):
1062        self._tlvs_factory = thread_discovery_tlvs_factory
1063
1064    def parse(self, data, message_info):
1065        tlvs = self._tlvs_factory.parse(data, message_info)
1066        return ThreadDiscovery(tlvs)
1067
1068
1069class CslChannel:
1070    # TODO: Not implemented yet
1071
1072    def __init__(self):
1073        print("CslChannel is not implemented yet.")
1074
1075
1076class CslChannelFactory:
1077    # TODO: Not implemented yet
1078
1079    def parse(self, data, message_info):
1080        return CslChannel()
1081
1082
1083class CslSynchronizedTimeout:
1084    # TODO: Not implemented yet
1085
1086    def __init__(self):
1087        print("CslSynchronizedTimeout is not implemented yet.")
1088
1089
1090class CslSynchronizedTimeoutFactory:
1091
1092    def parse(self, data, message_info):
1093        return CslSynchronizedTimeout()
1094
1095
1096class CslClockAccuracy:
1097    # TODO: Not implemented yet
1098
1099    def __init__(self):
1100        print("CslClockAccuracy is not implemented yet.")
1101
1102
1103class CslClockAccuracyFactory:
1104
1105    def parse(self, data, message_info):
1106        return CslClockAccuracy()
1107
1108
1109class TimeRequest:
1110    # TODO: Not implemented yet
1111
1112    def __init__(self):
1113        print("TimeRequest is not implemented yet.")
1114
1115
1116class TimeRequestFactory:
1117
1118    def parse(self, data, message_info):
1119        return TimeRequest()
1120
1121
1122class TimeParameter:
1123    # TODO: Not implemented yet
1124
1125    def __init__(self):
1126        print("TimeParameter is not implemented yet.")
1127
1128
1129class TimeParameterFactory:
1130
1131    def parse(self, data, message_info):
1132        return TimeParameter()
1133
1134
1135class LinkMetricsQuery:
1136    # TODO: Not implemented yet
1137
1138    def __init__(self):
1139        print("LinkMetricsQuery is not implemented yet.")
1140
1141
1142class LinkMetricsQueryFactory:
1143
1144    def parse(self, data, message_info):
1145        return LinkMetricsQuery()
1146
1147
1148class LinkMetricsManagement:
1149    # TODO: Not implemented yet
1150
1151    def __init__(self):
1152        print("LinkMetricsManagement is not implemented yet.")
1153
1154
1155class LinkMetricsManagementFactory:
1156
1157    def parse(self, data, message_info):
1158        return LinkMetricsManagement()
1159
1160
1161class LinkMetricsReport:
1162    # TODO: Not implemented yet
1163
1164    def __init__(self):
1165        print("LinkMetricsReport is not implemented yet.")
1166
1167
1168class LinkMetricsReportFactory:
1169
1170    def parse(self, data, message_info):
1171        return LinkMetricsReport()
1172
1173
1174class LinkProbe:
1175    # TODO: Not implemented yet
1176
1177    def __init__(self):
1178        print("LinkProbe is not implemented yet.")
1179
1180
1181class LinkProbeFactory:
1182
1183    def parse(self, data, message_info):
1184        return LinkProbe()
1185
1186
1187class MleCommand(object):
1188
1189    def __init__(self, _type, tlvs):
1190        self._type = _type
1191        self._tlvs = tlvs
1192
1193    @property
1194    def type(self):
1195        return self._type
1196
1197    @property
1198    def tlvs(self):
1199        return self._tlvs
1200
1201    def __repr__(self):
1202        tlvs_str = ", ".join(["{}".format(tlv) for tlv in self.tlvs])
1203        return "MleCommand(type={}, tlvs=[{}])".format(self.type.name, tlvs_str)
1204
1205
1206class MleCommandFactory:
1207
1208    _MARKER_EXTENDED_LENGTH = 0xff
1209
1210    def __init__(self, tlvs_factories):
1211        self._tlvs_factories = tlvs_factories
1212
1213    def _get_length(self, data):
1214        length = ord(data.read(1))
1215
1216        if length == self._MARKER_EXTENDED_LENGTH:
1217            length = struct.unpack(">H", data.read(2))[0]
1218
1219        return length
1220
1221    def _get_tlv_factory(self, _type):
1222        try:
1223            return self._tlvs_factories[_type]
1224        except KeyError:
1225            logging.error('Could not find TLV factory. Unsupported TLV type: {}'.format(_type))
1226            return UnknownTlvFactory(_type)
1227
1228    def _parse_tlv(self, data, message_info):
1229        _type = TlvType(ord(data.read(1)))
1230        length = self._get_length(data)
1231        value = data.read(length)
1232
1233        factory = self._get_tlv_factory(_type)
1234
1235        return factory.parse(io.BytesIO(value), message_info)
1236
1237    def parse(self, data, message_info):
1238        cmd_type = CommandType(ord(data.read(1)))
1239        tlvs = []
1240
1241        while data.tell() < len(data.getvalue()):
1242            tlv = self._parse_tlv(data, message_info)
1243            tlvs.append(tlv)
1244
1245        return MleCommand(cmd_type, tlvs)
1246
1247
1248class MleMessage(object):
1249
1250    def __init__(self, command):
1251        self._command = command
1252
1253    @property
1254    def command(self):
1255        return self._command
1256
1257    def __repr__(self):
1258        return "MleMessage(command={})".format(self.command)
1259
1260
1261class MleMessageSecured(MleMessage):
1262
1263    def __init__(self, aux_sec_hdr, command, mic):
1264        super(MleMessageSecured, self).__init__(command)
1265        self._aux_sec_hdr = aux_sec_hdr
1266        self._mic = mic
1267
1268    @property
1269    def aux_sec_hdr(self):
1270        return self._aux_sec_hdr
1271
1272    @property
1273    def mic(self):
1274        return self._mic
1275
1276    def __repr__(self):
1277        return "MleMessageSecured(aux_sec_hdr={}, command={}, mic=\"{}\")".format(self.aux_sec_hdr, self.command,
1278                                                                                  hexlify(self.mic))
1279
1280
1281class MleMessageFactory:
1282
1283    def __init__(self, aux_sec_hdr_factory, mle_command_factory, crypto_engine):
1284        self._aux_sec_hdr_factory = aux_sec_hdr_factory
1285        self._mle_command_factory = mle_command_factory
1286        self._crypto_engine = crypto_engine
1287
1288    def _create_mle_secured_message(self, data, message_info):
1289        aux_sec_hdr = self._aux_sec_hdr_factory.parse(data, message_info)
1290
1291        enc_data_length = len(data.getvalue())
1292
1293        enc_data = bytearray(data.read(enc_data_length - data.tell() - self._crypto_engine.mic_length))
1294        mic = bytearray(data.read())
1295
1296        dec_data = self._crypto_engine.decrypt(enc_data, mic, message_info)
1297
1298        command = self._mle_command_factory.parse(io.BytesIO(dec_data), message_info)
1299
1300        return MleMessageSecured(aux_sec_hdr, command, mic)
1301
1302    def _create_mle_message(self, data, message_info):
1303        command = self._mle_command_factory.parse(data, message_info)
1304
1305        return MleMessage(command)
1306
1307    def parse(self, data, message_info):
1308        security_indicator = ord(data.read(1))
1309
1310        if security_indicator == 0:
1311            return self._create_mle_secured_message(data, message_info)
1312
1313        elif security_indicator == 255:
1314            return self._create_mle_message(data, message_info)
1315
1316        else:
1317            raise RuntimeError(
1318                "Could not create MLE message. Unknown security indicator value: {}".format(security_indicator))
1319