docs(proxy): specify TCP lifecycle and UDP ownership
This commit is contained in:
1 parent
a4abe30b18
commit
23cec465bf
3 files changed
+117
-75
No files matched your search
@@ -1,7 +1,9 @@
|
||||
/* Клиент TCP-прокси: принимает IPv4 с отдельного TUN через lwIP либо TCP от SOCKS/HTTP listener.
|
||||
* Передаёт потоки через UTUN-router на via_node_id; proxy_flow обеспечивает окно и backpressure.
|
||||
* create/destroy и callbacks — в uasync. Владеет созданными TUN/lwIP/listener/потоками,
|
||||
* заимствует inst/ua/mappings до destroy. Без tun_name/tun_ip может работать только SOCKS/HTTP. */
|
||||
* заимствует inst/ua/mappings до destroy. Без пары непустых tun_name/tun_ip работает только SOCKS/HTTP.
|
||||
* Владелец сам записывает результат create в inst->tcp_proxy_client и обнуляет поле после destroy.
|
||||
* Клиент принимает TCP-ответы только от via_node_id в TOPO_GROUP_UTUN; UDP/ICMP требуют TUN. */
|
||||
#ifndef TCP_PROXY_CLIENT_H
|
||||
#define TCP_PROXY_CLIENT_H
|
||||
|
||||
@@ -32,12 +34,12 @@ struct listen_ctx;
|
||||
struct tcp_pcb;
|
||||
|
||||
struct tcp_proxy_client_conn {
|
||||
struct tcp_proxy_client_conn* next;
|
||||
struct tcp_proxy_client* proxy;
|
||||
struct tcp_pcb* pcb;
|
||||
struct tcp_proxy_client_conn* next; // следующий lwIP-поток клиента
|
||||
struct tcp_proxy_client* proxy; // заимствованный владелец
|
||||
struct tcp_pcb* pcb; // принадлежит lwIP; NULL после detach/err callback
|
||||
|
||||
struct proxy_flow flow;
|
||||
uint32_t stream_id;
|
||||
struct proxy_flow flow; // окно и retry CONNECTED/WINDOW/FIN
|
||||
uint32_t stream_id; // общий генератор с SOCKS/HTTP-потоками
|
||||
|
||||
struct ll_queue* to_lwip; // DATA от exit → lwIP (управление потоком)
|
||||
|
||||
@@ -47,54 +49,63 @@ struct tcp_proxy_client_conn {
|
||||
uint8_t rem_closed; // exit отправил CLOSE
|
||||
uint8_t error; // ошибка, немедленная очистка
|
||||
uint8_t close_sent; // отправили CLOSE/ERROR в exit
|
||||
uint8_t close_pending; // CLOSE/ERROR не доставлен, ждём повтора
|
||||
uint8_t close_pending; // CLOSE/ERROR не принят router, повтор в poll/free пока pc жив
|
||||
|
||||
struct ll_queue* tx_queue; // буфер lwIP→ETCP при backpressure (аналог server read_queue)
|
||||
struct queue_waiter_handle tx_waiter; // waiter на send_q (etcp_router_on_send_ready)
|
||||
void* tx_retry_timer; // retry safety-net (force=1), аналог server retry_timer
|
||||
|
||||
uint32_t bytes_to_exit; // байт отправлено в exit (из lwIP/TUN)
|
||||
uint32_t bytes_to_exit; // байт принято router для exit (из lwIP/TUN)
|
||||
uint32_t bytes_from_exit; // байт получено от exit (в lwIP/TUN)
|
||||
uint32_t bp_count; // счётчик backpressure (send fail)
|
||||
|
||||
uint8_t dest_ip[4];
|
||||
uint16_t dest_port;
|
||||
uint8_t dest_ip[4]; // IPv4 назначения в сетевом порядке
|
||||
uint16_t dest_port; // порт назначения в сетевом порядке
|
||||
};
|
||||
|
||||
struct tcp_proxy_client {
|
||||
struct UTUN_INSTANCE* inst;
|
||||
struct UASYNC* ua;
|
||||
struct tun_if* tun;
|
||||
struct tcp_proxy_client_conn* conns;
|
||||
int conn_count;
|
||||
struct memory_pool* entry_pool;
|
||||
uint32_t next_stream_id;
|
||||
uint64_t via_node_id;
|
||||
struct lwip_tcp_ctx* lwip;
|
||||
struct UTUN_INSTANCE* inst; // заимствован; NULL допустим для автономного TUN smoke test
|
||||
struct UASYNC* ua; // заимствованный event loop
|
||||
struct tun_if* tun; // собственный TUN или NULL для SOCKS/HTTP без TUN
|
||||
struct tcp_proxy_client_conn* conns; // список собственных lwIP-потоков
|
||||
int conn_count; // число lwIP-потоков, без SOCKS/HTTP
|
||||
struct memory_pool* entry_pool; // собственный пул ll_entry для очередей lwIP-потоков
|
||||
uint32_t next_stream_id; // общий счётчик TUN/SOCKS/HTTP, init=1, перед выдачей инкрементируется
|
||||
uint64_t via_node_id; // настроенный exit для всех потоков
|
||||
struct lwip_tcp_ctx* lwip; // собственный TCP-стек или NULL без TUN
|
||||
|
||||
struct tcp_proxy_client_mapping_config* mappings; // указатель на конфиг
|
||||
int mapping_count;
|
||||
int mapping_count; // число записей mappings, порты конфига в host byte order
|
||||
|
||||
// SOCKS proxy
|
||||
int socks_enabled;
|
||||
struct socks_proxy_conn* socks_conns;
|
||||
int socks_conn_count;
|
||||
struct listen_ctx* socks_listen;
|
||||
int socks_enabled; // запрошен SOCKS5; не гарантирует успешный listener
|
||||
struct socks_proxy_conn* socks_conns; // собственные SOCKS5-клиенты
|
||||
int socks_conn_count; // число SOCKS5-клиентов
|
||||
struct listen_ctx* socks_listen; // собственный listener или NULL при отключении/ошибке
|
||||
|
||||
// HTTP CONNECT proxy
|
||||
int http_proxy_enabled;
|
||||
struct socks_proxy_conn* http_conns;
|
||||
int http_conn_count;
|
||||
struct listen_ctx* http_listen;
|
||||
// HTTP forward proxy и CONNECT
|
||||
int http_proxy_enabled; // запрошен HTTP; не гарантирует успешный listener
|
||||
struct socks_proxy_conn* http_conns; // собственные HTTP-клиенты
|
||||
int http_conn_count; // число HTTP-клиентов
|
||||
struct listen_ctx* http_listen; // собственный listener или NULL при отключении/ошибке
|
||||
};
|
||||
|
||||
// Создать клиент; ua обязателен. Непустые tun_name И tun_ip включают TUN/lwIP (mtu<=0 → 1500).
|
||||
// test_mode передаётся tun_init_nat: ненулевой использует тестовый TUN; mappings заимствуются до destroy.
|
||||
// SOCKS/HTTP создаются при enabled и непустом IPv4:port; via_node_id — общий exit.
|
||||
// Результат: собственный p или NULL при критической ошибке/отсутствии режима.
|
||||
// Ошибки listener/router bind/UDP/ICMP не делают create атомарным: проверять listener и диагностику отдельно.
|
||||
struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua,
|
||||
const char* tun_name, const char* tun_ip, int mtu, int test_mode,
|
||||
struct tcp_proxy_client_mapping_config* mappings, int mapping_count,
|
||||
uint64_t via_node_id,
|
||||
int socks_enabled, const char* socks_addr,
|
||||
int http_proxy_enabled, const char* http_proxy_addr);
|
||||
// Остановить listeners, освободить потоки/TUN/lwIP и снять router binding; NULL допустим.
|
||||
// UDP/ICMP сохраняются, пока включён сервер того же inst. Не обнуляет inst->tcp_proxy_client.
|
||||
void tcp_proxy_client_destroy(struct tcp_proxy_client* p);
|
||||
// Router callback: conn->instance задаёт владельца, entry содержит метаданные router и передаёт владение.
|
||||
// Освобождает entry/dgram при любом исходе; CLOSE_ALL = пустой payload router, а не TCP-подкоманда.
|
||||
void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* Exit TCP-прокси: принимает CONNECT/DATA через UTUN-router и открывает обычный TCP к назначению.
|
||||
* Поток определяется парой peer_node_id/stream_id; proxy_flow согласует окна и half-close.
|
||||
* init учитывает tcp_proxy_server_enabled в конфиге; destroy закрывает потоки и общий UDP/ICMP-прокси.
|
||||
* Состояние принадлежит UTUN_INSTANCE, операции и callbacks выполняются в его uasync-потоке. */
|
||||
* Сначала init, затем приём через router callback, в конце destroy; повторный init живого сервера недопустим.
|
||||
* Состояние принадлежит UTUN_INSTANCE, операции и callbacks выполняются в его uasync-потоке.
|
||||
* CLOSE дренирует запись и прекращает чтение; ERROR немедленно уничтожает поток, FIN — half-close. */
|
||||
#ifndef TCP_PROXY_SERVER_H
|
||||
#define TCP_PROXY_SERVER_H
|
||||
|
||||
@@ -24,50 +26,70 @@ struct ETCP_CONN;
|
||||
#include "proxy_protocol.h"
|
||||
|
||||
struct tcp_proxy_server_conn {
|
||||
struct tcp_proxy_server_conn* next;
|
||||
struct tcp_proxy_server* ctx;
|
||||
struct tcp_conn* tc;
|
||||
struct proxy_flow flow;
|
||||
uint32_t stream_id;
|
||||
uint64_t peer_node_id;
|
||||
uint8_t dest_ip[4];
|
||||
uint16_t dest_port;
|
||||
struct tcp_proxy_server_conn* next; // следующий поток exit
|
||||
struct tcp_proxy_server* ctx; // заимствованный владелец в inst
|
||||
struct tcp_conn* tc; // собственный OS TCP к назначению
|
||||
struct proxy_flow flow; // окно, FIN и control retry
|
||||
uint32_t stream_id; // ID клиента; уникален в паре с peer_node_id
|
||||
uint64_t peer_node_id; // конечный ingress-узел, не транспортный сосед
|
||||
uint8_t dest_ip[4]; // IPv4 назначения в сетевом порядке
|
||||
uint16_t dest_port; // порт назначения в сетевом порядке
|
||||
|
||||
uint8_t close_queued;
|
||||
uint8_t close_queued; // close-маркер уже поставлен в write_queue TCP
|
||||
uint8_t cli_closed; // client sent CLOSE
|
||||
|
||||
void* diag_timer; // 1-секундный таймер диагностики
|
||||
uint8_t freed; // 1 = уже освобождён, защита от double-free
|
||||
uint8_t freed; // логически удалён; сама структура освобождается через call_soon
|
||||
|
||||
struct queue_waiter_handle pause_waiter;
|
||||
void* retry_timer; // fallback timer (500ms) for force=1 retry on stall
|
||||
uint32_t bytes_sent; // байт записано в destination (реальный TCP)
|
||||
uint32_t bytes_relayed; // байт отправлено клиенту через ETCP
|
||||
struct queue_waiter_handle pause_waiter; // ожидание свободного места в router send_q
|
||||
void* retry_timer; // fallback 500 мс: возобновить read_queue, DATA отправляется с force=0
|
||||
uint32_t bytes_sent; // сумма входящих DATA, учитывается до проверки/записи в TCP
|
||||
uint32_t bytes_relayed; // байт принято router для клиента
|
||||
uint32_t drain_count; // счётчик вызовов read_queue_drain_cb
|
||||
uint32_t data_count; // счётчик входящих DATA от клиента
|
||||
struct UASYNC* ua;
|
||||
struct UASYNC* ua; // заимствованный loop для таймеров и отложенного free
|
||||
};
|
||||
|
||||
struct tcp_proxy_server {
|
||||
int enabled;
|
||||
int conn_count;
|
||||
struct tcp_proxy_server_conn* conns;
|
||||
struct UTUN_INSTANCE* inst;
|
||||
int enabled; // exit обслуживает CONNECT/UDP/ICMP REQUEST
|
||||
int conn_count; // число потоков в conns
|
||||
struct tcp_proxy_server_conn* conns; // собственные потоки всех пиров
|
||||
struct UTUN_INSTANCE* inst; // заимствованный instance, содержащий сервер
|
||||
};
|
||||
|
||||
// Обнулить состояние и учесть config->global.tcp_proxy_server_enabled; NULL inst → -1, иначе 0.
|
||||
// Без конфига/при отключении не bind-ит сервис. При включении bind SERVER и вызывает UDP/ICMP init.
|
||||
// 0 не гарантирует успешный bind/UDP/ICMP: их ошибки не пробрасываются вызывающему коду.
|
||||
int tcp_proxy_server_init(struct UTUN_INSTANCE* inst);
|
||||
// Удалить потоки, выключить сервер, снять binding и уничтожить общие UDP/ICMP; NULL inst допустим.
|
||||
// Перед этим остановить tcp_proxy_client того же inst; дать ua обработать отложенные освобождения.
|
||||
void tcp_proxy_server_destroy(struct UTUN_INSTANCE* inst);
|
||||
|
||||
// Линейный поиск в ненулевом ctx по паре peer/sid; заимствованный указатель или NULL.
|
||||
struct tcp_proxy_server_conn* tcp_proxy_server_find_conn(struct tcp_proxy_server* ctx, uint64_t peer, uint32_t stream_id);
|
||||
// Немедленно удалить rc из списка, закрыть TCP и отменить таймеры/waiter; NULL допустим.
|
||||
// Память rc освобождается отложенно в ua; сама функция не отправляет CLOSE/ERROR пиру.
|
||||
void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc);
|
||||
|
||||
// Router callback: conn->instance — владелец, peer берётся из ROUTER_SVC_SRC_OFF entry.
|
||||
// Принимает владение entry/dgram при любом исходе. Пустой payload router удаляет потоки этого peer.
|
||||
void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
|
||||
// entry — полный приёмный пакет CONNECT с IPv4/портом; принимает владение entry/dgram.
|
||||
// inst должен быть включён, sid/src — уже разобранные поля. 0 = connect запущен/готов, -1 = ошибка.
|
||||
// CONNECTED отправляется только после реального connect; таймаут — из connect_timeout_ms (0 = отключён).
|
||||
int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry,
|
||||
uint32_t stream_id, uint64_t src_node_id);
|
||||
// Полный entry DATA не короче RECV_HDR_SIZE; принимает владение entry/dgram. conn сейчас не используется.
|
||||
// Peer берётся из entry; проверяет окно и ставит данные в OS write_queue. 0 = обработано, -1 = отклонено.
|
||||
// Ошибка выделения в write_queue может закрыть поток и при возврате 0: это не подтверждение OS send.
|
||||
int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id);
|
||||
// CLOSE: прекратить чтение, отбросить read_queue и закрыть TCP после записи уже принятой write_queue.
|
||||
void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint64_t peer, uint32_t stream_id);
|
||||
// ERROR: немедленно удалить поток вместе с непрочитанными/незаписанными данными.
|
||||
void tcp_proxy_server_handle_error(struct UTUN_INSTANCE* inst, uint64_t peer, uint32_t stream_id);
|
||||
// FIN: поставить shutdown(SHUT_WR) после уже принятых DATA; обратное направление продолжает работать.
|
||||
// handle_close/error/fin допускают NULL inst и отсутствующую пару peer/sid.
|
||||
void tcp_proxy_server_handle_fin(struct UTUN_INSTANCE* inst, uint64_t peer, uint32_t stream_id);
|
||||
|
||||
|
||||
|
||||
+35
-26
@@ -1,6 +1,9 @@
|
||||
/* UDP из TUN через UTUN-router: клиент отправляет REQUEST, exit открывает сокет для кортежа адресов
|
||||
* и возвращает REPLY. Потоки exit удаляются по таймауту неактивности.
|
||||
* init/destroy и callbacks — в uasync; контекст принадлежит inst, роль берётся из tcp_proxy_server.enabled. */
|
||||
* init/destroy и callbacks — в uasync; inst владеет контекстом в inst->udp_proxy.
|
||||
* init сохраняет роль в is_exit для диагностики; приём REQUEST проверяет текущее server.enabled,
|
||||
* а REPLY — наличие tcp_proxy_client и совпадение peer с via_node_id. Один inst может быть и клиентом, и exit.
|
||||
* API принимает IP/порты в network byte order, payload заимствован только на время вызова. */
|
||||
#ifndef UDP_PROXY_H
|
||||
#define UDP_PROXY_H
|
||||
|
||||
@@ -22,55 +25,61 @@ struct ETCP_CONN;
|
||||
#define UDP_PROXY_SUBCMD_REQUEST 0x01
|
||||
#define UDP_PROXY_SUBCMD_REPLY 0x02
|
||||
|
||||
// Заголовок сообщения на отправке (включая байт svc_id; src/dst node_id добавляет роутер):
|
||||
// Сообщение для etcp_route_send (не полный wire packet; метаданные приёма добавляет router):
|
||||
// svc_id(1) + subcmd(1) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2) + payload
|
||||
#define UDP_PROXY_HDR_SIZE 14
|
||||
// Полный заголовок на приёме (после router_deliver):
|
||||
// svc_id(1) + src_node_id(8) + dst_node_id(8) + subcmd(1) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2)
|
||||
#define UDP_PROXY_RECV_HDR_SIZE (ROUTER_SVC_PAYLOAD_OFF + 13) // 30
|
||||
// router header(26: svc/src/dst/flags/group) + subcmd(1) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2)
|
||||
#define UDP_PROXY_RECV_HDR_SIZE (ROUTER_SVC_PAYLOAD_OFF + 13) // 39
|
||||
|
||||
// Один UDP поток (сторона exit узла: сопоставляет client_node_id + кортеж адресов с OS сокетом)
|
||||
struct udp_flow {
|
||||
struct udp_flow* next;
|
||||
struct udp_proxy_ctx* ctx;
|
||||
uint64_t client_node_id;
|
||||
uint32_t src_ip;
|
||||
uint16_t src_port;
|
||||
uint32_t dst_ip;
|
||||
uint16_t dst_port;
|
||||
socket_t sock;
|
||||
void* read_id;
|
||||
struct UASYNC* ua;
|
||||
uint64_t created_tb;
|
||||
uint64_t last_activity_tb;
|
||||
struct udp_flow* next; // следующий flow exit
|
||||
struct udp_proxy_ctx* ctx; // заимствованный владелец
|
||||
uint64_t client_node_id; // конечный ingress-узел
|
||||
uint32_t src_ip; // исходный адрес приложения, network byte order
|
||||
uint16_t src_port; // исходный порт приложения, network byte order
|
||||
uint32_t dst_ip; // адрес назначения, network byte order
|
||||
uint16_t dst_port; // порт назначения, network byte order
|
||||
socket_t sock; // собственный неблокирующий UDP, bind ANY:0
|
||||
void* read_id; // регистрация сокета в ua
|
||||
struct UASYNC* ua; // заимствованный event loop
|
||||
uint64_t created_tb; // время создания (tb = 0.1 мс)
|
||||
uint64_t last_activity_tb; // последний REQUEST или ответ от ожидаемого dst_ip:dst_port
|
||||
};
|
||||
|
||||
struct udp_proxy_ctx {
|
||||
int initialized;
|
||||
int is_exit; // 1=exit узел (создаёт сокеты), 0=клиент (шлёт через etcp)
|
||||
struct UTUN_INSTANCE* inst;
|
||||
struct UASYNC* ua;
|
||||
struct udp_flow* flows;
|
||||
int flow_count;
|
||||
uint64_t flow_timeout_tb;
|
||||
int initialized; // init завершён, binding запрошен
|
||||
int is_exit; // снимок server.enabled на момент init, не фильтр recv_cb
|
||||
struct UTUN_INSTANCE* inst; // заимствованный instance-владелец
|
||||
struct UASYNC* ua; // заимствованный event loop
|
||||
struct udp_flow* flows; // собственные flows exit
|
||||
int flow_count; // число flows
|
||||
uint64_t flow_timeout_tb; // период проверки и порог неактивности, default=600000 (60 с)
|
||||
void* expire_timer; // периодический таймер очистки истёкших потоков
|
||||
};
|
||||
|
||||
|
||||
// Создать per-instance контекст и bind UDP service; повторный init существующего контекста даёт 0.
|
||||
// inst/ua должны жить до destroy. 0 = контекст есть (результат bind не проверяется), -1 = ошибка inst/памяти.
|
||||
int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua);
|
||||
// Снять binding, отменить таймер, закрыть flows и обнулить inst->udp_proxy; NULL/нет контекста допустимы.
|
||||
void udp_proxy_destroy(struct UTUN_INSTANCE* inst);
|
||||
|
||||
// Exit узел: принимает REQUEST, создаёт сокет, шлёт данные
|
||||
// Сторона клиента: принимает REPLY, доставляет на TUN
|
||||
// Сторона клиента: принимает REPLY от настроенного exit, доставляет на TUN.
|
||||
// conn->instance задаёт владельца; callback принимает владение entry/dgram при любом исходе.
|
||||
void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
|
||||
// Сторона клиента: принять IP/UDP пакет с TUN, отправить через etcp_route_send
|
||||
// Скопировать payload (0..65507 байт) в REQUEST и отправить в UTUN, force=0/mode=0.
|
||||
// Требует inst->udp_proxy; при payload_len>0 payload не NULL. 0 = принято router, <0 = ошибка.
|
||||
int udp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
|
||||
uint32_t src_ip, uint16_t src_port,
|
||||
uint32_t dst_ip, uint16_t dst_port,
|
||||
const uint8_t* payload, size_t payload_len);
|
||||
|
||||
// Сторона клиента: доставить ответ на TUN (вызывается из recv_cb)
|
||||
// Собрать IPv4/UDP src→dst и поставить в tun->input_queue (префикс версии 4); UDP checksum=0.
|
||||
// Требует inst->tcp_proxy_client->tun; payload=0..65506. 0 = поставлено в очередь, -1 = ошибка.
|
||||
int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst,
|
||||
uint32_t src_ip, uint16_t src_port,
|
||||
uint32_t dst_ip, uint16_t dst_port,
|
||||
|
||||
Reference in new issue
Block a user