Section 1

Why the gateway changed

Linux 7.1 drops the kernel AX.25 stack. Every program that does socket(AF_AX25, …) — ax25d, axspawn, call, DX cluster doors, mailboxes — needs somewhere else to go.

iw2ohx-gw used to carry a full Linux packet stack: kernel interfaces ax0/ax1/ax2, kissattach on pty pairs, two ax25ipd, kernel NET/ROM, flexnetd and URONode. Most of that duplicated what LinBPQ IW2OHX-13 (with our FlexNet extension) already does better: routing, NET/ROM, FlexNet, user commands. So the change came in two steps on the same day:

Step 1
URONode retired. LinBPQ IW2OHX-13 became the only node on the host. IW2OHX-7, IW2OHX-3 (flexnetd) and OHXGW:IW2OHX-1 (kernel NET/ROM) are gone. Their services are LinBPQ applications now: DX, CONV, IGATE, SHELL.
Step 2
Kernel AX.25 replaced by WAMPES. The few Linux programs that still need AX.25 — the DXSpider door, the shell login and a telnet gate — now reach it through WAMPES in user space, via the userspace build of libax25 (unix-ax25-stack, DL9SAU). The kernel modules are blocked; the host already runs as it will on 7.1.

Section 2 — the idea

A connector, not a node

WAMPES can be a complete node. Here it is deliberately not: it does no routing, carries no FlexNet and relays nothing between its ports. It only adapts.

LinBPQ IW2OHX-13 talks AXUDP to WAMPES IW2OHX-2, which hands AX.25 sessions to Linux programs through userspace libax25 The packet network RF 144.825 / 433.725 via IW2OHX-12 FlexNet / NET/ROM via IW2OHX-14 DLC7 telnet / SSH users 44Net, HAMNET, Internet LinBPQ IW2OHX-13 the node — all routing apps DX SHELL CONV IGATE BBS CHAT port 7 BPQAXIP UDP 10096, loopback WAMPES IW2OHX-2 connector — no routing iface bpq 127.0.0.1:10095 iface pcf UDP 94 <-> IW2OHX-12 listen IQ2LB-6 listen IW2OHX-11 (the permission list) IP 44.134.24.8 44.144.128.135 /tcp/sockets/ax25 Linux programs userspace libax25 ax25d IQ2LB-6 → DXSpider IW2OHX-11 → axspawn DXSpider DX cluster axspawn /bin/login ax25telnetd telnet :23 → AX.25 as the user AXUDP IW2OHX-12 reaches IQ2LB-6 directly over iface pcf (dashed line)

One AXUDP link on loopback joins the node and the connector; the service socket joins the connector and Linux

LinBPQ
Owns the network: RF, FlexNet, NET/ROM, telnet logins, every user command. Its port 7 is a plain BPQAXIP port pointing at 127.0.0.1.
WAMPES
Claims two callsigns with listen ax25 add … client: IQ2LB-6 (the cluster) and IW2OHX-11 (the shell). A connect to either is handed, as an open descriptor, to whichever program has bound that callsign over the service socket. Nothing else is answered or forwarded.
libax25
The userspace build intercepts socket, bind, connect, listen, accept. At bind() it sees that the port belongs to a WAMPES node (listed in wampes.conf) and moves the socket there. After connect() the program holds a plain byte stream — nothing of the library is left on the data path. ax25d and axspawn run unmodified.
Why not let WAMPES be the node?

Because the node already exists, and it is the one users know. Keeping all routing in LinBPQ means one routing table, one set of FlexNet links, one place to look when a path breaks. WAMPES adds exactly one capability that LinBPQ lacks — lending an AX.25 stack to ordinary Linux programs — and nothing that competes with it.

Section 3

One session, step by step

A user at the BPQBOL prompt types DX.

  1. LinBPQ runs the application DX = C 7 IQ2LB-6: an AX.25 SABM on port 7, from the user's own callsign to IQ2LB-6.
  2. BPQAXIP wraps the frame in UDP to 127.0.0.1:10095 (MAP entry for IQ2LB-6).
  3. WAMPES iface bpq receives it and learns the user's callsign → 127.0.0.1:10096 for the replies (axip-learn on).
  4. IQ2LB-6 has a client listener, and ax25d holds it: WAMPES accepts the link and hands the session over the service socket.
  5. ax25d matches [IQ2LB-6 VIA wampes] and starts the DXSpider client as user sysop, with the session on stdin/stdout and the caller's callsign as argument.
  6. DXSpider greets the user by their callsign — the identity survived every hop.

SHELL is the same path to IW2OHX-11, where ax25d starts axspawn → /bin/login. As at DB0FHN, the account is created on the first connect from the caller's callsign, and over AX.25 no password is ever asked — the callsign is the login. From that shell the user runs passwd if they also want the telnet gate (Section 6); a notice says so until a password is set.

Open registration on a gateway host — how it is contained

