Split patty_ax25_if_recv() into specific parts

Split patty_ax25_if_recv() into the following parts:

    * patty_ax25_if_fill(), to fill the internal PHY buffer

    * patty_ax25_if_drain(), to drain the internal PHY buffer and decode
      any data present in the buffer

    * patty_ax25_if_ready(), to test if a complete frame has been
      decoded

    * patty_ax25_if_flush(), to return the final number of bytes decoded
      in the PHY buffer and reset the decoding state

Other changes:

    * Implement a finite state machine for decoding KISS traffic in
      src/tnc.c, patty_ax25_kiss_drain()

    * Refactor src/server.c, handle_iface() to use the aforementioned
      frame receive/decode methods
This commit is contained in:
XANTRONIX Development 2020-09-17 01:10:34 -05:00 committed by XANTRONIX Industrial
parent 2f5c877762
commit d5e2062dea
5 changed files with 214 additions and 171 deletions

View file

@ -35,7 +35,13 @@ typedef int (patty_ax25_if_driver_fd)(void *);
typedef int (patty_ax25_if_driver_pending)(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_fill)(void *);
typedef ssize_t (patty_ax25_if_driver_drain)(void *, void *, size_t);
typedef int (patty_ax25_if_driver_ready)(void *);
typedef ssize_t (patty_ax25_if_driver_flush)(void *);
typedef ssize_t (patty_ax25_if_driver_send)(void *, const void *, size_t); typedef ssize_t (patty_ax25_if_driver_send)(void *, const void *, size_t);
@ -44,7 +50,10 @@ typedef struct _patty_ax25_if_driver {
patty_ax25_if_driver_destroy *destroy; patty_ax25_if_driver_destroy *destroy;
patty_ax25_if_driver_fd *fd; patty_ax25_if_driver_fd *fd;
patty_ax25_if_driver_pending *pending; patty_ax25_if_driver_pending *pending;
patty_ax25_if_driver_recv *recv; patty_ax25_if_driver_fill *fill;
patty_ax25_if_driver_drain *drain;
patty_ax25_if_driver_ready *ready;
patty_ax25_if_driver_flush *flush;
patty_ax25_if_driver_send *send; patty_ax25_if_driver_send *send;
} patty_ax25_if_driver; } patty_ax25_if_driver;
@ -100,10 +109,15 @@ int patty_ax25_if_promisc_delete(patty_ax25_if *iface,
int patty_ax25_if_fd(patty_ax25_if *iface); int patty_ax25_if_fd(patty_ax25_if *iface);
ssize_t patty_ax25_if_pending(patty_ax25_if *iface); int patty_ax25_if_pending(patty_ax25_if *iface);
ssize_t patty_ax25_if_recv(patty_ax25_if *iface, ssize_t patty_ax25_if_fill(patty_ax25_if *iface);
void **buf);
ssize_t patty_ax25_if_drain(patty_ax25_if *iface, void *buf, size_t len);
int patty_ax25_if_ready(patty_ax25_if *iface);
ssize_t patty_ax25_if_flush(patty_ax25_if *iface);
ssize_t patty_ax25_if_send(patty_ax25_if *iface, ssize_t patty_ax25_if_send(patty_ax25_if *iface,
const void *buf, const void *buf,

View file

@ -39,11 +39,17 @@ void patty_kiss_tnc_destroy(patty_kiss_tnc *tnc);
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc); size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc); int patty_kiss_tnc_pending(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc, ssize_t patty_kiss_tnc_fill(patty_kiss_tnc *tnc);
void *buf,
size_t len); ssize_t patty_kiss_tnc_drain(patty_kiss_tnc *tnc, void *buf, size_t len);
int patty_kiss_tnc_ready(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_flush(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, ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf, const void *buf,

View file

@ -275,43 +275,45 @@ int patty_ax25_if_fd(patty_ax25_if *iface) {
return iface->driver->fd(iface->phy); return iface->driver->fd(iface->phy);
} }
ssize_t patty_ax25_if_pending(patty_ax25_if *iface) { int patty_ax25_if_pending(patty_ax25_if *iface) {
return iface->driver->pending(iface->phy); return iface->driver->pending(iface->phy);
} }
ssize_t patty_ax25_if_recv(patty_ax25_if *iface, ssize_t patty_ax25_if_fill(patty_ax25_if *iface) {
void **buf) { return iface->driver->fill(iface->phy);
ssize_t readlen; }
struct promisc_frame frame;
if ((readlen = iface->driver->recv(iface->phy, ssize_t patty_ax25_if_drain(patty_ax25_if *iface, void *buf, size_t len) {
iface->rx_buf, return iface->driver->drain(iface->phy, buf, len);
iface->mru)) < 0) { }
goto error_driver_recv;
} else if (readlen == 0) { int patty_ax25_if_ready(patty_ax25_if *iface) {
goto done; return iface->driver->ready(iface->phy);
}
ssize_t patty_ax25_if_flush(patty_ax25_if *iface) {
ssize_t len = iface->driver->flush(iface->phy);
if (len > 0) {
struct promisc_frame frame = {
.buf = iface->rx_buf,
.len = len,
.iface = iface
};
iface->stats.rx_frames++;
iface->stats.rx_bytes += len;
if (patty_dict_each(iface->promisc_fds,
handle_promisc_frame,
&frame) < 0) {
goto error_handle_promisc_frame;
}
} }
*buf = iface->rx_buf; return len;
iface->stats.rx_frames++;
iface->stats.rx_bytes += readlen;
frame.buf = iface->rx_buf;
frame.len = readlen;
frame.iface = iface;
if (patty_dict_each(iface->promisc_fds,
handle_promisc_frame,
&frame) < 0) {
goto error_handle_promisc_frame;
}
done:
return readlen;
error_handle_promisc_frame: error_handle_promisc_frame:
error_driver_recv:
return -1; return -1;
} }

View file

@ -2007,33 +2007,46 @@ error_decode:
static int handle_iface(patty_ax25_server *server, patty_ax25_if *iface) { static int handle_iface(patty_ax25_server *server, patty_ax25_if *iface) {
int fd = patty_ax25_if_fd(iface); int fd = patty_ax25_if_fd(iface);
ssize_t len;
if (!FD_ISSET(fd, &server->fds_r)) { if (!FD_ISSET(fd, &server->fds_r)) {
goto done; goto done;
} }
do { if ((len = patty_ax25_if_fill(iface)) < 0) {
void *buf; goto error_io;
ssize_t readlen; } else if (len == 0) {
close(fd);
if ((readlen = patty_ax25_if_recv(iface, &buf)) < 0) { fd_clear(server, fd);
goto error_io;
} else if (readlen == 0) {
close(fd);
fd_clear(server, fd); goto done;
}
goto done; while (1) {
} ssize_t len;
if (handle_frame(server, iface, buf, readlen) < 0) { if ((len = patty_ax25_if_drain(iface, iface->rx_buf, iface->mru)) < 0) {
goto error_io; goto error_io;
} }
} while (patty_ax25_if_pending(iface));
if (!patty_ax25_if_ready(iface)) {
break;
}
if ((len = patty_ax25_if_flush(iface)) < 0) {
goto error_io;
}
if (handle_frame(server, iface, iface->rx_buf, len) < 0) {
goto error_handle_frame;
}
}
done: done:
return 0; return 0;
error_handle_frame:
error_io: error_io:
return -1; return -1;
} }

250
src/tnc.c
View file

@ -16,18 +16,18 @@
#include "config.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 { enum tnc_opts {
TNC_NONE = 0, TNC_NONE = 0,
TNC_CLOSE_ON_DESTROY = 1 << 0 TNC_CLOSE_ON_DESTROY = 1 << 0
}; };
enum state {
KISS_NONE,
KISS_FRAME,
KISS_FRAME_BODY,
KISS_FRAME_ESCAPE
};
struct _patty_kiss_tnc { struct _patty_kiss_tnc {
struct termios attrs, struct termios attrs,
attrs_old; attrs_old;
@ -35,14 +35,15 @@ struct _patty_kiss_tnc {
int fd, int fd,
opts; opts;
void *buf, void *buf;
*frame;
enum state state;
size_t bufsz, size_t bufsz,
offset, readlen,
offset_i,
offset_o,
dropped; dropped;
ssize_t readlen;
}; };
patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) { patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) {
@ -143,11 +144,13 @@ patty_kiss_tnc *patty_kiss_tnc_new(patty_kiss_tnc_info *info) {
errno = 0; errno = 0;
} }
tnc->opts = TNC_NONE; tnc->opts = TNC_NONE;
tnc->bufsz = PATTY_KISS_TNC_BUFSZ; tnc->state = KISS_NONE;
tnc->offset = 0; tnc->bufsz = PATTY_KISS_TNC_BUFSZ;
tnc->dropped = 0; tnc->offset_i = 0;
tnc->readlen = -1; tnc->offset_o = 0;
tnc->dropped = 0;
tnc->readlen = 0;
return tnc; return tnc;
@ -191,8 +194,10 @@ int patty_kiss_tnc_fd(patty_kiss_tnc *tnc) {
} }
static void tnc_drop(patty_kiss_tnc *tnc) { static void tnc_drop(patty_kiss_tnc *tnc) {
tnc->offset = 0; tnc->state = KISS_NONE;
tnc->readlen = -1; tnc->offset_i = 0;
tnc->offset_o = 0;
tnc->readlen = 0;
tnc->dropped++; tnc->dropped++;
} }
@ -200,132 +205,132 @@ size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
return tnc->dropped; return tnc->dropped;
} }
ssize_t patty_kiss_tnc_pending(patty_kiss_tnc *tnc) { int patty_kiss_tnc_pending(patty_kiss_tnc *tnc) {
if (tnc->readlen < 0) { return tnc->offset_i < tnc->readlen? 1: 0;
return -1;
}
if (tnc->offset == 0) {
return 0;
}
return tnc->readlen - tnc->offset;
} }
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc, ssize_t patty_kiss_tnc_fill(patty_kiss_tnc *tnc) {
void *buf, if ((tnc->readlen = read(tnc->fd, tnc->buf, tnc->bufsz)) < 0) {
size_t len) { goto error_read;
size_t o = 0; /* Number of bytes written to buf */
int flags = STATE_NONE;
if (tnc->offset == tnc->readlen) {
errno = 0;
return 0;
} }
while (1) { tnc->offset_i = 0;
return tnc->readlen;
error_read:
return -1;
}
ssize_t patty_kiss_tnc_drain(patty_kiss_tnc *tnc, void *buf, size_t len) {
size_t offset_start = tnc->offset_i;
while (tnc->offset_i < tnc->readlen) {
uint8_t c; uint8_t c;
if (o == len) { if (tnc->offset_o == len) {
tnc_drop(tnc); tnc_drop(tnc);
flags &= ~STATE_FRAME;
o = 0;
} }
if (tnc->offset == 0) { c = ((uint8_t *)tnc->buf)[tnc->offset_i++];
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++]; switch (tnc->state) {
case KISS_NONE:
if (tnc->offset == tnc->readlen) { if (c == PATTY_KISS_FEND) {
tnc->offset = 0; tnc->state = KISS_FRAME;
}
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 (o > 0) {
goto done;
} }
continue; break;
}
}
if (!(flags & STATE_COMMAND)) { case KISS_FRAME:
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) { if (c == PATTY_KISS_FEND) {
errno = EIO; break;
} else if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
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)[o++] = PATTY_KISS_FEND;
flags &= ~STATE_ESCAPE;
continue;
case PATTY_KISS_TFESC:
((uint8_t *)buf)[o++] = PATTY_KISS_FESC;
flags &= ~STATE_ESCAPE;
continue;
default:
errno = EIO; errno = EIO;
goto error_io; goto error_io;
} }
}
((uint8_t *)buf)[o++] = c; tnc->state = KISS_FRAME_BODY;
break;
case KISS_FRAME_BODY:
if (c == PATTY_KISS_FESC) {
tnc->state = KISS_FRAME_ESCAPE;
} else if (c == PATTY_KISS_FEND) {
tnc->state = KISS_FRAME;
goto done;
} else {
((uint8_t *)buf)[tnc->offset_o++] = c;
}
break;
case KISS_FRAME_ESCAPE:
if (c == PATTY_KISS_TFEND) {
((uint8_t *)buf)[tnc->offset_o++] = PATTY_KISS_FEND;
} else if (c == PATTY_KISS_TFESC) {
((uint8_t *)buf)[tnc->offset_o++] = PATTY_KISS_FESC;
} else {
errno = EIO;
goto error_io;
}
tnc->state = KISS_FRAME_BODY;
}
} }
done: done:
if (flags & STATE_FRAME) { return tnc->offset_i - offset_start;
tnc_drop(tnc);
errno = 0;
return 0;
}
return (ssize_t)o;
error_io: error_io:
return -1; return -1;
} }
int patty_kiss_tnc_ready(patty_kiss_tnc *tnc) {
return tnc->state == KISS_FRAME && tnc->offset_o > 0? 1: 0;
}
ssize_t patty_kiss_tnc_flush(patty_kiss_tnc *tnc) {
if (patty_kiss_tnc_ready(tnc)) {
ssize_t ret = (ssize_t)tnc->offset_o;
tnc->state = KISS_NONE;
tnc->offset_o = 0;
return ret;
}
return 0;
}
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc, void *buf, size_t len) {
while (!patty_kiss_tnc_ready(tnc)) {
ssize_t drained;
if ((drained = patty_kiss_tnc_drain(tnc, buf, len)) < 0) {
goto error_drain;
} else if (drained == 0) {
ssize_t filled;
if ((filled = patty_kiss_tnc_fill(tnc)) < 0) {
goto error_fill;
} else if (filled == 0) {
return 0;
}
}
}
return patty_kiss_tnc_flush(tnc);
error_drain:
error_fill:
return -1;
}
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc, ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf, const void *buf,
size_t len) { size_t len) {
@ -338,7 +343,10 @@ patty_ax25_if_driver *patty_kiss_tnc_driver() {
.destroy = (patty_ax25_if_driver_destroy *)patty_kiss_tnc_destroy, .destroy = (patty_ax25_if_driver_destroy *)patty_kiss_tnc_destroy,
.fd = (patty_ax25_if_driver_fd *)patty_kiss_tnc_fd, .fd = (patty_ax25_if_driver_fd *)patty_kiss_tnc_fd,
.pending = (patty_ax25_if_driver_pending *)patty_kiss_tnc_pending, .pending = (patty_ax25_if_driver_pending *)patty_kiss_tnc_pending,
.recv = (patty_ax25_if_driver_recv *)patty_kiss_tnc_recv, .fill = (patty_ax25_if_driver_fill *)patty_kiss_tnc_fill,
.drain = (patty_ax25_if_driver_drain *)patty_kiss_tnc_drain,
.ready = (patty_ax25_if_driver_ready *)patty_kiss_tnc_ready,
.flush = (patty_ax25_if_driver_flush *)patty_kiss_tnc_flush,
.send = (patty_ax25_if_driver_send *)patty_kiss_tnc_send .send = (patty_ax25_if_driver_send *)patty_kiss_tnc_send
}; };