KZ2X Packet Node Description

May 7, 2024

This is an updated description of the KZ2X packet node and its setup.

Overview

The packet node hosts two RF interface on 2m VHF, one for general AX.25 and one for APRS. Listeners in the Boston metro area may hear KZ2X-1 on 145.090 MHz or KZ2X-5 on 144.390 MHz.

We also support two AXUDP interfaces (AX.25 encapsulated over IP/UDP): one for general connectivity with other Internet connected packet stations, and one that acts as a “loopback” for testing. Contact KZ2X if you would like to peer over AXUDP.

The packet station is a single computer running Linux, using the in-kernel AX.25 and NET/ROM implementations. Our NET/ROM address is KZPAD:KZ2X-3.

Hardware and Systems Software

A single machine handles AX.25:

  • Host vhf1.kz2x.ampr.org (44.44.48.11). This is on my AMPRNet allocation, connected to the Internet through an IP-IP tunnel to the AMPRNet gateway at UCSD.
  • Udoo Bolt v8 SBC (AMD Ryzen x86_64, 16GiB RAM, 1TB NVMe SSD)
  • Arch Linux
  • uname -a: Linux vhf1.kz2x.ampr.org 6.8.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 02 May 2024 17:49:46 +0000 x86_64 GNU/Linux
  • Hamlib built from source
  • Running Direwolf built from dev branch source
  • ax25-tools “backported” from Debian and compiled from source (ChangLog v0.0.10)
  • ax25-apps similarly “backported” from Debian and compiled from source (ChangeLog v0.0.8)
  • Two Tigertronics SignaLink USB “sound cards”.
  • Dedicated Icom IC-2100H for AX.25 on 145.090 MHz
  • Dedicated Alinco DR-135T for APRS on 144.390 MHz
  • 75’ of LMR-400 and an RF choke at the end
  • Homebrew j-pole soldered from copper plumbing pipe for 145.090.
  • Homebrew ladder-line j-pole for APRS.

Software built from source is installed into the /opt/local directory tree. Since this includes shared libraries, we ensure those libraries will be found by the runtime linker by adding creating /etc/ld.so.conf.d/opt containing the line /opt/local/lib.

We load any needed kernel modules by creating configuration files under /etc/modules-load.d. Presently, we only need to add netrom.conf containing the line netrom to load the NET/ROM module at boot.

AX.25 Port Configuration

As mentioned above, we define four AX.25 ports: two for the RF interfaces on 2m, and two AXUDP interfaces. We do this by adding entries in the axports file:

#
# The format of this file is:
#
# name callsign speed paclen window description
#
vhf0    KZ2X-0          9600    255     2       145.090 MHz (1200 bps)
vhf1    KZ2X-5          9600    255     2       144.390 MHz (1200 bps) APRS
udp0    KZ2X-2          115200  255     2       AXUDP0
udp1    KZ2X-15         115200  255     2       AXUDP1

A few notes about this file: the assigned SSIDs given to each port must be globally unique. The line rate is for the port itself, not the transmission media. We use the maximum MTU and a window size of two. The last field is just descriptive text.

See the Linux man page axports(5) for more details about the format of the file.

Configuring and Starting Direwolf

We run two instances of Direwolf, one for each radio. Here are the direwolf.conf files, with comments and blank lines stripped out, and the IGLOGIN pass code redacted:

: vhf1; grep -v '^#' direwolf-vhf0.conf | noblanks
ADEVICE plughw:CODEC,0
ACHANNELS 1
CHANNEL 0
MYCALL KZ2X
MODEM 1200
AGWPORT 8000
KISSPORT 8001
FIX_BITS 1
: vhf1; grep -v '^#' direwolf-vhf1.conf | noblanks
ADEVICE plughw:CODEC_1,0
ACHANNELS 1
CHANNEL 0
MYCALL KZ2X-5
MODEM 1200
AGWPORT 8010
KISSPORT 8011
FIX_BITS 1
IGSERVER noam.aprs2.net
IGLOGIN KZ2X-5 1234
PBEACON sendto=IG delay=1 every=10 overlay=S symbol="igate" overlay=R zone=19T easting=326247 northing=4692237 power=50 height=40 gain=2 comment="Cambridge MA USA" via=WIDE1-1,WIDE2-1
PBEACON sendto=IG delay=1 every=10 overlay=S symbol="igate" overlay=T zone=19T easting=326247 northing=4692237 power=50 height=40 gain=2 comment="Cambridge MA USA" via=WIDE1-1,WIDE2-1
IGTXVIA 0 WIDE1-1
FILTER IG 0 t/m
IGTXLIMIT 6 10
: vhf1;

