You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8.4 KiB

Задача: продолжить исправление TCP-proxy (крупные файлы через транзит)

Продолжение незавершённой работы. Читай целиком, прежде чем что-то менять.

1. Что уже сделано (НЕ переделывать)

Унифицирован формат доставки сервисных кодограмм роутером. Раньше сервисы определяли источник через conn->peer_node_id, что ломало транзит (при промежуточном узле это был id промежуточного, а не реального клиента).

Теперь роутер доставляет сервисам единый формат:

[svc_id(1)][src_node_id(8)][dst_node_id(8)][payload...]

где src/dst — реальные end-to-end узлы из SVC_ROUTE заголовка.

Ключевые константы в src/routing_layer/etcp_router.h: ROUTER_SVC_SRC_OFF=1, ROUTER_SVC_DST_OFF=9, ROUTER_SVC_PAYLOAD_OFF=17, ROUTER_SVC_HDR_SIZE=17.

Изменённые файлы (все в некоммиченном состоянии, git status):

  • src/routing_layer/etcp_router.{h,c} — router_deliver + router_deliver_loopback + CLOSE/RESTART
  • src/proxy/tcp_proxy_server.{h,c} — recv +8→+16 сдвиг, src_node_id из dgram[1..8]
  • src/proxy/tcp_proxy_client.{h,c} — recv сдвиг + счётчики bytes_to_exit/from_exit/bp_count
  • src/proxy/udp_proxy.{h,c}, src/proxy/icmp_proxy.{h,c} — сдвиг + убран встроенный node_id
  • src/nat_transport.c — убран встроенный node_id
  • src/routing_layer/routing.c, conn_mgr_core.c — сдвиг
  • src/media_delivery/media_delivery.c — сдвиг + from_node из dgram[1..8]
  • tools/chatgui/transport/utun_node.cpp — сдвиг
  • тесты tests/test_etcp_router*.c, test_udp_proxy.c, test_icmp_proxy.c, test_nat_transport.c
  • tests/tcp_proxy_full/ — добавлен intermediate.conf (3-узловая топология), обновлены client.conf/exit.conf/run_test.sh

Статус проверки: ./check.sh = 73 passed, 0 failed, 1 skipped. Форматная часть ВЕРНА.

2. Что осталось (собственно задача)

Интеграционный тест прокси tests/tcp_proxy_full/run_test.sh (нужен sudo) — крупные передачи НЕ проходят. Проверено: проблема НЕ в формате и НЕ в транзите — прямой 2-узловой прогон падает идентично. Это предсуществующая проблема прокси/lwIP.

Результат sudo ./tests/tcp_proxy_full/run_test.sh:

  • idle (32 КБ) — PASS
  • half_close (64 КБ) — 64280/65536 (почти, теряется хвост)
  • basic_1mb (1 МБ), concurrent_*, stress — FAIL (timeout)

3. Диагноз (уже установлен по логам)

Пропускная способность ~24 КБ/с (1 пакет 1460 байт за ~60 мс). Для 1 МБ это ~43 с → таймаут 10 с.

Цепочка (видна в добавленных логах PROXY BP * и SEND_Q_ACKED):

  1. Клиент быстро шлёт 320 пакетов (initial burst ~33 МБ/с).
  2. Роутерный send_q переполняется (router_send_q: FULL count=64) → etcp_route_send возвращает -1.
  3. Прокси в tcp_proxy_client_recv_cb ставит одиночный tx_buf и сбрасывает дальнейшие данные (if (pc->tx_buf) { pbuf_free(p); return; }) — лог PROXY BP drop, без tcp_recved → окно lwIP схлопывается.
  4. Клиент вырождается в режим «1 пакет за ~60 мс».
  5. ~60 мс = таймер lwIP: ctx->tmr_interval_ms = TCP_TMR_INTERVAL / 4 = 62.5 мс (src/lwip_tcp/lwip_tcp.c:106). В режиме «пакет-за-пакетом» lwIP не шлёт ACK сразу (порог TCP_WND_UPDATE_THRESHOLD = TCP_WND/4 = 2920 не набирается за 1 пакет), и отложенный ACK уходит только по tcp_fasttmr раз в 62.5 мс.

