Browse Source

auto_socket: пропускать интерфейсы без default-маршрута (опция auto_socket_skip_no_default_route, default=on)

v2
evgeny 1 month ago
parent
commit
5d39083a6d
  1. 6
      src/config_parser.c
  2. 1
      src/config_parser.h
  3. 160
      src/transport_layer/auto_socket.c

6
src/config_parser.c

@ -410,6 +410,10 @@ static int parse_global(const char *key, const char *value, struct global_config
global->auto_sockets = strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0; global->auto_sockets = strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0;
return 0; return 0;
} }
if (strcmp(key, "auto_socket_skip_no_default_route") == 0) {
global->auto_socket_skip_no_default_route = strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0;
return 0;
}
if (strcmp(key, "debug_level") == 0) { if (strcmp(key, "debug_level") == 0) {
return assign_string(global->debug_level, sizeof(global->debug_level), value); return assign_string(global->debug_level, sizeof(global->debug_level), value);
} }
@ -864,6 +868,7 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
cfg->global.keepalive_adaptive = 1; // Default: adaptive enabled cfg->global.keepalive_adaptive = 1; // Default: adaptive enabled
cfg->global.bbr_max_cwnd = 1048576; // Default 1MB (was INFLIGHT_LIM_MAX) cfg->global.bbr_max_cwnd = 1048576; // Default 1MB (was INFLIGHT_LIM_MAX)
cfg->global.auto_sockets = 0; // Default: disabled, use manual [server] sockets cfg->global.auto_sockets = 0; // Default: disabled, use manual [server] sockets
cfg->global.auto_socket_skip_no_default_route = 1; // Default: в авто-режиме пропускать интерфейсы без default-маршрута
cfg->global.tcp_recv_buf = 0; // 0 = OS default cfg->global.tcp_recv_buf = 0; // 0 = OS default
cfg->global.firewall_rules = NULL; cfg->global.firewall_rules = NULL;
cfg->global.firewall_rule_count = 0; cfg->global.firewall_rule_count = 0;
@ -1161,6 +1166,7 @@ struct utun_config* parse_config_from_buf(const char *buf, size_t len, const cha
cfg->global.keepalive_adaptive = 1; cfg->global.keepalive_adaptive = 1;
cfg->global.bbr_max_cwnd = 1048576; cfg->global.bbr_max_cwnd = 1048576;
cfg->global.auto_sockets = 0; cfg->global.auto_sockets = 0;
cfg->global.auto_socket_skip_no_default_route = 1;
cfg->global.tcp_recv_buf = 0; cfg->global.tcp_recv_buf = 0;
cfg->global.firewall_rules = NULL; cfg->global.firewall_rules = NULL;
cfg->global.firewall_rule_count = 0; cfg->global.firewall_rule_count = 0;

1
src/config_parser.h

@ -190,6 +190,7 @@ struct global_config {
// TCP proxy server (exit node) configuration ([tcp_proxy_server] section) // TCP proxy server (exit node) configuration ([tcp_proxy_server] section)
int tcp_proxy_server_enabled; int tcp_proxy_server_enabled;
int auto_sockets; // 1 = auto-create sockets from interfaces, skip [server] int auto_sockets; // 1 = auto-create sockets from interfaces, skip [server]
int auto_socket_skip_no_default_route; // 1 = в авто-режиме не создавать сокеты на интерфейсах без default-маршрута
int tcp_recv_buf; // SO_RCVBUF для TCP сокетов (0=default ОС) int tcp_recv_buf; // SO_RCVBUF для TCP сокетов (0=default ОС)
int tcp_proxy_server_fib; // FreeBSD SO_SETFIB, 0=не менять int tcp_proxy_server_fib; // FreeBSD SO_SETFIB, 0=не менять
int tcp_proxy_server_so_mark; // Linux SO_MARK, 0=не менять int tcp_proxy_server_so_mark; // Linux SO_MARK, 0=не менять

160
src/transport_layer/auto_socket.c

@ -74,6 +74,7 @@
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <net/route.h> #include <net/route.h>
#include <net/if_dl.h> #include <net/if_dl.h>
#include <sys/sysctl.h>
#include <fcntl.h> #include <fcntl.h>
#elif defined(_WIN32) #elif defined(_WIN32)
#include <iphlpapi.h> #include <iphlpapi.h>
@ -787,6 +788,142 @@ static void auto_socket_scan_all(struct AUTO_SOCKET* as) {
#endif /* _WIN32 */ #endif /* _WIN32 */
/* ═══════════ default-маршрут: проверка возможности выхода в интернет ═══════════
*
* Фильтр для авто-режима: интерфейс без default-маршрута (шлюза) не может
* достучаться до публичных пиров и даёт только sendto ENETUNREACH (например
* VirtualBox host-only, изолированные VPN). Такие интерфейсы пропускаем.
* Возвращают 1 если default-маршрут есть, 0 если нет. При невозможности
* определить — 1 (не пропускаем, чтобы не сломать существующие сетапы). */
#if defined(__linux__)
static int iface_has_default_v4(uint32_t ifindex, const char* ifname) {
(void)ifindex;
FILE* f = fopen("/proc/net/route", "r");
if (!f) return 1;
char line[512];
int found = 0;
if (fgets(line, sizeof(line), f)) {
while (fgets(line, sizeof(line), f)) {
char iface[IFNAMSIZ];
unsigned long dest;
if (sscanf(line, "%15s %lx %*x %*x", iface, &dest) == 2) {
if (dest == 0 && strcmp(iface, ifname) == 0) { found = 1; break; }
}
}
}
fclose(f);
return found;
}
static int iface_has_default_v6(uint32_t ifindex, const char* ifname) {
(void)ifindex;
FILE* f = fopen("/proc/net/ipv6_route", "r");
if (!f) return 1;
char line[512];
int found = 0;
while (fgets(line, sizeof(line), f)) {
char dest[33], iface[IFNAMSIZ];
unsigned int prefixlen;
if (sscanf(line, "%32s %2x %*32s %*2x %*32s %*8x %*8x %*8x %*8x %15s", dest, &prefixlen, iface) == 3) {
if (prefixlen == 0 && strcmp(iface, ifname) == 0) { found = 1; break; }
}
}
fclose(f);
return found;
}
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
/* Вытаскивает sockaddr по индексу RTAX_* из rt_msghdr (вместо SA_SIZE — универсально). */
static struct sockaddr* rta_get(struct rt_msghdr* rtm, int rtax) {
if (!(rtm->rtm_addrs & (1 << rtax))) return NULL;
char* p = (char*)(rtm + 1);
for (int i = 0; i < rtax; i++) {
if (rtm->rtm_addrs & (1 << i)) {
struct sockaddr* sa = (struct sockaddr*)p;
p += 1 + ((((size_t)sa->sa_len - 1) | (sizeof(uint32_t) - 1)));
}
}
return (struct sockaddr*)p;
}
static int freebsd_iface_has_default(uint32_t ifindex, int family) {
int mib[6] = { CTL_NET, PF_ROUTE, 0, family, NET_RT_DUMP, 0 };
size_t need = 0;
if (sysctl(mib, 6, NULL, &need, NULL, 0) < 0 || need == 0) return 1;
char* buf = u_malloc(need);
if (!buf) return 1;
if (sysctl(mib, 6, buf, &need, NULL, 0) < 0) { u_free(buf); return 1; }
int found = 0;
char* p = buf;
char* end = buf + need;
while (p + sizeof(struct rt_msghdr) <= end) {
struct rt_msghdr* rtm = (struct rt_msghdr*)p;
if (rtm->rtm_msglen < sizeof(struct rt_msghdr) || rtm->rtm_msglen > (size_t)(end - p)) break;
struct sockaddr* mask = rta_get(rtm, RTAX_NETMASK);
struct sockaddr_dl* ifp = (struct sockaddr_dl*)rta_get(rtm, RTAX_IFP);
int is_default = 0;
if (mask && ifp && ifp->sdl_family == AF_LINK) {
if (family == AF_INET && mask->sa_family == AF_INET)
is_default = (((struct sockaddr_in*)mask)->sin_addr.s_addr == 0);
else if (family == AF_INET6 && mask->sa_family == AF_INET6)
is_default = IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)mask)->sin6_addr);
}
if (is_default && ifp->sdl_index == ifindex) { found = 1; break; }
p += rtm->rtm_msglen;
}
u_free(buf);
return found;
}
static int iface_has_default_v4(uint32_t ifindex, const char* ifname) {
(void)ifname;
return freebsd_iface_has_default(ifindex, AF_INET);
}
static int iface_has_default_v6(uint32_t ifindex, const char* ifname) {
(void)ifname;
return freebsd_iface_has_default(ifindex, AF_INET6);
}
#elif defined(_WIN32)
static int iface_has_default_v4(uint32_t ifindex, const char* ifname) {
(void)ifname;
MIB_IPFORWARD_TABLE2* table = NULL;
if (GetIpForwardTable2(AF_INET, &table) != NO_ERROR || !table) return 1;
int found = 0;
for (ULONG i = 0; i < table->NumEntries; i++) {
if (table->Table[i].InterfaceIndex == ifindex &&
table->Table[i].DestinationPrefix.PrefixLength == 0) { found = 1; break; }
}
FreeMibTable(table);
return found;
}
static int iface_has_default_v6(uint32_t ifindex, const char* ifname) {
(void)ifname;
MIB_IPFORWARD_TABLE2* table = NULL;
if (GetIpForwardTable2(AF_INET6, &table) != NO_ERROR || !table) return 1;
int found = 0;
for (ULONG i = 0; i < table->NumEntries; i++) {
if (table->Table[i].InterfaceIndex == ifindex &&
table->Table[i].DestinationPrefix.PrefixLength == 0) { found = 1; break; }
}
FreeMibTable(table);
return found;
}
#else
static int iface_has_default_v4(uint32_t ifindex, const char* ifname) { (void)ifindex; (void)ifname; return 1; }
static int iface_has_default_v6(uint32_t ifindex, const char* ifname) { (void)ifindex; (void)ifname; return 1; }
#endif
/* ═══════════ reconcile: сверка и исправление состояния сокетов ═══════════ /* ═══════════ reconcile: сверка и исправление состояния сокетов ═══════════
* *
* reconcile — ключевой механизм модуля. При любом изменении сети сравнивает * reconcile — ключевой механизм модуля. При любом изменении сети сравнивает
@ -841,6 +978,19 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
uint8_t v4_need_type = CFG_SERVER_TYPE_NAT, v6_need_type = CFG_SERVER_TYPE_NAT; uint8_t v4_need_type = CFG_SERVER_TYPE_NAT, v6_need_type = CFG_SERVER_TYPE_NAT;
if (scan_and_classify_iface(ifindex, ifname, &has_v4, &has_v6, &v4_need_type, &v6_need_type) < 0) return; if (scan_and_classify_iface(ifindex, ifname, &has_v4, &has_v6, &v4_need_type, &v6_need_type) < 0) return;
/* Фильтр авто-режима: интерфейс без default-маршрута не может достать до
* публичных пиров — не создаём на нём сокеты (иначе только sendto ENETUNREACH). */
if (as->instance->config->global.auto_socket_skip_no_default_route) {
if (has_v4 && !iface_has_default_v4(ifindex, ifname)) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] %s has no default v4 route — skip v4", ifname);
has_v4 = 0;
}
if (has_v6 && !iface_has_default_v6(ifindex, ifname)) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] %s has no default v6 route — skip v6", ifname);
has_v6 = 0;
}
}
struct auto_sock_iface* ifa = as->ifaces; struct auto_sock_iface* ifa = as->ifaces;
while (ifa && ifa->netif_index != ifindex) ifa = ifa->next; while (ifa && ifa->netif_index != ifindex) ifa = ifa->next;
@ -1020,6 +1170,12 @@ static void handle_nl_link(struct AUTO_SOCKET* as, struct ifinfomsg* ifi, int ms
} }
} }
/* При изменении маршрута (RTM_NEWROUTE / RTM_DELROUTE): default-маршрут
* (dst_len==0) мог появиться/исчезнуть на интерфейсе — пересканируем все. */
static void handle_nl_route(struct AUTO_SOCKET* as, struct rtmsg* rtm) {
if (rtm->rtm_dst_len == 0) auto_socket_scan_all(as);
}
/* Коллбэк uasync для netlink-сокета. Принимает все сообщения netlink, /* Коллбэк uasync для netlink-сокета. Принимает все сообщения netlink,
* разбирает их и направляет в handle_nl_addr / handle_nl_link. */ * разбирает их и направляет в handle_nl_addr / handle_nl_link. */
static void auto_socket_netlink_cb(int fd, void* arg) { static void auto_socket_netlink_cb(int fd, void* arg) {
@ -1040,6 +1196,8 @@ static void auto_socket_netlink_cb(int fd, void* arg) {
handle_nl_addr(as, (struct ifaddrmsg*)NLMSG_DATA(nlh), nlh->nlmsg_type); handle_nl_addr(as, (struct ifaddrmsg*)NLMSG_DATA(nlh), nlh->nlmsg_type);
else if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK) else if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK)
handle_nl_link(as, (struct ifinfomsg*)NLMSG_DATA(nlh), nlh->nlmsg_type); handle_nl_link(as, (struct ifinfomsg*)NLMSG_DATA(nlh), nlh->nlmsg_type);
else if (nlh->nlmsg_type == RTM_NEWROUTE || nlh->nlmsg_type == RTM_DELROUTE)
handle_nl_route(as, (struct rtmsg*)NLMSG_DATA(nlh));
if (nlh->nlmsg_type == NLMSG_DONE) break; if (nlh->nlmsg_type == NLMSG_DONE) break;
nlh = NLMSG_NEXT(nlh, remaining); nlh = NLMSG_NEXT(nlh, remaining);
} }
@ -1056,7 +1214,7 @@ static int auto_socket_init_monitor(struct AUTO_SOCKET* as) {
struct sockaddr_nl nladdr; struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr)); memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK; nladdr.nl_family = AF_NETLINK;
nladdr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; nladdr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
if (bind(as->nl_sock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) { if (bind(as->nl_sock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] netlink bind: %s — relying on JNI/scan_all for updates", strerror(errno)); DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] netlink bind: %s — relying on JNI/scan_all for updates", strerror(errno));
close(as->nl_sock); as->nl_sock = -1; return 0; close(as->nl_sock); as->nl_sock = -1; return 0;

Loading…
Cancel
Save