connection: use one-shot packet „queueing“ to send connection request

The connection module now has a separate queueing mechanism for packets that
are only transmitted once and not acknowledged through the regular Go-back-N
mechanism. An example is the Connection Request that is a one-time response to
a received beacon.

When such a packet shall be sent, a simple flag is set and the packet is
transmitted first in the next burst. Data packets may follow depending on the
one-shot packet type (Connection Request ends the burst immediately).
This commit is contained in:
Thomas Kolb 2025-02-22 20:19:34 +01:00
commit 56742ef811
2 changed files with 55 additions and 18 deletions

View file

@ -30,6 +30,8 @@ result_t connection_init(
ctx->my_addr = *my_addr;
ctx->peer_addr = *peer_addr;
ctx->pending_packet = CONN_PENDING_NONE;
uint64_t now = get_hires_time();
ctx->last_rx_time = now;
@ -165,21 +167,10 @@ static result_t handle_conn_mgmt(
if(ctx->conn_state == CONN_STATE_CONNECTING) {
LOG(LVL_INFO, "Received beacon; queueing connection request.");
// enqueue a connection request packet
layer2_packet_header_t conn_request_header;
// do not enqueue the connection request as it does not need a regular
// ACK, but set it up as a pending one-shot packet.
ctx->pending_packet = CONN_PENDING_CONNECTION_REQUEST;
conn_request_header.tx_request = true;
conn_request_header.dst_addr = ctx->peer_addr;
conn_request_header.src_addr = ctx->my_addr;
conn_request_header.msg_type = L2_MSG_TYPE_CONN_MGMT;
conn_request_header.rx_seq_nr = 0;
conn_request_header.tx_seq_nr = 0;
// create a persistent copy of the packet data.
uint8_t packetbuf[1];
packetbuf[0] = CONN_MGMT_TYPE_CONNECTION_REQUEST;
connection_enqueue_packet(ctx, &conn_request_header, packetbuf, 1);
} else {
LOG(LVL_WARN, "Beacons are ignored in states other than CONNECTING.");
return ERR_INVALID_STATE;
@ -485,6 +476,44 @@ size_t connection_encode_next_packet(connection_ctx_t *ctx, uint8_t *buf, size_t
break;
}
layer2_packet_header_t header;
// first check one-shot packets; ensure they are only processed once
connection_pending_packet_t pending = ctx->pending_packet;
ctx->pending_packet = CONN_PENDING_NONE;
if(pending != CONN_PENDING_NONE) {
// prepare the header (same for all packets transmitted outside Go-back-N)
header.tx_request = true;
header.dst_addr = ctx->peer_addr;
header.src_addr = ctx->my_addr;
header.rx_seq_nr = 0;
header.tx_seq_nr = 0;
*end_burst = true;
}
switch(pending) {
case CONN_PENDING_NONE:
// continue to check the regular packet queue
break;
case CONN_PENDING_CONNECTION_REQUEST:
{
// enqueue a connection request packet
header.msg_type = L2_MSG_TYPE_CONN_MGMT;
uint8_t payload[1];
payload[0] = CONN_MGMT_TYPE_CONNECTION_REQUEST;
return layer2_encode_packet(&header, payload, sizeof(payload), buf, buf_len);
}
default:
LOG(LVL_ERR, "Invalid pending packet type encountered: 0x%08x", ctx->pending_packet);
return 0;
}
const packet_queue_entry_t *entry = packet_queue_get(&ctx->packet_queue, ctx->next_packet_index);
if(!entry) {
@ -492,7 +521,7 @@ size_t connection_encode_next_packet(connection_ctx_t *ctx, uint8_t *buf, size_t
return 0;
}
layer2_packet_header_t header = entry->header;
header = entry->header;
header.rx_seq_nr = ctx->next_expected_seq;
header.tx_request = (ctx->next_packet_index == (packet_queue_get_used_space(&ctx->packet_queue) - 1));
@ -575,8 +604,9 @@ bool connection_can_transmit(const connection_ctx_t *ctx)
{
assert(ctx->conn_state != CONN_STATE_UNINITIALIZED);
return (packet_queue_get_used_space(&ctx->packet_queue) != 0)
&& (packet_queue_get(&ctx->packet_queue, ctx->next_packet_index) != NULL);
return (ctx->pending_packet != CONN_PENDING_NONE) ||
((packet_queue_get_used_space(&ctx->packet_queue) != 0)
&& (packet_queue_get(&ctx->packet_queue, ctx->next_packet_index) != NULL));
}

View file

@ -21,7 +21,7 @@ struct connection_ctx_s;
typedef enum {
CONN_STATE_UNINITIALIZED, //!< Uninitialized. Cannot be used in any way
CONN_STATE_INITIALIZED, //!< Initialized, no packets processed yet
CONN_STATE_CONNECTING, //!< Connection request sent, no two-way communication yet
CONN_STATE_CONNECTING, //!< Outgoing connection set up, waiting and responding to beacons
CONN_STATE_ESTABLISHED, //!< Connection is established
CONN_STATE_CLOSED //!< Connection has been closed (gracefully or by timeout)
} connection_state_t;
@ -32,6 +32,11 @@ typedef enum {
CONN_EVT_RETRANSMIT, //!< Packet queue transmission is restarted
} connection_evt_t;
typedef enum {
CONN_PENDING_NONE,
CONN_PENDING_CONNECTION_REQUEST,
} connection_pending_packet_t;
typedef struct connection_ctx_s {
connection_state_t conn_state; //!< State of the connection.
@ -45,6 +50,8 @@ typedef struct connection_ctx_s {
packet_queue_t packet_queue; //!< Transmission packet queue.
connection_pending_packet_t pending_packet; //!< Identifies a single one-shot packet that must be transmitted
size_t next_packet_index; //!< Index in the packet queue of the next packet to transmit.
uint8_t next_seq_nr; //!< Sequence number to tag the next transmitted packet with.