Added functions for basic layer 2 transmission handling

This commit is contained in:
Thomas Kolb 2024-07-18 21:49:16 +02:00
parent 2348cd0c88
commit 5adbbca786
4 changed files with 220 additions and 5 deletions

144
impl/src/layer2/layer2_tx.c Normal file
View file

@ -0,0 +1,144 @@
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#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;
}

View file

@ -0,0 +1,61 @@
#ifndef LAYER2_TX_H
#define LAYER2_TX_H
#include <results.h>
#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

View file

@ -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;
}

View file

@ -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.