dir refactor

This commit is contained in:
Craig McDaniel
2026-02-25 19:36:49 -06:00
parent f118037e57
commit 5c11988e73
36 changed files with 441 additions and 281 deletions
@@ -1,12 +1,13 @@
# Role: scanner_digirig
# Configure direwolf with the Digirig device
# CONFIGURE ARGUS AX.25 VHF STACK!
# This uses a dedicated 2 meter radio with a dedicated antenna, a Digirig and software like
# Direwolf, Socat and the Linux kernel AX.25 implementation.
#
# - Set up udev rules to make special device drivers for the Digirig in /dev/radio so we don't
# have to relay on /dev/ttyUSB0, 1 or whatever number the OS happens to assign. This gives the
# device names consistency.
# device names consistency. ALso name the ALSA sound device "digirig".
#
# - Set up systemd service to start Direwolf when the device is connected via USB, and to stop it
# when the radio is disconnected.
# - Set up systemd service to start the AX.25 stack when the device is connected via USB, and to
# stop it when the radio is disconnected.
#
# - Copy configuration files
#
@@ -15,17 +16,18 @@
file:
path: "{{item}}"
state: directory
owner: busnet
group: busnet
owner: argus
group: argus
mode: u=rwx,g=rwx,o=rx
with_items:
- /opt/busnet/direwolf
- /opt/busnet/direwolf/config
- /opt/busnet/direwolf/logs
- /opt/argus/scripts
- /opt/argus/direwolf
- /opt/argus/direwolf/config
- /opt/argus/direwolf/logs
- name: Make sure busnet user is in groups for audio and USB access
- name: Make sure argus user is in groups for audio and USB access
ansible.builtin.user:
name: busnet
name: argus
groups: "{{item}}"
append: yes
with_items:
@@ -34,10 +36,10 @@
- audio
- plugdev
- name: Grant full passwordless sudo access to busnet user
- name: Grant full passwordless sudo access to argus user
ansible.builtin.copy:
content: "busnet ALL=(ALL) NOPASSWD:ALL"
dest: /etc/sudoers.d/busnet
content: "argus ALL=(ALL) NOPASSWD:ALL"
dest: /etc/sudoers.d/argus
mode: '0440'
owner: root
group: root
@@ -50,108 +52,45 @@
- ax25-apps
- libax25
state: present
- name: Ensure AX.25 and NET/ROM kernel modules are loaded
community.general.modprobe:
name: "{{ item }}"
state: present
with_items:
- ax25
- netrom
###################################################################################################
# Direwolf bash script and config
# AX25 VHF bash script
###################################################################################################
- name: Copy digirig-direwolf.sh AX.25 stack script
- name: Copy ax25-vhf.sh script
copy:
src: "templates/digirig-direwolf.sh"
dest: "/opt/busnet/direwolf/digirig-direwolf.sh"
owner: busnet
group: busnet
src: "templates/ax25-vhf.sh"
dest: "/opt/argus/scripts/ax25-vhf.sh"
owner: argus
group: argus
mode: "0755"
- name: Copy Direwolf config for Digirig
template:
src: "templates/digirig-direwolf.conf.j2"
dest: "/opt/busnet/direwolf/config/digirig.conf"
owner: busnet
group: busnet
dest: "/opt/argus/direwolf/config/digirig.conf"
owner: argus
group: argus
###################################################################################################
# SYSTEMD: Direwolf
# SYSTEMD: Install argus-ax25-vhf.service
###################################################################################################
- name: Copy AX.25 bash script to start direwolf and utils
template:
src: "templates/digirig-direwolf.conf.j2"
dest: "/opt/busnet/direwolf/config/digirig.conf"
owner: busnet
group: busnet
# udev rules start this automatically when the radio USB is connected, and stop when disconnected.
- name: Install systemd unit file for Direwolf/AX.25 stack
- name: Install systemd unit file for AX.25 VHF stack
template:
src: "templates/digirig-direwolf.service.j2"
dest: "/etc/systemd/system/digirig-direwolf.service"
src: "templates/argus-ax25-vhf.service.j2"
dest: "/etc/systemd/system/argus-ax25-vhf.service"
owner: root
group: root
mode: "0644"
- name: Enable digirig-direwolf service
- name: Enable argus-ax25-vhf.service
systemd:
name: "digirig-direwolf"
name: "argus-ax25-vhf"
daemon_reload: true
enabled: true
###################################################################################################
# SYSTEMD: Net/Rom
###################################################################################################
- name: Copy mheardd and netromd systemd service files
ansible.builtin.copy:
src: "templates/{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
owner: root
group: root
mode: '0644'
with_items:
- mheardd.service
- netromd.service
register: services_copied
- name: Enable and start AX.25/NET/ROM daemons
ansible.builtin.systemd:
name: "{{ item }}"
enabled: true
state: started
daemon_reload: "{{ services_copied.changed }}"
with_items:
- mheardd
- netromd
###################################################################################################
# SYSTEMD: NET/ROM network interface init
# These get created when the service starts and they just stay running until you shut down the
# service or reboot.
###################################################################################################
- name: Install systemd unit file for NET/ROM network interfaces
ansible.builtin.copy:
src: "templates/netrom-init.service.j2"
dest: "/etc/systemd/system/netrom-init.service"
owner: root
group: root
mode: '0644'
register: netrom_init_service
- name: Enable and start netrom-init service
ansible.builtin.systemd:
name: netrom-init
enabled: true
state: started
daemon_reload: "{{ netrom_init_service.changed }}"
###################################################################################################
# AX35 and NET/ROM Config
###################################################################################################
@@ -168,6 +107,32 @@
- nrports
- nrbroadcast
###################################################################################################
# SYSTEMD: netromd and mheardd services
###################################################################################################
- name: Copy mheardd and netromd systemd service files
ansible.builtin.copy:
src: "templates/{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
owner: root
group: root
mode: '0644'
with_items:
- mheardd.service
- netromd.service
register: services_copied
- name: Enable and start mheardd and netromd services
ansible.builtin.systemd:
name: "{{ item }}"
enabled: true
state: started
daemon_reload: "{{ services_copied.changed }}"
with_items:
- mheardd
- netromd
###################################################################################################
# UDEV RULES (keep these last)
###################################################################################################
@@ -1,19 +1,25 @@
# udev rule to provide fixed device name for my Digirig and start/stop services automatically!
# This information was retrieved by the following. Assuming the radio USB sound card is hooked
# up to /dev/ttyUSB0
#
# craig@scanner:~$ udevadm info /dev/ttyUSB0 | grep ID_SERIAL
# This is how I found the serials, vendor and product id values:
#
# Plug in the digirig and not anything else so you know it's card card #1, then get the vendor and
# product id values like this:
#
# $ udevadm info /dev/ttyUSB0 | grep ID_SERIAL
# E: ID_SERIAL=Silicon_Labs_CP2102N_USB_to_UART_Bridge_Controller_809986b5195bed119acbed165820eef3
# E: ID_SERIAL_SHORT=809986b5195bed119acbed165820eef3
#
# $ sudo udevadm info -a /sys/class/sound/card1 | grep -E 'idVendor|idProduct'
# ATTRS{idProduct}=="0012"
# ATTRS{idVendor}=="0d8c"
# This creates /dev/radio/digirig-sound which is a USB sound card device
# It does not matter what USB port it's plugged into, or how many other USB devices are also plugged in.
# ---------------------------------------------------------------------------------------------------
SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="809986b5195bed119acbed165820eef3", SYMLINK+="radio/digirig-sound", MODE="660", GROUP="plugdev"
# This sets a custom sound card name of "digirig" in the Linux ALSA system.
SUBSYSTEM=="sound", ATTRS{idVendor}=="0d8c", ATTRS{idProduct}=="0012", ATTR{id}="digirig"
# Automatically start direwolf bash script when the radio is connected. Identify the radio by the
# unique setial number.
@@ -1,14 +1,14 @@
# This file is managed by BusNet Ansible.
# This file is managed by Ansible.
#
# This service starts the direwolf/AX.25 stack bash script when the Digirig device is connected to
# this computer. The bash script then initializes the Linux AX.25 stack.
# This service starts the dedicated 2m AX.25 stack bash script when the Digirig device is connected
# to this computer.
#
# There is a corresponding udev rule that triggers this service automatically when it detects that
# the radio's USB devices are connected. There is also a udev rule that shuts it down when the USB
# is disconnected.
[Unit]
Description=Direwolf TNC and AX.25 Stack
Description=Argus AX.25 VHF Stack
#After=network.target
# Link the service life cycle to the hardware device /dev/radio/digirig-sound. I don't know why, but
@@ -19,11 +19,11 @@ After=dev-radio-digirig\x2dsound.device
[Service]
Type=simple
ExecStart=/opt/busnet/direwolf/digirig-direwolf.sh
ExecStart=/opt/argus/scripts/ax25-vhf.sh
Restart=on-failure
RestartSec=5
User=busnet
Group=busnet
User=argus
Group=argus
# This tells systemd to kill all child processe started by the bash script.
KillMode=control-group
@@ -0,0 +1,246 @@
#!/bin/bash
# Start the AX.25 VHF Stack
# @author Craig McDaniel
#
# This script runs Direwolf, socat and kissatach. It starts them in that order and makes sure they
# all stay running and work together. If one crashes, this script stops everything. This effectively
# sets up a AX.25 interface on a Linux computer using the Linux AX.25 stack.
#
# It's designed to be uses with systemd for things like auto start, auto re-start.
#
#
# Here's a diagram of the stack this creates:
# -------------------------------------------------------------------------------------------------
#
# RF signals! _ RF signals!
# \ _/
# \ /
# +-------+
# | Radio | ----- RF signals!
# +-------+
# |
# | Analog cable
# |
# +---------+
# | Digirig |
# +---------+
# |
# | USB sound card
# |
# +----|----------------------------------------------------------------------------+
# | | This is your computer. The things in here are virtual components. |
# | | |
# | +-----------+ +-------+ +------------+ Linux kernel |
# | | Direwolf | <---> | Socat | <----> | Kissattach | <------> ax0 interface |
# | +-----------+ +-------+ +------------+ / | |
# | | /--------+ | |
# | AGWPE port / | |
# | available for +---------------+ +------------------+ |
# | use | mheard daemon | | netromd: NET/ROM | |
# | +---------------+ | Daemoon. | |
# | +------------------+ |
# +---------------------------------------------------------------------------------+
# Command to execute direwolf including any options and location of config file.
DIREWOLF_CMD="/usr/local/bin/direwolf -t 0 -X 1 -c /opt/argus/direwolf/config/digirig.conf"
# The AX.25 port for the radio defined in /etc/ax25/axports. Note that this is not the same thing as
# the kernel AX.25 interfaces, which are named things like ax0, ax1, etc...
AX25_PORT_NAME="digirig"
# The NET/ROM port for the radio defined in /etc/ax25/nrports. Note that this also is not the same
# thing as the kernel NET/ROM interface like nr0, nr1, etc...
NETROM_PORT_NAME="digirig-nr"
# The PTY/TTY (whatever it's called) that socat will create for kissattach
TNC_DEV="/dev/radio/digirig-tnc"
# This is the socat command to that connects one end of a virtual pipe to Direwolf's KISS TCP port,
# and the other end to the PTY/TTY. The IP and port specified here must match the Direwolf config
# file.
SOCAT_CMD="/usr/bin/socat PTY,raw,echo=0,link=${TNC_DEV} TCP4:127.0.0.1:8001"
# These are the commands to create the kernel AX.25 interface using the kissattach utility.
KISSATTACH_CMD="/usr/sbin/kissattach ${TNC_DEV} ${AX25_PORT_NAME}"
KISSPARAMS_CMD="kissparms -c 1 -p ${AX25_PORT_NAME}"
# This is for creating the NET/ROM kernel interface.
NRATTACH_CMD="nrattach ${NETROM_PORT_NAME}"
# Cleanup the AX.25 stack
cleanup_stack()
{
# Kill kissattach explicitly (it forks, so we can't track its PID)
sudo pkill -f "kissattach ${TNC_DEV} ${AX25_PORT_NAME}"
sleep 1
# Kill socat by PID
if [ -n "$SOCAT_PID" ]; then
sudo kill $SOCAT_PID 2>/dev/null
sleep 1
fi
# Kill direwolf by PID and wait for it to finish
if [ -n "$DIREWOLF_PID" ] && kill -0 $DIREWOLF_PID 2>/dev/null; then
kill $DIREWOLF_PID 2>/dev/null
wait $DIREWOLF_PID 2>/dev/null
fi
# The file /etc/ax25/nrports mapps the $NETROM_PORT_NAME to a unique callsign. We need to
# figure out what the associated callsign for the netrom port is.
# Example: digirig-nr K0BIT-8 8BITS 235 NET/ROM Port
local NETROM_CALLSIGN=$(grep "^${NETROM_PORT_NAME}" /etc/ax25/nrports | awk '{print $2}')
# make sure we actually have a corresponding NET/ROM device and callsign in nrports. If not,
# then we can just be done right now.
if [ -z "$NETROM_CALLSIGN" ]; then
return
fi
# Find all NET/ROM interfaces (like nr0, nr1, nr2, etc...) and check their callsigns.
# Shut down any NET/ROM interface with the $NETROM_CALLSIGN attached to it.
for IFACE in $(ip link show | grep -oE 'nr[0-9]+'); do
# Get the callsign for this interface
local IFACE_CALL=$(ip addr show $IFACE | grep 'netrom' | awk '{print $2}')
if [ "$IFACE_CALL" = "$NETROM_CALLSIGN" ]; then
echo "Bringing down NET/ROM interface ${IFACE} ..."
sudo ip link set $IFACE down
return
fi
done
}
# Start the AX.25 stack
start_stack()
{
# Start Direwolf in background
$DIREWOLF_CMD &
DIREWOLF_PID=$!
# NOTE: If Direwolf encounters an error, it will just WAIT something like 15 seconds before
# exiting. It does not fail fast. That means that the following check will pass, but then
# we go to move on to create socat which will fail. I don't have a good solution for this
# case just yet, but I'm recording this note here.
if ! kill -0 $DIREWOLF_PID; then
echo "Direwolf failed to start."
cleanup_stack
exit 1
fi
echo "Direwolf started (PID: $DIREWOLF_PID)"
# Start socat PTY bridge.
# socat creates a full duplex/bidirectional pipe between a serial port ${TNC_DEV} and Direwolf's
# KISS TCP interface, which is running on localhost:8001. The kissattach program needs a serial
# port to work, and this is how we accomplish connecting kissattach to direwolf.
sleep 3
sudo $SOCAT_CMD &
SOCAT_PID=$!
if ! kill -0 $SOCAT_PID; then
echo "Socat failed to start."
cleanup_stack
exit 1
fi
echo "socat started (PID: $SOCAT_PID)"
# Run kissatach to create an AX.25 network interface on the system.
# Kissattach annoyingly forks itself in the background, so we can't directly monitor the process.
# Kissattach cannot use KISS over TCP directly to Direwolf. It expects a serial terminal to exist,
# which is why we use socat to create the pipe between Direwolf's KISS TCP port and this serial
# device.
sleep 1
sudo $KISSATTACH_CMD
if [ $? -ne 0 ]; then
echo "kissattach failed to start."
cleanup_stack
fi
# We have to set something on the AX.25 interface that Direwolf requires via kissparams.
sudo $KISSPARAMS_CMD
if [ $? -ne 0 ]; then
echo "kissparams failure detected."
cleanup_stack
fi
# We must run the nrattach command to create a kernel NET/ROM interface.
sudo $NRATTACH_CMD
if [ $? -ne 0 ]; then
echo "Command ${NRATTACH_CMD} failed."
cleanup_stack
exit 1
fi
# Restart mheardd daemon. If mheardd was started before ax1, ax2 or other AX.25 kernel interface
# was created, and now we receive AX.25 packets on this new interface we just created, mheardd
# just throws errors about a new, unknown interface. Ugh.
if sudo systemctl is-enabled --quiet mheardd.service; then
echo "Restarting mheardd..."
sudo systemctl restart mheardd.service
fi
# If netromd is not running, restart it. Otherwise, leave it alone.
if sudo systemctl is-enabled --quiet netromd.service; then
if ! sudo systemctl is-active --quiet netromd.service; then
echo "Re-starting netromd..."
sudo systemctl start netromd.service
fi
fi
}
# This is our POSIX signal handler so we cleanly shut down before exiting.
handle_signal()
{
echo "We received a SIGINT or SIGTERM signal. Shutting down VHF AX.25 stack."
cleanup_stack
exit 0
}
###################################################################################################
#
# MAIN ENTRY POINT
#
###################################################################################################
echo ""
echo "======================================================================"
echo "VHF AX.25 Stack Started"
echo "======================================================================"
echo ""
# This is the bash way of intercepting these signals. We execute the cleanup function when we receive
# either one.
trap handle_signal SIGINT SIGTERM
# Load required kernel modules
echo "Loading kernel modules..."
sudo modprobe ax25
if [ $? -ne 0 ]; then
echo "Failed to load ax25 kernel module"
exit 1
fi
sudo modprobe netrom
if [ $? -ne 0 ]; then
echo "Failed to load netrom kernel module"
exit 1
fi
# Clean up any orphaned kissattach processes from previous runs. We must do this because kissattach
# forks itself in the background, and we therefore cannot keep track of its PID. We use pkill to
# make sure no previous kissattach is running.
sudo pkill -f "kissattach ${TNC_DEV} ${AX25_PORT_NAME}"
sleep 1
# Note that we don't have to do the same for nrattach. nrattach runs and then exits.
# Start the AX.25 stack
start_stack
echo "All processes started. Monitoring via wait..."
# 'wait -n' waits for ANY background process to exit.
# It returns as soon as Direwolf or Socat dies. Kissattach forks itself so we can't wait on it.
wait -n
# If we get here, one of the processes died. Cleanup the stack and exit.
cleanup_stack
@@ -1,4 +1,4 @@
# This file is managed by BusNet Ansible
# This file is managed by Ansible
#
# This is the Direwolf configuration for connecting Direwolf to the Digirig USB sound card. This assumes
# that we will operate using 1200 baud mode in 2 meters VHF with a dedicated radio for this purpose.
@@ -6,7 +6,9 @@
# There us a udev rule which creates "/dev/radio/digirig-sound" so that we do not have to worry about
# what USB port or tty number is assigned when this device is connected to the computer.
ADEVICE plughw:Device,0
# We create a custom ALSA sound card name of "digirig" in our udev rules. Direwolf uses ALSA directly,
# so this tells Direwolf to use the "digirig" sound card, subdevice #0 (there is only 1 subdevice).
ADEVICE plughw:CARD=digirig,DEV=0
# The custom udev rule makes this device available by name.
PTT /dev/radio/digirig-sound RTS
@@ -19,4 +21,7 @@ MODEM 1200
FX25TX 1
AGWPORT 8000
KISSPORT 8001
KISSPORT 8001
# In the future, I might set up a RV BBS! I would beacon it here.
#PBEACON every=1 overlay=S symbol="bus" lat=29.958260914551104 long=-90.05442788530239 comment="I'm like testing APRS, man" via=WIDE1-1,WIDE2-1
@@ -1,7 +1,7 @@
[Unit]
Description=NET/ROM Routing Daemon
After=digirig-direwolf.service
BindTo=digirig-direwolf.service
After=ax25-vhf.service
BindTo=ax25-vhf.service
# systemd has trouble with this daemon. Presumably it's old code and systemd cannot determine if
# the daemon starts. These settings seem to work.
@@ -16,19 +16,57 @@
# - Run kissattach to the socat PTY to create a kernel AX.25 interface.
# - Make sure mheardd daemon is restarted so it can see new AX.25 interface.
#
# Here's a diagram of the stack this creates:
# -------------------------------------------------------------------------------------------------
#
# RF signals! _ RF signals!
# \ _/
# \ /
# +-------+ FT991a has built in CAT and sound card accessible
# | Radio | ----- via USB socket on rear of radio.
# +-------+
# |
# | USB
# |
# +----|----------------------------------------------------------------------------+
# | | This is your computer. The things in here are virtual components. |
# | | |
# | +-----------+ +-------+ +------------+ Linux kernel |
# | | Direwolf | <---> | Socat | <----> | Kissattach | <------> ax2 interface |
# | +-----------+ +-------+ +------------+ / | |
# | | /--------+ | |
# | AGWPE port / | |
# | available for +---------------+ +------------------+ |
# | use | mheard daemon | | netromd: NET/ROM | |
# | +---------------+ | Daemoon. | |
# | +------------------+ |
# +---------------------------------------------------------------------------------+
# === Configuration ===
# Direwolf executable
DIREWOLF_PATH="/usr/local/bin/direwolf"
PORT_NAME="ft991a"
# The AX.25 port for the radio defined in /etc/ax25/axports. Note that this is not the same thing as
# the kernel AX.25 interfaces, which are named things like ax0, ax1, etc...
AX25_PORT_NAME="ft991a"
# The NET/ROM port for the radio defined in /etc/ax25/nrports. Note that this also is not the same
# thing as the kernel NET/ROM interface like nr0, nr1, etc...
NETROM_PORT_NAME="ft991a-nr"
# The PTY/TTY (whatever it's called) that socat will create for kissattach
TNC_DEV="/dev/radio/ft991a-tnc"
# The direwolf KISS TCP port that socat will connect to
KISS_TCP_PORT=8006
NR_PORT="ft991a-nr"
POLL_INTERVAL=5
# Rigctl stuff
RIGCTLD_HOST="localhost"
RIGCTLD_PORT=4000
POLL_RETRIES=1
POLL_INTERVAL=5
# Frequency ranges (Hz)
# Frequency ranges in Hz for the bands tha radio supports. This is how we know what direwolf config
# to use.
HF_MIN=1800000
HF_MAX=30000000
VHF_MIN=144390000
@@ -37,9 +75,9 @@ UHF_MIN=432000000
UHF_MAX=433000000
# Config files per band
CONFIG_HF="/opt/busnet/direwolf/config/ft991a-hf.conf"
CONFIG_VHF="/opt/busnet/direwolf/config/ft991a-vhf.conf"
CONFIG_UHF="/opt/busnet/direwolf/config/ft991a-uhf.conf"
CONFIG_HF="/opt/argus/direwolf/config/ft991a-hf.conf"
CONFIG_VHF="/opt/argus/direwolf/config/ft991a-vhf.conf"
CONFIG_UHF="/opt/argus/direwolf/config/ft991a-uhf.conf"
# State tracking
CUR_BAND="NONE"
@@ -101,6 +139,8 @@ cleanup_stack()
sudo pkill -f "kissattach ${TNC_DEV}"
sleep 1
# Note that we don't have to do the same for nrattach. nrattach runs and then exits.
if [ -n "$SOCAT_PID" ]; then
sudo kill $SOCAT_PID 2>/dev/null
sleep 1
@@ -111,6 +151,26 @@ cleanup_stack()
wait $DIREWOLF_PID 2>/dev/null
fi
# The file /etc/ax25/nrports mapps the $NETROM_PORT_NAME to a unique callsign. We need to
# figure out what the associated callsign for the netrom port is.
# Example: digirig-nr K0BIT-8 8BITS 235 NET/ROM Port
local NETROM_CALLSIGN=$(grep "^${NETROM_PORT_NAME}" /etc/ax25/nrports | awk '{print $2}')
# Find all NET/ROM interfaces (like nr0, nr1, nr2, etc...) and check their callsigns.
# Shut down any NET/ROM interface with the $NETROM_CALLSIGN attached to it.
if [ -n "$NETROM_CALLSIGN" ]; then
for IFACE in $(ip link show | grep -oE 'nr[0-9]+'); do
# Get the callsign for this interface
local IFACE_CALL=$(ip addr show $IFACE | grep 'netrom' | awk '{print $2}')
if [ "$IFACE_CALL" = "$NETROM_CALLSIGN" ]; then
echo "Bringing down NET/ROM interface ${IFACE} ..."
sudo ip link set $IFACE down
return
fi
done
fi
echo "FT991a AX.25 ${CUR_BAND} stack is now shut down."
CUR_BAND="NONE"
DIREWOLF_PID=""
@@ -136,30 +196,30 @@ start_stack()
echo "Starting $BAND stack..."
# Start Direwolf
echo "Starting Direwolf..."
# Start Direwolf in background
$DIREWOLF_PATH -t 0 -X 1 -c "$CONFIG" &
DIREWOLF_PID=$!
sleep 2
# NOTE: If Direwolf encounters an error, it will just WAIT something like 15 seconds before
# exiting. It does not fail fast. That means that the following check will pass, but then
# we go to move on to create socat which will fail. I don't have a good solution for this
# case just yet, but I'm recording this note here.
if ! kill -0 $DIREWOLF_PID 2>/dev/null; then
echo "FATAL: Direwolf failed to start" >&2
echo "Direwolf failed to start" >&2
DIREWOLF_PID=""
return 1
fi
echo "Direwolf started (PID: $DIREWOLF_PID)"
# Start socat PTY bridge
echo "Starting socat PTY bridge..."
sleep 3
sudo socat PTY,raw,echo=0,link=$TNC_DEV TCP4:127.0.0.1:$KISS_TCP_PORT &
SOCAT_PID=$!
sleep 1
if ! kill -0 $SOCAT_PID 2>/dev/null; then
echo "FATAL: socat failed to start" >&2
echo "socat failed to start" >&2
cleanup_stack
return 1
fi
echo "socat started (PID: $SOCAT_PID)"
# Wait for PTY device to exist
local WAIT_COUNT=0
@@ -169,7 +229,7 @@ start_stack()
done
if [ ! -L $TNC_DEV ]; then
echo "FATAL: PTY device $TNC_DEV not created" >&2
echo "PTY device $TNC_DEV not created" >&2
cleanup_stack
return 1
fi
@@ -177,7 +237,7 @@ start_stack()
# Kissattach connects the PTY device created by socat to the kernel.
echo "Running kissattach..."
sudo kissattach $TNC_DEV $PORT_NAME
sudo kissattach $TNC_DEV $AX25_PORT_NAME
if [ $? -ne 0 ]; then
echo "FATAL: kissattach failed" >&2
cleanup_stack
@@ -189,27 +249,43 @@ start_stack()
echo "Setting kissparms..."
case $BAND in
HF)
sudo kissparms -p $PORT_NAME -c 1 -t 350
sudo kissparms -p $AX25_PORT_NAME -c 1 -t 350
echo "Set kissparams for HF: window=1, txdelay=350ms"
;;
VHF)
sudo kissparms -p $PORT_NAME -c 3 -t 200
sudo kissparms -p $AX25_PORT_NAME -c 3 -t 200
echo "Set kissparams for VHF: window=3, txdelay=200ms"
;;
UHF)
sudo kissparms -p $PORT_NAME -c 3 -t 150
sudo kissparms -p $AX25_PORT_NAME -c 3 -t 150
echo "Set kissparams for UHF: window=3, txdelay=150ms"
;;
esac
# We must run the nrattach command to create a kernel NET/ROM interface.
sudo nrattach $NETROM_PORT_NAME
if [ $? -ne 0 ]; then
echo "nrattach failed."
cleanup_stack
return 1
fi
# Restart mheardd daemon. If mheardd was started before ax1, ax2 or other AX.25 kernel interface
# was created, and now we receive AX.25 packets on this new interface we just created, mheardd
# just throws errors about a new, unknown interface. Ugh.
if systemctl is-active --quiet mheardd.service; then
if sudo systemctl is-enabled --quiet mheardd.service; then
echo "Restarting mheardd..."
sudo systemctl restart mheardd.service
fi
# If netromd is not running, restart it. Otherwise, leave it alone.
if sudo ystemctl is-enabled --quiet netromd.service; then
if ! sudo systemctl is-active --quiet netromd.service; then
echo "Re-starting netromd..."
sudo systemctl start netromd.service
fi
fi
CUR_BAND="$BAND"
echo "FT991a AX.25 ${CUR_BAND} stack is online."
echo ""
@@ -241,9 +317,16 @@ echo ""
# either one.
trap handle_signal SIGINT SIGTERM
# Check if Direwolf exists
if [ ! -f $DIREWOLF_PATH ]; then
echo "FATAL: Direwolf executable not found at $DIREWOLF_PATH" >&2
# Load required kernel modules
echo "Loading kernel modules..."
sudo modprobe ax25
if [ $? -ne 0 ]; then
echo "Failed to load ax25 kernel module"
exit 1
fi
sudo modprobe netrom
if [ $? -ne 0 ]; then
echo "Failed to load netrom kernel module"
exit 1
fi
@@ -1,123 +0,0 @@
#!/bin/bash
# @author Craig McDaniel
#
# This script runs Direwolf, socat and kissatach. It starts them in that order and makes sure they
# all stay running and work together. If one crashes, this script stops everything. This effectively
# sets up a AX.25 interface on a Linux computer using the Linux AX.25 stack.
#
# It's designed to be uses with systemd for things like auto start, auto re-start.
#
#
# RF signals! _ RF signals!
# \ _/
# \ /
# +-------+
# | Radio | ----- RF signals!
# +-------+
# |
# | Analog cable
# |
# +---------+
# | Digirig |
# +---------+
# |
# | USB sound card
# |
# +----|----------------------------------------------------------------------------+
# | | This is your computer. The things in here are virtual components. |
# | | |
# | +-----------+ +-------+ +------------+ Linux kernel |
# | | Direwolf | <---> | Socat | <----> | Kissattach | <------> ax0 interface |
# | +-----------+ +-------+ +------------+ / | |
# | | /--------+ | |
# | AGWPE port / | |
# | available for +---------------+ +------------------+ |
# | use | mheard daemon | | netromd: NET/ROM | |
# | +---------------+ | Daemoon. | |
# | +------------------+ |
# +---------------------------------------------------------------------------------+
# Configuration
DIREWOLF_CMD="/usr/local/bin/direwolf -t 0 -X 1 -c /opt/busnet/direwolf/config/digirig.conf"
SOCAT_CMD="/usr/bin/socat PTY,raw,echo=0,link=/dev/radio/digirig-tnc TCP4:127.0.0.1:8001"
KISSATTACH_CMD="/usr/sbin/kissattach /dev/radio/digirig-tnc digirig"
KISSPARAMS_CMD="kissparms -c 1 -p digirig"
cleanup() {
echo
echo "Exit signal detected. Cleaning up..."
trap - SIGTERM # prevent recursion
kill 0
exit
}
# Trap exit signals
trap cleanup SIGINT SIGTERM
echo
echo "======================================================================"
echo "Starting Direwolf..."
echo "======================================================================"
echo
$DIREWOLF_CMD &
DIREWOLF_PID=$!
sleep 1
if ! kill -0 $DIREWOLF_PID; then
echo "Direwolf failed to start."
exit 1
fi
# Wait 3 seconds for Direwolf to initialize, then start socat.
# socat creates a full duplex/bidirectional pipe between a serial port /dev/radio/digirig-tnc and
# Direwolf's KISS TCP interface, which is running on localhost:8001. The kissattach program needs
# a serial port to work, and this is how we accomplish connecting kissattach to direwolf.
sleep 3
echo
echo "======================================================================"
echo "Starting socat..."
echo "======================================================================"
echo
sudo $SOCAT_CMD &
SOCAT_PID=$!
if ! kill -0 $SOCAT_PID; then
echo "Socat failed to start."
kill $DIREWOLF_PID
exit 1
fi
# Run kissatach to create an AX.25 network interface on the system.
#
# Kissattach annoyingly forks itself in the background, so we can't directly monitor the process.
# We rely on the control-group feature of systemd to kill all child processes for us, even if
# they fork!
#
# Kissattach cannot use KISS over TCP directly to Direwolf. It expects a serial terminal to exist,
# which is why we use socat to create the pipe between Direwolf's KISS TCP port and this serial
# device.
sleep 1
echo
echo "======================================================================"
echo "Starting kissattach and setting kissparams..."
echo "======================================================================"
echo
sudo $KISSATTACH_CMD
if [ $? -ne 0 ]; then
echo "kissattach failed to start."
cleanup
fi
# We have to set something on the AX.25 interface that Direwolf requires.
sudo $KISSPARAMS_CMD
echo
echo "All processes started. Monitoring via wait..."
# 'wait -n' waits for ANY background process to exit.
# It returns as soon as Direwolf or Socat dies.
wait -n
# If we get here, one of the processes died.
cleanup
@@ -1,22 +0,0 @@
[Unit]
Description=Initialize NET/ROM Interfaces at Boot
Documentation=man:nrattach(8)
After=network.target
Before=digirig-direwolf.service ft991a-direwolf.service mheardd.service netromd.service
ConditionPathExists=/etc/ax25/nrports
[Service]
Type=oneshot
RemainAfterExit=yes
# Create NET/ROM interfaces for each port in nrports
# nrattach finds first free device: digirig-nr gets nr0, ft991a-nr gets nr1
ExecStart=/usr/sbin/nrattach digirig-nr
ExecStart=/usr/sbin/nrattach ft991a-nr
# On stop/shutdown, bring down the interfaces cleanly
ExecStop=/usr/sbin/ifconfig nr0 down
ExecStop=/usr/sbin/ifconfig nr1 down
[Install]
WantedBy=multi-user.target