Background
rc, the resend counter, will terminate a connection if exceeded. It is normally reset on received RR response when everything is fully acked (C4.5b in 2017 spec, state diagram for Timer Recovery).
The intention of rc, as I understand it, is to break the connection if the other side is unable to confirm that they heard us.
This is usually done by sending an RR command, and waiting for the response.
The problem
If either the three RR commands heading out, or their responses, are lost, then the connection is cut.
But we are not actually interested in the RR directly. We want to know "are you hearing me, and am I hearing you?".
My suggestion
set rc to 0 whenever we update va.
When we update va it's because we received either an RR with a n nr, or an IFRAME with an nr higher than the last one we got.
Our outgoing IFRAME took the place of the outgoing RR Command, and the packet triggering va update takes the place of the RR Response.
Semantically we have established the same fact: they can hear us, and we can hear them.
I've added an experiment to my implemenation, with results below.
Before
I set up a test that drop packets randomly (well, by a specific random seed so that it's replayable). I also capture both sides of the connection into pcaps.
This test is with a very aggressive 75% packet loss.
Note that even though the client in packet 24 gets an IFRAME with a NR=2 (which, ehem, we already get in the incoming RR Commands, but that's issue #8), it cuts the connection right after.
$ tshark -r target/async_tcp_example_captures/lossy-data/rax25-async-captures-lossy-data-2343790-1778770388883562915/client.pcap
1 0.000000 M0TST-1 → M0TST-2 AX.25 15 U P, func=SABM
2 0.003040 M0TST-2 → M0TST-1 AX.25 15 U F, func=UA
3 0.003734 M0TST-2 → M0TST-1 AX.25-NoL3 39 Text
4 0.003867 M0TST-1 → M0TST-2 AX.25-NoL3 21 Text
5 0.106052 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=1
6 0.307623 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=1
7 0.316086 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=1
8 0.316318 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=1
9 0.330541 M0TST-2 → M0TST-1 AX.25-NoL3 28 Text
10 0.331235 M0TST-1 → M0TST-2 AX.25-NoL3 22 Text
11 0.432554 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
12 0.634387 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
13 0.836089 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
14 0.850344 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
15 0.850608 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
16 1.038495 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
17 1.239945 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
18 1.269953 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
19 1.270211 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
20 1.440963 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
21 1.642432 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
22 1.688341 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
23 1.688612 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
24 1.701733 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
25 1.702416 M0TST-1 → M0TST-2 AX.25-NoL3 24 Text
26 1.803975 M0TST-1 → M0TST-2 AX.25 15 U, func=DM
$ tshark -r target/async_tcp_example_captures/lossy-data/rax25-async-captures-lossy-data-2343790-1778770388883562915/server.pcap
1 0.000000 M0TST-1 → M0TST-2 AX.25 15 U P, func=SABM
2 0.000053 M0TST-2 → M0TST-1 AX.25 15 U F, func=UA
3 0.000084 M0TST-2 → M0TST-1 AX.25-NoL3 39 Text
4 0.003616 M0TST-1 → M0TST-2 AX.25-NoL3 21 Text
5 0.003664 M0TST-2 → M0TST-1 AX.25-NoL3 28 Text
6 0.104907 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=1
7 0.306328 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=1
8 0.322048 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=1
9 0.322314 M0TST-2 → M0TST-1 AX.25-NoL3 28 Text
10 0.335849 M0TST-1 → M0TST-2 AX.25-NoL3 22 Text
11 0.336085 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
12 0.437374 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
13 0.638722 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
14 0.840282 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
15 0.856493 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
16 0.856740 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
17 1.057522 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
18 1.259900 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
19 1.276182 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
20 1.276429 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
21 1.477858 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
22 1.679327 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
23 1.693697 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
24 1.693905 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
25 1.706951 M0TST-1 → M0TST-2 AX.25-NoL3 24 Text
26 1.707232 M0TST-2 → M0TST-1 AX.25-NoL3 31 Text
27 1.811249 M0TST-1 → M0TST-2 AX.25 15 U, func=DM
After
Notice how the received IFRAME in packet 24 on the client side now makes the connection stay alive, and successfully complete.
$ tshark -r target/async_tcp_example_captures/lossy-data/rax25-async-captures-lossy-data-2344949-1778770617002570576/client.pcap
1 0.000000 M0TST-1 → M0TST-2 AX.25 15 U P, func=SABM
2 0.003138 M0TST-2 → M0TST-1 AX.25 15 U F, func=UA
3 0.003815 M0TST-2 → M0TST-1 AX.25-NoL3 39 Text
4 0.004049 M0TST-1 → M0TST-2 AX.25-NoL3 21 Text
5 0.105564 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=1
6 0.306603 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=1
7 0.315614 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=1
8 0.315872 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=1
9 0.329554 M0TST-2 → M0TST-1 AX.25-NoL3 28 Text
10 0.330261 M0TST-1 → M0TST-2 AX.25-NoL3 22 Text
11 0.432420 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
12 0.633838 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
13 0.834596 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
14 0.843343 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
15 0.843443 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
16 1.036253 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
17 1.237229 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
18 1.250757 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
19 1.250817 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
20 1.439012 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
21 1.639826 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=2
22 1.656260 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
23 1.656311 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
24 1.659352 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
25 1.659596 M0TST-1 → M0TST-2 AX.25-NoL3 24 Text
26 1.760611 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=3
27 1.962347 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=3
28 2.164042 M0TST-1 → M0TST-2 AX.25 15 S P, func=RR, N(R)=3
29 2.174664 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=3
30 2.174941 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=3
31 2.190270 M0TST-2 → M0TST-1 AX.25-NoL3 31 Text
32 2.191088 M0TST-1 → M0TST-2 AX.25 15 U P, func=DISC
33 2.204244 M0TST-2 → M0TST-1 AX.25 15 U F, func=UA
$ tshark -r target/async_tcp_example_captures/lossy-data/rax25-async-captures-lossy-data-2344949-1778770617002570576/server.pcap
1 0.000000 M0TST-1 → M0TST-2 AX.25 15 U P, func=SABM
2 0.000064 M0TST-2 → M0TST-1 AX.25 15 U F, func=UA
3 0.000100 M0TST-2 → M0TST-1 AX.25-NoL3 39 Text
4 0.004078 M0TST-1 → M0TST-2 AX.25-NoL3 21 Text
5 0.004141 M0TST-2 → M0TST-1 AX.25-NoL3 28 Text
6 0.105679 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=1
7 0.306622 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=1
8 0.321564 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=1
9 0.321808 M0TST-2 → M0TST-1 AX.25-NoL3 28 Text
10 0.334729 M0TST-1 → M0TST-2 AX.25-NoL3 22 Text
11 0.334965 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
12 0.436276 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
13 0.637757 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
14 0.838892 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
15 0.844634 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
16 0.844737 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
17 1.046606 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
18 1.247438 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
19 1.250731 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
20 1.250786 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
21 1.452287 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
22 1.653105 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=2
23 1.656276 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=2
24 1.656326 M0TST-2 → M0TST-1 AX.25-NoL3 29 Text
25 1.659459 M0TST-1 → M0TST-2 AX.25-NoL3 24 Text
26 1.659514 M0TST-2 → M0TST-1 AX.25-NoL3 31 Text
27 1.761420 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=3
28 1.962816 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=3
29 2.164288 M0TST-2 → M0TST-1 AX.25 15 S P, func=RR, N(R)=3
30 2.181316 M0TST-1 → M0TST-2 AX.25 15 S F, func=RR, N(R)=3
31 2.181579 M0TST-2 → M0TST-1 AX.25-NoL3 31 Text
32 2.196047 M0TST-1 → M0TST-2 AX.25 15 U P, func=DISC
33 2.196285 M0TST-2 → M0TST-1 AX.25 15 U F, func=UA
Background
rc, the resend counter, will terminate a connection if exceeded. It is normally reset on received RR response when everything is fully acked (C4.5b in 2017 spec, state diagram for Timer Recovery).The intention of
rc, as I understand it, is to break the connection if the other side is unable to confirm that they heard us.This is usually done by sending an RR command, and waiting for the response.
The problem
If either the three RR commands heading out, or their responses, are lost, then the connection is cut.
But we are not actually interested in the RR directly. We want to know "are you hearing me, and am I hearing you?".
My suggestion
set
rcto 0 whenever we updateva.When we update
vait's because we received either an RR with a nnr, or an IFRAME with annrhigher than the last one we got.Our outgoing IFRAME took the place of the outgoing RR Command, and the packet triggering
vaupdate takes the place of the RR Response.Semantically we have established the same fact: they can hear us, and we can hear them.
I've added an experiment to my implemenation, with results below.
Before
I set up a test that drop packets randomly (well, by a specific random seed so that it's replayable). I also capture both sides of the connection into pcaps.
This test is with a very aggressive 75% packet loss.
Note that even though the client in packet 24 gets an IFRAME with a
NR=2(which, ehem, we already get in the incoming RR Commands, but that's issue #8), it cuts the connection right after.After
Notice how the received IFRAME in packet 24 on the client side now makes the connection stay alive, and successfully complete.