You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

100 lines
6.8 KiB

/**
* @file sock_match.h
* @brief Классификация сокетов и сопоставление адресов при подключении.
*
* ─── Общая идея ────────────────────────────────────────────────────────────
* Узел имеет несколько сокетов с разной природой сети (публичный IP, NAT,
* скрытый/приватный, без интернета). Чтобы при подключении не создавать
* заведомо мёртвые линки, каждый наш сокет сводится к одной из трёх РОЛЕЙ,
* а каждый адрес пира — к одному из трёх КЛАССОВ. Соединение пробуем только
* для совместимых пар «наш сокет → адрес пира». Для удобства формируем
* список наших сокетов в плоском виде (struct sock_view): роль, локальный IP
* (interface), публичный IP (NAT) и флаги достижимости.
*
* ─── Роли нашего сокета (что он умеет как ИСТОЧНИК) ───────────────────────
* DIRECT — публичный IP / EIM NAT / verified-direct: интернет в обе стороны.
* NAT — только исходящие (strict/unknown NAT или скрытый private):
* интернет есть, но входящих соединений нет.
* LOCAL — без интернета: type=local из конфига, либо любой сокет при
* недоступном NTP (узел оффлайн). Работает только в LAN.
*
* EIM-сокет приравнивается к DIRECT (endpoint-independent mapping пробивается
* снаружи, поэтому его публичный адрес достижим как у public).
*
* ─── Реклама адресов (что узел публикует в nodeinfo) ──────────────────────
* PUBLIC → свой публичный IP (INTERFACE).
* NAT + EIM → приватный IP (INTERFACE) + публичный IP (NAT).
* NAT + STRICT → только приватный IP (INTERFACE), публичный НЕ светим.
* LOCAL → только приватный IP (INTERFACE) — для связи двух клиентов
* в одной локалке.
* PRIVATE → скрыт, не рекламируется вовсе (но usable как источник).
*
* ─── Сопоставление (наш сокет → адрес пира) ───────────────────────────────
* Адрес пира классифицируется: PUB (публичный, из интернета), PRIV
* (приватный/LAN), UNREACH (напрямую недостижим). Правила:
* UNREACH → не пробуем (strict/unknown NAT-адрес);
* PUB → нужен интернет у нашего сокета (role != LOCAL), плюс
* hairpin-фильтр: если оба узла EIM за одним NAT (одинаковый
* публичный IP) — по публичному адресу не ходим, остаётся LAN;
* PRIV → только с нашего сокета, имеющего приватный интерфейс
* (has_priv) — приватный адрес достижим лишь из той же сети.
*/
#ifndef SOCK_MATCH_H
#define SOCK_MATCH_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
struct UTUN_INSTANCE;
struct ETCP_SOCKET;
/* Роли нашего сокета (как источника соединения) */
#define SOCK_ROLE_DIRECT 0 /* публичный / EIM / verified-direct — интернет в обе стороны */
#define SOCK_ROLE_NAT 1 /* только исходящие (strict/unknown NAT, скрытый private) */
#define SOCK_ROLE_LOCAL 2 /* без интернета (type=local или NTP недоступен) */
/* Классы целевого адреса пира */
#define SOCK_TC_PUB 0 /* публичный — достижим из интернета */
#define SOCK_TC_PRIV 1 /* приватный / LAN */
#define SOCK_TC_UNREACH 2 /* напрямую недостижим (strict/unknown NAT) */
/**
* Плоское представление нашего сокета для сопоставления.
* "ip сокета" — local_ip4 (interface), "публичный" — public_ip4 (NAT/прямой),
* "тип" — role + флаги has_inet/has_priv/is_eim.
*
* Флаги вычисляются из КЛАССИФИКАЦИИ АДРЕСА и результата NAT-детекции,
* а не из type сокета в конфиге:
* has_priv — локальный IPv4 приватный/не-маршрутизируемый (можем ходить в LAN);
* has_inet — исходящие считаем доступными в интернет (всегда 1);
* is_eim — результат NAT-детекции EIM (для hairpin-фильтра).
*/
struct sock_view {
struct ETCP_SOCKET* sock;
uint8_t role; /* SOCK_ROLE_* (производное, для логов) */
uint8_t has_inet; /* 1 — интернет доступен */
uint8_t has_priv; /* 1 — есть приватный (LAN) интерфейс */
uint8_t is_eim; /* 1 — NAT + EIM (для hairpin) */
uint8_t family; /* AF_INET / AF_INET6 */
uint8_t is_tcp;
uint32_t local_ip4; /* interface_addr, network order (0 если нет) */
uint32_t public_ip4; /* nat_addr / interface если public, network order (0 если нет) */
};
/* Класс адреса пира по типу адреса (TOPO_ADDR_*), конфиг-типу и NAT-типу сокета. */
uint8_t sock_target_class(uint8_t a_type, uint8_t tcfg, uint8_t tnat);
/* 1 — наш сокет (view) подходит для подключения к целевому адресу (target_ip4, network order). */
int sock_match(const struct sock_view* v, uint8_t a_type, uint8_t tcfg, uint8_t tnat, uint32_t target_ip4);
/* Собирает список наших сокетов в плоском виде (v4). Возвращает число элементов. */
int sock_collect_views(struct UTUN_INSTANCE* inst, struct sock_view* out, int max);
#ifdef __cplusplus
}
#endif
#endif /* SOCK_MATCH_H */