blob: 5cdf1e6284d797780e6a36a717bf34d3e4d0c887 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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 struct
from enum import IntEnum
from typing import List
import common
import ipaddress
import mle
class TlvType(IntEnum):
EXT_ADDRESS = 0
ADDRESS16 = 1
MODE = 2
POLLING_PERIOD = 3
CONNECTIVITY = 4
ROUTE64 = 5
LEADER_DATA = 6
NETWORK_DATA = 7
IPV6_ADDRESS_LIST = 8
MAC_COUNTERS = 9
BATTERY_LEVEL = 14
SUPPLY_VOLTAGE = 15
CHILD_TABLE = 16
CHANNEL_PAGES = 17
TYPE_LIST = 18
MAX_CHILD_TIMEOUT = 19
class Ipv6AddressList:
def __init__(self, addresses: List[ipaddress.IPv6Address]):
self._addresses = addresses
@property
def addresses(self):
return self._addresses
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.addresses == other.addresses
def __repr__(self):
return f'Ipv6AddressList({self.addresses})'
class Ipv6AddressListFactory:
def parse(self, data, message_info):
addresses = []
while data.tell() < message_info.length:
addresses.append(ipaddress.IPv6Address(data.read(16)))
return Ipv6AddressList(addresses)
class MacCounters:
def __init__(self, counters: List[int]):
self._counters = counters
@property
def if_in_unknown_protos(self):
return self._counters[0]
@property
def if_in_errors(self):
return self._counters[1]
@property
def if_out_errors(self):
return self._counters[2]
@property
def if_in_ucast_pkts(self):
return self._counters[3]
@property
def if_in_broadcast_pkts(self):
return self._counters[4]
@property
def if_in_discards(self):
return self._counters[5]
@property
def if_out_ucast_pkts(self):
return self._counters[6]
@property
def if_out_broadcast_pkts(self):
return self._counters[7]
@property
def if_out_discards(self):
return self._counters[8]
@property
def counters(self):
return self._counters
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.counters == other.counters
def __repr__(self):
return ('MacCounters(' +
f'if_in_unknown_protos={self.if_in_unknown_protos}, ' +
f'if_in_errors={self.if_in_errors}, ' +
f'if_out_errors={self.if_out_errors}, ' +
f'if_in_ucast_pkts={self.if_in_ucast_pkts}, ' +
f'if_in_broadcast_pkts={self.if_in_broadcast_pkts}, ' +
f'if_in_discards={self.if_in_discards}, ' +
f'if_out_ucast_pkts={self.if_out_ucast_pkts}, ' +
f'if_out_broadcast_pkts={self.if_out_broadcast_pkts}, ' +
f'if_out_discards={self.if_out_discards})')
class MacCountersFactory:
def parse(self, data, message_info):
return MacCounters(struct.unpack('>9I', data.read(4 * 9)))
class BatteryLevel:
def __init__(self, battery_level: int):
self._battery_level = battery_level
@property
def battery_level(self):
return self._battery_level
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.battery_level == other.battery_level
def __repr__(self):
return f'BatteryLevel(battery_level={self.battery_level})'
class BatteryLevelFactory:
def parse(self, data, message_info):
return BatteryLevel(struct.unpack('>B', data.read(1))[0])
class SupplyVoltage:
def __init__(self, supply_voltage: int):
self._supply_voltage = supply_voltage
@property
def supply_voltage(self):
return self._supply_voltage
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.supply_voltage == other.supply_voltage
def __repr__(self):
return f'SupplyVoltage(supply_voltage={self.supply_voltage})'
class SupplyVoltageFactory:
def parse(self, data, message_info):
return SupplyVoltage(struct.unpack('>H', data.read(2))[0])
class ChildTableEntry:
def __init__(self, timeout: int, child_id: int, mode: mle.Mode):
self._timeout = timeout
self._child_id = child_id
self._mode = mode
@property
def timeout(self):
return self._timeout
@property
def child_id(self):
return self._child_id
@property
def mode(self):
return self._mode
def __eq__(self, other):
common.expect_the_same_class(self, other)
return (self.timeout == other.timeout and
self.child_id == other.child_id and self.mode == other.mode)
def __repr__(self):
return f'ChildTableEntry(timeout={self.timeout}, child_id={self.child_id}, mode={self.mode})'
class ChildTable:
def __init__(self, children: List[ChildTableEntry]):
self._children = sorted(children, key=lambda child: child.child_id)
@property
def children(self):
return self._children
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.children == other.children
def __repr__(self):
return f'ChildTable({self.children})'
class ChildTableFactory:
def parse(self, data, message_info):
children = []
while message_info.length > 0:
timeout_and_id = struct.unpack('>H', data.read(2))[0]
message_info.length -= 2
timeout = (timeout_and_id & 0xf800) >> 11
child_id = timeout_and_id & 0x1fff
mode = mle.ModeFactory().parse(data, message_info)
message_info.length -= 1
children.append(ChildTableEntry(timeout, child_id, mode))
return ChildTable(children)
class ChannelPages:
def __init__(self, channel_pages: bytes):
self._channel_pages = channel_pages
@property
def channel_pages(self):
return self._channel_pages
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.channel_pages == other.channel_pages
def __repr__(self):
return f'ChannelPages(channel_pages={self.channel_pages})'
class ChannelPagesFactory:
def parse(self, data, message_info):
return ChannelPages(data.getvalue())
class TypeList:
def __init__(self, tlv_types: List[int]):
self._tlv_types = tlv_types
@property
def tlv_types(self):
return self._tlv_types
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.tlv_types == other.tlv_types
def __repr__(self):
return f'TypeList(tlv_types={self.tlv_types})'
class TypeListFactory:
def parse(self, data, message_info):
return TypeList([ord(t) for t in data.getvalue()])
class MaxChildTimeout:
def __init__(self, max_child_timeout: int):
self._max_child_timeout = max_child_timeout
@property
def max_child_timeout(self):
return self._max_child_timeout
def __eq__(self, other):
common.expect_the_same_class(self, other)
return self.max_child_timeout == other.max_child_timeout
def __repr__(self):
return f'MaxChildTimeout(max_child_timeout={self.max_child_timeout})'
class MaxChildTimeoutFactory:
def parse(self, data, message_info):
return MaxChildTimeout(struct.unpack('>I', data.read(4))[0])