Direwolf and the AX.25 software components are started by the systemd service manager. Since we have multiple AX.25 interfaces, we use systemd’s support for multiple service instances to manage each independently. Instance names are the corresponding AX.25 port names (vhf0, vhf1, etc).

Before we start direwolf, we set the audio levels on the audio devices using the ALSA mixer. The service files for both are as follows:

[Unit]
Description=mixer levels for the TNC audio device
Wants=dev-snd-controlC1.device dev-snd-controlC3.device network-online.target cleanup-old-state.service
After=dev-snd-controlC1.device dev-snd-controlC3.device network-online.target cleanup-old-state.service

[Service]
Type=oneshot
ExecStart=/opt/local/etc/rc.d/tnc-audio

[Install]
WantedBy=multi-user.target

/opt/local/etc/rc.d/tnc-audio is a small shell script:

#!/bin/sh
amixer -c CODEC set PCM 95%
amixer -c CODEC_1 set PCM 95%

We use service dependencies to ensure that this happens before starting direwolf. We also use dependencies to make sure that the network and the SignaLink USB devices are connected and online.

Note we also invoke a service to clean old state:

[Unit]
Description=cleanup old system state

[Service]
Type=oneshot
ExecStart=/opt/local/etc/rc.d/cleanup-old-state

[Install]
WantedBy=multi-user.target

The script this invokes just removes old files that may have been left behind after a system crash and is omitted.

The direwolf service file:

[Unit]
Description=Direwolf software TNC (%i)
After=tnc-audio.service
Wants=tnc-audio.service

[Service]
User=direwolf
Group=direwolf
Type=exec
PrivateTmp=true
ExecStartPre=/opt/local/etc/rc.d/pre-direwolf %i
ExecStart=/opt/local/etc/rc.d/direwolf %i
ExecStartPost=/opt/local/etc/rc.d/post-direwolf %i

[Install]
WantedBy=multi-user.target

Direwolf allocates a POSIX PTY pair that it uses to synthesize a KISS TNC. Since PTYs are dynamically allocated by the system, and thus don’t have predictable file files, direwolf helpfully creates a symbolic link pointing to the PTY file client software should connect to: /tmp/kisstnc. We use systemd’s support for private /tmp directories to ensure that links associated with different direwolf instances don’t conflict. The /opt/local/etc/rc.d/pre-direwolf shell script simply deletes that link if it exists before direwolf starts:

#!/bin/sh
rm -f /tmp/kisstnc /opt/local/var/direwolf/kisstnc.$1

Note that we delete /opt/local/var/direwolf/kisstnc.$1. So that we can access these from the AX.25 utilities, we copy the symlinks that Direwolf creates to a path tied to the instance name in a globally visible directory in the /opt/local/etc/rc.d/post-direwolf script:

#!/bin/sh
/opt/local/bin/wait-symlink /tmp/kisstnc
cp -P /tmp/kisstnc /opt/local/var/direwolf/kisstnc.$1

The /opt/local/bin/wait-symlink script simply waits for Direwolf to create the /tmp/kisstnc symbolic link, avoiding a race condition where we try to copy the symlink after Direwolf starts, but before it has created the link.

#!/bin/sh
while ! test -L "$@"
do
	sleep 1
done

And finally, /opt/local/etc/rc.d/direwolf invokes direwolf itself, taking the instance name as an argument:

#!/bin/sh

PATH=/opt/local/bin:$PATH
export PATH

config=/opt/local/etc/direwolf-$1.conf

if [ "x$1" = "xvhf0" ]; then
	direwolf -X 1 -q d -t 0 -p -c $config
else
	direwolf -t 0 -p -c $config
fi

Some notes on the invocation. Instance vhf0 is our general packet interface, and is treated specially:

  • The -X 1 option enables the FX.25 extension to AX.25, which embeds forward-error correction codes into transmitted frames.
  • The -q d option suppresses printing APRS debugging information, as we’re not using APRS in this configuration.
  • The -t 0 option disables text colors. As we start direwolf under systemd, its output is put into the systemd log. The terminal escape sequences that would be insert colors into its output make no sense in that environment.
  • The -p option enables the KISS interface pseudo-TTY.
  • The -c /opt/local/etc/direwolf.conf option gives the path to the direwolf configuration file.

For the APRS instance, we omit the -X 1 and -q d options.

