Implement patty_ax25_if_driver

Changes:

    * Implement patty_ax25_if_driver type, providing a vtable with
      pointers to methods implementing an AX.25 interface PHY

    * Implement patty_ax25_if_name() to return a pointer to the name
      string of an AX.25 interface

    * Decouple patty_kiss_tnc from src/if.c using patty_ax25_if_driver

    * Remove port input/output arguments from patty_kiss_tnc_send()
      and patty_kiss_tnc_recv(), respectively; use 0 as the default,
      but keep the port argument in patty_kiss_frame_send()

    * Implement patty_ax25_if_fd() to return file descriptor backing a
      PHY; use this rather than patty_kiss_tnc_fd() in src/server.c to
      further decouple interfaces from their implementation

    * Remove 'enum patty_ax25_if_type' type; refactor constructor
      patty_ax25_if_new() to no longer take this as an argument, but
      rather a patty_ax25_if_driver to use to instantiate the PHY
      with the information pointer passed

    * Break out patty_kiss_tnc code from src/kiss.c into src/tnc.c,
      leaving only patty_kiss_frame_send() in the original; this is
      needed to prevent a cyclic dependency within patty_ax25_sock and
      other areas

    * Rename patty_bin_if_config() to patty_bin_if_create(); a separate
      patty_bin_if_config() will likely be created later as necessary
      to change an existing interface

    * Split PHY-specific interface configuration code into separate
      delegates in bin/if.c

    * Implement usage of patty_error to better capture internal error
      states not currently representable with semantic error numbers
      while configuring and instantiating PHYs

    * Pass patty_error object to patty_bin_kiss_config() to receive
      detailed error information
This commit is contained in:
XANTRONIX Development 2020-09-13 16:56:10 -04:00 committed by XANTRONIX Industrial
parent 356b25fd94
commit cb33d799ff
16 changed files with 669 additions and 586 deletions

View file

