diff --git a/modules/sc-mesh-secure-deployment/src/1_5/common/SpectralMgr.py b/modules/sc-mesh-secure-deployment/src/1_5/common/SpectralMgr.py index b868d5c9d..7febd3bdd 100644 --- a/modules/sc-mesh-secure-deployment/src/1_5/common/SpectralMgr.py +++ b/modules/sc-mesh-secure-deployment/src/1_5/common/SpectralMgr.py @@ -1,52 +1,35 @@ #!/usr/bin/python -from datetime import datetime -import pandas as pd -import subprocess -import struct import math -import time -import yaml -import sys import os +import stat +import struct +import subprocess +import sys +import time +from datetime import datetime +import pandas as pd -class Spectral: - header_size = 3 - type1_packet_size = 17 + 56 - type2_packet_size = 24 + 128 - type3_packet_size = 26 + 64 - - # ieee 802.11 constants - sc_wide = 0.3125 # in MHz - - VALUES = [] - - # DRIVER - drivers = ["ath9k", "ath10k"] - driver = os.popen('ls /sys/kernel/debug/ieee80211/phy* | grep ath').read().strip() - if(driver not in drivers): - sys.exit("No driver detected.") +HEADER_SIZE = 3 +TYPE1_PACKET_SIZE = 17 + 56 +TYPE2_PACKET_SIZE = 24 + 128 +TYPE3_PACKET_SIZE = 26 + 64 - # CONFIG FILE - with open('config_spectralscan.yaml') as file: - try: - config = yaml.safe_load(file) - debug = config['debug'] - interface = config['interface'] - except yaml.YAMLError as exc: - print(exc) - sys.exit("Config file error.") +# ieee 802.11 constants +SC_WIDE = 0.3125 # in MHz +DRIVERS = ["ath9k", "ath10k"] +DRIVER = os.popen('ls /sys/kernel/debug/ieee80211/phy* | grep ath').read().strip() +if DRIVER not in DRIVERS: sys.exit("No driver detected.") - # DEBUG CSV FILE - if(debug): - outfile = "spectral_scan" +outfile = "scan" +class Spectral: def __init__(self): self.VALUES = dict() - def read(self, spectral_bin, size, all_channels, channels, csv_count, missing_scan_count): + def read(self, spectral_bin, size, channels, scan_count, debug): self.VALUES = dict() data = spectral_bin.read(size) # just read 2048 bytes @@ -54,16 +37,16 @@ def read(self, spectral_bin, size, all_channels, channels, csv_count, missing_sc pos = 0 while pos < len(data): (stype, slen) = struct.unpack_from(">BH", data, pos) - if not ((stype == 1 and slen == self.type1_packet_size) or - (stype == 2 and slen == self.type2_packet_size) or - (stype == 3 and slen == self.type3_packet_size)): + if not ((stype == 1 and slen == TYPE1_PACKET_SIZE) or + (stype == 2 and slen == TYPE2_PACKET_SIZE) or + (stype == 3 and slen == TYPE3_PACKET_SIZE)): print("skip malformed packet") break # 20 MHz if stype == 1: - if pos >= len(data) - self.header_size - self.type1_packet_size + 1: + if pos >= len(data) - HEADER_SIZE - TYPE1_PACKET_SIZE + 1: break - pos += self.header_size + pos += HEADER_SIZE (max_exp, freq, rssi, noise, max_mag, max_index, hweight, tsf) = \ struct.unpack_from(">BHbbHBBQ", data, pos) pos += 17 @@ -75,34 +58,35 @@ def read(self, spectral_bin, size, all_channels, channels, csv_count, missing_sc sum_square_sample = 0 samples = [] for raw_sample in sdata: - if raw_sample == 0: + if raw_sample == 0: sample = 1 - else: + else: sample = raw_sample << max_exp - sum_square_sample += sample * sample - samples.append(sample) + sum_square_sample += sample * sample + samples.append(sample) if sum_square_sample == 0: sum_square_sample = 1 sum_square_sample = 10 * math.log10(sum_square_sample) sc_total = 56 # HT20: 56 OFDM subcarrier, HT40: 128 - first_sc = freq - self.sc_wide * (sc_total / 2 + 0.5) + first_sc = freq - SC_WIDE * (sc_total / 2 + 0.5) for i, sample in enumerate(samples): - subcarrier_freq = first_sc + i * self.sc_wide + subcarrier_freq = first_sc + i * SC_WIDE sigval = noise + rssi + 20 * math.log10(sample) - sum_square_sample self.VALUES[count] = (tsf, subcarrier_freq, noise, rssi, sigval) - if(self.debug): - print("TSF: %d Freq: %d Noise: %d Rssi: %d Signal: %f" % (tsf, subcarrier_freq, noise, rssi, sigval)) + if (self.debug): + print("TSF: %d Freq: %d Noise: %d Rssi: %d Signal: %f" % ( + tsf, subcarrier_freq, noise, rssi, sigval)) count = count + 1 - + # 40 MHz elif stype == 2: - if pos >= len(data) - self.header_size - self.type2_packet_size + 1: + if pos >= len(data) - HEADER_SIZE - TYPE2_PACKET_SIZE + 1: break - pos += self.header_size + pos += HEADER_SIZE (chantype, freq, rssi_l, rssi_u, tsf, noise_l, noise_u, max_mag_l, max_mag_u, max_index_l, max_index_u, hweight_l, hweight_u, max_exp) = \ @@ -143,24 +127,22 @@ def read(self, spectral_bin, size, all_channels, channels, csv_count, missing_sc print("got unknown chantype: %d" % chantype) raise - first_sc = freq - self.sc_wide * (sc_total / 2 + 0.5) + first_sc = freq - SC_WIDE * (sc_total / 2 + 0.5) for i, sample in enumerate(samples): if i < 64: sigval = noise_l + rssi_l + 20 * math.log10(sample) - sum_square_sample_lower else: sigval = noise_u + rssi_u + 20 * math.log10(sample) - sum_square_sample_upper - subcarrier_freq = first_sc + i * self.sc_wide - self.VALUES[count] = (subcarrier_freq, (noise_l + noise_u) / 2, (rssi_l + rssi_u) / 2, sigval, (max_mag_l + max_mag_u)/2) - if(self.debug): - print("TSF: %d Freq: %d Noise: %d Rssi: %d Signal: %f Max Magnitude %d" % (tsf, subcarrier_freq, (noise_l+noise_u)/2, (rssi_l + rssi_u) / 2, sigval, (max_mag_l + max_mag_u)/2)) + subcarrier_freq = first_sc + i * SC_WIDE + self.VALUES[count] = (subcarrier_freq, (noise_l + noise_u) / 2, (rssi_l + rssi_u) / 2, sigval, (max_mag_l + max_mag_u) / 2) count = count + 1 # ath10k elif stype == 3: - if pos >= len(data) - self.header_size - self.type3_packet_size + 1: + if pos >= len(data) - HEADER_SIZE - TYPE3_PACKET_SIZE + 1: break - pos += self.header_size + pos += HEADER_SIZE (chanwidth, freq1, freq2, noise, max_mag, gain_db, base_pwr_db, tsf, max_index, rssi, relpwr_db, avgpwr_db, max_exp) = \ @@ -171,112 +153,94 @@ def read(self, spectral_bin, size, all_channels, channels, csv_count, missing_sc pos += 64 self.VALUES[count] = (freq1, noise, max_mag, gain_db, base_pwr_db, rssi, relpwr_db, avgpwr_db) - - if(self.debug): - print(f"Channel Width: {chanwidth} Freq1: {freq1} Freq2: {freq2} Noise: {noise} Max Magnitude: {max_mag} Gain_db: {gain_db} Base Power_db: {base_pwr_db} TSF: {tsf} Max Index: {max_index} Rssi: {rssi} Rel Power_db: {relpwr_db} Avg Power_db: {avgpwr_db} Max_exp: {max_exp}") count = count + 1 - + + if(debug): + print(f"Channel Width: {chanwidth} Freq1: {freq1} Freq2: {freq2} Noise: {noise} Max Magnitude: {max_mag} Gain_db: {gain_db} Base Power_db: {base_pwr_db} TSF: {tsf} Max Index: {max_index} Rssi: {rssi} Rel Power_db: {relpwr_db} Avg Power_db: {avgpwr_db} Max_exp: {max_exp}") vals_list = [] for key, value in self.VALUES.items(): vals_list.append(list(value)) - if(self.driver == "ath9k"): - spectral_capture_df = pd.DataFrame(vals_list, columns = ["freq1", "noise", "rssi", "signal", "max magnitude"]) - spectral_capture_df = spectral_capture_df.reindex(columns = ["freq1", "noise","signal", "max_magnitude","total_gain_db", "base_pwr_db", "rssi", "relpwr_db", "avgpwr_db"]) - if(self.debug): - print(f"all_saved csv {self.outfile}_{csv_count}.csv") - spectral_capture_df.to_csv(f'{self.outfile}_{csv_count}.csv', index=False) - return [all_channels, 0] - - elif(self.driver == "ath10k"): - spectral_capture_df = pd.DataFrame(vals_list, columns = ["freq1", "noise", "max_magnitude", "total_gain_db","base_pwr_db", "rssi", "relpwr_db", "avgpwr_db"]) - - - if(spectral_capture_df['freq1'].nunique() != len(channels.split())): # scan is missing channels - missing_scan_count += 1 - - # first instance of missing scan - if(missing_scan_count == 0): - present_channels = list(map(str, spectral_capture_df['freq1'].unique())) - vals_list_scanning = vals_list - - # add remaining missing channels from subsequent scans - elif(missing_scan_count > 0): - scanned_channels = list(map(str, spectral_capture_df['freq1'].unique())) - present_channels += scanned_channels - vals_list_scanning += vals_list - - - channels_missing_set = set(channels.split()) ^ set(present_channels) - sorted_channels = list(map(int,list(channels_missing_set))) - sorted_channels.sort() - channels = ' '.join(list(map(str, sorted_channels))) # converting to string to pass to do_scan_cmd - return [channels, missing_scan_count] - - if(channels == ''): # if all missing channels have been retreived in the scans - present_channels = [] - missing_scan_count = 0 - if(self.debug): - print(f"cont_saved csv {self.outfile}_{csv_count}.csv") - spectral_capture_df = pd.DataFrame(vals_list_scanning, columns = ["freq1", "noise", "max_magnitude", "total_gain_db","base_pwr_db", "rssi", "relpwr_db", "avgpwr_db"]) - spectral_capture_df.to_csv(f'{self.outfile}_{csv_count}.csv', index=False) - return [all_channels, missing_scan_count] - - elif(spectral_capture_df['freq1'].nunique() == len(channels.split())): # if scan has all channels - if(self.debug): - print(f"all_saved csv {self.outfile}_{csv_count}.csv") - spectral_capture_df.to_csv(f'{self.outfile}_{csv_count}.csv', index=False) - return [all_channels, missing_scan_count] - + if (DRIVER == "ath10k"): + spectral_capture_df = pd.DataFrame(vals_list, columns=["freq1", "noise", "max_magnitude", "total_gain_db", + "base_pwr_db", "rssi", "relpwr_db", "avgpwr_db"]) + + if(spectral_capture_df["freq1"].nunique() != len(channels.split())): + if(debug): + print("Missing: ", set(channels.split()) ^ set(spectral_capture_df["freq1"].unique())) + valid = 0 + return valid + + else: + valid = 1 + print(f"Scan {scan_count} complete") + spectral_capture_df.to_csv(f'{outfile}_{scan_count}.csv', index=False) + return valid + + if (DRIVER == "ath9k"): + spectral_capture_df = pd.DataFrame(vals_list, columns=["freq1", "noise", "rssi", "signal", "max magnitude"]) + spectral_capture_df = spectral_capture_df.reindex( + columns=["freq1", "noise", "signal", "max_magnitude", "total_gain_db", "base_pwr_db", "rssi", + "relpwr_db", "avgpwr_db"]) + return spectral_capture_df def get_values(self): return self.VALUES def initialize_scan(self): - if(self.driver == "ath9k"): - # cmd_function = "echo background > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl" - cmd_function = "echo manual > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl" - #cmd_count = "echo 25 > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_count" - cmd_trigger = "echo trigger > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl" - - subprocess.call(cmd_function, shell=True) - #subprocess.call(cmd_count, shell=True) - subprocess.call(cmd_trigger, shell=True) - - elif(self.driver == "ath10k"): - cmd_background = "echo background > /sys/kernel/debug/ieee80211/phy0/ath10k/spectral_scan_ctl" - cmd_trigger = "echo trigger > /sys/kernel/debug/ieee80211/phy0/ath10k/spectral_scan_ctl" - - subprocess.call(cmd_background, shell=True) - subprocess.call(cmd_trigger, shell=True) - - - def execute_scan(self, channels): + #write_fix = f"echo -n disable > /sys/kernel/debug/ieee80211/phy0/{DRIVER}/spectral_scan_ctl" + + if (DRIVER == "ath9k"): + # cmd_function = "echo background > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl" + cmd_function = "echo manual > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl" + # cmd_count = "echo 25 > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_count" + cmd_trigger = "echo trigger > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl" + + #subprocess.call(write_fix, shell=True) + subprocess.call(cmd_function, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL) + # subprocess.call(cmd_count, shell=True) + subprocess.call(cmd_trigger, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL) + + elif (DRIVER == "ath10k"): + file_exists = os.path.exists("/sys/kernel/debug/ieee80211/phy0/ath10k/spectral_scan_ctl") + if (file_exists == False): + print("no file spectral_scan_ctl") + cmd_background = "echo background > /sys/kernel/debug/ieee80211/phy0/ath10k/spectral_scan_ctl" + cmd_trigger = "echo trigger > /sys/kernel/debug/ieee80211/phy0/ath10k/spectral_scan_ctl" + + #subprocess.call(write_fix, shell=True) + subprocess.call(cmd_background, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL) + subprocess.call(cmd_trigger, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL) + + def execute_scan(self, interface, channels): scan = False - do_scan_cmd = f"iw dev {self.interface} scan freq {channels}" + do_scan_cmd = f"iw dev {interface} scan freq {channels} flush" + interface_up = f"ip link set dev {interface} up" # activate and enable the interface + print(do_scan_cmd) - while(scan == False): + while (scan == False): try: - proc = subprocess.run(do_scan_cmd, shell=True, stdout=subprocess.DEVNULL, check=True) - #proc = subprocess.run(do_scan_cmd, shell=True, stdout=subprocess.DEVNULL) + subprocess.call(interface_up, shell=True) + subprocess.run(do_scan_cmd, shell=True, stderr=subprocess.STDOUT,stdout=subprocess.DEVNULL, check=True) scan = True except: time.sleep(0.1) scan = False - if(scan == True): - print(f"Scan time {datetime.now()}") + if (scan == True): break + #cmd_background = f"echo -n background > /sys/kernel/debug/ieee80211/phy0/{DRIVER}/spectral_scan_ctl" + #cmd_scan = f"echo -n trigger > /sys/kernel/debug/ieee80211/phy0/{DRIVER}/spectral_scan_ctl" + cmd_disable = f"echo disable > /sys/kernel/debug/ieee80211/phy0/{DRIVER}/spectral_scan_ctl" + cmd_dump = f"cat /sys/kernel/debug/ieee80211/phy0/{DRIVER}/spectral_scan0 > /tmp/data" - cmd_scan = f"echo trigger > /sys/kernel/debug/ieee80211/phy0/{self.driver}/spectral_scan_ctl" - cmd_dump = f"cat /sys/kernel/debug/ieee80211/phy0/{self.driver}/spectral_scan0 > /tmp/data" - - subprocess.call(cmd_scan, shell=True) - subprocess.call(cmd_dump, shell=True) - + #subprocess.call(cmd_background, shell=True) + #subprocess.call(cmd_scan, shell=True) + subprocess.call(cmd_disable, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL) + subprocess.call(cmd_dump, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL) @staticmethod def file_close(file_pointer): @@ -285,8 +249,8 @@ def file_close(file_pointer): @staticmethod def file_open(fn="data"): file_exists = os.path.exists(fn) - if(file_exists): + if (file_exists): return open(fn, 'rb') - elif(file_exists == False): + elif (file_exists == False): os.system("touch " + fn) return open(fn, 'rb') diff --git a/modules/sc-mesh-secure-deployment/src/1_5/common/test/config.py b/modules/sc-mesh-secure-deployment/src/1_5/common/test/config.py new file mode 100644 index 000000000..7a1ed0845 --- /dev/null +++ b/modules/sc-mesh-secure-deployment/src/1_5/common/test/config.py @@ -0,0 +1,91 @@ +import yaml +import sys +import os +import re + +scan_interface_type = "mesh" + +# Get mesh interface and current frequency +iw_output = os.popen('iw dev').read() +iw_output = re.sub('\s+', ' ', iw_output).split(' ') + +# Parse multiple interfaces +size = len(iw_output) +idx_list = [idx - 1 for idx, val in enumerate(iw_output) if val == "Interface"] +if(len(idx_list) > 1): # if there is more than one interface up, create list for each + idx_list.pop(0) # print info not needed +iw_interfaces = [iw_output[i: j] for i, j in zip([0] + idx_list, idx_list + ([size] if idx_list[-1] != size else []))] + +# Check if mesh interface is up and get interface name and current channel +no_interface = False +for interface_list in iw_interfaces: + try: + interface_index = interface_list.index(scan_interface_type) # change to the type of the interface used to scan + interface_list = interface_list + no_interface = False + break + except: + no_interface = True + +if(no_interface): + sys.exit(f"No {scan_interface_type} interface to scan") + +interface_index = interface_list.index("Interface") + 1 +interface = interface_list[interface_index].split()[0] +try: + channel_index = interface_list.index("channel") + 2 # index of channel frequency + channel_freq = re.sub("[^0-9]", "", interface_list[channel_index]).split()[0] +except: + channel_freq = "" # interface is not set to any channel + +# Get channels available to be scanned by interface +iw_channels = os.popen(f'iwlist {interface} channel').read() +iw_channels = re.sub('\s+', ' ', iw_channels).split('GHz') + +channel_list = [] +count = 1 +for channel in iw_channels: + try: + if(count == 1): + temp_list = re.sub('\s+', ' ', channel).split(':') + freq_val = int(float(temp_list[-1].strip())*10**3) + else: + freq_val = int(float(channel[channel.index(":")+1:].strip())*10**3) + + channel_list.append(freq_val) + count += 1 + except: + None + +# Temporary workaround for mesh scanning: mesh interface cannot scan it's own channel so remove it temporarily +all_bands = ' '.join(str(ch) for ch in channel_list) +all_bands = all_bands.replace(channel_freq, '').strip() + +# Split at first instance of 5GHz channel +for i, num in enumerate(channel_list): + if str(num).startswith('5'): + split_index = i + break + +channels_2_4 = channel_list[:split_index] +channels_5 = channel_list[split_index-1:] # include one 2.4 channel to the 5 band as workaround to bug in max_mag range shift + +channels_2_4 = ' '.join(str(ch) for ch in channels_2_4) +channels_5 = ' '.join(str(ch) for ch in channels_5) + +# remove mesh channel +channels_2_4 = channels_2_4.replace(channel_freq, '').strip() +channels_5 = channels_5.replace(channel_freq, '').strip() + +# Spectral scan config +config = {'debug': True, + 'interface': interface, + 'default': {'channels': all_bands}, + 'low_latency': {'channels': channel_freq}, + 'high_latency': {'band_2_4': {'channels': channels_2_4}, + 'band_5': {'channels': channels_5}}} + +# Create YAML for config data +yaml.dump(config, sort_keys=False) +with open('config.yaml', 'w',) as f : + yaml.dump(config, f, sort_keys=False) diff --git a/modules/sc-mesh-secure-deployment/src/1_5/common/test/config.yaml b/modules/sc-mesh-secure-deployment/src/1_5/common/test/config.yaml new file mode 100644 index 000000000..4504ae065 --- /dev/null +++ b/modules/sc-mesh-secure-deployment/src/1_5/common/test/config.yaml @@ -0,0 +1,11 @@ +debug: true +interface: wlp1s0 +default: + channels: 2412 2417 2422 2427 2432 2437 2442 2447 2452 2457 2462 5200 5240 5260 5280 5300 5320 5745 5765 5785 5805 5825 5845 +low_latency: + channels: '5180' +high_latency: + band_2_4: + channels: 2412 2417 2422 2427 2432 2437 2442 2447 2452 2457 2462 + band_5: + channels: 5180 5200 5240 5260 5280 5300 5320 5745 5765 5785 5805 5825 5845 diff --git a/modules/sc-mesh-secure-deployment/src/1_5/common/test/spectral_capture.py b/modules/sc-mesh-secure-deployment/src/1_5/common/test/spectral_capture.py index 747164e72..b340aaaa8 100644 --- a/modules/sc-mesh-secure-deployment/src/1_5/common/test/spectral_capture.py +++ b/modules/sc-mesh-secure-deployment/src/1_5/common/test/spectral_capture.py @@ -7,39 +7,43 @@ from SpectralMgr import Spectral if __name__ == "__main__": + + spec = Spectral() scan_count = 0 - missing_scan_count = 0 subprocess.call("rm /tmp/data*", shell=True) # SCAN MODE: Check if scan mode arg passed, if not set to default - try: + try: scan_mode = sys.argv[1].strip() if(scan_mode != 'high_latency' and scan_mode != 'low_latency'): - scan_mode = 'default' + scan_mode = 'default' except: scan_mode = 'default' - - - # FREQUENCY BAND: Check if band arg passed, if not set to all bands - try: - band = sys.argv[2].strip() - if(scan_mode == 'high_latency' and (band != 'band_2_4' and band != 'band_5')): # if param passed but invalid input - scan_mode = 'default' - except: - scan_mode = 'default' - + + # FREQUENCY BAND: Check if band arg passed, if not set to all bands + if(scan_mode == 'high_latency'): + try: + band = sys.argv[2].strip() + if(band != 'band_2_4' and band != 'band_5'): # if param passed but invalid input + scan_mode = 'default' + except: + scan_mode = 'default' + + print(scan_mode) # CONFIG - with open('config_spectralscan.yaml') as file: - try: + with open('config.yaml') as file: + try: config = yaml.safe_load(file) - + interface = config["interface"] + debug = config["debug"] + if(scan_mode == 'low_latency'): - scan_channels = config[scan_mode]['channels'][0] + scan_channels = config[scan_mode]['channels'] all_channels = config[scan_mode]['channels'][0] elif(scan_mode == 'high_latency'): scan_channels = config[scan_mode][band]['channels'] - all_channels = config[scan_mode][band]['channels'] + all_channels = config[scan_mode][band]['channels'] else: scan_channels = config['default']['channels'] all_channels = config['default']['channels'] @@ -48,13 +52,12 @@ while (True): - spec = Spectral() spec.initialize_scan() - spec.execute_scan(scan_channels) + print(scan_channels) + spec.execute_scan(interface, scan_channels) f = spec.file_open(f"/tmp/data") file_stats = os.stat(f"/tmp/data") - channels_scanstatus = spec.read(f, file_stats.st_size, all_channels, scan_channels, scan_count, missing_scan_count) - scan_channels = channels_scanstatus[0] - missing_scan_count = channels_scanstatus[1] + valid = spec.read(f, file_stats.st_size, scan_channels, scan_count, debug) spec.file_close(f) - scan_count += 1 + if(valid == 1): + scan_count += 1