Finally, we use kissattach to attach the KISS TNC pseudo-device provided by Direwolf. Again, we use a multi-instance systemd service for this:

[Unit]
Description=KISS AX.25 interface for Direwolf software TNC (%i)
Requires=direwolf@%i.service
After=direwolf@%i.service

[Service]
Type=forking
ExecStartPre=/opt/local/etc/rc.d/pre-kiss-rf %i
ExecStart=/opt/local/etc/rc.d/kiss-rf %i
ExecStartPost=/opt/local/etc/rc.d/post-kiss-rf %i

[Install]
WantedBy=multi-user.target

The /opt/local/etc/rc.d/pre-kiss-rf script simply waits for the instance-specific pseudo-TTY symlink to be created. Since the kiss-rf service won’t be run until after the direwolf service starts executing, and the “pre” job for direwolf removes any stale symlinks before it invokes direwolf itself, this prevents kissattach from running until the instance symlink exists and points to the currently running direwolf.

Here is the kiss-rf startup script:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

kissattach -l /opt/local/var/direwolf/kisstnc.$1 $1

The “post” script /opt/local/etc/rc.d/post-kiss-rf sets parameters on the bound AX.25 interface:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

kissparms -c 1 -p "$@"

The -c 1 sets the port CRC type to “none” and -p "$@" specifies the instance name, which is also the RF port.

AXUDP Interfaces

The two AXUDP interfaces for internet connectivity and the testing loopback are started similarly, except that instead of direwolf, we run the ax25ipd program implement AX.25 over TCP/IP. We also employ a trick due to Marius Petrescu, YO2LOJ, and use the socat program to create symbolic links giving the PTY pairs used by ax25ipd and kissattach stable names.

The services and scripts to create pty pairs for the udp0 and udp1 ports are as follows:

/etc/systemd/service/socat-ax25ipd@.service:

[Unit]
Description=persistently named PTYs for AX.25 interface %i

[Service]
Type=exec
ExecStartPre=/opt/local/etc/rc.d/pre-socat-ax25ipd %i
ExecStart=/opt/local/etc/rc.d/socat-ax25ipd %i

[Install]
WantedBy=multi-user.target

/opt/local/etc/rc.d/pre-socat-ax25ipd:

#!/bin/sh
optvar=/opt/local/var
rm -f $optvar/ax25/pty-ax25ipd-$1 $optvar/ax25/kiss-ax25ipd-$1

/opt/local/etc/rc.d/socat-ax25ipd:

#!/bin/sh
optvar=/opt/local/var
socat pty,link=$optvar/ax25/pty-ax25ipd-$1,raw,echo=0 pty,link=$optvar/ax25/kiss-ax25ipd-$1,raw,echo=0

Now, we start ax25ipd. The configuration file for udp0 sets up routes and configuration options. With comments and blank lines stripped:

socket udp 10093
mode tnc
device /dev/ptmx
speed 115200
loglevel 3
broadcast QST-0 NODES-0
route kz2x-15 localhost udp 10094 b
route kz2x-14 pi1.kz2x.ampr.org udp 10093 b
route hb8nod hb1bbs.net udp 93 b
route hb8nos hb1bbs.net udp 93 b
route ka5d ka5d.ampr.org udp 10093 b
route kw1u kw1u.net udp 10093 b
route ky2d-1 ky2d.com udp 10093 b
route ky2d-15 vhfbbs.ky2d.ampr.org udp 10093 b
route n3hym n3hym.ddns.net udp 10093 b
route pe1rrr-7 static.ehvairport.com udp 10093 b
route pe1rrr-5 rrrnet.ehvairport.com udp 10093 b
route w1jt 76.24.175.218 udp 10093 b

Most of this is self-explanatory; if unsure, consult the Linux manpage for ax25ipd.conf(5).

Note the use of the device /dev/ptmx option. Since we use socat to create the PTY pair and specify this as a command line parameter when we invoke ax25ipd, this is superfluous and could probably be removed.

The loopback AXUDP interface configuration is similar:

/opt/local/etc/ax25/ax25ipd-loopback.conf:

socket udp 10094
mode tnc
device /dev/ptmx
speed 115200
loglevel 2
route kz2x localhost udp 10093 b

Note two things: first, we use UDP port 10094, and we configure a single route to localhost port 10093, which is the AXUDP port associated with the udp0 interface. Second, in the ax25ipd.conf file for udp0, we configure a route to KZ2X-15 (the SSID associated with port udp1) to localhost port 10094, with this instance of ax25ipd is bound on. This way, we can “call” ourselves over AXUDP by calling one of our SSIDs via port udp1; e.g., axcall udp1 kz2x-1. This is useful for testing.

