From 8683ddcbd642af1eb93e16479c9dd6d7c28a22bb Mon Sep 17 00:00:00 2001 From: wb2osz Date: Mon, 4 Jan 2021 19:43:00 -0500 Subject: [PATCH] Allow multiple TCP KISS ports and option for single radio channel. --- src/config.c | 85 +++++++- src/config.h | 24 ++- src/direwolf.c | 14 +- src/kiss.c | 8 +- src/kiss.h | 5 +- src/kiss_frame.c | 99 +++++---- src/kiss_frame.h | 48 ++++- src/kissnet.c | 522 ++++++++++++++++++++++++++++++----------------- src/kissnet.h | 10 +- src/kissserial.c | 6 +- src/kissserial.h | 6 +- src/kissutil.c | 7 +- src/tt_user.c | 6 +- 13 files changed, 575 insertions(+), 265 deletions(-) diff --git a/src/config.c b/src/config.c index 52d50f0..df2061d 100644 --- a/src/config.c +++ b/src/config.c @@ -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; ikiss_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); + } + } } /* diff --git a/src/config.h b/src/config.h index 41ba90c..3155343 100644 --- a/src/config.h +++ b/src/config.h @@ -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 */ diff --git a/src/direwolf.c b/src/direwolf.c index 188b087..9b2589e 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -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); } } diff --git a/src/kiss.c b/src/kiss.c index 76cb322..a246d80 100644 --- a/src/kiss.c +++ b/src/kiss.c @@ -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. */ diff --git a/src/kiss.h b/src/kiss.h index 4787440..1dc40da 100644 --- a/src/kiss.h +++ b/src/kiss.h @@ -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); diff --git a/src/kiss_frame.c b/src/kiss_frame.c index c087635..2804567 100644 --- a/src/kiss_frame.c +++ b/src/kiss_frame.c @@ -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 { diff --git a/src/kiss_frame.h b/src/kiss_frame.h index 0bddf3e..12985bd 100644 --- a/src/kiss_frame.h +++ b/src/kiss_frame.h @@ -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 */ diff --git a/src/kissnet.c b/src/kissnet.c index 7b4f1c4..b7b86f3 100644 --- a/src/kissnet.c +++ b/src/kissnet.c @@ -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; clientclient_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__ diff --git a/src/kissnet.h b/src/kissnet.h index ac00752..469e4e6 100644 --- a/src/kissnet.h +++ b/src/kissnet.h @@ -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 */ diff --git a/src/kissserial.c b/src/kissserial.c index 9f185ef..faa2970 100644 --- a/src/kissserial.c +++ b/src/kissserial.c @@ -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__ diff --git a/src/kissserial.h b/src/kissserial.h index a677529..44fb3c3 100644 --- a/src/kissserial.h +++ b/src/kissserial.h @@ -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); diff --git a/src/kissutil.c b/src/kissutil.c index 026a6ea..257b10f 100644 --- a/src/kissutil.c +++ b/src/kissutil.c @@ -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; diff --git a/src/tt_user.c b/src/tt_user.c index c071326..f6e0727 100644 --- a/src/tt_user.c +++ b/src/tt_user.c @@ -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) {