From 5adbbca7869762dafaa35bbf401e51b6ff99921d Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Thu, 18 Jul 2024 21:49:16 +0200 Subject: [PATCH] Added functions for basic layer 2 transmission handling --- impl/src/layer2/layer2_tx.c | 144 +++++++++++++++++++++++++++++++ impl/src/layer2/layer2_tx.h | 61 +++++++++++++ impl/src/layer2/packet_structs.c | 6 +- impl/src/layer2/packet_structs.h | 14 +++ 4 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 impl/src/layer2/layer2_tx.c create mode 100644 impl/src/layer2/layer2_tx.h diff --git a/impl/src/layer2/layer2_tx.c b/impl/src/layer2/layer2_tx.c new file mode 100644 index 0000000..acf9e82 --- /dev/null +++ b/impl/src/layer2/layer2_tx.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include +#include + +#include "layer2_tx.h" + +#include "packet_structs.h" +#include "packet_queue.h" + +#include "config.h" + +#define SEQ_NR_MASK 0xF + +result_t layer2_tx_init(layer2_tx_t *ctx) +{ + packet_queue_init(&ctx->packet_queue); + ctx->next_packet_index = 0; + ctx->next_seq_nr = 0; + return OK; +} + + +void layer2_tx_destroy(layer2_tx_t *ctx) +{ + packet_queue_destroy(&ctx->packet_queue); +} + + +result_t layer2_tx_fill_packet_queue(layer2_tx_t *ctx, int tun_fd) +{ + struct pollfd pfd = {0}; + + pfd.fd = tun_fd; + pfd.events = POLLIN; + + layer2_packet_header_t header; + + header.dst_addr.addr[0] = 0xFFFF; + header.dst_addr.length = 1; + header.src_addr.addr[0] = 0x0001; + header.src_addr.length = 1; + header.msg_type = L2_MSG_TYPE_DATA; + header.rx_seq_nr = 0; // will be filled in layer2_tx_encode_next_packet() + header.tx_request = 0; + + int ret = 0; + + do { + int ret = poll(&pfd, 1, 0); + if(ret < 0) { + LOG(LVL_ERR, "poll: %s", strerror(errno)); + return ERR_SYSCALL; + } else if(ret > 0) { + header.tx_seq_nr = ctx->next_seq_nr; + + uint8_t packetbuf[2048]; + ret = read(tun_fd, packetbuf, sizeof(packetbuf)); + if(ret < 0) { + LOG(LVL_ERR, "read: %s", strerror(errno)); + return ERR_SYSCALL; + } else if(ret == 0) { + // no more data + break; + } + + packet_queue_add(&ctx->packet_queue, &header, packetbuf, ret); + + ctx->next_seq_nr++; + ctx->next_seq_nr &= SEQ_NR_MASK; + } + } while((ret > 0) && (packet_queue_get_free_space(&ctx->packet_queue) > 0)); + + return OK; +} + + +size_t layer2_tx_encode_next_packet(layer2_tx_t *ctx, uint8_t ack_seq_nr, uint8_t *buf, size_t buf_len) +{ + const packet_queue_entry_t *entry = packet_queue_get(&ctx->packet_queue, ctx->next_packet_index); + + if(!entry) { + // no more entries + return 0; + } + + unsigned int crc_size = crc_sizeof_key(PAYLOAD_CRC_SCHEME); + + assert(buf_len >= 18 + crc_size + entry->data_len); + + layer2_packet_header_t header = entry->header; + header.rx_seq_nr = ack_seq_nr; + + // encode the header + size_t packet_size = layer2_encode_packet_header(&header, buf); + + // add the payload data + memcpy(buf + packet_size, entry->data, entry->data_len); + packet_size += entry->data_len; + + // calculate CRC of everything and append it to the packet + crc_append_key(PAYLOAD_CRC_SCHEME, buf, packet_size); + + packet_size += crc_size; + + ctx->next_packet_index++; + + return packet_size; +} + + +void layer2_tx_restart(layer2_tx_t *ctx) +{ + ctx->next_packet_index = 0; +} + + +void layer2_tx_handle_ack(layer2_tx_t *ctx, uint8_t acked_seq) +{ + ctx->next_packet_index = 0; + + size_t packets_to_remove = 0; + size_t packets_available = packet_queue_get_used_space(&ctx->packet_queue); + + for(size_t i = 0; i < packets_available; i++) { + const packet_queue_entry_t *entry = packet_queue_get(&ctx->packet_queue, i); + + if(entry->header.tx_seq_nr == acked_seq) { + break; + } + + packets_to_remove++; + } + + packet_queue_delete(&ctx->packet_queue, packets_to_remove); +} + + +bool layer2_tx_can_transmit(const layer2_tx_t *ctx) +{ + return packet_queue_get_used_space(&ctx->packet_queue) != 0; +} diff --git a/impl/src/layer2/layer2_tx.h b/impl/src/layer2/layer2_tx.h new file mode 100644 index 0000000..6685060 --- /dev/null +++ b/impl/src/layer2/layer2_tx.h @@ -0,0 +1,61 @@ +#ifndef LAYER2_TX_H +#define LAYER2_TX_H + +#include + +#include "packet_queue.h" + +typedef struct { + packet_queue_t packet_queue; + size_t next_packet_index; + uint8_t next_seq_nr; +} layer2_tx_t; + + +/*!\brief Initialize the layer 2 transmitter context. + */ +result_t layer2_tx_init(layer2_tx_t *ctx); + +/*!\brief Destroy the given layer 2 transmitter context. + */ +void layer2_tx_destroy(layer2_tx_t *ctx); + +/*!\brief Fill the packet queue from the given TUN device. + * \param ctx The transmitter context. + * \param tun_fd File descriptor of the TUN device to read packets from. + */ +result_t layer2_tx_fill_packet_queue(layer2_tx_t *ctx, int tun_fd); + +/*!\brief Encode the next packet for transmission. + * + * \note + * If no more packets are available, this function returns zero. In that case, + * either \ref layer2_tx_restart() or \ref layer2_tx_handle_ack() must be + * called to handle retransmits correctly. + * + * \param ctx The transmitter context. + * \param ack_seq_nr The received sequence number to send as an acknowledgement. + * \param buf Where to write the encoded packet data. + * \param buf_len Space available in the buffer. + * \returns The number of bytes written to buf or zero if no packet was available. + */ +size_t layer2_tx_encode_next_packet(layer2_tx_t *ctx, uint8_t ack_seq_nr, uint8_t *buf, size_t buf_len); + +/*!\brief Restart the transmission from the beginning of the packet queue. + */ +void layer2_tx_restart(layer2_tx_t *ctx); + +/*!\brief Handle acknowledgements. + * \details + * Removes all packets before the given sequence number from the queue. + * + * \param ctx The transmitter context. + * \param acked_seq The acknowledged (= next expected) sequence number. + */ +void layer2_tx_handle_ack(layer2_tx_t *ctx, uint8_t acked_seq); + +/*!\brief Check if there are packets queued for transmission. + */ +bool layer2_tx_can_transmit(const layer2_tx_t *ctx); + +#endif // LAYER2_TX_H diff --git a/impl/src/layer2/packet_structs.c b/impl/src/layer2/packet_structs.c index 73bb469..9396300 100644 --- a/impl/src/layer2/packet_structs.c +++ b/impl/src/layer2/packet_structs.c @@ -36,11 +36,7 @@ size_t layer2_encode_packet_header(const layer2_packet_header_t *header, uint8_t bool layer2_decode_packet_header(const uint8_t *encoded, size_t encoded_len, layer2_packet_header_t *header) { // check if there are enough bytes for the minimum header size: - // - 1 byte packet info - // - 1 byte sequence numbers - // - 2 bytes source address - // - 2 bytes destination address - if(encoded_len < 6) { + if(encoded_len < LAYER2_PACKET_HEADER_ENCODED_SIZE_MIN) { return false; } diff --git a/impl/src/layer2/packet_structs.h b/impl/src/layer2/packet_structs.h index 82f42d4..e1fa842 100644 --- a/impl/src/layer2/packet_structs.h +++ b/impl/src/layer2/packet_structs.h @@ -26,6 +26,20 @@ typedef struct layer2_packet_header_s { ham64_t dst_addr; //!< destination HAM-64 address } layer2_packet_header_t; +// maximum header size +// - 1 byte packet info +// - 1 byte sequence numbers +// - 8 bytes source address +// - 8 bytes destination address +#define LAYER2_PACKET_HEADER_ENCODED_SIZE_MAX 18 + +// minimum header size: +// - 1 byte packet info +// - 1 byte sequence numbers +// - 2 bytes source address +// - 2 bytes destination address +#define LAYER2_PACKET_HEADER_ENCODED_SIZE_MIN 6 + /*!\brief Encode a layer2 packet header to the transmitted form. * \param header The header structure to encode. * \param encoded A byte array where the encoded header will be stored. Must have space for 18 bytes.