#!/usr/bin/python3 ''' Sniff stations on one set of radios using secondary radios. make sure pexpect is installed: $ sudo yum install python3-pexpect $ sudo yum install python3-xlsxwriter You might need to install pexpect-serial using pip: $ pip3 install pexpect-serial $ pip3 install XlsxWriter The user is responsible for setting up the stations oustide of this script, however. When specifying ports, if the port starts with [Number]., like 1.eth1, then the 1 specifies the resource ID. ./lf_sniff.py --lfmgr 192.168.100.178 \ --station "1.wlan0 1.wlan1" --sniffer_radios "2.wiphy0 2.wiphy1" \ --duration 5 ''' # TODO: Maybe HTML output too? # TODO: Allow selecting tabs or commas for output files import sys if sys.version_info[0] != 3: print("This script requires Python 3") exit() import re import logging import time from time import sleep import pprint import argparse import subprocess import xlsxwriter from subprocess import PIPE NL = "\n" CR = "\r\n" Q = '"' A = "'" FORMAT = '%(asctime)s %(name)s %(levelname)s: %(message)s' lfmgr = "127.0.0.1" lfstation = "1.wlan0" sniffer_radios = "2.wiphy0" upstream = "" dur = 5 * 60 moni_flags = "0x100000000" # 160Mhz mode enabled # rssi_adjust = (current_nf - nf_at_calibration) def usage(): print("$0 ") print("--station: LANforge station names (1.wlan0 1.wlan1 ...)") print("--sniffer_radios: LANforge radios to use as sniffers (2.wiphy0 2.wiphy1 ...)") print("--lfmgr: LANforge manager IP address") print("--duration: Duration to run traffic, in minutes") print("--moni_flags: Monitor flags (see LANforge CLI help for set_wifi_monitor command) Default enables 160Mhz") print("--upstreams: Upstream ports to sniff (1.eth1 ...)") print("-h|--help") def main(): global lfmgr global lfstation global upstream global sniffer_radios global dur global moni_flags parser = argparse.ArgumentParser(description="Sniffer control Script") parser.add_argument("--sniffer_radios", type=str, help="LANforge sniffer radios to use (2.wiphy0 2.wiphy1 ...)") parser.add_argument("--station", type=str, help="LANforge stations to use (1.wlan0 1.wlan1 etc)") parser.add_argument("--lfmgr", type=str, help="LANforge Manager IP address") parser.add_argument("--duration", type=float, help="Duration to sniff, in minutes") parser.add_argument("--moni_flags", type=str, help="Monitor port flags, see LANforge CLI help for set_wifi_monitor. Default enables 160Mhz") parser.add_argument("--upstreams", type=str, help="Upstream ports to sniff (1.eth1 ...)") args = None try: args = parser.parse_args() if (args.station != None): lfstation = args.station if (args.upstreams != None): upstream = args.upstreams if (args.sniffer_radios != None): sniffer_radios = args.sniffer_radios if (args.lfmgr != None): lfmgr = args.lfmgr if (args.duration != None): dur = args.duration * 60 if (args.moni_flags != None): moni_flags = args.moni_flags filehandler = None except Exception as e: logging.exception(e); usage() exit(2); # Use subprocess.check_output("Cmd") to utilize existing LF scripts. upstreams = upstream.split() lfstations = lfstation.split() radios = sniffer_radios.split() monis_n = [] # monitor device names monis_r = [] # monitor device resources idx = 0 for sta in lfstations: sta_resource = "1" sta_name = sta; if sta[0].isdigit(): tmpa = sta.split(".", 1); sta_resource = tmpa[0]; sta_name = tmpa[1]; # Assume station is up and/or something else is bringing it up channel = 36 bsssid = "00:00:00:00:00:00" i = 0 wait_ip_print = False; wait_assoc_print = False; # Wait until LANforge station connects while True: port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", sta_resource, "--port_name", sta_name, "--show_port", "AP,IP,Mode,NSS,Bandwidth,Probed-Channel,Signal,Noise,Status,RX-Rate"], stdout=PIPE, stderr=PIPE); pss = port_stats.stdout.decode('utf-8', 'ignore'); _status = None _ip = None for line in pss.splitlines(): #print("line: %s\n"%line) m = re.search('AP:\s+(.*)', line) if (m != None): bssid = m.group(1) m = re.search('Status:\s+(.*)', line) if (m != None): _status = m.group(1) m = re.search('Probed-Channel:\s+(.*)', line) if (m != None): channel = m.group(1) m = re.search('IP:\s+(.*)', line) if (m != None): _ip = m.group(1) #print("IP %s Status %s"%(_ip, _status)) if (_status == "Authorized"): if ((_ip != None) and (_ip != "0.0.0.0")): print("Station is associated with IP address.") break else: if (not wait_ip_print): print("Waiting for station %s.%s to get IP Address."%(sta_resource, sta_name)) wait_ip_print = True else: if (not wait_assoc_print): print("Waiting up to 180s for station %s.%s to associate."%(sta_resource, sta_name)) wait_assoc_print = True i = i + 1 # We wait a fairly long time since AP will take a long time to start on a CAC channel. if (i > 180): err = "ERROR: Station did not connect within 180 seconds." print(err) e_tot += err e_tot += " " if (args.wait_forever): print("Will continue waiting, you may wish to debug the system...") i = 0 else: break time.sleep(1) # Get station AID and other info port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--cli_cmd", "probe_port 1 %s %s"%(sta_resource, sta_name)], stdout=PIPE, stderr=PIPE); pss = port_stats.stdout.decode('utf-8', 'ignore'); aid = 0 for line in pss.splitlines(): m = re.search('Local-AID:\s+(.*)', line) if (m != None): aid = m.group(1) break # Create monitor on radio X rad = radios[idx] rad_resource = "1" rad_name = rad; #print("idx: %i moni: %s\n"%(idx, moni)) if rad[0].isdigit(): tmpa = rad.split(".", 1) rad_resource = tmpa[0] rad_name = tmpa[1] # Set channel on the radio subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", rad_resource, "--port_name", rad_name, "--set_channel", "%s"%channel]); # Get radio index so we can name the monitor similar to how the system would auto-create them port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", rad_resource, "--port_name", rad_name, "--show_port", "Port"], stdout=PIPE, stderr=PIPE); pss = port_stats.stdout.decode('utf-8', 'ignore'); moni_idx = "0" for line in pss.splitlines(): m = re.search('Port:\s+(.*)', line) if (m != None): moni_idx = m.group(1) # Create monitor interface mname = "moni%sa"%(moni_idx); subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--cli_cmd", "add_monitor 1 %s %s %s %s 0xFFFFFFFFFFFF %s %s"%(rad_resource, rad_name, mname, moni_flags, aid, bssid)]) print("Created monitor interface: %s on resource %s\n"%(mname, rad_resource)) monis_n.append(mname) monis_r.append(rad_resource) idx = idx + 1 # Start sniffing on all monitor ports idx = 0 sflags = "0x02" # dumpcap, no terminal for m in monis_n: r = monis_r[idx] # Wait for monitor to be non-phantom isph = True while isph: port_stats = subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--card", r, "--port_name", m, "--show_port", "Current"], stdout=PIPE, stderr=PIPE); pss = port_stats.stdout.decode('utf-8', 'ignore'); for line in pss.splitlines(): #print("line: %s\n"%line) ma = re.search('Current:\s+(.*)', line) if (ma != None): cf = ma.group(1); isph = False for f in cf.split(): if (f == "PHANTOM"): isph = True break if isph: print("Waiting for monitor port %s.%s to become non-phantom\n"%(r, m)); sleep(1) print("Starting sniffer on port %s.%s for %s seconds, saving to file %s.pcap on resource %s\n"%(r, m, dur, m, r)) subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--cli_cmd", "sniff_port 1 %s %s NA %s %s.pcap %i"%(r, m, sflags, m, int(dur))]); idx = idx + 1 # Start sniffing on all upstream ports for u in upstreams: u_resource = "1" u_name = u; if u[0].isdigit(): tmpa = u.split(".", 1); u_resource = tmpa[0]; u_name = tmpa[1]; print("Starting sniffer on upstream port %s.%s for %s seconds, saving to file %s.pcap on resource %s\n"%(u_resource, u_name, dur, u_name, u_resource)) subprocess.run(["./lf_portmod.pl", "--manager", lfmgr, "--cli_cmd", "sniff_port 1 %s %s NA %s %s.pcap %i"%(u_resource, u_name, sflags, u_name, int(dur))]); # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- if __name__ == '__main__': main() #### #### ####