Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 174 additions & 115 deletions node/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,125 +15,194 @@
import logging
import statistics
import atexit
import serial

logger = logging.getLogger('scan.py')

import requests

def restart_wifi():
os.system("/sbin/ifdown --force wlan0")
os.system("/sbin/ifup --force wlan0")
os.system("iwconfig wlan0 mode managed")
while True:
ping_response = subprocess.Popen(
["/bin/ping", "-c1", "-w100", "lf.internalpositioning.com"], stdout=subprocess.PIPE).stdout.read()
if '64 bytes' in ping_response.decode('utf-8'):
break
time.sleep(1)


def num_wifi_cards():
cmd = 'iwconfig'
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
output = p.stdout.read().decode('utf-8')
return output.count("wlan")

class BaseScan:
def __init__(self, timeout):
self.timeout = timeout

def process_scan(time_window):
logger.debug("Reading files...")
output = ""
maxFileNumber = -1
fileNameToRead = ""
for filename in glob.glob("/tmp/tshark-temp*"):
fileNumber = int(filename.split("_")[1])
if fileNumber > maxFileNumber:
maxFileNumber = fileNumber
fileNameToRead = filename

logger.debug("Reading from %s" % fileNameToRead)
cmd = subprocess.Popen(("tshark -r "+fileNameToRead+" -T fields -e frame.time_epoch -e wlan.sa -e wlan.bssid -e radiotap.dbm_antsignal").split(
), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output += cmd.stdout.read().decode('utf-8')

timestamp_threshold = float(time.time()) - float(time_window)
fingerprints = {}
relevant_lines = 0
for line in output.splitlines():
try:
timestamp, mac, mac2, power_levels = line.split("\t")

if mac == mac2 or float(timestamp) < timestamp_threshold or len(mac) == 0:
def get_payload(self, fingerprints):
# Compute medians
fingerprints2 = []
for mac in fingerprints:
if len(fingerprints[mac]) == 0:
continue

relevant_lines+=1
rssi = power_levels.split(',')[0]
if len(rssi) == 0:
print(mac)
print(fingerprints[mac])
fingerprints2.append(
{"mac": mac, "rssi": int(statistics.median(fingerprints[mac]))})

logger.debug("Found %d fingerprints" % len(fingerprints2))

payload = {
"node": socket.gethostname(),
"signals": fingerprints2,
"timestamp": int(
time.time())}
logger.debug(payload)
return payload


class SerialScan(BaseScan):
"""
Class to implement mac/rssi finding via a serial device that outputs
periodic list of mac/rssi.

Protocol is to pass lines in the format of "mac_address rssi packet_count"
at 115200bps.
"""
def __init__(self, interface, timeout):
BaseScan.__init__(self, timeout)

self.interface = interface

def do_scan(self):
# Sleep then read all the data non-blocking until nothing left.
# Hopefully the internal buffers are big enough to hold it all.
ser = serial.Serial("/dev/%s" % self.interface, baudrate = 115200, timeout = 0);
time.sleep(self.timeout)
inp = ''
while True:
read = ser.read(10000)
if len(read) == 0:
break;
inp += str(read, encoding='ascii')
ser.close();

fingerprints = {}
for line in inp.splitlines():
if line.startswith("-"):
continue

mac, rssi, packets = line.split(" ")
if mac not in fingerprints:
fingerprints[mac] = []
fingerprints[mac].append(float(rssi))
except:
pass
logger.debug("..done")

# Compute medians
fingerprints2 = []
for mac in fingerprints:
if len(fingerprints[mac]) == 0:
continue
print(mac)
print(fingerprints[mac])
fingerprints2.append(
{"mac": mac, "rssi": int(statistics.median(fingerprints[mac]))})

logger.debug("Processed %d lines, found %d fingerprints in %d relevant lines" %
(len(output.splitlines()), len(fingerprints2),relevant_lines))

payload = {
"node": socket.gethostname(),
"signals": fingerprints2,
"timestamp": int(
time.time())}
logger.debug(payload)
return payload


def run_command(command):
p = subprocess.Popen(
command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return iter(p.stdout.readline, b'')


def tshark_is_running():
ps_output = subprocess.Popen(
"ps aux".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ps_stdout = ps_output.stdout.read().decode('utf-8')
isRunning = 'tshark' in ps_stdout and '[tshark]' not in ps_stdout
logger.debug("tshark is running: " + str(isRunning))
return isRunning


def start_scan(wlan):
if not tshark_is_running():

for i in range(0, int(packets)):
fingerprints[mac].append(float(rssi))

return self.get_payload(fingerprints)


class WifiScan(BaseScan):
"""
Class to implement mac/rssi finding via a tshark scan of a device in
monitor mode
"""
def __init__(self, interface, timeout, single_wifi):
BaseScan.__init__(self, timeout)

atexit.register(self.exit_handler)
self.single_wifi = single_wifi
self.interface = interface

def do_scan(self):
if self.single_wifi:
logger.debug("Stopping scan...")
if self.tshark_is_running():
self.stop_scan()
logger.debug("Stopping monitor mode...")
self.restart_wifi()
logger.debug("Restarting WiFi in managed mode...")

if not self.tshark_is_running():
self.start_scan()

time.sleep( self.timeout )
return self.process_scan()

def exit_handler(self):
print("Exiting...stopping scan..")
os.system("pkill -9 tshark")

def restart_wifi(self):
os.system("/sbin/ifdown --force wlan0")
os.system("/sbin/ifup --force wlan0")
os.system("iwconfig wlan0 mode managed")
while True:
ping_response = subprocess.Popen(
["/bin/ping", "-c1", "-w100", "lf.internalpositioning.com"], stdout=subprocess.PIPE).stdout.read()
if '64 bytes' in ping_response.decode('utf-8'):
break
time.sleep(1)


def process_scan(self):
logger.debug("Reading files...")
output = ""
maxFileNumber = -1
fileNameToRead = ""
for filename in glob.glob("/tmp/tshark-temp*"):
fileNumber = int(filename.split("_")[1])
if fileNumber > maxFileNumber:
maxFileNumber = fileNumber
fileNameToRead = filename

logger.debug("Reading from %s" % fileNameToRead)
cmd = subprocess.Popen(("tshark -r "+fileNameToRead+" -T fields -e frame.time_epoch -e wlan.sa -e wlan.bssid -e radiotap.dbm_antsignal").split(
), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output += cmd.stdout.read().decode('utf-8')

timestamp_threshold = float(time.time()) - float(self.timeout)
fingerprints = {}
relevant_lines = 0
for line in output.splitlines():
try:
timestamp, mac, mac2, power_levels = line.split("\t")

if mac == mac2 or float(timestamp) < timestamp_threshold or len(mac) == 0:
continue

relevant_lines+=1
rssi = power_levels.split(',')[0]
if len(rssi) == 0:
continue

if mac not in fingerprints:
fingerprints[mac] = []
fingerprints[mac].append(float(rssi))
except:
pass
logger.debug("..done (%d lines of which %d were relevant)" % (len(output.splitlines)(), relevant_lines))

return self.get_payload(fingerprints)

def tshark_is_running(self):
ps_output = subprocess.Popen(
"ps aux".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ps_stdout = ps_output.stdout.read().decode('utf-8')
isRunning = 'tshark' in ps_stdout and '[tshark]' not in ps_stdout
logger.debug("tshark is running: " + str(isRunning))
return isRunning


def start_scan(self):
# Remove previous files
for filename in glob.glob("/tmp/tshark-temp*"):
os.remove(filename)
subprocess.Popen(("/usr/bin/tshark -I -i " + wlan + " -b files:4 -b filesize:1000 -w /tmp/tshark-temp").split(),
subprocess.Popen(("/usr/bin/tshark -I -i " + self.interface + " -b files:4 -b filesize:1000 -w /tmp/tshark-temp").split(),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if tshark_is_running():
if self.tshark_is_running():
logger.info("Starting scan")


def stop_scan():
if tshark_is_running():
def stop_scan(self):
os.system("pkill -9 tshark")
if not tshark_is_running():
if not self.tshark_is_running():
logger.info("Stopped scan")


def num_wifi_cards():
cmd = 'iwconfig'
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
output = p.stdout.read().decode('utf-8')
return output.count("wlan")

def main():
# Check if SUDO
# http://serverfault.com/questions/16767/check-admin-rights-inside-python-script
Expand All @@ -156,7 +225,7 @@ def main():
"-i",
"--interface",
default=default_wlan,
help="Interface to listen on - default %s" % default_wlan)
help="Interface or tty to listen on - default %s" % default_wlan)
parser.add_argument(
"-t",
"--time",
Expand Down Expand Up @@ -202,16 +271,14 @@ def main():
print("Using group " + args.group)
logger.debug("Using group " + args.group)

if args.interface.startswith("ttyS"):
scanner = SerialScan(args.interface, float(args.time))
else:
scanner = WifiScan(args.interface, float(args.time), args.single_wifi)

while True:
try:
if args.single_wifi:
logger.debug("Stopping scan...")
stop_scan()
logger.debug("Stopping monitor mode...")
restart_wifi()
logger.debug("Restarting WiFi in managed mode...")
start_scan(args.interface)
payload = process_scan(args.time)
payload = scanner.do_scan()
payload['group'] = args.group
if len(payload['signals']) > 0:
r = requests.post(
Expand All @@ -220,16 +287,8 @@ def main():
json=payload)
logger.debug(
"Sent to server with status code: " + str(r.status_code))
time.sleep(float(args.time)) # Wait before getting next window
except Exception:
logger.error("Fatal error in main loop", exc_info=True)
time.sleep(float(args.time))


def exit_handler():
print("Exiting...stopping scan..")
os.system("pkill -9 tshark")

if __name__ == "__main__":
atexit.register(exit_handler)
main()