From f827d945bea0a0ec594fe4f62c75f906d2c5abf8 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 9 May 2026 04:43:22 +0200 Subject: [PATCH] Implemented path request ingress burst control and egress limiting --- RNS/Discovery.py | 3 ++- RNS/Interfaces/BackboneInterface.py | 2 +- RNS/Interfaces/Interface.py | 4 ++-- RNS/Transport.py | 28 +++++++++++++++++++--------- RNS/Utilities/rnstatus.py | 2 +- docs/source/interfaces.rst | 4 ++-- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/RNS/Discovery.py b/RNS/Discovery.py index f5ba4ed3..4c033695 100644 --- a/RNS/Discovery.py +++ b/RNS/Discovery.py @@ -661,10 +661,11 @@ class InterfaceDiscovery(): RNS.log(f"Auto-connecting discovered {interface_type} {interface_name}") interface.autoconnect_hash = endpoint_hash interface.autoconnect_source = info["network_id"] + mode = RNS.Interfaces.Interface.Interface.MODE_GATEWAY if RNS.Reticulum.transport_enabled() else None ar_target = RNS.Reticulum.get_instance()._default_ar_target() if RNS.Reticulum.transport_enabled() else None ar_penalty = RNS.Reticulum.get_instance()._default_ar_penalty() if RNS.Reticulum.transport_enabled() else None ar_grace = RNS.Reticulum.get_instance()._default_ar_grace() if RNS.Reticulum.transport_enabled() else None - RNS.Reticulum.get_instance()._add_interface(interface, ifac_netname=ifac_netname, ifac_netkey=ifac_netkey, configured_bitrate=5E6, + RNS.Reticulum.get_instance()._add_interface(interface, mode=mode, ifac_netname=ifac_netname, ifac_netkey=ifac_netkey, configured_bitrate=5E6, announce_rate_target=ar_target, announce_rate_grace=ar_grace, announce_rate_penalty=ar_penalty) self.monitor_interface(interface) diff --git a/RNS/Interfaces/BackboneInterface.py b/RNS/Interfaces/BackboneInterface.py index aafde443..4e012f72 100644 --- a/RNS/Interfaces/BackboneInterface.py +++ b/RNS/Interfaces/BackboneInterface.py @@ -263,7 +263,7 @@ class BackboneInterface(Interface): try: BackboneInterface.epoll.unregister(fileno) except Exception as e: - RNS.log(f"An error occurred while deregistering file descriptor {fileno}: {e}", RNS.LOG_WARNING) + RNS.log(f"An error occurred while deregistering file descriptor {fileno}: {e}", RNS.LOG_DEBUG) @staticmethod def deregister_listeners(): diff --git a/RNS/Interfaces/Interface.py b/RNS/Interfaces/Interface.py index 8ae875cb..69202564 100755 --- a/RNS/Interfaces/Interface.py +++ b/RNS/Interfaces/Interface.py @@ -76,12 +76,12 @@ class Interface: IC_BURST_FREQ_NEW = 3 IC_BURST_FREQ = 10 IC_PR_BURST_FREQ_NEW = 3 - IC_PR_BURST_FREQ = 10 + IC_PR_BURST_FREQ = 8 IC_BURST_HOLD = 15 IC_BURST_PENALTY = 15 IC_HELD_RELEASE_INTERVAL = 5 IC_DEQUE_MIN_SAMPLE = 2 - IC_BURST_MIN_SAMPLES = 8 + IC_BURST_MIN_SAMPLES = 6 EC_PR_FREQ = 5 EGRESS_CONTROL = False diff --git a/RNS/Transport.py b/RNS/Transport.py index c10085e8..6e528499 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -920,6 +920,7 @@ class Transport: try: for interface in Transport.interfaces: interface.should_ingress_limit() + interface.should_ingress_limit_pr() interface.process_held_announces() if interface.phy_keepalive: interface.send_keepalive() Transport.interface_last_jobs = time.time() @@ -2182,8 +2183,7 @@ class Transport: RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None except Exception as e: - RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR) - + RNS.log("Could not transport link request proof. The contained exception was: "+str(e), RNS.LOG_WARNING) else: RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None else: @@ -2895,15 +2895,16 @@ class Transport: @staticmethod def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None, tag=None): should_search_for_unknown = False + should_ingress_limit = False if attached_interface != None: - if RNS.Reticulum.transport_enabled() and attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR: - should_search_for_unknown = True - interface_str = " on "+str(attached_interface) - - else: interface_str = "" + should_ingress_limit = attached_interface.should_ingress_limit_pr() + if RNS.Reticulum.transport_enabled(): + if attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR: should_search_for_unknown = True - RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None + if RNS.sl(RNS.LOG_DEBUG): + interface_str = f" on {attached_interface}" + RNS.log(f"Path request for {RNS.prettyhexrep(destination_hash)}{interface_str}", RNS.LOG_DEBUG) destination_exists_on_local_client = False if len(Transport.local_client_interfaces) > 0: @@ -3000,6 +3001,15 @@ class Transport: if destination_hash in Transport.discovery_path_requests: RNS.log("There is already a waiting path request for "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None else: + # Abort recursive path request if receiving + # interface has PR burst active, or should + # otherwise ingress limit path requests. + if should_ingress_limit: + if RNS.sl(RNS.LOG_DEBUG): + interface_str = f" for {attached_interface}" if attached_interface else "" + RNS.log(f"Not sending recursive path request{interface_str} due to active ingress limiting", RNS.LOG_DEBUG) + return + # Forward path request on all interfaces # except the requestor interface RNS.log("Attempting to discover unknown path to "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None @@ -3009,7 +3019,7 @@ class Transport: for interface in Transport.interfaces: if not interface == attached_interface: if interface.should_egress_limit_pr(): - RNS.log(f"Not sending recursive path request on {interface} due to active egress limiting", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None + RNS.log(f"Not sending recursive path request on {interface} due to active egress limiting", RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None else: # Use the previously extracted tag from this path request # on the new path requests as well, to avoid potential loops diff --git a/RNS/Utilities/rnstatus.py b/RNS/Utilities/rnstatus.py index b663e342..3556ec37 100644 --- a/RNS/Utilities/rnstatus.py +++ b/RNS/Utilities/rnstatus.py @@ -556,7 +556,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= pburst_str = "" if "pr_burst_active" in ifstat and ifstat["pr_burst_active"]: for_str = RNS.prettytime(time.time()-ifstat["pr_burst_activated"]) - pburst_str = f" burst for {for_str}" + pburst_str = f"burst for {for_str}" rxb_str = "↓"+RNS.prettysize(ifstat["rxb"]) txb_str = "↑"+RNS.prettysize(ifstat["txb"]) diff --git a/docs/source/interfaces.rst b/docs/source/interfaces.rst index c4c6124b..079c5b1e 100644 --- a/docs/source/interfaces.rst +++ b/docs/source/interfaces.rst @@ -1460,10 +1460,10 @@ but all the parameters are exposed for configuration if needed. * | The ``ic_pr_burst_freq_new`` option sets the maximum path request ingress frequency for newly spawned interfaces. Defaults to ``3`` - announces per second. + path requests per second. * | The ``ic_pr_burst_freq`` option sets the maximum path request - ingress frequency for other interfaces. Defaults to ``10`` announces + ingress frequency for other interfaces. Defaults to ``8`` path requests per second. *If an interface exceeds its burst frequency, incoming path requests