While I have built a file transfer protocol for AX.25, I also wanted to try old BBS era protocols. They’re more tested and standardized.
The easiest way is probably to take lrzsz and let it talk
over AX.25 connected sockets. Ideally
socat should be enough, but it
seems that it does not support AX.25.
That’s actually fine, because ideally I want to run on my authenticated wrapped AX.25 (while encryption, obscuring the meaning, is banned, signatures are not).
So I had to make an adapter that bridges stdin/stdout to AX.25. Simple enough.
The setup is two Kenwood TH-D74s, set up the same way as before, in 9600bps.
D74 -> rfcomm -> kissattach -> axpipe -> socat -> lrzsz
The D74 is a great radio. It has the best text entry, menu system, and APRS support of any handheld radio I’ve seen, and it also has a built-in TNC (“modem”) that works both in 1200bps and 9600bps.
First, just for fun, let’s try YModem.
socat EXEC:'sz --ymodem axpipe.cc' EXEC:'./axpipe -r radio1 -l 0' socat EXEC:'rz --ymodem -t 100' EXEC:'./axpipe -r radio6 -s M6VMB-12 -c M0THC-1'
On ARM and RISC-V I have to specify a packet length (
-l) of 0, to
AX25_PACLEN at all. Otherwise it tries to set it to a
reasonable number, which doesn’t work on ARM and RISC-V because of
some kernel bug.
Speed: best attempt 720 bps when transferring ~3kB, counting all the overhead such as handshakes.
I got some timeouts, so I thought I could fix that by adding
turn off all timeout handling. But the YModem implementation doesn’t
actually work without timeouts, because then the receiver never
sends a single “ready to receive” message.
This seems like a bug in lrzsz. Surely it should send the first probe?
The king of transfer protocols in the BBS era was ZModem.
The fastest I could make this go for a 3112 byte file was 2095 bps using:
# Sender socat EXEC:'sz -b axpipe.cc' EXEC:'./axpipe -r radio1 -e -w 63 --t1=2 --t2=2' # Receiver socat EXEC:'rz -b -t 1000' EXEC:'./axpipe -r radio6 -c M0THC-1 -e -w 63 --t1=2 --t2=2'
Ok, so 2095bps is not so much “king” for this use case. It’s a bit too chatty. Every “over” and ACK spends precious bps.
I don’t know that this is optimally tuned, but it’s as far as I’m going with the kernel implementation today, since the kernel is prone to panic if you use AX.25 sockets.
-w options only occasionally work on RISC-V. There
appears to be some sort of race condition in the kernel. I’ve never
seen it work on ARM, but it works approximately one in five times on
Clearly I should be using my own AX.25 implementation in user space, since the kernel one is so bad. But for this blog post I wanted a more well known implementation.
ZModem on ax25ms (my AX.25 implementation)
I’ve not even implemented extended sequence numbers and still with the same settings I get 2294bps on those same 3684 bytes. For a 100kB file I managed 4878bps. The limiting factor is now the extended sequence numbers, causing the ACK windows to only be just over one second long before it needs to leave room for an ACK.
export LD_PRELOAD="$HOME/opt/ax25ms/lib/libpreload.so" export PATH="$PATH:$HOME/opt/axsh/bin" exec socat \ EXEC:"sz -O --start-8k -b -O -l 8000 rand.bin" \ EXEC:"axpipe -C 220 -s M0THC-1"
export LD_PRELOAD="$HOME/opt/ax25ms/lib/libpreload.so" export PATH="$PATH:$HOME/opt/axsh/bin" exec socat \ EXEC:"rz -y -b -O" \ EXEC:"axpipe -s M6VMB-1 -c M0THC-1"
The Linux AX.25 implementation is completely broken
The more I use the Linux kernel AX.25 implementation, the more I think it should just be destroyed. It’s mostly behaved well on x86_64, but today I got kernel panics pretty consistently for some tests.
I’m sure there are exploitable problems, but I think they’re only exposed if root has configured an AX.25 interface. So basically “don’t do that” on a production machine.
I’m not saying my implementation is perfect, but I would say that it’s better than the kernel even in this early stage.
Fun quote from the section 4
ax25 manpage: `BUGS: Too numerous to
list in full currently’.
Yeah… I believe that.
The D74 firmware is not much better
When testing speeds using my own AX.25 stack I tried increasing the packet size. At 800 byte packets the D74 corrupted enough packets that the AX.25 state machine never recovered into the good state. It kept making progress, but never reset the error counter.
Maybe not getting out of the recovery state is a bug in the AX.25 specs (I double checked and in this aspect I do follow the specs). The specs seem to say that the error counter should increase until there is a time when all sent packets have been ACKed. After the error counter reaches a fixed value (like 11), it cuts the connection.
Maybe all new incoming ACKs should reset the error counter?
But in any case corrupted packets is wasted airtime, so the right solution is to not have them.
At 400 byte packets it handled it better, except the D74 on the sending side kept crashing after a few minutes. At 220 byte (payload) packets it seems stable.
I could switch to Direwolf as the modem, instead of the D74’s built in TNC. This way I would be able to step through all the code end to end. But then I’d have to settle for 1200 bps, which obviously is slower. 9600bps can generally not be sent to a radio as audio. Hence why I want to keep using the D74 TNC.
Ham radio file transfer
It’s becoming more clear to me that amateur radio needs a modern file transfer program. I’ve not yet found one that does this one thing, and does it well.
There are plenty of tools that let you send emails and attach files and such (e.g. WinLink), but I’d like a file transfer primitive.
Probably the best thing would be “the bittorrent of ham radio”. But it would look very different, since it would need to take advantage of the effective “multicast” of radio.
This also means UI frames (AX.25’s version of UDP), which means it won’t need the buggiest and trickiest parts of the AX.25 stack. But on the flip side it’ll need to reinvent them.
Obviously such a protocol would not be usable for pirated material, both because of the low speed and because everyone uses registered callsigns.