The service file and script:

[Unit]
Description=KISS AX.25 interface for ax25ipd port %i
Wants=kiss-rf@vhf0.service kiss-rf@vhf1.service socat-ax25ipd@%i.service network-online.target
After=kiss-rf@vhf0.service kiss-rf@vhf1.service socat-ax25ipd@%i.service network-online.target

[Service]
Type=forking
ExecStartPre=/opt/local/etc/rc.d/pre-ax25ipd %i
ExecStart=/opt/local/etc/rc.d/ax25ipd %i

[Install]
WantedBy=multi-user.target

The pre-ax25ipd script simple waits for the link to the PTY device created by socat. We also wait until after RF services are running to start ax25ipd; this is not strictly necessary.

The way that we invoke ax25ipd is slightly different depending on the instance, but we handle this in the ax25ipd script:

/opt/local/etc/rc.d/ax25ipd:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

optvar=/opt/local/var
config=/opt/local/etc/ax25/ax25ipd.conf

if [ "x$1" = "xudp1" ]; then
	config=/opt/local/etc/ax25/ax25ipd-loopback.conf
fi

ax25ipd -d $optvar/ax25/pty-ax25ipd-$1 -c $config

Next, we attach virtual KISS TNCs synthesized by the ax25ipd instances to AX.25 interfaces by a another service:

[Unit]
Description=KISS AX.25 interface for ax25ipd port %i
Wants=ax25ipd@%i.service
After=ax25ipd@%i.service

[Service]
Type=forking
ExecStartPre=/opt/local/bin/wait-symlink /opt/local/var/ax25/kiss-ax25ipd-%i
ExecStart=/opt/local/etc/rc.d/kiss-ax25ipd %i

[Install]
WantedBy=multi-user.target

The kiss-ax25ipd script is very simple:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

optvar=/opt/local/var

kissattach -l $optvar/ax25/kiss-ax25ipd-$1 $1

A Note on AXUDP versus AXIP

AX.25 traffic is most commonly tunneled over IP using two protocols: AXIP, which defines an IP protocol type for tunneled AX.25 traffic, and AXUDP, which embeds AX.25 frames in UDP packets.

Of the two, there is little reason to prefer AXIP. While AXUDP does add a small amount of overhead in the form of a UDP header, this is negligible (8 octets) and AXUDP has the significant advantages of port addressibility and compatibility with network software that works with UDP, but not AXIP. Furthermore, the two have the same characteristics with respect to reliability and delivery guarantees.

AXIP has sigificant issues with respect to tranversing firewalls and the like and is just as unreliable as AXUDP (AXIP frames, after all, are just IP datagrams, which are connectionless and unreliable, by design).

AXIP should probably be retired and phased out in favor of AXUDP.

NET/ROM Configuration and Startup

Once the AX.25 interfaces are up, we start NET/ROM. Linux NET/ROM is configured using two files, nrports and nrbroadcast.

/opt/local/etc/ax25/nrports:

#
# The format of this file is:
#
# name callsign alias paclen description
#
netrom  KZ2X-3          KZPAD   235     KZ2X Amateur Radio Computing Resource Complex

This is similar to the axports file we saw earlier. The first field is the port name. We only have one netrom port, so we just use the name netrom. The second is the SSID associated with the port: this must be unique with respect to all SSIDs in use on the system; one cannot reuse an SSID from AX.25, for example. The third field is an alias for the system: KZPAD alludes to an X.25 Packet Assembly/Disassembly device, but I refer to it as a Public Access Dialthrough; this usage is not accurate. Note that the MTU is somewhat smaller than that for AX.25: the NET/ROM protocol introduces some overhead that we must account for, hence the smaller packet size. For more details on the file’s format, see nrports(5).

Our nrbroadcat file is:

#
# The format of this file is:
#
# ax25_name min_obs def_qual worst_qual verbose
#
vhf0    1       200     100     0
udp0    1       200     100     1

See the nrbroadcast(5) man page for details; note only that we clear the “verbose” flag on the RF port, so we do not broadcast downstream NET/ROM routes other than our own on that interface.

We invoke a service to attach the netrom port to a NET/ROM network interface:

[Unit]
Description=NET/ROM interface attach

[Service]
Type=exec
ExecStart=/opt/local/etc/rc.d/nrattach

[Install]
WantedBy=multi-user.target

/opt/local/etc/rc.d/nrattach:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

nrattach netrom

