Let's get clients on the channel!
This commit is contained in:
parent
81432e8e63
commit
8809059402
1 changed files with 75 additions and 14 deletions
|
@ -4,6 +4,55 @@ import csv
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pyshark
|
import pyshark
|
||||||
|
|
||||||
|
# United States regulatory domain channel lookup table
|
||||||
|
|
||||||
|
CHANNEL_LOOKUP_TABLE = {
|
||||||
|
# 2.4 GHz (non-DFS, always allowed)
|
||||||
|
1: {"freq": 2412, "dfs": False, "band": "2.4GHz"},
|
||||||
|
2: {"freq": 2417, "dfs": False, "band": "2.4GHz"},
|
||||||
|
3: {"freq": 2422, "dfs": False, "band": "2.4GHz"},
|
||||||
|
4: {"freq": 2427, "dfs": False, "band": "2.4GHz"},
|
||||||
|
5: {"freq": 2432, "dfs": False, "band": "2.4GHz"},
|
||||||
|
6: {"freq": 2437, "dfs": False, "band": "2.4GHz"},
|
||||||
|
7: {"freq": 2442, "dfs": False, "band": "2.4GHz"},
|
||||||
|
8: {"freq": 2447, "dfs": False, "band": "2.4GHz"},
|
||||||
|
9: {"freq": 2452, "dfs": False, "band": "2.4GHz"},
|
||||||
|
10: {"freq": 2457, "dfs": False, "band": "2.4GHz"},
|
||||||
|
11: {"freq": 2462, "dfs": False, "band": "2.4GHz"},
|
||||||
|
|
||||||
|
# 5 GHz UNII-1 (indoor only)
|
||||||
|
36: {"freq": 5180, "dfs": False, "band": "UNII-1"},
|
||||||
|
40: {"freq": 5200, "dfs": False, "band": "UNII-1"},
|
||||||
|
44: {"freq": 5220, "dfs": False, "band": "UNII-1"},
|
||||||
|
48: {"freq": 5240, "dfs": False, "band": "UNII-1"},
|
||||||
|
|
||||||
|
# 5 GHz UNII-2 (DFS required)
|
||||||
|
52: {"freq": 5260, "dfs": True, "band": "UNII-2"},
|
||||||
|
56: {"freq": 5280, "dfs": True, "band": "UNII-2"},
|
||||||
|
60: {"freq": 5300, "dfs": True, "band": "UNII-2"},
|
||||||
|
64: {"freq": 5320, "dfs": True, "band": "UNII-2"},
|
||||||
|
|
||||||
|
# 5 GHz UNII-2e (DFS required)
|
||||||
|
100: {"freq": 5500, "dfs": True, "band": "UNII-2e"},
|
||||||
|
104: {"freq": 5520, "dfs": True, "band": "UNII-2e"},
|
||||||
|
108: {"freq": 5540, "dfs": True, "band": "UNII-2e"},
|
||||||
|
112: {"freq": 5560, "dfs": True, "band": "UNII-2e"},
|
||||||
|
116: {"freq": 5580, "dfs": True, "band": "UNII-2e"},
|
||||||
|
120: {"freq": 5600, "dfs": True, "band": "UNII-2e"},
|
||||||
|
124: {"freq": 5620, "dfs": True, "band": "UNII-2e"},
|
||||||
|
128: {"freq": 5640, "dfs": True, "band": "UNII-2e"},
|
||||||
|
132: {"freq": 5660, "dfs": True, "band": "UNII-2e"},
|
||||||
|
136: {"freq": 5680, "dfs": True, "band": "UNII-2e"},
|
||||||
|
140: {"freq": 5700, "dfs": True, "band": "UNII-2e"},
|
||||||
|
|
||||||
|
# 5 GHz UNII-3 (outdoor/indoor, no DFS)
|
||||||
|
149: {"freq": 5745, "dfs": False, "band": "UNII-3"},
|
||||||
|
153: {"freq": 5765, "dfs": False, "band": "UNII-3"},
|
||||||
|
157: {"freq": 5785, "dfs": False, "band": "UNII-3"},
|
||||||
|
161: {"freq": 5805, "dfs": False, "band": "UNII-3"},
|
||||||
|
165: {"freq": 5825, "dfs": False, "band": "UNII-3"},
|
||||||
|
}
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--csv', required=True, help='Input speedtest CSV')
|
parser.add_argument('--csv', required=True, help='Input speedtest CSV')
|
||||||
|
@ -45,29 +94,39 @@ def get_clients_on_ap(capture, ap_bssid):
|
||||||
|
|
||||||
return len(clients)
|
return len(clients)
|
||||||
|
|
||||||
def get_clients_on_channel(capture, ap_channel, ap_bssid):
|
def get_clients_on_channel(capture, ap_channel):
|
||||||
|
from_channel_freq = CHANNEL_LOOKUP_TABLE.get(ap_channel, {}).get('freq', None)
|
||||||
|
if not from_channel_freq:
|
||||||
|
print(f"[!] Invalid channel: {ap_channel}")
|
||||||
|
return 0
|
||||||
|
|
||||||
clients = set()
|
clients = set()
|
||||||
ap_bssid = ap_bssid.lower() # Normalize for comparison
|
|
||||||
ap_channel = str(ap_channel) # Ensure channel is a string for comparison
|
|
||||||
|
|
||||||
for packet in capture:
|
for packet in capture:
|
||||||
try:
|
try:
|
||||||
if not hasattr(packet, 'wlan'):
|
if 'radiotap' not in packet or 'wlan' not in packet:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
channel = getattr(packet.wlan, 'channel', None)
|
radio = packet.radiotap
|
||||||
sa = getattr(packet.wlan, 'sa', '').lower()
|
wlan = packet.wlan
|
||||||
da = getattr(packet.wlan, 'da', '').lower()
|
|
||||||
bssid = getattr(packet.wlan, 'bssid', '').lower()
|
|
||||||
|
|
||||||
# Check if the packet is on the specified channel and not from the AP
|
# Printing the channel frequency for debugging
|
||||||
if channel == ap_channel and (sa != ap_bssid and da != ap_bssid):
|
print(f"Channel Frequency: {getattr(radio, 'channel_freq', None)}")
|
||||||
clients.add(sa)
|
|
||||||
clients.add(da)
|
packet_freq = int(getattr(radio, 'channel_freq', -1))
|
||||||
|
if packet_freq != from_channel_freq:
|
||||||
|
continue
|
||||||
|
|
||||||
|
sa = getattr(wlan, 'sa', '').lower()
|
||||||
|
da = getattr(wlan, 'da', '').lower()
|
||||||
|
|
||||||
|
for mac in (sa, da):
|
||||||
|
if mac and mac != 'ff:ff:ff:ff:ff:ff':
|
||||||
|
clients.add(mac.lower())
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return len(clients)
|
return len(clients)
|
||||||
|
|
||||||
def analyze_pcap(pcapng_path, start_ts, end_ts, ap_bssid, ap_channel):
|
def analyze_pcap(pcapng_path, start_ts, end_ts, ap_bssid, ap_channel):
|
||||||
|
@ -84,6 +143,8 @@ def analyze_pcap(pcapng_path, start_ts, end_ts, ap_bssid, ap_channel):
|
||||||
|
|
||||||
# Get clients on the specified channel
|
# Get clients on the specified channel
|
||||||
|
|
||||||
|
clients_on_channel = get_clients_on_channel(cap, ap_channel)
|
||||||
|
|
||||||
# Placeholder: Logic will be added for:
|
# Placeholder: Logic will be added for:
|
||||||
# - APsOnChannel
|
# - APsOnChannel
|
||||||
# - CongestionScore
|
# - CongestionScore
|
||||||
|
@ -93,7 +154,7 @@ def analyze_pcap(pcapng_path, start_ts, end_ts, ap_bssid, ap_channel):
|
||||||
|
|
||||||
cap.close()
|
cap.close()
|
||||||
|
|
||||||
return clients_on_ap, 0, 0, None, None, None, 0
|
return clients_on_ap, clients_on_channel, 0, None, None, None, 0
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue