From: Jorge Pereira Date: Wed, 20 Nov 2019 21:24:08 +0000 (-0300) Subject: Add script to convert .pcap payload to '{decode,encode}-proto' format X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87d95bc1beb2f67f93b887835caf0affb79a1ac8;p=thirdparty%2Ffreeradius-server.git Add script to convert .pcap payload to '{decode,encode}-proto' format It was created to help us to create the dhcpv6 unit tests based on the tcpdump .pcap files. --- diff --git a/scripts/pcap2decode-proto.py b/scripts/pcap2decode-proto.py new file mode 100755 index 00000000000..bcf93dac312 --- /dev/null +++ b/scripts/pcap2decode-proto.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2019 NetworkRADIUS SARL (legal@networkradius.com) +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# +# Author: Jorge Pereira +# Version $Id$ +# + +# +# EXAMPLE: +# +# 1. wget https://github.com/the-tcpdump-group/tcpdump/raw/master/tests/dhcpv6-sip-server-d.pcap +# 2. ./scripts/pcap2decode-proto.py -p dhcpv6 -f ./dhcpv6-sip-server-d.pcap > src/tests/unit/protocols/dhcpv6/packet_sip-server-d.txt +# 3. build/make/jlibtool --quiet --mode=execute build/bin/local/unit_test_attribute -xx -D share/dictionary src/tests/unit/protocols/dhcpv6/packet_sip-server-d.txt +# +# TODO: +# +# - verify if the jlibtool and unit_test_attribute exist +# - check if we could call dhcpv6_tp_decode_pair direct from libfreeradius-dhcpv6.{so,dylib} +# + +from __future__ import print_function +import argparse +import tempfile +import traceback +import sys +import os +import re + +unit_attr = "build/make/jlibtool --quiet --mode=execute " \ + "build/bin/local/unit_test_attribute -xx " \ + "-D share/dictionary " \ + "-d src/tests/unit" + +# print to stderr +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + +try: + from scapy.all import * +except Exception as e: + eprint("** ERROR: We need the 'scapy' package. e.g: pip3 install scapy") + sys.exit(-1) + +# It does like: unit_test_attribute ... /path/file.txt | sed '/got.*:/!d; s/.\{2\}/& /g; s/ $//g' +def unit_lookup_payload2attrs(proto, payload): + # Generating lookup file + fp = tempfile.NamedTemporaryFile(mode = "w+", delete = False) + fp.write("# Using {}\n".format(fp.name)) + fp.write("proto {}\n".format(proto)) + fp.write("proto-dictionary {}\n".format(proto)) + fp.write("\n") + fp.write("decode-proto {}\n".format(payload)) + fp.write("match Packet-Type = 1\n") + fp.flush() + fp.close() + + # call the unit_test_attribute + cmd_unit = "{} {}".format(unit_attr, fp.name) + cmd_out = os.popen(cmd_unit).read() + os.remove(fp.name) + + match = re.search(r"(.\sgot\s+:)\s(.*)", cmd_out) + if not match: + eprint("# ERROR: We didn't find the 'got' token in: {}".format(cmd_out)) + return None + + return match.group(2) + +def load_args(): + parser = argparse.ArgumentParser( + description = "Convert .pcap file to FreeRADIUS unit_test_attribute(encode/decode) format. {almost, try}", + ) + parser.add_argument( + "-f", + dest = "pcap_file", + help = "pcap file to extract the dhcpv6 payload", + required = True + ) + parser.add_argument( + "-p", + dest = "decode_proto", + help = "Protocol to be used in: 'proto $proto' and 'proto-dictionary $proto'", + required = True + ) + parser.add_argument( + "-b", + dest = "both", + help = "Perform the lookup adding the 'encode-proto attrs' and 'decode_proto -'", + action='store_true' + ) + return parser.parse_args() + +def _main(): + try: + args = load_args() + count_pkt = 0 + count_mat = 0 + pcap = rdpcap(args.pcap_file) + + print("# -*- text -*-") + print("# ATTENTION: It was generated automatically, be careful! :)") + print("# Based on {}".format(os.path.basename(args.pcap_file))) + print("#") + print("") + print("proto {}".format(args.decode_proto)) + print("proto-dictionary {}".format(args.decode_proto)) + print("") + count_mat += 2 + + for pkt in pcap: + # statements + count_pkt += 1 + print("#") + print("# {}.".format(count_pkt)) + print("#") + + # get the payload description, remove '#' and trim() spaces. + app = pkt.getlayer(3) + packet_desc = app.show(dump=True, indent=1).replace("#", "") + packet_desc = re.sub('^', "# ", packet_desc, flags=re.MULTILINE) + packet_desc = re.sub(' $', "", packet_desc, flags=re.MULTILINE) + print(packet_desc.strip()) + + # Convert the payload to hex separated by space. + payload = "" + for d in app.build(): + payload += "{:02x} ".format(d) + + # trim the left/right + payload = payload.strip() + + # lookup the attrs from the payload + attrs = unit_lookup_payload2attrs(args.decode_proto, payload) + if not attrs: + raise Exception("Error", "Problems to convert the payload to attrs") + + if args.both: + count_mat += 4 + print("encode-proto {}".format(attrs)) + print("match {}".format(payload)) + print("") + print("decode-proto -") + print("match {}".format(attrs)) + print("") + else: + count_mat += 2 + print("decode-proto {}".format(payload)) + print("match {}".format(attrs)) + print("") + + # append the 'count' + print("count") + print("match {}".format(count_mat)) + print("") + + except Exception as e: + eprint("** ERROR: Something wrong:\n {}\n".format(str(e))) + traceback.print_exc() + sys.exit(-1) + +if __name__ == "__main__": + _main()