Allow multiple TCP KISS ports and option for single radio channel.

This commit is contained in:
wb2osz 2021-01-04 19:43:00 -05:00
parent 81447ed49f
commit 8683ddcbd6
13 changed files with 575 additions and 265 deletions

View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 John Langner, WB2OSZ
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2021 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
@ -852,7 +852,14 @@ void config_init (char *fname, struct audio_s *p_audio_config,
memset (p_misc_config, 0, sizeof(struct misc_config_s));
p_misc_config->agwpe_port = DEFAULT_AGWPE_PORT;
p_misc_config->kiss_port = DEFAULT_KISS_PORT;
for (int i=0; i<MAX_KISS_TCP_PORTS; i++) {
p_misc_config->kiss_port[i] = 0; // entry not used.
p_misc_config->kiss_chan[i] = -1;
}
p_misc_config->kiss_port[0] = DEFAULT_KISS_PORT;
p_misc_config->kiss_chan[0] = -1; // all channels.
p_misc_config->enable_kiss_pt = 0; /* -p option */
p_misc_config->kiss_copy = 0;
@ -4477,27 +4484,89 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
/*
* KISSPORT - Port number for KISS over IP.
* KISSPORT port [ chan ] - Port number for KISS over IP.
*/
// Previously we allowed only a single TCP port for KISS.
// An increasing number of people want to run multiple radios.
// Unfortunately, most applications don't know how to deal with multi-radio TNCs.
// They ignore the channel on receive and always transmit to channel 0.
// Running multiple instances of direwolf is a work-around but this leads to
// more complex configuration and we lose the cross-channel digipeating capability.
// In release 1.7 we add a new feature to assign a single radio channel to a TCP port.
// e.g.
// KISSPORT 8001 # default, all channels. Radio channel = KISS channel.
//
// KISSPORT 7000 0 # Only radio channel 0 for receive.
// # Transmit to radio channel 0, ignoring KISS channel.
//
// KISSPORT 7001 1 # Only radio channel 1 for receive. KISS channel set to 0.
// # Transmit to radio channel 1, ignoring KISS channel.
// FIXME
else if (strcasecmp(t, "KISSPORT") == 0) {
int n;
int tcp_port = 0;
int chan = -1; // optional. default to all if not specified.
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Missing port number for KISSPORT command.\n", line);
dw_printf ("Line %d: Missing TCP port number for KISSPORT command.\n", line);
continue;
}
n = atoi(t);
if ((n >= MIN_IP_PORT_NUMBER && n <= MAX_IP_PORT_NUMBER) || n == 0) {
p_misc_config->kiss_port = n;
tcp_port = n;
}
else {
p_misc_config->kiss_port = DEFAULT_KISS_PORT;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid port number for KISS TCPIP Socket Interface. Using %d.\n",
line, p_misc_config->kiss_port);
dw_printf ("Line %d: Invalid TCP port number for KISS TCPIP Socket Interface.\n", line);
dw_printf ("Use something in the range of %d to %d.\n", MIN_IP_PORT_NUMBER, MAX_IP_PORT_NUMBER);
continue;
}
t = split(NULL,0);
if (t != NULL) {
chan = atoi(t);
if (chan < 0 || chan >= MAX_CHANS) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid channel %d for KISSPORT command. Must be in range 0 thru %d.\n", line, chan, MAX_CHANS-1);
continue;
}
}
// "KISSPORT 0" is used to remove the default entry.
if (tcp_port == 0) {
p_misc_config->kiss_port[0] = 0; // Should all be wiped out?
}
else {
// Try to find an empty slot.
// A duplicate TCP port number will overwrite the previous value.
int slot = -1;
for (int i = 0; i < MAX_KISS_TCP_PORTS && slot == -1; i++) {
if (p_misc_config->kiss_port[i] == tcp_port) {
slot = i;
if ( ! (slot == 0 && tcp_port == DEFAULT_KISS_PORT)) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Warning: Duplicate TCP port %d will overwrite previous value.\n", line, tcp_port);
}
}
else if (p_misc_config->kiss_port[i] == 0) {
slot = i;
}
}
if (slot >= 0) {
p_misc_config->kiss_port[slot] = tcp_port;
p_misc_config->kiss_chan[slot] = chan;
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Too many KISSPORT commands.\n", line);
}
}
}
/*

View File

@ -30,11 +30,31 @@ enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV };
#define MAX_BEACONS 30
#define MAX_KISS_TCP_PORTS (MAX_CHANS+1)
struct misc_config_s {
int agwpe_port; /* Port number for the "AGW TCPIP Socket Interface" */
int kiss_port; /* Port number for the "TCP KISS" protocol. */
int agwpe_port; /* TCP Port number for the "AGW TCPIP Socket Interface" */
// Previously we allowed only a single TCP port for KISS.
// An increasing number of people want to run multiple radios.
// Unfortunately, most applications don't know how to deal with multi-radio TNCs.
// They ignore the channel on receive and always transmit to channel 0.
// Running multiple instances of direwolf is a work-around but this leads to
// more complex configuration and we lose the cross-channel digipeating capability.
// In release 1.7 we add a new feature to assign a single radio channel to a TCP port.
// e.g.
// KISSPORT 8001 # default, all channels. Radio channel = KISS channel.
//
// KISSPORT 7000 0 # Only radio channel 0 for receive.
// # Transmit to radio channel 0, ignoring KISS channel.
//
// KISSPORT 7001 1 # Only radio channel 1 for receive. KISS channel set to 0.
// # Transmit to radio channel 1, ignoring KISS channel.
int kiss_port[MAX_KISS_TCP_PORTS]; /* TCP Port number for the "TCP KISS" protocol. */
int kiss_chan[MAX_KISS_TCP_PORTS]; /* Radio Channel number for this port or -1 for all. */
int kiss_copy; /* Data from network KISS client is copied to all others. */
int enable_kiss_pt; /* Enable pseudo terminal for KISS. */
/* Want this to be off by default because it hangs */

View File

@ -1419,10 +1419,10 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
flen = ax25_pack(pp, fbuf);
server_send_rec_packet (chan, pp, fbuf, flen); // AGW net protocol
kissnet_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS TCP
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS serial port
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1); // KISS pseudo terminal
server_send_rec_packet (chan, pp, fbuf, flen); // AGW net protocol
kissnet_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1); // KISS TCP
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1); // KISS serial port
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1); // KISS pseudo terminal
if (A_opt_ais_to_obj && strlen(ais_obj_packet) != 0) {
packet_t ao_pp = ax25_from_text (ais_obj_packet, 1);
@ -1431,9 +1431,9 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
int ao_flen = ax25_pack(ao_pp, ao_fbuf);
server_send_rec_packet (chan, ao_pp, ao_fbuf, ao_flen);
kissnet_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, -1);
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, -1);
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, -1);
kissnet_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, NULL, -1);
kissserial_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, NULL, -1);
kisspt_send_rec_packet (chan, KISS_CMD_DATA_FRAME, ao_fbuf, ao_flen, NULL, -1);
ax25_delete (ao_pp);
}
}

