FPAC 4.2: dynamic ROSE routes and idle White Pages sessions

Specification, working draft of 2026-10-13. Work in progress: the fpacrtd daemon is being tested on three nodes; its code is not released yet. Version française

1. Purpose

Today an FPAC node only knows the routes written by hand in fpac.routes. A DNIC or a node missing from that file cannot be reached, even when a direct neighbour knows how to reach it.

The purpose is that each node learns from its direct neighbours what they can reach, installs the routes it lacks, and removes them when they are no longer valid. The fpac.routes file keeps the last word.

2. What the mechanism does, and does not do

It does:

It does not:

3. Architecture

A new daemon, fpacrtd, started by myfpac.sh after the other FPAC daemons and stopped before them, so that its routes are withdrawn while fpad is still running.

Any local script that stops AX.25 or FPAC without going through myfpac.sh must stop fpacrtd too, and first: on 2026-10-11 a restart of AX.25 on one node left the old daemon alive beside the new one.

The same protection covers the four other daemons since 2026-10-12: fpad, fpacwpd, fpacstat and fpacroute each hold a lock on /var/ax25/fpac/<name>.lock and refuse to start when another copy is running. They also check their arguments before touching anything: -h or -? shows a short help, -V the version, and an unknown option or a stray argument is an error. Until then fpad and fpacroute ignored every argument and started: fpad -V, typed on a running node to read its version, started a second fpad that cleared and rebuilt the ROSE routes of the node and listened on the same addresses as the first one. Note that fpacwpd -v is still the verbose mode and starts the daemon.

A lock file that cannot be written (file system full or read-only) is opened read-only, which is enough to hold the lock. When it cannot be opened at all, the daemon logs a warning and starts without the protection: a node that does not come back is worse than a missing lock. A daemon built before the lock is not seen by a new one; the protection holds once the new programs run.

Why a separate daemon and not fpacwpd: a current fpacwpd closes the session when it receives a message type it does not know (wp_receive_pdu returns -1). Adding a message to the White Pages sessions would break the synchronisation with every node not yet updated. A separate service leaves the White Pages untouched.

The service callsign is FPACRT, not ROUTE: fpacroute, the traceroute application, already listens on ROUTE on every FPAC node (found during the first test, 2026-10-08).

A node without fpacrtd does NOT refuse a call to FPACRT: its fpad takes it for a user call, records the caller in the White Pages and tries an AX.25 connection to a station named FPACRT. A neighbour is therefore called only when its Node block carries Route = yes (see 10).

4. What is announced

Two kinds of destinations only:

Kind Mask Example
Node address 10 2080 175526
Whole DNIC 4 5050

Intermediate prefixes (masks 5 to 9, for example 2080 44) are neither announced nor learned. Routing loops came from them; they remain the business of fpac.routes.

Origin of an announcement:

Everything else is relaying: a node re-announces a learned destination at the received cost plus 1, and nothing else. A route of fpac.routes never produces an announcement by itself: a wrong or stale line therefore has no effect outside the node.

A node relays only what its own RouteLearn lets it retain: with RouteLearn = no it relays nothing, and with RouteLearn = nodes it relays no DNIC.

A node declaring RouteOrigin = 5050 must itself have a route to that DNIC in fpac.routes. Otherwise fpacrtd reports it in the log and does not announce that DNIC.

The DNIC is announced only while its exit link is open, that is while a neighbour of that route is "open" in rose_neigh. When the link closes the DNIC is withdrawn (W), and announced again when it reopens; the state is checked every minute and each change is written in the log. The link with the neighbour the announcement goes to does not count: a DNIC is never announced to a node on the strength of the link with that same node.

5. Exchanges

The dialogue is plain text, one line per message, so that it stays readable with listen.

Message Meaning
FPACRT 1 <call> opening: version, L3 callsign
R <adr> <m> <c> route: address, mask, cost
W <adr> <m> withdrawal of that route
E <n> end of table, n routes sent

Timing:

6. Reception rules

For each route received from a neighbour V, with cost c:

  1. retained cost = c + 1; above 6 the route is ignored;
  2. if the destination is one of the node's own addresses: ignored;
  3. if fpac.routes already holds that address with that mask: the route is not installed, the manual route stays in place; the destination is still relayed to the other neighbours;
  4. if V is not "open" in rose_neigh: not installed;
  5. otherwise it is installed if no learned route exists for that destination, or if its cost is strictly better.

