Issue 169 - Fix AGW protocol 'Y' command.

This commit is contained in:
wb2osz 2019-01-06 11:45:14 -05:00
parent e1a4716857
commit 41a85d87a7
5 changed files with 233 additions and 31 deletions

View File

@ -887,6 +887,7 @@ static ax25_dlsm_t *get_link_handle (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LE
// //
// dl_connect_request // dl_connect_request
// dl_disconnect_request // dl_disconnect_request
// dl_outstanding_frames_request - (mine) Ask about outgoing queue for a link.
// dl_data_request - send connected data // dl_data_request - send connected data
// dl_unit_data_request - not implemented. APRS & KISS bypass this // dl_unit_data_request - not implemented. APRS & KISS bypass this
// dl_flow_off - not implemented. Not in AGW API. // dl_flow_off - not implemented. Not in AGW API.
@ -1505,6 +1506,80 @@ void dl_unregister_callsign (dlq_item_t *E)
/*------------------------------------------------------------------------------
*
* Name: dl_outstanding_frames_request
*
* Purpose: Client app wants to know how many frames are still on their way
* to other station. This is handy for flow control. We would like
* to keep the pipeline filled sufficiently to take advantage of a
* large window size (MAXFRAMES). It is also good to know that the
* the last packet sent was actually received before we commence
* the disconnect.
*
* Inputs: E - Event from the queue.
* The caller will free it.
*
* Outputs: This gets back to the AGW server which sends the 'Y' reply.
*
* Description: This is the sum of:
* - Incoming connected data, from application still in the queue.
* - I frames which have been transmitted but not yet acknowleged.
*
*------------------------------------------------------------------------------*/
void dl_outstanding_frames_request (dlq_item_t *E)
{
ax25_dlsm_t *S;
int ok_to_create = 0; // must exist already.
if (s_debug_client_app) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("dl_outstanding_frames_request ( to %s )\n", E->addrs[PEERCALL]);
}
S = get_link_handle (E->addrs, E->num_addr, E->chan, E->client, ok_to_create);
if (S == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't get outstanding frames for %s -> %s, chan %d\n", E->addrs[OWNCALL], E->addrs[PEERCALL], E->chan);
server_outstanding_frames_reply (E->chan, E->client, E->addrs[OWNCALL], E->addrs[PEERCALL], 0);
return;
}
// Add up these
//
// cdata_t *i_frame_queue; // Connected data from client which has not been transmitted yet.
// // Linked list.
// // The name is misleading because these are just blocks of
// // data, not "I frames" at this point. The name comes from
// // the protocol specification.
//
// cdata_t *txdata_by_ns[128]; // Data which has already been transmitted.
// // Indexed by N(S) in case it gets lost and needs to be sent again.
// // Cleared out when we get ACK for it.
int count1 = 0;
cdata_t *incoming;
for (incoming = S->i_frame_queue; incoming != NULL; incoming = incoming->next) {
count1++;
}
int count2 = 0;
int k;
for (k = 0; k < S->modulo; k++) {
if (S->txdata_by_ns[k] != NULL) {
count2++;
}
}
server_outstanding_frames_reply (S->chan, S->client, S->addrs[OWNCALL], S->addrs[PEERCALL], count1 + count2);
} // end dl_outstanding_frames_request
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* *
* Name: dl_client_cleanup * Name: dl_client_cleanup

66
dlq.c
View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2014, 2015, 2016 John Langner, WB2OSZ // Copyright (C) 2014, 2015, 2016, 2018 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -553,7 +553,69 @@ void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int
append_to_queue (pnew); append_to_queue (pnew);
} /* end dlq_connect_request */ } /* end dlq_disconnect_request */
/*-------------------------------------------------------------------
*
* Name: dlq_outstanding_frames_request
*
* Purpose: Client application wants to know number of outstanding information
* frames supplied, supplied by the client, that have not yet been
* delivered to the remote station.
*
* Inputs: addrs - Source (owncall), destination (peercall)
*
* num_addr - Number of addresses. Should be 2.
* If more they will be ignored.
*
* chan - Channel, 0 is first.
*
* client - Client application instance. We could have multiple
* applications, all on the same channel, connecting
* to different stations. We need to know which one
* should get the results.
*
* Outputs: Request is appended to queue for processing by
* the data link state machine.
*
* Description: The data link state machine will count up all information frames
* for the given source(mycall) / destination(remote) / channel link.
* A 'Y' response will be sent back to the client application.
*
*--------------------------------------------------------------------*/
void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client)
{
struct dlq_item_s *pnew;
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("dlq_outstanding_frames_request (...)\n");
#endif
assert (chan >= 0 && chan < MAX_CHANS);
/* Allocate a new queue item. */
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
s_new_count++;
pnew->type = DLQ_OUTSTANDING_FRAMES_REQUEST;
pnew->chan = chan;
memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
pnew->num_addr = num_addr;
pnew->client = client;
/* Put it into queue. */
append_to_queue (pnew);
} /* end dlq_outstanding_frames_request */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------

4
dlq.h
View File

@ -35,7 +35,7 @@ typedef struct cdata_s {
/* Types of things that can be in queue. */ /* Types of things that can be in queue. */
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t; typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_OUTSTANDING_FRAMES_REQUEST, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t;
/* A queue item. */ /* A queue item. */
@ -108,6 +108,8 @@ void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num
void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client); void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client);
void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client);
void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len); void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len);
void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client); void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);

117
server.c
View File