View File

@ -94,7 +94,7 @@ void kisspt_set_debug (int n)
return;
}
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *kps, int client)
{
return;
}
@ -374,7 +374,7 @@ static int kisspt_open_pt (void)
* flen - Length of raw received frame not including the FCS
* or -1 for a text string.
*
* client - Not used for pseudo terminal.
* kps, client - Not used for pseudo terminal.
* Here so that 3 related functions all have
* the same parameter list.
*
@ -385,7 +385,7 @@ static int kisspt_open_pt (void)
*--------------------------------------------------------------------*/
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *kps, int client)
{
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
int kiss_len;
@ -591,7 +591,7 @@ static void * kisspt_listen_thread (void *arg)
while (1) {
ch = kisspt_get();
kiss_rec_byte (&kf, ch, kisspt_debug, -1, kisspt_send_rec_packet);
kiss_rec_byte (&kf, ch, kisspt_debug, NULL, -1, kisspt_send_rec_packet);
}
return (void *) 0; /* Unreachable but avoids compiler warning. */

View File

@ -10,12 +10,13 @@
#include "config.h"
#include "kiss_frame.h" // for struct kissport_status_s
void kisspt_init (struct misc_config_s *misc_config);
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client);
void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen,
struct kissport_status_s *notused1, int notused2);
void kisspt_set_debug (int n);

View File

