#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; } /* * 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; }