@ -35,7 +35,7 @@ static void usage(int argc, char **argv, const char *message, ...) {
}
int main(int argc, char **argv) {
patty_client *client;
patty_client *client = NULL;
struct option opts[] = {
{ NULL, 0, NULL, 0 }
@ -44,8 +44,7 @@ int main(int argc, char **argv) {
uint8_t buf[4096];
ssize_t readlen;
patty_bin_kiss_data data;
patty_kiss_tnc_info *info = &data.info;
patty_kiss_tnc_info info;
patty_kiss_tnc *raw;
struct stat st;
@ -66,7 +65,7 @@ int main(int argc, char **argv) {
goto error_stat;
}
memset(&data, '\0', sizeof(data));
memset(&info, '\0', sizeof(info));
if ((st.st_mode & S_IFMT) == S_IFSOCK) {
patty_client_setsockopt_if ifreq;
@ -75,7 +74,7 @@ int main(int argc, char **argv) {
usage(argc, argv, "No interface name provided");
}
info->flags = PATTY_KISS_TNC_FD;
info.flags = PATTY_KISS_TNC_FD;
if ((client = patty_client_new(argv[1])) == NULL) {
fprintf(stderr, "%s: %s: %s: %s\n",
@ -84,7 +83,7 @@ int main(int argc, char **argv) {
goto error_client_new;
}
if ((info->fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) {
if ((info.fd = patty_client_socket(client, PATTY_AX25_PROTO_NONE, PATTY_AX25_SOCK_RAW)) < 0) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], "patty_client_socket()", strerror(errno));
@ -95,29 +94,31 @@ int main(int argc, char **argv) {
ifreq.state = PATTY_AX25_SOCK_PROMISC;
if (patty_client_setsockopt(client, info->fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) {
if (patty_client_setsockopt(client, info.fd, PATTY_AX25_SOCK_IF, &ifreq, sizeof(ifreq)) < 0) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], "patty_client_setsockopt()", strerror(errno));
goto error_client_setsockopt;
}
} else {
if (patty_bin_kiss_config(&data, argc - 1, argv + 1) < 0) {
patty_error e;
if (patty_bin_kiss_config(argc - 1, argv + 1, &info, &e) < 0) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], argv[1], data.err);
argv[0], argv[1], patty_error_string(&e));
goto error_kiss_config;
}
}
if ((raw = patty_kiss_tnc_new(info)) == NULL) {
if ((raw = patty_kiss_tnc_new(&info)) == NULL) {
fprintf(stderr, "%s: fd %d: %s: %s\n",
argv[0], info->fd, "patty_kiss_tnc_new()", strerror(errno));
argv[0], info.fd, "patty_kiss_tnc_new()", strerror(errno));
goto error_kiss_tnc_new;
}
while ((readlen = patty_kiss_tnc_recv(raw, buf, sizeof(buf), NULL)) > 0) {
while ((readlen = patty_kiss_tnc_recv(raw, buf, sizeof(buf))) > 0) {
ssize_t decoded,
offset = 0;
@ -178,7 +179,7 @@ error_ax25_frame_decode_address:
patty_kiss_tnc_destroy(raw);
if ((st.st_mode & S_IFMT) == S_IFSOCK) {
patty_client_close(client, info->fd);
patty_client_close(client, info.fd);
patty_client_destroy(client);
}
@ -192,10 +193,10 @@ error_kiss_tnc_new:
error_kiss_config:
error_client_setsockopt:
error_client_socket:
if ((st.st_mode & S_IFMT) == S_IFSOCK) patty_client_close(client, info->fd);
if (client) patty_client_close(client, info.fd);
error_client_new:
if ((st.st_mode & S_IFMT) == S_IFSOCK) patty_client_destroy(client);
if (client) patty_client_destroy(client);
error_stat:
return 1;

195
bin/if.c
View file

@ -6,6 +6,13 @@
#include <patty/bin/if.h>
struct context {
patty_error *err;
char *name;
patty_ax25_addr addr;
};
enum mode {
MODE_NONE,
MODE_IFNAME,
@ -16,30 +23,119 @@ enum mode {
MODE_FLOW
};
static int eprintf(patty_bin_if_data *data, const char *format, ...) {
int ret;
va_list args;
static patty_ax25_if *create_kiss(struct context *ctx, int argc, char **argv) {
enum mode mode = MODE_KISS;
va_start(args, format);
ret = vsnprintf(data->err, sizeof(data->err)-1, format, args);
va_end(args);
return ret;
}
int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
enum mode mode = MODE_NONE;
patty_kiss_tnc_info *info = &data->info;
patty_kiss_tnc_info info;
int i;
memset(&info, '\0', sizeof(info));
if (argc == 0) {
patty_error_fmt(ctx->err, "Too few parameters for 'kiss' interface");
goto error_invalid;
}
for (i=0; i<argc; i++) {
switch (mode) {
case MODE_KISS:
info.flags |= PATTY_KISS_TNC_DEVICE;
info.device = argv[i];
mode = MODE_IFOPTS;
break;
case MODE_IFOPTS:
if (strcmp(argv[i], "baud") == 0) {
mode = MODE_BAUD;
} else if (strcmp(argv[i], "flow") == 0) {
mode = MODE_FLOW;
} else {
patty_error_fmt(ctx->err, "Invalid parameter '%s'",
argv[i]);
}
break;
case MODE_BAUD:
if (!(argv[i][0] >= '0' && argv[i][0] <= '9')) {
patty_error_fmt(ctx->err, "Invalid baud rate '%s'",
argv[i]);
goto error_invalid;
}
info.flags |= PATTY_KISS_TNC_BAUD;
info.baud = atoi(argv[i]);
mode = MODE_IFOPTS;
break;
case MODE_FLOW:
if (strcmp(argv[i], "crtscts") == 0) {
info.flags |= PATTY_KISS_TNC_FLOW;
info.flow = PATTY_KISS_TNC_FLOW_CRTSCTS;
} else if (strcmp(argv[i], "xonxoff") == 0) {
info.flags |= PATTY_KISS_TNC_FLOW;
info.flow = PATTY_KISS_TNC_FLOW_XONXOFF;
} else {
patty_error_fmt(ctx->err, "Invalid flow control '%s'",
argv[i]);
goto error_invalid;
}
mode = MODE_IFOPTS;
break;
default:
break;
}
}
return patty_ax25_if_new( ctx->name,
&ctx->addr,
patty_kiss_tnc_driver(),
&info);
error_invalid:
return NULL;
}
struct if_type {
const char *name;
patty_ax25_if *(*func)(struct context *ctx, int argc, char **argv);
};
struct if_type if_types[] = {
{ "kiss", create_kiss },
{ NULL, NULL }
};
patty_ax25_if *patty_bin_if_create(int argc, char **argv, patty_error *e) {
enum mode mode = MODE_NONE;
struct context ctx = {
.err = e,
.name = NULL
};
int i;
patty_error_clear(e);
memset(&ctx.addr, '\0', sizeof(ctx.addr));
for (i=0; i<argc; i++) {
switch (mode) {
case MODE_NONE:
if (strcmp(argv[i], "if") != 0) {
eprintf(data, "Unexpected start of expression '%s'",
patty_error_fmt(e, "Unexpected start of expression '%s'",
argv[i]);
goto error_invalid;
@ -50,7 +146,7 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
break;
case MODE_IFNAME:
data->name = argv[i];
ctx.name = argv[i];
mode = MODE_IFOPTS;
@ -59,14 +155,18 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
case MODE_IFOPTS:
if (strcmp(argv[i], "ax25") == 0) {
mode = MODE_IFADDR;
} else if (strcmp(argv[i], "kiss") == 0) {
mode = MODE_KISS;
} else if (strcmp(argv[i], "baud") == 0) {
mode = MODE_BAUD;
} else if (strcmp(argv[i], "flow") == 0) {
mode = MODE_FLOW;
} else {
eprintf(data, "Invalid parameter '%s'", argv[i]);
int t;
for (t=0; if_types[t].name; t++) {
if (strcmp(if_types[t].name, argv[i]) == 0) {
return if_types[t].func(&ctx,
argc - i - 1,
argv + i + 1);
}
}
patty_error_fmt(e, "Invalid parameter '%s'", argv[i]);
goto error_invalid;
}
@ -74,8 +174,8 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
break;
case MODE_IFADDR:
if (patty_ax25_pton(argv[i], &data->addr) < 0) {
eprintf(data, "Invalid AX.25 address '%s': %s",
if (patty_ax25_pton(argv[i], &ctx.addr) < 0) {
patty_error_fmt(e, "Invalid AX.25 address '%s': %s",
argv[i], strerror(errno));
goto error_invalid;
@ -85,50 +185,13 @@ int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv) {
break;
case MODE_KISS:
data->type = PATTY_AX25_IF_KISS_TNC;
info->flags |= PATTY_KISS_TNC_DEVICE;
info->device = argv[i];
mode = MODE_IFOPTS;
break;
case MODE_BAUD:
if (!(argv[i][0] >= '0' && argv[i][0] <= '9')) {
eprintf(data, "Invalid baud rate '%s'", argv[i]);
goto error_invalid;
}
info->flags |= PATTY_KISS_TNC_BAUD;
info->baud = atoi(argv[i]);
mode = MODE_IFOPTS;
break;
case MODE_FLOW:
if (strcmp(argv[i], "crtscts") == 0) {
info->flags |= PATTY_KISS_TNC_FLOW;
info->flow = PATTY_KISS_TNC_FLOW_CRTSCTS;
} else if (strcmp(argv[i], "xonxoff") == 0) {
info->flags |= PATTY_KISS_TNC_FLOW;
info->flow = PATTY_KISS_TNC_FLOW_XONXOFF;
} else {
eprintf(data, "Invalid flow control '%s'", argv[i]);
goto error_invalid;
}
mode = MODE_IFOPTS;
default:
break;
}
}
return 0;
patty_error_fmt(e, "Media type not provided");
error_invalid:
return -1;
return NULL;
}

View file

@ -4,10 +4,13 @@
#include <patty/bin/kiss.h>
int patty_bin_kiss_config(patty_bin_kiss_data *data, int argc, char **argv) {
int patty_bin_kiss_config(int argc,
char **argv,
patty_kiss_tnc_info *info,
patty_error *e) {
int i;
patty_kiss_tnc_info *info = &data->info;
memset(info, '\0', sizeof(*info));
info->flags |= PATTY_KISS_TNC_DEVICE;
info->device = argv[0];
@ -25,8 +28,8 @@ int patty_bin_kiss_config(patty_bin_kiss_data *data, int argc, char **argv) {
info->flags |= PATTY_KISS_TNC_BAUD;
info->baud = baud;
} else {
snprintf(data->err, sizeof(data->err)-1,
"Invalid KISS TNC device parameter '%s'", argv[i]);
patty_error_fmt(e, "Invalid KISS TNC device parameter '%s'",
argv[i]);
goto error_invalid_device_setting;
}

View file

@ -94,10 +94,8 @@ static int handle_if(struct context *ctx,
int lineno,
int argc,
char **argv) {
patty_bin_if_data data;
patty_ax25_if *iface;
memset(&data, '\0', sizeof(data));
patty_error e;
if (argc < 2) {
fprintf(stderr, "%s: line %d: No interface name provided\n",
@ -111,30 +109,27 @@ static int handle_if(struct context *ctx,
goto error_invalid;
}
if (patty_bin_if_config(&data, argc, argv) < 0) {
if ((iface = patty_bin_if_create(argc, argv, &e)) == NULL) {
fprintf(stderr, "%s: line %d: %s\n",
ctx->config_file, lineno, data.err);
ctx->config_file, lineno, patty_error_string(&e));
goto error_invalid;
}
if ((iface = patty_ax25_if_new(PATTY_AX25_IF_KISS_TNC,
data.name,
&data.info,
&data.addr)) == NULL) {
goto error_if_new;
} else {
int fd = patty_kiss_tnc_fd(iface->tnc);
int fd = patty_ax25_if_fd(iface);
char *pty;
if (isatty(fd) && (pty = ptsname(fd)) != NULL) {
printf("if %s pty %s\n", data.name, pty);
printf("if %s pty %s\n",
patty_ax25_if_name(iface), pty);
}
}
if (patty_daemon_if_add(ctx->daemon, iface) < 0) {
fprintf(stderr, "%s: line %d: Unable to create interface %s: %s\n",
ctx->config_file, lineno, data.name, strerror(errno));
ctx->config_file,
lineno,
patty_ax25_if_name(iface),
strerror(errno));
goto error_daemon_if_add;
}
@ -144,7 +139,6 @@ static int handle_if(struct context *ctx,
error_daemon_if_add:
patty_ax25_if_destroy(iface);
error_if_new:
error_invalid:
return -1;
}
@ -281,9 +275,8 @@ static int handle_standalone(patty_daemon *daemon,
patty_ax25_if *iface;
patty_ax25_addr addr;
patty_bin_kiss_data data;
memset(&data, '\0', sizeof(data));
patty_kiss_tnc_info info;
patty_error e;
if (patty_daemon_set_sock_path(daemon, argv[0]) < 0) {
fprintf(stderr, "%s: Unable to set socket path to %s: %s\n",
@ -299,23 +292,23 @@ static int handle_standalone(patty_daemon *daemon,
goto error_invalid_callsign;
}
if (patty_bin_kiss_config(&data, argc - 2, argv + 2) < 0) {
if (patty_bin_kiss_config(argc - 2, argv + 2, &info, &e) < 0) {
fprintf(stderr, "%s: %s: %s\n",
argv0, argv[2], data.err);
argv0, argv[2], patty_error_string(&e));
goto error_kiss_config;
}
if ((iface = patty_ax25_if_new(PATTY_AX25_IF_KISS_TNC,
DEFAULT_IFNAME,
&data.info,
&addr)) == NULL) {
if ((iface = patty_ax25_if_new(DEFAULT_IFNAME,
&addr,
patty_kiss_tnc_driver(),
&info)) == NULL) {
fprintf(stderr, "%s: Unable to create network interface %s: %s\n",
argv0, DEFAULT_IFNAME, strerror(errno));
goto error_if_new;
} else {
int fd = patty_kiss_tnc_fd(iface->tnc);
int fd = patty_ax25_if_fd(iface);
char *pty;
if (isatty(fd) && (pty = ptsname(fd)) != NULL) {

View file

@ -5,7 +5,6 @@
#include <sys/types.h>
#include <sys/time.h>
#include <patty/kiss.h>
#include <patty/list.h>
#include <patty/dict.h>
@ -104,6 +103,7 @@ typedef struct _patty_ax25_if patty_ax25_if;
#include <patty/client.h>
#include <patty/ax25/frame.h>
#include <patty/ax25/if.h>
#include <patty/kiss/tnc.h>
#include <patty/ax25/route.h>
#include <patty/ax25/sock.h>
#include <patty/ax25/server.h>

View file

@ -12,12 +12,6 @@
#define PATTY_AX25_IF_DEFAULT_MTU 4096
#define PATTY_AX25_IF_DEFAULT_MRU 4096
enum patty_ax25_if_type {
PATTY_AX25_IF_UNKNOWN,
PATTY_AX25_IF_KISS_TNC,
PATTY_AX25_IF_HDLC
};
enum patty_ax25_if_flags {
PATTY_AX25_IF_HALF_DUPLEX = (1 << 0),
PATTY_AX25_IF_FULL_DUPLEX = (1 << 1),
@ -33,9 +27,33 @@ typedef struct _patty_ax25_if_stats {
dropped;
} patty_ax25_if_stats;
typedef void *(patty_ax25_if_driver_create)(void *);
typedef void (patty_ax25_if_driver_destroy)(void *);
typedef int (patty_ax25_if_driver_fd)(void *);
typedef int (patty_ax25_if_driver_pending)(void *);
typedef ssize_t (patty_ax25_if_driver_recv)(void *, void *, size_t);
typedef ssize_t (patty_ax25_if_driver_send)(void *, const void *, size_t);
typedef struct _patty_ax25_if_driver {
patty_ax25_if_driver_create *create;
patty_ax25_if_driver_destroy *destroy;
patty_ax25_if_driver_fd *fd;
patty_ax25_if_driver_pending *pending;
patty_ax25_if_driver_recv *recv;
patty_ax25_if_driver_send *send;
} patty_ax25_if_driver;
typedef void patty_ax25_if_phy;
typedef void patty_ax25_if_info;
typedef struct _patty_ax25_if {
enum patty_ax25_if_type type;
patty_ax25_if_stats stats;
patty_ax25_if_stats stats;
char name[8];
@ -47,21 +65,23 @@ typedef struct _patty_ax25_if {
size_t mru,
mtu;
patty_kiss_tnc *tnc;
patty_ax25_addr addr;
patty_list *aliases;
patty_dict *promisc_fds;
patty_ax25_if_driver *driver;
patty_ax25_if_phy *phy;
} patty_ax25_if;
typedef void patty_ax25_if_info;
patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type,
const char *name,
patty_ax25_if_info *info,
patty_ax25_addr *addr);
patty_ax25_if *patty_ax25_if_new(const char *name,
patty_ax25_addr *addr,
patty_ax25_if_driver *driver,
patty_ax25_if_info *info);
void patty_ax25_if_destroy(patty_ax25_if *iface);
char *patty_ax25_if_name(patty_ax25_if *iface);
int patty_ax25_if_addr_each(patty_ax25_if *iface,
int (*callback)(patty_ax25_addr *, void *),
void *ctx);
@ -78,6 +98,8 @@ int patty_ax25_if_promisc_add(patty_ax25_if *iface,
int patty_ax25_if_promisc_delete(patty_ax25_if *iface,
int fd);
int patty_ax25_if_fd(patty_ax25_if *iface);
ssize_t patty_ax25_if_pending(patty_ax25_if *iface);
ssize_t patty_ax25_if_recv(patty_ax25_if *iface,

View file

@ -123,7 +123,7 @@ typedef struct _patty_ax25_sock {
/*
* Reader for raw packets for PATTY_AX25_SOCK_RAW type
*/
patty_kiss_tnc *raw;
struct _patty_kiss_tnc *raw;
/*
* File descriptor information

View file

@ -2,16 +2,10 @@
#define _PATTY_BIN_IF_H
#include <patty/ax25.h>
#include <patty/error.h>
typedef struct _patty_bin_if_data {
char *name;
char err[256];
enum patty_ax25_if_type type;
patty_ax25_addr addr;
patty_kiss_tnc_info info;
} patty_bin_if_data;
int patty_bin_if_config(patty_bin_if_data *data, int argc, char **argv);
patty_ax25_if *patty_bin_if_create(int argc,
char **argv,
patty_error *e);
#endif /* _PATTY_BIN_IF_H */

View file

@ -1,13 +1,13 @@
#ifndef _PATTY_BIN_KISS_H
#define _PATTY_BIN_KISS_H
#include <patty/kiss.h>
#include <patty/error.h>
typedef struct _patty_bin_kiss_data {
char err[256];
patty_kiss_tnc_info info;
} patty_bin_kiss_data;
#include <patty/kiss/tnc.h>
int patty_bin_kiss_config(patty_bin_kiss_data *data, int argc, char **argv);
int patty_bin_kiss_config(int argc,
char **argv,
patty_kiss_tnc_info *info,
patty_error *e);
#endif /* _PATTY_BIN_KISS_H */

View file

@ -2,17 +2,14 @@
#define _PATTY_KISS_H
#include <sys/types.h>
#include <termios.h>
#define PATTY_KISS_FEND 0xc0
#define PATTY_KISS_FESC 0xdb
#define PATTY_KISS_TFEND 0xdc
#define PATTY_KISS_TFESC 0xdd
#define PATTY_KISS_BUFSZ 4096
#define PATTY_KISS_COMMAND(cmd) \
(cmd & 0x0f)
((cmd & 0x0f))
#define PATTY_KISS_COMMAND_PORT(cmd) \
((cmd & 0xf0) >> 4)
@ -33,46 +30,4 @@ ssize_t patty_kiss_frame_send(int fd,
size_t len,
int port);
typedef struct _patty_kiss_tnc patty_kiss_tnc;
#define PATTY_KISS_TNC_DEVICE (1 << 0)
#define PATTY_KISS_TNC_FD (1 << 1)
#define PATTY_KISS_TNC_BAUD (1 << 2)
#define PATTY_KISS_TNC_FLOW (1 << 3)
enum patty_kiss_tnc_flow {
PATTY_KISS_TNC_FLOW_NONE,
PATTY_KISS_TNC_FLOW_CRTSCTS,
PATTY_KISS_TNC_FLOW_XONXOFF
};
typedef struct _patty_kiss_tnc_info {
int flags;
const char *device;
int fd;
speed_t baud;
enum patty_kiss_tnc_flow flow;
} patty_kiss_tnc_info;
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info);
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc);
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc);
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
void *buf,
size_t len,
int *port);
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf,
size_t len,
int port);
#endif /* _PATTY_KISS_H */

54
include/patty/kiss/tnc.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef _PATTY_KISS_TNC_H
#define _PATTY_KISS_TNC_H
#include <sys/types.h>
#include <termios.h>
#include <patty/ax25.h>
#define PATTY_KISS_TNC_BUFSZ 4096
#define PATTY_KISS_TNC_PORT 0
typedef struct _patty_kiss_tnc patty_kiss_tnc;
#define PATTY_KISS_TNC_DEVICE (1 << 0)
#define PATTY_KISS_TNC_FD (1 << 1)
#define PATTY_KISS_TNC_BAUD (1 << 2)
#define PATTY_KISS_TNC_FLOW (1 << 3)
enum patty_kiss_tnc_flow {
PATTY_KISS_TNC_FLOW_NONE,
PATTY_KISS_TNC_FLOW_CRTSCTS,
PATTY_KISS_TNC_FLOW_XONXOFF
};
typedef struct _patty_kiss_tnc_info {
int flags;
const char *device;
int fd;
speed_t baud;
enum patty_kiss_tnc_flow flow;
} patty_kiss_tnc_info;
patty_ax25_if_driver *patty_kiss_tnc_driver();
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info);
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc);
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc);
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
void *buf,
size_t len);
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf,
size_t len);
#endif /* _PATTY_KISS_H */

106
src/if.c
View file

@ -5,46 +5,36 @@
#include <errno.h>
#include <patty/ax25.h>
#include <patty/kiss.h>
static int init_tnc(patty_ax25_if *iface, patty_kiss_tnc_info *info) {
if ((iface->tnc = patty_kiss_tnc_new(info)) == NULL) {
goto error_kiss_tnc_new;
}
iface->type = PATTY_AX25_IF_KISS_TNC;
return 0;
error_kiss_tnc_new:
return -1;
}
static void destroy_tnc(patty_ax25_if *iface) {
patty_kiss_tnc_destroy(iface->tnc);
}
patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type,
const char *name,
patty_ax25_if_info *info,
patty_ax25_addr *addr) {
patty_ax25_if *patty_ax25_if_new(const char *name,
patty_ax25_addr *addr,
patty_ax25_if_driver *driver,
patty_ax25_if_info *info) {
patty_ax25_if *iface;
if ((iface = malloc(sizeof(*iface))) == NULL) {
goto error_malloc_iface;
}
memset(iface, '\0', sizeof(*iface));
strncpy(iface->name, name, sizeof(iface->name));
if (addr->callsign[0] == '\0') {
errno = EINVAL;
goto error_invalid_address;
}
if ((iface = malloc(sizeof(*iface))) == NULL) {
goto error_malloc_iface;
}
memset(iface, '\0', sizeof(*iface));
if ((iface->phy = driver->create(info)) == NULL) {
goto error_phy_create;
}
iface->driver = driver;
strncpy(iface->name, name, sizeof(iface->name));
/*
* TODO: Eventually inherit the half/full-duplex flag from the TNC this
* TODO: Eventually inherit the half/full-duplex flag from the PHY this
* interface is bound to
*/
iface->flags_classes = PATTY_AX25_IF_DEFAULT_CLASSES;
@ -69,28 +59,10 @@ patty_ax25_if *patty_ax25_if_new(enum patty_ax25_if_type type,
goto error_dict_new_promisc_fds;
}
switch (type) {
case PATTY_AX25_IF_KISS_TNC:
if (init_tnc(iface, info) < 0) {
goto error_init;
}
break;
default:
errno = ENOSYS;
goto error_invalid_if_type;
}
memcpy(&iface->addr, addr, sizeof(iface->addr));
return iface;
error_invalid_if_type:
error_init:
patty_dict_destroy(iface->promisc_fds);
error_dict_new_promisc_fds:
patty_list_destroy(iface->aliases);
@ -101,6 +73,9 @@ error_malloc_tx_buf:
free(iface->rx_buf);
error_malloc_rx_buf:
driver->destroy(iface->phy);
error_phy_create:
free(iface);
error_malloc_iface:
@ -109,13 +84,8 @@ error_invalid_address:
}
void patty_ax25_if_destroy(patty_ax25_if *iface) {
switch (iface->type) {
case PATTY_AX25_IF_KISS_TNC:
destroy_tnc(iface);
break;
default:
break;
if (iface->driver->destroy) {
iface->driver->destroy(iface->phy);
}
patty_dict_destroy(iface->promisc_fds);
@ -126,6 +96,10 @@ void patty_ax25_if_destroy(patty_ax25_if *iface) {
free(iface);
}
char *patty_ax25_if_name(patty_ax25_if *iface) {
return iface->name;
}
int patty_ax25_if_addr_each(patty_ax25_if *iface,
int (*callback)(patty_ax25_addr *, void *),
void *ctx) {
@ -297,21 +271,23 @@ static int handle_promisc_frame(uint32_t key,
return patty_kiss_frame_send(fd, frame->buf, frame->len, 0);
}
int patty_ax25_if_fd(patty_ax25_if *iface) {
return iface->driver->fd(iface->phy);
}
ssize_t patty_ax25_if_pending(patty_ax25_if *iface) {
return patty_kiss_tnc_pending(iface->tnc);
return iface->driver->pending(iface->phy);
}
ssize_t patty_ax25_if_recv(patty_ax25_if *iface,
void **buf) {
ssize_t readlen;
int port;
struct promisc_frame frame;
if ((readlen = patty_kiss_tnc_recv(iface->tnc,
if ((readlen = iface->driver->recv(iface->phy,
iface->rx_buf,
iface->mru,
&port)) < 0) {
goto error_kiss_tnc_recv;
iface->mru)) < 0) {
goto error_driver_recv;
} else if (readlen == 0) {
goto done;
}
@ -335,7 +311,7 @@ done:
return readlen;
error_handle_promisc_frame:
error_kiss_tnc_recv:
error_driver_recv:
return -1;
}
@ -345,8 +321,8 @@ ssize_t patty_ax25_if_send(patty_ax25_if *iface,
ssize_t wrlen;
struct promisc_frame frame;
if ((wrlen = patty_kiss_tnc_send(iface->tnc, buf, len, 0)) < 0) {
goto error_kiss_tnc_send;
if ((wrlen = iface->driver->send(iface->phy, buf, len)) < 0) {
goto error_driver_send;
}
iface->stats.tx_frames++;
@ -365,6 +341,6 @@ ssize_t patty_ax25_if_send(patty_ax25_if *iface,
return wrlen;
error_handle_promisc_frame:
error_kiss_tnc_send:
error_driver_send:
return -1;
}

View file

@ -15,35 +15,6 @@
#include "config.h"
enum kiss_flags {
KISS_NONE = 0,
KISS_FRAME = 1 << 0,
KISS_COMMAND = 1 << 1,
KISS_ESCAPE = 1 << 2
};
enum tnc_opts {
TNC_NONE = 0,
TNC_CLOSE_ON_DESTROY = 1 << 0
};
struct _patty_kiss_tnc {
struct termios attrs,
attrs_old;
int fd,
opts;
void *buf,
*frame;
size_t bufsz,
offset,
dropped;
ssize_t readlen;
};
static inline ssize_t write_byte(int fd, uint8_t c) {
return write(fd, &c, sizeof(c));
}
@ -121,299 +92,3 @@ ssize_t patty_kiss_frame_send(int fd,
error_io:
return -1;
}
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) {
patty_kiss_tnc *tnc;
if ((tnc = malloc(sizeof(*tnc))) == NULL) {
goto error_malloc_tnc;
}
if ((tnc->buf = malloc(PATTY_KISS_BUFSZ)) == NULL) {
goto error_malloc_buf;
}
if (info->flags & PATTY_KISS_TNC_DEVICE) {
struct stat st;
if (strcmp(info->device, "/dev/ptmx") == 0) {
int ptysub;
if (openpty(&tnc->fd, &ptysub, NULL, NULL, NULL) < 0) {
goto error_open;
}
} else if (stat(info->device, &st) < 0) {
goto error_stat;
} else if ((st.st_mode & S_IFMT) == S_IFSOCK) {
struct sockaddr_un addr;
if (strlen(info->device) > sizeof(addr.sun_path)) {
errno = EOVERFLOW;
goto error_overflow;
}
if ((tnc->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
goto error_socket;
}
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, info->device, sizeof(addr.sun_path)-1);
if (connect(tnc->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
goto error_connect;
}
} else {
if ((tnc->fd = open(info->device, O_RDWR | O_NOCTTY)) < 0) {
goto error_open;
}
}
tnc->opts |= TNC_CLOSE_ON_DESTROY;
} else if (info->flags & PATTY_KISS_TNC_FD) {
tnc->fd = info->fd;
} else {
errno = EINVAL;
goto error_no_fd;
}
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
if (tcgetattr(tnc->fd, &tnc->attrs) < 0) {
goto error_tcgetattr;
}
memcpy(&tnc->attrs_old, &tnc->attrs, sizeof(tnc->attrs_old));
cfmakeraw(&tnc->attrs);
if (info->flags & PATTY_KISS_TNC_BAUD) {
cfsetspeed(&tnc->attrs, info->baud);
}
if (info->flags & PATTY_KISS_TNC_FLOW) {
switch (info->flow) {
case PATTY_KISS_TNC_FLOW_NONE:
break;
case PATTY_KISS_TNC_FLOW_CRTSCTS:
tnc->attrs.c_cflag |= CRTSCTS;
break;
case PATTY_KISS_TNC_FLOW_XONXOFF:
tnc->attrs.c_iflag |= IXON | IXOFF;
break;
}
}
if (tcflush(tnc->fd, TCIOFLUSH) < 0) {
goto error_tcflush;
}
if (tcsetattr(tnc->fd, TCSANOW, &tnc->attrs) < 0) {
goto error_tcsetattr;
}
} else {
errno = 0;
}
tnc->opts = TNC_NONE;
tnc->bufsz = PATTY_KISS_BUFSZ;
tnc->offset = 0;
tnc->dropped = 0;
tnc->readlen = -1;
return tnc;
error_tcflush:
error_tcsetattr:
error_tcgetattr:
error_connect:
if (info->flags & PATTY_KISS_TNC_DEVICE) {
(void)close(tnc->fd);
}
error_open:
error_no_fd:
error_socket:
error_overflow:
error_stat:
free(tnc->buf);
error_malloc_buf:
free(tnc);
error_malloc_tnc:
return NULL;
}
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc) {
return tnc->fd;
}
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc) {
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
(void)tcsetattr(tnc->fd, TCSANOW, &tnc->attrs_old);
}
if (tnc->opts & TNC_CLOSE_ON_DESTROY) {
(void)close(tnc->fd);
}
free(tnc->buf);
free(tnc);
}
static void tnc_drop(patty_kiss_tnc *tnc) {
tnc->offset = 0;
tnc->readlen = -1;
tnc->dropped++;
}
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
return tnc->dropped;
}
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc) {
if (tnc->readlen < 0) {
return -1;
}
if (tnc->offset == 0) {
return 0;
}
return tnc->readlen - tnc->offset;
}
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
void *buf,
size_t len,
int *port) {
size_t r = 0, /* Number of bytes read */
w = 0; /* Number of bytes written to buf */
int flags = KISS_NONE;
if (tnc->offset == tnc->readlen) {
errno = 0;
return 0;
}
while (1) {
uint8_t c;
r++;
if (w == len) {
tnc_drop(tnc);
flags &= ~KISS_FRAME;
w = 0;
}
if (tnc->offset == 0) {
if ((tnc->readlen = read(tnc->fd, tnc->buf, tnc->bufsz)) < 0) {
goto error_io;
} else if (tnc->readlen == 0) {
goto done;
}
}
c = ((uint8_t *)tnc->buf)[tnc->offset++];
if (tnc->offset == tnc->readlen) {
tnc->offset = 0;
}
if (!(flags & KISS_FRAME)) {
if (c == PATTY_KISS_FEND) {
flags = KISS_FRAME;
continue;
} else {
errno = EIO;
goto error_io;
}
} else {
if (c == PATTY_KISS_FEND) {
flags = KISS_NONE;
if (w > 0) {
goto done;
}
continue;
}
}
if (!(flags & KISS_COMMAND)) {
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
errno = EIO;
goto error_io;
}
if (port) {
*port = PATTY_KISS_COMMAND_PORT(c);
}
flags |= KISS_COMMAND;
continue;
}
if (!(flags & KISS_ESCAPE)) {
if (c == PATTY_KISS_FESC) {
flags |= KISS_ESCAPE;
continue;
}
} else {
switch (c) {
case PATTY_KISS_TFEND:
((uint8_t *)buf)[w++] = PATTY_KISS_FEND;
flags &= ~KISS_ESCAPE;
continue;
case PATTY_KISS_TFESC:
((uint8_t *)buf)[w++] = PATTY_KISS_FESC;
flags &= ~KISS_ESCAPE;
continue;
default:
errno = EIO;
goto error_io;
}
}
((uint8_t *)buf)[w++] = c;
}
done:
if (flags & KISS_FRAME) {
tnc_drop(tnc);
errno = 0;
return 0;
}
return (ssize_t)w;
error_io:
return -1;
}
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf,
size_t len,
int port) {
return patty_kiss_frame_send(tnc->fd, buf, len, port);
}

View file

@ -432,7 +432,7 @@ int patty_ax25_server_if_add(patty_ax25_server *server,
patty_ax25_if *iface) {
int fd;
if ((fd = patty_kiss_tnc_fd(iface->tnc)) >= 0) {
if ((fd = patty_ax25_if_fd(iface)) >= 0) {
fd_watch(server, fd);
}
@ -456,7 +456,7 @@ int patty_ax25_server_if_delete(patty_ax25_server *server,
patty_ax25_if *iface = item->value;
if (strncmp(iface->name, ifname, sizeof(iface->name)) == 0) {
if ((fd = patty_kiss_tnc_fd(iface->tnc)) >= 0) {
if ((fd = patty_ax25_if_fd(iface)) >= 0) {
fd_clear(server, fd);
}
@ -2006,7 +2006,7 @@ error_decode:
}
static int handle_iface(patty_ax25_server *server, patty_ax25_if *iface) {
int fd = patty_kiss_tnc_fd(iface->tnc);
int fd = patty_ax25_if_fd(iface);
if (!FD_ISSET(fd, &server->fds_r)) {
goto done;

View file

@ -10,6 +10,7 @@
#include <errno.h>
#include <patty/ax25.h>
#include <patty/kiss/tnc.h>
#include "config.h"
@ -621,15 +622,13 @@ error_invalid_type:
ssize_t patty_ax25_sock_recv(patty_ax25_sock *sock,
void *buf,
size_t len) {
int port;
if (sock->type != PATTY_AX25_SOCK_RAW) {
errno = EINVAL;
goto error_invalid_type;
}
return patty_kiss_tnc_recv(sock->raw, buf, len, &port);
return patty_kiss_tnc_recv(sock->raw, buf, len);
error_invalid_type:
return -1;

348
src/tnc.c Normal file
View file

@ -0,0 +1,348 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <patty/kiss.h>
#include <patty/kiss/tnc.h>
#include "config.h"
enum state_flags {
STATE_NONE = 0,
STATE_FRAME = 1 << 0,
STATE_COMMAND = 1 << 1,
STATE_ESCAPE = 1 << 2
};
enum tnc_opts {
TNC_NONE = 0,
TNC_CLOSE_ON_DESTROY = 1 << 0
};
struct _patty_kiss_tnc {
struct termios attrs,
attrs_old;
int fd,
opts;
void *buf,
*frame;
size_t bufsz,
offset,
dropped;
ssize_t readlen;
};
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) {
patty_kiss_tnc *tnc;
if ((tnc = malloc(sizeof(*tnc))) == NULL) {
goto error_malloc_tnc;
}
if ((tnc->buf = malloc(PATTY_KISS_TNC_BUFSZ)) == NULL) {
goto error_malloc_buf;
}
if (info->flags & PATTY_KISS_TNC_DEVICE) {
struct stat st;
if (strcmp(info->device, "/dev/ptmx") == 0) {
int ptysub;
if (openpty(&tnc->fd, &ptysub, NULL, NULL, NULL) < 0) {
goto error_open;
}
} else if (stat(info->device, &st) < 0) {
goto error_stat;
} else if ((st.st_mode & S_IFMT) == S_IFSOCK) {
struct sockaddr_un addr;
if (strlen(info->device) > sizeof(addr.sun_path)) {
errno = EOVERFLOW;
goto error_overflow;
}
if ((tnc->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
goto error_socket;
}
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, info->device, sizeof(addr.sun_path)-1);
if (connect(tnc->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
goto error_connect;
}
} else {
if ((tnc->fd = open(info->device, O_RDWR | O_NOCTTY)) < 0) {
goto error_open;
}
}
tnc->opts |= TNC_CLOSE_ON_DESTROY;
} else if (info->flags & PATTY_KISS_TNC_FD) {
tnc->fd = info->fd;
} else {
errno = EINVAL;
goto error_no_fd;
}
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
if (tcgetattr(tnc->fd, &tnc->attrs) < 0) {
goto error_tcgetattr;
}
memcpy(&tnc->attrs_old, &tnc->attrs, sizeof(tnc->attrs_old));
cfmakeraw(&tnc->attrs);
if (info->flags & PATTY_KISS_TNC_BAUD) {
cfsetspeed(&tnc->attrs, info->baud);
}
if (info->flags & PATTY_KISS_TNC_FLOW) {
switch (info->flow) {
case PATTY_KISS_TNC_FLOW_NONE:
break;
case PATTY_KISS_TNC_FLOW_CRTSCTS:
tnc->attrs.c_cflag |= CRTSCTS;
break;
case PATTY_KISS_TNC_FLOW_XONXOFF:
tnc->attrs.c_iflag |= IXON | IXOFF;
break;
}
}
if (tcflush(tnc->fd, TCIOFLUSH) < 0) {
goto error_tcflush;
}
if (tcsetattr(tnc->fd, TCSANOW, &tnc->attrs) < 0) {
goto error_tcsetattr;
}
} else {
errno = 0;
}
tnc->opts = TNC_NONE;
tnc->bufsz = PATTY_KISS_TNC_BUFSZ;
tnc->offset = 0;
tnc->dropped = 0;
tnc->readlen = -1;
return tnc;
error_tcflush:
error_tcsetattr:
error_tcgetattr:
error_connect:
if (info->flags & PATTY_KISS_TNC_DEVICE) {
(void)close(tnc->fd);
}
error_open:
error_no_fd:
error_socket:
error_overflow:
error_stat:
free(tnc->buf);
error_malloc_buf:
free(tnc);
error_malloc_tnc:
return NULL;
}
void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc) {
if (isatty(tnc->fd) && ptsname(tnc->fd) == NULL) {
(void)tcsetattr(tnc->fd, TCSANOW, &tnc->attrs_old);
}
if (tnc->opts & TNC_CLOSE_ON_DESTROY) {
(void)close(tnc->fd);
}
free(tnc->buf);
free(tnc);
}
int patty_kiss_tnc_fd(patty_kiss_tnc *tnc) {
return tnc->fd;
}
static void tnc_drop(patty_kiss_tnc *tnc) {
tnc->offset = 0;
tnc->readlen = -1;
tnc->dropped++;
}
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
return tnc->dropped;
}
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc) {
if (tnc->readlen < 0) {
return -1;
}
if (tnc->offset == 0) {
return 0;
}
return tnc->readlen - tnc->offset;
}
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
void *buf,
size_t len) {
size_t r = 0, /* Number of bytes read */
w = 0; /* Number of bytes written to buf */
int flags = STATE_NONE;
if (tnc->offset == tnc->readlen) {
errno = 0;
return 0;
}
while (1) {
uint8_t c;
r++;
if (w == len) {
tnc_drop(tnc);
flags &= ~STATE_FRAME;
w = 0;
}
if (tnc->offset == 0) {
if ((tnc->readlen = read(tnc->fd, tnc->buf, tnc->bufsz)) < 0) {
goto error_io;
} else if (tnc->readlen == 0) {
goto done;
}
}
c = ((uint8_t *)tnc->buf)[tnc->offset++];
if (tnc->offset == tnc->readlen) {
tnc->offset = 0;
}
if (!(flags & STATE_FRAME)) {
if (c == PATTY_KISS_FEND) {
flags = STATE_FRAME;
continue;
} else {
errno = EIO;
goto error_io;
}
} else {
if (c == PATTY_KISS_FEND) {
flags = STATE_NONE;
if (w > 0) {
goto done;
}
continue;
}
}
if (!(flags & STATE_COMMAND)) {
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
errno = EIO;
goto error_io;
}
flags |= STATE_COMMAND;
continue;
}
if (!(flags & STATE_ESCAPE)) {
if (c == PATTY_KISS_FESC) {
flags |= STATE_ESCAPE;
continue;
}
} else {
switch (c) {
case PATTY_KISS_TFEND:
((uint8_t *)buf)[w++] = PATTY_KISS_FEND;
flags &= ~STATE_ESCAPE;
continue;
case PATTY_KISS_TFESC:
((uint8_t *)buf)[w++] = PATTY_KISS_FESC;
flags &= ~STATE_ESCAPE;
continue;
default:
errno = EIO;
goto error_io;
}
}
((uint8_t *)buf)[w++] = c;
}
done:
if (flags & STATE_FRAME) {
tnc_drop(tnc);
errno = 0;
return 0;
}
return (ssize_t)w;
error_io:
return -1;
}
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf,
size_t len) {
return patty_kiss_frame_send(tnc->fd, buf, len, PATTY_KISS_TNC_PORT);
}
patty_ax25_if_driver *patty_kiss_tnc_driver() {
static patty_ax25_if_driver driver = {
.destroy = (patty_ax25_if_driver_destroy *)patty_kiss_tnc_destroy,
.fd = (patty_ax25_if_driver_fd *)patty_kiss_tnc_fd,
.pending = (patty_ax25_if_driver_pending *)patty_kiss_tnc_pending,
.recv = (patty_ax25_if_driver_recv *)patty_kiss_tnc_recv,
.send = (patty_ax25_if_driver_send *)patty_kiss_tnc_send
};
return &driver;
}