Browse Source

Добавлено поле interface_addr в ETCP_SOCKET и interface_ip/port в BGP_NAT_CHECK_REQ

- platform_compat: get_interface_ip_by_index() для получения IP по ifindex
- ETCP_SOCKET: interface_addr заполняется из конфига/interface/default route
- BGP_NAT_CHECK_REQ: добавлены interface_ip/interface_port (network byte order)
- route_bgp_send_nat_check_req: отправляет interface_addr линка
- route_bgp_handle_nat_check_req: принимает interface данные в sockaddr-заглушку
congestion
Evgeny 5 months ago
parent
commit
b466d14fe4
  1. 28
      lib/platform_compat.c
  2. 4
      lib/platform_compat.h
  3. 219
      nat.txt
  4. 36
      src/etcp_connections.c
  5. 1
      src/etcp_connections.h
  6. 34
      src/route_bgp.c
  7. 2
      src/route_bgp.h

28
lib/platform_compat.c

@ -136,3 +136,31 @@ uint32_t get_default_route_ip(void) {
return get_default_route_ip_linux(); return get_default_route_ip_linux();
#endif #endif
} }
#ifndef _WIN32
uint32_t get_interface_ip_by_index(uint32_t netif_index) {
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs(&ifaddr) == -1) return 0;
uint32_t result = 0;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL) continue;
if (ifa->ifa_name == NULL) continue;
unsigned int idx = if_nametoindex(ifa->ifa_name);
if (idx != netif_index) continue;
if (ifa->ifa_addr->sa_family != AF_INET) continue;
struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
result = addr->sin_addr.s_addr;
break;
}
freeifaddrs(ifaddr);
return result;
}
#else
uint32_t get_interface_ip_by_index(uint32_t netif_index) {
(void)netif_index;
return 0;
}
#endif

4
lib/platform_compat.h

@ -159,5 +159,9 @@ int random_bytes(uint8_t *buffer, size_t len);
// Returns IPv4 address in network byte order, 0 on error // Returns IPv4 address in network byte order, 0 on error
uint32_t get_default_route_ip(void); uint32_t get_default_route_ip(void);
// Get IPv4 address of interface by its index (ifindex)
// Returns IPv4 address in network byte order, 0 on error
uint32_t get_interface_ip_by_index(uint32_t netif_index);
#endif // PLATFORM_COMPAT_H #endif // PLATFORM_COMPAT_H

219
nat.txt

