diff --git a/pyroute2/decoder/loader.py b/pyroute2/decoder/loader.py index b04f8ffee..6feccb501 100644 --- a/pyroute2/decoder/loader.py +++ b/pyroute2/decoder/loader.py @@ -1,9 +1,12 @@ import io +import json import shlex import struct from collections import namedtuple from importlib import import_module +from pyroute2.common import hexdump + PcapMetaData = namedtuple( "pCAPMetaData", ( @@ -26,6 +29,46 @@ ) +class Message: + + def __init__(self, packet_header, ll_header, met, data): + self.packet_header = packet_header + self.ll_header = ll_header + self.cls = None + self.met = met + self.data = data + self.exception = None + self.msg = None + + def get_message_class(self): + if hasattr(self.met, 'msg_map'): + (msg_type,) = struct.unpack('H', self.data[4:6]) + return self.met.msg_map[msg_type] + return self.met + + def decode(self): + try: + self.cls = self.get_message_class() + self.msg = self.cls(self.data) + self.msg.decode() + self.msg = self.msg.dump() + except Exception as e: + self.exception = repr(e) + self.msg = hexdump(self.data) + + def __repr__(self): + return json.dumps( + { + "pcap header": repr(self.packet_header), + "link layer header": repr(self.ll_header), + "message class": repr(self.cls), + "exception": self.exception, + "data": self.msg, + }, + indent=4, + ) + + class Match: @staticmethod @@ -127,9 +170,6 @@ def decode_ll_header(self, data, offset): *struct.unpack(">HHIIHH", data[offset : offset + 16]) + (16,) ) - def get_message_class(self, packet_header, ll_header, data, offset): - return self.cls - def match(self, packet_header, ll_header, data, offset): stack = [] for method in self.parser.filters: @@ -144,11 +184,13 @@ def data(self): ll_header = self.decode_ll_header(self.raw, self.offset) self.offset += ll_header.header_len length = packet_header.incl_len - ll_header.header_len - met = self.get_message_class( - packet_header, ll_header, self.raw, self.offset - ) if self.match(packet_header, ll_header, self.raw, self.offset): - msg = met(self.raw[self.offset : self.offset + length]) + msg = Message( + packet_header, + ll_header, + self.cls, + self.raw[self.offset : self.offset + length], + ) msg.decode() yield msg self.offset += length diff --git a/tests/decoder/decoder.py b/tests/decoder/decoder.py index cb2685a62..1b7b7374f 100644 --- a/tests/decoder/decoder.py +++ b/tests/decoder/decoder.py @@ -12,30 +12,73 @@ Module is a name within rtnl hierarchy. File should be a binary data in the escaped string format (see samples). ''' -import sys +import argparse +import struct from importlib import import_module from pprint import pprint from pyroute2.common import hexdump, load_dump -mod = sys.argv[1] +argument_parser = argparse.ArgumentParser() +argument_parser.add_argument( + 'message_class', help='message class to use for decoding the data' +) +argument_parser.add_argument('data_file', help='data dump file') +argument_parser.add_argument( + '-f', '--format', default='hex', help='data file format: hex, pcap' +) +argument_parser.add_argument( + '-m', + '--match', + default=0, + type=int, + help='match protocol family (only for pcap data)', +) +args = argument_parser.parse_args() + +mod = args.message_class mod = mod.replace('/', '.') -f = open(sys.argv[2], 'r') s = mod.split('.') package = '.'.join(s[:-1]) module = s[-1] +print(package, module) m = import_module(package) met = getattr(m, module) +data = None - -data = load_dump(f) +if args.format == 'hex': + with open(args.data_file, 'r') as f: + data = load_dump(f) +elif args.format == 'pcap': + with open(args.data_file, 'rb') as f: + data = f.read() offset = 0 inbox = [] +if args.format == 'pcap': + # read the global header + pcap_header = struct.unpack('IHHiIII', data[:24]) + offset = 24 while offset < len(data): - msg = met(data[offset:]) - msg.decode() - print(hexdump(msg.data)) - pprint(msg) - print('.' * 40) - offset += msg['header']['length'] + msg = None + if args.format == 'pcap': + packet_header = struct.unpack('IIII', data[offset : offset + 16]) + print('pcap packet header', hexdump(data[offset : offset + 16])) + offset += 16 + length = packet_header[2] + print('length', length) + print('link layer header', hexdump(data[offset : offset + 16])) + ll_header = struct.unpack('HHIIHH', data[offset : offset + 16]) + print('family', ll_header[5]) + if args.match == ll_header[5]: + msg = met(data[offset + 16 : offset + length]) + offset += length + else: + msg = met(data[offset:]) + if msg is not None: + msg.decode() + print(hexdump(msg.data)) + pprint(msg) + print('.' * 40) + if args.format == 'hex': + offset += msg['header']['length']