docs(proxy): document per-instance datagram contexts
This commit is contained in:
1 parent
23cec465bf
commit
27cf6ae365
3 files changed
+131
-156
No files matched your search
+41
-28
@@ -1,7 +1,9 @@
|
||||
/* IPv4 ICMP echo через UTUN-router: exit отправляет ping через raw socket, сопоставляет reply
|
||||
* по собственному wire id/seq и восстанавливает исходные id/seq клиента. Запросы ограничены таймаутом.
|
||||
* init/destroy и callbacks — в uasync; контекст принадлежит inst, роль берётся из tcp_proxy_server.enabled.
|
||||
* test_loopback возвращает виртуальные ответы без сетевой отправки и предназначен для тестов. */
|
||||
* по wire id/seq и IP цели, восстанавливая исходные id/seq клиента. Запросы ограничены таймаутом.
|
||||
* init/destroy и callbacks — в uasync; inst владеет контекстом в inst->icmp_proxy.
|
||||
* init создаёт raw socket, если server.enabled; recv_cb проверяет текущий server.enabled для REQUEST
|
||||
* и via_node_id клиентского proxy для REPLY. IP/id/seq в API — network byte order.
|
||||
* test_loopback возвращает виртуальные ответы только при отсутствии raw socket; флаг его не отключает. */
|
||||
#ifndef ICMP_PROXY_H
|
||||
#define ICMP_PROXY_H
|
||||
|
||||
@@ -23,59 +25,70 @@ 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; src/dst node_id добавляет роутер):
|
||||
// Сообщение для etcp_route_send (не полный wire packet; метаданные приёма добавляет router):
|
||||
// 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
|
||||
// router header(26: svc/src/dst/flags/group) + 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) // 39
|
||||
|
||||
// Активный ICMP echo запрос (сторона exit узла)
|
||||
struct icmp_request {
|
||||
struct icmp_request* next;
|
||||
uint64_t client_node_id;
|
||||
uint32_t dst_ip;
|
||||
struct icmp_request* next; // следующий ожидающий запрос exit
|
||||
uint64_t client_node_id; // ingress-узел для обратной доставки
|
||||
uint32_t dst_ip; // IP цели ping, network byte order
|
||||
uint32_t orig_src_ip; // IP исходного отправителя (чтобы вернуть reply правильному адресату)
|
||||
uint16_t echo_id;
|
||||
uint16_t echo_seq;
|
||||
uint16_t wire_id;
|
||||
uint16_t wire_seq;
|
||||
uint8_t payload[1500];
|
||||
size_t payload_len;
|
||||
uint64_t sent_tb;
|
||||
socket_t sock; // raw сокет (только если per-request)
|
||||
uint16_t echo_id; // исходный id приложения, network byte order
|
||||
uint16_t echo_seq; // исходный seq приложения, network byte order
|
||||
uint16_t wire_id; // заменённый id в raw ICMP, network byte order
|
||||
uint16_t wire_seq; // заменённый seq, уникален вместе с wire_id и dst_ip среди pending
|
||||
uint8_t payload[1500]; // сохранённый префикс запроса, не используется для сборки ответа
|
||||
size_t payload_len; // длина сохранённого префикса, не больше sizeof(payload)
|
||||
uint64_t sent_tb; // время отправки, tb = 0.1 мс
|
||||
socket_t sock; // не используется; запросы идут через общий ctx->raw_sock
|
||||
};
|
||||
|
||||
struct icmp_proxy_ctx {
|
||||
int initialized;
|
||||
int is_exit;
|
||||
struct UTUN_INSTANCE* inst;
|
||||
struct UASYNC* ua;
|
||||
int initialized; // init завершён, binding запрошен
|
||||
int is_exit; // снимок server.enabled при init (определяет создание raw socket)
|
||||
struct UTUN_INSTANCE* inst; // заимствованный instance-владелец
|
||||
struct UASYNC* ua; // заимствованный event loop
|
||||
socket_t raw_sock; // один SOCK_RAW для всего ICMP на exit
|
||||
void* raw_read_id;
|
||||
struct icmp_request* pending;
|
||||
uint32_t next_token;
|
||||
uint64_t request_timeout_tb;
|
||||
int test_loopback; // 1 = виртуальный loopback без raw сокета (только тесты)
|
||||
void* raw_read_id; // регистрация raw_sock в ua
|
||||
struct icmp_request* pending; // собственные запросы, ожидающие ответа
|
||||
uint32_t next_token; // генератор wire id/seq со случайным начальным значением
|
||||
uint64_t request_timeout_tb; // период проверки и порог ожидания, default=50000 (5 с)
|
||||
int test_loopback; // fallback при raw_sock==SOCKET_INVALID, только для тестов
|
||||
void* expire_timer; // периодический таймер очистки истёкших запросов
|
||||
};
|
||||
|
||||
|
||||
// Создать контекст и bind ICMP service; повторный init не меняет существующий контекст и даёт 0.
|
||||
// inst/ua живут до destroy. -1 = нет inst/памяти/случайного token, 0 = контекст создан.
|
||||
// 0 не гарантирует raw socket на exit: ошибка socket/регистрации не проваливает init; bind не проверяется.
|
||||
int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua);
|
||||
// Снять binding, отменить таймер, закрыть raw socket, удалить pending и обнулить inst->icmp_proxy; NULL допустим.
|
||||
void icmp_proxy_destroy(struct UTUN_INSTANCE* inst);
|
||||
|
||||
// Единый etcp_router коллбэк
|
||||
// Router callback: conn->instance задаёт владельца; REQUEST требует server.enabled, REPLY — настроенного exit.
|
||||
// Принимает владение entry/dgram при любом исходе, в том числе при отклонении пакета.
|
||||
void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
|
||||
// Сторона клиента: принять IP/ICMP echo request с TUN, отправить через etcp
|
||||
// Скопировать payload (0..65507) в REQUEST к exit в UTUN, force=0/mode=0; не требует локального контекста.
|
||||
// dst_ip — цель ping, orig_src_ip — адрес приложения; payload заимствован, при длине>0 не NULL.
|
||||
// 0 = принято router, <0 = ошибка; это не подтверждение raw send или получения echo reply.
|
||||
int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
|
||||
uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq,
|
||||
const uint8_t* payload, size_t payload_len);
|
||||
|
||||
// Собрать Echo Reply с IPv4 src_ip→dst_ip и исходными id/seq; пересчитать обе checksum.
|
||||
// Требует inst->tcp_proxy_client->tun, payload заимствован (0..65506); 0 = в tun input_queue, -1 = ошибка.
|
||||
int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst,
|
||||
uint32_t dst_ip, uint32_t src_ip, uint16_t echo_id, uint16_t echo_seq,
|
||||
const uint8_t* payload, size_t payload_len);
|
||||
|
||||
// Изменить тестовый fallback существующего контекста; без контекста ничего не делает.
|
||||
// При наличии raw socket реальные echo-запросы имеют приоритет независимо от enabled.
|
||||
void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled);
|
||||
|
||||
|
||||
|
||||
+52
-65
@@ -1,77 +1,64 @@
|
||||
# ICMP Proxy (icmp_proxy)
|
||||
# ICMP proxy
|
||||
|
||||
## 1. Назначение
|
||||
Проксирование ICMP Echo (ping) между клиентским и exit-узлом по протоколу ETCP. Клиент
|
||||
принимает ICMP Echo Request с TUN-интерфейса, упаковывает запрос и отправляет через
|
||||
`etcp_route_send` на exit. Exit-узел через нативный `SOCK_RAW`-сокет отправляет ICMP Echo
|
||||
во внешнюю сеть, сопоставляет ответы по `echo_id`/`echo_seq` и возвращает клиенту. Клиент
|
||||
восстанавливает IP/ICMP Echo Reply и доставляет в TUN.
|
||||
`icmp_proxy.c/h` передаёт IPv4 Echo Request/Reply через `ETCP_RT_ID_ICMP_PROXY`
|
||||
в `TOPO_GROUP_UTUN`. Общий формат router описан в
|
||||
[doc/proxy_protocol.md](../../doc/proxy_protocol.md), контракты API — в
|
||||
[icmp_proxy.h](icmp_proxy.h). Другие типы ICMP модуль не проксирует.
|
||||
|
||||
Один экземпляр контекста на процесс (глобальный `g_icmp_ctx`). Режим определяется флагом
|
||||
`inst->tcp_proxy_server.enabled`. Для тестов предусмотрен loopback-режим без raw-сокета.
|
||||
## Контекст и raw socket
|
||||
|
||||
## 2. Как пользоваться
|
||||
Каждый `UTUN_INSTANCE` владеет отдельным `inst->icmp_proxy`, глобального контекста нет.
|
||||
`init(inst, ua)` задаёт таймаут 50000 tb (5 с), случайное начало `next_token` и
|
||||
сохраняет `server.enabled` в `is_exit`. Для exit открывает один неблокирующий
|
||||
`AF_INET/SOCK_RAW/IPPROTO_ICMP` и регистрирует его чтение в uasync.
|
||||
|
||||
### Инициализация
|
||||
```c
|
||||
icmp_proxy_init(inst, ua); // exit создаёт SOCK_RAW, клиент — нет
|
||||
```
|
||||
При инициализации регистрируется коллбэк `icmp_proxy_recv_cb` в `etcp_router` для сервиса
|
||||
`ETCP_RT_ID_ICMP_PROXY`. На exit создаётся один `SOCK_RAW(IPPROTO_ICMP)` на весь модуль.
|
||||
Повторный init сохраняет существующий контекст и возвращает 0. Ошибка создания
|
||||
контекста или случайного token возвращает -1. Ошибка raw socket/регистрации
|
||||
оставляет контекст без рабочего сокета, но init возвращает 0 и запрашивает router binding;
|
||||
результат bind также не пробрасывается. Поэтому успешный init сам по себе не подтверждает
|
||||
возможность отправки реального ping. Raw socket требует соответствующих прав ОС.
|
||||
|
||||
### Клиент: отправка ping-запроса
|
||||
```c
|
||||
icmp_proxy_send_to_exit(inst, exit_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, len);
|
||||
```
|
||||
Вызывается из обработчика TUN-трафика. Запрос упаковывается и отправляется через `etcp_route_send`.
|
||||
Ответ приходит асинхронно в `icmp_proxy_recv_cb` (REPLY), где вызывается `icmp_proxy_deliver_reply`
|
||||
для сборки IP/ICMP Echo Reply и записи в TUN.
|
||||
`test_loopback` по умолчанию выключен. `icmp_proxy_set_test_loopback(inst, 1)` разрешает
|
||||
виртуальный REPLY **только при отсутствии raw socket**; открытый сокет имеет приоритет,
|
||||
флаг его не отключает. Без контекста setter ничего не делает.
|
||||
|
||||
### Тестовый loopback
|
||||
```c
|
||||
icmp_proxy_set_test_loopback(inst, 1); // exit отвечает эмуляцией без raw-сокета
|
||||
```
|
||||
В этом режиме exit не отправляет реальный ICMP Echo, а сразу возвращает REPLY клиенту — полезно
|
||||
для тестирования прохождения пакетов через ETCP без необходимости в SOCK_RAW и внешней сети.
|
||||
## Запрос и сопоставление ответа
|
||||
|
||||
### Очистка
|
||||
```c
|
||||
icmp_proxy_destroy(inst); // закрывает raw-сокет, удаляет pending-запросы, unbind
|
||||
```
|
||||
REQUEST принимается при текущем `server.enabled`. Exit заменяет исходные echo id/seq
|
||||
32-битным возрастающим token: старшие 16 бит — wire_id, младшие — wire_seq (в сетевом порядке).
|
||||
При совпадении `wire_id/wire_seq/dst_ip` с pending выбирает следующий token.
|
||||
Сопоставление ответа использует эту тройку; исходные id/seq и адрес приложения хранятся
|
||||
в `icmp_request`. Разные клиенты с одинаковыми исходными id/seq не смешиваются.
|
||||
|
||||
### Нюансы
|
||||
- Запросы истекают через 5 секунд (`ICMP_TIMEOUT_TB`). Таймер `expire_timer` запускается
|
||||
при добавлении первого запроса и перезапускается после каждой очистки.
|
||||
- Неопознанные echo-ответы (не найденные в pending) логируются как `DEBUG_WARN` и игнорируются.
|
||||
- Контрольная сумма ICMP пересчитывается на exit (при отправке) и на клиенте (при сборке ответа).
|
||||
- Поле `orig_src_ip` в протоколе нужно для восстановления исходного dst_ip на клиенте.
|
||||
Raw reader проверяет IPv4 IHL/протокол, Echo Reply type/code и ICMP checksum, ищет pending
|
||||
по wire id/seq и адресу отправителя, удаляет найденный запрос и отправляет REPLY ingress-узлу.
|
||||
Данные ответа берутся из raw пакета. Сохранённый в request префикс payload до 1500 байт
|
||||
для формирования ответа не используется; он не ограничивает длину реального echo payload.
|
||||
|
||||
## 3. API
|
||||
Таймер запускается после первого отправленного запроса, затем каждые 5 с, пока pending
|
||||
не пуст. Удаляет записи с возрастом **больше** 5 с на очередном обходе; удаление не происходит
|
||||
точно в момент достижения порога. `destroy` снимает binding, отменяет таймер, закрывает
|
||||
raw socket/регистрацию, освобождает pending и обнуляет `inst->icmp_proxy`.
|
||||
Все действия — в uasync; `inst` и `ua` заимствуются до destroy.
|
||||
|
||||
### Структуры
|
||||
| Структура | Назначение |
|
||||
|-----------|-----------|
|
||||
| `struct icmp_request` | Состояние активного echo-запроса на exit: client_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, время отправки. Связный список. |
|
||||
| `struct icmp_proxy_ctx` | Глобальный контекст: is_exit, inst, ua, raw_sock, raw_read_id, список pending, таймаут запросов, флаг test_loopback, таймер очистки. |
|
||||
## Сообщения и доставка
|
||||
|
||||
### Формат пакета
|
||||
| Смещение | Размер | Поле |
|
||||
|----------|--------|------|
|
||||
| 0 | 1 | `svc_id` = `ETCP_RT_ID_ICMP_PROXY` |
|
||||
| 1 | 1 | `subcmd` = REQUEST (0x01) / REPLY (0x02) |
|
||||
| 2 | 8 | `sender_node_id` |
|
||||
| 10 | 4 | `dst_ip` (IP цели для ping) |
|
||||
| 14 | 4 | `orig_src_ip` (IP исходного отправителя для обратной доставки) |
|
||||
| 18 | 2 | `echo_id` (icmp_id) |
|
||||
| 20 | 2 | `echo_seq` (icmp_seq) |
|
||||
| 22 | N | `payload` (данные ICMP echo) |
|
||||
Вход `etcp_route_send`:
|
||||
`svc:1, cmd:1, dst_ip:4, orig_src_ip:4, echo_id:2, echo_seq:2, payload`.
|
||||
REQUEST=1, REPLY=2, заголовок отправки — 14 байт. В REPLY `dst_ip` остаётся адресом
|
||||
цели ping, `orig_src_ip` — адресом приложения. Router callback получает
|
||||
26-байтовые метаданные перед cmd, итого заголовок приёма — 39 байт.
|
||||
Peer находится в метаданных router; отдельного sender ID в ICMP payload нет.
|
||||
IP/id/seq передаются в network byte order.
|
||||
|
||||
### Функции
|
||||
| Функция | Назначение |
|
||||
|---------|-----------|
|
||||
| `icmp_proxy_init(inst, ua)` | Выделяет контекст, определяет is_exit, на exit создаёт SOCK_RAW и регистрирует его в uasync. Регистрирует коллбэк в etcp_router. |
|
||||
| `icmp_proxy_destroy(inst)` | Закрывает raw-сокет, отменяет таймер, освобождает pending, отвязывает коллбэк, освобождает память. |
|
||||
| `icmp_proxy_recv_cb(conn, entry)` | Единый коллбэк etcp_router. По subcmd направляет REQUEST → `exit_handle_request`, REPLY → `client_handle_reply`. |
|
||||
| `icmp_proxy_send_to_exit(inst, ...)` | Клиент: упаковывает ICMP Echo Request и отправляет через etcp_route_send на exit. |
|
||||
| `icmp_proxy_deliver_reply(inst, ...)` | Клиент: собирает IP/ICMP Echo Reply-пакет и помещает в TUN input_queue. |
|
||||
| `icmp_proxy_set_test_loopback(inst, enabled)` | Включает/выключает тестовый loopback-режим на exit (эмуляция echo-ответа без raw-сокета). |
|
||||
`send_to_exit` копирует payload 0..65507 и возвращает результат router
|
||||
(0 = принято в очередь, <0 = ошибка). Локальный ICMP-контекст для этой отправки не требуется.
|
||||
Используются `force=0, mode=0`, собственного повтора REQUEST/REPLY нет.
|
||||
|
||||
Клиент допускает REPLY только от `tcp_proxy_client->via_node_id`.
|
||||
`deliver_reply(inst, dst_ip, src_ip, ...)` требует клиентский TUN, допускает payload
|
||||
0..65506, собирает IPv4 `src_ip→dst_ip` с Echo Reply, TTL 64 и обеими checksum.
|
||||
Нечётный хвост checksum дополняется нулём без чтения за границей.
|
||||
Результат с префиксом версии 4 ставится в `tun->input_queue`:
|
||||
0 = поставлен в очередь, -1 = ошибка. Payload заимствован на время вызова.
|
||||
`recv_cb` принимает владение входным entry/dgram при любом исходе.
|
||||
+38
-63
@@ -1,73 +1,48 @@
|
||||
# UDP Proxy (udp_proxy)
|
||||
# UDP proxy
|
||||
|
||||
## 1. Назначение
|
||||
Проксирование UDP-датаграмм между клиентским и exit-узлом по протоколу ETCP. Клиентский узел
|
||||
принимает UDP-пакеты с TUN-интерфейса, инкапсулирует их и отправляет через `etcp_route_send`
|
||||
на exit-узел. Exit-узел создаёт нативные UDP-сокеты для каждого потока (5-tuple) и ретранслирует
|
||||
датаграммы во внешнюю сеть. Ответы проходят обратный путь — от exit к клиенту и затем в TUN.
|
||||
`udp_proxy.c/h` передаёт UDP между TUN клиента и обычными UDP-сокетами exit через
|
||||
`ETCP_RT_ID_UDP_PROXY` в `TOPO_GROUP_UTUN`. Общие форматы и ограничения:
|
||||
[doc/proxy_protocol.md](../../doc/proxy_protocol.md). Контракты API и полей — в
|
||||
[udp_proxy.h](udp_proxy.h).
|
||||
|
||||
Один экземпляр контекста на процесс (глобальный `g_udp_ctx`), режим определяется флагом
|
||||
`inst->tcp_proxy_server.enabled`: включён → `is_exit=1`, иначе `is_exit=0`.
|
||||
## Контекст и жизненный цикл
|
||||
|
||||
## 2. Как пользоваться
|
||||
Контекст принадлежит `inst->udp_proxy`, глобального контекста нет. `init(inst, ua)`
|
||||
создаёт его и запрашивает router binding, повторный init возвращает 0, сохраняя контекст.
|
||||
`is_exit` — снимок роли при init, но диспетчер проверяет текущее
|
||||
`inst->tcp_proxy_server.enabled` для REQUEST. REPLY принимается только при наличии
|
||||
`tcp_proxy_client` и совпадении peer с его `via_node_id`. Один instance может совмещать роли.
|
||||
|
||||
### Инициализация
|
||||
```c
|
||||
udp_proxy_init(inst, ua); // exit или клиент — определяется автоматически
|
||||
```
|
||||
При инициализации регистрируется коллбэк `udp_proxy_recv_cb` в `etcp_router` для сервиса
|
||||
`ETCP_RT_ID_UDP_PROXY`.
|
||||
Exit индексирует flows линейным списком по
|
||||
`client_node_id + src_ip:src_port + dst_ip:dst_port`. У каждого flow собственный
|
||||
неблокирующий `AF_INET/SOCK_DGRAM`, привязанный к `INADDR_ANY:0`, и read callback в uasync.
|
||||
Ответ принимается только от исходного `dst_ip:dst_port`; в REPLY адреса и порты меняются местами.
|
||||
REQUEST и принятый ответ обновляют `last_activity_tb`.
|
||||
|
||||
### Клиент: отправка датаграммы в exit
|
||||
```c
|
||||
udp_proxy_send_to_exit(inst, exit_node_id, src_ip, src_port, dst_ip, dst_port, payload, len);
|
||||
```
|
||||
Вызывается из обработчика TUN-трафика. Пакет упаковывается в формат ETCP и отправляется
|
||||
через `etcp_route_send`. Ответ приходит асинхронно в `udp_proxy_recv_cb`, который вызывает
|
||||
`udp_proxy_deliver_reply` для записи IP/UDP-пакета обратно в TUN.
|
||||
Таймер запускается при первом flow и проверяет список каждые 600000 tb (60 с),
|
||||
пока flows остаются. Удаляет flow при неактивности **больше** 60 с; удаление происходит
|
||||
на очередном обходе, а не точно в момент достижения порога. `destroy` снимает binding,
|
||||
отменяет таймер, закрывает сокеты/регистрации и обнуляет `inst->udp_proxy`.
|
||||
Все операции выполняются в uasync; `inst` и `ua` заимствуются до destroy.
|
||||
|
||||
### Exit: приём датаграмм
|
||||
Exit автоматически принимает REQUEST-ы через `etcp_router`. Для каждого нового потока
|
||||
(уникальный `client_node_id + src_ip:port + dst_ip:port`) создаётся нативный UDP-сокет,
|
||||
который регистрируется в `uasync` на чтение. При получении ответа от внешнего хоста
|
||||
ответ упаковывается и отправляется обратно клиенту.
|
||||
## Сообщения и доставка
|
||||
|
||||
### Очистка
|
||||
```c
|
||||
udp_proxy_destroy(inst); // закрывает все flows, отменяет таймер, unbind из etcp_router
|
||||
```
|
||||
На вход `etcp_route_send` передаётся:
|
||||
`svc:1, cmd:1, src_ip:4, src_port:2, dst_ip:4, dst_port:2, payload`.
|
||||
REQUEST=1, REPLY=2. Заголовок отправки — 14 байт. Router callback получает 26 байт метаданных
|
||||
router перед подкомандой, поэтому заголовок приёма — 39 байт. Отдельного `sender_node_id`
|
||||
в UDP payload нет: peer берётся из метаданных router. Адреса и порты — network byte order.
|
||||
|
||||
### Нюансы
|
||||
- Потоки истекают через 60 секунд неактивности (`UDP_FLOW_TIMEOUT_TB`). Таймер `expire_timer`
|
||||
запускается при создании первого потока, перезапускается после каждой очистки.
|
||||
- UDP-сокеты на exit привязываются к `INADDR_ANY:0` (bind), трафик отправляется через `sendto`.
|
||||
- Ответы в TUN на клиенте доставляются через `inst->tcp_proxy_client->tun->input_queue`.
|
||||
`send_to_exit` требует созданный UDP-контекст, копирует заимствованный payload
|
||||
длиной 0..65507 и возвращает результат router: 0 означает принятие в очередь,
|
||||
отрицательное значение — ошибку. Собственного окна/повтора UDP у модуля нет;
|
||||
отправка использует `force=0, mode=0`.
|
||||
|
||||
## 3. API
|
||||
`deliver_reply` требует `inst->tcp_proxy_client->tun`, допускает payload 0..65506,
|
||||
собирает IPv4/UDP с TTL 64, пересчитывает IPv4 checksum и оставляет UDP checksum=0.
|
||||
Пакет получает однобайтовый префикс версии 4 и ставится в `tun->input_queue`.
|
||||
Возврат 0 означает постановку в очередь; -1 — отсутствие TUN, недопустимый размер
|
||||
или ошибку выделения. Входной payload остаётся у вызывающего кода.
|
||||
|
||||
### Структуры
|
||||
| Структура | Назначение |
|
||||
|-----------|-----------|
|
||||
| `struct udp_flow` | Состояние одного UDP-потока на exit: 5-tuple, нативный сокет, uasync read_id, времена создания и последней активности. Связный список. |
|
||||
| `struct udp_proxy_ctx` | Глобальный контекст: флаг is_exit, inst, ua, список flows, таймаут истечения, таймер очистки. |
|
||||
|
||||
### Формат пакета
|
||||
| Смещение | Размер | Поле |
|
||||
|----------|--------|------|
|
||||
| 0 | 1 | `svc_id` = `ETCP_RT_ID_UDP_PROXY` |
|
||||
| 1 | 1 | `subcmd` = `UDP_PROXY_SUBCMD_DATA` (0x01) |
|
||||
| 2 | 8 | `sender_node_id` |
|
||||
| 10 | 4 | `src_ip` (клиент→exit: IP отправителя; exit→клиент: dst_ip) |
|
||||
| 14 | 2 | `src_port` |
|
||||
| 16 | 4 | `dst_ip` (клиент→exit: IP назначения; exit→клиент: src_ip) |
|
||||
| 20 | 2 | `dst_port` |
|
||||
| 22 | N | `payload` (UDP-датаграмма) |
|
||||
|
||||
### Функции
|
||||
| Функция | Назначение |
|
||||
|---------|-----------|
|
||||
| `udp_proxy_init(inst, ua)` | Выделяет контекст, определяет is_exit, регистрирует коллбэк в etcp_router. Возвращает 0 при успехе. |
|
||||
| `udp_proxy_destroy(inst)` | Закрывает все потоки, сокеты, отменяет таймеры, отвязывает коллбэк, освобождает память. |
|
||||
| `udp_proxy_recv_cb(conn, entry)` | Единый коллбэк etcp_router. По subcmd направляет в exit_handle_data (exit) или client_handle_reply (клиент). |
|
||||
| `udp_proxy_send_to_exit(inst, ...)` | Клиент: упаковывает UDP-датаграмму и отправляет через etcp_route_send на exit. |
|
||||
| `udp_proxy_deliver_reply(inst, ...)` | Клиент: собирает IP/UDP-пакет и помещает в TUN input_queue для доставки приложению. |
|
||||
`recv_cb` принимает владение entry/dgram при любом исходе. `init` не пробрасывает
|
||||
ошибку router bind, поэтому 0 не подтверждает работоспособность регистрации.
|
||||
Reference in new issue
Block a user