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.
76 lines
4.4 KiB
76 lines
4.4 KiB
/* sock_match.c — классификация сокетов и сопоставление адресов при подключении */ |
|
#include "sock_match.h" |
|
|
|
#include <string.h> |
|
#include "../lib/platform_compat.h" |
|
#include "../lib/debug_config.h" |
|
#include "utun_instance.h" |
|
#include "etcp_connections.h" |
|
#include "config_parser.h" |
|
#include "topo_node.h" |
|
|
|
/* Нормализация NAT-типа: сырые (NAT_TYPE_*) или проверенные (NAT_VERIFIED_*) значения. */ |
|
static int nat_is_eim(uint8_t n) { return n == NAT_TYPE_EIM || n == NAT_VERIFIED_EIM; } |
|
static int nat_is_direct(uint8_t n) { return n == NAT_TYPE_DIRECT || n == NAT_VERIFIED_DIRECT; } |
|
|
|
uint8_t sock_target_class(uint8_t a_type, uint8_t tcfg, uint8_t tnat) { |
|
if (tcfg == CFG_SERVER_TYPE_PUBLIC || tcfg == CFG_SERVER_TYPE_UNKNOWN) return SOCK_TC_PUB; |
|
if (tcfg == CFG_SERVER_TYPE_PRIVATE || tcfg == CFG_SERVER_TYPE_LOCAL) return SOCK_TC_PRIV; |
|
/* CFG_SERVER_TYPE_NAT */ |
|
if (nat_is_eim(tnat) || nat_is_direct(tnat)) |
|
return (a_type == TOPO_ADDR_INTERFACE) ? SOCK_TC_PRIV : SOCK_TC_PUB; /* EIM=direct: NAT-адрес публичный, INTERFACE — приватный */ |
|
return (a_type == TOPO_ADDR_INTERFACE) ? SOCK_TC_PRIV : SOCK_TC_UNREACH; /* strict/unknown: публичный адрес не пробиваем */ |
|
} |
|
|
|
int sock_match(const struct sock_view* v, uint8_t a_type, uint8_t tcfg, uint8_t tnat, uint32_t target_ip4) { |
|
uint8_t tc = sock_target_class(a_type, tcfg, tnat); |
|
if (tc == SOCK_TC_UNREACH) return 0; /* strict/unknown NAT-адрес — напрямую не пробьём */ |
|
if (tc == SOCK_TC_PUB) { |
|
if (!v->has_inet) return 0; /* у нашего сокета нет интернета */ |
|
if (a_type == TOPO_ADDR_NAT && v->is_eim && nat_is_eim(tnat) |
|
&& v->public_ip4 && v->public_ip4 == target_ip4) return 0; /* оба EIM за одним NAT — hairpin */ |
|
return 1; |
|
} |
|
return v->has_priv; /* приватный адрес — только с нашего приватного интерфейса */ |
|
} |
|
|
|
int sock_collect_views(struct UTUN_INSTANCE* inst, struct sock_view* out, int max) { |
|
if (!inst || !out || max <= 0) return 0; |
|
int n = 0; |
|
struct ETCP_SOCKET* s = inst->etcp_sockets; |
|
while (s && n < max) { |
|
if (s->local_addr.ss_family != AF_INET) { s = s->next; continue; } |
|
struct sock_view* v = &out[n++]; |
|
v->sock = s; |
|
v->family = AF_INET; |
|
v->is_tcp = s->is_tcp; |
|
v->local_ip4 = 0; v->public_ip4 = 0; |
|
|
|
/* Локальный адрес (interface, иначе local). */ |
|
{ const struct sockaddr_storage* ia = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr; |
|
if (ia && ia->ss_family == AF_INET) v->local_ip4 = ((const struct sockaddr_in*)ia)->sin_addr.s_addr; } |
|
|
|
/* Публичный адрес (NAT): nat_addr, иначе локальный если он публичный. */ |
|
{ const struct sockaddr_in* na = (const struct sockaddr_in*)&s->nat_addr; |
|
if (s->nat_addr.ss_family == AF_INET && na->sin_addr.s_addr != 0) |
|
v->public_ip4 = na->sin_addr.s_addr; } |
|
|
|
/* Классификация по адресу, а не по type из конфига: |
|
* приватный локальный адрес → можем ходить в LAN (has_priv); |
|
* исходящие считаем доступными (интернет + локалка). */ |
|
int local_public = v->local_ip4 && ip_is_public(v->local_ip4); |
|
v->has_priv = (uint8_t)!local_public; |
|
v->has_inet = 1; |
|
/* NAT-результат (главный критерий если есть) — для hairpin-фильтра. */ |
|
v->is_eim = (uint8_t)nat_is_eim(s->nat_type); |
|
|
|
if (!v->public_ip4 && local_public) v->public_ip4 = v->local_ip4; |
|
v->role = local_public ? SOCK_ROLE_DIRECT : (v->is_eim ? SOCK_ROLE_DIRECT : SOCK_ROLE_NAT); |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "sock_view[%d] sock=%s type=%d nat=%d role=%d inet=%d priv=%d eim=%d tcp=%d local=%08x public=%08x", |
|
n - 1, s->name, s->type, s->nat_type, v->role, v->has_inet, v->has_priv, v->is_eim, v->is_tcp, |
|
v->local_ip4, v->public_ip4); |
|
s = s->next; |
|
} |
|
return n; |
|
}
|
|
|