Finally, we invoke the netromd daemon:

[Unit]
Description=NET/ROM service daemon
Wants=kiss-rf@vhf0.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service nrattach.service
After=kiss-rf@vhf0.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service nrattach.service

[Service]
Type=forking
ExecStart=/opt/local/etc/rc.d/netromd

[Install]
WantedBy=multi-user.target

Note that the service requires all of the AX.25 interfaces and the NET/ROM interface to be up before starting, but that we don’t wait for the APRS interface: NET/ROM is not relevant there.

/opt/local/etc/rc.d/netromd is simple:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

netromd -i -l

The -i option causes it to broadcast a route immediately on startup, and -l enables error logging.

Services

Once the interfaces are online, we run a few local services.

We run the mheardd daemon to keep track of what stations we hear on all interfaces. It’s service definition:

[Unit]
Description=mheard daemon
Wants=kiss-rf@vhf0.service kiss-rf@vhf1.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service netromd.service
After=kiss-rf@vhf0.service kiss-rf@vhf1.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service netromd.service

[Service]
Type=forking
ExecStart=/opt/local/etc/rc.d/mheardd

[Install]
WantedBy=multi-user.target

/opt/local/etc/rc.d/mheardd is trivial:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

mheardd

We also run the beacon program to send out a periodic announcement of our system on the RF interface:

[Unit]
Description=periodic beacon daemon
Wants=kiss-rf@vhf0.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service netromd.service
After=kiss-rf@vhf0.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service netromd.service

[Service]
Type=forking
ExecStart=/opt/local/etc/rc.d/beacon

[Install]
WantedBy=multi-user.target

/opt/local/etc/rc.d/beacon:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

beacon vhf0 -H -d 'BEACON BROCK' 'KZ2X-1 Unix and public access dial-through for Amateur Radio Timesharing. Cambridge MA USA'

Finally, we run the ax25d daemon to direct incoming connections to useful services. Here is the configuration file:

#
# ax25d Configuration File.
#
# AX.25 Ports begin with a '['.
#
[KZ2X-1 via vhf0]
NOCALL   * * * * * *  L
default  * * * * * *  - root    /opt/local/sbin/axtip axtip
#
[KZ2X-4 via vhf0]
NOCALL   * * * * * *  L
default  * * * * * *  - root    /opt/local/sbin/ttylinkd ttylinkd -f /opt/local/etc/ax25/ttylinkd.conf
#
[KZ2X-1 via udp0]
NOCALL   * * * * * *  L
default  * * * * * *  - root    /opt/local/sbin/axtip axtip
#
[KZ2X-4 via udp0]
NOCALL   * * * * * *  L
default  * * * * * *  - root    /opt/local/sbin/ttylinkd ttylinkd -f /opt/local/etc/ax25/ttylinkd.conf
#
# NET/ROM Ports begin with a '<'.
#
<netrom>
NOCALL  * * * * * *  L
default * * * * * *  -  root  /opt/local/sbin/axtip     axtip

The service definition:

[Unit]
Description=ax25 daemon
Wants=kiss-rf@vhf0.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service netromd.service
After=kiss-rf@vhf0.service kiss-ax25ipd@udp0.service kiss-ax25ipd@udp1.service netromd.service

[Service]
Type=forking
ExecStart=/opt/local/etc/rc.d/ax25d

[Install]
WantedBy=multi-user.target

/opt/local/etc/rc.d/ax25d:

#!/bin/sh

PATH=/opt/local/sbin:$PATH
export PATH

ax25d -c /opt/local/etc/ax25/ax25d.conf

Note that we configure service on AX.25 SSID KZ2X-1 on all ports pointing to the axtip program. This is a custom program that’s derived from Joerg Reuter (DL1BKE’s) axwrapper from the ax25-tools package. It uses the sockets interface to grab the calling user’s callsign and protocol (AX.25, NET/ROM, whatever) and invokes my “public access dial-through” program attached to a new pseudo-tty.

axpad implements a user interface that lets users connect to the machines I have running locally on my network (mostly via telnet) while handling things like buffering the output from the distant end before writing to the AX.25 or NET/ROM interface.

The ttylinkd program available at KZ2X-4 uses the BSD ntalk protocol to send a chat request to me on kz2x.ampr.org, the primary Unix machine on my AMPR subnetwork. However, ttylinkd is very buggy and this only works sometimes.

Interactive Use and Monitoring

