Accept full GATT RX when reused ACL has unknown TX MTU

This commit is contained in:
evgeny committed 2026-10-30 23:58:52 +01:00
1 parent 3124c82bd4
commit 01af2df02b
3 files changed
+34 -6

No files matched your search

+11 -5
View File
@@ -32,7 +32,8 @@ struct ble_connection {
struct ble_stats stats;
struct ble_endpoint endpoint;
struct ble_channel_state channels[BLE_CHANNEL_COUNT];
unsigned int capacity;
unsigned int capacity; /* Безопасный TX budget; после reuse ACL backend может знать только минимум MTU23. */
int rx_above_tx_logged;
uint16_t pending_bytes;
/* ID монотонен по модулю 65536; кольцо хранит размеры и границы кадров, собственных ретрансляций нет. */
uint16_t tx_next, tx_acked, rx_last, ack_sent, flight_head, flight_count, flight_bytes;
@@ -442,10 +443,15 @@ void ble_backend_received(struct ble_transport* t, ble_link_id id, enum ble_wire
DEBUG_WARN(CAT, "BLE value before mandatory subscriptions link=%llu channel=%d bytes=%zu", (unsigned long long)id, wire, len);
close_link(t, c, "value before subscriptions"); return;
}
if (!data || !len || len > c->capacity || (unsigned)wire >= BLE_WIRE_COUNT) {
if (!data || !len || len > BLE_VALUE_MAX || (unsigned)wire >= BLE_WIRE_COUNT) {
DEBUG_WARN(CAT, "BLE invalid wire value link=%llu channel=%d bytes=%zu", (unsigned long long)id, wire, len);
close_link(t, c, "invalid wire value"); return;
}
if (len > c->capacity && !c->rx_above_tx_logged) {
c->rx_above_tx_logged = 1;
DEBUG_INFO(CAT, "BLE RX exceeds conservative TX budget link=%llu bytes=%zu TX_capacity=%u RX_limit=%u",
(unsigned long long)id, len, c->capacity, BLE_VALUE_MAX);
}
if (wire == BLE_WIRE_ACK) {
if (len != 2) {
DEBUG_WARN(CAT, "BLE invalid ACK length link=%llu bytes=%zu", (unsigned long long)id, len);
@@ -510,9 +516,9 @@ void ble_backend_received(struct ble_transport* t, ble_link_id id, enum ble_wire
if (t->config.callbacks.connection) t->config.callbacks.connection(t->config.arg, id, peer, BLE_READY, "HELLO accepted");
pump(t, c); return;
}
if (!c->ready || len <= HEADER || len > c->capacity || data[0] != FRAME) {
DEBUG_WARN(CAT, "BLE invalid value link=%llu channel=%d bytes=%zu capacity=%u ready=%d type=%u",
(unsigned long long)id, channel, len, c->capacity, c->ready, len ? data[0] : 0);
if (!c->ready || len <= HEADER || data[0] != FRAME) {
DEBUG_WARN(CAT, "BLE invalid value link=%llu channel=%d bytes=%zu ready=%d type=%u",
(unsigned long long)id, channel, len, c->ready, len ? data[0] : 0);
c->stats.dropped++;
ch->rx_size = ch->rx_offset = 0; return;
}
+22 -1
View File
@@ -14,6 +14,7 @@ struct fake_link {
struct ble_backend { struct ble_transport* owner; struct UASYNC* ua; void* task; int index, started; struct fake_link links[16]; };
static struct ble_backend* backends[3];
static unsigned int mtu = 23;
static int conservative_receiver;
static int hold, drop, reject_peer, ready_count, writable_count;
static int hold_ack;
static unsigned ack_delay_us;
@@ -73,7 +74,7 @@ static void run(void* arg) {
uint8_t ack[2] = {f->ack_id >> 8, f->ack_id}; f->ack_held = 0; f->ack_delivered = f->ack_id;
if (f->peer && slot(f->peer, f->other)) ble_backend_received(f->peer->owner, f->other, BLE_WIRE_ACK, ack, 2);
}
if (!f->len) { f->len = 1; ble_backend_opened(b->owner, f->id, mtu); }
if (!f->len) { f->len = 1; ble_backend_opened(b->owner, f->id, conservative_receiver && b->index == 1 ? 23 : mtu); }
if (!f->pending || hold) continue;
struct fake_link* target = f->peer ? slot(f->peer, f->other) : NULL;
if (target && !target->len) { schedule(b); continue; } /* Удалённая подписка должна завершиться до доставки HELLO. */
@@ -388,10 +389,30 @@ static void exercise_frame_window(unsigned test_mtu) {
ble_destroy(a); ble_destroy(b); uasync_destroy(ua, 0);
printf("BLE frame window PASS (MTU=%u, two frames, partial/final/duplicate ACK, writable, independent INVITE)\n", test_mtu);
}
/* ACL уже имеет MTU517, но новый Android GATT server ещё не получил onMtuChanged. RX не ограничивается TX fallback. */
static void exercise_unknown_mtu(void) {
mtu = 517; conservative_receiver = 1; hold = hold_ack = drop = reject_peer = 0;
struct UASYNC* ua = uasync_create(); CHECK(ua);
struct ble_config cfg = {.node_id = 1, .company_id = 0xffff};
struct ble_transport* a = ble_create(ua, &cfg); cfg.node_id = 2;
struct ble_transport* b = ble_create(ua, &cfg); CHECK(a && b && !ble_start(a) && !ble_start(b)); drain(ua);
struct ble_endpoint ep = {"1"}; ble_link_id id = ble_connect(a, &ep, 2); CHECK(id); drain(ua);
ble_link_id other = incoming_ids[1];
CHECK(!ble_tx_put(a, id, frame(BLE_FRAME_MAX, 0x91)));
CHECK(!ble_channel_tx_put(a, id, BLE_CHANNEL_INVITE, frame(BLE_INVITE_FRAME_MAX, 0x92)));
CHECK(!ble_tx_put(b, other, frame(BLE_FRAME_MAX, 0x93))); drain(ua);
struct ll_entry* e = ble_rx_get(b, other); CHECK(e && e->len == BLE_FRAME_MAX && e->dgram[1499] == 0x91); release(e);
e = ble_channel_rx_get(b, other, BLE_CHANNEL_INVITE);
CHECK(e && e->len == BLE_INVITE_FRAME_MAX && e->dgram[BLE_INVITE_FRAME_MAX - 1] == 0x92); release(e);
e = ble_rx_get(a, id); CHECK(e && e->len == BLE_FRAME_MAX && e->dgram[1499] == 0x93); release(e);
ble_destroy(a); ble_destroy(b); uasync_destroy(ua, 0); conservative_receiver = 0;
puts("BLE unknown MTU PASS (TX minimum23, RX517, duplex DATA and full INVITE)");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
size_t before = u_get_allocated_count();
exercise_unknown_mtu(); CHECK(u_get_allocated_count() == before);
for (unsigned i = 0; i < 2; i++) {
exercise_resume_order(i ? 517 : 23, 0); exercise_resume_order(i ? 517 : 23, 1);
exercise_resume_stream(i ? 517 : 23); CHECK(u_get_allocated_count() == before);
@@ -1,6 +1,7 @@
/* Реальная доставка INVITE без GUI: config peer_hex link_file. GUI с этой идентичностью должен быть остановлен.
* Конфиг только читается; БД в памяти, IP-службы и CHAT не запускаются, ETCP требует известного участника. */
#include "../ble_etcp.h"
#include "../../../src/utun_instance.h"
#include "../../../src/chat/chat_local.h"
#include "../../../src/chat/chat_event.h"
#include "../../../lib/debug_config.h"