-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathslave.py
166 lines (144 loc) · 6.69 KB
/
slave.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import pandas as pd
import argparse
import subprocess
import os
import time
import traceback
import requests
from requests.exceptions import ConnectionError
from tqdm import tqdm
from urllib import urlencode
from urllib import urlopen
from dotenv import load_dotenv, find_dotenv
from StringIO import StringIO
load_dotenv(find_dotenv())
class Slave(object):
LOG_FILE = 'airodump-log'
FNULL = open(os.devnull, 'w')
AIRODUMP_KILL_COMMAND = 'sudo killall airodump-ng'
REMOVE_CSV_FILES_COMMAND = 'sudo rm -rf *.csv'
WAITING_DELAY = 15
UPDATE_INTERVAL = 30
MAXIMUM_AGE = 5 * 60
SERVER_UNREACHABLE_DELAY = 10
def __init__(self, network_name, wifi_interface, slave_id, master_address):
self.network_name = network_name
self.wifi_interface = wifi_interface
self.slave_id = slave_id
self.master_address = master_address
self.access_point_mac = None
self.airodump_command = "sudo airodump-ng --output-format csv --write {} {}".format(Slave.LOG_FILE, wifi_interface)
def start_wifi_monitoring(self):
print "Starting background Wifi monitoring ..."
os.system(Slave.REMOVE_CSV_FILES_COMMAND)
subprocess.Popen(self.airodump_command.split(" "), shell=False, stdout=Slave.FNULL, stderr=subprocess.STDOUT)
def read_df(self):
read_df = False
while not read_df:
try:
csv_content = open(Slave.LOG_FILE + '-01.csv').read().strip()
df = pd.read_csv(StringIO(csv_content), engine='c', error_bad_lines=False)
read_df = True
except:
time.sleep(0.5)
return df
def run(self):
for _ in tqdm(range(Slave.WAITING_DELAY)):
time.sleep(1)
if not os.path.isfile(Slave.LOG_FILE + '-01.csv'):
raise Exception(Slave.LOG_FILE + '-01.csv does not exist. Please make sure "' + self.airodump_command + '" succeeds.')
df = self.read_df()
df = df.rename(index=str, columns=dict(zip(df.columns, [str(c).strip() for c in df.columns])))
try:
df[["ESSID", "BSSID"]] = df[["ESSID", "BSSID"]].apply(lambda x: x.str.strip())
except Exception, e:
raise Exception(str(e) + df.to_string())
self.access_point_mac = df.loc[df["ESSID"] == self.network_name].BSSID.unique()[0]
while True:
for _ in tqdm(range(Slave.UPDATE_INTERVAL)):
time.sleep(1)
df = self.read_df()
df_stations = self.get_stations(df)
if len(df_stations) > 0:
self.send_measurements_to_server(df_stations)
print df_stations.to_string(index=False)
else:
print "No nearby connected Wifi devices found"
def stop_wifi_monitoring(self):
print "Stopping background Wifi monitoring ..."
os.system(Slave.AIRODUMP_KILL_COMMAND)
def get_stations(self, df):
station_index = df.loc[df["BSSID"] == "Station MAC"].index[0]
df_stations = df.loc[station_index:, :]
new_header = df_stations.loc[station_index]
df_stations = df_stations.loc[station_index + 1:]
df_stations = df_stations.rename(columns=new_header)
return self.get_relevant_stations(df_stations)
def get_relevant_stations(self, df):
df_stations = df.copy()
df_stations = df_stations.rename(index=str, columns=dict(zip(df_stations.columns, [str(c).strip() for c in df_stations.columns])))
df_stations = df_stations[["Station MAC", "Last time seen", "BSSID", "Power"]]
time_pattern = ' %Y-%m-%d %H:%M:%S'
try:
df_stations["Last time seen"] = df_stations["Last time seen"].apply(lambda x: int(time.mktime(time.strptime(x, time_pattern))))
except:
raise Exception("Can't parse " + df_stations.to_string())
df_stations["Time delta"] = (time.time() - df_stations["Last time seen"])
df_stations = df_stations[df_stations["Time delta"] < Slave.MAXIMUM_AGE]
#df_stations = df_stations.loc[df_stations["BSSID"] == self.access_point_mac]
df_stations = df_stations[df_stations["Power"].astype(int) < 0]
return df_stations[["Station MAC", "Power", "Last time seen", "Time delta"]]
def send_measurements_to_server(self, df):
data = []
for _, row in df.iterrows():
data.append({
'mac': row["Station MAC"],
'power': row["Power"],
'last_seen': str(row["Last time seen"])
})
request_sent = True
while True:
try:
requests.post(self.master_address + '/update', json={'slave_id': self.slave_id, 'data': data}, verify=False)
except ConnectionError, e:
request_sent = False
print e
if request_sent:
break
time.sleep(Slave.SERVER_UNREACHABLE_DELAY)
def send_notification_to_flowdock(content):
flowdock_token = os.environ.get("FLOW_TOKEN", None)
if flowdock_token:
params = urlencode({
'event': 'message',
'external_user_name': 'ErrorTracker',
'content': content
})
urlopen('https://api.flowdock.com/v1/messages/chat/' + flowdock_token, params)
def main():
parser = argparse.ArgumentParser(description='Monitor nearby Wifi devices that are connected to the same network')
parser.add_argument('-n', '--network', required=True, help='Name of the shared network')
parser.add_argument('-w', '--wifi-interface', required=True, help='Name of the Wifi network interface e.g. wlan0 or wlp3s0')
parser.add_argument('-s', '--slave-id', required=True, help='Unique identifier of the slave device')
parser.add_argument('-m', '--master-address', required=True, help='URL and port of the master device e.g. http://192.168.1.2:5000')
args = parser.parse_args()
network_name, wifi_interface, slave_id, master_address = args.network, args.wifi_interface, args.slave_id, args.master_address
slave = Slave(network_name, wifi_interface, slave_id, master_address)
while True:
try:
slave.start_wifi_monitoring()
slave.run()
except Exception, e:
slave.stop_wifi_monitoring()
stack_trace = traceback.format_exc()
message = "@team Exception '{e}' from Slave '{slave_id}' for network {network_name} with interface {wifi_interface} with master {master_address}: {stack_trace}".format(**locals())
try:
send_notification_to_flowdock(message)
except Exception, e:
print e
print message
time.sleep(60)
if __name__ == '__main__':
main()