@ -0,0 +1,219 @@
Диаграмма вызовов функций при определении типа NAT (NAT detection)
=====================================================================
=== ИНИЦИАЛИЗАЦИЯ BGP ===
route_bgp_init(instance)
└── etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL)
└── При создании нового ETCP_CONN:
route_bgp_etcp_conn_cbk(conn, arg)
├── etcp_conn_set_up_cbk(conn, route_bgp_on_conn_up, bgp)
└── etcp_conn_set_down_cbk(conn, route_bgp_on_conn_down, bgp)
=== УСТАНОВКА СОЕДИНЕНИЯ (сервер получает INIT_REQUEST) ===
etcp_connections_read_callback_socket(sock, arg)
└── Получает ETCP_INIT_REQUEST
├── etcp_connection_create(instance, name) [если новый peer]
│ └── Вызывает instance->etcp_new_conn_cbk [route_bgp_etcp_conn_cbk]
├── etcp_link_new(etcp, e_sock, remote_addr, is_server=1)
├── Отправляет ETCP_INIT_RESPONSE (с NAT info)
├── link->initialized = 1
└── etcp_conn_ready(link->etcp)
└── conn->ready_cbk
└── etcp_on_up(etcp)
└── etcp->up_cbk = route_bgp_on_conn_up(etcp, bgp)
=== УСТАНОВКА СОЕДИНЕНИЯ (клиент получает INIT_RESPONSE) ===
etcp_connections_read_callback_socket(sock, arg)
└── Получает ETCP_INIT_RESPONSE
├── Сохраняет NAT address: link->nat_ip, link->nat_port
├── link->initialized = 1
└── etcp_conn_ready(link->etcp)
└── etcp_on_up(etcp)
└── etcp->up_cbk = route_bgp_on_conn_up(etcp, bgp)
=== ЗАПУСК ПРОВЕРКИ NAT (при поднятии соединения) ===
route_bgp_on_conn_up(etcp, arg)
└── route_bgp_new_conn(conn)
├── route_bgp_add_to_senders(bgp, conn)
└── Сканирует ВСЕ линки ВСЕХ соединений:
if (link->initialized && link->conn && link->nat_check_status < NAT_CHECK_IN_PROGRESS)
└── route_bgp_start_link_nat_check(bgp, link)
├── if (nat_check_status == IN_PROGRESS) return
├── route_bgp_find_third_node(bgp, exclude=link->etcp)
│ └── Ищет другой ETCP_CONN в senders_list
├── Определяет target_ip/target_port:
│ ├── Если link->nat_ip != 0: использует nat_ip/nat_port
│ └── Иначе: использует link->remote_addr
├── Проверяет is_local_subnet(target_ip) [если запрещено - skip]
├── route_bgp_extract_nat_addr(conn, &nat_ip, &nat_port)
│ └── Ищет первый линк с nat_ip != 0
├── Создает nat_check_arg { link, nat_ip, nat_port }
└── route_ping_send_req_addr(bgp, third_conn, target_ip, target_port,
count=3, interval=500ms, timeout=1000ms,
wait_timeout=5000ms,
cb=nat_link_check_cb, arg=nat_check_arg,
pubkey=peer_pubkey)
=== ОТПРАВКА ЗАПРОСА НА ПИНГ (через third node) ===
route_ping_send_req_addr(bgp, to_conn, target_ip, target_port, ...)
├── Создает BGP_PING_REQUEST { cmd, subcmd=PING_REQ, request_id, count, interval, timeout, target_ipv4, target_port, pubkey }
├── etcp_send(to_conn, entry) [отправляет запрос third node]
├── Создает route_ping_pending { request_id, callback=nat_link_check_cb, arg, timeout_timer }
└── Добавляет в bgp->ping_pending
=== ОБРАБОТКА PING_REQ (на third node) ===
route_bgp_receive_cbk(data, len, from_conn, bgp)
└── ROUTE_SUBCMD_PING_REQ
└── route_ping_handle_req(bgp, from_conn, data, len)
├── Создает route_ping_series_ctx { reply_conn, request_id, target_addr, pubkey, local_sock, count_total }
├── Находит первый IPv4 ETCP_SOCKET -> local_sock
└── etcp_send_ping_to_socket(instance, local_sock, pubkey, &target_addr,
timeout_ms, route_ping_single_cb, ctx, NULL, 0)
└── Отправляет ETCP пинг на target_addr
=== ОТВЕТ НА ПИНГ (целевой узел) ===
etcp_connections_read_callback_socket()
└── Получает ETCP пинг -> отправляет pong
=== ПОЛУЧЕНИЕ ОТВЕТА НА ПИНГ (third node) ===
route_ping_single_cb(success, rtt, arg, nonce, resp_data, resp_data_len)
├── Обновляет статистику: count_sent++, count_ok++, sum_rtt += rtt
├── Если count_sent < count_total:
│ └── etcp_send_ping_to_socket(...) [следующий пинг]
└── Иначе:
└── route_ping_series_finish(ctx)
├── Вычисляет avg_rtt
└── Создает BGP_PING_RESPONSE { cmd, subcmd=PING_RESP, request_id, count_sent, count_ok, avg_rtt }
└── etcp_send(reply_conn, entry) [отправляет результат инициатору]
=== ПОЛУЧЕНИЕ PING_RESP (на инициаторе) ===
route_bgp_receive_cbk(data, len, from_conn, bgp)
└── ROUTE_SUBCMD_PING_RESP
└── route_ping_handle_resp(bgp, from_conn, data, len)
├── Ищет route_ping_pending по request_id
├── Отменяет timeout_timer
└── Вызывает callback: nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg)
=== CALLBACK ПРОВЕРКИ NAT ===
nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg)
├── Определяет nat_type = success ? NAT_TYPE_OPEN : NAT_TYPE_RESTRICTED
├── link->nat_type = nat_type
├── link->nat_check_status = success ? NAT_CHECK_OPEN : NAT_CHECK_RESTRICTED
└── route_bgp_send_nat_info(link->etcp, socket_id, nat_ip, nat_port, nat_type)
├── Создает BGP_NAT_INFO { cmd, subcmd=NAT_INFO, socket_id, nat_ip[4], nat_port, nat_type }
└── etcp_send(conn, entry) [отправляет результат клиенту]
=== ПРИЕМ NAT_INFO (на клиенте) ===
route_bgp_receive_cbk(data, len, from_conn, bgp)
└── ROUTE_SUBCMD_NAT_INFO
└── route_bgp_handle_nat_info(bgp, from_conn, data, len)
├── Ищет линки по remote_socket_id
├── Устанавливает link->nat_type = info->nat_type
└── Логирует результат NAT detection
=== ЗАПРОС НА ПРОВЕРКУ NAT (NAT_CHECK_REQ - новый механизм) ===
route_bgp_send_nat_check_req(conn, socket_id)
├── Создает BGP_NAT_CHECK_REQ { cmd, subcmd=NAT_CHECK_REQ, socket_id, interface_ip, interface_port }
│ ├── Ищет линк с remote_socket_id == socket_id
│ └── Берет interface_addr из линка -> interface_ip/interface_port
└── etcp_send(conn, entry)
route_bgp_handle_nat_check_req(bgp, from_conn, data, len)
├── Принимает BGP_NAT_CHECK_REQ
├── Сохраняет interface_ip/interface_port в struct sockaddr_storage dummy
├── Ищет линк с remote_socket_id == req->socket_id
└── Если линк найден и не в процессе проверки:
└── route_bgp_start_link_nat_check(bgp, target_link)
[далее по стандартному flow]
=== СВЯЗЬ СТРУКТУР ===
struct ETCP_SOCKET
├── local_addr [адрес бинда из конфига]
├── interface_addr [IP интерфейса + порт: интерфейс/default route/конфиг]
└── ...
struct ETCP_LINK
├── remote_addr [адрес удаленного узла]
├── nat_ip [NAT IP, полученный из INIT_RESPONSE]
├── nat_port [NAT port, полученный из INIT_RESPONSE]
├── nat_type [OPEN/RESTRICTED, результат проверки]
├── nat_check_status [NONE/WAITING/IN_PROGRESS/OPEN/RESTRICTED]
├── initialized [0/1, флаг завершения handshake]
└── ...
struct ETCP_CONN
├── links [список ETCP_LINK]
├── up_cbk [route_bgp_on_conn_up]
├── down_cbk [route_bgp_on_conn_down]
├── ready_cbk [вызывается при etcp_conn_ready]
├── initialized [0/1, флаг готовности]
├── links_up [0/1, есть ли активные линки]
└── ...
=== СТАТУСЫ NAT CHECK ===
NAT_CHECK_NONE = 0 [не проверялся]
NAT_CHECK_WAITING = 1 [ожидает]
NAT_CHECK_IN_PROGRESS = 2 [проверка запущена]
NAT_CHECK_OPEN = 3 [результат: OPEN]
NAT_CHECK_RESTRICTED = 4 [результат: RESTRICTED]
NAT_TYPE_UNKNOWN = 0
NAT_TYPE_OPEN = 1
NAT_TYPE_RESTRICTED = 2
NAT_VERIFIED_UNKNOWN = 4
NAT_VERIFIED_OPEN = 5
NAT_VERIFIED_RESTRICTED = 6
NAT_VERIFIED_DIRECT = 7
=== СООБЩЕНИЯ ПРОТОКОЛА ===
ETCP_INIT_REQUEST (0x02) [клиент -> сервер]
ETCP_INIT_RESPONSE (0x03) [сервер -> клиент, включает NAT info]
ETCP_INIT_REQUEST_NOINIT (0x04)
ETCP_INIT_RESPONSE_NOINIT (0x05)
ETCP_PING (0x06)
ETCP_PONG (0x07)
ROUTE_SUBCMD_NODEINFO (0x04)
ROUTE_SUBCMD_REQUEST_TABLE (0x05)
ROUTE_SUBCMD_WITHDRAW (0x06)
ROUTE_SUBCMD_PING_REQ (0x07)
ROUTE_SUBCMD_PING_RESP (0x08)
ROUTE_SUBCMD_NAT_INFO (0x09)
ROUTE_SUBCMD_NAT_CHECK_REQ (0x0A)
=== ПОЛНЫЙ FLOW (одна строка) ===
Handshake: etcp_connections_read_callback_socket -> INIT_REQUEST/RESPONSE -> link->initialized=1 -> etcp_conn_ready -> etcp_on_up -> route_bgp_on_conn_up -> route_bgp_new_conn -> route_bgp_start_link_nat_check -> route_ping_send_req_addr -> (через ETCP) -> route_ping_handle_req -> etcp_send_ping_to_socket -> (UDP) -> route_ping_single_cb -> route_ping_series_finish -> route_ping_handle_resp -> nat_link_check_cb -> route_bgp_send_nat_info -> route_bgp_handle_nat_info
NAT_CHECK_REQ flow: route_bgp_send_nat_check_req -> BGP_NAT_CHECK_REQ -> route_bgp_handle_nat_check_req -> route_bgp_start_link_nat_check -> [стандартный ping flow]

