Skip to content

Add AGWPE Connected-Mode Support and Port-to-Channel Mapping for Pat Winlink #595

Description

@AI7BQ

Add AGWPE Connected-Mode Support and Configurable Port-to-Channel Mapping for Pat Winlink

Summary

Graywolf's AGWPE server supports APRS/UI traffic, but it cannot currently
replace Direwolf when Pat uses AGWPE for connected-mode AX.25 Winlink sessions.

Two enhancements are needed:

  1. Implement the AGWPE connected-mode commands used by Pat.
  2. Allow each AGWPE radio port to map to a selected Graywolf channel.

Use Case

Pat is configured as follows:

"ax25": {
  "engine": "agwpe"
},
"agwpe": {
  "addr": "graywolf.lan:8000",
  "radio_port": 0
}

The desired routing is:

Pat AGWPE port 0 -> Graywolf channel 2 ("VHF Packet")

An AGWPE version request succeeds and reports version 2004.1. Callsign
registration and UI-frame submission also work. However, Pat cannot establish
a connected AX.25 session through Graywolf.

Evidence: Connected Mode Is Explicitly Unsupported

Graywolf describes its AGW implementation as a minimal APRS-only subset:

https://github.com/chrissnell/graywolf/blob/main/pkg/agw/protocol.go#L1-L28

The source explicitly states that connected-mode frame types are accepted and
logged but not implemented.

The AGW dispatcher routes unsupported connected-mode commands to its default
branch, where they are logged and dropped:

https://github.com/chrissnell/graywolf/blob/main/pkg/agw/server.go#L397-L423

Raw AGW frames do not provide a workaround because non-UI AX.25 frames are
also explicitly ignored by that dispatcher.

Graywolf already contains an AX.25 connected-mode state machine under
pkg/ax25conn, including:

  • SABM and SABME establishment
  • I-frame data transfer
  • RR, RNR, REJ, and SREJ handling
  • Disconnect handling
  • Retransmission timers
  • Packet-channel validation

That implementation is not currently connected to the AGWPE server:

https://github.com/chrissnell/graywolf/tree/main/pkg/ax25conn

Evidence: Commands Required by Pat

Pat uses the AGWPE implementation from wl2k-go. Its required command kinds
are defined here:

https://github.com/la5nta/wl2k-go/blob/master/transport/ax25/agwpe/frame_kinds.go#L7-L21

Pat requires these connected-mode operations:

Command Purpose
C Connect without digipeaters
v Connect through digipeaters
D Send and receive connected data
d Disconnect and disconnect notification
Y Query outstanding frames for a connection
X Register a callsign
x Unregister a callsign
g Query port capabilities
R Query AGWPE version

Pat waits for a C success response or a d failure response after requesting
a connection:

https://github.com/la5nta/wl2k-go/blob/master/transport/ax25/agwpe/conn.go#L211-L251

Pat uses D for net.Conn reads and writes and d during teardown:

https://github.com/la5nta/wl2k-go/blob/master/transport/ax25/agwpe/conn.go#L135-L209

Pat also uses Y to enforce MAXFRAME and flush queued frames before
disconnect. Without a Y response, writes or shutdown can time out:

https://github.com/la5nta/wl2k-go/blob/master/transport/ax25/agwpe/conn.go#L57-L97

Pat initializes its configured AGWPE radio port and registers it as a
connection-oriented transport here:

https://github.com/la5nta/pat/blob/master/app/connect.go#L423-L447

Evidence: Port Mapping Is Hardcoded

Graywolf currently constructs its AGW server with:

PortToChannel: map[uint8]uint32{0: 1},

Source:

https://github.com/chrissnell/graywolf/blob/main/pkg/app/wiring.go#L1216-L1237

The persisted AGW configuration contains only:

  • listen_addr
  • callsigns
  • enabled

It does not contain a channel mapping:

https://github.com/chrissnell/graywolf/blob/main/pkg/configstore/models.go#L259-L277

