--- /dev/null
+#!/usr/local/bin/python2.7
+
+""" -----------------------------------------------------------------------------
+ CAPture - a pcap file analyzer and report generator
+ (c) 2017 - Rahmadi Trimananda
+ University of California, Irvine - Programming Language and Systems
+ -----------------------------------------------------------------------------
+ Credits to tutorial: https://dpkt.readthedocs.io/en/latest/
+ -----------------------------------------------------------------------------
+"""
+
+import datetime
+import dpkt
+from dpkt.compat import compat_ord
+
+import socket
+import sys
+
+""" -----------------------------------------------------------------------------
+ Global variable declarations
+ -----------------------------------------------------------------------------
+"""
+# Command line arguments
+INPUT = "-i"
+OUTPUT = "-o"
+POINT_TO_MANY = "-pm"
+VERBOSE = "-v"
+
+
+def mac_addr(address):
+ # Courtesy of: https://dpkt.readthedocs.io/en/latest/
+ """ Convert a MAC address to a readable/printable string
+ Args:
+ address (str): a MAC address in hex form (e.g. '\x01\x02\x03\x04\x05\x06')
+ Returns:
+ str: Printable/readable MAC address
+ """
+ return ':'.join('%02x' % compat_ord(b) for b in address)
+
+
+def inet_to_str(inet):
+ # Courtesy of: https://dpkt.readthedocs.io/en/latest/
+ """ Convert inet object to a string
+ Args:
+ inet (inet struct): inet network address
+ Returns:
+ str: Printable/readable IP address
+ """
+ # First try ipv4 and then ipv6
+ try:
+ return socket.inet_ntop(socket.AF_INET, inet)
+ except ValueError:
+ return socket.inet_ntop(socket.AF_INET6, inet)
+
+
+def show_usage():
+ """ Show usage of this Python script
+ """
+ print "Usage: python CAPture.py [ -i <file-name>.pcap ] [ -o <file-name>.pcap ] [ -pm ] [ -v ]"
+ print
+ print "[ -o ] = output file"
+ print "[ -pm ] = point-to-many analysis"
+ print "[ -v ] = verbose output"
+ print "By default, this script does simple statistical analysis of IP, TCP, and UDP packets."
+ print "(c) 2017 - University of California, Irvine - Programming Language and Systems"
+
+
+def show_progress(verbose, counter):
+ """ Show packet processing progress
+ Args:
+ verbose: verbose output (True/False)
+ counter: counter of all packets
+ """
+ if verbose:
+ print "Processing packet number: ", counter
+ else:
+ if counter % 100000 == 0:
+ print "Processing %s packets..." % counter
+
+
+def show_summary(counter, ip_counter, tcp_counter, udp_counter):
+ """ Show summary of statistics of PCAP file
+ Args:
+ counter: counter of all packets
+ ip_counter: counter of all IP packets
+ tcp_counter: counter of all TCP packets
+ udp_counter: counter of all UDP packets
+ """
+ print
+ print "Total number of packets in the pcap file: ", counter
+ print "Total number of ip packets: ", ip_counter
+ print "Total number of tcp packets: ", tcp_counter
+ print "Total number of udp packets: ", udp_counter
+ print
+
+
+def save_to_file(tbl_header, dictionary, filename_out):
+ """ Show summary of statistics of PCAP file
+ Args:
+ tbl_header: header for the saved table
+ dictionary: dictionary to be saved
+ filename_out: file name to save
+ """
+ # Appending, not overwriting!
+ f = open(filename_out, 'a')
+ # Write the table header
+ f.write("\n\n" + str(tbl_header) + "\n");
+ # Iterate over dictionary and write (key, value) pairs
+ for key, value in dictionary.iteritems():
+ f.write(str(key) + ", " + str(value) + "\n")
+
+ f.close()
+ print "Writing output to file: ", filename_out
+
+
+def statistical_analysis(verbose, pcap, counter, ip_counter, tcp_counter, udp_counter):
+ """ This is the default analysis of packet statistics (generic)
+ Args:
+ verbose: verbose output (True/False)
+ pcap: object that handles PCAP file content
+ counter: counter of all packets
+ ip_counter: counter of all IP packets
+ tcp_counter: counter of all TCP packets
+ udp_counter: counter of all UDP packets
+ """
+ for time_stamp, packet in pcap:
+
+ counter += 1
+ eth = dpkt.ethernet.Ethernet(packet)
+
+ if verbose:
+ # Print out the timestamp in UTC
+ print "Timestamp: ", str(datetime.datetime.utcfromtimestamp(time_stamp))
+ # Print out the MAC addresses
+ print "Ethernet frame: ", mac_addr(eth.src), mac_addr(eth.dst), eth.data.__class__.__name__
+
+ # Process only IP data
+ if not isinstance(eth.data, dpkt.ip.IP):
+
+ is_ip = False
+ if verbose:
+ print "Non IP packet type not analyzed... skipping..."
+ else:
+ is_ip = True
+
+ if is_ip:
+ ip = eth.data
+ ip_counter += 1
+
+ # Pull out fragment information (flags and offset all packed into off field, so use bitmasks)
+ do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)
+ more_fragments = bool(ip.off & dpkt.ip.IP_MF)
+ fragment_offset = ip.off & dpkt.ip.IP_OFFMASK
+
+ if verbose:
+ # Print out the complete IP information
+ print "IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)\n" % \
+ (inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment,
+ more_fragments, fragment_offset)
+
+ # Count TCP packets
+ if ip.p == dpkt.ip.IP_PROTO_TCP:
+ tcp_counter += 1
+
+ # Count UDP packets
+ if ip.p == dpkt.ip.IP_PROTO_UDP:
+ udp_counter += 1
+
+ show_progress(verbose, counter)
+
+ # Print general statistics
+ show_summary(counter, ip_counter, tcp_counter, udp_counter)
+
+
+def point_to_many_analysis(filename_out, dev_add, verbose, pcap, counter, ip_counter,
+ tcp_counter, udp_counter):
+ """ This analysis presents how 1 device (MAC address or IP address) communicates
+ to every other device in the analyzed PCAP file.
+ Args:
+ dev_add: device address (MAC or IP address)
+ verbose: verbose output (True/False)
+ pcap: object that handles PCAP file content
+ counter: counter of all packets
+ ip_counter: counter of all IP packets
+ tcp_counter: counter of all TCP packets
+ udp_counter: counter of all UDP packets
+ """
+ # Dictionary that preserves the mapping between destination address to frequency
+ mac2freq = dict()
+ ip2freq = dict()
+ for time_stamp, packet in pcap:
+
+ counter += 1
+ eth = dpkt.ethernet.Ethernet(packet)
+
+ # Save the timestamp and MAC addresses
+ tstamp = str(datetime.datetime.utcfromtimestamp(time_stamp))
+ mac_src = mac_addr(eth.src)
+ mac_dst = mac_addr(eth.dst)
+
+ # Process only IP data
+ if not isinstance(eth.data, dpkt.ip.IP):
+
+ is_ip = False
+ if verbose:
+ print "Non IP packet type not analyzed... skipping..."
+ print
+ else:
+ is_ip = True
+
+ if is_ip:
+ ip = eth.data
+ ip_counter += 1
+
+ # Pull out fragment information (flags and offset all packed into off field, so use bitmasks)
+ do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)
+ more_fragments = bool(ip.off & dpkt.ip.IP_MF)
+ fragment_offset = ip.off & dpkt.ip.IP_OFFMASK
+
+ # Save IP addresses
+ ip_src = inet_to_str(ip.src)
+ ip_dst = inet_to_str(ip.dst)
+
+ if verbose:
+ # Print out the complete IP information
+ print "IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)\n" % \
+ (ip_src, ip_dst, ip.len, ip.ttl, do_not_fragment,
+ more_fragments, fragment_offset)
+
+ # Categorize packets based on source device address
+ # Save the destination device addresses (point-to-many)
+ if dev_add == ip_src:
+ if ip_dst in ip2freq:
+ freq = ip2freq[ip_dst]
+ ip2freq[ip_dst] = freq + 1
+ else:
+ ip2freq[ip_dst] = 1
+
+ if dev_add == mac_src:
+ if mac_dst in ip2freq:
+ freq = mac2freq[mac_dst]
+ mac2freq[mac_dst] = freq + 1
+ else:
+ mac2freq[mac_dst] = 1
+
+ # Count TCP packets
+ if ip.p == dpkt.ip.IP_PROTO_TCP:
+ tcp_counter += 1
+
+ # Count UDP packets
+ if ip.p == dpkt.ip.IP_PROTO_UDP:
+ udp_counter += 1
+
+ show_progress(verbose, counter)
+
+ # Print general statistics
+ show_summary(counter, ip_counter, tcp_counter, udp_counter)
+ # Save results into file if filename_out is not empty
+ if not filename_out == "":
+ print "Saving results into file: ", filename_out
+ ip_tbl_header = "Point-to-many Analysis - IP destinations for " + dev_add
+ mac_tbl_header = "Point-to-many Analysis - MAC destinations for " + dev_add
+ save_to_file(ip_tbl_header, ip2freq, filename_out)
+ save_to_file(mac_tbl_header, mac2freq, filename_out)
+ else:
+ print "Output file name is not specified... exitting now!"
+
+
+def parse_cli_args(argv):
+ """ Parse command line arguments and store them in a dictionary
+ Args:
+ argv: list of command line arguments and their values
+ Returns:
+ str: dictionary that maps arguments to their values
+ """
+ options = dict()
+ # First argument is "CAPture.py", so skip it
+ argv = argv[1:]
+ # Loop and collect arguments and their values
+ while argv:
+ print "Examining argument: ", argv[0]
+ # Check the first character of each argv list
+ # If it is a '-' then it is a command line argument
+ if argv[0][0] == '-':
+ if argv[0] == VERBOSE:
+ # We don't have value for the argument VERBOSE
+ options[argv[0]] = argv[0]
+ # Remove one command line argument and its value
+ argv = argv[1:]
+ else:
+ options[argv[0]] = argv[1]
+ # Remove one command line argument and its value
+ argv = argv[2:]
+
+ return options
+
+
+""" -----------------------------------------------------------------------------
+ Main Running Methods
+ -----------------------------------------------------------------------------
+"""
+def main():
+ # Variable declarations
+ global CAP_EXTENSION
+ global PCAP_EXTENSION
+ global VERBOSE
+ global POINT_TO_MANY
+
+ # Counters
+ counter = 0
+ ip_counter = 0
+ tcp_counter = 0
+ udp_counter = 0
+ # Booleans as flags
+ verbose = False
+ is_ip = True
+ is_statistical_analysis = True
+ is_point_to_many_analysis = False
+ # Names
+ filename_in = ""
+ filename_out = ""
+ dev_add = ""
+
+ # Welcome message
+ print
+ print "Welcome to CAPture version 1.0 - A PCAP file instant analyzer!"
+
+ # Get file name from user input
+ # Show usage if file name is not specified (only accept 1 file name for now)
+ if len(sys.argv) < 2:
+ show_usage()
+ print
+ return
+
+ # Check and process sys.argv
+ options = parse_cli_args(sys.argv)
+ for key, value in options.iteritems():
+ # Process "-i" - input PCAP file
+ if key == INPUT:
+ filename_in = value
+ elif key == OUTPUT:
+ filename_out = value
+ elif key == VERBOSE:
+ verbose = True
+ elif key == POINT_TO_MANY:
+ is_statistical_analysis = False
+ is_point_to_many_analysis = True
+ dev_add = value
+
+ # Show manual again if input is not correct
+ if filename_in == "":
+ print "File name is empty!"
+ print
+ show_usage()
+ print
+ return
+
+ # dev_add is needed for these analyses
+ if is_point_to_many_analysis and dev_add == "":
+ print "Device address is empty!"
+ print
+ show_usage()
+ print
+ return
+
+ # One PCAP file name is specified - now analyze!
+ print "Analyzing PCAP file: ", filename_in
+
+ # Opening and analyzing PCAP file
+ f = open(filename_in,'rb')
+ pcap = dpkt.pcap.Reader(f)
+
+ # Choose from the existing options
+ if is_statistical_analysis:
+ statistical_analysis(verbose, pcap, counter, ip_counter, tcp_counter, udp_counter)
+ elif is_point_to_many_analysis:
+ point_to_many_analysis(filename_out, dev_add, verbose, pcap, counter, ip_counter,
+ tcp_counter, udp_counter)
+
+
+if __name__ == "__main__":
+ # call main function since this is being run as the start
+ main()
+
+