diff --git a/impl/src/layer2/digipeater.c b/impl/src/layer2/digipeater.c new file mode 100644 index 0000000..06f324b --- /dev/null +++ b/impl/src/layer2/digipeater.c @@ -0,0 +1,122 @@ +#include +#include + +#include "digipeater.h" + +#include "config.h" +#include "layer2/connection.h" +#include "layer2/ham64.h" +#include "results.h" +#include "utils.h" + +result_t digipeater_init(digipeater_ctx_t *ctx, const ham64_t *my_addr, digipeater_data_callback_t data_cb) +{ + ctx->my_addr = *my_addr; + ctx->data_cb = data_cb; + + ctx->state = DIGIPEATER_STATE_CONN; + + uint64_t now = get_hires_time(); + ctx->next_beacon_time = now + HRTIME_MS(BEACON_INTERVAL_MS); + + return connection_list_init(&ctx->conn_list); +} + + +void digipeater_destroy(digipeater_ctx_t *ctx) +{ + connection_list_destroy(&ctx->conn_list); +} + + +result_t digipeater_handle_packet(digipeater_ctx_t *ctx, const uint8_t *buf, size_t buf_len) +{ + // check the CRC + size_t packet_size = buf_len - crc_sizeof_key(PAYLOAD_CRC_SCHEME); + + if(!crc_check_key(PAYLOAD_CRC_SCHEME, (unsigned char*)buf, packet_size)) { + LOG(LVL_ERR, "CRC check failed!"); + return ERR_INTEGRITY; + } + + // decode the header + layer2_packet_header_t header; + + if(!layer2_decode_packet_header(buf, buf_len, &header)) { + LOG(LVL_ERR, "Header could not be decoded!"); + return ERR_INTEGRITY; + } + + layer2_dump_packet_header(LVL_DUMP, &header); + + // check if the packet really should be handled by us + if(ham64_is_equal(&ctx->my_addr, &header.src_addr)) { + LOG(LVL_DEBUG, "Packet is from ourselves. Ignored."); + return OK; + } + + if(!ham64_is_equal(&header.dst_addr, &ctx->my_addr)) { + char fmt_dst_addr[HAM64_FMT_MAX_LEN]; + char fmt_my_addr[HAM64_FMT_MAX_LEN]; + + ham64_format(&header.dst_addr, fmt_dst_addr); + ham64_format(&ctx->my_addr, fmt_my_addr); + + LOG(LVL_ERR, "Packet has the wrong destination address: got %s, expected %s", + fmt_dst_addr, fmt_my_addr); + + return ERR_INVALID_ADDRESS; + } + + // FIXME: handle connection management packets here + // FIXME: handle data and empty packets in the current connection + + size_t header_size = layer2_get_encoded_header_size(&header); + + const uint8_t *payload = buf + header_size; + size_t payload_len = packet_size - header_size; + + connection_handle_packet_prechecked(current_conn, &header, payload, payload_len); + + return OK; +} + + +size_t digipeater_encode_next_packet(digipeater_ctx_t *ctx, uint8_t *buf, size_t buf_len, bool *end_burst) +{ + uint64_t now = get_hires_time(); + + *end_burst = false; + + if(now > ctx->next_beacon_time) { + // TODO: build a beacon packet + return 0; + } + + // TODO: pull packets from the current connection + + return 0; +} + + +bool digipeater_can_transmit(const digipeater_ctx_t *ctx) +{ + uint64_t now = get_hires_time(); + + if(now > ctx->next_beacon_time) { + return true; + } + + // TODO: also true if next TX time has expired and there are packets waiting for this connection. + + /*return (packet_queue_get_used_space(&ctx->packet_queue) != 0) + && (packet_queue_get(&ctx->packet_queue, ctx->next_packet_index) != NULL); */ + return false; +} + + +result_t digipeater_maintain(digipeater_ctx_t *ctx) +{ + (void)ctx; + return OK; +} diff --git a/impl/src/layer2/digipeater.h b/impl/src/layer2/digipeater.h new file mode 100644 index 0000000..5fb75b8 --- /dev/null +++ b/impl/src/layer2/digipeater.h @@ -0,0 +1,94 @@ +/* + * This file contains functions to handle a single layer 2 digipeater. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Copyright (C) 2024 Thomas Kolb + */ + +#ifndef DIGIPEATER_H +#define DIGIPEATER_H + +#include +#include + +#include "connection_list.h" + +struct digipeater_ctx_s; + +typedef enum { + DIGIPEATER_STATE_BEACON, //!< Beacon sent, waiting for connection requests + DIGIPEATER_STATE_CONN, //!< Handling client connections +} digipeater_state_t; + +/*!\brief Type for a callback function that is called when a data packet was received. */ +typedef void (*digipeater_data_callback_t)(struct digipeater_ctx_s *conn, const uint8_t *data, size_t len); + +typedef struct digipeater_ctx_s { + digipeater_state_t state; //!< Current operating state + + digipeater_data_callback_t data_cb; //!< Callback function for received data packets. + + ham64_t my_addr; //!< The local link layer address. + + uint64_t next_beacon_time; //!< Absolute timestamp of the next beacon transmission. + + connection_list_t conn_list; //!< List of connections. +} digipeater_ctx_t; + + +/*!\brief Initialize the digipeater context. + * + * \param ctx The digipeater context to initialize. + * \param my_addr The local link layer address. + * \param data_cb Callback function that handles received (decoded) data packets. + * \returns OK if everything worked or a fitting error code. + */ +result_t digipeater_init( + digipeater_ctx_t *ctx, + const ham64_t *my_addr, + digipeater_data_callback_t data_cb); + +/*!\brief Destroy the given digipeater context. + */ +void digipeater_destroy(digipeater_ctx_t *ctx); + +/*!\brief Handle a received packet. + * + * \param ctx The digipeater context. + * \param buf Pointer to the packet data. + * \param buf_len Length of the packet. + * \returns A result code from the packet handling procedure. + */ +result_t digipeater_handle_packet(digipeater_ctx_t *ctx, const uint8_t *buf, size_t buf_len); + +/*!\brief Enqueue a packet for transmission. + * \param ctx The digipeater context. + * \param tun_fd File descriptor for an open TUN device. + */ +result_t digipeater_fill_packet_queues_from_tundev(digipeater_ctx_t *ctx, int tun_fd); + +/*!\brief Encode the next packet for transmission. + * + * \note + * If no packets are currently available for transmission, this function returns zero. + * + * \param ctx The digipeater context. + * \param buf Where to write the encoded packet data. + * \param buf_len Space available in the buffer. + * \param end_burst Set to true if this packet should end the current burst and start the transmission. + * \returns The number of bytes written to buf or zero if no packet was available. + */ +size_t digipeater_encode_next_packet(digipeater_ctx_t *ctx, uint8_t *buf, size_t buf_len, bool *end_burst); + +/*!\brief Check if there are packets queued for transmission. + */ +bool digipeater_can_transmit(const digipeater_ctx_t *ctx); + +/*!\brief Handle internal maintenance tasks. + * + * This should be called periodically to handle timeouts and retransmissions. + */ +result_t digipeater_maintain(digipeater_ctx_t *ctx); + +#endif // DIGIPEATER_H