36
src/etcp_connections.c

@ -550,6 +550,42 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Listen socket initialized: name=%s fd=%d addr=%s:%d", e_sock->name, e_sock->fd, ip_to_str(&sin6->sin6_addr, AF_INET6).str, ntohs(sin6->sin6_port)); DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Listen socket initialized: name=%s fd=%d addr=%s:%d", e_sock->name, e_sock->fd, ip_to_str(&sin6->sin6_addr, AF_INET6).str, ntohs(sin6->sin6_port));
} }
} }
// Определяем interface_addr: IP интерфейса или из конфига
memset(&e_sock->interface_addr, 0, sizeof(e_sock->interface_addr));
if (netif_index > 0) {
uint32_t if_ip = get_interface_ip_by_index(netif_index);
if (if_ip != 0) {
struct sockaddr_in* sin = (struct sockaddr_in*)&e_sock->interface_addr;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = if_ip;
}
} else if (ip && ip->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ip;
if (sin->sin_addr.s_addr == 0) {
uint32_t def_ip = get_default_route_ip();
if (def_ip != 0) {
struct sockaddr_in* isin = (struct sockaddr_in*)&e_sock->interface_addr;
isin->sin_family = AF_INET;
isin->sin_addr.s_addr = def_ip;
}
}
}
if (((struct sockaddr_in*)&e_sock->interface_addr)->sin_addr.s_addr == 0) {
if (ip) memcpy(&e_sock->interface_addr, ip, sizeof(struct sockaddr_storage));
} else {
// скопируем порт из конфига в interface_addr
if (ip && ip->ss_family == AF_INET) {
struct sockaddr_in* sin_cfg = (struct sockaddr_in*)ip;
struct sockaddr_in* sin_if = (struct sockaddr_in*)&e_sock->interface_addr;
sin_if->sin_port = sin_cfg->sin_port;
}
}
{
struct sockaddr_in* isin = (struct sockaddr_in*)&e_sock->interface_addr;
if (isin->sin_addr.s_addr != 0) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] interface_addr for %s: %s:%d", e_sock->name, ip_to_str(&isin->sin_addr, AF_INET).str, ntohs(isin->sin_port));
}
}
e_sock->instance = instance; e_sock->instance = instance;
e_sock->errorcode = 0; e_sock->errorcode = 0;
e_sock->pkt_format_errors = 0; e_sock->pkt_format_errors = 0;

