#include #include #include #include #include #include #include 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; } if ((iface->rx_buf = malloc(PATTY_AX25_IF_BUFSZ)) == NULL) { goto error_malloc_rx_buf; } else { iface->rx_bufsz = PATTY_AX25_IF_BUFSZ; } if ((iface->tx_buf = malloc(PATTY_AX25_IF_BUFSZ)) == NULL) { goto error_malloc_tx_buf; } else { iface->tx_bufsz = PATTY_AX25_IF_BUFSZ; } 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 *, uint8_t, 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, ssid, 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, ssid, 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, uint8_t ssid) { patty_list_item *item = iface->aliases->first; char buf[7]; uint8_t addr_ssid; if (patty_ax25_ntop(&iface->addr, buf, &addr_ssid, sizeof(buf)) < 0) { goto error_ntop_addr; } if (strncmp(buf, callsign, sizeof(buf)) == 0 && addr_ssid == ssid) { return &iface->addr; } while (item) { patty_ax25_addr *addr = item->value; if (patty_ax25_ntop(addr, buf, &addr_ssid, sizeof(buf)) < 0) { goto error_ntop; } if (strncmp(buf, callsign, sizeof(buf)) != 0) { goto next; } if (addr_ssid != ssid) { 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, uint8_t ssid) { patty_ax25_addr *addr; if (find_addr(iface, callsign, ssid) != NULL) { errno = EEXIST; goto error_exists; } if ((addr = malloc(sizeof(*addr))) == NULL) { goto error_malloc_addr; } if (patty_ax25_pton(callsign, ssid, 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, uint8_t ssid) { patty_list_item *item = iface->aliases->first; int i = 0; while (item) { char buf[7]; uint8_t addr_ssid; patty_ax25_addr *addr = item->value; if (patty_ax25_ntop(addr, buf, &addr_ssid, sizeof(buf)) < 0) { goto error_ntop; } if (strncmp(buf, callsign, sizeof(buf)) == 0 && addr_ssid == ssid) { 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) { if (patty_dict_get_with_hash(iface->promisc_fds, (uint32_t)fd)) { errno = EEXIST; goto error_exists; } if (patty_dict_set_with_hash(iface->promisc_fds, NULL + fd, sizeof(fd), NULL + fd, (uint32_t)fd) == NULL) { errno = ENOMEM; goto error_dict_set_with_hash; } return 0; error_dict_set_with_hash: error_exists: return -1; } int patty_ax25_if_promisc_delete(patty_ax25_if *iface, int fd) { if (patty_dict_delete_with_hash(iface->promisc_fds, (uint32_t)fd) < 0) { errno = ENOENT; goto error_notfound; } return 0; error_notfound: return -1; } struct promisc_frame { const void *buf; size_t len; patty_ax25_if *iface; }; static int handle_promisc_frame(void *key, size_t keysz, void *value, void *ctx) { int fd = (int)(value - NULL); struct promisc_frame *frame = ctx; if (write(fd, frame->buf, frame->len) < 0) { goto error_write; } return tcdrain(fd); error_write: return -1; } 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->rx_bufsz, &port)) < 0) { goto error_kiss_tnc_recv; } *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; } 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; }