- consumer_ack flag: ACK sent only on consumption, not assembly
- etcp_router_consumer_ack() — called from tcp_proxy_client feed_from_transport
- last_ack_sent_tb: interval-based throttle — send immediately if >=10ms passed,
otherwise timer for remaining time
- timer rules: NULL handle after cancel/fire, NULL check before start
- ROUTER_ACK_INTERVAL_TB 1000→100 (100ms→10ms)
- test_etcp_router_unit: updated test 14 for new immediate-send behavior
- tcp_proxy_client_handle_error: set error flag + send CLOSE once if flag was clear
- tcp_proxy_client_handle_data/handle_close: drop silently, don't send ERROR back
- tcp_proxy_server_handle_data/handle_close: drop silently, don't send ERROR back
- Add recv→send 1MB integration test (3 clients × 3 requests, shell + python)
queue_waiter_handle stores defer_q/cb/arg, no extra malloc.
queue_set_waiter_defer(q, 1) enables call_soon dispatch for waiter callbacks.
bbr_integration uses it instead of manual send_waiter_cb wrapper.
rx_acked was used for two conflicting purposes:
1. remote ack of our sends (inflight = tx_seq - rx_acked)
2. our last sent ACK seq (dedup: rx_seq != rx_acked)
When the ack timer fired and set rx_acked = rx_seq, it overwrote
the inflight-tracking value. If rx_seq > tx_seq, the computation
tx_seq - rx_acked underflowed (e.g. 18 - 24 = 0xFFFFFFFA),
permanently blocking router_drain_send_q and causing send_q to
grow indefinitely.
Fix:
- Split rx_acked into tx_acked (remote ack, for inflight) and
last_sent_ack_seq (our ACK, for dedup)
- Incoming ACK handler only advances tx_acked forward (stale guard)
- Use int32_t cast on all inflight comparisons to handle stale states
- Add etcp_router architecture diagram (doc/etcp_router_arch.md)
- handle_data: добавить pc->tun_closed в проверку сброса данных от exit
- proxy_feed_from_transport: не писать в PCB после tun_closed
- предотвращает ERR_RST (-14) при записи в TIME_WAIT PCB
- link_status теперь требует двусторонней слышимости: remote && local
- recv_keepalive восстанавливается ДО вычисления link_status (избегает заклинивания)
- loadbalancer_link_ready по переходу 0→1 с проверкой was_up
- ROUTER_SEQ_CLOSE_FLAG (0x80000000): normal conn close
- ROUTER_SEQ_RST_FLAG (0x40000000): conn not found
- router_send_to(): SVC_ROUTE with flags, no conn needed
- router_send_close_to_service(): notify local service [svc_id],len=1
- router_close_and_notify(): CLOSE to remote + close to local + cleanup
- etcp_router_conn_close(): sends CLOSE to remote before cleanup
- etcp_router_conn_close_all_for_node(): close all conns for dead peer
- recv_cb: flags checked only when pl_len==0 (avoid seq collision)
- etcp_router_destroy: close notification to each service
- New SVC_ROUTE_HDR (packed struct, 22 bytes): cmd+dst+src+seq+svc_id
- ETCP_ROUTER_CONN: state per (remote_node_id, svc_id), hash-indexed in router_conns
- Reorder via recv_q (hash by seq), dedup with 32-bit circular compare
- Periodic ACK (100ms), idle ACK (500ms), inflight limit via tx_seq - rx_acked
- Legacy mode: no conn → direct delivery without reorder
- etcp_router_conn_get/send/close API for seq-managed connections
- Unit test test_etcp_router_unit: 18 tests, 3ms, no ETCP/sockets/BGP
- remove sock_transport, transport abstraction, active connections API
- simplify proxy_conn to 13 fields (was 15), remove tcp_proxy_mapping linked list
- new protocol: stream_id 4B, seq 2B cyclic, HDR_SIZE 8B
- add ERROR subcmd for immediate abort, CLOSE for graceful half-close
- proper half-close: shutdown SHUT_WR, keep reading, no timer
- exhaustive DEBUG_ERROR/WARN on all error/unusual branches
- config parser: report unknown options with filename:line and valid keys
- proxy_uip_retry_cb: retry send from uip_to_transport every 5ms
- proxy_recv_cb: buffer to uip_to_transport when send fails, set retry timer
- proxy_feed: drain transport_to_uip when closing_tun || closing_sent (prevents RST)
- proxy_recv_cb(p==NULL): only tcp_recved + closing_tun, no drain
- proxy_poll_cb/tcp_proxy_destroy: cancel retry timer on cleanup
- uip_retry_timer field in proxy_conn
- proxy_recv_cb(p==NULL): tcp_recved(pcb,gap) before closing_tun
- proxy_feed_from_transport: stop when closing_sent
- proxy_poll_cb cleanup: free unsent before tcp_close
- proxy_try_close helper: shared between poll_cb (500ms) and recv_cb
- pending_out check: don't close if data still queued for exit
- Configs: traffic=info, tun=info for diagnostics
- Add closing_sent flag to proxy_conn (CLOSE sent to exit, no need to wait for reply)
- proxy_poll_cb: send CLOSE when done || sndbuf==0; cleanup when tun && (rem||sent)
- Destroy transport in proxy_poll_cb cleanup (fix 56-byte leak)
- remote_proxy_handle_close: shutdown(SHUT_WR) + 2s timer instead of immediate rp_conn_free
- Remove SHUTDOWN subcommand entirely (not needed with new lifecycle)