From 86576adf72cd57b31725cdfd78eeed215fa8772b Mon Sep 17 00:00:00 2001 From: Kumar Murugesan Date: Thu, 26 Sep 2024 14:08:40 +0400 Subject: [PATCH 1/9] SECO-7824: bat0 OGM interval to 500ms to handle mobility use cases --- .../sc-mesh-secure-deployment/src/nats/src/bat_ctrl_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/bat_ctrl_utils.py b/modules/sc-mesh-secure-deployment/src/nats/src/bat_ctrl_utils.py index a21f94e0..d0e13b8e 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/bat_ctrl_utils.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/bat_ctrl_utils.py @@ -127,8 +127,9 @@ def configure_batman_interface(self, batman_if: str) -> None: ["batctl", "meshif", batman_if, "fragmentation", "1"], check=True ) + ogm_interval = "5000" if is_upper else "500" subprocess.run( - ["batctl", "meshif", batman_if, "orig_interval", "5000"], check=True + ["batctl", "meshif", batman_if, "orig_interval", ogm_interval], check=True ) except subprocess.CalledProcessError as e: From 8ad5563b69ff01c6d1240e706025defd75842224 Mon Sep 17 00:00:00 2001 From: Johan Juntunen Date: Tue, 1 Oct 2024 14:02:41 +0400 Subject: [PATCH 2/9] Fix typo that caused CBMA not to find upper CBMA private key path -typo caused enum instead of string to be passed as key path -SECO-7892 --- .../sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py b/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py index 29a02eb7..782f6e94 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py @@ -51,7 +51,7 @@ def __init__( self.__cbma_certs_path = Constants.ECDSA_BIRTH_FILEBASED.value self.__upper_cbma_certs_path = Constants.DOWNLOADED_RSA_CERTS_PATH.value self.__upper_cbma_ca_cert_path = Constants.DOWNLOADED_UPPER_CBMA_CA_CERT.value - self.__upper_cbma_key_path = Constants.RSA_BIRTH_KEY + self.__upper_cbma_key_path = Constants.RSA_BIRTH_KEY.value self.__lower_cbma_controller = None self.__upper_cbma_controller = None self.__lower_cbma_interfaces: List[Interface] = [] From f34c78ceb4bc26653cc2bf5b93bbe737571099a2 Mon Sep 17 00:00:00 2001 From: Johan Juntunen Date: Wed, 25 Sep 2024 10:19:27 +0400 Subject: [PATCH 3/9] If MDMServer is available and port in mDNS response is None, keep listening for mDNS responses -SECO-7799 --- .../src/nats/src/comms_service_discovery.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_discovery.py b/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_discovery.py index 364fdc83..fff6ece6 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_discovery.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_discovery.py @@ -172,6 +172,10 @@ def __on_service_state_change( "ipv6_addresses": info._ipv6_addresses, "port": info.port,"status": service_available} + if service_available and info.port == None: + self.__logger.error("Service is available but port in mDNS response is None, will keep listening for more packets") + return + self.service_callback(**kwargs) From 426b7a8916a078ddc795002cb45baed7fce0fa87 Mon Sep 17 00:00:00 2001 From: Kumar Murugesan Date: Mon, 14 Oct 2024 08:21:04 +0400 Subject: [PATCH 4/9] fied test log update with exp.throughput and inactive time --- .../field_test_logger/field_test_logger.py | 2 + common/tools/field_test_logger/wifi_info.py | 39 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/common/tools/field_test_logger/field_test_logger.py b/common/tools/field_test_logger/field_test_logger.py index a3db7da3..820f870b 100644 --- a/common/tools/field_test_logger/field_test_logger.py +++ b/common/tools/field_test_logger/field_test_logger.py @@ -142,6 +142,8 @@ def timestamp() -> str: ftl.register_logger_function("Halow SNR", wifi_stats.get_snr) ftl.register_logger_function("RX MCS [MAC,MCS;MAC,MCS ...]", wifi_stats.get_rx_mcs) ftl.register_logger_function("TX MCS [MAC,MCS;MAC,MCS ...]", wifi_stats.get_tx_mcs) + ftl.register_logger_function("Exp. throughput(Mbps) [MAC,Exp.Tput ...]", wifi_stats.get_expected_throughput) + ftl.register_logger_function("inactive time (ms) [MAC,inactive time ...]", wifi_stats.get_inactive_time) ftl.register_logger_function("RX throughput [Bits/s]", wifi_stats.get_rx_throughput) ftl.register_logger_function("TX throughput [Bits/s]", wifi_stats.get_tx_throughput) ftl.register_logger_function("Neighbors", wifi_stats.get_neighbors) diff --git a/common/tools/field_test_logger/wifi_info.py b/common/tools/field_test_logger/wifi_info.py index 54b819d0..f7631178 100644 --- a/common/tools/field_test_logger/wifi_info.py +++ b/common/tools/field_test_logger/wifi_info.py @@ -88,6 +88,24 @@ def get_tx_mcs(self): return out + def get_expected_throughput(self): + out = "" + for i in self.__stations.keys(): + # Expected throughput is at index 3 + out = f"{out}{i},{self.__stations[i][3]};" + + # Remove semicolon after the last station + return out[:-1] + + def get_inactive_time(self): + out = "" + for i in self.__stations.keys(): + # Inactive Time is at index 4 + out = f"{out}{i},{self.__stations[i][4]};" + + # Remove semicolon after the last station + return out[:-1] + def get_txpower(self): return self.__txpower @@ -150,6 +168,8 @@ def __update_mcs_and_rssi(self): tx_mcs = "NaN" rx_mcs = "NaN" rssi = "NaN" + expected_throughput = "NaN" + inactive_time = "NaN" # halow station info fetched from cli_app if needed. halow_stations = None @@ -189,7 +209,24 @@ def __update_mcs_and_rssi(self): rx_mcs = halow_stations.get(station_mac)[1] except (IndexError, TypeError): pass - self.__stations[station_mac] = [rssi, tx_mcs, rx_mcs] + + if "expected throughput:" in line: + # Extract the value and remove the "Mbps" suffix + throughput_str = line.split("expected throughput:")[1].strip().replace("Mbps", "").strip() + try: + expected_throughput = float(throughput_str) + except ValueError: + pass + + if "inactive time:" in line: + # Extract the value and remove the "ms" suffix + inactive_time_str = line.split("inactive time:")[1].strip().replace("ms", "").strip() + try: + inactive_time = int(inactive_time_str) + except ValueError: + pass + + self.__stations[station_mac] = [rssi, tx_mcs, rx_mcs, expected_throughput, inactive_time] def get_halow_stations(self) -> dict: cli_app_cmd = ['/usr/local/bin/cli_app', 'show', 'sta', '0', 'all'] From d50591920d42947f6579a9546f34ce748226f3bc Mon Sep 17 00:00:00 2001 From: Kumar Murugesan Date: Tue, 15 Oct 2024 16:04:00 +0400 Subject: [PATCH 5/9] SECO-8049: Changing guard interval to short for better throughput --- common/scripts/mesh-11s_nats.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/scripts/mesh-11s_nats.sh b/common/scripts/mesh-11s_nats.sh index 75023c7e..1be646ac 100755 --- a/common/scripts/mesh-11s_nats.sh +++ b/common/scripts/mesh-11s_nats.sh @@ -177,7 +177,7 @@ EOF # Radio parameters echo "set radio parameters" # /usr/local/bin/cli_app set txpwr fixed 23 - /usr/local/bin/cli_app set gi long + /usr/local/bin/cli_app set gi short /usr/local/bin/cli_app set support_ch_width 1 /usr/local/bin/cli_app set mesh_rssi_threshold -105 From f4af286b8302bf67512b0c28e68dd05df4a63911 Mon Sep 17 00:00:00 2001 From: Saku Auvinen Date: Tue, 22 Oct 2024 16:12:01 +0300 Subject: [PATCH 6/9] Change br-lan control Jira-ID: SECO-8033, SECO-8006 Bridge control changed so that br-lan is not deleted when CBMA/MDM Agent is stopped. Also trying to use static MAC for the bridge using MAC from some physical interfaces (osf or ethernet) that is not configured to lower CBMA. Default configs changed so that eth0/end0/end or lan1 interface is addde to bridge. Signed-off-by: Saku Auvinen --- .../src/nats/conf/default_ms_config.yaml | 30 +++-- .../src/nats/src/cbma_adaptation.py | 113 +++++++++++++++--- 2 files changed, 116 insertions(+), 27 deletions(-) diff --git a/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml b/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml index 4710d932..3f2c79aa 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml +++ b/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml @@ -21,10 +21,16 @@ CBMA: - sap0 - sta0 - wfd0 - white_interfaces: + - end0 + - end1 + white_interfaces: red_interfaces: - wlan1 - usb0 + - eth0 + - end0 + - end1 + - lan1 BATMAN: routing_algo: BATMAN_V @@ -35,17 +41,17 @@ BATMAN: hardif: halow1: 20 -VLAN: +#VLAN: # Remember that IP address definitions for such interface that is added to # CBMA's red_interfaces list (i.e. br-lan bridge) are not effective. - vlan_black: - parent_interface: eth0 - vlan_id: 100 - ipv4_address: 192.168.1.1 - ipv4_subnet_mask: 255.255.255.0 - ipv6_local_address: fe80::192.168.1.1 - ipv6_prefix_length: 64 - vlan_red: - parent_interface: eth0 - vlan_id: 200 +# vlan_black: +# parent_interface: eth0 +# vlan_id: 100 +# ipv4_address: 192.168.1.1 +# ipv4_subnet_mask: 255.255.255.0 +# ipv6_local_address: fe80::192.168.1.1 +# ipv6_prefix_length: 64 +# vlan_red: +# parent_interface: eth0 +# vlan_id: 200 diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py b/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py index 782f6e94..3adff0c1 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py @@ -60,7 +60,7 @@ def __init__( self.BR_NAME: str = Constants.BR_NAME.value self.LOWER_BATMAN: str = Constants.LOWER_BATMAN.value self.UPPER_BATMAN: str = Constants.UPPER_BATMAN.value - self.ALLOWED_KIND_LIST = {"vlan", "batadv"} + self.ALLOWED_KIND_LIST = {"vlan", "batadv", "dsa"} # Set minimum required configuration self.__white_interfaces = [self.LOWER_BATMAN] @@ -116,9 +116,11 @@ def __validate_cbma_config( black_interfaces: List[str] = [] # Validate input param types - if not isinstance(white_interfaces, list) or \ - not isinstance(red_interfaces, list) or \ - not isinstance(exclude_interfaces, list): + if ( + not isinstance(white_interfaces, list) + or not isinstance(red_interfaces, list) + or not isinstance(exclude_interfaces, list) + ): self.logger.error("Input params are not lists!") return False @@ -270,6 +272,7 @@ def __delete_vlan_interfaces(self) -> bool: interface.interface_name == vlan_name for interface in self.__interfaces ): + self.__shutdown_interface(vlan_name) # Delete existing VLAN interface subprocess.run(["ip", "link", "delete", vlan_name], check=True) except subprocess.CalledProcessError as e: @@ -310,8 +313,8 @@ def __get_interfaces(self) -> None: # thus should add only physical interfaces, vlan and batadv interfaces if kind is None or kind in self.ALLOWED_KIND_LIST: interfaces.append(interface_info) - # We want to monitor also br-lan status thus expcetion to basic rules - elif kind == "bridge" and ifname== Constants.BR_NAME.value: + # We want to monitor also br-lan status thus exception to basic rules + elif kind == "bridge" and ifname == Constants.BR_NAME.value: interfaces.append(interface_info) ip.close() @@ -362,6 +365,7 @@ def __add_interface_to_bridge( ip = IPRoute() try: + # Get the index of the bridge bridge_indices = ip.link_lookup(ifname=bridge_name) if bridge_indices: bridge_index = bridge_indices[0] @@ -374,7 +378,6 @@ def __add_interface_to_bridge( # Get the index of the interface to add interface_indices = ip.link_lookup(ifname=interface_to_add) - if not interface_indices: self.logger.debug( "Cannot add interface %s to bridge %s!", @@ -384,9 +387,27 @@ def __add_interface_to_bridge( return interface_index = interface_indices[0] - # Add the interface to the bridge - ip.link("set", index=interface_index, master=bridge_index) + # Get the current interface details + interface_info = ip.get_links(interface_index)[0] + current_master = interface_info.get("master") + + if current_master == bridge_index: + self.logger.info( + "Interface %s is already part of the bridge %s.", + interface_to_add, + bridge_name, + ) + else: + # Add the interface to the bridge + ip.link("set", index=interface_index, master=bridge_index) + self.logger.info( + "Successfully added interface %s to bridge %s.", + interface_to_add, + bridge_name, + ) + # Bring the interface up + self.__set_interface_up(interface_to_add) except Exception as e: self.logger.error( "Error adding interface %s to bridge %s! Error: %s", @@ -415,6 +436,7 @@ def __shutdown_interface(self, interface_name: str) -> None: try: index = ip.link_lookup(ifname=interface_name)[0] ip.link("set", index=index, state="down") + ip.link("set", index=index, master=None) except IndexError: self.logger.debug( "Not able to shutdown interface %s! Interface not found!", @@ -510,6 +532,35 @@ def __update_cbma_interface_lists(self) -> None: if interface in self.__lower_cbma_interfaces: self.__lower_cbma_interfaces.remove(interface) + def __update_red_interfaces_list(self): + """ + Updates the red_interfaces list: + 1. Filters red_interfaces to keep only those interfaces that exist in self.__interfaces. + 2. Handles mutually exclusive interfaces, removing eth/end interfaces if a lan interface is found. + 3. Ensures "UPPER_BATMAN" is present in the red_interfaces list. + """ + # Step 1: Filter red_interfaces to keep only interfaces that exist in self.__interfaces. + red_interfaces = [ + interface + for interface in self.__red_interfaces + if any(iface.interface_name == interface for iface in self.__interfaces) + ] + + # Step 2: Remove eth_end_interfaces if any lan interface is found in red_interfaces. + lan_interfaces = {"lan1", "lan2", "lan3"} + eth_end_interfaces = {"eth0", "end0", "end1"} + + if any(lan in red_interfaces for lan in lan_interfaces): + red_interfaces = [ + iface for iface in red_interfaces if iface not in eth_end_interfaces + ] + + # Step 3: Ensure "UPPER_BATMAN" is present in the red_interfaces list. + if self.UPPER_BATMAN not in red_interfaces: + red_interfaces.append(self.UPPER_BATMAN) + # Update the internal red interfaces list. + self.__red_interfaces = red_interfaces + def __wait_for_ap(self, timeout: int = 4) -> bool: start_time = time.time() while True: @@ -587,13 +638,40 @@ def __create_mac(self, randomized: bool = False, interface_mac: str = "") -> str return bytes(mac).hex(sep=":", bytes_per_sep=1) else: # Flip the locally administered bit - mac_bytes = bytearray.fromhex(interface_mac.replace(':', '')) + mac_bytes = bytearray.fromhex(interface_mac.replace(":", "")) mac_bytes[0] ^= 0x2 - return mac_bytes.hex(sep=':', bytes_per_sep=1) + return mac_bytes.hex(sep=":", bytes_per_sep=1) + + def __create_mac_for_bridge(self) -> str: + """ + Creates a MAC address for the bridge. MAC is created from osf, eth0, + end0, end1 or wlan1 interface MAC by flipping the local admin bit. + In case all those interfaces are configured as black interfaces + (i.e. they are configured as red or white interfaces or excluded from CBMA) + then a random MAC is generated for the bridge. + + :return: generated MAC for the bridge usage + """ + interfaces: List[str] = ["osf", "eth0", "end0", "end1", "wlan1"] + for interface in interfaces: + if ( + interface in self.__white_interfaces + or interface in self.__red_interfaces + or interface in self.__na_cbma_interfaces + ): + mac = self.__get_mac_addr(interface) + if mac: + # Flip the locally administered bit + mac_bytes = bytearray.fromhex(mac.replace(":", "")) + if not (mac_bytes[0] & 0x2): + mac_bytes[0] |= 0x2 + return mac_bytes.hex(sep=":", bytes_per_sep=1) + # Generate random MAC in case didn't get any earlier + return self.__create_mac(True) def __init_batman_and_bridge(self) -> None: if_name = self.__comms_ctrl.settings.mesh_vif[0] - if if_name.startswith("halow"): + if if_name.startswith("halow") or if_name.startswith("morse"): if_pattern = "wlp*s0" for interface_name in self.__comms_ctrl.settings.mesh_vif: if fnmatch.fnmatch(interface_name, if_pattern): @@ -615,7 +693,7 @@ def __init_batman_and_bridge(self) -> None: self.__create_bridge(self.BR_NAME) # Set random MAC address for the bridge - self.__set_interface_mac(self.BR_NAME, self.__create_mac(True)) + self.__set_interface_mac(self.BR_NAME, self.__create_mac_for_bridge()) self.__wait_for_interface(self.LOWER_BATMAN) self.__wait_for_interface(self.UPPER_BATMAN) @@ -661,7 +739,7 @@ def __setup_radios(self) -> bool: for interface_name in self.__comms_ctrl.settings.mesh_vif: self.logger.debug("mesh_vif: %s", interface_name) timeout = 5 - if interface_name.startswith("halow"): + if interface_name.startswith("halow") or interface_name.startswith("morse"): timeout = 20 self.__wait_for_interface(interface_name, timeout) @@ -734,6 +812,7 @@ def setup_cbma(self) -> bool: self.__init_batman_and_bridge() self.__update_cbma_interface_lists() + self.__update_red_interfaces_list() # Set radios on self.__setup_radios() @@ -962,6 +1041,11 @@ def __setup_upper_cbma(self) -> bool: return intf_added def __cleanup_cbma(self) -> None: + """ + Shuts down /which removes them from bridge also) Batman interfaces + and then deletes them. Also deletes any created VLAN interfaces but + lets bridge remain for eample for ethernet usage. + """ self.__get_interfaces() self.__shutdown_interface(self.LOWER_BATMAN) self.__shutdown_interface(self.UPPER_BATMAN) @@ -970,7 +1054,6 @@ def __cleanup_cbma(self) -> None: self.__batman.destroy_batman_interface(self.UPPER_BATMAN) self.__delete_vlan_interfaces() - self.__shutdown_and_delete_bridge(self.BR_NAME) def __stop_controller(self, controller, name: str) -> bool: """ From 0031f38a4ed7f93f0e0eeff6b4c25a2472a280c0 Mon Sep 17 00:00:00 2001 From: Mika Joenpera Date: Wed, 23 Oct 2024 11:42:34 +0300 Subject: [PATCH 7/9] Fix "IPv6 connection unreliable from external PC" Avahi workaround Jira-Id: SECO-7551 Signed-off-by: Mika Joenpera --- .../src/nats/conf/default_ms_config.yaml | 3 + .../src/nats/src/cbma_adaptation.py | 35 +++++++++++ .../src/nats/src/comms_service_refresh.py | 59 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py diff --git a/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml b/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml index 4710d932..004bd78a 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml +++ b/modules/sc-mesh-secure-deployment/src/nats/conf/default_ms_config.yaml @@ -2,6 +2,9 @@ # Mesh Shield config file # ########################### +# hostname of the device +hostname: nixos + # All the interfaces are black by default. # Excluded interfaces or interfaces without macsec certificates are not added to lower CBMA. # White interfaces are added to upper CBMA and are excluded automatically from lower CBMA. diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py b/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py index 782f6e94..05b9cd64 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/cbma_adaptation.py @@ -22,6 +22,7 @@ from src.interface import Interface from src import comms_config_store from src.bat_ctrl_utils import BatCtrlUtils +from src.comms_service_refresh import CommsServiceRefresh from controller import CBMAController from models.certificates import CBMACertificates @@ -85,6 +86,7 @@ def __init__( if self.__config is not None: self.__cbma_config = self.__config.read("CBMA") self.__vlan_config = self.__config.read("VLAN") + self.__hostname = self.__config.read("hostname") # Create VLAN interfaces if configured self.__create_vlan_interfaces() @@ -106,6 +108,9 @@ def __init__( self.__red_interfaces.extend(red_interfaces) self.__na_cbma_interfaces.extend(exclude_interfaces) + self.__service_refresh = CommsServiceRefresh(self.__hostname, logger) + self.__service_thread = None + def __validate_cbma_config( self, exclude_interfaces: List[str], @@ -277,6 +282,24 @@ def __delete_vlan_interfaces(self) -> bool: success = False return success + def __set_hostname(self) -> None: + """ + Set hostname for device configured in ms_config.yaml. + """ + try: + subprocess.run(["hostname", self.__hostname], check=True) + except subprocess.CalledProcessError as e: + self.logger.error(f"Error setting hostname {self.__hostname}: {e}") + + def __update_avahi_hostname(self) -> None: + """ + Update avahi hostname for device configured in ms_config.yaml. + """ + try: + subprocess.run(["avahi-set-host-name", self.__hostname], check=True) + except subprocess.CalledProcessError as e: + self.logger.error(f"Error updating avahi hostname {self.__hostname}: {e}") + def __get_interfaces(self) -> None: interfaces = [] ip = IPRoute() @@ -731,6 +754,10 @@ def setup_cbma(self) -> bool: :return: True if both lower and upper CBMA was setup successfully. Returns False otherwise. """ + # hostname updates + self.__set_hostname() + self.__update_avahi_hostname() + self.__init_batman_and_bridge() self.__update_cbma_interface_lists() @@ -762,6 +789,10 @@ def setup_cbma(self) -> bool: # Set batman hop penalty self.__batman.set_hop_penalty() + # Start the service publisher thread + self.__service_thread = threading.Thread(target=self.__service_refresh.dns_service_refresh) + self.__service_thread.start() + return True def __is_valid_ipv6_local(self, address: tuple[str, int]) -> bool: @@ -1007,4 +1038,8 @@ def stop_cbma(self) -> bool: self.__cleanup_cbma() self.stop_radios() + self.__service_refresh.shutdown_service() + if self.__service_thread is not None: + self.__service_thread.join() + return lower_stopped and upper_stopped diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py b/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py new file mode 100644 index 00000000..f7bc6ab6 --- /dev/null +++ b/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py @@ -0,0 +1,59 @@ +""" +Refresher class for the comms service registration +""" +import logging +import threading +import time +import subprocess + +class CommsServiceRefresh: + """ + Comms service publisher class + """ + DNS_SERVICE_EVENT_LOOP_TIMEOUT: int = 5 + + def __init__(self, __hostname, __logger: logging.Logger) -> None: + + self.logger: logging.Logger = __logger.getChild("CommsServicePublisher") + self.logger.setLevel(logging.INFO) + self.__event: threading.Event = threading.Event() + self.__hostname: str = __hostname + + + def __refresh_hostname(self) -> None: + """ + Refresh the hostname with avahi-resolve-host-name + """ + try: + subprocess.run(["avahi-resolve-host-name", self.__hostname + ".local"], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + except subprocess.CalledProcessError: + self.logger.exception("Error refreshing hostname") + + def dns_service_refresh(self) -> None: + """ + Register and re-announce service periodically + """ + self.logger.info("Refresh start") + while not self.__event.is_set(): + time.sleep(self.DNS_SERVICE_EVENT_LOOP_TIMEOUT) + self.__refresh_hostname() + self.logger.info("Refresh stopped") + + + def shutdown_service(self) -> None: + """ + Shutdown the service + """ + self.__event.set() + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger("CommsServiceRefresh") + comms_service_refresh = CommsServiceRefresh("mika-battlestation", logger) + comms_service_refresh.dns_service_refresh() + time.sleep(100) + # Wait for the service to be registered + comms_service_refresh.shutdown_service() From 9e81465dd3dfea8f88ddbf950b5d85c1ea3cfa78 Mon Sep 17 00:00:00 2001 From: Mika Joenpera Date: Thu, 24 Oct 2024 08:05:48 +0300 Subject: [PATCH 8/9] Review fixes Jira-Id: SECO-7551 Signed-off-by: Mika Joenpera --- .../src/nats/src/comms_service_refresh.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py b/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py index f7bc6ab6..2ee46a6b 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py +++ b/modules/sc-mesh-secure-deployment/src/nats/src/comms_service_refresh.py @@ -48,12 +48,3 @@ def shutdown_service(self) -> None: Shutdown the service """ self.__event.set() - -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) - logger = logging.getLogger("CommsServiceRefresh") - comms_service_refresh = CommsServiceRefresh("mika-battlestation", logger) - comms_service_refresh.dns_service_refresh() - time.sleep(100) - # Wait for the service to be registered - comms_service_refresh.shutdown_service() From cf25e2a66b393fd0143e0bde25441884ef85324d Mon Sep 17 00:00:00 2001 From: Mika Joenpera Date: Thu, 24 Oct 2024 12:42:37 +0300 Subject: [PATCH 9/9] Add script for CBMA unittests and fix tests Jira-Id: SECO-7805 Signed-off-by: Mika Joenpera --- .../unittests/test_certificate_handler.py | 12 +++--- .../nats/cbma/unittests/test_secure_socket.py | 2 +- .../src/nats/cbma_coverage_report.txt | 42 +++++++++++++++++++ .../src/nats/run_cbma_unittests_PC.sh | 31 ++++++++++++++ ...sts_PC.sh => run_mdmagent_unittests_PC.sh} | 0 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 modules/sc-mesh-secure-deployment/src/nats/cbma_coverage_report.txt create mode 100755 modules/sc-mesh-secure-deployment/src/nats/run_cbma_unittests_PC.sh rename modules/sc-mesh-secure-deployment/src/nats/{run_unittests_PC.sh => run_mdmagent_unittests_PC.sh} (100%) diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_certificate_handler.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_certificate_handler.py index 4c6e7593..5f49f075 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_certificate_handler.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_certificate_handler.py @@ -23,9 +23,9 @@ def test_openssl_certificate() -> None: end_data: datetime = certificate.get_end_date() assert end_data - assert '64:73:81:83:B3:F1:72:AD:28:6D:BE:9B:0F:A1:D9:D4:7F:75:72:C2' == str(skid) - assert '04:14:64:73:81:83:b3:f1:72:ad:28:6d:be:9b:0f:a1:d9:d4:7f:75:72:c2' == skid.hex(':') - assert '2024-05-13 09:16:24' == str(end_data) - assert ('keyid:64:73:81:83:B3:F1:72:AD:28:6D:BE:9B:0F:A1:D9:D4:7F:75:72:C2\n' - 'DirName:/CN=Stop-Gap Insecure CA\n' - 'serial:13:51:37:43:26:0A:A9:DE:32:FD:1C:EF:18:F2:9B:E7:A6:16:54:EF') == str(akid) + assert '04:14:75:05:b3:00:68:0b:9c:d0:c0:46:e5:b5:aa:58:cb:8b:44:a2:92:d5' == skid.hex(':') + assert '2024-03-17 08:21:07' == str(end_data) + assert '30:16:80:14:14:38:d5:36:b1:5d:ab:f3:30:83:23:f0:6f:5b:c7:bf:2b:51:f5:9c' == akid.hex(':') + + + diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_secure_socket.py b/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_secure_socket.py index 15f6227b..5c241b31 100644 --- a/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_secure_socket.py +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma/unittests/test_secure_socket.py @@ -26,7 +26,7 @@ def __init__(self, cert_paths: CBMACertificates, tls_method: int) -> None: def verify(self, conn, cert, errnum, depth, ok) -> bool: return True - secure_socket = TestSecureSocket(cert_paths, SSL.TLS_SERVER_METHOD) + secure_socket = TestSecureSocket(cert_paths, SSL.SSLv23_METHOD) try: secure_socket.create_ssl_context() assert True diff --git a/modules/sc-mesh-secure-deployment/src/nats/cbma_coverage_report.txt b/modules/sc-mesh-secure-deployment/src/nats/cbma_coverage_report.txt new file mode 100644 index 00000000..f9769d2e --- /dev/null +++ b/modules/sc-mesh-secure-deployment/src/nats/cbma_coverage_report.txt @@ -0,0 +1,42 @@ +============================= test session starts ============================== +platform linux -- Python 3.10.12, pytest-8.0.0, pluggy-1.4.0 -- /home/mika/work/nix/mesh_com/modules/sc-mesh-secure-deployment/src/nats/unittest_cbma/bin/python3 +cachedir: .pytest_cache +rootdir: /home/mika/work/nix/mesh_com/modules/sc-mesh-secure-deployment/src/nats +plugins: cov-5.0.0 +collecting ... collected 2 items + +cbma/unittests/test_certificate_handler.py::test_openssl_certificate PASSED [ 50%] +cbma/unittests/test_secure_socket.py::TestCreateSSLContext::test_certificate_loading PASSED [100%] + +---------- coverage: platform linux, python 3.10.12-final-0 ---------- +Name Stmts Miss Cover Missing +------------------------------------------------------------------------------ +cbma/cbma.py 134 134 0% 1-207 +cbma/certificates/certificates.py 29 3 90% 20, 28, 44 +cbma/controller.py 122 122 0% 1-169 +cbma/models/certificates.py 19 3 84% 17, 21, 25 +cbma/models/secure_socket/secure_connection.py 28 8 71% 10, 15, 20, 25, 30, 35, 40, 45 +cbma/models/secure_socket/secure_context.py 8 1 88% 13 +cbma/models/secure_socket/verification.py 11 1 91% 19 +cbma/secure_socket/__init__.py 0 0 100% +cbma/secure_socket/client.py 79 79 0% 1-127 +cbma/secure_socket/secure_connection.py 47 28 40% 27-30, 34, 38, 42-46, 50-56, 60-70 +cbma/secure_socket/secure_context.py 34 7 79% 58-61, 65-68 +cbma/secure_socket/secure_socket.py 11 2 82% 14-16 +cbma/secure_socket/server.py 73 73 0% 1-105 +cbma/secure_socket/verification.py 78 54 31% 43-54, 58-68, 72-76, 80-103, 107-114, 128-145 +cbma/standalone.py 106 106 0% 1-175 +cbma/unittests/test_certificate_handler.py 19 0 100% +cbma/unittests/test_secure_socket.py 24 3 88% 27, 33-34 +cbma/utils/__init__.py 0 0 100% +cbma/utils/certificates.py 22 14 36% 18-33 +cbma/utils/common.py 38 29 24% 12-28, 33-48, 52, 56, 60-61 +cbma/utils/logging.py 132 73 45% 29, 49, 58-60, 68-69, 87-123, 127-136, 145, 154-159, 169-199 +cbma/utils/macsec.py 81 81 0% 1-125 +cbma/utils/multicast.py 2 2 0% 3-8 +cbma/utils/networking.py 30 21 30% 13-16, 20-25, 29-40, 44-49 +------------------------------------------------------------------------------ +TOTAL 1127 844 25% + + +============================== 2 passed in 0.22s =============================== diff --git a/modules/sc-mesh-secure-deployment/src/nats/run_cbma_unittests_PC.sh b/modules/sc-mesh-secure-deployment/src/nats/run_cbma_unittests_PC.sh new file mode 100755 index 00000000..69712661 --- /dev/null +++ b/modules/sc-mesh-secure-deployment/src/nats/run_cbma_unittests_PC.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# preconditions +if [ ! -f "$(pwd)/$(basename $0)" ]; then + echo "Script is not being executed in the same folder" + exit 1 +fi + +# python virtualenv +python3 -m venv unittest_cbma +source unittest_cbma/bin/activate + +# install dependencies to virtualenv +pip install -r ./cbma/requirements.txt +pip install -r ./requirements.txt +# install testing only related dependencies +pip install pytest pytest-cov + +# discover and run unittests +pytest --cov=cbma --cov-report term-missing -v --ignore=lucius/unittests --ignore=debug_tests --ignore=cbma/unittest --ignore=cbma/tests --ignore=tests |& tee ./cbma_coverage_report.txt + +## deactivate virtualenv +deactivate + +# Clean up __pycache__ directories +find . -type d -name '__pycache__' -exec rm -rf {} + +# Clean up unittest venv +rm -rf unittest_cbma +# Clean up coverage tool's SQL database +rm -f .coverage + diff --git a/modules/sc-mesh-secure-deployment/src/nats/run_unittests_PC.sh b/modules/sc-mesh-secure-deployment/src/nats/run_mdmagent_unittests_PC.sh similarity index 100% rename from modules/sc-mesh-secure-deployment/src/nats/run_unittests_PC.sh rename to modules/sc-mesh-secure-deployment/src/nats/run_mdmagent_unittests_PC.sh