From 01af2df02b513fddae2329d13bc0927f51b9543d Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 23:58:52 +0100 Subject: [PATCH] Accept full GATT RX when reused ACL has unknown TX MTU --- lib/ble/ble_transport.c | 16 +++++++++---- lib/ble/tests/test_ble_transport.c | 23 ++++++++++++++++++- tools/cgathui_common/tests/ble_invite_probe.c | 1 + 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/ble/ble_transport.c b/lib/ble/ble_transport.c index 105d4f2b..d541b14f 100644 --- a/lib/ble/ble_transport.c +++ b/lib/ble/ble_transport.c @@ -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; } diff --git a/lib/ble/tests/test_ble_transport.c b/lib/ble/tests/test_ble_transport.c index 6c509f00..c91aa5df 100644 --- a/lib/ble/tests/test_ble_transport.c +++ b/lib/ble/tests/test_ble_transport.c @@ -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); diff --git a/tools/cgathui_common/tests/ble_invite_probe.c b/tools/cgathui_common/tests/ble_invite_probe.c index 226af9b9..70ad2b3f 100644 --- a/tools/cgathui_common/tests/ble_invite_probe.c +++ b/tools/cgathui_common/tests/ble_invite_probe.c @@ -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"