A route that RouteLearn does not admit is not retained at all: it is neither installed nor relayed (see 4).

One learned route per destination: the best one. No backup neighbour in version 1.

7. Transmission rules

8. Lifetime

A learned route is removed from the kernel when:

At startup, fpacrtd first removes the routes left by its previous run, according to fpacrt.dat. Every minute it also checks that its routes are still in the kernel and puts back any that disappeared.

A neighbour that stays silent is handled too: when a node holds routes learned from a neighbour and has received no full table from it for 90 minutes, it closes the circuit. The routes learned from that neighbour are removed, and it is called again 30 minutes later. A neighbour that never announced anything (RouteAnnounce = no) is silent by design and is left alone.

9. Manual routes and learned routes

10. Configuration

In fpac.conf and fpac.nodes, four settings, all inactive by default:

Keyword Values Effect
RouteAnnounce no, yes sends its table
RouteLearn no, nodes, learns nothing, nodes,
all or nodes and DNICs
RouteOrigin one DNIC per line exit gateway of the DNIC
Route yes, in a Node this neighbour takes
block part

Without these lines, a 4.2 node behaves like a 4.1 node. A neighbour without Route = yes is never called and its calls are refused.

Example for f6bvp-8, which has the real links abroad:

RouteAnnounce = yes
RouteLearn    = all
RouteOrigin   = 2020
RouteOrigin   = 2400
RouteOrigin   = 3020
RouteOrigin   = 3100
RouteOrigin   = 5050

and, in fpac.nodes, one more line in the block of each neighbour that runs fpacrtd (here f6bvp-12). Nothing is exchanged without it:

Node = F6BVP-13
 ...
 Route = yes
End

11. Display

Planned, not coded yet. The Routes command of fpacnode will mark the learned routes (dyn), and a routes dyn command will list the learned routes only, with the neighbour that announces them.

Today the learned routes can be read in the log of fpacrtd (lines "add route" and "del route") and in fpacrt.dat.

12. Compatibility

13. Test plan

  1. Pair f6bvp-12 / f6bvp-8 (f6bvp-8 is the best connected node): f6bvp-8 with RouteAnnounce = yes and its RouteOrigin lines, f6bvp-12 with RouteLearn = all. First remove a distant DNIC, for example 5050, from fpac.routes of f6bvp-12: it must come back by itself. Also check the removal when the neighbour closes and the purge at restart.
  2. Enable announcing both ways on the pair, then add f6bvp-10 with RouteLearn = all: it must receive 5050, which it lacks today.
  3. Provoked loop test: cut the exit link of a DNIC and check that the route disappears everywhere in less than 2 minutes, with no frame going round (rose-controle.py).
  4. One week of observation before extending to the other nodes.

Status on 2026-10-12: step 1 passed on 2026-10-08, including the removal when the neighbour's daemon stops and the purge at restart. Since 2026-10-09 three nodes take part: f6bvp-8 announces, f6bvp-12 and f6bvp-10 learn, each directly from f6bvp-8. After each of four nightly restarts of FPAC the sessions and the learned routes came back by themselves within 50 seconds, and the table of 6 routes has been received every 30 minutes without a miss. Announcing both ways (step 2) is not enabled yet. Not tested on the air yet: ageing, the 90 minute rule, relaying through a third node, and a real call through a learned route.

The daemons with the single instance lock (see 3) have been running on seven nodes since 2026-10-12, built from the 4.1.6-rc7 sources plus this change. On each node the locks are held and a second start of each daemon is refused.

14. Decisions taken and open points

Decided (F6BVP, 2026-10-08):

Still open:

Decided (F6BVP, 2026-10-13):

15. Numbering

This feature adds a daemon, a protocol between nodes and configuration keywords: it justifies FPAC 4.2. Proposed order:

  1. release the stable 4.1.6 with the fixes of rc7;
  2. open 4.2-rc1 with fpacrtd and WpIdle (see 16), both inactive by default, the change to the vectors (see 17) and the QSl command (see 18).

The code under test is public: branch fpac-4.2 of https://github.com/f6bvp/fpac. It is experimental and not a release.

16. Second item: White Pages sessions closed when idle

Decided by F6BVP on 2026-10-08. Nothing is coded yet.

Today fpacwpd keeps one ROSE circuit open for ever with each neighbour (WP-0 to WP-0), although it only carries a record now and then and one vector exchange per hour.

New keyword in fpac.conf:

Keyword Value Effect
WpIdle minutes, 0 = off closes an idle WP session

