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
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 */
|
|
|