Browse Source
- New files: transport_layer/socket_monitor.c/h — socket state monitoring with per-instance ctx - etcp_api: expose get_latency/1s_pkt_count measuring, add etcp_hashrnd for tests - utun_instance: store socket_monitor per instance, add is_socket_ok check - topo_node: detect IPv4 addresses with port from listening sockets - topo_group: use node_name for display in routes - Makefile.am: add socket_monitor.c to buildtopo_upd
10 changed files with 420 additions and 0 deletions
@ -0,0 +1,261 @@
|
||||
#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> |
||||
|
||||
#ifndef _WIN32 |
||||
#include <linux/netlink.h> |
||||
#include <linux/rtnetlink.h> |
||||
#include <net/if.h> |
||||
#endif |
||||
|
||||
struct SOCKET_MONITOR { |
||||
struct UTUN_INSTANCE* instance; |
||||
socket_t nl_sock; |
||||
void* uasync_handle; |
||||
int seq; |
||||
uint32_t addr_changes; |
||||
uint32_t link_changes; |
||||
uint32_t route_changes; |
||||
uint32_t socket_updates; |
||||
}; |
||||
|
||||
static void socket_monitor_read_cb(int fd, void* arg); |
||||
|
||||
#ifndef _WIN32 |
||||
static 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_check_and_fire(struct SOCKET_MONITOR* sm, struct ETCP_SOCKET* es, uint32_t ifindex) { |
||||
if (es->netif_index != ifindex) return; |
||||
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++; |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket %s interface_addr changed: %s -> %s", |
||||
es->name, 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_handle_addr(struct SOCKET_MONITOR* sm, struct ifaddrmsg* ifa, int msg_type) { |
||||
sm->addr_changes++; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "addr event: type=%s ifindex=%u family=%d", |
||||
msg_type == RTM_NEWADDR ? "NEW" : "DEL", 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; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "link event: type=%s ifindex=%u flags=0x%x up=%d", |
||||
msg_type == RTM_NEWLINK ? "NEW" : "DEL", ifi->ifi_index, ifi->ifi_flags, is_up); |
||||
struct ETCP_SOCKET* es = sm->instance->etcp_sockets; |
||||
while (es) { |
||||
if (es->netif_index == ifi->ifi_index) { |
||||
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_DEBUG(DEBUG_CATEGORY_SOCKET, "default route event: type=%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++; |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket %s interface_addr changed via default route: %s -> %s", |
||||
es->name, 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); |
||||
} |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
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; } |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "initializing socket monitor"); |
||||
|
||||
#ifdef _WIN32 |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket monitor not available on Windows"); |
||||
return 0; |
||||
#else |
||||
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, 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; |
||||
#endif |
||||
} |
||||
|
||||
void socket_monitor_destroy(struct UTUN_INSTANCE* instance) { |
||||
if (!instance) return; |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "destroying socket monitor"); |
||||
#ifdef _WIN32 |
||||
return; |
||||
#else |
||||
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); |
||||
#endif |
||||
} |
||||
@ -0,0 +1,16 @@
|
||||
#ifndef SOCKET_MONITOR_H |
||||
#define SOCKET_MONITOR_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
struct UTUN_INSTANCE; |
||||
|
||||
int socket_monitor_init(struct UTUN_INSTANCE* instance); |
||||
void socket_monitor_destroy(struct UTUN_INSTANCE* instance); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
#endif |
||||
Loading…
Reference in new issue