Without the keyword, or with 0, nothing changes: the session stays open as in 4.1. Recommended value: WpIdle = 10.

Behaviour with WpIdle = 10:

What the code needs:

With a neighbour still running 4.1, which reopens its session every 30 minutes and keeps it, the circuit is closed again 10 minutes later. This is harmless but shows in the logs.

The kernel can clear an idle ROSE circuit by itself (socket option ROSE_IDLE). Closing from fpacwpd is preferred, because it knows whether an exchange is in progress; to be settled when coding.

Test: on a pair, check that an idle session closes, that a record changed with wpedit reopens it and reaches the neighbour, and that the hourly vector exchange still takes place.

17. Third item: obsolete deleted records left out of the vectors

Found on 2026-10-11. Two nodes kept logging Vectors still differ every hour although both ran the same version.

A deleted record older than 14 days (WP_OBSOLETE) is neither sent nor accepted between nodes, and a deleted record for an unknown callsign is refused. Yet these records count in the vectors, whose checksum covers the date and the address of every record. So when two nodes mark a record deleted, or erase it, on different days, their vectors differ and the exchange cannot repair it: the records that would settle the difference are exactly the ones that are not exchanged. It lasts until both nodes have erased them.

Here one node ran its nightly wpmaint -d 100 while the others ran wpmaint -d 30: it held 12 deleted records the others had erased, and two records still valid that were deleted elsewhere.

Decided (F6BVP, 2026-10-11): leave out of the vector checksum the deleted records too old to be exchanged. Coded on 2026-10-13, not yet in service.

A node with the change and a node without it compute different vectors and would differ for ever. Decided (F6BVP, 2026-10-13): the vector version number changes with the new checksum; this is what it is for.

fpacwpd up to 4.1.6 closes the White Pages session when it receives a vector of another version, and calls again 30 minutes later. With nothing more, a 4.2 node and a 4.1.6 node would no longer keep a session. Decided (F6BVP, 2026-10-13): 4.2 goes on speaking the old version, with the old checksum, to a neighbour that is still older; the new version is used between 4.2 nodes only. A network may then be upgraded node by node.

As coded on 2026-10-13: the new vector version is 0x112, the old one 0x111. A node answers a vector request in the version it came in. For its own requests it first takes every neighbour for a 4.2 node; if the neighbour hangs up before answering, it is spoken to in the old version from then on, with one line in the log. A vector in the new version received from that neighbour proves it was upgraded. Since a session may also drop for another reason at that very moment, the new version is tried again once a week. The price with a 4.1 neighbour: one session closed at the first exchange, then once a week; it reopens within 30 minutes.

Not covered: a recently deleted record that the other node never knew is refused there and still counts in the vectors, until it becomes obsolete in turn.

Until then: use the same wpmaint delays on every node of a network. The difference is harmless - three vector rounds an hour at most since 4.1.6-rc7 - but it fills the log.

18. Fourth item: the QSl command of the node

Asked for on 2026-10-13: a QSL card confirming a connection to the node, in the manner of the card sent after a radio contact.

A user types QSl (QS is enough). The node shows the card as text in the session: the heading "FPAC node confirming connection with ", the callsign of the user, the date and time in UTC, the time the session began, then what the Status command shows down to the FPAC version.

A session only carries text, so the image of the card cannot go through it. Decided (F6BVP, 2026-10-13): the node makes the image and gives a link to download it with a web browser; no e-mail. The image is one JPEG file, the picture of the node above (front side) and the text below (back side).

This needs a web server on the node, so it is optional:

Keyword Value Effect
QslDir directory where the cards are written
QslUrl web address the same directory on the web
QslImage file picture of the front side
QslKeep days, 30 by default how long a card is kept

They are written in fpac.conf, or in a file fpacqsl.conf next to it. Without QslDir and QslUrl the node gives the text card only. The front picture is by default the file QSL_fpac.jpg placed next to fpac.conf by the sysop; without it the card has its text side only.

The image is drawn by a separate program, fpacqsl, written in Python: the node itself gains no dependency. It needs the python3-pil package.

Limits: the cards older than QslKeep days are erased; a user who asks again within 10 minutes is given the card already made; no image is made while the directory holds 500 cards. Each request is written in fpac/qsl.log.

State on 2026-10-13: coded, and on trial on one node, which serves the cards under http://f6bvp.org/qsl/. The user guide is the file doc/qsl.txt of the sources.