#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include static int bind_pty(patty_ax25_sock *sock) { struct termios t; if ((sock->fd = open("/dev/ptmx", O_RDWR)) < 0) { goto error_open; } if (grantpt(sock->fd) < 0) { goto error_grantpt; } if (unlockpt(sock->fd) < 0) { goto error_unlockpt; } if (ptsname_r(sock->fd, sock->path, sizeof(sock->path)) < 0) { goto error_ptsname_r; } if (tcgetattr(sock->fd, &t) < 0) { goto error_tcgetattr; } cfmakeraw(&t); if (tcsetattr(sock->fd, TCSANOW, &t) < 0) { goto error_tcsetattr; } return 0; error_tcsetattr: error_tcgetattr: error_ptsname_r: error_unlockpt: error_grantpt: close(sock->fd); error_open: return -1; } patty_ax25_sock *patty_ax25_sock_new(enum patty_ax25_proto proto, enum patty_ax25_sock_type type) { patty_ax25_sock *sock; if ((sock = malloc(sizeof(*sock))) == NULL) { goto error_malloc_sock; } memset(sock, '\0', sizeof(*sock)); if ((sock->buf = malloc(PATTY_AX25_SOCK_BUFSZ)) == NULL) { goto error_malloc_buf; } if (bind_pty(sock) < 0) { goto error_bind_pty; } sock->bufsz = PATTY_AX25_SOCK_BUFSZ; sock->proto = proto; sock->type = type; sock->status = PATTY_AX25_SOCK_CLOSED; sock->mode = PATTY_AX25_SOCK_DM; sock->n_maxlen = PATTY_AX25_FRAME_DEFAULT_MAXLEN; sock->n_window = PATTY_AX25_FRAME_DEFAULT_WINDOW; return sock; error_bind_pty: free(sock->buf); error_malloc_buf: free(sock); error_malloc_sock: return NULL; } void patty_ax25_sock_reset(patty_ax25_sock *sock) { sock->status = PATTY_AX25_SOCK_CLOSED; sock->mode = PATTY_AX25_SOCK_DM; sock->timer_ack = 0; sock->timer_response = 0; sock->timer_keepalive = 0; sock->n_maxlen = 0; sock->n_retry = 0; sock->n_window = 0; sock->seq_send = 0; sock->seq_recv = 0; } void patty_ax25_sock_destroy(patty_ax25_sock *sock) { if (sock->fd) { if (sock->iface) { (void)patty_ax25_if_promisc_delete(sock->iface, sock->fd); } close(sock->fd); } free(sock->buf); free(sock); } char *patty_ax25_sock_pty(patty_ax25_sock *sock) { return sock->path; } int patty_ax25_sock_bind_if(patty_ax25_sock *sock, patty_ax25_if *iface) { sock->iface = iface; if (sock->type == PATTY_AX25_SOCK_RAW) { if (patty_ax25_if_promisc_add(iface, sock->fd) < 0) { goto error_if_promisc_add; } } return 0; error_if_promisc_add: return -1; } void patty_ax25_sock_seq_send_incr(patty_ax25_sock *sock) { if (sock->mode == PATTY_AX25_SOCK_SABME) { sock->seq_send = (sock->seq_send + 1) & 0x07; } else { sock->seq_send = (sock->seq_send + 1) & 0x7f; } } void patty_ax25_sock_seq_recv_incr(patty_ax25_sock *sock) { if (sock->mode == PATTY_AX25_SOCK_SABME) { sock->seq_recv = (sock->seq_recv + 1) & 0x07; } else { sock->seq_recv = (sock->seq_recv + 1) & 0x7f; } } static size_t copy_addr_to_tx_buf(patty_ax25_sock *sock) { void *buf = sock->iface->tx_buf; unsigned int i; size_t offset = 0; memcpy((uint8_t *)buf + offset, &sock->remote, sizeof(patty_ax25_addr)); offset += sizeof(patty_ax25_addr); ((uint8_t *)buf)[offset-1] &= ~1; memcpy((uint8_t *)buf + offset, &sock->local, sizeof(patty_ax25_addr)); offset += sizeof(patty_ax25_addr); ((uint8_t *)buf)[offset-1] &= ~1; for (i=0; ihops; i++) { memcpy((uint8_t *)buf + offset, &sock->repeaters[i], sizeof(patty_ax25_addr)); offset += sizeof(patty_ax25_addr); ((uint8_t *)buf)[offset-1] &= ~1; } ((uint8_t *)buf)[offset-1] |= 1; return offset; } static inline int toobig(patty_ax25_sock *sock, uint16_t control, size_t infolen) { return PATTY_AX25_FRAME_SIZE(sock->mode == PATTY_AX25_SOCK_SABME? PATTY_AX25_FRAME_EXTENDED: PATTY_AX25_FRAME_NORMAL, sock->hops, control, infolen) > sock->iface->tx_bufsz? 1: 0; } ssize_t patty_ax25_sock_send(patty_ax25_sock *sock, uint16_t control, void *info, size_t infolen) { size_t offset; uint8_t *buf = (uint8_t *)sock->iface->tx_buf; if (toobig(sock, control, infolen)) { goto error_toobig; } offset = copy_addr_to_tx_buf(sock); if (sock->mode == PATTY_AX25_SOCK_SABME) { buf[offset++] = (control & 0xff00) >> 8; buf[offset++] = control & 0x00ff; } else { buf[offset++] = control; } if (info) { buf[offset++] = (uint8_t)sock->proto; memcpy(buf + offset, info, infolen); offset += infolen; } return patty_ax25_if_send(sock->iface, buf, offset); error_toobig: return -1; } static uint16_t control_i(patty_ax25_sock *sock, int flag) { switch (sock->mode) { case PATTY_AX25_SOCK_SABM: return ((sock->seq_recv & 0x07) << 5) | ((sock->seq_send & 0x07)) | (flag << 4); case PATTY_AX25_SOCK_SABME: return ((sock->seq_recv & 0x7f) << 9) | ((sock->seq_send & 0x7f)) | (flag << 8); default: return 0; } } static uint16_t control_s(patty_ax25_sock *sock, enum patty_ax25_frame_type type, int flag) { switch (sock->mode) { case PATTY_AX25_SOCK_SABM: return ((sock->seq_recv & 0x07) << 5) | (type & 0x0f) | (flag << 4); case PATTY_AX25_SOCK_SABME: return ((sock->seq_recv & 0x7f) << 9) | (type & 0x0f) | (flag << 8); default: return 0; } } static uint16_t control_u(enum patty_ax25_frame_type type, int flag) { return (type & PATTY_AX25_FRAME_U_MASK) | (flag << 4); } ssize_t patty_ax25_sock_send_rr(patty_ax25_sock *sock) { return patty_ax25_sock_send(sock, control_s(sock, PATTY_AX25_FRAME_RR, 1), NULL, 0); } ssize_t patty_ax25_sock_send_rnr(patty_ax25_sock *sock) { return patty_ax25_sock_send(sock, control_s(sock, PATTY_AX25_FRAME_RNR, 1), NULL, 0); } ssize_t patty_ax25_sock_send_rej(patty_ax25_sock *sock) { return patty_ax25_sock_send(sock, control_s(sock, PATTY_AX25_FRAME_REJ, 1), NULL, 0); } ssize_t patty_ax25_sock_send_srej(patty_ax25_sock *sock) { return patty_ax25_sock_send(sock, control_s(sock, PATTY_AX25_FRAME_SREJ, 1), NULL, 0); } ssize_t patty_ax25_sock_send_sabm(patty_ax25_sock *sock, int flag) { return patty_ax25_sock_send(sock, control_u(PATTY_AX25_FRAME_SABM, flag), NULL, 0); } ssize_t patty_ax25_sock_send_sabme(patty_ax25_sock *sock, int flag) { return patty_ax25_sock_send(sock, control_u(PATTY_AX25_FRAME_SABME, flag), NULL, 0); } ssize_t patty_ax25_sock_send_disc(patty_ax25_sock *sock, int flag) { return patty_ax25_sock_send(sock, control_u(PATTY_AX25_FRAME_DISC, flag), NULL, 0); } ssize_t patty_ax25_sock_write(patty_ax25_sock *sock, void *buf, size_t len) { uint16_t control; if (sock->type == PATTY_AX25_SOCK_RAW) { return patty_ax25_if_send(sock->iface, buf, len); } if (sock->mode == PATTY_AX25_SOCK_DM) { errno = EBADF; goto error_invalid_mode; } control = control_i(sock, 0); if (patty_ax25_sock_send(sock, control, buf, len) < 0) { goto error_send; } patty_ax25_sock_seq_send_incr(sock); return len; error_send: error_invalid_mode: return -1; }