xref: /aosp_15_r20/external/openthread/tests/scripts/thread-cert/pktverify/summary.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*cfb92d14SAndroid Build Coastguard Worker#
3*cfb92d14SAndroid Build Coastguard Worker#  Copyright (c) 2019, The OpenThread Authors.
4*cfb92d14SAndroid Build Coastguard Worker#  All rights reserved.
5*cfb92d14SAndroid Build Coastguard Worker#
6*cfb92d14SAndroid Build Coastguard Worker#  Redistribution and use in source and binary forms, with or without
7*cfb92d14SAndroid Build Coastguard Worker#  modification, are permitted provided that the following conditions are met:
8*cfb92d14SAndroid Build Coastguard Worker#  1. Redistributions of source code must retain the above copyright
9*cfb92d14SAndroid Build Coastguard Worker#     notice, this list of conditions and the following disclaimer.
10*cfb92d14SAndroid Build Coastguard Worker#  2. Redistributions in binary form must reproduce the above copyright
11*cfb92d14SAndroid Build Coastguard Worker#     notice, this list of conditions and the following disclaimer in the
12*cfb92d14SAndroid Build Coastguard Worker#     documentation and/or other materials provided with the distribution.
13*cfb92d14SAndroid Build Coastguard Worker#  3. Neither the name of the copyright holder nor the
14*cfb92d14SAndroid Build Coastguard Worker#     names of its contributors may be used to endorse or promote products
15*cfb92d14SAndroid Build Coastguard Worker#     derived from this software without specific prior written permission.
16*cfb92d14SAndroid Build Coastguard Worker#
17*cfb92d14SAndroid Build Coastguard Worker#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*cfb92d14SAndroid Build Coastguard Worker#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*cfb92d14SAndroid Build Coastguard Worker#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*cfb92d14SAndroid Build Coastguard Worker#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21*cfb92d14SAndroid Build Coastguard Worker#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*cfb92d14SAndroid Build Coastguard Worker#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*cfb92d14SAndroid Build Coastguard Worker#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*cfb92d14SAndroid Build Coastguard Worker#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*cfb92d14SAndroid Build Coastguard Worker#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*cfb92d14SAndroid Build Coastguard Worker#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*cfb92d14SAndroid Build Coastguard Worker#  POSSIBILITY OF SUCH DAMAGE.
28*cfb92d14SAndroid Build Coastguard Worker#
29*cfb92d14SAndroid Build Coastguard Worker
30*cfb92d14SAndroid Build Coastguard Workerimport logging
31*cfb92d14SAndroid Build Coastguard Workerimport operator
32*cfb92d14SAndroid Build Coastguard Workerimport sys
33*cfb92d14SAndroid Build Coastguard Worker
34*cfb92d14SAndroid Build Coastguard Workerfrom pktverify import consts
35*cfb92d14SAndroid Build Coastguard Workerfrom pktverify.test_info import TestInfo
36*cfb92d14SAndroid Build Coastguard Worker
37*cfb92d14SAndroid Build Coastguard Worker
38*cfb92d14SAndroid Build Coastguard Workerclass NodeSummary(object):
39*cfb92d14SAndroid Build Coastguard Worker    """
40*cfb92d14SAndroid Build Coastguard Worker    Represents a summary of a node.
41*cfb92d14SAndroid Build Coastguard Worker    """
42*cfb92d14SAndroid Build Coastguard Worker
43*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, role, extaddr):
44*cfb92d14SAndroid Build Coastguard Worker        self._role = role
45*cfb92d14SAndroid Build Coastguard Worker        self._extaddr = extaddr
46*cfb92d14SAndroid Build Coastguard Worker        self._ipaddrs = {}
47*cfb92d14SAndroid Build Coastguard Worker
48*cfb92d14SAndroid Build Coastguard Worker    @property
49*cfb92d14SAndroid Build Coastguard Worker    def role(self):
50*cfb92d14SAndroid Build Coastguard Worker        return self._role
51*cfb92d14SAndroid Build Coastguard Worker
52*cfb92d14SAndroid Build Coastguard Worker    @property
53*cfb92d14SAndroid Build Coastguard Worker    def extaddr(self):
54*cfb92d14SAndroid Build Coastguard Worker        return self._extaddr
55*cfb92d14SAndroid Build Coastguard Worker
56*cfb92d14SAndroid Build Coastguard Worker    @property
57*cfb92d14SAndroid Build Coastguard Worker    def ipaddr_link_local(self):
58*cfb92d14SAndroid Build Coastguard Worker        for a, _ in self._iter_ipaddrs_rev():
59*cfb92d14SAndroid Build Coastguard Worker            if a.is_link_local:
60*cfb92d14SAndroid Build Coastguard Worker                return a
61*cfb92d14SAndroid Build Coastguard Worker
62*cfb92d14SAndroid Build Coastguard Worker        return None
63*cfb92d14SAndroid Build Coastguard Worker
64*cfb92d14SAndroid Build Coastguard Worker    @property
65*cfb92d14SAndroid Build Coastguard Worker    def ipaddr_mleid(self):
66*cfb92d14SAndroid Build Coastguard Worker        for a, _ in self._iter_ipaddrs_rev():
67*cfb92d14SAndroid Build Coastguard Worker            if a.is_mleid:
68*cfb92d14SAndroid Build Coastguard Worker                return a
69*cfb92d14SAndroid Build Coastguard Worker
70*cfb92d14SAndroid Build Coastguard Worker        return None
71*cfb92d14SAndroid Build Coastguard Worker
72*cfb92d14SAndroid Build Coastguard Worker    def _iter_ipaddrs_rev(self):
73*cfb92d14SAndroid Build Coastguard Worker        return sorted(self._ipaddrs.items(), key=operator.itemgetter(1), reverse=True)
74*cfb92d14SAndroid Build Coastguard Worker
75*cfb92d14SAndroid Build Coastguard Worker    def add_ipaddr(self, ipaddr, index):
76*cfb92d14SAndroid Build Coastguard Worker        if ipaddr not in self._ipaddrs:
77*cfb92d14SAndroid Build Coastguard Worker            self._ipaddrs[ipaddr] = index
78*cfb92d14SAndroid Build Coastguard Worker
79*cfb92d14SAndroid Build Coastguard Worker    def __str__(self):
80*cfb92d14SAndroid Build Coastguard Worker        return "[node {role} extaddr {extaddr} ipaddrs {ipaddrs}]".format(
81*cfb92d14SAndroid Build Coastguard Worker            role=self._role,
82*cfb92d14SAndroid Build Coastguard Worker            extaddr=self.extaddr,
83*cfb92d14SAndroid Build Coastguard Worker            ipaddrs=", ".join(map(str, sorted(self._ipaddrs))),
84*cfb92d14SAndroid Build Coastguard Worker        )
85*cfb92d14SAndroid Build Coastguard Worker
86*cfb92d14SAndroid Build Coastguard Worker    __repr__ = __str__
87*cfb92d14SAndroid Build Coastguard Worker
88*cfb92d14SAndroid Build Coastguard Worker
89*cfb92d14SAndroid Build Coastguard Workerclass Summary(object):
90*cfb92d14SAndroid Build Coastguard Worker    """
91*cfb92d14SAndroid Build Coastguard Worker    Represents a summary of the test.
92*cfb92d14SAndroid Build Coastguard Worker    """
93*cfb92d14SAndroid Build Coastguard Worker
94*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, pkts, test_info: TestInfo):
95*cfb92d14SAndroid Build Coastguard Worker        self._pkts = pkts
96*cfb92d14SAndroid Build Coastguard Worker        self._test_info = test_info
97*cfb92d14SAndroid Build Coastguard Worker        self._leader_id = None
98*cfb92d14SAndroid Build Coastguard Worker        self._analyze()
99*cfb92d14SAndroid Build Coastguard Worker
100*cfb92d14SAndroid Build Coastguard Worker    def iterroles(self):
101*cfb92d14SAndroid Build Coastguard Worker        return self._role_to_node.items()
102*cfb92d14SAndroid Build Coastguard Worker
103*cfb92d14SAndroid Build Coastguard Worker    def _analyze(self):
104*cfb92d14SAndroid Build Coastguard Worker        self._analyze_test_info()
105*cfb92d14SAndroid Build Coastguard Worker
106*cfb92d14SAndroid Build Coastguard Worker        with self._pkts.save_index():
107*cfb92d14SAndroid Build Coastguard Worker            for f in [
108*cfb92d14SAndroid Build Coastguard Worker                    self._analyze_leader,
109*cfb92d14SAndroid Build Coastguard Worker                    self._analyze_packets,
110*cfb92d14SAndroid Build Coastguard Worker            ]:
111*cfb92d14SAndroid Build Coastguard Worker                self._pkts.index = (0, 0)
112*cfb92d14SAndroid Build Coastguard Worker                f()
113*cfb92d14SAndroid Build Coastguard Worker
114*cfb92d14SAndroid Build Coastguard Worker    def _analyze_test_info(self):
115*cfb92d14SAndroid Build Coastguard Worker        self._role_to_node = {}
116*cfb92d14SAndroid Build Coastguard Worker        self._extaddr_to_node = {}
117*cfb92d14SAndroid Build Coastguard Worker
118*cfb92d14SAndroid Build Coastguard Worker        for role, extaddr in self._test_info.extaddrs.items():
119*cfb92d14SAndroid Build Coastguard Worker            assert role not in self._role_to_node
120*cfb92d14SAndroid Build Coastguard Worker            assert extaddr not in self._extaddr_to_node
121*cfb92d14SAndroid Build Coastguard Worker
122*cfb92d14SAndroid Build Coastguard Worker            node = NodeSummary(role, extaddr)
123*cfb92d14SAndroid Build Coastguard Worker            self._role_to_node[role] = node
124*cfb92d14SAndroid Build Coastguard Worker            self._extaddr_to_node[extaddr] = node
125*cfb92d14SAndroid Build Coastguard Worker
126*cfb92d14SAndroid Build Coastguard Worker    def _analyze_leader(self):
127*cfb92d14SAndroid Build Coastguard Worker        for p in self._pkts:
128*cfb92d14SAndroid Build Coastguard Worker
129*cfb92d14SAndroid Build Coastguard Worker            if p.mle.cmd in [consts.MLE_DATA_RESPONSE, consts.MLE_ADVERTISEMENT]:
130*cfb92d14SAndroid Build Coastguard Worker
131*cfb92d14SAndroid Build Coastguard Worker                p.mle.__getattr__('tlv')
132*cfb92d14SAndroid Build Coastguard Worker                p.mle.__getattr__('tlv.leader_data')
133*cfb92d14SAndroid Build Coastguard Worker                p.mle.__getattr__('tlv.leader_data.router_id')
134*cfb92d14SAndroid Build Coastguard Worker
135*cfb92d14SAndroid Build Coastguard Worker                tlv = p.mle.tlv
136*cfb92d14SAndroid Build Coastguard Worker                if tlv.leader_data:
137*cfb92d14SAndroid Build Coastguard Worker                    self._leader_id = tlv.leader_data.router_id
138*cfb92d14SAndroid Build Coastguard Worker                    logging.info("leader found in pcap: %d", self._leader_id)
139*cfb92d14SAndroid Build Coastguard Worker                    break
140*cfb92d14SAndroid Build Coastguard Worker        else:
141*cfb92d14SAndroid Build Coastguard Worker            logging.warning("leader not found in pcap")
142*cfb92d14SAndroid Build Coastguard Worker
143*cfb92d14SAndroid Build Coastguard Worker    def _analyze_packets(self):
144*cfb92d14SAndroid Build Coastguard Worker        for i, p in enumerate(self._pkts):
145*cfb92d14SAndroid Build Coastguard Worker            extaddr, src = None, None
146*cfb92d14SAndroid Build Coastguard Worker            # each packet should be either wpan or eth
147*cfb92d14SAndroid Build Coastguard Worker            assert (p.wpan and not p.eth) or (p.eth and not p.wpan)
148*cfb92d14SAndroid Build Coastguard Worker            if p.wpan:
149*cfb92d14SAndroid Build Coastguard Worker                # it is a 802.15.4 packet
150*cfb92d14SAndroid Build Coastguard Worker                extaddr = p.wpan.src64
151*cfb92d14SAndroid Build Coastguard Worker
152*cfb92d14SAndroid Build Coastguard Worker            if p.ipv6:
153*cfb92d14SAndroid Build Coastguard Worker                # it is a IPv6 packet
154*cfb92d14SAndroid Build Coastguard Worker                src = p.ipv6.src
155*cfb92d14SAndroid Build Coastguard Worker
156*cfb92d14SAndroid Build Coastguard Worker            if extaddr and src:
157*cfb92d14SAndroid Build Coastguard Worker                if extaddr in self._extaddr_to_node:
158*cfb92d14SAndroid Build Coastguard Worker                    role_sum = self._extaddr_to_node[extaddr]
159*cfb92d14SAndroid Build Coastguard Worker                    role_sum.add_ipaddr(src, i)
160*cfb92d14SAndroid Build Coastguard Worker                else:
161*cfb92d14SAndroid Build Coastguard Worker                    logging.warn("Extaddr %s is not in the testbed", extaddr)
162*cfb92d14SAndroid Build Coastguard Worker
163*cfb92d14SAndroid Build Coastguard Worker    def show(self):
164*cfb92d14SAndroid Build Coastguard Worker        show_roles = "\n\t\t".join(map(str, self._role_to_node.values()))
165*cfb92d14SAndroid Build Coastguard Worker        sys.stderr.write("""{header}
166*cfb92d14SAndroid Build Coastguard Worker    Pcap Summary:
167*cfb92d14SAndroid Build Coastguard Worker        packets = {num_packets}
168*cfb92d14SAndroid Build Coastguard Worker        roles = {num_roles}
169*cfb92d14SAndroid Build Coastguard Worker            {show_roles}
170*cfb92d14SAndroid Build Coastguard Worker    {tailer}
171*cfb92d14SAndroid Build Coastguard Worker    """.format(
172*cfb92d14SAndroid Build Coastguard Worker            header='>' * 120,
173*cfb92d14SAndroid Build Coastguard Worker            num_packets=len(self._pkts),
174*cfb92d14SAndroid Build Coastguard Worker            num_roles=len(self._role_to_node),
175*cfb92d14SAndroid Build Coastguard Worker            show_roles=show_roles,
176*cfb92d14SAndroid Build Coastguard Worker            tailer='<' * 120,
177*cfb92d14SAndroid Build Coastguard Worker        ))
178*cfb92d14SAndroid Build Coastguard Worker
179*cfb92d14SAndroid Build Coastguard Worker    def ipaddr_mleid_by_role(self, role):
180*cfb92d14SAndroid Build Coastguard Worker        node = self._role_to_node[role]
181*cfb92d14SAndroid Build Coastguard Worker        return node.ipaddr_mleid
182*cfb92d14SAndroid Build Coastguard Worker
183*cfb92d14SAndroid Build Coastguard Worker    def ipaddr_link_local_by_role(self, role):
184*cfb92d14SAndroid Build Coastguard Worker        node = self._role_to_node[role]
185*cfb92d14SAndroid Build Coastguard Worker        return node.ipaddr_link_local
186*cfb92d14SAndroid Build Coastguard Worker
187*cfb92d14SAndroid Build Coastguard Worker    def role(self, r):
188*cfb92d14SAndroid Build Coastguard Worker        return self._role_to_node[r]
189