@ -139,7 +139,8 @@ void text_color_set (dw_color_t c)
#ifndef DECAMAIN
#ifndef KISSUTIL
static void kiss_set_hardware (int chan, char *command, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
static void kiss_set_hardware (int chan, char *command, int debug, struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient));
#endif
#endif
@ -340,6 +341,8 @@ int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
* Inputs: kf - Current state of building a frame.
* ch - A byte from the input stream.
* debug - Activates debug output.
* kps - KISS TCP port status block.
* NULL for pseudo terminal and serial port.
* client - Client app number for TCP KISS.
* Ignored for pseudo termal and serial port.
* sendfun - Function to send something to the client application.
@ -377,7 +380,9 @@ int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out)
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug,
struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient))
{
//dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch);
@ -420,10 +425,10 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 ||
strcasecmp("reset\r", (char*)(kf->noise)) == 0) {
// first 2 parameters don't matter when length is -1 indicating text.
(*sendfun) (0, 0, (unsigned char *)"\xc0\xc0", -1, client);
(*sendfun) (0, 0, (unsigned char *)"\xc0\xc0", -1, kps, client);
}
else {
(*sendfun) (0, 0, (unsigned char *)"\r\ncmd:", -1, client);
(*sendfun) (0, 0, (unsigned char *)"\r\ncmd:", -1, kps, client);
}
#endif
kf->noise_len = 0;
@ -469,7 +474,7 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
hex_dump (unwrapped+1, ulen-1);
}
kiss_process_msg (unwrapped, ulen, debug, client, sendfun);
kiss_process_msg (unwrapped, ulen, debug, kps, client, sendfun);
kf->state = KS_SEARCHING;
return;
@ -506,6 +511,9 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
*
* debug - Debug option is selected.
*
* kps - Used only for TCP KISS.
* Should be NULL for pseudo terminal and serial port.
*
* client - Client app number for TCP KISS.
* Should be -1 for pseudo termal and serial port.
*
@ -519,28 +527,40 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, v
// Some functions are only for the TNC end.
// Other functions are suitble for both TNC and client app.
// This is used only by the TNC sided.
// This is used only by the TNC side.
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *kps, int client))
{
int port; // Should rename to chan because that's what we use everywhere else.
int chan;
int cmd;
packet_t pp;
alevel_t alevel;
port = (kiss_msg[0] >> 4) & 0xf;
// New in 1.7:
// We can have KISS TCP ports which convey only a single radio channel.
// This is to allow operation by applications which only know how to talk to single radio TNCs.
if (kps != NULL && kps->chan != -1) {
// Ignore channel from KISS and substitute radio channel for that KISS TCP port.
chan = kps->chan;
}
else {
// Normal case of getting radio channel from the KISS frame.
chan = (kiss_msg[0] >> 4) & 0xf;
}
cmd = kiss_msg[0] & 0xf;
switch (cmd)
{
case KISS_CMD_DATA_FRAME: /* 0 = Data Frame */
if (client >= 0) {
kissnet_copy (kiss_msg, kiss_len, port, cmd, client);
}
// kissnet_copy clobbers first byte but we don't care
// because we have already determined channel and command.
kissnet_copy (kiss_msg, kiss_len, chan, cmd, kps, client);
/* Note July 2017: There is a variant of of KISS, called SMACK, that assumes */
/* a TNC can never have more than 8 ports. http://symek.de/g/smack.html */
/* a TNC can never have more than 8 channels. http://symek.de/g/smack.html */
/* It uses the MSB to indicate that a checksum is added. I wonder if this */
/* is why we sometimes hear about a request to transmit on channel 8. */
/* Should we have a message that asks the user if SMACK is being used, */
@ -580,20 +600,22 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
// Would it make sense to implement SMACK? I don't think so.
// Adding a checksum to the KISS data offers no benefit because it is very reliable.
// It violates the original protocol specification which states that 16 ports (radio channels) are possible.
// It violates the original protocol specification which states that 16 radio channels are possible.
// (Some times the term 'port' is used but I try to use 'channel' all the time because 'port'
// has too many other meanings. Serial port, TCP port, ...)
// SMACK imposes a limit of 8. That limit might have been OK back in 1991 but not now.
// There are people using more than 8 radio channels (using SDR not traditional radios) with direwolf.
/* Verify that the port (channel) number is valid. */
/* Verify that the radio channel number is valid. */
/* Any sort of medium should be OK here. */
if (port < 0 || port >= MAX_CHANS || save_audio_config_p->achan[port].medium == MEDIUM_NONE) {
if (chan < 0 || chan >= MAX_CHANS || save_audio_config_p->achan[chan].medium == MEDIUM_NONE) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Invalid transmit channel %d from KISS client app.\n", port);
dw_printf ("Invalid transmit channel %d from KISS client app.\n", chan);
dw_printf ("\n");
dw_printf ("Are you using AX.25 for Linux? It might be trying to use a modified\n");
dw_printf ("version of KISS which uses the port (channel) field differently than the\n");
dw_printf ("version of KISS which uses the channel field differently than the\n");
dw_printf ("original KISS protocol specification. The solution might be to use\n");
dw_printf ("a command like \"kissparms -c 1 -p radio\" to set CRC none mode.\n");
dw_printf ("Another way of doing this is pre-loading the \"kiss\" kernel module with CRC disabled:\n");
@ -606,7 +628,7 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
}
memset (&alevel, 0xff, sizeof(alevel));
pp = ax25_from_frame (kiss_msg+1, kiss_len-1, alevel);
packet_t pp = ax25_from_frame (kiss_msg+1, kiss_len-1, alevel);
if (pp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Invalid KISS data frame from client app.\n");
@ -621,10 +643,10 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
if (ax25_get_num_repeaters(pp) >= 1 &&
ax25_get_h(pp,AX25_REPEATER_1)) {
tq_append (port, TQ_PRIO_0_HI, pp);
tq_append (chan, TQ_PRIO_0_HI, pp);
}
else {
tq_append (port, TQ_PRIO_1_LO, pp);
tq_append (chan, TQ_PRIO_1_LO, pp);
}
}
break;
@ -637,13 +659,13 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
return;
}
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set TXDELAY = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
dw_printf ("KISS protocol set TXDELAY = %d (*10mS units = %d mS), chan %d\n", kiss_msg[1], kiss_msg[1] * 10, chan);
if (kiss_msg[1] < 4 || kiss_msg[1] > 100) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Are you sure you want such an extreme value for TXDELAY?\n");
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
}
xmit_set_txdelay (port, kiss_msg[1]);
xmit_set_txdelay (chan, kiss_msg[1]);
break;
case KISS_CMD_PERSISTENCE: /* 2 = Persistence */
@ -654,13 +676,13 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
return;
}
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set Persistence = %d, port %d\n", kiss_msg[1], port);
dw_printf ("KISS protocol set Persistence = %d, chan %d\n", kiss_msg[1], chan);
if (kiss_msg[1] < 5 || kiss_msg[1] > 250) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Are you sure you want such an extreme value for PERSIST?\n");
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
}
xmit_set_persist (port, kiss_msg[1]);
xmit_set_persist (chan, kiss_msg[1]);
break;
case KISS_CMD_SLOTTIME: /* 3 = SlotTime */
@ -671,13 +693,13 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
return;
}
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set SlotTime = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
dw_printf ("KISS protocol set SlotTime = %d (*10mS units = %d mS), chan %d\n", kiss_msg[1], kiss_msg[1] * 10, chan);
if (kiss_msg[1] < 2 || kiss_msg[1] > 50) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Are you sure you want such an extreme value for SLOTTIME?\n");
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
}
xmit_set_slottime (port, kiss_msg[1]);
xmit_set_slottime (chan, kiss_msg[1]);
break;
case KISS_CMD_TXTAIL: /* 4 = TXtail */
@ -688,13 +710,13 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
return;
}
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set TXtail = %d (*10mS units = %d mS), port %d\n", kiss_msg[1], kiss_msg[1] * 10, port);
dw_printf ("KISS protocol set TXtail = %d (*10mS units = %d mS), chan %d\n", kiss_msg[1], kiss_msg[1] * 10, chan);
if (kiss_msg[1] < 2) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Setting TXTAIL so low is asking for trouble. You probably don't want to do this.\n");
dw_printf ("See \"Radio Channel - Transmit Timing\" section of User Guide for explanation.\n");
}
xmit_set_txtail (port, kiss_msg[1]);
xmit_set_txtail (chan, kiss_msg[1]);
break;
case KISS_CMD_FULLDUPLEX: /* 5 = FullDuplex */
@ -705,8 +727,8 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
return;
}
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set FullDuplex = %d, port %d\n", kiss_msg[1], port);
xmit_set_fulldup (port, kiss_msg[1]);
dw_printf ("KISS protocol set FullDuplex = %d, chan %d\n", kiss_msg[1], chan);
xmit_set_fulldup (chan, kiss_msg[1]);
break;
case KISS_CMD_SET_HARDWARE: /* 6 = TNC specific */
@ -718,11 +740,11 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
}
kiss_msg[kiss_len] = '\0';
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol set hardware \"%s\", port %d\n", (char*)(kiss_msg+1), port);
kiss_set_hardware (port, (char*)(kiss_msg+1), debug, client, sendfun);
dw_printf ("KISS protocol set hardware \"%s\", chan %d\n", (char*)(kiss_msg+1), chan);
kiss_set_hardware (chan, (char*)(kiss_msg+1), debug, kps, client, sendfun);
break;
case KISS_CMD_END_KISS: /* 15 = End KISS mode, port should be 15. */
case KISS_CMD_END_KISS: /* 15 = End KISS mode, channel should be 15. */
/* Ignore it. */
text_color_set(DW_COLOR_INFO);
dw_printf ("KISS protocol end KISS mode - Ignored.\n");
@ -842,7 +864,8 @@ void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int cli
#ifndef KISSUTIL
static void kiss_set_hardware (int chan, char *command, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
static void kiss_set_hardware (int chan, char *command, int debug, struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient))
{
char *param;
char response[100];
@ -860,7 +883,7 @@ static void kiss_set_hardware (int chan, char *command, int debug, int client, v
}
snprintf (response, sizeof(response), "DIREWOLF %d.%d", MAJOR_VERSION, MINOR_VERSION);
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), client);
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), kps, client);
}
else if (strcmp(command, "TXBUF") == 0) { /* TXBUF - Number of bytes in transmit queue. */
@ -872,7 +895,7 @@ static void kiss_set_hardware (int chan, char *command, int debug, int client, v
int n = tq_count (chan, -1, "", "", 1);
snprintf (response, sizeof(response), "TXBUF:%d", n);
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), client);
(*sendfun) (chan, KISS_CMD_SET_HARDWARE, (unsigned char *)response, strlen(response), kps, client);
}
else {

View File

@ -1,6 +1,10 @@
/* kiss_frame.h */
#ifndef KISS_FRAME_H
#define KISS_FRAME_H
#include "audio.h" /* for struct audio_s */
@ -61,6 +65,40 @@ typedef struct kiss_frame_s {
} kiss_frame_t;
// This is used only for TCPKISS but it put in kissnet.h,
// there would be a circular dependecy between the two header files.
// Each KISS TCP port has its own status block.
struct kissport_status_s {
struct kissport_status_s *pnext; // To next in list.
volatile int arg2; // temp for passing second arg into
// kissnet_listen_thread
int tcp_port; // default 8001
int chan; // Radio channel for this tcp port.
// -1 for all.
// The default is a limit of 3 client applications at the same time.
// You can increase the limit by changing the line below.
// A larger number consumes more resources so don't go crazy by making it larger than needed.
#define MAX_NET_CLIENTS 3
int client_sock[MAX_NET_CLIENTS];
/* File descriptor for socket for */
/* communication with client application. */
/* Set to -1 if not connected. */
/* (Don't use SOCKET type because it is unsigned.) */
kiss_frame_t kf[MAX_NET_CLIENTS];
/* Accumulated KISS frame and state of decoder. */
};
#ifndef KISSUTIL
void kiss_frame_init (struct audio_s *pa);
#endif
@ -69,12 +107,18 @@ int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out);
int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out);
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient));
typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int));
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient));
void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len);
#endif // KISS_FRAME_H
/* end kiss_frame.h */

