Skip unavailable automatic sockets and retain working transports
This commit is contained in:
1 parent
4f2ad13e39
commit
60af36902c
3 files changed
+49
-51
No files matched your search
@@ -271,6 +271,8 @@ static void auto_socket_post_sockets_changed(struct AUTO_SOCKET* as) {
|
||||
int old_v4 = as->v4_usable, old_v6 = as->v6_usable;
|
||||
as->v4_usable = as_check_usable(as, AF_INET);
|
||||
as->v6_usable = as_check_usable(as, AF_INET6);
|
||||
if ((old_v4 || old_v6) && !as->v4_usable && !as->v6_usable)
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] no usable sockets; waiting for network changes");
|
||||
|
||||
int need_reconnect = 0;
|
||||
if ((!old_v4 && as->v4_usable) || (!old_v6 && as->v6_usable))
|
||||
@@ -354,11 +356,9 @@ static struct ETCP_SOCKET* create_iface_udp_socket(struct AUTO_SOCKET* as, uint3
|
||||
struct ETCP_SOCKET* es = etcp_socket_add(inst, &server);
|
||||
if (!es) {
|
||||
int error = socket_get_error();
|
||||
if (attempt == 0 && saved_port)
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] UDP reuse port failed if=%s fam=%d port=%u — trying random", ifname, family, port);
|
||||
else
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] UDP socket create failed if=%s fam=%d port=%u att=%d err=%d",
|
||||
ifname, family, port, attempt + 1, socket_get_error());
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] UDP socket create failed iface=%s ifidx=%u family=%s port=%u attempt=%d reused=%d err=%d",
|
||||
ifname, ifindex, family == AF_INET6 ? "IPv6" : "IPv4", port, attempt + 1,
|
||||
attempt == 0 && saved_port != 0, error);
|
||||
if (error != AS_ADDR_IN_USE) return NULL;
|
||||
continue;
|
||||
}
|
||||
@@ -367,11 +367,11 @@ static struct ETCP_SOCKET* create_iface_udp_socket(struct AUTO_SOCKET* as, uint3
|
||||
|
||||
{ const struct sockaddr_storage* ia = es->interface_addr.ss_family ? &es->interface_addr : &es->local_addr;
|
||||
if (ia->ss_family == AF_INET && ((struct sockaddr_in*)ia)->sin_addr.s_addr == INADDR_ANY) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] UDP socket %s has INADDR_ANY — removing", es->name);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] UDP socket %s has INADDR_ANY — removing", es->name);
|
||||
ncd_remove_socket_links(inst, es); etcp_socket_remove(es); return NULL;
|
||||
}
|
||||
if (ia->ss_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)ia)->sin6_addr)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] UDP socket %s has :: — removing", es->name);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] UDP socket %s has :: — removing", es->name);
|
||||
ncd_remove_socket_links(inst, es); etcp_socket_remove(es); return NULL;
|
||||
}
|
||||
}
|
||||
@@ -439,24 +439,22 @@ static struct stcp_server* create_iface_tcp_socket(struct AUTO_SOCKET* as, uint3
|
||||
}
|
||||
|
||||
struct ETCP_SOCKET* ts = tcp_socket_add(inst, &server);
|
||||
if (!ts) { DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP tcp_socket_add failed if=%s fam=%d port=%u", ifname, family, port); continue; }
|
||||
if (!ts) { DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP tcp_socket_add failed if=%s fam=%d port=%u", ifname, family, port); return NULL; }
|
||||
|
||||
{ const struct sockaddr_storage* ia = (ts->interface_addr.ss_family) ? &ts->interface_addr : &ts->local_addr;
|
||||
if ((ia->ss_family == AF_INET && ((const struct sockaddr_in*)ia)->sin_addr.s_addr == INADDR_ANY) ||
|
||||
(ia->ss_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&((const struct sockaddr_in6*)ia)->sin6_addr))) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] TCP socket %s has null addr — removing", server.name);
|
||||
tcp_socket_remove(ts); continue;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP socket %s has null addr — removing", server.name);
|
||||
tcp_socket_remove(ts); return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct stcp_server* tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, ts);
|
||||
if (!tsrv) {
|
||||
int error = socket_get_error();
|
||||
if (attempt == 0 && saved_port)
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP reuse port failed if=%s fam=%d port=%u — trying random", ifname, family, port);
|
||||
else
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP socket create failed if=%s fam=%d port=%u att=%d err=%d",
|
||||
ifname, family, port, attempt + 1, socket_get_error());
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP socket create failed iface=%s ifidx=%u family=%s port=%u attempt=%d reused=%d err=%d",
|
||||
ifname, ifindex, family == AF_INET6 ? "IPv6" : "IPv4", port, attempt + 1,
|
||||
attempt == 0 && saved_port != 0, error);
|
||||
tcp_socket_remove(ts);
|
||||
if (error != AS_ADDR_IN_USE) return NULL;
|
||||
continue;
|
||||
@@ -1046,7 +1044,7 @@ static void reconcile_prune_ifa(struct AUTO_SOCKET* as, struct auto_sock_iface*
|
||||
* и оповещает GUI если были изменения. */
|
||||
static int reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname) {
|
||||
if (!ifindex) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] invalid interface index: %s", ifname); return -1; }
|
||||
int has_v4 = 0, has_v6 = 0, changed = 0, result = 0;
|
||||
int has_v4 = 0, has_v6 = 0, changed = 0;
|
||||
uint8_t v4_type = CFG_SERVER_TYPE_NAT, v6_type = CFG_SERVER_TYPE_NAT;
|
||||
if (scan_and_classify_iface(ifindex, ifname, &has_v4, &has_v6, &v4_type, &v6_type) < 0) return -1;
|
||||
if (as->instance->config->global.auto_socket_skip_no_default_route) {
|
||||
@@ -1102,9 +1100,8 @@ static int reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char*
|
||||
else *slot = create_iface_udp_socket(as, ifindex, ifname, family, type);
|
||||
if (*slot) { if (tcp) *tcp_addr = (*slot)->interface_addr; changed = 1; }
|
||||
else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] incomplete endpoint iface=%s family=%d protocol=%s",
|
||||
ifname, family, tcp ? "TCP" : "UDP");
|
||||
result = -1;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] skipping unavailable socket iface=%s ifidx=%u family=%s protocol=%s; retry on next scan",
|
||||
ifname, ifindex, v6 ? "IPv6" : "IPv4", tcp ? "TCP" : "UDP");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1112,7 +1109,7 @@ static int reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char*
|
||||
ifa->v4_type = v4_type; ifa->v6_type = v6_type;
|
||||
reconcile_prune_ifa(as, ifa, ifindex, ifname);
|
||||
if (changed) auto_socket_post_sockets_changed(as);
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ═══════════ Platform monitoring ═══════════ */
|
||||
@@ -1359,35 +1356,32 @@ int auto_socket_init(struct UTUN_INSTANCE* inst) {
|
||||
as->protocols = (uint8_t)inst->config->global.auto_socket_protocols;
|
||||
inst->auto_socket_state = as;
|
||||
|
||||
/* Android-режим: источник сокетов — [server] секции конфига (генерирует Kotlin).
|
||||
* Системный мониторинг и таблица портов в БД не нужны. */
|
||||
/* Android получает адреса из конфига; системный режим — от монитора интерфейсов. */
|
||||
if (as->mode == CFG_AUTO_SOCKETS_ANDROID) {
|
||||
if (auto_socket_reconcile_config(as) < 0) { auto_socket_destroy(inst); return -1; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] initialized (android mode)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (inst->topo_sqlite_db) {
|
||||
int rc = sqlite3_exec(inst->topo_sqlite_db,
|
||||
"CREATE TABLE IF NOT EXISTS auto_socket_ports ("
|
||||
" if_name TEXT NOT NULL,"
|
||||
" family INTEGER NOT NULL,"
|
||||
" protocol INTEGER NOT NULL,"
|
||||
" port INTEGER NOT NULL,"
|
||||
" updated_at INTEGER DEFAULT (unixepoch()),"
|
||||
" UNIQUE(if_name, family, protocol)"
|
||||
");", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] cannot create port table rc=%d: %s", rc, sqlite3_errmsg(inst->topo_sqlite_db));
|
||||
auto_socket_destroy(inst); return -1;
|
||||
} else {
|
||||
if (inst->topo_sqlite_db) {
|
||||
int rc = sqlite3_exec(inst->topo_sqlite_db,
|
||||
"CREATE TABLE IF NOT EXISTS auto_socket_ports ("
|
||||
" if_name TEXT NOT NULL,"
|
||||
" family INTEGER NOT NULL,"
|
||||
" protocol INTEGER NOT NULL,"
|
||||
" port INTEGER NOT NULL,"
|
||||
" updated_at INTEGER DEFAULT (unixepoch()),"
|
||||
" UNIQUE(if_name, family, protocol)"
|
||||
");", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] cannot create port table rc=%d: %s", rc, sqlite3_errmsg(inst->topo_sqlite_db));
|
||||
auto_socket_destroy(inst); return -1;
|
||||
}
|
||||
}
|
||||
if (auto_socket_init_monitor(as) < 0) { auto_socket_destroy(inst); return -1; }
|
||||
if (auto_socket_scan_all(as) < 0) { auto_socket_destroy(inst); return -1; }
|
||||
}
|
||||
|
||||
if (auto_socket_init_monitor(as) < 0) { auto_socket_destroy(inst); return -1; }
|
||||
|
||||
if (auto_socket_scan_all(as) < 0) { auto_socket_destroy(inst); return -1; }
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] initialized protocols=%s",
|
||||
int v4 = as_check_usable(as, AF_INET), v6 = as_check_usable(as, AF_INET6);
|
||||
if (!v4 && !v6) DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] initialized without usable sockets; waiting for network/config changes");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] initialized mode=%s usable_v4=%d usable_v6=%d protocols=%s",
|
||||
as->mode == CFG_AUTO_SOCKETS_ANDROID ? "android" : "system", v4, v6,
|
||||
as->protocols == CFG_AUTO_SOCKET_BOTH ? "tcp+udp" : as->protocols == CFG_AUTO_SOCKET_TCP ? "tcp" : "udp");
|
||||
return 0;
|
||||
}
|
||||
@@ -1477,7 +1471,6 @@ static void remove_config_socket(struct AUTO_SOCKET* as, struct ETCP_SOCKET* soc
|
||||
static int auto_socket_reconcile_config(struct AUTO_SOCKET* as) {
|
||||
struct UTUN_INSTANCE* inst = as->instance;
|
||||
if (!inst || !inst->config) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] missing config for reconcile"); return -1; }
|
||||
int result = 0;
|
||||
|
||||
/* Тип сокета определяем по фактическому адресу (не доверяем type из конфига):
|
||||
* публичный адрес → PUBLIC, приватный/link-local → NAT. */
|
||||
@@ -1527,12 +1520,13 @@ static int auto_socket_reconcile_config(struct AUTO_SOCKET* as) {
|
||||
s->name, s->transport ? "tcp" : "udp", sockaddr_storage_to_str(&s->ip).str);
|
||||
ncd_add_socket_links(inst, created);
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] configured endpoint creation failed name=%s", s->name); result = -1;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] skipping unavailable configured socket name=%s ifidx=%u protocol=%s addr=%s; retry on next reload",
|
||||
s->name, s->netif_index, s->transport ? "TCP" : "UDP", sockaddr_storage_to_str(&s->ip).str);
|
||||
}
|
||||
}
|
||||
|
||||
auto_socket_post_sockets_changed(as);
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void auto_socket_reload_config(struct UTUN_INSTANCE* inst, struct utun_config* new_cfg) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* При auto_sockets=yes init сканирует интерфейсы и включает мониторинг Linux/BSD/Windows;
|
||||
* auto_socket_protocols выбирает TCP, UDP или TCP+UDP (по умолчанию); адреса и сокеты обновляются автоматически.
|
||||
* Порты сохраняются в SQLite для повторного использования.
|
||||
* Недоступные сокеты пропускаются с предупреждением; рабочие сохраняются даже при частичном отказе.
|
||||
* Отсутствующие сокеты повторно создаются при сканировании сети, в Android — при обновлении конфига.
|
||||
* Destroy отключает мониторинг и закрывает созданные системные endpoint; сохранённые порты остаются в БД.
|
||||
* В режиме android сокеты приходят из конфигурации frontend и принадлежат ядру, а не списку системных интерфейсов.
|
||||
* Основной API — uasync-поток. Из другого потока вызывать только on_network_change;
|
||||
@@ -30,7 +32,9 @@ struct AUTO_SOCKET;
|
||||
/* Инициализирует модуль: создаёт таблицу портов в БД, запускает платформенный
|
||||
* мониторинг интерфейсов, сканирует все текущие UP-интерфейсы и создаёт сокеты.
|
||||
* instance/ua обязательны. Если нет конфига или auto_sockets=no — ничего не делает, возвращает 0.
|
||||
* Успех — 0; ошибка — -1 с откатом собственных endpoint/монитора. Повторный активный init — ошибка.
|
||||
* Успех — 0, в том числе если отдельные или все сокеты недоступны; мониторинг/состояние сохраняются.
|
||||
* Ошибка модуля (БД, мониторинг, сканирование, внутреннее состояние) — -1 с откатом собственных endpoint/монитора.
|
||||
* Повторный активный init — ошибка.
|
||||
* Системное полное сканирование ограничено 64 UP-интерфейсами; превышение — ошибка. */
|
||||
int auto_socket_init(struct UTUN_INSTANCE* inst);
|
||||
|
||||
@@ -49,7 +53,7 @@ void auto_socket_on_network_change(struct UTUN_INSTANCE* inst);
|
||||
* конфига. new_cfg — уже распарсенный конфиг; его список servers переносится в
|
||||
* inst->config, остальное освобождается, в том числе при отказе режима/аргументов.
|
||||
* new_cfg должен быть новым отдельным объектом; переданный текущий inst->config отклоняется без free.
|
||||
* Обновление не атомарно: при частичном отказе новый список сохраняется, отсутствующие endpoint логируются.
|
||||
* Обновление не атомарно: недоступные сокеты пропускаются с предупреждением, новый список и рабочие сокеты сохраняются.
|
||||
* Неизменные endpoint, включая port=0, сохраняются; смена параметров bind пересоздаёт endpoint.
|
||||
* Только uasync-поток, вне callbacks сверки. */
|
||||
void auto_socket_reload_config(struct UTUN_INSTANCE* inst, struct utun_config* new_cfg);
|
||||
|
||||
@@ -188,7 +188,7 @@ struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t po
|
||||
ss->tcp_sock = tcp_sock;
|
||||
ss->srv = stcp_server_create(cfg->inst->ua, port, &cfg->inst->my_keys, cfg->inst->my_ed25519_pubkey,
|
||||
cfg->inst, server_accept_cb, ss, NULL, NULL, cfg->listen_family);
|
||||
if (!ss->srv) { u_free(ss); return NULL; }
|
||||
if (!ss->srv) { int error = socket_get_error(); u_free(ss); socket_set_error(error); return NULL; }
|
||||
uint16_t bound_port = stcp_server_get_port(ss->srv);
|
||||
if (!bound_port) { stcp_server_destroy(ss->srv); u_free(ss); return NULL; }
|
||||
if (tcp_sock) {
|
||||
|
||||
Reference in new issue
Block a user