With the configuration presented thus far, I can use the call program to connect make outbound AX.25 connections over both RF and AXUDP. For example, axcall vhf0 BROCK will connect me to the W1MV-7 digipeater in Brockton, MA via RF. Similarly, axcall udp0 ka5d-7 will connect me to the BPQ node at the University of Texas in Austin via AXUDP and axcall netrom GAMES will connect me to a European text game server via NET/ROM over AXUDP.

When logged in interactively, I often run listen to monitor AX.25 traffic and NET/ROM on VHF and the general use AXUDP interfaces. For example, to watch RF traffic in a tmux window:

: vhf1; sudo /opt/local/bin/listen -ar -tttt -p vhf0

The -a option shows outbound frames in addition to incoming data, -tttt presents a timestamp on each line, -r makes the output “readable” and -p vhf0 specifies the RF port.

: vhf1; is my shell prompt.

Problems, Observations and Debugging Tips

Putting this together was the product of a lot of Internet searching and reading the Linux AX.25 HOWTO document, along with lots of experimentation.

Something that helped greatly, particularly while debugging AXUDP, was being able to monitor traffic on my border router as it egressed my network and headed for the Internet. tcpdump is a critical tool in one’s debugging arsenal.

Sadly, the “new” style of Linux networking discarded the traditional complement of ifconfig, route, and netstat. The functionality of the former two was subsumed by the ip tool, while the latter is replaced by ss. While the new tools provide enhanced functionality relative to their venerable ancestors, ss in particular does not understand either AX.25 or NET/ROM, so to get status information about the network I had to install netstat. Caveat emptor.

AX.25 seems to work pretty reliably. I have no problems connecting to stations near my QTH.

Occasionally I have observed destructive behavior with NET/ROM connections: systems will infinitely loop sending data to my system, but that data is never acknowledged. Usually I have to restart the system to reset these connections, which is obviously not ideal. I have no idea what’s going on, but have some packet capture data if anyone would like it for debugging.

The use of a software TNC in lieu of a hardware TNC has been useful. Software modems can employ advanced signal processing techniques to recover data from noisy RF environments, while hardware modems usually cannot. For example, Direwolf runs multiple AFSK 1200 demodulators in parallel, returning frames decode successfully by any of them. It also applies heuristics based on knowledge of the structure of AX.25 to fix one and two bit decoding errors. Further, soft modems can evolve and advance on the same hardware, while hardware is constrained to how it was built. FX.25 introduces forward error correction in a backwards compatible way into AX.25 frames: TNCs built before the invention of FX.25 cannot take advantage of this, while software can be upgraded.

Conclusion

And this concludes our description of the KZ2X packet station. Putting this together has been an interesting and educational challenge; I do wish there was more use of packet generally to make the exercise truly useful.

A Modest Proposal

Jan 16, 2022

I have a modest proposal: I would like to see a HamWan-like network spanning the eastern seaboard, from (at least) Washington, DC into Nova Scotia.

Ok, maybe that’s not so modest.

Regardless, I envision hams running a high-speed microwave data network providing both IPv4 and IPv6, with multiple Internet peering points. This network would provide a true resource complex for radio amateurs, with hosts on the network providing a variety of services for research, experimentation, collaboration, and even emergency communications.

I imagine using this resource to interconnect AX.25 networks using this resource, providing AXUDP over the microwave network to interconnect low-speed packet networks.

With such infrastructure in place, we can begin to think about what the future might hold for amateur radio internetworking. Robert Quattlebaum, N6DRC, has designed a next generation link-layer protocol for amateur radio designed to directly support IP networking. Dubbed ARNGLL for “Amateur Radio Next-Generation Link-Layer”, this is roughly based on the 6LoWPAN standard for IPv6 connectivity for low-power devices. I have been working with him (slowly) in our mutual spare time to develop a reference implementation of the protocol and the associated ARNCE (Amateur Radio Numeric Callsign Encoding) specification, which describes how to convert callsigns into a numeric representation suitable for direct use as a station address.

An advanced future?

In the future, I envision a high-speed, long distance microwave amateur radio internetwork based on TCP/IP, peered with the Internet, with slow- and medium-speed devices connected with IP over ARNGLL and AX.25 tunneled over AXUDP.

The KZ2X Computing Resource Complex

Jan 16, 2022

So I have an AMPRNet allocation and a packet station. These are tremendous resources, but their existence immediately begs the question: what should I do with them?

In the post I made about AMPRNet, I mentioned the documentary film Computer Networks: The Heralds of Resource Sharing that goes into detail about the origins of the ARPNANET, the forerunner to today’s Internet. In that film, at about the 9:05 mark, is a quote from Frank Heart at Bolt, Beranek and Newman that I find fascinating:

