patty/src/if.c

409 lines
8.8 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <patty/ax25.h>
static int init_tnc(patty_ax25_if *iface, patty_ax25_if_kiss_tnc_info *info) {
static char *prefix = "kiss";
static int number = 0;
iface->tnc = (info->type == PATTY_AX25_IF_KISS_TNC_INFO_FD)?
patty_kiss_tnc_new_fd(info->fd):
patty_kiss_tnc_new(info->path);
if (iface->tnc == NULL) {
goto error_kiss_tnc_new;
}
iface->type = PATTY_AX25_IF_KISS_TNC;
snprintf(iface->name, sizeof(iface->name), "%s%d", prefix, number++);
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(int opts, patty_ax25_if_info *info) {
patty_ax25_if *iface;
if ((iface = malloc(sizeof(*iface))) == NULL) {
goto error_malloc_iface;
}
memset(iface, '\0', sizeof(*iface));
if (info->addr.callsign[0] == '\0') {
errno = EINVAL;
goto error_invalid_address;
}
/*
* TODO: Eventually inherit the half/full-duplex flag from the TNC this
* interface is bound to
*/
iface->flags = PATTY_AX25_PARAM_DEFAULT_CLASSES
| PATTY_AX25_PARAM_CLASSES_HALF_DUPLEX;
if ((iface->rx_buf = malloc(PATTY_AX25_IF_DEFAULT_MRU)) == NULL) {
goto error_malloc_rx_buf;
} else {
iface->mru = PATTY_AX25_IF_DEFAULT_MRU;
}
if ((iface->tx_buf = malloc(PATTY_AX25_IF_DEFAULT_MTU)) == NULL) {
goto error_malloc_tx_buf;
} else {
iface->mtu = PATTY_AX25_IF_DEFAULT_MTU;
}
if ((iface->aliases = patty_list_new()) == NULL) {
goto error_list_new_aliases;
}
if ((iface->promisc_fds = patty_dict_new()) == NULL) {
goto error_dict_new_promisc_fds;
}
switch (PATTY_AX25_IF_OPT_TYPE(opts)) {
case PATTY_AX25_IF_KISS_TNC:
if (init_tnc(iface, (patty_ax25_if_kiss_tnc_info *)info) < 0) {
goto error_init;
}
break;
default:
errno = EINVAL;
goto error_invalid_if_type;
}
memcpy(&iface->addr, &info->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);
error_list_new_aliases:
free(iface->tx_buf);
error_malloc_tx_buf:
free(iface->rx_buf);
error_malloc_rx_buf:
free(iface);
error_malloc_iface:
error_invalid_address:
return NULL;
}
void patty_ax25_if_destroy(patty_ax25_if *iface) {
switch (iface->type) {
case PATTY_AX25_IF_KISS_TNC:
destroy_tnc(iface);
break;
default:
break;
}
patty_dict_destroy(iface->promisc_fds);
patty_list_destroy(iface->aliases);
free(iface->tx_buf);
free(iface->rx_buf);
free(iface);
}
int patty_ax25_if_addr_each(patty_ax25_if *iface,
int (*callback)(char *, void *), void *ctx) {
patty_list_item *item = iface->aliases->first;
char buf[7];
uint8_t ssid;
if (patty_ax25_ntop(&iface->addr, buf, &ssid, sizeof(buf)) < 0) {
goto error_ntop_addr;
}
if (callback(buf, ctx) < 0) {
goto error_callback_addr;
}
while (item) {
patty_ax25_addr *addr = item->value;
if (patty_ax25_ntop(addr, buf, &ssid, sizeof(buf)) < 0) {
goto error_ntop;
}
if (callback(buf, ctx) < 0) {
goto error_callback;
}
item = item->next;
}
return 0;
error_callback:
error_ntop:
error_callback_addr:
error_ntop_addr:
return -1;
}
static patty_ax25_addr *find_addr(patty_ax25_if *iface,
const char *callsign) {
patty_list_item *item = iface->aliases->first;
char buf[7];
uint8_t ssid;
if (patty_ax25_ntop(&iface->addr, buf, &ssid, sizeof(buf)) < 0) {
goto error_ntop_addr;
}
if (strncmp(buf, callsign, sizeof(buf)) == 0) {
return &iface->addr;
}
while (item) {
patty_ax25_addr *addr = item->value;
if (patty_ax25_ntop(addr, buf, &ssid, sizeof(buf)) < 0) {
goto error_ntop;
}
if (strncmp(buf, callsign, sizeof(buf)) != 0) {
goto next;
}
return addr;
next:
item = item->next;
}
error_ntop:
error_ntop_addr:
return NULL;
}
int patty_ax25_if_addr_add(patty_ax25_if *iface,
const char *callsign) {
patty_ax25_addr *addr;
if (find_addr(iface, callsign) != NULL) {
errno = EEXIST;
goto error_exists;
}
if ((addr = malloc(sizeof(*addr))) == NULL) {
goto error_malloc_addr;
}
if (patty_ax25_pton(callsign, 0, addr) < 0) {
goto error_pton;
}
if ((patty_list_append(iface->aliases, addr)) == NULL) {
goto error_list_append;
}
return 0;
error_list_append:
error_pton:
free(addr);
error_malloc_addr:
error_exists:
return -1;
}
int patty_ax25_if_addr_delete(patty_ax25_if *iface,
const char *callsign) {
patty_list_item *item = iface->aliases->first;
int i = 0;
while (item) {
char buf[7];
uint8_t ssid;
patty_ax25_addr *addr = item->value;
if (patty_ax25_ntop(addr, buf, &ssid, sizeof(buf)) < 0) {
goto error_ntop;
}
if (strncmp(buf, callsign, sizeof(buf)) == 0) {
if (patty_list_splice(iface->aliases, i) == NULL) {
goto error_list_splice;
}
}
item = item->next;
i++;
}
return 0;
error_list_splice:
error_ntop:
return -1;
}
int patty_ax25_if_promisc_add(patty_ax25_if *iface,
int fd) {
patty_kiss_tnc *tnc;
if (patty_dict_get(iface->promisc_fds, (uint32_t)fd)) {
errno = EEXIST;
goto error_exists;
}
if ((tnc = patty_kiss_tnc_new_fd(fd)) == NULL) {
goto error_kiss_tnc_new_fd;
}
if (patty_dict_set(iface->promisc_fds,
(uint32_t)fd,
tnc) == NULL) {
errno = ENOMEM;
goto error_dict_set;
}
return 0;
error_dict_set:
patty_kiss_tnc_destroy(tnc);
error_kiss_tnc_new_fd:
error_exists:
return -1;
}
int patty_ax25_if_promisc_delete(patty_ax25_if *iface,
int fd) {
patty_kiss_tnc *tnc;
if ((tnc = patty_dict_get(iface->promisc_fds, (uint32_t)fd)) == NULL) {
errno = ENOENT;
goto error_notfound;
}
patty_kiss_tnc_destroy(tnc);
return patty_dict_delete(iface->promisc_fds, (uint32_t)fd);
error_notfound:
return -1;
}
struct promisc_frame {
const void *buf;
size_t len;
patty_ax25_if *iface;
};
static int handle_promisc_frame(uint32_t key,
void *value,
void *ctx) {
patty_kiss_tnc *tnc = value;
struct promisc_frame *frame = ctx;
return patty_kiss_tnc_send(tnc, frame->buf, frame->len, 0);
}
ssize_t patty_ax25_if_pending(patty_ax25_if *iface) {
return patty_kiss_tnc_pending(iface->tnc);
}
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,
iface->rx_buf,
iface->mru,
&port)) < 0) {
goto error_kiss_tnc_recv;
} else if (readlen == 0) {
goto done;
}
*buf = iface->rx_buf;
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_kiss_tnc_recv:
return -1;
}
ssize_t patty_ax25_if_send(patty_ax25_if *iface,
const void *buf,
size_t len) {
ssize_t wrlen;
struct promisc_frame frame;
if ((wrlen = patty_kiss_tnc_send(iface->tnc, buf, len, 0)) < 0) {
goto error_kiss_tnc_send;
}
iface->stats.tx_frames++;
iface->stats.tx_bytes += wrlen;
frame.buf = buf;
frame.len = wrlen;
frame.iface = iface;
if (patty_dict_each(iface->promisc_fds,
handle_promisc_frame,
&frame) < 0) {
goto error_handle_promisc_frame;
}
return wrlen;
error_handle_promisc_frame:
error_kiss_tnc_send:
return -1;
}