@ -136,11 +136,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#ifdef __OpenBSD__
#include <errno.h> #include <errno.h>
#else
#include <sys/errno.h>
#endif
#endif #endif
#include <unistd.h> #include <unistd.h>
@ -1121,6 +1117,50 @@ void server_rec_conn_data (int chan, int client, char *remote_call, char *own_ca
} /* end server_rec_conn_data */ } /* end server_rec_conn_data */
/*-------------------------------------------------------------------
*
* Name: server_outstanding_frames_reply
*
* Purpose: Send 'Y' Outstanding frames for connected data to the application.
*
* Inputs: chan - Which radio channel.
*
* client - Which one of potentially several clients.
*
* own_call - Callsign[-ssid] of my end.
*
* remote_call - Callsign[-ssid] of remote station.
*
* count - Number of frames sent from the application but
* not yet received by the other station.
*
*--------------------------------------------------------------------*/
void server_outstanding_frames_reply (int chan, int client, char *own_call, char *remote_call, int count)
{
struct {
struct agwpe_s hdr;
int count_NETLE;
} reply;
memset (&reply.hdr, 0, sizeof(reply.hdr));
reply.hdr.portx = chan;
reply.hdr.datakind = 'Y';
strlcpy (reply.hdr.call_from, own_call, sizeof(reply.hdr.call_from));
strlcpy (reply.hdr.call_to, remote_call, sizeof(reply.hdr.call_to));
reply.hdr.data_len_NETLE = host2netle(4);
reply.count_NETLE = host2netle(count);
send_to_client (client, &reply);
} /* end server_outstanding_frames_reply */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
* Name: read_from_socket * Name: read_from_socket
@ -1220,8 +1260,9 @@ static THREAD_F cmd_listen_thread (void *arg)
struct { struct {
struct agwpe_s hdr; /* Command header. */ struct agwpe_s hdr; /* Command header. */
char data[512]; /* Additional data used by some commands. */ char data[AX25_MAX_PACKET_LEN]; /* Additional data used by some commands. */
/* Maximum for 'V': 1 + 8*10 + 256 */ /* Maximum for 'V': 1 + 8*10 + 256 */
/* Maximum for 'D': Info part length + 1 */
} cmd; } cmd;
int client = (int)(long)arg; int client = (int)(long)arg;
@ -1864,6 +1905,7 @@ static THREAD_F cmd_listen_thread (void *arg)
int n = 0; int n = 0;
if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) { if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) {
// Count both normal and expedited in transmit queue for given channel.
n = tq_count (cmd.hdr.portx, -1, "", "", 0); n = tq_count (cmd.hdr.portx, -1, "", "", 0);
} }
reply.data_NETLE = host2netle(n); reply.data_NETLE = host2netle(n);
@ -1874,34 +1916,53 @@ static THREAD_F cmd_listen_thread (void *arg)
case 'Y': /* How Many Outstanding frames wait for tx for a particular station */ case 'Y': /* How Many Outstanding frames wait for tx for a particular station */
/* Number of frames sitting in transmit queue for given channel, */ // This is different than the above 'y' because this refers to a specific
/* source (optional) and destination addresses. */ // link in connected mode.
// This would be useful for a couple different purposes.
// When sending bulk data, we want to keep a fair amount queued up to take
// advantage of large window sizes (MAXFRAME, EMAXFRAME). On the other
// hand we don't want to get TOO far ahead when transferring a large file.
// Before disconnecting from another station, it would be good to know
// that it actually received the last message we sent. For this reason,
// I think it would be good for this to include information frames that were
// transmitted but not yet acknowleged.
// You could say that a particular frame is still waiting to be sent even
// if was already sent because it could be sent again if lost previously.
// The documentation is inconsistent about the address order.
// One place says "callfrom" is my callsign and "callto" is the other guy.
// That would make sense. We are asking about frames going to the other guy.
// But another place says it depends on who initiated the connection.
//
// "If we started the connection CallFrom=US and CallTo=THEM
// If the other end started the connection CallFrom=THEM and CallTo=US"
//
// The response description says nothing about the order; it just mentions two addresses.
// If you are writing a client or server application, the order would
// be clear but right here it could be either case.
//
// Another version of the documentation mentioned the source address being optional.
//
// The only way to get this information is from inside the data link state machine.
// We will send a request to it and the result coming out will be used to
// send the reply back to the client application.
{ {
char source[AX25_MAX_ADDR_LEN];
char dest[AX25_MAX_ADDR_LEN];
struct { char callsigns[2][AX25_MAX_ADDR_LEN];
struct agwpe_s hdr; const int num_calls = 2;
int data_NETLE; // Little endian order.
} reply;
strlcpy (source, cmd.hdr.call_from, sizeof(source)); strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
strlcpy (dest, cmd.hdr.call_to, sizeof(dest)); strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
memset (&reply, 0, sizeof(reply)); // Issue 169. Proper implementation for 'Y'.
reply.hdr.portx = cmd.hdr.portx; /* Reply with same port number, addresses. */ dlq_outstanding_frames_request (callsigns, num_calls, cmd.hdr.portx, client);
reply.hdr.datakind = 'Y';
strlcpy (reply.hdr.call_from, source, sizeof(reply.hdr.call_from));
strlcpy (reply.hdr.call_to, dest, sizeof(reply.hdr.call_to));
reply.hdr.data_len_NETLE = host2netle(4);
int n = 0;
if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) {
n = tq_count (cmd.hdr.portx, -1, source, dest, 0);
}
reply.data_NETLE = host2netle(n);
send_to_client (client, &reply);
} }
break; break;

View File

@ -24,5 +24,7 @@ void server_link_terminated (int chan, int client, char *remote_call, char *own_
void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len); void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len);
void server_outstanding_frames_reply (int chan, int client, char *own_call, char *remote_call, int count);
/* end server.h */ /* end server.h */