As soon as the network began working, we really had a nationwide resource complex. And this resource complex was very attractive to many users who had no resource of their own to really contribute. These people wanted direct terminal access to the net even though they had no host to get that access through. So we designed a new kind of IMP; we called it a Terminal Interface Message Processor; we call it a TIP for short. And this machine really includes a very tiny mini-host. With this TIP, many different kinds of terminals can be directly connected to the TIP or can be dialed in through low-speed lines to the tip and can provide access to the entire nationwide resource pool to users at various kinds of terminals.

The critical, and intriguing thing here, is the realization that the ARPANET was designed to provide access to shared resources to its users. Later in the documentary, at roughly 15:06, Prof. Fernando J. Corbató, principle investigator of the MIT Multics project, goes on to say:

One of the things that is coming out as people begin to use the network more and more is that they must have, at the nodes of the network, computers which are of a Multics-like class, or essentially a major computer utility which is capable of manipulating information because the network itself doesn’t hold information, doesn’t keep information; an important design criteria, in fact.

That is, while the networks themselves provide access, it is important that they provide access to some kind of resource complex.

As hams, we’ve got networks: we have the AX.25 and NET/ROM packet network, and we have the AMPRNet. These are powerful means of communications, but what resources are available for use? What are we sharing on those networks? And how are we manipulating the information that traverses them?

As near as I can tell, most modern use of AX.25 is for NTS-style traffic handling. There’s certainly nothing wrong with that and it is undeniably useful, but what else? The AMPRNet seems mostly just not used.

We can do better.

Creating a Resource Complex.

I decided to approach this problem by creating my own complex of resources accessible to our networks. To start, I installed a modern computer running Unix on my AMPRNet segment. This machine is powerful: it has 16 Intel i7 threads, 64 GiB of RAM and a 1TB NVMe SSD. It runs DragonFly BSD, a derivative of 4.4BSD. I am happy to give accounts to other hams, and is accessible as kz2x.ampr.org.

I have also made this machine available for interactive use over AX.25 and NET/ROM. Connecting to the KZPAD:KZ2X-3 NET/ROM node or to KZ2X-1 on 145.090 MHz in the greater Boston area will allow hams to connect it, login, write and run programs, play games, send and receive local mail, access the Internet, etc.

This experiment was a tentative success: I have an RF-accessible networked timesharing system. But what else can one do?

Going back to Corbató’s comment about a major computing utility of a Multics-like class at the nodes of the network, I asked myself, “why not just provide access to Multics?”

And that’s what I’ve done. I set up an instance of Multics on an emulated Honeywell/Bull DPS8/M mainframe, and made it similarly accessible via AX.25 and TCP/IP. This worked surprisingly well, which then led to installation of a DECSYSTEM-20 running TOPS-20, a VAX 3900 running VAX/VMS, another VAX running 4.3BSD Unix, a PDP-11/93 running RSTS/E 10.1-L, a PDP-11/70 running 7th Edition Unix, a CDC Cyber 175 running NOS 2.8.7, and an IBM 4381 mainframe running VM/370. These machines are all virtualized on modern hardware, but are interconnected locally using both TCP/IP and DECnet, accessible from the Internet via AMPRNet, and accessible from AX.25 and NET/ROM via KZPAD:KZ2X-3.

Interestingly, since so many of these systems date from an era where computers were accessed via line-oriented terminal concentrators, using low-bandwidth serial lines and high-latency mechanical teletypewriters, they are very well suited for use in low-bandwidth, high-latency, high-loss RF environments like AX.25.

Here is an example of an interactive Multics session:

Multics MR12.7: KZ2X Computing Complex (Channel d.h001)
Load = 6.0 out of 90.0 units: users = 6, 01/16/22  1832.4 est Sun
l KZ2X
Password:
You are protected from preemption until 18:32.
KZ2X.Ham logged in 01/16/22  1832.5 est Sun from ASCII terminal "none".
Last login 01/13/22  0856.5 est Thu from ASCII terminal "none".
No mail.
r 18:32 0.766 54

ls -a

Segments = 6, Lengths = 5.

r w    1  skull.txt
r w    1  KZ2X.profile
r w    1  start_up.ec
r w    1  hello.pl1
       0  KZ2X.mbx
r w    1  KZ2X.value

Directories = 1.

sma  exec_com

r 18:32 0.037 0

who -lg

