diff --git a/RNS/Discovery.py b/RNS/Discovery.py index c458a49..affd946 100644 --- a/RNS/Discovery.py +++ b/RNS/Discovery.py @@ -442,9 +442,11 @@ class InterfaceDiscovery(): while self.monitoring_autoconnects: time.sleep(self.monitor_interval) detached_interfaces = [] + online_interfaces = 0 for interface in self.monitored_interfaces: try: if interface.online: + online_interfaces += 1 if hasattr(interface, "autoconnect_down") and interface.autoconnect_down != None: RNS.log(f"Auto-discovered interface {interface} reconnected") interface.autoconnect_down = None @@ -458,19 +460,32 @@ class InterfaceDiscovery(): down_for = time.time()-interface.autoconnect_down if down_for >= self.detach_threshold: RNS.log(f"Auto-discovered interface {interface} has been down for {RNS.prettytime(down_for)}, detaching", RNS.LOG_DEBUG) - interface.detach() detached_interfaces.append(interface) except Exception as e: RNS.log(f"Error while checking auto-connected interface state for {interface}: {e}", RNS.LOG_ERROR) + if online_interfaces >= RNS.Reticulum.max_autoconnected_interfaces(): + for interface in RNS.Transport.interfaces: + if hasattr(interface, "bootstrap_only") and interface.bootstrap_only == True: + RNS.log(f"Tearing down bootstrap-only {interface} since target connected auto-discovered interface count has been reached", RNS.LOG_INFO) + if not interface in detached_interfaces: detached_interfaces.append(interface) + + if online_interfaces == 0: + RNS.log(f"No auto-discovered interfaces connected, re-enabling bootstrap interfaces", RNS.LOG_NOTICE) + # TODO: Implement + RNS.log(f"Available bootstrap configs:\n{RNS.Reticulum.get_instance().bootstrap_configs}", RNS.LOG_DEBUG) + for interface in detached_interfaces: - try: - self.monitored_interfaces.remove(interface) - RNS.Transport.interfaces.remove(interface) + try: self.teardown_interface(interface) except Exception as e: RNS.log(f"Error while de-registering auto-connected interface from transport: {e}", RNS.LOG_ERROR) + def teardown_interface(self, interface): + interface.detach() + RNS.Transport.interfaces.remove(interface) + self.monitored_interfaces.remove(interface) + def autoconnect_count(self): return len([i for i in RNS.Transport.interfaces if hasattr(i, "autoconnect_hash")]) diff --git a/RNS/Interfaces/Interface.py b/RNS/Interfaces/Interface.py index 6c67c72..bbae508 100755 --- a/RNS/Interfaces/Interface.py +++ b/RNS/Interfaces/Interface.py @@ -87,6 +87,7 @@ class Interface: self.supports_discovery = False self.discoverable = False self.last_discovery_announce = 0 + self.bootstrap_only = False self.parent_interface = None self.spawned_interfaces = None self.tunnel_id = None diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index cb46804..d3a2abf 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -276,6 +276,7 @@ class Reticulum: self.rpc_key = None self.rpc_type = "AF_INET" self.use_af_unix = False + self.bootstrap_configs = [] self.ifac_salt = Reticulum.IFAC_SALT @@ -704,6 +705,9 @@ class Reticulum: if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100: announce_cap = c.as_float("announce_cap")/100.0 + bootstrap_only = False + if "bootstrap_only" in c: bootstrap_only = c.as_bool("bootstrap_only") + ignore_config_warnings = False if "ignore_config_warnings" in c: ignore_config_warnings = c.as_bool("ignore_config_warnings") @@ -753,13 +757,12 @@ class Reticulum: try: def interface_post_init(interface): if interface != None: - if "outgoing" in c and c.as_bool("outgoing") == False: - interface.OUT = False - else: - interface.OUT = True + if "outgoing" in c and c.as_bool("outgoing") == False: interface.OUT = False + else: interface.OUT = True interface.mode = interface_mode interface.announce_cap = announce_cap + interface.bootstrap_only = bootstrap_only if configured_bitrate: interface.bitrate = configured_bitrate interface.optimise_mtu() @@ -894,6 +897,8 @@ class Reticulum: interface = WeaveInterface.WeaveInterface(RNS.Transport, interface_config) interface_post_init(interface) + if bootstrap_only: self.bootstrap_configs.append(interface_config) + if interface == None: # Interface was not handled by any internal interface types, # attempt to load and initialise it from user-supplied modules @@ -936,22 +941,20 @@ class Reticulum: RNS.log("System interfaces are ready", RNS.LOG_VERBOSE) - def _add_interface(self, interface, mode = None, configured_bitrate=None, ifac_size=None, ifac_netname=None, ifac_netkey=None, announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None): + def _add_interface(self, interface, mode = None, configured_bitrate=None, ifac_size=None, ifac_netname=None, ifac_netkey=None, + announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None, bootstrap_only=False): if not self.is_connected_to_shared_instance: if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface): - if mode == None: - mode = Interface.Interface.MODE_FULL + if mode == None: mode = Interface.Interface.MODE_FULL interface.mode = mode - if configured_bitrate: - interface.bitrate = configured_bitrate + if configured_bitrate: interface.bitrate = configured_bitrate + if bootstrap_only == True: interface.bootstrap_only = True interface.optimise_mtu() - if ifac_size != None: - interface.ifac_size = ifac_size - else: - interface.ifac_size = 8 + if ifac_size != None: interface.ifac_size = ifac_size + else: interface.ifac_size = 8 interface.announce_cap = announce_cap if announce_cap != None else Reticulum.ANNOUNCE_CAP/100.0 interface.announce_rate_target = announce_rate_target