1#!/usr/bin/env python3
2#
3#  Copyright (c) 2019, 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#
29import datetime
30import sys
31import time
32from typing import Any, Union
33
34from pyshark.packet.fields import LayerFieldsContainer, LayerField
35from pyshark.packet.packet import Packet as RawPacket
36
37from pktverify.addrs import EthAddr, ExtAddr, Ipv6Addr
38from pktverify.bytes import Bytes
39from pktverify.consts import VALID_LAYER_NAMES
40from pktverify.null_field import nullField
41
42
43def _auto(v: Union[LayerFieldsContainer, LayerField]):
44    """parse the layer field automatically according to its format"""
45    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1 or v.get_default_value() is not None, v.fields
46    dv = v.get_default_value()
47    rv = v.raw_value
48
49    if dv.startswith('0x'):
50        return int(dv, 16)
51
52    try:
53        if dv == rv:
54            return int(dv)
55        elif int(dv) == int(rv, 16):
56            return int(dv)
57    except (ValueError, TypeError):
58        pass
59
60    if rv is None:
61        try:
62            return int(dv)
63        except (ValueError, TypeError):
64            pass
65
66    if ':' in dv and '::' not in dv and dv.replace(':', '') == rv:  # '88:00', '8800'
67        return int(rv, 16)
68
69    # timestamp: 'Jan  1, 1970 08:00:00.000000000 CST', '0000000000000000'
70    # convert to seconds from 1970, ignore the nanosecond for now since
71    # there are integer seconds applied in the test cases
72    try:
73        time_str = datetime.datetime.strptime(dv, "%b  %d, %Y %H:%M:%S.%f000 %Z")
74        time_in_sec = time.mktime(time_str.utctimetuple())
75        return int(time_in_sec)
76    except (ValueError, TypeError):
77        pass
78
79    try:
80        int(rv, 16)
81        return int(dv)
82    except Exception:
83        pass
84
85    raise ValueError((v, v.get_default_value(), v.raw_value))
86
87
88def _payload(v: Union[LayerFieldsContainer, LayerField]) -> bytearray:
89    """parse the layer field as a bytearray"""
90    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
91    hex_value = v.raw_value
92    assert len(hex_value) % 2 == 0
93    s = bytearray()
94    for i in range(0, len(hex_value), 2):
95        s.append(int(hex_value[i:i + 2], 16))
96
97    return s
98
99
100def _hex(v: Union[LayerFieldsContainer, LayerField]) -> int:
101    """parse the layer field as a hex string"""
102    # split v into octets and reverse the order
103    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
104    return int(v.get_default_value(), 16)
105
106
107def _raw_hex(v: Union[LayerFieldsContainer, LayerField]) -> int:
108    """parse the layer field as a raw hex string"""
109    # split v into octets and reverse the order
110    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
111    iv = v.hex_value
112
113    try:
114        int(v.get_default_value())
115        assert int(v.get_default_value()) == iv, (v.get_default_value(), v.raw_value)
116    except ValueError:
117        pass
118
119    try:
120        int(v.get_default_value(), 16)
121        assert int(v.get_default_value(), 16) == iv, (v.get_default_value(), v.raw_value)
122    except ValueError:
123        pass
124
125    return iv
126
127
128def _raw_hex_rev(v: Union[LayerFieldsContainer, LayerField]) -> int:
129    """parse the layer field as a reversed raw hex string"""
130    # split v into octets and reverse the order
131    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
132    rv = v.raw_value
133    octets = [rv[i:i + 2] for i in range(0, len(rv), 2)]
134
135    iv = int(''.join(reversed(octets)), 16)
136
137    try:
138        int(v.get_default_value())
139        assert int(v.get_default_value()) == iv, (v.get_default_value(), v.raw_value)
140    except ValueError:
141        pass
142
143    try:
144        int(v.get_default_value(), 16)
145        assert int(v.get_default_value(), 16) == iv, (v.get_default_value(), v.raw_value)
146    except ValueError:
147        pass
148
149    return iv
150
151
152def _dec(v: Union[LayerFieldsContainer, LayerField]) -> int:
153    """parse the layer field as a decimal"""
154    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
155    return int(v.get_default_value())
156
157
158def _float(v: Union[LayerFieldsContainer, LayerField]) -> float:
159    """parse the layer field as a float"""
160    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
161    return float(v.get_default_value())
162
163
164def _str(v: Union[LayerFieldsContainer, LayerField]) -> str:
165    """parse the layer field as a string"""
166    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
167    return str(v.get_default_value())
168
169
170def _bytes(v: Union[LayerFieldsContainer, LayerField]) -> Bytes:
171    """parse the layer field as raw bytes"""
172    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
173    return Bytes(v.raw_value)
174
175
176def _ext_addr(v: Union[LayerFieldsContainer, LayerField]) -> ExtAddr:
177    """parse the layer field as an extended address"""
178    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
179    return ExtAddr(v.get_default_value())
180
181
182def _ipv6_addr(v: Union[LayerFieldsContainer, LayerField]) -> Ipv6Addr:
183    """parse the layer field as an IPv6 address"""
184    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
185    return Ipv6Addr(v.get_default_value())
186
187
188def _eth_addr(v: Union[LayerFieldsContainer, LayerField]) -> EthAddr:
189    """parse the layer field as an Ethernet MAC address"""
190    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1, v.fields
191    return EthAddr(v.get_default_value())
192
193
194def _routerid_set(v: Union[LayerFieldsContainer, LayerField]) -> set:
195    """parse the layer field as a set of router ids
196
197       Notes: the router ID mask in wireshark is a
198              hexadecimal string separated by ':'
199    """
200    assert not isinstance(v, LayerFieldsContainer) or len(v.fields) == 1
201
202    try:
203        ridmask = str(v.get_default_value())
204        assert isinstance(ridmask, str), ridmask
205        ridmask_int = int(ridmask.replace(':', ''), base=16)
206        rid_set = set()
207        count = 0
208        while ridmask_int:
209            count += 1
210            if ridmask_int & 1:
211                rid_set.add(64 - count)
212            ridmask_int = ridmask_int >> 1
213    except ValueError:
214        pass
215
216    return rid_set
217
218
219class _first(object):
220    """parse the first layer field"""
221
222    def __init__(self, sub_parse):
223        self._sub_parse = sub_parse
224
225    def __call__(self, v: Union[LayerFieldsContainer, LayerField]):
226        return self._sub_parse(v.fields[0])
227
228
229class _list(object):
230    """parse all layer fields into a list"""
231
232    def __init__(self, sub_parse):
233        self._sub_parse = sub_parse
234
235    def __call__(self, v: Union[LayerFieldsContainer, LayerField]):
236        return [self._sub_parse(f) for f in v.fields]
237
238
239_LAYER_FIELDS = {
240    # WPAN
241    'wpan.fcf': _raw_hex_rev,
242    'wpan.cmd': _auto,
243    'wpan.security': _auto,
244    'wpan.frame_type': _auto,
245    'wpan.pending': _auto,
246    'wpan.ack_request': _auto,
247    'wpan.pan_id_compression': _auto,
248    'wpan.seqno_suppression': _auto,
249    'wpan.ie_present': _auto,
250    'wpan.dst_addr_mode': _auto,
251    'wpan.version': _auto,
252    'wpan.src_addr_mode': _auto,
253    'wpan.dst_pan': _auto,
254    'wpan.seq_no': _auto,
255    'wpan.src16': _auto,
256    'wpan.dst16': _auto,
257    'wpan.src64': _ext_addr,
258    'wpan.dst64': _ext_addr,
259    'wpan.fcs': _raw_hex_rev,
260    'wpan.fcs_ok': _auto,
261    'wpan.frame_length': _dec,
262    'wpan.key_number': _auto,
263    'wpan.aux_sec.sec_suite': _auto,
264    'wpan.aux_sec.security_control_field': _auto,
265    'wpan.aux_sec.sec_level': _auto,
266    'wpan.aux_sec.key_id_mode': _auto,
267    'wpan.aux_sec.frame_counter_suppression': _auto,
268    'wpan.aux_sec.asn_in_nonce': _auto,
269    'wpan.aux_sec.reserved': _auto,
270    'wpan.aux_sec.frame_counter': _auto,
271    'wpan.aux_sec.key_source': _auto,
272    'wpan.aux_sec.key_index': _auto,
273    'wpan.aux_sec.hdr': _str,
274    'wpan.mic': _auto,
275    'wpan.channel': _auto,
276    'wpan.header_ie.id': _list(_auto),
277    'wpan.header_ie.csl.period': _auto,
278    'wpan.payload_ie.vendor.oui': _auto,
279
280    # MLE
281    'mle.cmd': _auto,
282    'mle.sec_suite': _hex,
283    'mle.tlv.type': _list(_dec),
284    'mle.tlv.len': _list(_dec),
285    'mle.tlv.mode.receiver_on_idle': _auto,
286    'mle.tlv.mode.reserved1': _auto,
287    'mle.tlv.mode.reserved2': _auto,
288    'mle.tlv.mode.device_type_bit': _auto,
289    'mle.tlv.mode.network_data': _auto,
290    'mle.tlv.challenge': _bytes,
291    'mle.tlv.scan_mask.r': _auto,
292    'mle.tlv.scan_mask.e': _auto,
293    'mle.tlv.version': _auto,
294    'mle.tlv.source_addr': _auto,
295    'mle.tlv.active_tstamp': _auto,
296    'mle.tlv.pending_tstamp': _auto,
297    'mle.tlv.leader_data.partition_id': _auto,
298    'mle.tlv.leader_data.weighting': _auto,
299    'mle.tlv.leader_data.data_version': _auto,
300    'mle.tlv.leader_data.stable_data_version': _auto,
301    'mle.tlv.leader_data.router_id': _auto,
302    'mle.tlv.route64.nbr_out': _list(_auto),
303    'mle.tlv.route64.nbr_in': _list(_auto),
304    'mle.tlv.route64.id_seq': _auto,
305    'mle.tlv.route64.id_mask': _routerid_set,
306    'mle.tlv.route64.cost': _list(_auto),
307    'mle.tlv.response': _bytes,
308    'mle.tlv.mle_frm_cntr': _auto,
309    'mle.tlv.ll_frm_cntr': _auto,
310    'mle.tlv.link_margin': _auto,
311    'mle.tlv.conn.sed_dgram_cnt': _auto,
312    'mle.tlv.conn.sed_buf_size': _auto,
313    'mle.tlv.conn.lq3': _auto,
314    'mle.tlv.conn.lq2': _auto,
315    'mle.tlv.conn.lq1': _auto,
316    'mle.tlv.conn.leader_cost': _auto,
317    'mle.tlv.conn.id_seq': _auto,
318    'mle.tlv.conn.flags.pp': _auto,
319    'mle.tlv.conn.active_rtrs': _auto,
320    'mle.tlv.timeout': _auto,
321    'mle.tlv.addr16': _auto,
322    'mle.tlv.channel': _auto,
323    'mle.tlv.addr_reg_iid': _list(_auto),
324    'mle.tlv.link_enh_ack_flags': _auto,
325    'mle.tlv.link_forward_series': _list(_auto),
326    'mle.tlv.link_requested_type_id_flags': _list(_hex),
327    'mle.tlv.link_sub_tlv': _auto,
328    'mle.tlv.link_status_sub_tlv': _auto,
329    'mle.tlv.query_id': _auto,
330    'mle.tlv.metric_type_id_flags.type': _list(_hex),
331    'mle.tlv.metric_type_id_flags.metric': _list(_hex),
332    'mle.tlv.metric_type_id_flags.l': _list(_hex),
333    'mle.tlv.link_requested_type_id_flags': _bytes,
334
335    # IP
336    'ip.version': _auto,
337    'ip.src': _str,
338    'ip.src_host': _str,
339    'ip.dst': _str,
340    'ip.dst_host': _str,
341    'ip.ttl': _auto,
342    'ip.proto': _auto,
343    'ip.len': _auto,
344    'ip.id': _auto,
345    'ip.host': _list(_str),
346    'ip.hdr_len': _dec,
347    'ip.frag_offset': _auto,
348    'ip.flags.rb': _auto,
349    'ip.flags.mf': _auto,
350    'ip.flags.df': _auto,
351    'ip.dsfield.ecn': _auto,
352    'ip.dsfield.dscp': _auto,
353    'ip.checksum.status': _auto,
354    'ip.addr': _list(_str),
355    'ip.options.routeralert': _bytes,
356    'ip.opt.type.number': _auto,
357    'ip.opt.type.copy': _auto,
358    'ip.opt.type.class': _auto,
359    'ip.opt.ra': _auto,
360    'ip.opt.len': _auto,
361    # UDP
362    'udp.stream': _auto,
363    'udp.srcport': _auto,
364    'udp.dstport': _auto,
365    'udp.length': _auto,
366    'udp.port': _list(_dec),
367    'udp.checksum.status': _auto,
368
369    # IPv6
370    'ipv6.version': _auto,
371    'ipv6.src': _ipv6_addr,
372    'ipv6.src_host': _ipv6_addr,
373    'ipv6.dst': _ipv6_addr,
374    'ipv6.dst_host': _ipv6_addr,
375    'ipv6.addr': _list(_ipv6_addr),
376    'ipv6.tclass.dscp': _auto,
377    'ipv6.tclass.ecn': _auto,
378    'ipv6.flow': _auto,
379    'ipv6.hlim': _auto,
380    'ipv6.nxt': _auto,
381    'ipv6.hopopts.len': _auto,
382    'ipv6.hopopts.nxt': _auto,
383    'ipv6.hopopts.len_oct': _dec,
384    'ipv6.host': _list(_ipv6_addr),
385    'ipv6.plen': _auto,
386    'ipv6.opt.type.rest': _list(_auto),
387    'ipv6.opt.type.change': _list(_auto),
388    'ipv6.opt.type.action': _list(_auto),
389    'ipv6.opt.router_alert': _auto,
390    'ipv6.opt.padn': _str,
391    'ipv6.opt.length': _list(_auto),
392    'ipv6.opt.mpl.seed_id': _bytes,
393    'ipv6.opt.mpl.sequence': _auto,
394    'ipv6.opt.mpl.flag.v': _auto,
395    'ipv6.opt.mpl.flag.s': _auto,
396    'ipv6.opt.mpl.flag.rsv': _auto,
397    'ipv6.opt.mpl.flag.m': _auto,
398
399    # Eth
400    'eth.src': _eth_addr,
401    'eth.src_resolved': _eth_addr,
402    'eth.dst': _eth_addr,
403    'eth.dst_resolved': _eth_addr,
404    'eth.type': _auto,
405    'eth.addr': _list(_eth_addr),
406    'eth.addr_resolved': _list(_eth_addr),
407    'eth.ig': _list(_auto),
408    'eth.lg': _list(_auto),
409    # 6LOWPAN
410    '6lowpan.src': _ipv6_addr,
411    '6lowpan.dst': _ipv6_addr,
412    '6lowpan.udp.src': _auto,
413    '6lowpan.udp.dst': _auto,
414    '6lowpan.udp.checksum': _auto,
415    '6lowpan.frag.offset': _auto,
416    '6lowpan.frag.tag': _auto,
417    '6lowpan.frag.size': _auto,
418    '6lowpan.pattern': _list(_auto),
419    '6lowpan.hops': _auto,
420    '6lowpan.padding': _auto,
421    '6lowpan.next': _auto,
422    '6lowpan.flow': _auto,
423    '6lowpan.ecn': _auto,
424    '6lowpan.iphc.tf': _auto,
425    '6lowpan.iphc.m': _auto,
426    '6lowpan.iphc.nh': _auto,
427    '6lowpan.iphc.hlim': _auto,
428    '6lowpan.iphc.cid': _auto,
429    '6lowpan.iphc.sac': _auto,
430    '6lowpan.iphc.sam': _auto,
431    '6lowpan.iphc.dac': _auto,
432    '6lowpan.iphc.dam': _auto,
433    '6lowpan.iphc.sci': _auto,
434    '6lowpan.iphc.dci': _auto,
435    '6lowpan.iphc.sctx.prefix': _ipv6_addr,
436    '6lowpan.iphc.dctx.prefix': _ipv6_addr,
437    '6lowpan.mesh.v': _auto,
438    '6lowpan.nhc.pattern': _list(_auto),
439    '6lowpan.nhc.udp.checksum': _auto,
440    '6lowpan.nhc.udp.ports': _auto,
441    '6lowpan.nhc.ext.nh': _auto,
442    '6lowpan.nhc.ext.length': _auto,
443    '6lowpan.nhc.ext.eid': _auto,
444    '6lowpan.reassembled.length': _auto,
445    '6lowpan.fragments': _str,
446    '6lowpan.fragment.count': _auto,
447    '6lowpan.mesh.orig16': _auto,
448    '6lowpan.mesh.hops8': _auto,
449    '6lowpan.mesh.hops': _auto,
450    '6lowpan.mesh.f': _auto,
451    '6lowpan.mesh.dest16': _auto,
452
453    # ICMPv6
454    'icmpv6.type': _first(_auto),
455    'icmpv6.code': _first(_auto),
456    'icmpv6.checksum': _first(_auto),
457    'icmpv6.reserved': _raw_hex,
458    'icmpv6.resptime': _float,
459    'icmpv6.resp_to': _auto,
460    'icmpv6.mldr.nb_mcast_records': _auto,
461    'icmpv6.nd.ra.cur_hop_limit': _auto,
462    'icmpv6.nd.ns.target_address': _ipv6_addr,
463    'icmpv6.nd.na.target_address': _ipv6_addr,
464    'icmpv6.nd.na.flag.s': _auto,
465    'icmpv6.nd.na.flag.o': _auto,
466    'icmpv6.nd.na.flag.r': _auto,
467    'icmpv6.nd.na.flag.rsv': _auto,
468    'icmpv6.mldr.mar.record_type': _list(_auto),
469    'icmpv6.mldr.mar.aux_data_len': _list(_auto),
470    'icmpv6.mldr.mar.nb_sources': _list(_auto),
471    'icmpv6.mldr.mar.multicast_address': _list(_ipv6_addr),
472    'icmpv6.opt.type': _list(_auto),
473    'icmpv6.opt.nonce': _bytes,
474    'icmpv6.opt.linkaddr': _eth_addr,
475    'icmpv6.opt.src_linkaddr': _eth_addr,
476    'icmpv6.opt.target_linkaddr': _eth_addr,
477    'icmpv6.opt.route_lifetime': _auto,
478    'icmpv6.opt.route_info.flag.route_preference': _auto,
479    'icmpv6.opt.route_info.flag.reserved': _auto,
480    'icmpv6.opt.prefix.valid_lifetime': _auto,
481    'icmpv6.opt.prefix.preferred_lifetime': _auto,
482    'icmpv6.opt.prefix.length': _list(_auto),
483    'icmpv6.opt.prefix.flag.reserved': _auto,
484    'icmpv6.opt.prefix.flag.r': _auto,
485    'icmpv6.opt.prefix.flag.l': _auto,
486    'icmpv6.opt.prefix.flag.a': _auto,
487    'icmpv6.opt.length': _list(_auto),
488    'icmpv6.opt.reserved': _str,
489    'icmpv6.nd.ra.router_lifetime': _auto,
490    'icmpv6.nd.ra.retrans_timer': _auto,
491    'icmpv6.nd.ra.reachable_time': _auto,
492    'icmpv6.nd.ra.flag.rsv': _auto,
493    'icmpv6.nd.ra.flag.prf': _auto,
494    'icmpv6.nd.ra.flag.p': _auto,
495    'icmpv6.nd.ra.flag.o': _auto,
496    'icmpv6.nd.ra.flag.m': _auto,
497    'icmpv6.nd.ra.flag.h': _auto,
498    'icmpv6.echo.sequence_number': _auto,
499    'icmpv6.echo.identifier': _auto,
500    'icmpv6.data.len': _auto,
501
502    # COAP
503    'coap.code': _auto,
504    'coap.version': _auto,
505    'coap.type': _auto,
506    'coap.mid': _auto,
507    'coap.token_len': _auto,
508    'coap.token': _auto,
509    'coap.opt.uri_path': _list(_str),
510    'coap.opt.name': _list(_str),
511    'coap.opt.length': _list(_auto),
512    'coap.opt.uri_path_recon': _str,
513    'coap.payload': _payload,
514    'coap.payload_length': _auto,
515    'coap.payload_desc': _str,
516    'coap.opt.end_marker': _auto,
517    'coap.opt.desc': _list(_str),
518    'coap.opt.delta': _list(_auto),
519    'coap.response_to': _auto,
520    'coap.response_time': _float,
521    # COAP TLVS
522    'coap.tlv.type': _list(_auto),
523    'coap.tlv.status': _auto,
524    'coap.tlv.target_eid': _ipv6_addr,
525    'coap.tlv.ml_eid': _ext_addr,
526    'coap.tlv.last_transaction_time': _auto,
527    'coap.tlv.rloc16': _auto,
528    'coap.tlv.net_name': _str,
529    'coap.tlv.ext_mac_addr': _ext_addr,
530    'coap.tlv.router_mask_assigned': _auto,
531    'coap.tlv.router_mask_id_seq': _auto,
532
533    # dtls
534    'dtls.handshake.type': _list(_auto),
535    'dtls.handshake.cookie': _auto,
536    'dtls.record.content_type': _list(_auto),
537    'dtls.alert_message.desc': _auto,
538
539    # thread beacon
540    'thread_bcn.protocol': _auto,
541    'thread_bcn.version': _auto,
542    'thread_bcn.network_name': _str,
543    'thread_bcn.epid': _ext_addr,
544
545    # thread_address
546    'thread_address.tlv.len': _list(_auto),
547    'thread_address.tlv.type': _list(_auto),
548    'thread_address.tlv.status': _auto,
549    'thread_address.tlv.target_eid': _ipv6_addr,
550    'thread_address.tlv.ext_mac_addr': _ext_addr,
551    'thread_address.tlv.router_mask_id_seq': _auto,
552    'thread_address.tlv.router_mask_assigned': _bytes,
553    'thread_address.tlv.rloc16': _hex,
554    'thread_address.tlv.target_eid': _ipv6_addr,
555    'thread_address.tlv.ml_eid': _ext_addr,
556
557    # thread bl
558    'thread_bl.tlv.type': _list(_auto),
559    'thread_bl.tlv.len': _list(_auto),
560    'thread_bl.tlv.target_eid': _ipv6_addr,
561    'thread_bl.tlv.ml_eid': _ext_addr,
562    'thread_bl.tlv.last_transaction_time': _auto,
563    'thread_bl.tlv.timeout': _auto,
564    # THEAD NM
565    'thread_nm.tlv.type': _list(_auto),
566    'thread_nm.tlv.ml_eid': _ext_addr,
567    'thread_nm.tlv.target_eid': _ipv6_addr,
568    'thread_nm.tlv.status': _auto,
569    'thread_nm.tlv.timeout': _auto,
570    # thread_meshcop is not a real layer
571    'thread_meshcop.len_size_mismatch': _str,
572    'thread_meshcop.tlv.type': _list(_auto),
573    'thread_meshcop.tlv.len8': _list(_auto),
574    'thread_meshcop.tlv.net_name': _list(_str),  # from thread_bl
575    'thread_meshcop.tlv.commissioner_id': _str,
576    'thread_meshcop.tlv.commissioner_sess_id': _auto,  # from mle
577    "thread_meshcop.tlv.channel_page": _auto,  # from ble
578    "thread_meshcop.tlv.channel": _list(_auto),  # from ble
579    "thread_meshcop.tlv.chan_mask": _str,  # from ble
580    'thread_meshcop.tlv.chan_mask_page': _auto,
581    'thread_meshcop.tlv.chan_mask_len': _auto,
582    'thread_meshcop.tlv.chan_mask_mask': _bytes,
583    'thread_meshcop.tlv.discovery_req_ver': _auto,
584    'thread_meshcop.tlv.discovery_rsp_ver': _auto,
585    'thread_meshcop.tlv.discovery_rsp_n': _auto,
586    'thread_meshcop.tlv.energy_list': _list(_auto),
587    'thread_meshcop.tlv.pan_id': _list(_auto),
588    'thread_meshcop.tlv.xpan_id': _bytes,
589    'thread_meshcop.tlv.ml_prefix': _bytes,
590    'thread_meshcop.tlv.master_key': _bytes,
591    'thread_meshcop.tlv.pskc': _bytes,
592    'thread_meshcop.tlv.sec_policy_rot': _auto,
593    'thread_meshcop.tlv.sec_policy_o': _auto,
594    'thread_meshcop.tlv.sec_policy_n': _auto,
595    'thread_meshcop.tlv.sec_policy_r': _auto,
596    'thread_meshcop.tlv.sec_policy_c': _auto,
597    'thread_meshcop.tlv.sec_policy_b': _auto,
598    'thread_meshcop.tlv.state': _auto,
599    'thread_meshcop.tlv.steering_data': _bytes,
600    'thread_meshcop.tlv.unknown': _bytes,
601    'thread_meshcop.tlv.udp_port': _list(_auto),
602    'thread_meshcop.tlv.ba_locator': _auto,
603    'thread_meshcop.tlv.jr_locator': _auto,
604    'thread_meshcop.tlv.active_tstamp': _auto,
605    'thread_meshcop.tlv.pending_tstamp': _auto,
606    'thread_meshcop.tlv.delay_timer': _auto,
607    'thread_meshcop.tlv.ipv6_addr': _list(_ipv6_addr),
608
609    # THREAD NWD
610    'thread_nwd.tlv.type': _list(_auto),
611    'thread_nwd.tlv.len': _list(_auto),
612    'thread_nwd.tlv.stable': _list(_auto),
613    'thread_nwd.tlv.service.t': _auto,
614    'thread_nwd.tlv.service.s_id': _auto,
615    'thread_nwd.tlv.service.s_data_len': _auto,
616    'thread_nwd.tlv.service.s_data.seqno': _auto,
617    'thread_nwd.tlv.service.s_data.rrdelay': _auto,
618    'thread_nwd.tlv.service.s_data.mlrtimeout': _auto,
619    'thread_nwd.tlv.server_16': _list(_auto),
620    'thread_nwd.tlv.border_router_16': _list(_auto),
621    'thread_nwd.tlv.sub_tlvs': _list(_str),
622    # TODO: support thread_nwd.tlv.prefix.length and thread_nwd.tlv.prefix.domain_id
623    'thread_nwd.tlv.prefix': _list(_ipv6_addr),
624    'thread_nwd.tlv.border_router.pref': _auto,
625    'thread_nwd.tlv.border_router.flag.s': _list(_auto),
626    'thread_nwd.tlv.border_router.flag.r': _list(_auto),
627    'thread_nwd.tlv.border_router.flag.p': _list(_auto),
628    'thread_nwd.tlv.border_router.flag.o': _list(_auto),
629    'thread_nwd.tlv.border_router.flag.n': _list(_auto),
630    'thread_nwd.tlv.border_router.flag.dp': _list(_auto),
631    'thread_nwd.tlv.border_router.flag.d': _list(_auto),
632    'thread_nwd.tlv.border_router.flag.c': _list(_auto),
633    'thread_nwd.tlv.6co.flag.reserved': _auto,
634    'thread_nwd.tlv.6co.flag.cid': _auto,
635    'thread_nwd.tlv.6co.flag.c': _list(_auto),
636    'thread_nwd.tlv.6co.context_length': _auto,
637
638    # Thread Diagnostic
639    'thread_diagnostic.tlv.type': _list(_auto),
640    'thread_diagnostic.tlv.len8': _list(_auto),
641    'thread_diagnostic.tlv.general': _list(_str)
642}
643
644_layer_containers = set()
645
646for key in _LAYER_FIELDS:
647    assert key.strip() == key and ' ' not in key, key
648    secs = key.split('.')
649    assert len(secs) >= 2
650    assert secs[0] in VALID_LAYER_NAMES, secs[0]
651    for i in range(len(secs) - 2):
652        path = secs[0] + '.' + '.'.join(secs[1:i + 2])
653        assert path not in _LAYER_FIELDS, '%s can not be both field and path' % path
654        _layer_containers.add(path)
655
656
657def is_layer_field(uri: str) -> bool:
658    """
659    Returns if the URI is a valid layer field.
660
661    :param uri: The layer field URI.
662    """
663    return uri in _LAYER_FIELDS
664
665
666def is_layer_field_container(uri: str) -> bool:
667    """
668    Returns if the URI is a valid layer field container.
669
670    :param uri: The layer field container URI.
671    """
672    return uri in _layer_containers
673
674
675def get_layer_field(packet: RawPacket, field_uri: str) -> Any:
676    """
677    Get a given layer field from the packet.
678
679    :param packet: The packet.
680    :param field_uri: The layer field URI.
681
682    :return: The specified layer field.
683    """
684    assert isinstance(packet, RawPacket)
685    secs = field_uri.split('.')
686    layer_depth = 0
687    layer_name = secs[0]
688    if layer_name.endswith('inner'):
689        layer_name = layer_name[:-len('inner')]
690        field_uri = '.'.join([layer_name] + secs[1:])
691        layer_depth = 1
692
693    if is_layer_field(field_uri):
694        candidate_layers = _get_candidate_layers(packet, layer_name)
695        for layers in candidate_layers:
696            if layer_depth >= len(layers):
697                continue
698            layer = layers[layer_depth]
699            v = layer.get_field(field_uri)
700            if v is not None:
701                try:
702                    v = _LAYER_FIELDS[field_uri](v)
703                    print("[%s = %r] " % (field_uri, v), file=sys.stderr)
704                    return v
705                except Exception as ex:
706                    raise ValueError('can not parse field %s = %r' % (field_uri,
707                                                                      (v.get_default_value(), v.raw_value))) from ex
708
709        print("[%s = %s] " % (field_uri, "null"), file=sys.stderr)
710        return nullField
711
712    elif is_layer_field_container(field_uri):
713        from pktverify.layer_fields_container import LayerFieldsContainer
714        return LayerFieldsContainer(packet, field_uri)
715    else:
716        raise NotImplementedError('Field %s is not valid, please add it to `_LAYER_FIELDS`' % field_uri)
717
718
719def check_layer_field_exists(packet, field_uri):
720    """
721    Check if a given layer field URI exists in the packet.
722
723    :param packet: The packet to check.
724    :param field_uri: The layer field URI.
725    :return: Whether the layer field URI exists in the packet.
726    """
727    assert isinstance(packet, RawPacket)
728    secs = field_uri.split('.')
729    layer_name = secs[0]
730
731    if not is_layer_field(field_uri) and not is_layer_field_container(field_uri):
732        raise NotImplementedError('%s is neither a field or field container' % field_uri)
733
734    candidate_layers = _get_candidate_layers(packet, layer_name)
735    for layers in candidate_layers:
736        for layer in layers:
737            for k, v in layer._all_fields.items():
738                if k == field_uri or k.startswith(field_uri + '.'):
739                    return True
740
741    return False
742
743
744def _get_candidate_layers(packet, layer_name):
745    if layer_name == 'thread_meshcop':
746        candidate_layer_names = ['thread_meshcop', 'mle', 'coap', 'thread_bl', 'thread_nm']
747    elif layer_name == 'thread_nwd':
748        candidate_layer_names = ['mle', 'thread_address', 'thread_diagnostic']
749    elif layer_name == 'wpan':
750        candidate_layer_names = ['wpan', 'mle']
751    elif layer_name == 'ip':
752        candidate_layer_names = ['ip', 'ipv6']
753    elif layer_name == 'thread_bcn':
754        candidate_layer_names = ['thread_bcn']
755    else:
756        candidate_layer_names = [layer_name]
757
758    layers = []
759    for ln in candidate_layer_names:
760        if hasattr(packet, ln):
761            layers.append(packet.get_multiple_layers(ln))
762
763    return layers
764