]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add script to convert .pcap payload to '{decode,encode}-proto' format
authorJorge Pereira <jpereiran@gmail.com>
Wed, 20 Nov 2019 21:24:08 +0000 (18:24 -0300)
committerAlan DeKok <aland@freeradius.org>
Tue, 26 Nov 2019 14:45:59 +0000 (09:45 -0500)
It was created to help us to create the dhcpv6 unit tests based on
the tcpdump .pcap files.

scripts/pcap2decode-proto.py [new file with mode: 0755]

diff --git a/scripts/pcap2decode-proto.py b/scripts/pcap2decode-proto.py
new file mode 100755 (executable)
index 0000000..bcf93da
--- /dev/null
@@ -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 <jpereira@freeradius.org>
+#  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()