tcp_proxy: fix throughput (tx_queue backpressure) + connect timeout + integration test
- Router: unified service delivery format [svc_id][src][dst][payload] (end-to-end src/dst from SVC_ROUTE header) - tcp_proxy_client: replace single tx_buf drop-on-backpressure with bounded tx_queue + send_q waiter + retry timer (no data loss, bulk window reopen — removes 62.5ms delayed-ACK stall) - tcp_io: configurable connect timeout (tcp_conn_set_connect_timeout) - tcp_proxy_server: connect_timeout config option (ms, default 2000, 0=off) - check.sh: run tcp_proxy_full integration test with sudo (skip if passwordless sudo unavailable) - test_tcp_io: connect timeout unit test; docs (lwip_nuances.md)
This commit is contained in:
1 parent
acfaae2801
commit
f47ad7d87f
36 files changed
+758
-269
No files matched your search
+28
-31
@@ -137,11 +137,10 @@ static void raw_read_cb(socket_t sock, void* arg) {
|
||||
if (!e->dgram) { queue_entry_free(e); return; }
|
||||
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
|
||||
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY;
|
||||
memcpy(e->dgram + 2, &r->client_node_id, 8);
|
||||
memcpy(e->dgram + 10, &r->dst_ip, 4);
|
||||
memcpy(e->dgram + 14, &r->orig_src_ip, 4);
|
||||
memcpy(e->dgram + 18, &icmp_hdr->icmp_id, 2);
|
||||
memcpy(e->dgram + 20, &icmp_hdr->icmp_seq, 2);
|
||||
memcpy(e->dgram + 2, &r->dst_ip, 4);
|
||||
memcpy(e->dgram + 6, &r->orig_src_ip, 4);
|
||||
memcpy(e->dgram + 10, &icmp_hdr->icmp_id, 2);
|
||||
memcpy(e->dgram + 12, &icmp_hdr->icmp_seq, 2);
|
||||
if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len);
|
||||
e->len = ICMP_PROXY_HDR_SIZE + payload_len;
|
||||
int ret = etcp_route_send(g_icmp_ctx->inst, TOPO_GROUP_UTUN, r->client_node_id, e, 0);
|
||||
@@ -155,15 +154,15 @@ static void raw_read_cb(socket_t sock, void* arg) {
|
||||
// ====================================================================
|
||||
static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL);
|
||||
if (!inst || !g_icmp_ctx || entry->len < ICMP_PROXY_HDR_SIZE + 1) goto drop;
|
||||
if (!inst || !g_icmp_ctx || entry->len < ICMP_PROXY_RECV_HDR_SIZE + 1) goto drop;
|
||||
|
||||
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + 2, 8);
|
||||
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + 10, 4);
|
||||
uint32_t orig_src_ip; memcpy(&orig_src_ip, entry->dgram + 14, 4);
|
||||
uint16_t echo_id; memcpy(&echo_id, entry->dgram + 18, 2);
|
||||
uint16_t echo_seq; memcpy(&echo_seq, entry->dgram + 20, 2);
|
||||
uint8_t* payload = entry->dgram + ICMP_PROXY_HDR_SIZE;
|
||||
size_t payload_len = entry->len - ICMP_PROXY_HDR_SIZE;
|
||||
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
|
||||
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
|
||||
uint32_t orig_src_ip; memcpy(&orig_src_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 5, 4);
|
||||
uint16_t echo_id; memcpy(&echo_id, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 9, 2);
|
||||
uint16_t echo_seq; memcpy(&echo_seq, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2);
|
||||
uint8_t* payload = entry->dgram + ICMP_PROXY_RECV_HDR_SIZE;
|
||||
size_t payload_len = entry->len - ICMP_PROXY_RECV_HDR_SIZE;
|
||||
|
||||
if (g_icmp_ctx->raw_sock != SOCKET_INVALID) {
|
||||
exit_send_echo(inst, client_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, payload_len);
|
||||
@@ -175,11 +174,10 @@ static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
if (e->dgram) {
|
||||
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
|
||||
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY;
|
||||
memcpy(e->dgram + 2, &client_node_id, 8);
|
||||
memcpy(e->dgram + 10, &dst_ip, 4);
|
||||
memcpy(e->dgram + 14, &orig_src_ip, 4);
|
||||
memcpy(e->dgram + 18, &echo_id, 2);
|
||||
memcpy(e->dgram + 20, &echo_seq, 2);
|
||||
memcpy(e->dgram + 2, &dst_ip, 4);
|
||||
memcpy(e->dgram + 6, &orig_src_ip, 4);
|
||||
memcpy(e->dgram + 10, &echo_id, 2);
|
||||
memcpy(e->dgram + 12, &echo_seq, 2);
|
||||
if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len);
|
||||
e->len = ICMP_PROXY_HDR_SIZE + payload_len;
|
||||
etcp_route_send(inst, TOPO_GROUP_UTUN, client_node_id, e, 0);
|
||||
@@ -197,19 +195,19 @@ drop:
|
||||
// Сторона клиента: принять REPLY, доставить echo ответ в TUN
|
||||
// ====================================================================
|
||||
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (entry->len < ICMP_PROXY_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
if (entry->len < ICMP_PROXY_RECV_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
uint32_t src_ip;
|
||||
uint32_t orig_src_ip;
|
||||
uint16_t echo_id, echo_seq;
|
||||
memcpy(&src_ip, entry->dgram + 10, 4);
|
||||
memcpy(&orig_src_ip, entry->dgram + 14, 4);
|
||||
memcpy(&echo_id, entry->dgram + 18, 2);
|
||||
memcpy(&echo_seq, entry->dgram + 20, 2);
|
||||
memcpy(&src_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
|
||||
memcpy(&orig_src_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 5, 4);
|
||||
memcpy(&echo_id, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 9, 2);
|
||||
memcpy(&echo_seq, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: client got reply id=0x%04x seq=%u from=0x%08x dst=0x%08x",
|
||||
echo_id, echo_seq, src_ip, orig_src_ip);
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL);
|
||||
icmp_proxy_deliver_reply(inst, orig_src_ip, src_ip, echo_id, echo_seq,
|
||||
entry->dgram + ICMP_PROXY_HDR_SIZE, entry->len - ICMP_PROXY_HDR_SIZE);
|
||||
entry->dgram + ICMP_PROXY_RECV_HDR_SIZE, entry->len - ICMP_PROXY_RECV_HDR_SIZE);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
}
|
||||
|
||||
@@ -217,11 +215,11 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
// Единый etcp_router коллбэк
|
||||
// ====================================================================
|
||||
void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!entry || !entry->dgram || entry->len < 2) {
|
||||
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_HDR_SIZE) {
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF];
|
||||
if (subcmd == ICMP_PROXY_SUBCMD_REQUEST) { exit_handle_request(conn, entry); return; }
|
||||
if (subcmd == ICMP_PROXY_SUBCMD_REPLY) { client_handle_reply(conn, entry); return; }
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
@@ -240,11 +238,10 @@ int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
|
||||
if (!e->dgram) { queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
|
||||
e->dgram[1] = ICMP_PROXY_SUBCMD_REQUEST;
|
||||
memcpy(e->dgram + 2, &inst->node_id, 8);
|
||||
memcpy(e->dgram + 10, &dst_ip, 4);
|
||||
memcpy(e->dgram + 14, &orig_src_ip, 4);
|
||||
memcpy(e->dgram + 18, &echo_id, 2);
|
||||
memcpy(e->dgram + 20, &echo_seq, 2);
|
||||
memcpy(e->dgram + 2, &dst_ip, 4);
|
||||
memcpy(e->dgram + 6, &orig_src_ip, 4);
|
||||
memcpy(e->dgram + 10, &echo_id, 2);
|
||||
memcpy(e->dgram + 12, &echo_seq, 2);
|
||||
if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len);
|
||||
e->len = ICMP_PROXY_HDR_SIZE + payload_len;
|
||||
return etcp_route_send(inst, TOPO_GROUP_UTUN, exit_node_id, e, 0);
|
||||
|
||||
@@ -9,6 +9,7 @@ extern "C" {
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../routing_layer/etcp_router.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct UASYNC;
|
||||
@@ -19,9 +20,12 @@ struct ETCP_CONN;
|
||||
#define ICMP_PROXY_SUBCMD_REQUEST 0x01 // client→exit: пингани dst_ip
|
||||
#define ICMP_PROXY_SUBCMD_REPLY 0x02 // exit→client: echo reply
|
||||
|
||||
// Заголовок сообщения (не включая байт svc_id)
|
||||
// svc_id(1) + subcmd(1) + sender_node_id(8) + dst_ip(4) + orig_src_ip(4) + icmp_id(2) + icmp_seq(2) + payload
|
||||
#define ICMP_PROXY_HDR_SIZE 22
|
||||
// Заголовок сообщения на отправке (включая байт svc_id; src/dst node_id добавляет роутер):
|
||||
// svc_id(1) + subcmd(1) + dst_ip(4) + orig_src_ip(4) + icmp_id(2) + icmp_seq(2) + payload
|
||||
#define ICMP_PROXY_HDR_SIZE 14
|
||||
// Полный заголовок на приёме (после router_deliver):
|
||||
// svc_id(1) + src_node_id(8) + dst_node_id(8) + subcmd(1) + dst_ip(4) + orig_src_ip(4) + icmp_id(2) + icmp_seq(2)
|
||||
#define ICMP_PROXY_RECV_HDR_SIZE (ROUTER_SVC_PAYLOAD_OFF + 13) // 30
|
||||
|
||||
// Активный ICMP echo запрос (сторона exit узла)
|
||||
struct icmp_request {
|
||||
|
||||
+167
-81
@@ -45,8 +45,10 @@ static struct ll_entry* tcp_proxy_client_entry_from_data(struct memory_pool* poo
|
||||
static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc);
|
||||
static void tcp_proxy_client_conn_finish(struct tcp_proxy_client_conn *pc);
|
||||
static int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len, int force);
|
||||
static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len);
|
||||
static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, void* arg);
|
||||
static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len, int force);
|
||||
static void tcp_proxy_client_tx_queue_drain_cb(struct ll_queue* q, void* arg);
|
||||
static void tcp_proxy_client_pause_resume_cb(struct ll_queue* q, void* arg);
|
||||
static void tcp_proxy_client_retry_timer_cb(void* arg);
|
||||
|
||||
// ====================================================================
|
||||
// Помощник ll_entry
|
||||
@@ -90,10 +92,17 @@ static int tcp_proxy_client_send_connect(struct tcp_proxy_client_conn* pc) {
|
||||
TCP_PROXY_SUBCMD_CONNECT, pc->stream_id, buf, 6, 1);
|
||||
}
|
||||
|
||||
static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY SEND sid=%08x len=%u", pc->stream_id, len);
|
||||
return tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id,
|
||||
TCP_PROXY_SUBCMD_DATA, pc->stream_id, data, len, 0);
|
||||
static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len, int force) {
|
||||
int ret = tcp_proxy_client_send_msg(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id,
|
||||
TCP_PROXY_SUBCMD_DATA, pc->stream_id, data, len, force);
|
||||
if (ret == 0) pc->bytes_to_exit += len;
|
||||
else {
|
||||
pc->bp_count++;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP send-fail sid=%08x len=%u ret=%d tot=%u bp#=%u rcv_wnd=%u",
|
||||
pc->stream_id, len, ret, pc->bytes_to_exit, pc->bp_count,
|
||||
pc->pcb ? pc->pcb->rcv_wnd : 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcp_proxy_client_send_close(struct tcp_proxy_client_conn* pc) {
|
||||
@@ -238,6 +247,13 @@ static err_t tcp_proxy_client_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t
|
||||
tcp_nagle_disable(newpcb);
|
||||
|
||||
pc->to_lwip = queue_new(p->ua, 0, 0, 0, "to_lwip");
|
||||
pc->tx_queue = queue_new(p->ua, 0, 0, 0, "tx_queue");
|
||||
if (!pc->tx_queue) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: tx_queue alloc failed sid=%08x", pc->stream_id);
|
||||
queue_free(pc->to_lwip); pc->to_lwip = NULL;
|
||||
tcp_arg(newpcb, NULL); tcp_abort(newpcb); u_free(pc); return LERR_MEM;
|
||||
}
|
||||
queue_set_callback(pc->tx_queue, tcp_proxy_client_tx_queue_drain_cb, pc);
|
||||
|
||||
if (tcp_proxy_client_send_connect(pc) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy client: send_connect failed sid=%08x to %d.%d.%d.%d:%d", pc->stream_id, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port)); tcp_arg(newpcb, NULL); tcp_abort(newpcb); queue_free(pc->to_lwip); u_free(pc); return LERR_MEM; }
|
||||
|
||||
@@ -248,23 +264,83 @@ static err_t tcp_proxy_client_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Backpressure: waiter callback при освобождении normalizer очереди
|
||||
// Backpressure: буфер lwIP→ETCP (tx_queue) с пачковым восстановлением окна.
|
||||
// Зеркало tcp_proxy_server.c read_queue_drain_cb + pause_resume_cb + retry_timer_cb.
|
||||
// ====================================================================
|
||||
static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, void* arg) {
|
||||
|
||||
// После отправки последних данных (очередь пуста): релеить FIN/CLOSE в exit.
|
||||
// Возвращает 1, если соединение завершено и pc освобождён (дальше pc/q использовать нельзя).
|
||||
static int tcp_proxy_client_fin_flush(struct tcp_proxy_client_conn* pc) {
|
||||
if (pc->fin_local && !pc->close_sent && !pc->close_pending) {
|
||||
if (pc->fin_remote || pc->rem_closed)
|
||||
tcp_proxy_client_send_close(pc);
|
||||
else
|
||||
tcp_proxy_client_send_fin(pc);
|
||||
}
|
||||
if (pc->fin_local && pc->fin_remote) {
|
||||
tcp_proxy_client_conn_finish(pc);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Дрейн tx_queue → ETCP. tcp_recved вызывается только после успешной отправки:
|
||||
// окно закрывается плавно (штатная backpressure), а при возобновлении открывается
|
||||
// пачкой (за 2 пакета wnd_inflation ≥ TCP_WND_UPDATE_THRESHOLD → немедленный ACK).
|
||||
static void tcp_proxy_client_tx_queue_drain_cb(struct ll_queue* q, void* arg) {
|
||||
struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg;
|
||||
if (!pc || !pc->proxy || pc->rem_closed) return;
|
||||
|
||||
struct ll_entry* e = queue_data_get(q);
|
||||
if (!e) { queue_resume_callback(q); return; }
|
||||
|
||||
int ret = tcp_proxy_client_send_data(pc, e->dgram, e->len, 0);
|
||||
if (ret == 0) {
|
||||
if (pc->pcb) tcp_recved(pc->pcb, e->len);
|
||||
queue_dgram_free(e); queue_entry_free(e);
|
||||
if (queue_entry_count(q) == 0 && tcp_proxy_client_fin_flush(pc)) return;
|
||||
queue_resume_callback(q);
|
||||
} else {
|
||||
queue_data_put_first(q, e);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP stall sid=%08x q=%d rcv_wnd=%u",
|
||||
pc->stream_id, queue_entry_count(q), pc->pcb ? pc->pcb->rcv_wnd : 0);
|
||||
etcp_router_on_send_ready(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id,
|
||||
ETCP_RT_ID_TCP_PROXY_SERVER, &pc->tx_waiter,
|
||||
tcp_proxy_client_pause_resume_cb, pc);
|
||||
if (!pc->tx_retry_timer)
|
||||
pc->tx_retry_timer = uasync_set_timeout(pc->proxy->ua, 5000, pc, tcp_proxy_client_retry_timer_cb, "tpc_retry");
|
||||
}
|
||||
}
|
||||
|
||||
// send_q освободился — возобновить дрейн tx_queue
|
||||
static void tcp_proxy_client_pause_resume_cb(struct ll_queue* q, void* arg) {
|
||||
(void)q;
|
||||
struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg;
|
||||
if (!pc || !pc->proxy || pc->rem_closed || !pc->tx_buf) return;
|
||||
int ret = tcp_proxy_client_send_data(pc, pc->tx_buf, pc->tx_len);
|
||||
if (!pc || !pc->proxy || pc->rem_closed || !pc->tx_queue) return;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP resume sid=%08x q=%d",
|
||||
pc->stream_id, queue_entry_count(pc->tx_queue));
|
||||
queue_resume_callback(pc->tx_queue);
|
||||
}
|
||||
|
||||
// Safety-net: форсированный ретрай, если waiter не сработал
|
||||
static void tcp_proxy_client_retry_timer_cb(void* arg) {
|
||||
struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg;
|
||||
if (!pc || !pc->proxy) return;
|
||||
pc->tx_retry_timer = NULL;
|
||||
if (pc->rem_closed || !pc->tx_queue) return;
|
||||
struct ll_entry* e = queue_data_get(pc->tx_queue);
|
||||
if (!e) { queue_resume_callback(pc->tx_queue); return; }
|
||||
int ret = tcp_proxy_client_send_data(pc, e->dgram, e->len, 1);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_PROXY, "PROXY BP retry(force) sid=%08x len=%u ret=%d",
|
||||
pc->stream_id, e->len, ret);
|
||||
if (ret == 0) {
|
||||
if (pc->pcb) tcp_recved(pc->pcb, pc->tx_len);
|
||||
u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0;
|
||||
if (pc->fin_local && !pc->close_sent && !pc->close_pending) {
|
||||
if (pc->fin_remote || pc->rem_closed)
|
||||
tcp_proxy_client_send_close(pc);
|
||||
else
|
||||
tcp_proxy_client_send_fin(pc);
|
||||
}
|
||||
if (pc->fin_local && pc->fin_remote) tcp_proxy_client_conn_finish(pc);
|
||||
if (pc->pcb) tcp_recved(pc->pcb, e->len);
|
||||
queue_dgram_free(e); queue_entry_free(e);
|
||||
if (queue_entry_count(pc->tx_queue) == 0 && tcp_proxy_client_fin_flush(pc)) return;
|
||||
queue_resume_callback(pc->tx_queue);
|
||||
} else {
|
||||
queue_data_put_first(pc->tx_queue, e);
|
||||
pc->tx_retry_timer = uasync_set_timeout(pc->proxy->ua, 5000, pc, tcp_proxy_client_retry_timer_cb, "tpc_retry");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,16 +351,13 @@ static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbu
|
||||
if (p == NULL || err != LERR_OK) {
|
||||
pc->fin_local = 1;
|
||||
if (p == NULL && err == ERR_OK) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY FIN sid=%08x pcb_state=%u sndbuf=%u cwnd=%u unsent=%p unacked=%p",
|
||||
pc->stream_id, pcb->state, pcb->snd_buf, pcb->cwnd,
|
||||
(void*)pcb->unsent, (void*)pcb->unacked);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY FIN sid=%08x to_exit=%u from_exit=%u bp#=%u pcb_state=%u sndbuf=%u cwnd=%u",
|
||||
pc->stream_id, pc->bytes_to_exit, pc->bytes_from_exit, pc->bp_count,
|
||||
pcb->state, pcb->snd_buf, pcb->cwnd);
|
||||
{ uint16_t wnd_gap = TCP_WND_MAX(pcb) - pcb->rcv_wnd;
|
||||
if (wnd_gap > 0) tcp_recved(pcb, wnd_gap); }
|
||||
if (!pc->tx_buf) {
|
||||
tcp_proxy_client_send_fin(pc);
|
||||
if (pc->fin_remote && !pc->close_sent && !pc->close_pending)
|
||||
tcp_proxy_client_send_close(pc);
|
||||
if (pc->fin_remote) { tcp_proxy_client_conn_finish(pc); return LERR_OK; }
|
||||
if (pc->tx_queue && queue_entry_count(pc->tx_queue) == 0) {
|
||||
if (tcp_proxy_client_fin_flush(pc)) return LERR_OK;
|
||||
}
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY ERROR recv sid=%08x pcb_state=%u err=%d",
|
||||
@@ -300,20 +373,19 @@ static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbu
|
||||
tcp_recved(pcb, len); pbuf_free(p); return LERR_OK;
|
||||
}
|
||||
|
||||
if (pc->tx_buf) { pbuf_free(p); return LERR_OK; }
|
||||
|
||||
uint8_t *data = u_malloc(len);
|
||||
if (data) {
|
||||
pbuf_copy_partial(p, data, len, 0);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY RECV client->exit sid=%08x len=%u", pc->stream_id, len);
|
||||
int ret = tcp_proxy_client_send_data(pc, data, len);
|
||||
if (ret == 0) { tcp_recved(pcb, len); u_free(data); }
|
||||
else {
|
||||
pc->tx_buf = data; pc->tx_len = len;
|
||||
etcp_router_waiter_register(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, &pc->tx_waiter,
|
||||
tcp_proxy_client_tx_waiter_cb, pc);
|
||||
}
|
||||
} else DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY recv malloc(%u) failed sid=%08x", len, pc->stream_id);
|
||||
if (!data) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY recv malloc(%u) failed sid=%08x", len, pc->stream_id);
|
||||
pbuf_free(p); return LERR_OK;
|
||||
}
|
||||
pbuf_copy_partial(p, data, len, 0);
|
||||
struct ll_entry* e = queue_entry_new_from_pool(pc->proxy->entry_pool);
|
||||
if (!e) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "PROXY recv entry pool exhausted sid=%08x", pc->stream_id);
|
||||
u_free(data); pbuf_free(p); return LERR_OK;
|
||||
}
|
||||
e->dgram = data; e->len = len;
|
||||
queue_data_put(pc->tx_queue, e);
|
||||
pbuf_free(p);
|
||||
return LERR_OK;
|
||||
}
|
||||
@@ -435,8 +507,13 @@ static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc) {
|
||||
while ((e = queue_data_get(pc->to_lwip))) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
queue_free(pc->to_lwip); pc->to_lwip = NULL;
|
||||
}
|
||||
if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; }
|
||||
if (pc->proxy && pc->proxy->inst) etcp_router_waiter_cancel(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, &pc->tx_waiter);
|
||||
if (pc->tx_queue) {
|
||||
struct ll_entry *e;
|
||||
while ((e = queue_data_get(pc->tx_queue))) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
queue_free(pc->tx_queue); pc->tx_queue = NULL;
|
||||
}
|
||||
if (pc->tx_retry_timer) { uasync_cancel_timeout(p->ua, pc->tx_retry_timer); pc->tx_retry_timer = NULL; }
|
||||
if (p && p->inst) etcp_router_cancel_send_ready(p->inst, TOPO_GROUP_UTUN, p->via_node_id, ETCP_RT_ID_TCP_PROXY_SERVER, &pc->tx_waiter);
|
||||
u_free(pc);
|
||||
}
|
||||
|
||||
@@ -471,10 +548,11 @@ static void tcp_proxy_client_handle_data(struct tcp_proxy_client* p, struct ETCP
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY DATA sid=%08x — no conn/closed, dropping", stream_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
size_t data_len = entry->len - TCP_PROXY_HDR_SIZE;
|
||||
size_t data_len = entry->len - TCP_PROXY_RECV_HDR_SIZE;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY DATA <- sid=%08x len=%zu", stream_id, data_len);
|
||||
if (data_len > 0) {
|
||||
struct ll_entry* e = tcp_proxy_client_entry_from_data(pc->proxy->entry_pool, entry->dgram + TCP_PROXY_HDR_SIZE, (uint16_t)data_len);
|
||||
pc->bytes_from_exit += (uint32_t)data_len;
|
||||
struct ll_entry* e = tcp_proxy_client_entry_from_data(pc->proxy->entry_pool, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, (uint16_t)data_len);
|
||||
if (e) queue_data_put(pc->to_lwip, e);
|
||||
tcp_proxy_client_feed_from_transport(pc);
|
||||
}
|
||||
@@ -486,8 +564,6 @@ static void tcp_proxy_client_handle_close(struct tcp_proxy_client* p, uint32_t s
|
||||
if (!pc) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "PROXY CLOSE sid=%08x — no conn, dropping", stream_id); return; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY REM_CLOSED sid=%08x fin_local=%d pcb_state=%u", stream_id, pc->fin_local, pc->pcb ? pc->pcb->state : 0);
|
||||
pc->rem_closed = 1;
|
||||
if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; }
|
||||
etcp_router_waiter_cancel(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, &pc->tx_waiter);
|
||||
if (pc->pcb) {
|
||||
tcp_arg(pc->pcb, NULL);
|
||||
tcp_recv(pc->pcb, NULL);
|
||||
@@ -514,8 +590,6 @@ static void tcp_proxy_client_handle_error(struct tcp_proxy_client* p, uint32_t s
|
||||
pc->pcb = NULL;
|
||||
}
|
||||
pc->rem_closed = 1;
|
||||
if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; }
|
||||
etcp_router_waiter_cancel(pc->proxy->inst, TOPO_GROUP_UTUN, pc->proxy->via_node_id, &pc->tx_waiter);
|
||||
tcp_proxy_client_conn_free(pc);
|
||||
}
|
||||
|
||||
@@ -541,49 +615,56 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
|
||||
struct tcp_proxy_client* proxy = inst ? inst->tcp_proxy_client : NULL;
|
||||
|
||||
if (!entry || !entry->dgram || entry->len < TCP_PROXY_HDR_SIZE) {
|
||||
if (entry && entry->len == 9 && !conn && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY_CLIENT) {
|
||||
uint64_t peer_id;
|
||||
memcpy(&peer_id, entry->dgram + 1, 8);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY CLOSE_ALL from %016llx — clearing client conns for peer",
|
||||
(unsigned long long)peer_id);
|
||||
if (proxy) {
|
||||
struct tcp_proxy_client_conn *pc, *next;
|
||||
for (pc = proxy->conns; pc; pc = next) {
|
||||
next = pc->next;
|
||||
if (pc->pcb) {
|
||||
tcp_arg(pc->pcb, NULL);
|
||||
tcp_recv(pc->pcb, NULL);
|
||||
tcp_sent(pc->pcb, NULL);
|
||||
tcp_err(pc->pcb, NULL);
|
||||
tcp_poll(pc->pcb, NULL, 0);
|
||||
tcp_abort(pc->pcb);
|
||||
pc->pcb = NULL;
|
||||
}
|
||||
tcp_proxy_client_conn_free(pc);
|
||||
}
|
||||
proxy->conns = NULL; proxy->conn_count = 0;
|
||||
socks_proxy_conn_free_all(&proxy->socks_conns, &proxy->socks_conn_count);
|
||||
socks_proxy_conn_free_all(&proxy->http_conns, &proxy->http_conn_count);
|
||||
}
|
||||
}
|
||||
if (!entry || !entry->dgram) {
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4);
|
||||
// CLOSE_ALL / restart-уведомление: [svc_id][src][dst] без payload
|
||||
if (entry->len == ROUTER_SVC_HDR_SIZE && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY_CLIENT) {
|
||||
uint64_t peer_id;
|
||||
memcpy(&peer_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY CLOSE_ALL from %016llx — clearing client conns for peer",
|
||||
(unsigned long long)peer_id);
|
||||
if (proxy) {
|
||||
struct tcp_proxy_client_conn *pc, *next;
|
||||
for (pc = proxy->conns; pc; pc = next) {
|
||||
next = pc->next;
|
||||
if (pc->pcb) {
|
||||
tcp_arg(pc->pcb, NULL);
|
||||
tcp_recv(pc->pcb, NULL);
|
||||
tcp_sent(pc->pcb, NULL);
|
||||
tcp_err(pc->pcb, NULL);
|
||||
tcp_poll(pc->pcb, NULL, 0);
|
||||
tcp_abort(pc->pcb);
|
||||
pc->pcb = NULL;
|
||||
}
|
||||
tcp_proxy_client_conn_free(pc);
|
||||
}
|
||||
proxy->conns = NULL; proxy->conn_count = 0;
|
||||
socks_proxy_conn_free_all(&proxy->socks_conns, &proxy->socks_conn_count);
|
||||
socks_proxy_conn_free_all(&proxy->http_conns, &proxy->http_conn_count);
|
||||
}
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
if (entry->len < TCP_PROXY_RECV_HDR_SIZE) {
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF];
|
||||
uint32_t stream_id; memcpy(&stream_id, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY CLIENT RECV subcmd=%02x sid=%08x pkt_len=%u", subcmd, stream_id, entry->len);
|
||||
|
||||
if (proxy) {
|
||||
size_t data_len = entry->len > TCP_PROXY_HDR_SIZE ? entry->len - TCP_PROXY_HDR_SIZE : 0;
|
||||
size_t data_len = entry->len > TCP_PROXY_RECV_HDR_SIZE ? entry->len - TCP_PROXY_RECV_HDR_SIZE : 0;
|
||||
// Пробуем SOCKS/HTTP first (у них приоритет — могут быть без TUN)
|
||||
if (proxy->socks_enabled && socks_proxy_handle_etcp(&proxy->socks_conns, &proxy->socks_conn_count,
|
||||
stream_id, subcmd, entry->dgram + TCP_PROXY_HDR_SIZE, data_len)) {
|
||||
stream_id, subcmd, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, data_len)) {
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
if (proxy->http_proxy_enabled && socks_proxy_handle_etcp(&proxy->http_conns, &proxy->http_conn_count,
|
||||
stream_id, subcmd, entry->dgram + TCP_PROXY_HDR_SIZE, data_len)) {
|
||||
stream_id, subcmd, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, data_len)) {
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
// Существующие lwIP conns
|
||||
@@ -707,8 +788,13 @@ void tcp_proxy_client_destroy(struct tcp_proxy_client* p) {
|
||||
while ((e = queue_data_get(pc->to_lwip))) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
queue_free(pc->to_lwip); pc->to_lwip = NULL;
|
||||
}
|
||||
if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; }
|
||||
if (p->inst) etcp_router_waiter_cancel(p->inst, TOPO_GROUP_UTUN, p->via_node_id, &pc->tx_waiter);
|
||||
if (pc->tx_queue) {
|
||||
struct ll_entry *e;
|
||||
while ((e = queue_data_get(pc->tx_queue))) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
queue_free(pc->tx_queue); pc->tx_queue = NULL;
|
||||
}
|
||||
if (pc->tx_retry_timer) { uasync_cancel_timeout(p->ua, pc->tx_retry_timer); pc->tx_retry_timer = NULL; }
|
||||
if (p->inst) etcp_router_cancel_send_ready(p->inst, TOPO_GROUP_UTUN, p->via_node_id, ETCP_RT_ID_TCP_PROXY_SERVER, &pc->tx_waiter);
|
||||
u_free(pc); pc = next;
|
||||
}
|
||||
p->conns = NULL;
|
||||
|
||||
@@ -43,9 +43,13 @@ struct tcp_proxy_client_conn {
|
||||
uint8_t close_sent; // отправили CLOSE/ERROR в exit
|
||||
uint8_t close_pending; // CLOSE/ERROR не доставлен, ждём повтора
|
||||
|
||||
uint8_t* tx_buf; // буфер при backpressure (lwIP→ETCP send fail)
|
||||
uint16_t tx_len;
|
||||
struct queue_waiter_handle tx_waiter;
|
||||
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_from_exit; // байт получено от exit (в lwIP/TUN)
|
||||
uint32_t bp_count; // счётчик backpressure (send fail)
|
||||
|
||||
uint8_t dest_ip[4];
|
||||
uint16_t dest_port;
|
||||
|
||||
@@ -352,8 +352,8 @@ struct tcp_proxy_server_conn* tcp_proxy_server_find_conn(struct tcp_proxy_server
|
||||
int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry, uint32_t stream_id, uint64_t src_node_id) {
|
||||
if (!inst || !inst->tcp_proxy_server.enabled) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return -1; }
|
||||
struct tcp_proxy_server* ctx = &inst->tcp_proxy_server;
|
||||
if (entry->len < TCP_PROXY_CONNECT_HDR_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: CONNECT too short len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); return -1; }
|
||||
uint8_t* dest_ip = entry->dgram + TCP_PROXY_HDR_SIZE;
|
||||
if (entry->len < TCP_PROXY_RECV_HDR_SIZE + 6) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: CONNECT too short len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); return -1; }
|
||||
uint8_t* dest_ip = entry->dgram + TCP_PROXY_RECV_HDR_SIZE;
|
||||
uint16_t dest_port = 0; memcpy(&dest_port, dest_ip + 4, 2);
|
||||
|
||||
struct tcp_proxy_server_conn* rc = u_calloc(1, sizeof(struct tcp_proxy_server_conn));
|
||||
@@ -365,8 +365,9 @@ int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry*
|
||||
socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "TCP proxy server: socket() failed"); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; }
|
||||
ctx->conn_count++;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "SOCK:NEW fd=%d sid=%08x dest=%d.%d.%d.%d:%d total=%d",
|
||||
(int)sock, stream_id, dest_ip[0],dest_ip[1],dest_ip[2],dest_ip[3],ntohs(dest_port), ctx->conn_count);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "SOCK:NEW fd=%d sid=%08x src=%016llx dest=%d.%d.%d.%d:%d total=%d",
|
||||
(int)sock, stream_id, (unsigned long long)src_node_id,
|
||||
dest_ip[0],dest_ip[1],dest_ip[2],dest_ip[3],ntohs(dest_port), ctx->conn_count);
|
||||
socket_set_nonblocking(sock);
|
||||
|
||||
struct global_config* g = &inst->config->global;
|
||||
@@ -413,6 +414,7 @@ int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry*
|
||||
}
|
||||
|
||||
rc->next = ctx->conns; ctx->conns = rc;
|
||||
tcp_conn_set_connect_timeout(rc->tc, g->tcp_proxy_server_connect_timeout_ms);
|
||||
rc->diag_timer = uasync_set_timeout(rc->ua, 10000, rc, diag_timer_cb, "tps_diag");
|
||||
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
@@ -428,7 +430,7 @@ int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* c
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TPS handle_data: no/closed conn sid=%08x, dropping", stream_id);
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return -1;
|
||||
}
|
||||
size_t data_len = entry->len - TCP_PROXY_HDR_SIZE;
|
||||
size_t data_len = entry->len - TCP_PROXY_RECV_HDR_SIZE;
|
||||
rc->bytes_sent += (uint32_t)data_len; rc->data_count++;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "TPS DATA #%u sid=%08x len=%zu",
|
||||
rc->data_count, stream_id, data_len);
|
||||
@@ -441,7 +443,7 @@ int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* c
|
||||
struct ll_entry* e = queue_entry_new_from_pool(rc->tc->entry_pool);
|
||||
uint8_t* buf = memory_pool_alloc(rc->tc->data_pool);
|
||||
if (e && buf) {
|
||||
memcpy(buf, entry->dgram + TCP_PROXY_HDR_SIZE, data_len);
|
||||
memcpy(buf, entry->dgram + TCP_PROXY_RECV_HDR_SIZE, data_len);
|
||||
e->dgram = buf; e->len = (uint16_t)data_len;
|
||||
queue_data_put(rc->tc->write_queue, e);
|
||||
} else {
|
||||
@@ -503,30 +505,39 @@ void tcp_proxy_server_handle_fin(struct UTUN_INSTANCE* inst, uint32_t stream_id)
|
||||
void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
|
||||
|
||||
if (!entry || !entry->dgram || entry->len < TCP_PROXY_HDR_SIZE) {
|
||||
if (entry && entry->len == 9 && !conn && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY_SERVER) {
|
||||
uint64_t peer_id;
|
||||
memcpy(&peer_id, entry->dgram + 1, 8);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY CLOSE_ALL from %016llx — clearing server conns for peer",
|
||||
(unsigned long long)peer_id);
|
||||
if (inst && inst->tcp_proxy_server.enabled) {
|
||||
struct tcp_proxy_server_conn *rc, *next;
|
||||
for (rc = inst->tcp_proxy_server.conns; rc; rc = next) {
|
||||
next = rc->next;
|
||||
if (rc->peer_node_id == peer_id) tcp_proxy_server_conn_free(rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!entry || !entry->dgram) {
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4);
|
||||
// CLOSE_ALL / restart-уведомление: [svc_id][src][dst] без payload
|
||||
if (entry->len == ROUTER_SVC_HDR_SIZE && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY_SERVER) {
|
||||
uint64_t peer_id;
|
||||
memcpy(&peer_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "PROXY CLOSE_ALL from %016llx — clearing server conns for peer",
|
||||
(unsigned long long)peer_id);
|
||||
if (inst && inst->tcp_proxy_server.enabled) {
|
||||
struct tcp_proxy_server_conn *rc, *next;
|
||||
for (rc = inst->tcp_proxy_server.conns; rc; rc = next) {
|
||||
next = rc->next;
|
||||
if (rc->peer_node_id == peer_id) tcp_proxy_server_conn_free(rc);
|
||||
}
|
||||
}
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
if (entry->len < TCP_PROXY_RECV_HDR_SIZE) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "TCP proxy server: short packet len=%u", entry->len);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF];
|
||||
uint32_t stream_id; memcpy(&stream_id, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_PROXY, "PROXY SERVER RECV subcmd=%02x sid=%08x pkt_len=%u", subcmd, stream_id, entry->len);
|
||||
|
||||
if (subcmd == TCP_PROXY_SUBCMD_CONNECT) {
|
||||
uint64_t src_node_id = conn ? conn->peer_node_id : (inst ? inst->node_id : 0);
|
||||
uint64_t src_node_id;
|
||||
memcpy(&src_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
|
||||
tcp_proxy_server_handle_connect(inst, entry, stream_id, src_node_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ extern "C" {
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/tcp_io.h"
|
||||
#include "../routing_layer/etcp_router.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct UASYNC;
|
||||
@@ -26,6 +27,9 @@ struct ETCP_CONN;
|
||||
#define TCP_PROXY_HDR_SIZE 6 // svc_id(1)+subcmd(1)+stream_id(4)
|
||||
#define TCP_PROXY_CONNECT_HDR_SIZE 12 // HDR_SIZE + dest_ip(4)+dest_port(2)
|
||||
|
||||
// recv-формат (после router_deliver): [svc_id][src][dst][subcmd][stream_id][data]
|
||||
#define TCP_PROXY_RECV_HDR_SIZE (ROUTER_SVC_PAYLOAD_OFF + 5) // 22: до data (subcmd+stream_id)
|
||||
|
||||
struct tcp_proxy_server_conn {
|
||||
struct tcp_proxy_server_conn* next;
|
||||
struct tcp_proxy_server* ctx;
|
||||
|
||||
@@ -28,6 +28,7 @@ TCP прокси-сервер на exit node. Принимает CONNECT-зап
|
||||
- **Graceful CLOSE:** при получении CLOSE от клиента чтение из OS сокета паузится через `tcp_conn_pause_read()`, read_queue сливается (данные отбрасываются), push-ится close в OS сокет.
|
||||
- **Ретрай CLOSE/ERROR:** при неудачной отправке `send_msg()` — экспоненциальный backoff (50→100→200→…→5000 tb = 5ms→500ms) через `close_timer` + `close_retry_cb`.
|
||||
- **Диагностический таймер (1s):** логирует TCP буферы ОС, размеры read/write очередей, free counts entry/data pool-ов.
|
||||
- **Connect-таймаут:** `connect()` к destination асинхронный; если за `connect_timeout` мс (конфиг `[tcp_proxy_server]`, по умолчанию 2000, `0` = без таймаута) соединение не установилось — срабатывает `tcp_conn_handle_error(ETIMEDOUT)` → клиенту уходит ERROR, соединение освобождается (защита от «чёрной дыры» и утечки fd).
|
||||
- **CLOSE_ALL:** специальный 9-байтовый пакет без валидного conn удаляет все соединения для заданного peer.
|
||||
|
||||
## 3. API
|
||||
|
||||
+27
-29
@@ -55,13 +55,12 @@ static void flow_read_cb(socket_t sock, void* arg) {
|
||||
if (!e->dgram) { queue_entry_free(e); return; }
|
||||
e->dgram[0] = ETCP_RT_ID_UDP_PROXY;
|
||||
e->dgram[1] = UDP_PROXY_SUBCMD_DATA;
|
||||
memcpy(e->dgram + 2, &f->client_node_id, 8);
|
||||
// Ответ src = оригинальный dst_ip:dest_port
|
||||
memcpy(e->dgram + 10, &f->dst_ip, 4);
|
||||
memcpy(e->dgram + 14, &f->dst_port, 2);
|
||||
memcpy(e->dgram + 2, &f->dst_ip, 4);
|
||||
memcpy(e->dgram + 6, &f->dst_port, 2);
|
||||
// Ответ dst = оригинальный src_ip:src_port
|
||||
memcpy(e->dgram + 16, &f->src_ip, 4);
|
||||
memcpy(e->dgram + 20, &f->src_port, 2);
|
||||
memcpy(e->dgram + 8, &f->src_ip, 4);
|
||||
memcpy(e->dgram + 12, &f->src_port, 2);
|
||||
memcpy(e->dgram + UDP_PROXY_HDR_SIZE, buf, n);
|
||||
e->len = UDP_PROXY_HDR_SIZE + n;
|
||||
|
||||
@@ -73,15 +72,15 @@ static void flow_read_cb(socket_t sock, void* arg) {
|
||||
// ====================================================================
|
||||
static void exit_handle_data(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_udp_ctx ? g_udp_ctx->inst : NULL);
|
||||
if (!inst || !g_udp_ctx || entry->len < UDP_PROXY_HDR_SIZE + 1) goto drop;
|
||||
if (!inst || !g_udp_ctx || entry->len < UDP_PROXY_RECV_HDR_SIZE + 1) goto drop;
|
||||
|
||||
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + 2, 8);
|
||||
uint32_t src_ip; memcpy(&src_ip, entry->dgram + 10, 4);
|
||||
uint16_t src_port; memcpy(&src_port, entry->dgram + 14, 2);
|
||||
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + 16, 4);
|
||||
uint16_t dst_port; memcpy(&dst_port, entry->dgram + 20, 2);
|
||||
uint8_t* payload = entry->dgram + UDP_PROXY_HDR_SIZE;
|
||||
size_t payload_len = entry->len - UDP_PROXY_HDR_SIZE;
|
||||
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
|
||||
uint32_t src_ip; memcpy(&src_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
|
||||
uint16_t src_port; memcpy(&src_port, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 5, 2);
|
||||
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 7, 4);
|
||||
uint16_t dst_port; memcpy(&dst_port, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2);
|
||||
uint8_t* payload = entry->dgram + UDP_PROXY_RECV_HDR_SIZE;
|
||||
size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE;
|
||||
|
||||
struct udp_flow* f = flow_find(g_udp_ctx->flows, client_node_id, src_ip, src_port, dst_ip, dst_port);
|
||||
if (!f) {
|
||||
@@ -115,17 +114,17 @@ drop:
|
||||
// Сторона клиента: принять REPLY, доставить в TUN
|
||||
// ====================================================================
|
||||
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (entry->len < UDP_PROXY_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
// svc_id уже обработан диспетчером etcp_router
|
||||
if (entry->len < UDP_PROXY_RECV_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
const uint8_t* d = entry->dgram;
|
||||
uint32_t src_ip, dst_ip;
|
||||
uint16_t src_port, dst_port;
|
||||
uint8_t* payload = entry->dgram + 1 + 1 + 8 + 4 + 2 + 4 + 2; // svc_id + subcmd + node_id + src_ip:port + dst_ip:port
|
||||
size_t payload_len = entry->len - UDP_PROXY_HDR_SIZE;
|
||||
|
||||
memcpy(&dst_ip, entry->dgram + 1 + 1 + 8 + 4 + 2, 4); // src в сообщении → dst в ответе
|
||||
memcpy(&dst_port, entry->dgram + 1 + 1 + 8 + 4 + 2 + 4, 2);
|
||||
memcpy(&src_ip, entry->dgram + 1 + 1 + 8, 4); // dst в сообщении → src в ответе
|
||||
memcpy(&src_port, entry->dgram + 1 + 1 + 8 + 4, 2);
|
||||
// В сообщении: src_ip:src_port = оригинальный dst, dst_ip:dst_port = оригинальный src
|
||||
memcpy(&src_ip, d + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
|
||||
memcpy(&src_port, d + ROUTER_SVC_PAYLOAD_OFF + 5, 2);
|
||||
memcpy(&dst_ip, d + ROUTER_SVC_PAYLOAD_OFF + 7, 4);
|
||||
memcpy(&dst_port, d + ROUTER_SVC_PAYLOAD_OFF + 11, 2);
|
||||
uint8_t* payload = d + UDP_PROXY_RECV_HDR_SIZE;
|
||||
size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE;
|
||||
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_udp_ctx ? g_udp_ctx->inst : NULL);
|
||||
udp_proxy_deliver_reply(inst, src_ip, src_port, dst_ip, dst_port, payload, payload_len);
|
||||
@@ -136,11 +135,11 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
// Единый etcp_router коллбэк
|
||||
// ====================================================================
|
||||
void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!entry || !entry->dgram || entry->len < 2) {
|
||||
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_HDR_SIZE) {
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF];
|
||||
if (subcmd == UDP_PROXY_SUBCMD_DATA) {
|
||||
if (g_udp_ctx && g_udp_ctx->is_exit) exit_handle_data(conn, entry);
|
||||
else client_handle_reply(conn, entry);
|
||||
@@ -163,11 +162,10 @@ int udp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
|
||||
if (!e->dgram) { queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_RT_ID_UDP_PROXY;
|
||||
e->dgram[1] = UDP_PROXY_SUBCMD_DATA;
|
||||
memcpy(e->dgram + 2, &inst->node_id, 8);
|
||||
memcpy(e->dgram + 10, &src_ip, 4);
|
||||
memcpy(e->dgram + 14, &src_port, 2);
|
||||
memcpy(e->dgram + 16, &dst_ip, 4);
|
||||
memcpy(e->dgram + 20, &dst_port, 2);
|
||||
memcpy(e->dgram + 2, &src_ip, 4);
|
||||
memcpy(e->dgram + 6, &src_port, 2);
|
||||
memcpy(e->dgram + 8, &dst_ip, 4);
|
||||
memcpy(e->dgram + 12, &dst_port, 2);
|
||||
if (payload_len > 0) memcpy(e->dgram + UDP_PROXY_HDR_SIZE, payload, payload_len);
|
||||
e->len = UDP_PROXY_HDR_SIZE + payload_len;
|
||||
return etcp_route_send(inst, TOPO_GROUP_UTUN, exit_node_id, e, 0);
|
||||
|
||||
@@ -9,6 +9,7 @@ extern "C" {
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../routing_layer/etcp_router.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct UASYNC;
|
||||
@@ -18,9 +19,12 @@ struct ETCP_CONN;
|
||||
// Подкоманды
|
||||
#define UDP_PROXY_SUBCMD_DATA 0x01 // client→exit: пробрось датаграмму | exit→client: ответ
|
||||
|
||||
// Заголовок сообщения (не включая байт svc_id)
|
||||
// svc_id(1) + subcmd(1) + sender_node_id(8) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2) + payload
|
||||
#define UDP_PROXY_HDR_SIZE 22
|
||||
// Заголовок сообщения на отправке (включая байт svc_id; src/dst node_id добавляет роутер):
|
||||
// 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
|
||||
|
||||
// Один UDP поток (сторона exit узла: сопоставляет client_node_id + кортеж адресов с OS сокетом)
|
||||
struct udp_flow {
|
||||
|
||||
Reference in new issue
Block a user