mirror of https://github.com/wb2osz/direwolf.git
353 lines
9.8 KiB
C
353 lines
9.8 KiB
C
//
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
//
|
|
// Copyright (C) 2016, 2017 John Langner, WB2OSZ
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Name: cdigipeater.c
|
|
*
|
|
* Purpose: Act as an digital repeater for connected AX.25 mode.
|
|
* Similar digipeater.c is for APRS.
|
|
*
|
|
*
|
|
* Description: Decide whether the specified packet should
|
|
* be digipeated. Put my callsign in the digipeater field used.
|
|
*
|
|
* APRS and connected mode were two split into two
|
|
* separate files. Yes, there is duplicate code but they
|
|
* are significantly different and I thought it would be
|
|
* too confusing to munge them together.
|
|
*
|
|
* References: The Ax.25 protocol barely mentions digipeaters and
|
|
* and doesn't describe how they should work.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
#define CDIGIPEATER_C
|
|
|
|
#include "direwolf.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h> /* for isdigit, isupper */
|
|
#include "regex.h"
|
|
#include <unistd.h>
|
|
|
|
#include "ax25_pad.h"
|
|
#include "cdigipeater.h"
|
|
#include "textcolor.h"
|
|
#include "tq.h"
|
|
#include "pfilter.h"
|
|
|
|
|
|
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
|
int has_alias, regex_t *alias, int to_chan, char *cfilter_str);
|
|
|
|
|
|
/*
|
|
* Keep pointer to configuration options.
|
|
* Set by cdigipeater_init and used later.
|
|
*/
|
|
|
|
|
|
static struct audio_s *save_audio_config_p;
|
|
static struct cdigi_config_s *save_cdigi_config_p;
|
|
|
|
|
|
/*
|
|
* Maintain count of packets digipeated for each combination of from/to channel.
|
|
*/
|
|
|
|
static int cdigi_count[MAX_CHANS][MAX_CHANS];
|
|
|
|
int cdigipeater_get_count (int from_chan, int to_chan) {
|
|
return (cdigi_count[from_chan][to_chan]);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: cdigipeater_init
|
|
*
|
|
* Purpose: Initialize with stuff from configuration file.
|
|
*
|
|
* Inputs: p_audio_config - Configuration for audio channels.
|
|
*
|
|
* p_cdigi_config - Connected Digipeater configuration details.
|
|
*
|
|
* Outputs: Save pointers to configuration for later use.
|
|
*
|
|
* Description: Called once at application startup time.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void cdigipeater_init (struct audio_s *p_audio_config, struct cdigi_config_s *p_cdigi_config)
|
|
{
|
|
save_audio_config_p = p_audio_config;
|
|
save_cdigi_config_p = p_cdigi_config;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: cdigipeater
|
|
*
|
|
* Purpose: Re-transmit packet if it matches the rules.
|
|
*
|
|
* Inputs: chan - Radio channel where it was received.
|
|
*
|
|
* pp - Packet object.
|
|
*
|
|
* Returns: None.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void cdigipeater (int from_chan, packet_t pp)
|
|
{
|
|
int to_chan;
|
|
|
|
// Connected mode is allowed only for channels with internal modem.
|
|
// It probably wouldn't matter for digipeating but let's keep that rule simple and consistent.
|
|
|
|
if ( from_chan < 0 || from_chan >= MAX_CHANS || save_audio_config_p->chan_medium[from_chan] != MEDIUM_RADIO) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("cdigipeater: Did not expect to receive on invalid channel %d.\n", from_chan);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* First pass: Look at packets being digipeated to same channel.
|
|
*
|
|
* There was a reason for two passes for APRS.
|
|
* Might not have a benefit here.
|
|
*/
|
|
|
|
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
|
if (save_cdigi_config_p->enabled[from_chan][to_chan]) {
|
|
if (to_chan == from_chan) {
|
|
packet_t result;
|
|
|
|
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
|
save_audio_config_p->achan[to_chan].mycall,
|
|
save_cdigi_config_p->has_alias[from_chan][to_chan],
|
|
&(save_cdigi_config_p->alias[from_chan][to_chan]), to_chan,
|
|
save_cdigi_config_p->cfilter_str[from_chan][to_chan]);
|
|
if (result != NULL) {
|
|
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
|
cdigi_count[from_chan][to_chan]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Second pass: Look at packets being digipeated to different channel.
|
|
*/
|
|
|
|
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
|
if (save_cdigi_config_p->enabled[from_chan][to_chan]) {
|
|
if (to_chan != from_chan) {
|
|
packet_t result;
|
|
|
|
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
|
save_audio_config_p->achan[to_chan].mycall,
|
|
save_cdigi_config_p->has_alias[from_chan][to_chan],
|
|
&(save_cdigi_config_p->alias[from_chan][to_chan]), to_chan,
|
|
save_cdigi_config_p->cfilter_str[from_chan][to_chan]);
|
|
if (result != NULL) {
|
|
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
|
cdigi_count[from_chan][to_chan]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* end cdigipeater */
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: cdigipeat_match
|
|
*
|
|
* Purpose: A simple digipeater for connected mode AX.25.
|
|
*
|
|
* Input: pp - Pointer to a packet object.
|
|
*
|
|
* mycall_rec - Call of my station, with optional SSID,
|
|
* associated with the radio channel where the
|
|
* packet was received.
|
|
*
|
|
* mycall_xmit - Call of my station, with optional SSID,
|
|
* associated with the radio channel where the
|
|
* packet is to be transmitted. Could be the same as
|
|
* mycall_rec or different.
|
|
*
|
|
* has_alias - True if we have an alias.
|
|
*
|
|
* alias - Optional compiled pattern for my station aliases.
|
|
* Do NOT attempt to use this if 'has_alias' is false.
|
|
*
|
|
* to_chan - Channel number that we are transmitting to.
|
|
*
|
|
* cfilter_str - Filter expression string for the from/to channel pair or NULL.
|
|
* Note that only a subset of the APRS filters are applicable here.
|
|
*
|
|
* Returns: Packet object for transmission or NULL.
|
|
* The original packet is not modified. The caller is responsible for freeing it.
|
|
* We make a copy and return that modified copy!
|
|
* This is very important because we could digipeat from one channel to many.
|
|
*
|
|
* Description: The packet will be digipeated if the next unused digipeater
|
|
* field matches one of the following:
|
|
*
|
|
* - mycall_rec
|
|
* - alias list
|
|
*
|
|
* APRS digipeating drops duplicates within 30 seconds but we don't do that here.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
|
|
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
|
int has_alias, regex_t *alias, int to_chan, char *cfilter_str)
|
|
{
|
|
int r;
|
|
char repeater[AX25_MAX_ADDR_LEN];
|
|
int err;
|
|
char err_msg[100];
|
|
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("cdigipeat_match (from_chan=%d, pp=%p, mycall_rec=%s, mycall_xmit=%s, has_alias=%d, alias=%p, to_chan=%d, cfilter_str=%s\n",
|
|
from_chan, pp, mycall_rec, mycall_xmit, has_alias, alias, to_chan, cfilter_str);
|
|
#endif
|
|
|
|
/*
|
|
* First check if filtering has been configured.
|
|
* Note that we have three different config file filter commands:
|
|
*
|
|
* FILTER - APRS digipeating and IGate client side.
|
|
* Originally this was the only one.
|
|
* Should we change it to AFILTER to make it clearer?
|
|
* CFILTER - Similar for connected moded digipeater.
|
|
* IGFILTER - APRS-IS (IGate) server side - completely different.
|
|
* Confusing with similar name but much different idea.
|
|
* Maybe this should be renamed to SUBSCRIBE or something like that.
|
|
*
|
|
* Logically this should come later, after an address/alias match.
|
|
* But here we only have to do it once.
|
|
*/
|
|
|
|
if (cfilter_str != NULL) {
|
|
|
|
if (pfilter(from_chan, to_chan, cfilter_str, pp, 0) != 1) {
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Find the first repeater station which doesn't have "has been repeated" set.
|
|
*
|
|
* r = index of the address position in the frame.
|
|
*/
|
|
r = ax25_get_first_not_repeated(pp);
|
|
|
|
if (r < AX25_REPEATER_1) {
|
|
return (NULL); // Nothing to do.
|
|
}
|
|
|
|
ax25_get_addr_with_ssid(pp, r, repeater);
|
|
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("First unused digipeater is %s\n", repeater);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* First check for explicit use of my call.
|
|
* Note that receive and transmit channels could have different callsigns.
|
|
*/
|
|
|
|
if (strcmp(repeater, mycall_rec) == 0) {
|
|
packet_t result;
|
|
|
|
result = ax25_dup (pp);
|
|
assert (result != NULL);
|
|
|
|
/* If using multiple radio channels, they could have different calls. */
|
|
|
|
ax25_set_addr (result, r, mycall_xmit);
|
|
ax25_set_h (result, r);
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* If we have an alias match, substitute MYCALL.
|
|
*/
|
|
if (has_alias) {
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("Checking %s for alias match.\n", repeater);
|
|
#endif
|
|
err = regexec(alias,repeater,0,NULL,0);
|
|
if (err == 0) {
|
|
packet_t result;
|
|
|
|
result = ax25_dup (pp);
|
|
assert (result != NULL);
|
|
|
|
ax25_set_addr (result, r, mycall_xmit);
|
|
ax25_set_h (result, r);
|
|
return (result);
|
|
}
|
|
else if (err != REG_NOMATCH) {
|
|
regerror(err, alias, err_msg, sizeof(err_msg));
|
|
text_color_set (DW_COLOR_ERROR);
|
|
dw_printf ("%s\n", err_msg);
|
|
}
|
|
}
|
|
else {
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("No alias was specified.\n");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Don't repeat it if we get here.
|
|
*/
|
|
return (NULL);
|
|
|
|
} /* end cdigipeat_match */
|
|
|
|
|
|
|
|
/* end cdigipeater.c */
|