diff --git a/common/scripts/mesh-11s_nats.sh b/common/scripts/mesh-11s_nats.sh index 3696c5968..4931e33de 100755 --- a/common/scripts/mesh-11s_nats.sh +++ b/common/scripts/mesh-11s_nats.sh @@ -229,7 +229,7 @@ EOF "mesh") cat </var/run/wpa_supplicant-11s_"$INDEX"_"$wifidev".conf -ctrl_interface=DIR=/var/run/wpa_supplicant +ctrl_interface=DIR=/var/run/wpa_supplicant_$INDEX # use 'ap_scan=2' on all devices connected to the network # this is unnecessary if you only want the network to be created when no other networks.. ap_scan=1 @@ -321,7 +321,7 @@ EOF iptables -A FORWARD --in-interface "$batman_iface" -j ACCEPT iptables --table nat -A POSTROUTING --out-interface "$br_lan_ip" -j MASQUERADE - wpa_supplicant -i "$wifidev" -c /var/run/wpa_supplicant-11s_"$INDEX"_"$wifidev".conf -D nl80211 -C /var/run/wpa_supplicant/ -f /tmp/wpa_supplicant_11s_"$INDEX".log + wpa_supplicant -i "$wifidev" -c /var/run/wpa_supplicant-11s_"$INDEX"_"$wifidev".conf -D nl80211 -C /var/run/wpa_supplicant_"$INDEX"/ -f /tmp/wpa_supplicant_11s_"$INDEX".log ;; "ap+mesh_mcc") wait_for_intf "$bridge_name" diff --git a/common/scripts/mesh-helper.sh b/common/scripts/mesh-helper.sh index f66ef6990..37b96f3ea 100644 --- a/common/scripts/mesh-helper.sh +++ b/common/scripts/mesh-helper.sh @@ -45,7 +45,7 @@ EOF generate_br_lan_ip() { local mesh_if_mac - mesh_if_mac="$(ip -brief link | grep "$id0_MESH_VIF" | awk '{print $3; exit}')" + mesh_if_mac="$(ip -brief link | grep "${id0_MESH_VIF}" | awk '{print $3; exit}')" local ip_random ip_random="$(echo "$mesh_if_mac" | cut -b 16-17)" br_lan_ip="192.168.1."$((16#$ip_random)) diff --git a/modules/sc-mesh-secure-deployment/src/nats/comms_nats_controller.py b/modules/sc-mesh-secure-deployment/src/nats/comms_nats_controller.py index 3ae5e36b9..5693faf64 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/comms_nats_controller.py +++ b/modules/sc-mesh-secure-deployment/src/nats/comms_nats_controller.py @@ -91,18 +91,19 @@ def __init__(self, server: str, port: str, interval: int = 1000): console_handler.setFormatter(log_formatter) self.main_logger.addHandler(console_handler) - self.comms_status = [] + self.c_status = [] #TODO how many radios? for i in range(0, 3): - self.comms_status.append(comms_status.CommsStatus(self.main_logger.getChild(f"status {str(i)}"), i)) + self.c_status.append(comms_status.CommsStatus(self.main_logger.getChild(f"status {str(i)}"), i)) - self.settings = comms_settings.CommsSettings(self.comms_status, + self.settings = comms_settings.CommsSettings(self.c_status, self.main_logger.getChild("settings")) - for cstat in self.comms_status: - cstat.settings(self.settings) + for cstat in self.c_status: + if cstat.index < len(self.settings.mesh_vif): + cstat.wifi_interface = self.settings.mesh_vif[cstat.index] - self.command = comms_command.Command(server, port, self.comms_status, + self.command = comms_command.Command(server, port, self.c_status, self.main_logger.getChild("command")) self.telemetry = MeshTelemetry(self.interval, self.main_logger.getChild("telemetry")) @@ -110,16 +111,6 @@ def __init__(self, server: str, port: str, interval: int = 1000): self.logger = self.main_logger.getChild("controller") -class CommsCsa: # pylint: disable=too-few-public-methods - """ - Comms CSA class to storage settings for CSA for a state change - """ - - def __init__(self): - self.delay = "0" - self.ack_sent = False - - # pylint: disable=too-many-arguments, too-many-locals, too-many-statements async def main(server, port, keyfile=None, certfile=None, interval=1000): """ @@ -127,7 +118,6 @@ async def main(server, port, keyfile=None, certfile=None, interval=1000): """ cc = CommsController(server, port, interval) nats_client = NATS() - csac = CommsCsa() status, _, identity_dict = cc.command.get_identity() @@ -178,23 +168,6 @@ async def reconnected_cb(): disconnected_cb=disconnected_cb, max_reconnect_attempts=-1) - async def handle_settings_csa_post(ret): - if ret == "OK": - ret = "ACK" - elif ret == "TRIGGER": - cmd = json.dumps({"api_version": 1, "cmd": "APPLY"}) - cc.command.handle_command(cmd, cc, True, csac.delay) - elif ret == "COUNT": - ret = "COUNT" # not to send ACK/NACK - else: - ret = "NACK" - - if ret in ("ACK", "NACK") and csac.ack_sent is False: - response = {'status': ret} - cc.logger.debug("publish response: %s", str(response)) - await nats_client.publish("comms.settings_csa", - json.dumps(response).encode("utf-8")) - csac.ack_sent = True async def message_handler(message): # reply = message.reply @@ -205,39 +178,30 @@ async def message_handler(message): if subject == f"comms.settings.{identity}": ret, info = cc.settings.handle_mesh_settings(data) - elif subject == "comms.settings_csa": - ret, info, delay = cc.settings.handle_mesh_settings_csa(data) - csac.delay = delay - csac.ack_sent = "status" in data - - elif subject == f"comms.command.{identity}" or subject == "comms.identity": + elif subject in (f"comms.command.{identity}", "comms.identity"): ret, info, resp = cc.command.handle_command(data, cc) elif subject == f"comms.status.{identity}": ret, info = "OK", "Returning current status" - if subject == "comms.settings_csa": - await handle_settings_csa_post(ret) - else: - # Update status info - _ = [item.refresh_status() for item in cc.comms_status] + # Update status info + _ = [item.refresh_status() for item in cc.c_status] - response = {'status': ret, 'info': info, - 'mesh_status': [item.mesh_status for item in cc.comms_status], - 'mesh_cfg_status': [item.mesh_cfg_status for item in cc.comms_status], - 'visualisation_active': [item.is_visualisation_active for item in cc.comms_status], - 'mesh_radio_on': [item.is_mesh_radio_on for item in cc.comms_status], - 'ap_radio_on': [item.is_ap_radio_on for item in cc.comms_status], - 'security_status': [item.security_status for item in cc.comms_status] - } + response = {'status': ret, 'info': info, + 'mesh_status': [item.mesh_status for item in cc.c_status], + 'mesh_cfg_status': [item.mesh_cfg_status for item in cc.c_status], + 'visualisation_active': [item.is_visualisation_active for item in cc.c_status], + 'mesh_radio_on': [item.is_mesh_radio_on for item in cc.c_status], + 'ap_radio_on': [item.is_ap_radio_on for item in cc.c_status], + 'security_status': [item.security_status for item in cc.c_status] + } - if resp != "": - response['data'] = resp + if resp != "": + response['data'] = resp - cc.logger.debug("Sending response: %s", str(response)[:1000]) - await message.respond(json.dumps(response).encode("utf-8")) + cc.logger.debug("Sending response: %s", str(response)[:1000]) + await message.respond(json.dumps(response).encode("utf-8")) await nats_client.subscribe(f"comms.settings.{identity}", cb=message_handler) - await nats_client.subscribe("comms.settings_csa", cb=message_handler) await nats_client.subscribe(f"comms.command.{identity}", cb=message_handler) await nats_client.subscribe("comms.identity", cb=message_handler) await nats_client.subscribe(f"comms.status.{identity}", cb=message_handler) diff --git a/modules/sc-mesh-secure-deployment/src/nats/scripts/cli_settings_request.py b/modules/sc-mesh-secure-deployment/src/nats/scripts/cli_settings_request.py index eb9308345..89c4e4633 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/scripts/cli_settings_request.py +++ b/modules/sc-mesh-secure-deployment/src/nats/scripts/cli_settings_request.py @@ -31,7 +31,7 @@ async def main(): }, { "radio_index": "1", - "ssid": "test_mesh", + "ssid": "test_mesh2", "key": "1234567890", "ap_mac": "00:11:22:33:44:55", "country": "FI", @@ -44,11 +44,11 @@ async def main(): "tx_power": "5", "mode": "mesh", "mesh_vif": "wlp1s2", - "phy": "phy1", + "phy": "phy2", "batman_iface": "bat0", }, ], - "bridge": "br-lan bat0 lan1" + "bridge": "br-lan bat0 eth1 lan1" } cmd = json.dumps(cmd_dict) @@ -56,7 +56,7 @@ async def main(): f"comms.settings.{config.MODULE_IDENTITY}", cmd.encode(), timeout=2 ) parameters = json.loads(rep.data) - print(parameters) + print(json.dumps(parameters, indent=2)) await nc.close() exit(0) diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/comms_command.py b/modules/sc-mesh-secure-deployment/src/nats/src/comms_command.py index 9511349d4..54e8b262e 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/comms_command.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/comms_command.py @@ -12,7 +12,6 @@ from .comms_common import STATUS, COMMAND from .comms_status import CommsStatus - class LogFiles: # pylint: disable=too-few-public-methods """ LogFiles class @@ -59,15 +58,13 @@ def __init__(self, server, port, comms_status: [CommsStatus, ...], logger): self.interval = 1 self.comms_status = comms_status - def handle_command(self, msg: str, cc, csa=False, delay="0") -> (str, str, str): + def handle_command(self, msg: str, cc) -> (str, str, str): """ handler for commands Args: msg: JSON formatted data from NATS message. cc: CommsController class - csa: bool: True if CSA is active - delay: str: delay for channel change in csa case Returns: str: OK/FAIL @@ -98,7 +95,7 @@ def handle_command(self, msg: str, cc, csa=False, delay="0") -> (str, str, str): elif self.command == COMMAND.revoke: ret, info = self.__revoke(cc) elif self.command == COMMAND.apply: - ret, info = self.__apply_mission_config(csa, delay) + ret, info = self.__apply_mission_config() elif self.command == COMMAND.wifi_down: ret, info = self.__radio_down() elif self.command == COMMAND.wifi_up: @@ -172,7 +169,7 @@ def __revoke(self, cc) -> (str, str): return "OK", "Mesh settings revoked" - def __apply_mission_config(self, csa=False, delay="0") -> (str, str): + def __apply_mission_config(self) -> (str, str): """ Replaces active mesh configuration file with previously stored content and restarts S9011Mesh with new configs. @@ -206,18 +203,10 @@ def __apply_mission_config(self, csa=False, delay="0") -> (str, str): # matches then mission config is applied. Otherwise, default mesh # is applied. That logic is based on assumption that some # wireless connectivity needs to be ensured after reboot. - if csa: - processes = ["/opt/S9011sNatsMesh", "/opt/S90APoint"] - else: - processes = ["/opt/S9011sNatsMesh", "/opt/S90APoint", "/opt/S90nats_discovery"] + processes = ["/opt/S9011sNatsMesh", "/opt/S90APoint", "/opt/S90nats_discovery"] for process in processes: # delay before restarting mesh using delay - if delay != "0": - ret = subprocess.run(["sleep", delay], - shell=False, check=True, - capture_output=True) - ret = subprocess.run([process, "restart", "id" + self.radio_index], shell=False, check=True, capture_output=True) diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/comms_settings.py b/modules/sc-mesh-secure-deployment/src/nats/src/comms_settings.py index 1f2242218..a4c913db5 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/comms_settings.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/comms_settings.py @@ -46,12 +46,10 @@ def __init__(self, comms_status: [cs.CommsStatus, ...], logger): self.msversion: str = "" self.delay:str = "" # delay for channel change self.comms_status = comms_status - self.csa_state: int = 0 # 0: not started, 1: stored, 2: triggered - self.csa_count: int = 0 # number of CSA triggered - self.device_amount: str = "0" # number of devices to trigger CSA from nats message # TODO: check can we do this - # ret, info = self.__load_settings() - # self.logger.debug("load settings: %s, %s", ret, info) + self.default_mesh = True + ret, info = self.__load_settings() + self.logger.debug("load settings: %s, %s", ret, info) def validate_mesh_settings(self, index: int) -> (str, str): """ @@ -123,59 +121,6 @@ def validate_mesh_settings(self, index: int) -> (str, str): return "OK", "Mesh settings OK" - def handle_mesh_settings_csa(self, msg: str, path="/opt", - file="mesh_stored.conf") -> (str, str, str): - """ - Handle mesh settings - """ - # TODO: check implementation - try: - parameters = json.loads(msg) - if "status" in parameters and self.csa_state == 1: - self.csa_count = self.csa_count + 1 - if self.csa_count >= int(self.device_amount): - self.csa_state = 2 - self.logger.debug(f"Trigger channel switch to {self.frequency}") - return "TRIGGER", "Channel switch triggered", self.delay - return "COUNT", "Channel switch count", self.delay - - self.csa_state = 0 - ret, info = self.__load_settings() - self.logger.debug("load settings: %s, %s", ret, info) - - #self.api_version = int(parameters["api_version"]) - self.frequency, self.delay, self.device_amount= map(quote, - (str(parameters["frequency"]), - str(parameters["delay"]), - str(parameters["amount"]))) - - if validation.validate_delay(self.delay) and validation.validate_frequency(int(self.frequency)): - ret, info = "OK", "CSA settings OK" - else: - ret, info = "FAIL", "Invalid delay or frequency" - - self.logger.debug(" settings validation: %s, %s", ret, info) - if ret == "FAIL": - self.comms_status.mesh_cfg_status = \ - comms.STATUS.mesh_cfg_not_stored - self.logger.error("save settings failed: %s, %s", ret, info) - else: - ret, info = self.__save_settings(path, file, index) - self.logger.debug("save settings: %s, %s", ret, info) - self.csa_state = 1 - self.csa_count = 0 - - return ret, info, self.delay - - except (json.decoder.JSONDecodeError, KeyError, - TypeError, AttributeError) as error: - self.comms_status.mesh_cfg_status = \ - comms.STATUS.mesh_cfg_not_stored - ret, info = "FAIL", "JSON format not correct" + str(error) - self.logger.error("csa settings validation: %s, %s", ret, info) - - return ret, info, self.delay - def handle_mesh_settings(self, msg: str, path="/opt", file="mesh_stored.conf") -> (str, str): """ @@ -183,10 +128,29 @@ def handle_mesh_settings(self, msg: str, path="/opt", """ try: parameters_set = json.loads(msg) + self.msversion = "nats" self.api_version = int(parameters_set["api_version"]) self.role = quote(str(parameters_set["role"])) + if self.default_mesh: + self.radio_index = [] + self.ssid = [] + self.key = [] + self.ap_mac = [] + self.country = [] + self.frequency = [] + self.frequency_mcc = [] + self.ip_address = [] + self.subnet = [] + self.tx_power = [] + self.mode = [] + self.routing = [] + self.priority = [] + self.mesh_vif = [] + self.phy = [] + self.batman_iface = [] + for parameters in parameters_set["radios"]: self.radio_index.append(int(parameters["radio_index"])) self.ssid.append(quote(str(parameters["ssid"]))) @@ -207,8 +171,6 @@ def handle_mesh_settings(self, msg: str, path="/opt", self.bridge = quote(str(parameters_set["bridge"])) - ret, info = "FAIL", "Before validation" - for index in self.radio_index: self.logger.debug("Mesh settings validation index: %s", str(index)) ret, info = self.validate_mesh_settings(index) @@ -222,6 +184,8 @@ def handle_mesh_settings(self, msg: str, path="/opt", ret, info = self.__save_settings(path, file, index) self.logger.debug("save settings index %s: %s, %s", str(index), ret, info) + self.default_mesh = False + except (json.decoder.JSONDecodeError, KeyError, TypeError, AttributeError) as error: self.comms_status.mesh_cfg_status = \ @@ -330,16 +294,20 @@ def __load_settings(self) -> (str, str): for index in range(0, len(self.comms_status)): file = f"/opt/{str(index)}_mesh.conf" if os.path.exists(file): + self.default_mesh = False self.logger.debug("mesh config file %s found", file) with open(file, "r", encoding="utf-8") as mesh_conf: mesh_conf_lines = mesh_conf.read() self.__read_configs(mesh_conf_lines) else: - self.logger.debug("mesh config file %s not found, loading default", file) if index == 0: + self.default_mesh = True + self.logger.debug("mesh config file %s not found, loading default", file) with open(config_file_path, "r", encoding="utf-8") as mesh_conf: mesh_conf_lines = mesh_conf.read() self.__read_configs(mesh_conf_lines) + else: + self.logger.debug("index not supported for default: %s", index) except: self.logger.error("not able to read mesh config files") return "FAIL", "not able to read mesh config files" diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/comms_status.py b/modules/sc-mesh-secure-deployment/src/nats/src/comms_status.py index fcfad0030..b8c4bac23 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/comms_status.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/comms_status.py @@ -103,7 +103,8 @@ def __eq__(self, other): return False def __init__(self, logger: logging, index: int): - self.__index: int = index + self.index: int = index + self.wifi_interface: str = "" self.__lock = threading.Lock() self.__thread_running: bool = False self.__logger = logger @@ -119,14 +120,7 @@ def __init__(self, logger: logging, index: int): self.__is_visualisation_active: bool = False self.__is_hash_file: bool = False self.__is_ap_radio_on: bool = False - self.__settings = None # Refresh status - - def settings(self, value): - """ - Set mesh settings - """ - self.__settings = value self.__update_status() @property @@ -305,7 +299,7 @@ def __get_wpa_supplicant_pid(self) -> str: ps_command = ["ps", "ax"] try: grep_command = ["grep", "-E", - f"[w]pa_supplicant-11s_id{str(self.__index)}_{self.__settings.mesh_vif[self.__index]}.conf"] + f"[w]pa_supplicant-11s_id{str(self.index)}_{self.wifi_interface}.conf"] except IndexError: return "" awk_command = ["awk", '{print $1}'] @@ -326,7 +320,7 @@ def __get_wpa_cli_status(self): """ Get wpa_supplicant states via wpa_cli tool. """ - wpa_cli_command = ["wpa_cli", "-i", f"{self.__settings.mesh_vif[self.__index]}" "status"] + wpa_cli_command = ["wpa_cli", "-p", f"/var/run/wpa_supplicant_id{str(self.index)}/", "status"] proc = subprocess.Popen(wpa_cli_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = proc.communicate() @@ -444,9 +438,9 @@ def __get_mission_cfg_status(self): Checks whether mission config file exists and does it match with hash file of previously applied settings. """ - config_file_path = f"/opt/{str(self.__index)}_mesh.conf" - pending_config_file_path = f"/opt/{str(self.__index)}_mesh_stored.conf" - hash_file_path = f"/opt/{str(self.__index)}_mesh.conf_hash" + config_file_path = f"/opt/{str(self.index)}_mesh.conf" + pending_config_file_path = f"/opt/{str(self.index)}_mesh_stored.conf" + hash_file_path = f"/opt/{str(self.index)}_mesh.conf_hash" old_mesh_cfg_status = self.__mesh_cfg_status old_is_mission_cfg = self.__is_mission_cfg try: diff --git a/modules/utils/docker/entrypoint_nats.sh b/modules/utils/docker/entrypoint_nats.sh index 2debf5290..701e654a7 100755 --- a/modules/utils/docker/entrypoint_nats.sh +++ b/modules/utils/docker/entrypoint_nats.sh @@ -22,11 +22,12 @@ else generate_br_lan_ip echo "starting 11s mesh service" + # todo for loop range 0..3 /opt/S9011sNatsMesh start id0 - if [ -f "opt/1_mesh.conf" ]; then + if [ -f "/opt/1_mesh.conf" ]; then /opt/S9011sNatsMesh start id1 fi - if [ -f "opt/2_mesh.conf" ]; then + if [ -f "/opt/2_mesh.conf" ]; then /opt/S9011sNatsMesh start id2 fi