1
src/etcp_connections.h

@ -73,6 +73,7 @@ struct ETCP_SOCKET {
uint32_t local_defaultroute_ip; // auto-detected IPv4 for public servers (network byte order) uint32_t local_defaultroute_ip; // auto-detected IPv4 for public servers (network byte order)
uint8_t local_defaultroute_ip6[16]; // auto-detected IPv6 for public servers uint8_t local_defaultroute_ip6[16]; // auto-detected IPv6 for public servers
uint8_t only_local; // 1 = only local connections, no forwarding uint8_t only_local; // 1 = only local connections, no forwarding
struct sockaddr_storage interface_addr; // адрес интерфейса: если bind на интерфейс - его IP, если 0.0.0.0 без интерфейса - IP интерфейса через который идет default route, иначе - адрес из конфига
}; };
// NAT check status // NAT check status

34
src/route_bgp.c

@ -995,6 +995,14 @@ static void route_bgp_handle_nat_check_req(struct ROUTE_BGP* bgp, struct ETCP_CO
const struct BGP_NAT_CHECK_REQ* req = (const struct BGP_NAT_CHECK_REQ*)data; const struct BGP_NAT_CHECK_REQ* req = (const struct BGP_NAT_CHECK_REQ*)data;
uint8_t socket_id = req->socket_id; uint8_t socket_id = req->socket_id;
struct sockaddr_storage dummy;
memset(&dummy, 0, sizeof(dummy));
if (req->interface_ip != 0) {
struct sockaddr_in* sin = (struct sockaddr_in*)&dummy;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = req->interface_ip;
sin->sin_port = req->interface_port;
}
struct ETCP_LINK* target_link = NULL; struct ETCP_LINK* target_link = NULL;
struct ETCP_LINK* l = from_conn->links; struct ETCP_LINK* l = from_conn->links;
@ -1007,8 +1015,11 @@ static void route_bgp_handle_nat_check_req(struct ROUTE_BGP* bgp, struct ETCP_CO
} }
if (!target_link) { if (!target_link) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: no link with remote_socket_id=%u", struct sockaddr_in* sin = (struct sockaddr_in*)&dummy;
from_conn->log_name, socket_id); DEBUG_WARN(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: no link with remote_socket_id=%u interface=%s:%u",
from_conn->log_name, socket_id,
sin->sin_addr.s_addr ? ip_to_str(&sin->sin_addr, AF_INET).str : "none",
(unsigned)ntohs(sin->sin_port));
} else if (target_link->nat_check_status == NAT_CHECK_IN_PROGRESS) { } else if (target_link->nat_check_status == NAT_CHECK_IN_PROGRESS) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: check already in progress for socket_id=%u", DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NAT_CHECK_REQ from %s: check already in progress for socket_id=%u",
from_conn->log_name, socket_id); from_conn->log_name, socket_id);
@ -1060,6 +1071,19 @@ void route_bgp_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id) {
pkt->subcmd = ROUTE_SUBCMD_NAT_CHECK_REQ; pkt->subcmd = ROUTE_SUBCMD_NAT_CHECK_REQ;
pkt->socket_id = socket_id; pkt->socket_id = socket_id;
struct ETCP_LINK* l = conn->links;
while (l) {
if (l->remote_socket_id == socket_id) {
struct sockaddr_in* sin = (struct sockaddr_in*)&l->conn->interface_addr;
if (sin->sin_family == AF_INET) {
pkt->interface_ip = sin->sin_addr.s_addr;
pkt->interface_port = sin->sin_port;
}
break;
}
l = l->next;
}
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
if (!e) { if (!e) {
u_free(pkt); u_free(pkt);
@ -1068,6 +1092,8 @@ void route_bgp_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id) {
e->dgram = (uint8_t*)pkt; e->dgram = (uint8_t*)pkt;
e->len = sizeof(struct BGP_NAT_CHECK_REQ); e->len = sizeof(struct BGP_NAT_CHECK_REQ);
etcp_send(conn, e); etcp_send(conn, e);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_send_nat_check_req to %s: socket_id=%u", DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_send_nat_check_req to %s: socket_id=%u interface=%s:%u",
conn->log_name, socket_id); conn->log_name, socket_id,
ip_to_str(&pkt->interface_ip, AF_INET).str,
(unsigned)ntohs(pkt->interface_port));
} }

2
src/route_bgp.h

@ -73,6 +73,8 @@ struct BGP_NAT_CHECK_REQ {
uint8_t cmd; uint8_t cmd;
uint8_t subcmd; // ROUTE_SUBCMD_NAT_CHECK_REQ uint8_t subcmd; // ROUTE_SUBCMD_NAT_CHECK_REQ
uint8_t socket_id; // ID сокета клиента, который надо проверить uint8_t socket_id; // ID сокета клиента, который надо проверить
uint32_t interface_ip; // interface_addr IP клиента (network byte order)
uint16_t interface_port; // interface_addr port клиента (network byte order)
} __attribute__((packed)); } __attribute__((packed));
struct route_ping_pending; struct route_ping_pending;

Loading…
Cancel
Save