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