View File

@ -1,7 +1,6 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011-2014, 2015, 2017 John Langner, WB2OSZ
// Copyright (C) 2011-2014, 2015, 2017, 2021 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
@ -87,6 +86,83 @@
*
*---------------------------------------------------------------*/
/*
Separate TCP ports per radio:
An increasing number of people are using multiple radios.
direwolf is capable of handling many radio channels and
provides cross-band repeating, etc.
Maybe a single stereo audio interface is used for 2 radios.
+------------+ tcp 8001, all channels
Radio A -------- | | -------------------------- Application A
| direwolf |
Radio B -------- | | -------------------------- Application B
+------------+ tcp 8001, all channels
The KISS protocol has a 4 bit field for the TNC port (which I prefer to
call channel because port has too many different meanings).
direwolf handles this fine. However, most applications were written assuming
that a TNC could only talk to a single radio. On reception, they ignore the
channel in the KISS frame. For transmit, the channel is always set to 0.
Many people are using the work-around of two separate instances of direwolf.
+------------+ tcp 8001, KISS ch 0
Radio A -------- | direwolf | -------------------------- Application A
+------------+
+------------+ tcp 8002, KISS ch 0
Radio B -------- | direwolf | -------------------------- Application B
+------------+
Or they might be using a single application that knows how to talk to multiple
single port TNCs. But they don't know how to multiplex multiple channels
thru a single KISS stream.
+------------+ tcp 8001, KISS ch 0
Radio A -------- | direwolf | ------------------------
+------------+ \
-- Application
+------------+ tcp 8002, KISS ch 0 /
Radio B -------- | direwolf | ------------------------
+------------+
Using two different instances of direwolf means more complex configuration
and loss of cross-channel digipeating. It is possible to use a stereo
audio interface but some ALSA magic is required to make it look like two
independent virtual mono interfaces.
In version 1.7, we add the capability of multiple KISS TCP ports, each for
a single radio channel. e.g.
KISSPORT 8001 1
KISSPORT 8002 2
Now can use a single instance of direwolf.
+------------+ tcp 8001, KISS ch 0
Radio A -------- | | -------------------------- Application A
| direwolf |
Radio B -------- | | -------------------------- Application B
+------------+ tcp 8002, KISS ch 0
When receiving, the KISS channel is set to 0.
- only radio channel 1 would be sent over tcp port 8001.
- only radio channel 2 would be sent over tcp port 8001.
When transmitting, the KISS channel is ignored.
- frames from tcp port 8001 are transmitted on radio channel 1.
- frames from tcp port 8002 are transmitted on radio channel 2.
Of course, you could also use an application, capable of connecting to
multiple single radio TNCs. Separate TCP ports actually go to the
same direwolf instance.
*/
/*
* Native Windows: Use the Winsock interface.
@ -127,25 +203,6 @@
void hex_dump (unsigned char *p, int len); // This should be in a .h file.
/*
* Early on we allowed one AGW connection and one KISS TCP connection at a time.
* In version 1.1, we allowed multiple concurrent client apps to attach with the AGW network protocol.
* In Version 1.5, we do essentially the same here to allow multiple concurrent KISS TCP clients.
* The default is a limit of 3 client applications at the same time.
* You can increase the limit by changing the line below.
* A larger number consumes more resources so don't go crazy by making it larger than needed.
*/
#define MAX_NET_CLIENTS 3
static int client_sock[MAX_NET_CLIENTS];
/* File descriptor for socket for */
/* communication with client application. */
/* Set to -1 if not connected. */
/* (Don't use SOCKET type because it is unsigned.) */
static kiss_frame_t kf[MAX_NET_CLIENTS];
/* Accumulated KISS frame and state of decoder. */
@ -162,6 +219,12 @@ static THREAD_F kissnet_listen_thread (void *arg);
static struct misc_config_s *s_misc_config_p;
// Each TCP port has its own status block.
// There is a variable number so use a linked list.
static struct kissport_status_s *all_ports = NULL;
static int kiss_debug = 0; /* Print information flowing from and to client. */
void kiss_net_set_debug (int n)
@ -177,11 +240,9 @@ void kiss_net_set_debug (int n)
*
* Purpose: Set up a server to listen for connection requests from
* an application such as Xastir or APRSIS32.
* This is called once from the main program.
*
* Inputs: mc->kiss_port - TCP port for server.
* Main program has default of 8000 but allows
* an alternative to be specified on the command line
*
* 0 means disable. New in version 1.2.
*
* Outputs:
@ -193,8 +254,29 @@ void kiss_net_set_debug (int n)
*
*--------------------------------------------------------------------*/
static void kissnet_init_one (struct kissport_status_s *kps);
void kissnet_init (struct misc_config_s *mc)
{
s_misc_config_p = mc;
for (int i = 0; i < MAX_KISS_TCP_PORTS; i++) {
if (mc->kiss_port[i] != 0) {
struct kissport_status_s *kps = calloc(sizeof(struct kissport_status_s), 1);
kps->tcp_port = mc->kiss_port[i];
kps->chan = mc->kiss_chan[i];
kissnet_init_one (kps);
// Add to list.
kps->pnext = all_ports;
all_ports = kps;
}
}
}
static void kissnet_init_one (struct kissport_status_s *kps)
{
int client;
@ -206,23 +288,22 @@ void kissnet_init (struct misc_config_s *mc)
pthread_t cmd_listen_tid[MAX_NET_CLIENTS];
int e;
#endif
s_misc_config_p = mc;
int kiss_port = mc->kiss_port; /* default 8001 but easily changed. */
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("kissnet_init ( %d )\n", kiss_port);
dw_printf ("kissnet_init ( tcp port %d, radio chan = %d )\n", kps->tcp_port, kps->chan);
#endif
for (client=0; client<MAX_NET_CLIENTS; client++) {
client_sock[client] = -1;
memset (&(kf[client]), 0, sizeof(kf[client]));
kps->client_sock[client] = -1;
memset (&(kps->kf[client]), 0, sizeof(kps->kf[client]));
}
if (kiss_port == 0) {
if (kps->tcp_port == 0) {
text_color_set(DW_COLOR_INFO);
dw_printf ("Disabled KISS network client port.\n");
return;
@ -232,17 +313,18 @@ void kissnet_init (struct misc_config_s *mc)
* This waits for a client to connect and sets client_sock[n].
*/
#if __WIN32__
connect_listen_th = (HANDLE)_beginthreadex (NULL, 0, connect_listen_thread, (void *)(ptrdiff_t)kiss_port, 0, NULL);
connect_listen_th = (HANDLE)_beginthreadex (NULL, 0, connect_listen_thread, (void *)kps, 0, NULL);
if (connect_listen_th == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create KISS socket connect listening thread\n");
dw_printf ("Could not create KISS socket connect listening thread for tcp port %d, radio chan %d\n", kps->tcp_port, kps->chan);
return;
}
#else
e = pthread_create (&connect_listen_tid, NULL, connect_listen_thread, (void *)(ptrdiff_t)kiss_port);
e = pthread_create (&connect_listen_tid, NULL, connect_listen_thread, (void *)kps);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
perror("Could not create KISS socket connect listening thread");
dw_printf ("for tcp port %d, radio chan %d\n", kps->tcp_port, kps->chan);
return;
}
#endif
@ -254,15 +336,17 @@ void kissnet_init (struct misc_config_s *mc)
*/
for (client = 0; client < MAX_NET_CLIENTS; client++) {
kps->arg2 = client;
#if __WIN32__
cmd_listen_th[client] = (HANDLE)_beginthreadex (NULL, 0, kissnet_listen_thread, (void*)(ptrdiff_t)client, 0, NULL);
cmd_listen_th[client] = (HANDLE)_beginthreadex (NULL, 0, kissnet_listen_thread, (void*)kps, 0, NULL);
if (cmd_listen_th[client] == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create KISS command listening thread for client %d\n", client);
return;
}
#else
e = pthread_create (&(cmd_listen_tid[client]), NULL, kissnet_listen_thread, (void *)(ptrdiff_t)client);
e = pthread_create (&(cmd_listen_tid[client]), NULL, kissnet_listen_thread, (void *)kps);
if (e != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not create KISS command listening thread for client %d\n", client);
@ -271,6 +355,18 @@ void kissnet_init (struct misc_config_s *mc)
return;
}
#endif
// Wait for new thread to get content of arg2 before reusing it for the next thread create.
int timer = 0;
while (kps->arg2 >= 0) {
SLEEP_MS(10);
timer++;
if (timer > 100) { // 1 second - thread did not start
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS data listening thread did not start for tcp port %d, client slot %d\n", kps->tcp_port, client);
kps->arg2 = -1; // Keep moving along.
}
}
}
}
@ -281,9 +377,7 @@ void kissnet_init (struct misc_config_s *mc)
*
* Purpose: Wait for a connection request from an application.
*
* Inputs: arg - TCP port for server.
* Main program has default of 8001 but allows
* an alternative to be specified on the command line
* Inputs: arg - KISS port status block.
*
* Outputs: client_sock - File descriptor for communicating with client app.
*
@ -296,20 +390,22 @@ void kissnet_init (struct misc_config_s *mc)
static THREAD_F connect_listen_thread (void *arg)
{
struct kissport_status_s *kps = arg;
#if __WIN32__
struct addrinfo hints;
struct addrinfo *ai = NULL;
int err;
char kiss_port_str[12];
char tcp_port_str[12];
SOCKET listen_sock;
WSADATA wsadata;
snprintf (kiss_port_str, sizeof(kiss_port_str), "%d", (int)(ptrdiff_t)arg);
snprintf (tcp_port_str, sizeof(tcp_port_str), "%d", kps->tcp_port);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DEBUG: kissnet port = %d = '%s'\n", (int)(ptrdiff_t)arg, kiss_port_str);
dw_printf ("DEBUG: kissnet port = %d = '%s'\n", (int)(ptrdiff_t)arg, tcp_port_str);
#endif
err = WSAStartup (MAKEWORD(2,2), &wsadata);
if (err != 0) {
@ -332,7 +428,7 @@ static THREAD_F connect_listen_thread (void *arg)
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
err = getaddrinfo(NULL, kiss_port_str, &hints, &ai);
err = getaddrinfo(NULL, tcp_port_str, &hints, &ai);
if (err != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf("getaddrinfo failed: %d\n", err);
@ -350,14 +446,14 @@ static THREAD_F connect_listen_thread (void *arg)
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("Binding to port %s ... \n", kiss_port_str);
dw_printf("Binding to port %s ... \n", tcp_port_str);
#endif
err = bind( listen_sock, ai->ai_addr, (int)ai->ai_addrlen);
if (err == SOCKET_ERROR) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Bind failed with error: %d\n", WSAGetLastError()); // TODO: provide corresponding text.
dw_printf("Some other application is probably already using port %s.\n", kiss_port_str);
dw_printf("Some other application is probably already using port %s.\n", tcp_port_str);
dw_printf("Try using a different port number with KISSPORT in the configuration file.\n");
freeaddrinfo(ai);
closesocket(listen_sock);
@ -369,7 +465,7 @@ static THREAD_F connect_listen_thread (void *arg)
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("opened KISS socket as fd (%d) on port (%s) for stream i/o\n", listen_sock, kiss_port_str );
dw_printf("opened KISS socket as fd (%d) on port (%s) for stream i/o\n", listen_sock, tcp_port_str );
#endif
while (1) {
@ -379,7 +475,7 @@ static THREAD_F connect_listen_thread (void *arg)
client = -1;
for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
if (client_sock[c] <= 0) {
if (kps->client_sock[c] <= 0) {
client = c;
}
}
@ -397,11 +493,16 @@ static THREAD_F connect_listen_thread (void *arg)
}
text_color_set(DW_COLOR_INFO);
dw_printf("Ready to accept KISS TCP client application %d on port %s ...\n", client, kiss_port_str);
client_sock[client] = accept(listen_sock, NULL, NULL);
if (kps->chan == -1) {
dw_printf("Ready to accept KISS TCP client application %d on port %s ...\n", client, tcp_port_str);
}
else {
dw_printf("Ready to accept KISS TCP client application %d on port %s (radio channel %d) ...\n", client, tcp_port_str, kps->chan);
}
if (client_sock[client] == -1) {
kps->client_sock[client] = accept(listen_sock, NULL, NULL);
if (kps->client_sock[client] == -1) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Accept failed with error: %d\n", WSAGetLastError());
closesocket(listen_sock);
@ -410,10 +511,15 @@ static THREAD_F connect_listen_thread (void *arg)
}
text_color_set(DW_COLOR_INFO);
dw_printf("\nAttached to KISS TCP client application %d ...\n\n", client);
if (kps->chan == -1) {
dw_printf("\nAttached to KISS TCP client application %d on port %s ...\n\n", client, tcp_port_str);
}
else {
dw_printf("\nAttached to KISS TCP client application %d on port %s (radio channel %d) ...\n\n", client, tcp_port_str, kps->chan);
}
// Reset the state and buffer.
memset (&(kf[client]), 0, sizeof(kf[client]));
memset (&(kps->kf[client]), 0, sizeof(kps->kf[client]));
}
else {
SLEEP_SEC(1); /* wait then check again if more clients allowed. */
@ -421,12 +527,11 @@ static THREAD_F connect_listen_thread (void *arg)
}
#else /* End of Windows case, now Linux. */
#else /* End of Windows case, now Linux / Unix / Mac OSX. */
struct sockaddr_in sockaddr; /* Internet socket address stuct */
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
int kiss_port = (int)(ptrdiff_t)arg;
int listen_sock;
int bcopt = 1;
@ -446,19 +551,19 @@ static THREAD_F connect_listen_thread (void *arg)
setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bcopt, 4);
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(kiss_port);
sockaddr.sin_port = htons(kps->tcp_port);
sockaddr.sin_family = AF_INET;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf("Binding to port %d ... \n", kiss_port);
dw_printf("Binding to port %d ... \n", kps->tcp_port);
#endif
if (bind(listen_sock,(struct sockaddr*)&sockaddr,sizeof(sockaddr)) == -1) {
text_color_set(DW_COLOR_ERROR);
dw_printf("Bind failed with error: %d\n", errno);
dw_printf("%s\n", strerror(errno));
dw_printf("Some other application is probably already using port %d.\n", kiss_port);
dw_printf("Some other application is probably already using port %d.\n", kps->tcp_port);
dw_printf("Try using a different port number with KISSPORT in the configuration file.\n");
return (NULL);
}
@ -477,7 +582,7 @@ static THREAD_F connect_listen_thread (void *arg)
client = -1;
for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
if (client_sock[c] <= 0) {
if (kps->client_sock[c] <= 0) {
client = c;
}
}
@ -492,15 +597,25 @@ static THREAD_F connect_listen_thread (void *arg)
}
text_color_set(DW_COLOR_INFO);
dw_printf("Ready to accept KISS TCP client application %d on port %d ...\n", client, kiss_port);
client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
if (kps->chan == -1) {
dw_printf("Ready to accept KISS TCP client application %d on port %s ...\n", client, tcp_port_str);
}
else {
dw_printf("Ready to accept KISS TCP client application %d on port %s (radio channel %d) ...\n", client, tcp_port_str, kps->chan);
}
kps->client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
text_color_set(DW_COLOR_INFO);
dw_printf("\nAttached to KISS TCP client application %d...\n\n", client);
if (kps->chan == -1) {
dw_printf("\nAttached to KISS TCP client application %d on port %s ...\n\n", client, tcp_port_str);
}
else {
dw_printf("\nAttached to KISS TCP client application %d on port %s (radio channel %d) ...\n\n", client, tcp_port_str, kps->chan);
}
// Reset the state and buffer.
memset (&(kf[client]), 0, sizeof(kf[client]));
memset (&(kps->kf[client]), 0, sizeof(kps->kf[client]));
}
else {
SLEEP_SEC(1); /* wait then check again if more clients allowed. */
@ -517,7 +632,7 @@ static THREAD_F connect_listen_thread (void *arg)
*
* Name: kissnet_send_rec_packet
*
* Purpose: Send a received packet to the client app.
* Purpose: Send a packet, received over the radio, to the client app.
*
* Inputs: chan - Channel number where packet was received.
* 0 = first, 1 = second if any.
@ -537,119 +652,131 @@ static THREAD_F connect_listen_thread (void *arg)
* it is using a traditional TNC and tries to put it
* into KISS mode.
*
* tcpclient - It is possible to have more than client attached
* onlykps - KISS TCP status block pointer or NULL.
*
* onlyclient - It is possible to have more than client attached
* at the same time with TCP KISS.
* When a frame is received from the radio we want it
* to go to all of the clients. In this case specify -1.
* Starting with version 1.7 we can have multiple TCP ports.
* When a frame is received from the radio we normally want it
* to go to all of the clients.
* In this case specify NULL for onlykps and -1 tcp client.
* When responding to a command from the client, we want
* to send only to that one client app. In this case
* use the value 0 .. MAX_NET_CLIENTS-1.
* a non NULL kps and onlyclient >= 0.
*
* Description: Send message to client(s) if connected.
* Disconnect from client, and notify user, if any error.
*
*--------------------------------------------------------------------*/
void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int tcpclient)
void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen,
struct kissport_status_s *onlykps, int onlyclient)
{
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
int kiss_len;
int err;
int first, last, client;
// Something received over the radio would be sent to all attached clients.
// Something received over the radio would normally be sent to all attached clients.
// However, there are times we want to send a response only to a particular client.
// In the case of a serial port or pseudo terminal, there is only one potential client.
// so the response would be sent to only one place. A new parameter has been added for this.
if (tcpclient >= 0 && tcpclient < MAX_NET_CLIENTS) {
first = tcpclient;
last = tcpclient;
}
else if (tcpclient == -1) {
first = 0;
last = MAX_NET_CLIENTS - 1;
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS TCP: Internal error, kissnet_send_rec_packet, tcpclient = %d.\n", tcpclient);
return;
}
for (struct kissport_status_s *kps = all_ports; kps != NULL; kps = kps->pnext) {
if (onlykps == NULL || kps == onlykps) {
for (client = first; client <= last; client++) {
for (int client = 0; client < MAX_NET_CLIENTS; client++) {
if (client_sock[client] != -1) {
if (onlyclient == -1 || client == onlyclient) {
if (flen < 0) {
if (kps->client_sock[client] != -1) {
if (flen < 0) {
// A client app might think it is attached to a traditional TNC.
// It might try sending commands over and over again trying to get the TNC into KISS mode.
// We recognize this attempt and send it something to keep it happy.
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS TCP: Something unexpected from client application.\n");
dw_printf ("Is client app treating this like an old TNC with command mode?\n");
dw_printf ("This can be caused by the application sending commands to put a\n");
dw_printf ("traditional TNC into KISS mode. It is usually a harmless warning.\n");
dw_printf ("For best results, configure for a KISS-only TNC to avoid this.\n");
dw_printf ("In the case of APRSISCE/32, use \"Simply(KISS)\" rather than \"KISS.\"\n");
text_color_set(DW_COLOR_ERROR);
dw_printf ("KISS TCP: Something unexpected from client application.\n");
dw_printf ("Is client app treating this like an old TNC with command mode?\n");
dw_printf ("This can be caused by the application sending commands to put a\n");
dw_printf ("traditional TNC into KISS mode. It is usually a harmless warning.\n");
dw_printf ("For best results, configure for a KISS-only TNC to avoid this.\n");
dw_printf ("In the case of APRSISCE/32, use \"Simply(KISS)\" rather than \"KISS.\"\n");
flen = strlen((char*)fbuf);
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
}
strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
kiss_len = strlen((char *)kiss_buff);
}
else {
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
flen = strlen((char*)fbuf);
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
}
strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
kiss_len = strlen((char *)kiss_buff);
}
else {
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
assert (flen < (int)(sizeof(stemp)));
assert (flen < (int)(sizeof(stemp)));
stemp[0] = (chan << 4) | kiss_cmd;
memcpy (stemp+1, fbuf, flen);
// New in 1.7.
// Previously all channels were sent to everyone.
// We now have tcp ports which carry only a single radio channel.
// The application will see KISS channel 0 regardless of the radio channel.
if (kiss_debug >= 2) {
/* AX.25 frame with the CRC removed. */
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n");
dw_printf ("Packet content before adding KISS framing and any escapes:\n");
hex_dump (fbuf, flen);
}
if (kps->chan == -1) {
// Normal case, all channels.
stemp[0] = (chan << 4) | kiss_cmd;
}
else if (kps->chan == chan) {
// Single radio channel for this port. Application sees 0.
stemp[0] = (0 << 4) | kiss_cmd;
}
else {
// Skip it.
continue;
}
kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
memcpy (stemp+1, fbuf, flen);
/* This has the escapes and the surrounding FENDs. */
if (kiss_debug >= 2) {
/* AX.25 frame with the CRC removed. */
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n");
dw_printf ("Packet content before adding KISS framing and any escapes:\n");
hex_dump (fbuf, flen);
}
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
}
}
kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
/* This has the escapes and the surrounding FENDs. */
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
}
}
#if __WIN32__
err = SOCK_SEND(client_sock[client], (char*)kiss_buff, kiss_len);
if (err == SOCKET_ERROR)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to KISS client %d application. Closing connection.\n\n", WSAGetLastError(), client);
closesocket (client_sock[client]);
client_sock[client] = -1;
WSACleanup();
}
err = SOCK_SEND(kps->client_sock[client], (char*)kiss_buff, kiss_len);
if (err == SOCKET_ERROR) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to KISS client application %d on port %d. Closing connection.\n\n", WSAGetLastError(), client, kps->tcp_port);
closesocket (kps->client_sock[client]);
kps->client_sock[client] = -1;
WSACleanup();
}
#else
err = SOCK_SEND (client_sock[client], kiss_buff, kiss_len);
if (err <= 0)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending message to KISS client %d application. Closing connection.\n\n", client);
close (client_sock[client]);
client_sock[client] = -1;
}
err = SOCK_SEND (kps->client_sock[client], kiss_buff, kiss_len);
if (err <= 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to KISS client application %d on port %d. Closing connection.\n\n", WSAGetLastError(), client, kps->tcp_port);
close (kps->client_sock[client]);
kps->client_sock[client] = -1;
}
#endif
}
}
} // frame length >= 0
} // if all clients or the one specifie
} // for each client on the tcp port
} // if all ports or the one specified
} // for each tcp port
} /* end kissnet_send_rec_packet */
@ -661,14 +788,14 @@ void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int f
* Purpose: Send data from one network KISS client to all others.
*
* Inputs: in_msg - KISS frame data without the framing or escapes.
* The first byte is channel (port) and command (should be data).
* The first byte is channel and command (should be data).
* Caller no longer cares this byte. We will clobber it here.
*
* in_len - Number of bytes in above.
*
* chan - Channel. Redundant because it is also in first byte of kiss_msg.
* Not currently used.
* chan - Channel. Use this instead of first byte of in_msg.
*
* cmd - KISS command nybble. Redundant because it is in first byte.
* cmd - KISS command nybble.
* Should be 0 because I'm expecting this only for data.
*
* from_client - Number of network (TCP) client instance.
@ -687,52 +814,66 @@ void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int f
*--------------------------------------------------------------------*/
void kissnet_copy (unsigned char *in_msg, int in_len, int chan, int cmd, int from_client)
void kissnet_copy (unsigned char *in_msg, int in_len, int chan, int cmd, struct kissport_status_s *from_kps, int from_client)
{
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN];
int kiss_len;
int err;
int send_to;
(void) chan;
(void) cmd;
if (s_misc_config_p->kiss_copy) {
for (send_to = 0; send_to < MAX_NET_CLIENTS; send_to++) {
for (struct kissport_status_s *kps = all_ports; kps != NULL; kps = kps->pnext) {
if (send_to != from_client && client_sock[send_to] != -1) {
for (int client = 0; client < MAX_NET_CLIENTS; client++) {
kiss_len = kiss_encapsulate (in_msg, in_len, kiss_buff);
if ( ! ( kps == from_kps && client == from_client ) ) { // To all but origin.
/* This has the escapes and the surrounding FENDs. */
if (kps->client_sock[client] != -1) {
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
}
if (kps-> chan == -1 || kps->chan == chan) {
// Two different cases here:
// - The TCP port allows all channels, or
// - The TCP port allows only one channel. In this case set KISS channel to 0.
if (kps->chan == -1) {
in_msg[0] = (chan << 4) | cmd;
}
else {
in_msg[0] = 0 | cmd; // set channel to zero.
}
int kiss_len = kiss_encapsulate (in_msg, in_len, kiss_buff);
/* This has the escapes and the surrounding FENDs. */
if (kiss_debug) {
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
}
#if __WIN32__
err = SOCK_SEND(client_sock[send_to], (char*)kiss_buff, kiss_len);
if (err == SOCKET_ERROR)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d copying message to KISS client %d application. Closing connection.\n\n", WSAGetLastError(), send_to);
closesocket (client_sock[send_to]);
client_sock[send_to] = -1;
WSACleanup();
}
err = SOCK_SEND(kps->client_sock[client], (char*)kiss_buff, kiss_len);
if (err == SOCKET_ERROR) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d copying message to KISS TCP port %d client %d application. Closing connection.\n\n", WSAGetLastError(), kps->tcp_port, client);
closesocket (kps->client_sock[client]);
kps->client_sock[client] = -1;
WSACleanup();
}
#else
err = SOCK_SEND (client_sock[send_to], kiss_buff, kiss_len);
if (err <= 0)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError copying message to KISS client %d application. Closing connection.\n\n", send_to);
close (client_sock[send_to]);
client_sock[send_to] = -1;
}
err = SOCK_SEND (kps->client_sock[client], kiss_buff, kiss_len);
if (err <= 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError copying message to KISS TCP port %d client %d application. Closing connection.\n\n", kps->tcp_port, client);
close (kps->client_sock[client]);
kps->client_sock[client] = -1;
}
#endif
} // if origin and destination different.
} // loop over all KISS network clients.
} // Channel is allowed on this port.
} // socket is open
} // if origin and destination different.
} // loop over all KISS network clients for one port.
} // loop over all KISS TCP ports
} // Feature enabled.
} /* end kissnet_copy */
@ -759,20 +900,19 @@ void kissnet_copy (unsigned char *in_msg, int in_len, int chan, int cmd, int fro
/* Return one byte (value 0 - 255) */
static int kiss_get (int client)
static int kiss_get (struct kissport_status_s *kps, int client)
{
unsigned char ch;
int n;
while (1) {
while (client_sock[client] <= 0) {
while (kps->client_sock[client] <= 0) {
SLEEP_SEC(1); /* Not connected. Try again later. */
}
/* Just get one byte at a time. */
n = SOCK_RECV (client_sock[client], (char *)(&ch), 1);
unsigned char ch;
int n = SOCK_RECV (kps->client_sock[client], (char *)(&ch), 1);
if (n == 1) {
#if DEBUG9
@ -792,13 +932,13 @@ static int kiss_get (int client)
}
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nKISS client application %d has gone away.\n\n", client);
dw_printf ("\nKISS client application %d on TCP port %d has gone away.\n\n", client, kps->tcp_port);
#if __WIN32__
closesocket (client_sock[client]);
closesocket (kps->client_sock[client]);
#else
close (client_sock[client]);
close (kps->client_sock[client]);
#endif
client_sock[client] = -1;
kps->client_sock[client] = -1;
}
}
@ -806,17 +946,19 @@ static int kiss_get (int client)
static THREAD_F kissnet_listen_thread (void *arg)
{
unsigned char ch;
struct kissport_status_s *kps = arg;
int client = (int)(ptrdiff_t)arg;
int client = kps->arg2;
assert (client >= 0 && client < MAX_NET_CLIENTS);
kps->arg2 = -1; // Indicates thread is running so
// arg2 can be reused for the next one.
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("kissnet_listen_thread ( client = %d, socket fd = %d )\n", client, client_sock[client]);
dw_printf ("kissnet_listen_thread ( tcp_port = %d, client = %d, socket fd = %d )\n", kps->tcp_port, client, kps->client_sock[client]);
#endif
assert (client >= 0 && client < MAX_NET_CLIENTS);
// So why is kissnet_send_rec_packet mentioned here for incoming from the client app?
@ -832,8 +974,8 @@ static THREAD_F kissnet_listen_thread (void *arg)
while (1) {
ch = kiss_get(client);
kiss_rec_byte (&(kf[client]), ch, kiss_debug, client, kissnet_send_rec_packet);
unsigned char ch = kiss_get(kps, client);
kiss_rec_byte (&(kps->kf[client]), ch, kiss_debug, kps, client, kissnet_send_rec_packet);
}
#if __WIN32__

View File

@ -3,21 +3,27 @@
* Name: kissnet.h
*/
#ifndef KISSNET_H
#define KISSNET_H
#include "ax25_pad.h" /* for packet_t */
#include "config.h"
#include "kiss_frame.h"
void kissnet_init (struct misc_config_s *misc_config);
void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client);
void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen,
struct kissport_status_s *onlykps, int onlyclient);
void kiss_net_set_debug (int n);
void kissnet_copy (unsigned char *kiss_msg, int kiss_len, int chan, int cmd, int from_client);
void kissnet_copy (unsigned char *kiss_msg, int kiss_len, int chan, int cmd, struct kissport_status_s *from_kps, int from_client);
#endif // KISSNET_H
/* end kissnet.h */

View File

@ -261,6 +261,7 @@ void kissserial_init (struct misc_config_s *mc)
* flen - Length of raw received frame not including the FCS
* or -1 for a text string.
*
* kps
* client - Not used for serial port version.
* Here so that 3 related functions all have
* the same parameter list.
@ -272,7 +273,8 @@ void kissserial_init (struct misc_config_s *mc)
*--------------------------------------------------------------------*/
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen,
struct kissport_status_s *notused1, int notused2)
{
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
int kiss_len;
@ -488,7 +490,7 @@ static THREAD_F kissserial_listen_thread (void *arg)
while (1) {
ch = kissserial_get();
kiss_rec_byte (&kf, ch, kissserial_debug, -1, kissserial_send_rec_packet);
kiss_rec_byte (&kf, ch, kissserial_debug, NULL, -1, kissserial_send_rec_packet);
}
#if __WIN32__

View File

@ -8,12 +8,14 @@
#include "config.h"
#include "kiss_frame.h"
void kissserial_init (struct misc_config_s *misc_config);
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client);
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen,
struct kissport_status_s *notused1, int notused2);
void kissserial_set_debug (int n);

