Thoughts about unidirectional connections (especially in LoRa networks)
Started by nilu96 ·
I really like both of those proposed changes. Very elegant and non-disruptive. Correct me if I'm wrong, but this still leaves the simplest case of a PLAIN packet without PROOF prone to pathing blackholes, is that right? Not saying that's a deal breaker at all, just calling it out. I know that often times sensor devices have very low power budgets and rely on fire-and-forget followed immediately by a deep sleep to conserve power. Is it safe to say that these sorts of applications would remain vulnerable?
gdane wrote:
I really like both of those proposed changes. Very elegant and non-disruptive. Correct me if I'm wrong, but this still leaves the simplest case of a PLAIN packet without PROOF prone to pathing blackholes, is that right? Not saying that's a deal breaker at all, just calling it out. I know that often times sensor devices have very low power budgets and rely on fire-and-forget followed immediately by a deep sleep to conserve power. Is it safe to say that these sorts of applications would remain vulnerable?
Yep, that's the case that still stays dark. And as I sit with it, I think that's correct? It's fundamentally fire and forget. Mechanisms are there if you want lightweight but confirmed (the proof).
As far as I can tell, this is endemic to the problem of wanting fire and forget, or wanting confirmation. And the retry policy is still purely the app's choice (like LXMF, in the common case).
KenAKAFrosty wrote:
Yep, that's the case that still stays dark. And as I sit with it, I think that's correct? It's fundamentally fire and forget. Mechanisms are there if you want lightweight but confirmed (the proof).
As far as I can tell, this is endemic to the problem of wanting fire and forget, or wanting confirmation. And the retry policy is still purely the app's choice (like LXMF, in the common case).
Understood. Technically I still think that it should be the stack's responsibility to detect an asymmetry and not route packets to a next-hop that can't hear it, but that being said we do at least have a proposed solution for the vast majority of cases. And in the case of fire-and-forget, it's not terribly inconvenient for the sender to occasionally request a proof just to give intermediate hops an opportunity to detect and path around asymmetries.
So the tldr if I'm not mistaken is that when sending a packet, a client can request that a proof is sent back to the client, and then all the intermediate transports as well as the orginal client can use the fact that the proof did or did not come back to detect asymmetric/non working connections.
If this does work how I think it does then it also beats most types of malicious nodes as they can't fake the packets and it also lets upstream transport nodes get some extra information about if the interface they are sending to, ie, "the packets I'm sending on the behalf of destination X on interface Y aren't coming back, maybe I should send it through interface Z instead", using the hop count as the basis you could have closer nodes choose attempt other paths before further ones and that might help if a lot of nodes in a single area get distributed. Also you be able to instantly tell a transport node has gone offline or become unreliable possibly within a few hundred milliseconds because if all proofs stop coming back you know to back off which makes the network more reliable
It also begs the question if a transport node should try resending the packet itself if it knows that path is unreliable instead of relying on the packet, probabaly not but worth thinking about.
One last note which is semi related, maybe we could use one or two bits of the header to denote what the characteristics of the pathing, bassically if total capacity, latency or reliability, if you are doing file transfer it makes sense to juse use the path with the highest capacity regardless of latency and packet loss (up to a point), for a game latency is probabaly most important and for a microcontroller with limited resources, sending multiple packets and dealing with packet loss may be very expensive and latency and capacity are non issues. I have not thought about this at all I'm just throwing it out there.
gdane wrote:
KenAKAFrosty wrote:
> Yep, that's the case that still stays dark. And as I sit with it, I think that's correct? It's fundamentally fire and forget. Mechanisms are there if you want lightweight but confirmed (the proof).
>
> As far as I can tell, this is endemic to the problem of wanting fire and forget, or wanting confirmation. And the retry policy is still purely the app's choice (like LXMF, in the common case).Understood. Technically I still think that it should be the stack's responsibility to detect an asymmetry and not route packets to a next-hop that can't hear it, but that being said we do at least have a proposed solution for the vast majority of cases. And in the case of fire-and-forget, it's not terribly inconvenient for the sender to occasionally request a proof just to give intermediate hops an opportunity to detect and path around asymmetries.
Yeah I'm with ya there, I think I just find myself in the camp that says the proof back is that way (and most techniques that we might come up with would probably reduce back to the same kind of 'ack-based' approach anyway).
Btw, you mentioned getting some nodes up to collect data. That will be HUGE. I can do the same and would love to work with you on a good way to pool our data all together. I'm also looking at the same timeline (~by the end of this month)
welo wrote:
So the tldr if I'm not mistaken is that when sending a packet, a client can request that a proof is sent back to the client, and then all the intermediate transports as well as the orginal client can use the fact that the proof did or did not come back to detect asymmetric/non working connections.
If this does work how I think it does then it also beats most types of malicious nodes as they can't fake the packets and it also lets upstream transport nodes get some extra information about if the interface they are sending to, ie, "the packets I'm sending on the behalf of destination X on interface Y aren't coming back, maybe I should send it through interface Z instead", using the hop count as the basis you could have closer nodes choose attempt other paths before further ones and that might help if a lot of nodes in a single area get distributed. Also you be able to instantly tell a transport node has gone offline or become unreliable possibly within a few hundred milliseconds because if all proofs stop coming back you know to back off which makes the network more reliable
It also begs the question if a transport node should try resending the packet itself if it knows that path is unreliable instead of relying on the packet, probabaly not but worth thinking about.
One last note which is semi related, maybe we could use one or two bits of the header to denote what the characteristics of the pathing, bassically if total capacity, latency or reliability, if you are doing file transfer it makes sense to juse use the path with the highest capacity regardless of latency and packet loss (up to a point), for a game latency is probabaly most important and for a microcontroller with limited resources, sending multiple packets and dealing with packet loss may be very expensive and latency and capacity are non issues. I have not thought about this at all I'm just throwing it out there.
You make some great points, yeah! Once the data is there, transport node logic could keep slowly evolving to use these kinds of things in more refined ways. And yes! the timings here can be pretty tight, the adaptation will happen faster than it seems when we whiteboard it out.
And one clarification: the proof expected from initiator, and the proof handling at the recipient ---- those are all active today. THat's not new machinery. The only major difference here is basically the metadata that the initiator is expecting a proof. It already was expecting it, now that fact can just be picked up by transport.
For this preposed solution, is there anything that guaranteeing that the return path is the same the initial send oath or are we just assuming it'll probabaly be the same
welo wrote:
For this preposed solution, is there anything that guaranteeing that the return path is the same the initial send oath or are we just assuming it'll probabaly be the same
It fundamentally is and has to be. It already is; it's the 'reverse table' and it's just like its name implies
KenAKAFrosty wrote:
This proposal would extend the previous benefits to SINGLE DATA packets (and by extension, LXMF's OPPORTUNISTIC message type), not just Links.
...
Thoughts, objections, additions, concerns?
Been mulling over this for a few days and even wrote a PoC implementation for microReticulum. I also put some of my thoughts and questions to Claude and got back a few points of concern that are worth considering:
- Metadata leak / traffic correlation. Reticulum is unusually protective about on-path metadata — no source address, no version/timestamp on the Fernet-style token, etc. A plaintext "I expect a return packet" bit visible to every hop makes request/response correlation easier for an on-path adversary than it is today. It's a small marginal leak (proofs are already observable when they flow back), but it makes the correlation cheap — you only watch flagged packets.
- False negatives from receiver autonomy. Today the receiver alone decides whether to prove. A transport node treating "flag set + no proof = unresponsive path" will misclassify any path whose endpoint is PROVE_NONE or whose PROVE_APP callback declined. Without changing receiver semantics, the signal is noisy by design.
- State amplification. Per-flagged-packet timers across all transport nodes on the route are cheap individually but represent a new attack surface: a sender flooding flagged packets to unresponsive destinations forces every hop to allocate and time-out state. Mitigable but worth bounding.
#2 I think can be largely mitigated by the contract between sender and receiver. Like you mentioned previously, for LXMF there's already an expectation of receiving a proof so adding the flag really only apprises transport nodes to this fact.
#3 I think is exaggerated since we're already maintaining the reverse table, and we're really only adding a few fields to those existing entries to track proof timeouts.
#1 is the outstanding concern imo. I'm sure Mark would balk at it because it does expose state that could be exploited, although it's not immediately clear to me what the risk actually is.
Just some more things to consider.
I'd like to put forward an alternative proposal for detection of asymmetric next-hop failures using passive neighbor probing.
I propose adding local-only transport-side liveness inference to transport nodes. Each transport node passively tracks per-direct-neighbor counters of forwarded packets and returned proofs using the existing reverse_table flow. When a neighbor shows recent forwarding activity but no proof returns (regardless of whether proofs are expected or not) within a configurable window, the local node issues a single rnprobe-style packet to that neighbor's built-in probe_destination (which is PROVE_ALL and announced via mgmt_announce). A successful probe confirms bidirectional symmetry; timeout demotes every path table entry whose next-hop is the asymmetric neighbor via the existing mark_path_unresponsive() primitive, which integrates with announce-driven replacement logic to allow recovery when a fresh announce arrives.
The mechanism reuses existing infrastructure: Transport.probe_destination on the receiver side, mgmt_destinations announce propagation, reverse_table for forwarding accounting, Identity.recall with from_identity_hash=True for resolving the neighbor's Identity from its transport_id, mark_path_unresponsive / mark_path_responsive and the announce-replacement logic that consumes their state, and PacketReceipt delivery/timeout callbacks. No new packet types, contexts, header bits, or wire-format changes.
This proposal enables asymmetry detection even with SINGLE DATA (fire-and-forget) traffic and doesn't require any node opt-in or mechanisms that aren't already included in the reference implementation today. Of course transport nodes that don't implement this would not benefit from asymmetric neighbor detection, but it still plays well with those "stock" nodes.
Note that this is already implemented as a PoC in the latest microReticulum so we can begin to gather stats and anecdotal information about effectiveness.
Would love to hear any comments, concerns, or suggestions for improving.
I've been thinking about this issue of lost messages while I've been out working on the farm trying to raise chemical free nutrient dense food to save humanity by God's grace. I was going to write Mark a note and this seems to be the best thread to do so. From what I understand (correct me if I'm wrong), path tables are constructed based on announcements; these path tables contain every hop necessary for the sender to reach a specific destination establishing a link or transmitting packets. All of these hops are transport=enabled nodes. By the time a message is sent, the paths are already established based on previous announces. I know that transport nodes are supposed to be stationary, but the problem is that many of the client nodes travel around and while one path may work now, 15 minutes later, the needed path will be different. If the announce schedule is set to 30 minutes, the new path will not have been constructed yet. What complicates this, is when there are 2 competing transport nodes within close enough range, but the one in the current path is not stable. If we are going to make this work better, I suggest we think in terms of making MOBILE transport nodes.
I believe that there is a receipt of delivery mechanism in RNS (although this may be in the application layer of LXMF). As such, I suggest:
- When transport is enabled, the last 2 paths must be saved instead of overwritten.
- If a receipt of delivery is not received or message fails, retry the send with the last successful path. (That does mean the message must start over from the sender)
- If that fails, try the third saved path
- If the third try fails, send to propagation if enabled.
This method would, I believe, require everyone to use the same update of RNS and microReticulum. Just my two cents. Thank you for all you, Chad and Mark are doing!
daylight-hub wrote:
I've been thinking about this issue of lost messages while I've been out working on the farm trying to raise chemical free nutrient dense food to save humanity by God's grace.
Good on you! We should all be getting our hands dirty in our food system, figuratively and literally.
I was going to write Mark a note and this seems to be the best thread to do so. From what I understand (correct me if I'm wrong), path tables are constructed based on announcements; these path tables contain every hop necessary for the sender to reach a specific destination establishing a link or transmitting packets.
Yes, but the way you word it makes it sound like every transport node stores a full list of hops necessary to reach a given destination, when in fact each transport node only stores the single next hop to reach that destination. That's probably what you meant but wanted to clarify for anyone else reading something else into it.
All of these hops are transport=enabled nodes. By the time a message is sent, the paths are already established based on previous announces. I know that transport nodes are supposed to be stationary, but the problem is that many of the client nodes travel around and while one path may work now, 15 minutes later, the needed path will be different. If the announce schedule is set to 30 minutes, the new path will not have been constructed yet. What complicates this, is when there are 2 competing transport nodes within close enough range, but the one in the current path is not stable. If we are going to make this work better, I suggest we think in terms of making MOBILE transport nodes.
There is already the concept of a "mobile node" in RNS configured by setting an interface mode to "roaming". Among other things, this adjusts internal timers to make paths expire faster. Afaik it does nothing to "heal" paths caused by a transport node going out of range or disappearing though.
I believe that there is a receipt of delivery mechanism in RNS (although this may be in the application layer of LXMF). As such, I suggest:
- When transport is enabled, the last 2 paths must be saved instead of overwritten.
- If a receipt of delivery is not received or message fails, retry the send with the last successful path. (That does mean the message must start over from the sender)
- If that fails, try the third saved path
- If the third try fails, send to propagation if enabled.
This method would, I believe, require everyone to use the same update of RNS and microReticulum. Just my two cents. Thank you for all you, Chad and Mark are doing!
Yes there is a general packet delivery receipt built-in to RNS, but it's controlled by the recipient so senders can't request one, and transport nodes have no way of knowing whether to expect one.
Links are an exception here because they implicitly use these delivery receipts, so transport nodes can and currently do exploit this to detect next-hops that have stopped responding (per your #2 above) and they are marked UNRESPONSIVE. This triggers some logic to try to find a new path, but that logic falls short imo.
@KenAKAFrosty proposed extending it to stop a transport node from announcing unresponsive paths which is a huge improvement imo.
My proposal above attempts to further extend this by passively (and heuristically) detecting when a next-hop may have either gone away or is suffering from an asymmetry (where we can hear it but it can't hear us resulting in an effective blackhole).
I like your idea of extending further to keep multiple prioritized paths to a destination so that we have a backup if needed. I does present challenges on MCUs however where storage is often extremely tight and we already don't have space to store as many paths as we'd like. We can alternatively send a path request to our neighbors to find a new path when one is marked unresponsive. RNS may already be doing this under certain circumstances but I haven't fully traced that yet.
Passive direct neighbor tracking is exactly what I’ve been thinking about over the past few weeks. Here are some of my notes and ideas—hopefully. Maybe some of this helps for further discussions.
Overarching Goal
Identify stable, bidirectional connections so we can prioritize routes where we are confident the next hop will receive the packet.
Discarded Idea: Tracking Repeated Announces
Initially, I considered tracking repeated announces that a node recently broadcasted. However, announce behavior in a complex mesh is too chaotic for this to be reliable. Announces can be received from other nodes, and the rules around retry counters (decreasing on same hop count, dropping on hops+1) make it too unpredictable.
Proposed Tracking Mechanisms
Instead, here are three passive methods that might yield better results:
- 1-Hop Announces: Listen only to announces for our own destinations where
hops == 1. Since there are no alternative paths for a 1-hop announce, this should be a reliable way to track responsiveness of direct neighbors. BUT This only works to set neighbors to aRESPONSIVEstate. If a nearby node heard an announce it might not repeat it if another node already repeated it before. - Passive Packet Monitoring: Listen for transport nodes to repeat regular packets. If a packet is sent to a next hop that is a transport node (not the final destination) and that node does not repeat it, mark the neighbor as
UNRESPONSIVE. - Link Establishment Proofs (
LRPROOF/PROOF):- If received: Mark neighbor as
RESPONSIVE. - If missing: Mark neighbor as
UNRESPONSIVE. (Note: This missing check only reliably works forLRPROOFwhen we are the last node before the final destination. RegularPROOFisn't mandatory, andLRPROOFcould just be lost on earlier hops, if we are multiple hops away from final destination).
- If received: Mark neighbor as
State Management
A strict binary state (RESPONSIVE / UNRESPONSIVE) might be too brittle. It could be better to:
- Allow thresholds: Tolerate a certain amount of packet loss before flipping a neighbor to unresponsive.
- Allow healing: Add a timeout mechanism so the
UNRESPONSIVEflag can "heal" and revert after a certain period of time.
Routing Impacts
If a neighbor is flagged as UNRESPONSIVE, here is how I imagine it should impact routing rules:
Handling Announces from UNRESPONSIVE Neighbors:
- Do not repeat announces from this node (since we know we can't reliably reach them).
- Path Creation: If the destination is currently unknown, create a new path entry.
- Path Updates:
- DON'T overwrite an existing path that goes through a
RESPONSIVE(or yet unclassified) neighbor, even if theUNRESPONSIVEroute has fewer hops. (maybe use an internal hop penalty?) - DO update the path regularly (depending on hop count) if the existing route also relies on an
UNRESPONSIVEneighbor.
- DON'T overwrite an existing path that goes through a
These rules would allow that an announce that took a slightly longer but stable path to be preferred over a path via an unresponsive neighbor.
Handling Path Requests for UNRESPONSIVE Neighbors:
- Repeat the path request, but do not answer it directly.
Very mildly off topic, do we have any ways of testing these proposals, they all seem to work with some drawbacks and benefits but it's impossible to know until we test them, and testing them practically is probably not possible, is there any easy way to do a simulation for these?
That is a good point. There seems to be a Simulator by RFnexus: https://github.com/RFnexus/reticulated
But I did not get my hands on it yet. I think it spins up multiple instances of RNS and then simulates a mesh. So it should be possible to make adjustments to the local RNS instance to test behaviour in different networks.
Some additional thought about the initial implementation in microReticulum:
When a neighbor shows recent forwarding activity but no proof returns (regardless of whether proofs are expected or not) within a configurable window, the local node issues a single rnprobe-style packet to that neighbor's built-in probe_destination (which is PROVE_ALL and announced via mgmt_announce). A successful probe confirms bidirectional symmetry
As far as I understand, this will not if the probe destination is disabled for a transport node in the neighborhood, right? So paths will marked unresponsive, even tho there might be a fully bidirectional connection between nodes.
timeout demotes every path table entry whose next-hop is the asymmetric neighbor via the existing mark_path_unresponsive() primitive, which integrates with announce-driven replacement logic to allow recovery when a fresh announce arrives.
Am I right, that a fresh announce would reset the path to its old state? So let's assume Node A announces itself. Node B acts as a transport node and repeats the announce. Node C hears it. But the connection between B and C is unidirectional (C can hear B but not the other way around). Of course, probing will fail because B doesn't hear the probe request of C. The path to A will be marked as UNRESPONSIVE. But after a fresh announce of A, that takes the very same path again, this UNRESPONSIVE state will be resetted. Is that correct?
nilu96 wrote:
Some additional thought about the initial implementation in microReticulum:
> When a neighbor shows recent forwarding activity but no proof returns (regardless of whether proofs are expected or not) within a configurable window, the local node issues a single rnprobe-style packet to that neighbor's built-in probe_destination (which is PROVE_ALL and announced via mgmt_announce). A successful probe confirms bidirectional symmetry
As far as I understand, this will not if the probe destination is disabled for a transport node in the neighborhood, right? So paths will marked unresponsive, even tho there might be a fully bidirectional connection between nodes.
No it shouldn't mark paths to a neighbor without probes enabled as unresponsive. It first checks to see if it has a path to the probe destination before sending a probe, which it will not if the neighbor is not announcing the probe destination. If the probe is never sent then it can never timeout and trigger unresponsive neighbor handling.
> timeout demotes every path table entry whose next-hop is the asymmetric neighbor via the existing mark_path_unresponsive() primitive, which integrates with announce-driven replacement logic to allow recovery when a fresh announce arrives.
Am I right, that a fresh announce would reset the path to its old state? So let's assume Node A announces itself. Node B acts as a transport node and repeats the announce. Node C hears it. But the connection between B and C is unidirectional (C can hear B but not the other way around). Of course, probing will fail because B doesn't hear the probe request of C. The path to A will be marked as
UNRESPONSIVE. But after a fresh announce of A, that takes the very same path again, thisUNRESPONSIVEstate will be resetted. Is that correct?
Currently it just triggers the same unresponsive path handling as the Python reference implementation through Transport.mark_path_unresponsive(), and I believe you're correct that the next announce from that node will
reset it. I think we can do better than this though, and at least compare the received announce neighbor against the neighbor that is unresponsive and reject it for some period of time.
Another interesting attack vector would be if a large of nodes got compromised and decided to void all traffic to a destination at once. For example imagine a nation state set up a very large amount of reticulum back bone connections across its country but people still own and operate their own nodes which can bridge any two reticulum instances.
If that nation state then wants to block a certain destination, it can't just blacklist it as normal because then the announcements would go through the alternative paths and create valid paths, so instead the packets traffic must be voided and announcements from that destination must still pass through. The network can still see the destination appear through announcements but cannot connect to it. Obviously in a real life situation people would notice if a lot of destinations got blocked and would start blocking nodes they didn't fully trust but we'll ignore this.
What I find interesting about Ken's preposal, which nobody has given a name for so I'm going to suggest the name Proofback, is that the network would still find a valid path in this situation. Nodes that are one or two hops away from the banned destination would at first realise that all the packets going to the destination are not be coming back and assume that it's just offline, but a smart implementation would eventually discover that the packet proof does come back when sending the civilian infrastructure by randomly using new interfaces when no proofs are coming back and thus would update its pathing table and route correcrly avoiding the state nodes, then any instances a hop away from that transport node may undergo the same process and repeat until everyone has alternative path for that destination.
Now this does require that the destination be popular enough to have clients who are only 1, 2 or maybe 3 hops away to start the process, if a client and destination are 6 hops apart and a large part of that network has that destination blocked, then there's just too many unknowns and a connection is unlikely to happen, though assuming those transport nodes follow the same protocol of randomly changing which interface they use, by pure chance a valid path with no state run nodes might be found and as soon it is then all those instances will know where to point traffic to get to that destination, so even as soon as one person accidently finds a path then every client after will find it easier as if they connect to a node that knows the path then they'll connect. Fascinating that this protocol seems to resistent to even that as much as reasonably possible without having to floodfill or use the network too much.
So I think, the problem with this neighbor responsiveness check by probing has some major limitations:
- Since it uses a probing destination, this is somehow limited to reliably track other transport nodes with activated probing destination (which I think is disabled in the Python reference implementation by default).
- Probing will produce some additional Lora traffic overhead.
A benefit of this solution is of course the reliability and the ram efficiency.
Did you also thought about a completely passive method that tracks responses of neighbor nodes to recently sent packets? Example:
A sends a regular SINGLE destination packet to C. The discovered path is A - B - C.
A transmits a packet, B receives it and repeats the packet. A receives the repeated packet and therefore knows: B is a responsive neighbor. This would of course mean to cache transmitted packets and assign them a transport node that is supposed to answer. Of course received proof packets and announces could also be used passively to track neighbor responsiveness. That‘s what I already supposed somewhere up there 👆🏻 what do you think about this?
Second, I think using the same path state table for tracking direct neighbor responsiveness as for the end-to-end path is not the best option. Imagine a scenario where many discovered paths have the same transport node as next hop. Identifying this neighbor node as unresponsive would allow to disable multiple paths at once without having to iterate through the full path table. Same for setting the neighbor as responsive.
Using a separate neighbor_responsiveness table would also allow to distinct where the connection issue lies: Is is (a) my direct neighbor that is unreachable, or (b) is the problem somewhere down the path. (a) is potentially fixable for a transport node by allowing other paths but (b) is not, since the problem lies somewhere else.
What do you guys think about that?
Cheers!
nilu96 wrote:
Did you also thought about a completely passive method that tracks responses of neighbor nodes to recently sent packets? Example:
Asends a regular SINGLE destination packet toC. The discovered path isA-B-C.
Atransmits a packet,Breceives it and repeats the packet.Areceives the repeated packet and therefore knows:Bis a responsive neighbor. This would of course mean to cache transmitted packets and assign them a transport node that is supposed to answer. Of course received proof packets and announces could also be used passively to track neighbor responsiveness. That‘s what I already supposed somewhere up there 👆🏻 what do you think about this?
I really like this passive approach. It also has some edge cases we need to consider though.
First, it won't detect an asymmetry with the destination node (where C can't hear B in your example) because that node will never repeat. Active neighbor probing would detect this (assuming the destination node has probes enabled) and could potentially stop responding to path requests for C (a slow convergence).
Second, it won't work at all on non-broadcast transports of course, but I think that's ok considering those don't typically suffer from asymmetry anyway.
All in all I think it has great potential as another tool to detect asymmetry. I'm going to work on an experimental implementation of this in microReticulum and we'll see how it performs irl.
Second, I think using the same path state table for tracking direct neighbor responsiveness as for the end-to-end path is not the best option. Imagine a scenario where many discovered paths have the same transport node as next hop. Identifying this neighbor node as unresponsive would allow to disable multiple paths at once without having to iterate through the full path table. Same for setting the neighbor as responsive.
Using a separateneighbor_responsivenesstable would also allow to distinct where the connection issue lies: Is is (a) my direct neighbor that is unreachable, or (b) is the problem somewhere down the path. (a) is potentially fixable for a transport node by allowing other paths but (b) is not, since the problem lies somewhere else.
Not sure I follow this exactly so could use some clarification. The neighbor responsiveness is already tracked in a separate table in microReticulum,_neighbor_stats.
It's true however that when a neighbor is deemed to be unresponsive, it does trigger a potentially expensive walk of the full path table.
Are you proposing to track neighbor responsiveness along with destination in a separate table, or something else?