Select frontend network callbacks for auto_sockets=android instead of opening a denied netlink monitor. Preserve monitor descriptor ownership, keep native monitoring failures fatal, and release caller config on failed Android core creation and restart. Register ConnectivityManager before requesting startup, report readiness after SERVICE_STARTED, and refresh addresses after initialization with serialized updates and generation checks. Add regression coverage for denied netlink, socket failures, address updates, and fd=0 preservation. Record the separate cancellation issue for startup before readiness. Validation: full check.sh passed, including proxy/burst/load integrations. Clean Android build installed successfully on SM-A525F and Q8; logcat confirms both cores initialized and entered the poll loop with external monitoring and post-start address refresh.
551 lines
24 KiB
C
551 lines
24 KiB
C
#include "socket_monitor.h"
|
|
#include "etcp_connections.h"
|
|
#include "etcp_api.h"
|
|
#include "../lib/socket_compat.h"
|
|
#include "../lib/platform_compat.h"
|
|
#include "../lib/getmyip.h"
|
|
#include "../lib/u_async.h"
|
|
#include "../lib/debug_config.h"
|
|
#include "../lib/mem.h"
|
|
#include "utun_instance.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef __linux__
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <net/if.h>
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
#include <net/route.h>
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#elif defined(_WIN32)
|
|
#include <iphlpapi.h>
|
|
#include <netioapi.h>
|
|
#include <ws2ipdef.h>
|
|
#endif
|
|
|
|
struct SOCKET_MONITOR {
|
|
struct UTUN_INSTANCE* instance;
|
|
void* uasync_handle;
|
|
uint32_t addr_changes;
|
|
uint32_t link_changes;
|
|
uint32_t route_changes;
|
|
uint32_t socket_updates;
|
|
#ifdef __linux__
|
|
socket_t nl_sock;
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
socket_t route_sock;
|
|
#elif defined(_WIN32)
|
|
HANDLE addr_notify_handle;
|
|
HANDLE route_notify_handle;
|
|
#endif
|
|
};
|
|
|
|
/* ===== Common functions (all platforms) ===== */
|
|
|
|
void socket_monitor_update_if_addr(struct ETCP_SOCKET* es) {
|
|
if (es->local_addr.ss_family == AF_INET) {
|
|
if (es->netif_index > 0) {
|
|
uint32_t if_ip = get_interface_ip_by_index(es->netif_index);
|
|
if (if_ip != 0) {
|
|
struct sockaddr_in* sin = (struct sockaddr_in*)&es->interface_addr;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_addr.s_addr = if_ip;
|
|
}
|
|
} else {
|
|
struct sockaddr_in* sin = (struct sockaddr_in*)&es->local_addr;
|
|
if (sin->sin_addr.s_addr == 0) {
|
|
struct sockaddr_storage remote;
|
|
memset(&remote, 0, sizeof(remote));
|
|
struct sockaddr_in* rem_sin = (struct sockaddr_in*)&remote;
|
|
rem_sin->sin_family = AF_INET;
|
|
rem_sin->sin_port = htons(53);
|
|
inet_pton(AF_INET, "8.8.8.8", &rem_sin->sin_addr);
|
|
get_outgoing_local_ip(&es->local_addr, &remote, &es->interface_addr);
|
|
}
|
|
}
|
|
} else if (es->local_addr.ss_family == AF_INET6) {
|
|
if (es->netif_index > 0) {
|
|
uint8_t v6addr[16];
|
|
int got = get_interface_ipv6_addr_nl(es->netif_index, 1, v6addr);
|
|
if (got != 0) got = get_interface_ipv6_addr_nl(es->netif_index, 0, v6addr);
|
|
if (got == 0) {
|
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&es->interface_addr;
|
|
sin6->sin6_family = AF_INET6;
|
|
memcpy(&sin6->sin6_addr, v6addr, 16);
|
|
}
|
|
} else {
|
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&es->local_addr;
|
|
if (memcmp(&sin6->sin6_addr, &in6addr_any, 16) == 0) {
|
|
uint16_t v6if = get_default_route_netif_index(AF_INET6);
|
|
if (v6if > 0) {
|
|
uint8_t v6addr[16];
|
|
int got = get_interface_ipv6_addr_nl(v6if, 1, v6addr);
|
|
if (got != 0) got = get_interface_ipv6_addr_nl(v6if, 0, v6addr);
|
|
if (got == 0) {
|
|
struct sockaddr_in6* if6 = (struct sockaddr_in6*)&es->interface_addr;
|
|
if6->sin6_family = AF_INET6;
|
|
memcpy(&if6->sin6_addr, v6addr, 16);
|
|
}
|
|
}
|
|
if (es->interface_addr.ss_family == 0) {
|
|
struct sockaddr_storage remote;
|
|
memset(&remote, 0, sizeof(remote));
|
|
struct sockaddr_in6* rem_sin6 = (struct sockaddr_in6*)&remote;
|
|
rem_sin6->sin6_family = AF_INET6;
|
|
rem_sin6->sin6_port = htons(53);
|
|
inet_pton(AF_INET6, "2001:4860:4860::8888", &rem_sin6->sin6_addr);
|
|
get_outgoing_local_ip(&es->local_addr, &remote, &es->interface_addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (es->interface_addr.ss_family == es->local_addr.ss_family && es->local_addr.ss_family != 0) {
|
|
if (es->local_addr.ss_family == AF_INET) {
|
|
struct sockaddr_in* sin_cfg = (struct sockaddr_in*)&es->local_addr;
|
|
struct sockaddr_in* sin_if = (struct sockaddr_in*)&es->interface_addr;
|
|
sin_if->sin_port = sin_cfg->sin_port;
|
|
} else if (es->local_addr.ss_family == AF_INET6) {
|
|
struct sockaddr_in6* sin6_cfg = (struct sockaddr_in6*)&es->local_addr;
|
|
struct sockaddr_in6* sin6_if = (struct sockaddr_in6*)&es->interface_addr;
|
|
sin6_if->sin6_port = sin6_cfg->sin6_port;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void socket_monitor_fire_one(struct SOCKET_MONITOR* sm, struct ETCP_SOCKET* es) {
|
|
struct sockaddr_storage old_if_addr = es->interface_addr;
|
|
socket_monitor_update_if_addr(es);
|
|
if (memcmp(&old_if_addr, &es->interface_addr, sizeof(old_if_addr)) != 0) {
|
|
sm->socket_updates++;
|
|
char ifname[IF_NAMESIZE]; if (!if_indextoname(es->netif_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket %s (%s idx=%u) addr changed: %s → %s",
|
|
es->name, ifname, es->netif_index,
|
|
sockaddr_storage_to_str(&old_if_addr).str, sockaddr_storage_to_str(&es->interface_addr).str);
|
|
etcp_socket_cbk_fire(es, ETCP_SOCKET_EVENT_ADDR_CHANGED);
|
|
}
|
|
}
|
|
|
|
static void socket_monitor_check_and_fire(struct SOCKET_MONITOR* sm, struct ETCP_SOCKET* es, uint32_t ifindex) {
|
|
if (es->netif_index != ifindex) return;
|
|
socket_monitor_fire_one(sm, es);
|
|
}
|
|
|
|
/* ===== Linux: netlink ===== */
|
|
#ifdef __linux__
|
|
|
|
static void socket_monitor_handle_addr(struct SOCKET_MONITOR* sm, struct ifaddrmsg* ifa, int msg_type) {
|
|
sm->addr_changes++;
|
|
char ifname[IFNAMSIZ]; if (!if_indextoname(ifa->ifa_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "addr %s: %s (idx=%u) family=%d",
|
|
msg_type == RTM_NEWADDR ? "NEW" : "DEL", ifname, ifa->ifa_index, ifa->ifa_family);
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) { socket_monitor_check_and_fire(sm, es, ifa->ifa_index); es = es->next; }
|
|
}
|
|
|
|
static void socket_monitor_handle_link(struct SOCKET_MONITOR* sm, struct ifinfomsg* ifi, int msg_type) {
|
|
sm->link_changes++;
|
|
int is_up = (ifi->ifi_flags & IFF_UP) != 0;
|
|
char ifname[IFNAMSIZ]; if (!if_indextoname(ifi->ifi_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "link %s: %s (idx=%u) flags=0x%x",
|
|
is_up ? "UP" : "DOWN", ifname, ifi->ifi_index, ifi->ifi_flags);
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) {
|
|
if (es->netif_index == ifi->ifi_index) {
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, " → socket %s firing STATUS_CHANGED", es->name);
|
|
etcp_socket_cbk_fire(es, ETCP_SOCKET_EVENT_STATUS_CHANGED);
|
|
if (is_up) socket_monitor_check_and_fire(sm, es, ifi->ifi_index);
|
|
}
|
|
es = es->next;
|
|
}
|
|
}
|
|
|
|
static void socket_monitor_handle_route(struct SOCKET_MONITOR* sm, struct rtmsg* rtm, int msg_type) {
|
|
if (rtm->rtm_dst_len != 0) return;
|
|
sm->route_changes++;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "default route %s: family=%d",
|
|
msg_type == RTM_NEWROUTE ? "NEW" : "DEL", rtm->rtm_family);
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) {
|
|
if (es->netif_index != 0 || es->local_addr.ss_family == 0) { es = es->next; continue; }
|
|
int is_any = 0;
|
|
if (es->local_addr.ss_family == AF_INET) {
|
|
struct sockaddr_in* sin = (struct sockaddr_in*)&es->local_addr;
|
|
is_any = (sin->sin_addr.s_addr == 0);
|
|
} else if (es->local_addr.ss_family == AF_INET6) {
|
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&es->local_addr;
|
|
is_any = (memcmp(&sin6->sin6_addr, &in6addr_any, 16) == 0);
|
|
}
|
|
if (is_any) {
|
|
struct sockaddr_storage old_if_addr = es->interface_addr;
|
|
socket_monitor_update_if_addr(es);
|
|
if (memcmp(&old_if_addr, &es->interface_addr, sizeof(old_if_addr)) != 0) {
|
|
sm->socket_updates++;
|
|
char ifname[IFNAMSIZ]; if (!if_indextoname(es->netif_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket %s (%s idx=%u) addr changed via default route: %s → %s",
|
|
es->name, ifname, es->netif_index,
|
|
sockaddr_storage_to_str(&old_if_addr).str, sockaddr_storage_to_str(&es->interface_addr).str);
|
|
etcp_socket_cbk_fire(es, ETCP_SOCKET_EVENT_ADDR_CHANGED);
|
|
}
|
|
}
|
|
es = es->next;
|
|
}
|
|
}
|
|
|
|
static void socket_monitor_read_cb(int fd, void* arg) {
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)arg;
|
|
char buf[8192];
|
|
struct sockaddr_nl nladdr;
|
|
socklen_t nladdr_len = sizeof(nladdr);
|
|
for (;;) {
|
|
ssize_t n = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&nladdr, &nladdr_len);
|
|
if (n < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "netlink recvfrom fd=%d failed: %s", fd, strerror(errno));
|
|
break;
|
|
}
|
|
if (n == 0) break;
|
|
size_t remaining = (size_t)n;
|
|
struct nlmsghdr* nlh = (struct nlmsghdr*)buf;
|
|
while (NLMSG_OK(nlh, remaining)) {
|
|
if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR) {
|
|
socket_monitor_handle_addr(sm, (struct ifaddrmsg*)NLMSG_DATA(nlh), nlh->nlmsg_type);
|
|
} else if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK) {
|
|
socket_monitor_handle_link(sm, (struct ifinfomsg*)NLMSG_DATA(nlh), nlh->nlmsg_type);
|
|
} else if (nlh->nlmsg_type == RTM_NEWROUTE || nlh->nlmsg_type == RTM_DELROUTE) {
|
|
socket_monitor_handle_route(sm, (struct rtmsg*)NLMSG_DATA(nlh), nlh->nlmsg_type);
|
|
}
|
|
if (nlh->nlmsg_type == NLMSG_DONE) break;
|
|
nlh = NLMSG_NEXT(nlh, remaining);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===== FreeBSD: AF_ROUTE routing socket ===== */
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
|
|
#ifndef RT_ROUNDUP
|
|
#define RT_ROUNDUP(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
|
|
#endif
|
|
|
|
static int sockaddr_to_plen(const struct sockaddr* sa) {
|
|
if (!sa || sa->sa_len == 0) return 0;
|
|
switch (sa->sa_family) {
|
|
case AF_INET: {
|
|
uint32_t m = ntohl(((const struct sockaddr_in*)sa)->sin_addr.s_addr);
|
|
int bits = 0;
|
|
while (bits < 32 && (m & 0x80000000)) { bits++; m <<= 1; }
|
|
return bits;
|
|
}
|
|
case AF_INET6: {
|
|
const uint8_t* a = ((const struct sockaddr_in6*)sa)->sin6_addr.s6_addr;
|
|
int bits = 0;
|
|
for (int i = 0; i < 16; i++) {
|
|
if (a[i] == 0xff) { bits += 8; continue; }
|
|
uint8_t b = a[i];
|
|
while (b & 0x80) { bits++; b <<= 1; }
|
|
break;
|
|
}
|
|
return bits;
|
|
}
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static void sa_to_str(const struct sockaddr* sa, char* buf, size_t bufsz) {
|
|
if (!sa || sa->sa_len == 0) { snprintf(buf, bufsz, "?"); return; }
|
|
switch (sa->sa_family) {
|
|
case AF_INET:
|
|
inet_ntop(AF_INET, &((const struct sockaddr_in*)sa)->sin_addr, buf, (socklen_t)bufsz); break;
|
|
case AF_INET6:
|
|
inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buf, (socklen_t)bufsz); break;
|
|
default: snprintf(buf, bufsz, "?%d", sa->sa_family); break;
|
|
}
|
|
}
|
|
|
|
/* walk RTA sockaddrs after rtm header, extract addr, gw, plen */
|
|
static void bsd_parse_rtm(struct rt_msghdr* rtm, int mask, char* addr, size_t as, char* gw, size_t gs, int* plen) {
|
|
struct sockaddr* sa = (struct sockaddr*)(rtm + 1);
|
|
addr[0] = gw[0] = '\0'; *plen = 0;
|
|
for (int i = 0, b = 1; i <= RTAX_MAX && mask; i++, b <<= 1) {
|
|
if (!(mask & b)) continue;
|
|
int sl = sa->sa_len ? (int)RT_ROUNDUP(sa->sa_len) : (int)sizeof(struct sockaddr);
|
|
switch (i) {
|
|
case RTAX_DST: sa_to_str(sa, addr, as); break;
|
|
case RTAX_IFA: sa_to_str(sa, addr, as); break;
|
|
case RTAX_GATEWAY: sa_to_str(sa, gw, gs); break;
|
|
case RTAX_NETMASK: *plen = sockaddr_to_plen(sa); break;
|
|
}
|
|
mask &= ~b;
|
|
sa = (struct sockaddr*)((char*)sa + sl);
|
|
}
|
|
}
|
|
|
|
static void socket_monitor_bsd_read_cb(int fd, void* arg) {
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)arg;
|
|
char buf[8192];
|
|
for (;;) {
|
|
ssize_t n = read(fd, buf, sizeof(buf));
|
|
if (n < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "route socket read fd=%d failed: %s", fd, strerror(errno));
|
|
break;
|
|
}
|
|
if (n < (ssize_t)sizeof(struct rt_msghdr)) break;
|
|
|
|
struct rt_msghdr* rtm = (struct rt_msghdr*)buf;
|
|
if (rtm->rtm_version != RTM_VERSION) continue;
|
|
|
|
switch (rtm->rtm_type) {
|
|
case RTM_IFINFO:
|
|
case RTM_IFANNOUNCE: {
|
|
sm->link_changes++;
|
|
struct if_msghdr* ifm = (struct if_msghdr*)rtm;
|
|
char ifname[IFNAMSIZ];
|
|
if (!if_indextoname(ifm->ifm_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
const char* nm = rtm->rtm_type == RTM_IFINFO ? "IFINFO" : "IFANNOUNCE";
|
|
int is_up = (ifm->ifm_flags & IFF_UP) != 0;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "link event %s: %s (idx=%u) %s",
|
|
nm, ifname, ifm->ifm_index, is_up ? "UP" : "DOWN");
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) {
|
|
if (es->netif_index == ifm->ifm_index) {
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, " → socket %s firing STATUS_CHANGED", es->name);
|
|
etcp_socket_cbk_fire(es, ETCP_SOCKET_EVENT_STATUS_CHANGED);
|
|
if (is_up) socket_monitor_fire_one(sm, es);
|
|
}
|
|
es = es->next;
|
|
}
|
|
} break;
|
|
case RTM_NEWADDR:
|
|
case RTM_DELADDR: {
|
|
sm->addr_changes++;
|
|
struct ifa_msghdr* ifam = (struct ifa_msghdr*)rtm;
|
|
char ifname[IFNAMSIZ], addr[INET6_ADDRSTRLEN]; int plen;
|
|
if (!if_indextoname(ifam->ifam_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
bsd_parse_rtm(rtm, ifam->ifam_addrs, addr, sizeof(addr), NULL, 0, &plen);
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "addr %s: %s/%d on %s (idx=%u)",
|
|
rtm->rtm_type == RTM_NEWADDR ? "NEW" : "DEL", addr, plen, ifname, ifam->ifam_index);
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) { socket_monitor_fire_one(sm, es); es = es->next; }
|
|
} break;
|
|
case RTM_ADD:
|
|
case RTM_DELETE: {
|
|
sm->route_changes++;
|
|
char ifname[IFNAMSIZ], dst[INET6_ADDRSTRLEN], gw[INET6_ADDRSTRLEN]; int plen;
|
|
if (!if_indextoname(rtm->rtm_index, ifname)) snprintf(ifname, sizeof(ifname), "?");
|
|
bsd_parse_rtm(rtm, rtm->rtm_addrs, dst, sizeof(dst), gw, sizeof(gw), &plen);
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "route %s: %s/%d gw=%s dev=%s (idx=%u)",
|
|
rtm->rtm_type == RTM_ADD ? "ADD" : "DELETE",
|
|
dst[0] ? dst : "default", plen, gw, ifname, rtm->rtm_index);
|
|
{
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) {
|
|
if (es->netif_index != 0 || es->local_addr.ss_family == 0) { es = es->next; continue; }
|
|
int is_any = 0;
|
|
if (es->local_addr.ss_family == AF_INET) {
|
|
struct sockaddr_in* sin = (struct sockaddr_in*)&es->local_addr;
|
|
is_any = (sin->sin_addr.s_addr == 0);
|
|
} else if (es->local_addr.ss_family == AF_INET6) {
|
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&es->local_addr;
|
|
is_any = (memcmp(&sin6->sin6_addr, &in6addr_any, 16) == 0);
|
|
}
|
|
if (is_any) socket_monitor_fire_one(sm, es);
|
|
es = es->next;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===== Windows: IP Helper callbacks ===== */
|
|
#elif defined(_WIN32)
|
|
|
|
static void process_win_addr_notify(void* arg) {
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)arg;
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) { socket_monitor_fire_one(sm, es); es = es->next; }
|
|
}
|
|
|
|
static void process_win_route_notify(void* arg) {
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)arg;
|
|
struct ETCP_SOCKET* es = sm->instance->etcp_sockets;
|
|
while (es) {
|
|
if (es->netif_index != 0 || es->local_addr.ss_family == 0) { es = es->next; continue; }
|
|
int is_any = 0;
|
|
if (es->local_addr.ss_family == AF_INET) {
|
|
struct sockaddr_in* sin = (struct sockaddr_in*)&es->local_addr;
|
|
is_any = (sin->sin_addr.s_addr == 0);
|
|
} else if (es->local_addr.ss_family == AF_INET6) {
|
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&es->local_addr;
|
|
is_any = (memcmp(&sin6->sin6_addr, &in6addr_any, 16) == 0);
|
|
}
|
|
if (is_any) socket_monitor_fire_one(sm, es);
|
|
es = es->next;
|
|
}
|
|
}
|
|
|
|
static void WINAPI addr_change_cb(PVOID ctx, PMIB_UNICASTIPADDRESS_ROW row, MIB_NOTIFICATION_TYPE type) {
|
|
(void)row; (void)type;
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)ctx;
|
|
sm->addr_changes++;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "addr event: type=%d", (int)type);
|
|
uasync_post(sm->instance->ua, process_win_addr_notify, sm);
|
|
}
|
|
|
|
static void WINAPI route_change_cb(PVOID ctx, PMIB_IPFORWARD_ROW2 row, MIB_NOTIFICATION_TYPE type) {
|
|
(void)row; (void)type;
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)ctx;
|
|
sm->route_changes++;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "route event: type=%d", (int)type);
|
|
uasync_post(sm->instance->ua, process_win_route_notify, sm);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ===== Public API ===== */
|
|
|
|
int socket_monitor_init(struct UTUN_INSTANCE* instance) {
|
|
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "instance is NULL"); return -1; }
|
|
if (instance->socket_monitor) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socket monitor already initialized"); return -1; }
|
|
if (instance->config && instance->config->global.auto_sockets == CFG_AUTO_SOCKETS_ANDROID) {
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "Socket monitoring delegated to external network callbacks (auto_sockets=android)");
|
|
return 0;
|
|
}
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "initializing socket monitor");
|
|
|
|
#ifdef __linux__
|
|
struct SOCKET_MONITOR* sm = u_calloc(1, sizeof(struct SOCKET_MONITOR));
|
|
if (!sm) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "alloc failed"); return -1; }
|
|
sm->instance = instance;
|
|
sm->nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
if (sm->nl_sock < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "failed to create netlink socket: %s", strerror(errno));
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
struct sockaddr_nl nladdr;
|
|
memset(&nladdr, 0, sizeof(nladdr));
|
|
nladdr.nl_family = AF_NETLINK;
|
|
nladdr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR
|
|
| RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
|
|
if (bind(sm->nl_sock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "failed to bind netlink socket: %s", strerror(errno));
|
|
close(sm->nl_sock);
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
if (socket_set_nonblocking(sm->nl_sock) != 0) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "failed to set netlink socket non-blocking: %s", strerror(errno));
|
|
}
|
|
sm->uasync_handle = uasync_add_socket(instance->ua, sm->nl_sock, socket_monitor_read_cb, NULL, NULL, "netlink_monitor", sm);
|
|
if (!sm->uasync_handle) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "failed to register netlink socket fd=%d with uasync", sm->nl_sock);
|
|
close(sm->nl_sock);
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
instance->socket_monitor = sm;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor initialized: nl_sock=%d", sm->nl_sock);
|
|
return 0;
|
|
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
struct SOCKET_MONITOR* sm = u_calloc(1, sizeof(struct SOCKET_MONITOR));
|
|
if (!sm) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "alloc failed"); return -1; }
|
|
sm->instance = instance;
|
|
sm->route_sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
|
if (sm->route_sock < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "failed to create route socket: %s", strerror(errno));
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
if (fcntl(sm->route_sock, F_SETFL, O_NONBLOCK) < 0) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "failed to set route socket non-blocking: %s", strerror(errno));
|
|
}
|
|
sm->uasync_handle = uasync_add_socket(instance->ua, sm->route_sock, socket_monitor_bsd_read_cb, NULL, NULL, "route_monitor", sm);
|
|
if (!sm->uasync_handle) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "failed to register route socket fd=%d with uasync", sm->route_sock);
|
|
close(sm->route_sock);
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
instance->socket_monitor = sm;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor initialized: route_sock=%d", sm->route_sock);
|
|
return 0;
|
|
|
|
#elif defined(_WIN32)
|
|
struct SOCKET_MONITOR* sm = u_calloc(1, sizeof(struct SOCKET_MONITOR));
|
|
if (!sm) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "alloc failed"); return -1; }
|
|
sm->instance = instance;
|
|
|
|
DWORD ret = NotifyUnicastIpAddressChange(AF_UNSPEC, addr_change_cb, sm, FALSE, &sm->addr_notify_handle);
|
|
if (ret != NO_ERROR) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "NotifyUnicastIpAddressChange failed: %lu", (unsigned long)ret);
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "NotifyUnicastIpAddressChange registered: handle=%p", sm->addr_notify_handle);
|
|
|
|
ret = NotifyRouteChange2(AF_UNSPEC, route_change_cb, sm, FALSE, &sm->route_notify_handle);
|
|
if (ret != NO_ERROR) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "NotifyRouteChange2 failed: %lu", (unsigned long)ret);
|
|
CancelMibChangeNotify2(sm->addr_notify_handle);
|
|
u_free(sm);
|
|
return -1;
|
|
}
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "NotifyRouteChange2 registered: handle=%p", sm->route_notify_handle);
|
|
|
|
instance->socket_monitor = sm;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor initialized via IP Helper");
|
|
return 0;
|
|
|
|
#else
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor not available on this platform");
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void socket_monitor_destroy(struct UTUN_INSTANCE* instance) {
|
|
if (!instance) return;
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "destroying socket monitor");
|
|
|
|
#ifdef __linux__
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)instance->socket_monitor;
|
|
if (!sm) return;
|
|
instance->socket_monitor = NULL;
|
|
uasync_remove_socket(instance->ua, sm->uasync_handle);
|
|
close(sm->nl_sock);
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor destroyed: nl_sock=%d addr_changes=%u link_changes=%u route_changes=%u socket_updates=%u",
|
|
sm->nl_sock, sm->addr_changes, sm->link_changes, sm->route_changes, sm->socket_updates);
|
|
u_free(sm);
|
|
|
|
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)instance->socket_monitor;
|
|
if (!sm) return;
|
|
instance->socket_monitor = NULL;
|
|
uasync_remove_socket(instance->ua, sm->uasync_handle);
|
|
close(sm->route_sock);
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor destroyed: route_sock=%d addr_changes=%u link_changes=%u route_changes=%u socket_updates=%u",
|
|
sm->route_sock, sm->addr_changes, sm->link_changes, sm->route_changes, sm->socket_updates);
|
|
u_free(sm);
|
|
|
|
#elif defined(_WIN32)
|
|
struct SOCKET_MONITOR* sm = (struct SOCKET_MONITOR*)instance->socket_monitor;
|
|
if (!sm) return;
|
|
instance->socket_monitor = NULL;
|
|
CancelMibChangeNotify2(sm->addr_notify_handle);
|
|
CancelMibChangeNotify2(sm->route_notify_handle);
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor destroyed: addr_changes=%u link_changes=%u route_changes=%u socket_updates=%u",
|
|
sm->addr_changes, sm->link_changes, sm->route_changes, sm->socket_updates);
|
|
u_free(sm);
|
|
|
|
#else
|
|
(void)instance;
|
|
#endif
|
|
}
|