The AGW API request DTO similarly has no channel-mapping field:

https://github.com/chrissnell/graywolf/blob/main/pkg/webapi/dto/agw.go#L1-L32

Consequently, Pat's radio_port: 0 always routes to Graywolf channel ID 1,
even when the intended packet channel is channel ID 2.

Changing Pat to radio_port: 1 is not a valid workaround because it does not
change Graywolf's hardcoded port-to-channel mapping.

Requested Implementation

Configurable Port Mapping

  • Add an explicit mapping from each AGWPE port number to a Graywolf channel ID.
  • Persist the mapping in Graywolf's configuration database.
  • Expose the mapping through the REST API.
  • Add a channel selector for each AGWPE port in the web interface.
  • Reject mappings to nonexistent, disabled, or APRS-only channels.
  • Preserve backward compatibility for existing single-port configurations.

A possible representation would be:

{
  "enabled": true,
  "listen_addr": "0.0.0.0:8000",
  "ports": [
    {
      "port": 0,
      "callsign": "AI7BQ",
      "channel_id": 2
    }
  ]
}

Connected-Mode AGWPE Support

Connect the AGWPE server to the existing pkg/ax25conn implementation.

Implement the following behavior:

  • Route C requests into ax25conn.Manager to initiate a connection.
  • Route v requests into ax25conn.Manager with the requested digipeater path.
  • Return a compatible C notification when the AX.25 connection succeeds.
  • Return a compatible d notification when establishment fails.
  • Route client D frames into the connected AX.25 session.
  • Return received connected data to the AGWPE client as D frames.
  • Process client d requests and close the associated AX.25 session.
  • Notify the AGWPE client with d when the remote station disconnects.
  • Implement Y responses for outstanding frames on a connection.
  • Support inbound connections for callsigns registered with X.
  • Associate sessions with the correct AGWPE client, port, local callsign,
    remote callsign, and digipeater path.
  • Clean up sessions when an AGWPE TCP client disconnects.

Direwolf Compatibility

Responses should match the behavior expected by Pat's wl2k-go AGWPE client.

In particular:

  • A successful outbound connection must return a C frame whose payload begins
    with *** CONNECTED With .
  • An inbound connection notification must return a C frame whose payload
    begins with *** CONNECTED To .
  • Connection failure and teardown must return a d frame.
  • A Y request must return a four-byte little-endian outstanding-frame count.
  • D frames must preserve callsigns, radio port, PID, and payload.

Acceptance Criteria

  1. An AGWPE port can be assigned to any enabled Graywolf packet-capable channel.
  2. AGWPE port 0 can be mapped to channel ID 2 without modifying Pat.
  3. Pat can connect to an RMS gateway through Graywolf using ax25+agwpe.
  4. Pat can exchange Winlink data bidirectionally over the connection.
  5. Pat can cancel or close a session without waiting for AGWPE timeouts.
  6. Remote disconnects are reported promptly to Pat.
  7. Incoming connected-mode sessions work through Pat's AGWPE listener.
  8. Connections through one or more digipeaters work using the v command.
  9. Integration tests exercise C, v, D, d, and Y.
  10. At least one integration test uses la5nta/wl2k-go as the AGWPE client.
  11. Existing APRS, monitor, unproto, and raw-frame behavior remains functional.
  12. Existing AGWPE configurations migrate without losing their settings.

Current Observed Behavior

The AGWPE listener responds successfully to R, G, and M, proving that
the TCP interface and basic protocol framing are operational.

A test M packet submitted on AGWPE port 0 was routed to internal channel
1. Graywolf logged:

tx drop: no backend for channel
component=txbackend channel=1 frame_id=1 len=36

This confirms the current hardcoded mapping:

AGWPE port 0 -> Graywolf channel 1

The desired packet channel is channel ID 2, named VHF Packet.

Even after correcting that mapping, Pat cannot establish a session because
Graywolf's AGWPE dispatcher currently drops the connected-mode commands that
Pat requires.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions