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/RESTARTsrc/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_countsrc/proxy/udp_proxy.{h,c},src/proxy/icmp_proxy.{h,c}— сдвиг + убран встроенный node_idsrc/nat_transport.c— убран встроенный node_idsrc/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 КБ) — PASShalf_close(64 КБ) — 64280/65536 (почти, теряется хвост)basic_1mb(1 МБ),concurrent_*,stress— FAIL (timeout)
3. Диагноз (уже установлен по логам)
Пропускная способность ~24 КБ/с (1 пакет 1460 байт за ~60 мс). Для 1 МБ это ~43 с → таймаут 10 с.
Цепочка (видна в добавленных логах PROXY BP * и SEND_Q_ACKED):
- Клиент быстро шлёт 320 пакетов (initial burst ~33 МБ/с).
- Роутерный
send_qпереполняется (router_send_q: FULL count=64) →etcp_route_sendвозвращает -1. - Прокси в
tcp_proxy_client_recv_cbставит одиночныйtx_bufи сбрасывает дальнейшие данные (if (pc->tx_buf) { pbuf_free(p); return; }) — логPROXY BP drop, безtcp_recved→ окно lwIP схлопывается. - Клиент вырождается в режим «1 пакет за ~60 мс».
- ~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_1mbPASS.
Полезные grep-и по логу клиента:
grep -E "PROXY BP|SEND_Q_ACKED|PROXY FIN" client_utun.log