Anyone with a callsign can get a Unix shell here, so the shells are boxed in: the group ax25 may open no network connection at all (an iptables owner match on OUTPUT allows only the local DX cluster port — not the AGWPE port or the node's AXUDP port, which would let a shell transmit under any callsign), has no sudo and no cron, runs under process limits, and gets a private home. An empty password authenticates nowhere: the nullok option is gone from PAM and both SSH daemons refuse passwords. A shell over the Internet is not offered.

Section 4

How to connect

The node is IW2OHX-13, NET/ROM alias BPQBOL. URONode callsigns IW2OHX-7, IW2OHX-3 and OHXGW are retired.

FromHowNotes
RF VHF/UHF144.825 MHz AFSK 1200 Bd or 433.725 MHz G3RUH 9600 Bd → C IW2OHX-12, then C IW2OHX-13IW2OHX-12 BOLFLX is the RF access node
FlexNetC IW2OHX-13 · cluster: C IQ2LB-6through IW2OHX-14 (X)Net DLC7 BOLNET
NET/ROMC BPQBOLalso BOLNET:IW2OHX-14, BOLHF:IW2OHX-15
44Nettelnet iw2ohx.ampr.org 23 (gate) · telnet iw2ohx.ampr.org 7300 (DXSpider)gate: password set via SHELL first — Section 6
HAMNETtelnet pr.iw2ohx.hamnet.radio 23 (gate) · telnet dx.iw2ohx.hamnet.radio 7300same
InternetSSH gateway, key-only, by requestmenu: DXSpider / IW2OHX-13

At the BPQBOL prompt: DX cluster, CONV convers (DB0FHN-12 via IGATE), IGATE, CHAT, BBS, XNET, BPQHF, SHELL, FL FlexNet links, D destinations.

Section 5 — copy the reasoning, not the numbers

Sample configurations

The live files of iw2ohx-gw, trimmed. WAMPES is built with TCPDIR=/tcp, libax25 and the ax25 tools with prefix /opt/uax25.

WAMPES — /tcp/net.rc (AX.25 part)
ax25 mycall iw2ohx-2 ax25 digipeat 0 # never relay between the ifaces flexnet filter default in none advert no feed no # no FlexNet here axsock mode 0660 # who may use the service socket: axsock group hams # root + group hams (default is 0666!) # iface bpq: loopback AXUDP to LinBPQ IW2OHX-13 port 7 atta axip bpq udp 10095:10096 bind=127.0.0.1 ifc bpq encap ax25i mtu 1500 ifconfig bpq axip-learn on # users arrive with their OWN callsign axip rou add permanent iw2ohx-13:bpq 127.0.0.1 10096 axip route add default:bpq 127.0.0.1 10096 ax25 rou add permanent vj bpq iw2ohx-13 # iface pcf: the LinBPQ on the LAN (IW2OHX-12) reaches IQ2LB-6 directly atta axip pcf udp 94 bind=192.168.1.202 ifc pcf encap ax25i mtu 1500 ifconfig pcf axip-learn on # safe only if the firewall lets IW2OHX-12 alone in axip rou add permanent iw2ohx-12:pcf 192.168.1.201 94 ax25 rou add permanent vj pcf iw2ohx-12 # the callsigns libax25 programs may claim - this list IS the permission listen ax25 add --silent iq2lb-6 client # DXSpider, both ifaces listen ax25 add --silent iface=bpq iw2ohx-11 client # shell, only via the node

net.rc — the whole AX.25 side of the connector

libax25 — /opt/uax25/etc/ax25/wampes.conf and axports
# wampes.conf: which nodes exist and where they listen wampes /tcp/sockets/ax25 the WAMPES on this host # axports: ONE node-wide entry. Two entries may not share a callsign, # and which iface a call may use is decided in net.rc anyway. # name callsign speed paclen window description wampes IW2OHX-2 0 236 4 WAMPES on this host
ax25d — /opt/uax25/etc/ax25/ax25d.conf
[IQ2LB-6 VIA wampes] NOCALL * * * * * * L default * * * * * * - sysop /spider/src/client client %s ax25 [IW2OHX-11 VIA wampes] NOCALL * * * * * * L IW2OHX * * * * * * L # the station call never gets a shell default * * * * * * - root /opt/uax25/sbin/axspawn axspawn %S
axspawn — /opt/uax25/etc/ax25/axspawn.conf
create yes # account on the first AX.25 connect create_empty_password yes # the user sets one with passwd create_with_useradd yes pwcheck call # AX.25: the callsign is the login first_uid 2000 max_uid 4999 guest no group ax25 home /home/ax25 associate no # no outgoing AX.25 in the user's name

Built with a prefix such as /opt/uax25, axspawn looks for useradd in that prefix — a symlink to /usr/sbin/useradd is needed, or every new account fails

LinBPQ — bpq32.cfg, port 7 and the two applications
PORT PORTNUM=7 ID=WAMPES (gw userspace AX.25) DRIVER=BPQAXIP QUALITY=0 ; learn no NET/ROM here FRACK=3000 RESPTIME=10 RETRIES=5 MAXFRAME=5 PACLEN=236 DIGIFLAG=0 ; nothing is routed through WAMPES CONFIG UDP 10096 AUTOADDQUIET ; learn users' calls - loopback-only port! MAP IW2OHX-2 127.0.0.1 UDP 10095 MAP IQ2LB-6 127.0.0.1 UDP 10095 MAP IW2OHX-11 127.0.0.1 UDP 10095 ENDPORT APPLICATION 4,SHELL,C 7 IW2OHX-11 APPLICATION 7,DX,C 7 IQ2LB-6
The firewall lines that matter
# BEFORE any "accept the LAN / accept 44Net" rule - order is the whole point iptables -A INPUT -p udp --dport 94 ! -s 192.168.1.201 -j DROP # pcf learns: IW2OHX-12 only iptables -A INPUT -p udp --dport 10096 -j DROP # LinBPQ binds 0.0.0.0: lo only

LinBPQ binds port 7 on every address. Loopback is accepted earlier in the chain, so this DROP makes it reachable from the host alone.

systemd — wampes.service
[Service] Type=simple # net does not fork without -g WorkingDirectory=/tcp Environment=TZ=Europe/Rome # WAMPES otherwise assumes MEZ-1MESZ ExecStart=/tcp/sbin/net /tcp/net.rc Restart=on-failure

After a WAMPES restart, ax25d notices the lost listeners and claims IQ2LB-6 and IW2OHX-11 again by itself within about ten seconds.

Own IP addresses, too

WAMPES has its own IP stack. On iw2ohx-gw it is dual-homed through two tun links: 44.134.24.8 on 44Net and 44.144.128.135 on HAMNET (proxy ARP on the HAMNET interface). Policy rules on the host send traffic from .8 into the AMPRNet tunnel and keep .135 strictly on HAMNET. It is not needed for the AX.25 connector — it is there for IP-over-AX.25 later.

Section 6

The telnet gate: AX.25 as yourself

Modelled on the telnet port of DB0FHN, rewritten for this host.

  1. Telnet to port 23 on iw2ohx.ampr.org or pr.iw2ohx.hamnet.radio. Only 44Net and HAMNET sources are let in.
  2. Login: your callsign, SSID allowed. Station calls and NOCALL are refused before anything else.
  3. Password: the one you set yourself with passwd after your first SHELL by radio, FlexNet or NET/ROM. An account without a password cannot log in here. Five failures lock it for 15 minutes.
  4. The daemon drops root and opens an AX.25 connection from your callsign to IW2OHX-13 through WAMPES. You land at the BPQBOL prompt exactly as if you had come in by radio.

ax25telnetd is about 1,300 lines of C11: a telnet filter that strips every option negotiation and maps line ends (CR LF ↔ CR), the callsign rules, PAM, privilege drop and a relay loop. It is an ordinary libax25 program — socket(AF_AX25), bind to the user's callsign via the WAMPES port, connect — and contains no WAMPES-specific code at all. No account is handed out by mail: the first SHELL over packet radio creates it. The password travels in clear, so use one you use nowhere else.

Section 7

What cost us time

Commits used: libax25 1ff923b, ax25-tools 3b33d6b, ax25-apps b78e3cc, wampes c028c51 (October 2026), on a Raspberry Pi 4 with 64-bit Debian 13.

WAMPES on 64-bit ARM needs two patches

At this commit net is not built at all (proc.h sees HAS_UCONTEXT before configure.h; the top-level make runs with -i, so it says nothing), and once built it segfaults: alloc.c uses WAMPES' own 32-bit allocator on every 64-bit platform except x86_64. Check that /tcp/sbin/net exists after make install. Reported upstream.

axip-learn off drops your users

A session from LinBPQ carries the user's callsign, not the node's. On a non-learning iface WAMPES only accepts callsigns the sysop wrote down, so every user frame vanished before the interface counters even moved.

A learned route remembers the wrong port

A callsign heard first via the node, then via IW2OHX-12, got its address updated but stayed bound to the first iface — replies went out the loopback socket and were lost. Fixed here with a three-line patch to axip.c, reported upstream.

chmod on the socket does nothing

WAMPES recreates its sockets and resets sockets/ to 0755 at every start. Access is set in net.rc with axsock mode/group. Leave sockets/packet at root: a client whose primary group owns it may close any session.

Block the kernel modules

Even with nothing configured, a probe socket loaded ax25.ko at every start. install ax25 /bin/false in modprobe.d made the host behave like a 7.1 kernel — and everything kept working.

Debian packages shadow the userspace library

A packaged libax25 is built for the kernel stack. An apt pin with Pin-Priority: -1 for libax25, ax25-tools and friends keeps them off the host.