Multiple fixes in packet handling
All checks were successful
/ build-hamnet70 (push) Successful in 33s
/ build-doc (push) Successful in 19s
/ deploy-doc (push) Has been skipped

- add and handle layer 2 packet type correctly in data packets
- don't produce garbage packets if a packet could not be decoded or was not a
  data packet
- handle beacon/connection request/connection parameters handshake
- digipeater cycle timeout does not reset beacon timer anymore. This prevented
  any beacon transmission.
- Reset the connection timeout when empty packets are received
This commit is contained in:
Thomas Kolb 2024-12-15 22:24:21 +01:00
parent f85bb7f83e
commit d77f4d4498
6 changed files with 107 additions and 33 deletions

View file

@ -100,6 +100,9 @@ result_t connection_handle_packet(connection_ctx_t *ctx, const uint8_t *buf, siz
break; break;
} }
data_packet->payload_type = L2_PAYLOAD_TYPE_INVALID;
data_packet->payload_len = 0;
// check the CRC // check the CRC
size_t packet_size = buf_len - crc_sizeof_key(PAYLOAD_CRC_SCHEME); size_t packet_size = buf_len - crc_sizeof_key(PAYLOAD_CRC_SCHEME);
@ -122,7 +125,8 @@ result_t connection_handle_packet(connection_ctx_t *ctx, const uint8_t *buf, siz
return OK; return OK;
} }
if(!ham64_is_equal(&header.dst_addr, &ctx->my_addr)) { if(ham64_get_addr_type(&header.dst_addr) != HAM64_ADDR_TYPE_BROADCAST
&& !ham64_is_equal(&header.dst_addr, &ctx->my_addr)) {
char fmt_dst_addr[HAM64_FMT_MAX_LEN]; char fmt_dst_addr[HAM64_FMT_MAX_LEN];
char fmt_my_addr[HAM64_FMT_MAX_LEN]; char fmt_my_addr[HAM64_FMT_MAX_LEN];
@ -157,29 +161,41 @@ static result_t handle_conn_mgmt(
uint8_t packet_type = payload[0]; uint8_t packet_type = payload[0];
if(packet_type == CONN_MGMT_TYPE_BEACON) { switch(packet_type) {
if(ctx->conn_state == CONN_STATE_CONNECTING) { case CONN_MGMT_TYPE_BEACON:
LOG(LVL_INFO, "Received beacon; queueing connection request."); if(ctx->conn_state == CONN_STATE_CONNECTING) {
LOG(LVL_INFO, "Received beacon; queueing connection request.");
// enqueue a connection request packet // enqueue a connection request packet
layer2_packet_header_t conn_request_header; layer2_packet_header_t conn_request_header;
conn_request_header.tx_request = true; conn_request_header.tx_request = true;
conn_request_header.dst_addr = ctx->peer_addr; conn_request_header.dst_addr = ctx->peer_addr;
conn_request_header.src_addr = ctx->my_addr; conn_request_header.src_addr = ctx->my_addr;
conn_request_header.msg_type = L2_MSG_TYPE_CONN_MGMT; conn_request_header.msg_type = L2_MSG_TYPE_CONN_MGMT;
conn_request_header.rx_seq_nr = 0; conn_request_header.rx_seq_nr = 0;
conn_request_header.tx_seq_nr = 0; conn_request_header.tx_seq_nr = 0;
// create a persistent copy of the packet data. // create a persistent copy of the packet data.
uint8_t packetbuf[1]; uint8_t packetbuf[1];
packetbuf[0] = CONN_MGMT_TYPE_CONNECTION_REQUEST; packetbuf[0] = CONN_MGMT_TYPE_CONNECTION_REQUEST;
connection_enqueue_packet(ctx, &conn_request_header, packetbuf, 1); connection_enqueue_packet(ctx, &conn_request_header, packetbuf, 1);
} else { } else {
LOG(LVL_WARN, "Beacons are ignored in states other than CONNECTING."); LOG(LVL_WARN, "Beacons are ignored in states other than CONNECTING.");
return ERR_INVALID_STATE; return ERR_INVALID_STATE;
} }
break;
case CONN_MGMT_TYPE_CONNECTION_PARAMETERS:
LOG(LVL_INFO, "Connection parameters received! -> connection established");
ctx->next_expected_seq = 1; // connection parameters are packet 0
ctx->conn_state = CONN_STATE_ESTABLISHED;
break;
default:
LOG(LVL_WARN, "Ignored connection management type %d", packet_type);
break;
} }
return OK; return OK;
@ -231,6 +247,10 @@ result_t connection_handle_packet_prechecked(
switch(header->msg_type) { switch(header->msg_type) {
case L2_MSG_TYPE_EMPTY: case L2_MSG_TYPE_EMPTY:
LOG(LVL_DEBUG, "Empty packet: accepted ACK for %u.", ctx->last_acked_seq); LOG(LVL_DEBUG, "Empty packet: accepted ACK for %u.", ctx->last_acked_seq);
// empty packets also reset the timeout timer
ctx->last_rx_time = get_hires_time();
// handle the acknowledgement internally // handle the acknowledgement internally
connection_handle_ack(ctx, header->rx_seq_nr, false); connection_handle_ack(ctx, header->rx_seq_nr, false);
return OK; // do not ACK return OK; // do not ACK
@ -318,7 +338,11 @@ result_t connection_enqueue_packet(
} }
result_t connection_enqueue_data_packet(connection_ctx_t *ctx, uint8_t *buf, size_t buf_len) result_t connection_enqueue_data_packet(
connection_ctx_t *ctx,
layer2_payload_type_t payload_type,
uint8_t *buf,
size_t buf_len)
{ {
// check the connection state // check the connection state
switch(ctx->conn_state) { switch(ctx->conn_state) {
@ -343,7 +367,12 @@ result_t connection_enqueue_data_packet(connection_ctx_t *ctx, uint8_t *buf, siz
header.tx_request = 0; header.tx_request = 0;
header.tx_seq_nr = ctx->next_seq_nr; header.tx_seq_nr = ctx->next_seq_nr;
ERR_CHECK(connection_enqueue_packet(ctx, &header, buf, buf_len)); uint8_t packet_with_type[buf_len + 1];
packet_with_type[0] = (uint8_t)payload_type;
memcpy(packet_with_type+1, buf, buf_len);
ERR_CHECK(connection_enqueue_packet(ctx, &header, packet_with_type, sizeof(packet_with_type)));
ctx->next_seq_nr++; ctx->next_seq_nr++;
ctx->next_seq_nr &= SEQ_NR_MASK; ctx->next_seq_nr &= SEQ_NR_MASK;
@ -531,8 +560,11 @@ void connection_handle_ack(connection_ctx_t *ctx, uint8_t acked_seq, bool do_ack
packet_queue_delete(&ctx->packet_queue, packets_to_remove); packet_queue_delete(&ctx->packet_queue, packets_to_remove);
// send the next requested packet (all previous ones were deleted above). // send the next requested packet (all previous ones were deleted above).
assert(ctx->next_packet_index >= packets_to_remove); if(ctx->next_packet_index >= packets_to_remove) {
ctx->next_packet_index -= packets_to_remove; ctx->next_packet_index -= packets_to_remove;
} else {
ctx->next_packet_index = 0;
}
packets_available = packet_queue_get_used_space(&ctx->packet_queue); packets_available = packet_queue_get_used_space(&ctx->packet_queue);

View file

@ -138,10 +138,15 @@ result_t connection_enqueue_packet(
/*!\brief Enqueue a data packet for transmission. /*!\brief Enqueue a data packet for transmission.
* \param ctx The connection context. * \param ctx The connection context.
* \param payload_type Type of the payload.
* \param buf Pointer to the data buffer. * \param buf Pointer to the data buffer.
* \param buf_len Length of the data. * \param buf_len Length of the data.
*/ */
result_t connection_enqueue_data_packet(connection_ctx_t *ctx, uint8_t *buf, size_t buf_len); result_t connection_enqueue_data_packet(
connection_ctx_t *ctx,
layer2_payload_type_t payload_type,
uint8_t *buf,
size_t buf_len);
/*!\brief Check if there is free space in the TX packet queue. /*!\brief Check if there is free space in the TX packet queue.
* \param ctx The connection context. * \param ctx The connection context.

View file

@ -1,10 +1,14 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#define LOGGER_MODULE_NAME "clist"
#include "logger.h"
#include "connection_list.h" #include "connection_list.h"
#include "layer2/connection.h" #include "layer2/connection.h"
#include "results.h" #include "results.h"
#include "utils.h"
#define SEQ_NR_MASK 0xF #define SEQ_NR_MASK 0xF
@ -213,7 +217,7 @@ result_t connection_list_enqueue_packet(connection_list_t *list, uint8_t *data,
return ERR_INVALID_ADDRESS; return ERR_INVALID_ADDRESS;
} }
return connection_enqueue_data_packet(&ptr->connection, data, data_len); return connection_enqueue_data_packet(&ptr->connection, L2_PAYLOAD_TYPE_IPV6, data, data_len);
} }

View file

@ -186,7 +186,7 @@ result_t digipeater_fill_packet_queues_from_tundev(digipeater_ctx_t *ctx, int tu
// first check if any queue is already full, so we don't have to drop packets // first check if any queue is already full, so we don't have to drop packets
// from the TUN device queue. // from the TUN device queue.
if(!connection_list_can_enqueue_packet(&ctx->conn_list)) { if(!connection_list_can_enqueue_packet(&ctx->conn_list)) {
LOG(LVL_DEBUG, "No free space in queues."); LOG(LVL_DEBUG, "No connection or no free space in queues.");
return OK; // do nothing return OK; // do nothing
} }
@ -215,6 +215,9 @@ result_t digipeater_fill_packet_queues_from_tundev(digipeater_ctx_t *ctx, int tu
} else if(ret == 0) { } else if(ret == 0) {
// no more data, should not happen // no more data, should not happen
break; break;
} else if(ret < 4) {
LOG(LVL_ERR, "Not enough data from TUN read() to check packet type!");
return ERR_SYSCALL;
} }
uint16_t flags = *(uint16_t*)packetbuf; uint16_t flags = *(uint16_t*)packetbuf;
@ -222,9 +225,13 @@ result_t digipeater_fill_packet_queues_from_tundev(digipeater_ctx_t *ctx, int tu
LOG(LVL_DUMP, "TUN Flags: 0x%04x", flags); LOG(LVL_DUMP, "TUN Flags: 0x%04x", flags);
LOG(LVL_DUMP, "TUN Proto: 0x%04x", proto); LOG(LVL_DUMP, "TUN Proto: 0x%04x", proto);
uint8_t *packet_data = packetbuf + 4;
size_t packet_length = ret - 4;
// note: octets are swapped in case statements
switch(proto) { switch(proto) {
case 0x86dd: // IPv6 case 0xdd86: // IPv6
ERR_CHECK(connection_list_enqueue_packet(&ctx->conn_list, packetbuf, ret)); WARN_ON_ERR(connection_list_enqueue_packet(&ctx->conn_list, packet_data, packet_length));
break; break;
default: default:
@ -258,6 +265,7 @@ size_t digipeater_encode_next_packet(digipeater_ctx_t *ctx, uint8_t *buf, size_t
switch(ctx->state) { switch(ctx->state) {
case DIGIPEATER_STATE_BEACON: case DIGIPEATER_STATE_BEACON:
packet_size = encode_beacon_packet(ctx, buf, buf_len); packet_size = encode_beacon_packet(ctx, buf, buf_len);
ctx->next_beacon_time += HRTIME_MS(BEACON_INTERVAL_MS);
*end_burst = true; *end_burst = true;
break; break;
@ -318,7 +326,6 @@ result_t digipeater_end_cycle(digipeater_ctx_t *ctx)
uint64_t now = get_hires_time(); uint64_t now = get_hires_time();
if(now >= ctx->next_beacon_time) { if(now >= ctx->next_beacon_time) {
ctx->next_beacon_time += HRTIME_MS(BEACON_INTERVAL_MS);
ctx->state = DIGIPEATER_STATE_BEACON; ctx->state = DIGIPEATER_STATE_BEACON;
} else { } else {
// TODO: adjust the time based on connection activity; right now this results // TODO: adjust the time based on connection activity; right now this results

View file

@ -49,4 +49,12 @@ typedef enum {
} \ } \
} while(0); } while(0);
#define WARN_ON_ERR(call) \
do { \
result_t err_check_result = call; \
if(err_check_result != OK) { \
LOG(LVL_WARN, "Error ignored at %s:%d: %d", __FILE__, __LINE__, err_check_result); \
} \
} while(0);
#endif // RESULTS_H #endif // RESULTS_H

View file

@ -21,6 +21,7 @@
#include <errno.h> #include <errno.h>
#include "layer2/ham64.h" #include "layer2/ham64.h"
#include "results.h"
#include "utils.h" #include "utils.h"
#define LOGGER_MODULE_NAME "main" #define LOGGER_MODULE_NAME "main"
@ -314,12 +315,25 @@ int main(void)
} else if(ret == 0) { } else if(ret == 0) {
// no more data, should not happen // no more data, should not happen
break; break;
} else if(ret < 4) {
LOG(LVL_ERR, "Not enough data from TUN read() to check packet type!");
return ERR_SYSCALL;
} }
LOG(LVL_DUMP, "TUN Flags: 0x%04x", *(uint16_t*)packetbuf); uint16_t flags = *(uint16_t*)packetbuf;
LOG(LVL_DUMP, "TUN Proto: 0x%04x", *((uint16_t*)packetbuf + 1)); uint16_t proto = *((uint16_t*)packetbuf + 1);
LOG(LVL_DUMP, "TUN Flags: 0x%04x", flags);
LOG(LVL_DUMP, "TUN Proto: 0x%04x", proto);
RESULT_CHECK(connection_enqueue_data_packet(&l2conn, packetbuf, ret)); uint8_t *packet_data = packetbuf + 4;
size_t packet_length = ret - 4;
if(proto != 0xdd86) {
LOG(LVL_WARN, "Non-IPv6 packet ignored. Proto: 0x%04x", proto);
continue;
}
RESULT_CHECK(connection_enqueue_data_packet(&l2conn, L2_PAYLOAD_TYPE_IPV6, packet_data, packet_length));
} }
} }
@ -411,6 +425,10 @@ int main(void)
LOG(LVL_ERR, "Packet not in the expected sequence."); LOG(LVL_ERR, "Packet not in the expected sequence.");
break; break;
case ERR_INVALID_STATE:
LOG(LVL_WARN, "Packet ignored due to invalid state.");
break;
default: // all other errors default: // all other errors
LOG(LVL_ERR, "layer2_rx_handle_packet() returned error code %u.", result); LOG(LVL_ERR, "layer2_rx_handle_packet() returned error code %u.", result);
break; break;