Multics MR12.7; KZ2X Computing Complex
Load = 7.0 out of 90.0 units; users = 7, 2 interactive, 5 daemons.
Absentee users = 0 background; Max background absentee users = 3
System up since 12/25/21  1512.1 est Sat
Last shutdown was at 12/25/21  1510.9 est Sat

    Login at      TTY  Load   User ID

12/28/21  21:41  none  1.0   Cross.SysEng
01/16/22  18:32  none  1.0   KZ2X.Ham

r 18:32 0.038 0

logout
KZ2X.Ham logged out 01/16/22  1833.0 est Sun
CPU usage 1 sec, memory usage 0.2 units, cost $0.07.
hangup
Multics has disconnected you

As far as I know, accessing Multics in this way over AX.25 is an historical first.

Similarly, here is TOPS-20 session in which I write a small Lisp program and run it interactively:

CONNECTING TO TOPS20
Trying 44.44.107.10...
Connected to tops20.kz2x.ampr.org.
Escape character is 'off'.

 KZ2X Twenex, PANDA TOPS-20 Monitor 7.1(21733)-4

--> Amateur Radio DECSYSTEM-20

This system is for authorized use only.  It is part of
the KZ2X amateur radio computing resource complex.  For
problems or questions, send email to root@kz2x.ampr.org.

@log KZ2X
 Job 9 on TTY44 vhf1.kz2x.ampr.org(TCP) 16-Jan-2022 18:58:18
  Last interactive login 16-Jan-2022 18:56:32
  Last non-interactive login Never
@dir

   TOPS20:<KZ2X>
 HELLO.CLISP.1
 LOGIN.CMD.1
 SKULL.TXT.1

 Total of 3 pages in 3 files
@type hello.clisp
(defun hello (who)
  (format t "Hello, ~A!~%" who))
@clisp

Uppsala Common Lisp, Version of 15-Dec-1987, (c) 1985, C. Hedrick
Please mail bugs and suggestions to BUG-CLISP@tops20.kz2x.ampr.org
Use (NEWS) to see what's new in this version.
CL>(defun hi (who)
     (format t "Hi, ~A~%" who))
HI
CL>(load "hello")
;Loading tops20.kz2x.ampr.org::TOPS20:<KZ2X>HELLO.CLISP.1 into package USER.
T
CL>(hello "Dan")
Hello, Dan!
NIL
CL>(hi "Amateur Radio")
Hi, Amateur Radio
NIL
CL>(pprint #'hi)

(LAMBDA (WHO)
  (BLOCK HI
    (FORMAT T "Hi, ~A~%" WHO)))
CL>(quit)
@sy
 Sun 16-Jan-2022 18:59:35  Up 99:59:13
 1+8 Jobs   Load av   0.01   0.01   0.01

 No operator in attendance

 System is remedial

 Job  Line Program  User              Origin
   9*   44  SYSTAT  KZ2X              vhf1.kz2x.ampr.org(TCP)

   1    13  OPR     OPERATOR
   2   DET  SYSJB1  OPERATOR
   3    14  RESOLV  OPERATOR
   4    15  NETSRV  OPERATOR
   5    16  SMTJFN  OPERATOR
   6    17  MMAILR  OPERATOR
   7    20  IMAPSV  OPERATOR
   8    21  FTS     OPERATOR
@logout
Killed Job 9, User KZ2X, TTY44 vhf1.kz2x.ampr.org, at 16-Jan-2022 18:59:40
 Used 0:00:00 in 0:01:21
Connection closed by foreign host.

HOST TOPS20 DISCONNECTED

As of today, the following hosts are available in the KZ2X Computing Resource Complex:

System Name     Description
UNIX x86_64 Unix Host
BSD VAX 3900 4.3BSD
V7 PDP-11/70 7th Edition Unix
RSTS PDP-11/93 RSTS/E
MULTICS DPS8/M Multics
VM IBM 4381 VM/CMS
VMS VAX 3900 VAX/VMS
TWENEX DECSYSTEM-20 TOPS-20 7.1 (Panda)
CYBER CDC 6600 series Cyber 175 NOS 2.8.7
 

[Note that the IBM mainframe isn’t directly accessible, as the 3270 protocol cannot be easily tunneled over AX.25. But it is accessible via AMPRNet.]

Conclusion

Using these systems in conjunction with amateur radio is not only fun, but it’s exciting. The things we can do with even antiquated systems with providing interesting, entertaining, and genuinely useful services to ourselves and served organizations is limited only by our imaginations and Part 97.

What other resources does it make to introduce into our amateur radio milieux?