View File

@ -663,7 +663,7 @@ static THREAD_F tnc_listen_net (void *arg)
// on the assumption it was being used in only one direction.
// Not worried enough about it to do anything at this time.
kiss_rec_byte (&kstate, data[j], verbose, client, NULL);
kiss_rec_byte (&kstate, data[j], verbose, NULL, client, NULL);
}
}
@ -726,7 +726,7 @@ static THREAD_F tnc_listen_serial (void *arg)
// Feed in one byte at a time.
// kiss_process_msg is called when a complete frame has been accumulated.
kiss_rec_byte (&kstate, ch, verbose, client, NULL);
kiss_rec_byte (&kstate, ch, verbose, NULL, client, NULL);
}
} /* end tnc_listen_serial */
@ -754,7 +754,8 @@ static THREAD_F tnc_listen_serial (void *arg)
*
*-----------------------------------------------------------------*/
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, int client, void (*sendfun)(int,int,unsigned char*,int,int))
void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct kissport_status_s *kps, int client,
void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient))
{
int chan;
int cmd;

View File

@ -897,9 +897,9 @@ static void xmit_object_report (int i, int first_time)
flen = ax25_pack(pp, fbuf);
server_send_rec_packet (save_tt_config_p->obj_recv_chan, pp, fbuf, flen);
kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1);
kissserial_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1);
kisspt_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, -1);
kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1);
kissserial_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1);
kisspt_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1);
}
if (first_time && save_tt_config_p->obj_send_to_ig) {