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