Триггер — сброс данных при backpressure в прокси (tcp_proxy_client.c), НЕ lwIP.

4. Ограничения (ВАЖНО)

  • В src/lwip_tcp/ ничего НЕ править без явного согласования пользователя. Разрешён только debug-лог.
  • Правки исходников только через Edit tool (никакого sed для массовых замен).
  • Ошибки/нештатные ветки — обязательно DEBUG_ERROR/DEBUG_WARN.
  • Логи информативные, без спама. Debug-категории: proxy, etcp_route.
  • Перед сборкой make clean.

5. План продолжения

Шаг 1 (диагностика, закрыть пробел — по желанию, уже почти доказано):

  • Добавить в lwIP только DEBUG-лог (разрешено): в tcp_fasttmr — счётчик отложенных ACK; в tcp_recved — rcv_wnd.
  • В прокси (tcp_proxy_client.c, НЕ lwIP) в момент BP drop лог pcb->rcv_wnd.
  • Подтвердить, что 62.5 мс — это отложенный ACK.

Шаг 2 (фикс, требуется согласование — предложи пользователю):

  • Вариант A (предпочтительно, НЕ lwIP): в прокси заменить одиночный tx_buf + сброс на очередь с backpressure (буферизовать все пакеты во время backpressure, не сбрасывать; tcp_recved вызывать по мере отправки). Тогда окно закрывается плавно, OS TCP не сваливается в congestion avoidance, деградации нет. Смотри struct tcp_proxy_client_conn в tcp_proxy_client.h (сейчас: tx_buf/tx_len/tx_waiter). Для образца очереди с backpressure смотри src/proxy/tcp_proxy_server.c (там read_queue + pause_waiter).
  • Вариант B (lwIP, только по согласованию): уменьшить tmr_interval_ms (TCP_TMR_INTERVAL/4 → чаще), либо повысить TCP_WND_UPDATE_THRESHOLD/TCP_WND, чтобы ACK слался сразу.

Шаг 3 (почистить тестовую обвязку):

  • В tests/tcp_proxy_full/*.conf сейчас включены отладочные уровни (etcp_route=trace, proxy=trace) — перед коммитом вернуть к разумным (etcp_route=debug/proxy=debug или error).
  • Решить: оставить добавленные в tcp_proxy_client.c счётчики/BP-логи или вычистить.
  • В run_test.sh тайминги elapsed через date +%s%3N дают мусор в этой среде — поправить (например date +%s без миллисекунд, или SECONDS). Stress-таймаут 60s можно уменьшить до ~15-20s.

6. Сборка и тест

./build.sh --full -j4          # или make -j4 (после make clean)
./check.sh                     # unit-тесты: должны остаться 73 passed
cp src/utun utun               # обновить корневой бинарник для run_test.sh!
sudo ./tests/tcp_proxy_full/run_test.sh   # интеграционный (нужен root/TUN/iptables)

Важно: run_test.sh использует UTUN_BIN="$SCRIPT_DIR/../../utun" (корневой utun), а make собирает src/utun. После каждой пересборки делать cp src/utun utun.

7. Как быстро убедиться, что фикс помог

Прогнать basic_1mb и смотреть лог клиента tests/tcp_proxy_full/log/client_utun.log:

  • ДО фикса: PROXY BP drop + SEND_Q_ACKED с интервалом ~60 мс.
  • ПОСЛЕ фикса: нет BP drop, интервал ACK ~10 мс, basic_1mb PASS.

Полезные grep-и по логу клиента:

grep -E "PROXY BP|SEND_Q_ACKED|PROXY FIN" client_utun.log