- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
558 lines
18 KiB
C
558 lines
18 KiB
C
// tun_route.c - Cross-platform system routing table management
|
|
// Linux: netlink sockets, Windows: IP Helper API, BSD: routing sockets
|
|
|
|
#include "tun_route.h"
|
|
#include "config_parser.h"
|
|
#include "../lib/debug_config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include "../lib/mem.h"
|
|
|
|
// Platform detection
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
#elif defined(__linux__)
|
|
#include <linux/netlink.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <net/if.h>
|
|
#include <poll.h>
|
|
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
|
|
#include <sys/socket.h>
|
|
#include <net/route.h>
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <netinet/in.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
// Convert prefix length to netmask
|
|
static uint32_t prefix_to_netmask(uint8_t prefix_len) {
|
|
return (prefix_len == 0) ? 0 : htonl(~((1U << (32 - prefix_len)) - 1));
|
|
}
|
|
|
|
#ifdef __linux__
|
|
|
|
// Linux netlink implementation
|
|
|
|
struct nl_req {
|
|
struct nlmsghdr nl;
|
|
struct rtmsg rt;
|
|
char buf[256];
|
|
};
|
|
|
|
static int netlink_route(int ifindex, uint32_t network, uint8_t prefix_len, int cmd, int flags) {
|
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
if (fd < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create netlink socket: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
// Bind to receive responses
|
|
struct sockaddr_nl addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.nl_family = AF_NETLINK;
|
|
addr.nl_pid = getpid();
|
|
addr.nl_groups = 0;
|
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bind netlink socket: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
// Prepare destination address (kernel)
|
|
struct sockaddr_nl dest;
|
|
memset(&dest, 0, sizeof(dest));
|
|
dest.nl_family = AF_NETLINK;
|
|
dest.nl_pid = 0; // kernel
|
|
dest.nl_groups = 0;
|
|
|
|
struct nl_req req;
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
|
req.nl.nlmsg_type = cmd; // RTM_NEWROUTE or RTM_DELROUTE
|
|
req.nl.nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK; // Request ACK from kernel
|
|
req.nl.nlmsg_seq = 1;
|
|
req.nl.nlmsg_pid = getpid();
|
|
|
|
req.rt.rtm_family = AF_INET;
|
|
req.rt.rtm_table = RT_TABLE_MAIN;
|
|
req.rt.rtm_protocol = RTPROT_STATIC;
|
|
req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
|
|
req.rt.rtm_type = RTN_UNICAST;
|
|
req.rt.rtm_dst_len = prefix_len;
|
|
|
|
// Add destination address attribute
|
|
struct rtattr *rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nl.nlmsg_len));
|
|
rta->rta_type = RTA_DST;
|
|
rta->rta_len = RTA_LENGTH(4);
|
|
memcpy(RTA_DATA(rta), &network, 4);
|
|
req.nl.nlmsg_len = NLMSG_ALIGN(req.nl.nlmsg_len) + RTA_ALIGN(rta->rta_len);
|
|
|
|
// Add output interface attribute
|
|
rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nl.nlmsg_len));
|
|
rta->rta_type = RTA_OIF;
|
|
rta->rta_len = RTA_LENGTH(4);
|
|
memcpy(RTA_DATA(rta), &ifindex, 4);
|
|
req.nl.nlmsg_len = NLMSG_ALIGN(req.nl.nlmsg_len) + RTA_ALIGN(rta->rta_len);
|
|
|
|
if (sendto(fd, &req, req.nl.nlmsg_len, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to send netlink message: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Netlink message sent: seq=%u pid=%u len=%d",
|
|
req.nl.nlmsg_seq, req.nl.nlmsg_pid, req.nl.nlmsg_len);
|
|
|
|
// Wait for response with timeout using poll
|
|
struct pollfd pfd = { .fd = fd, .events = POLLIN };
|
|
int poll_ret = poll(&pfd, 1, 1000); // 1 second timeout
|
|
if (poll_ret < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "poll() failed: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
} else if (poll_ret == 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Timeout waiting for netlink response (sent %d bytes)", req.nl.nlmsg_len);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Poll returned: revents=%d", pfd.revents);
|
|
|
|
// Receive response
|
|
char reply[4096];
|
|
ssize_t len = recv(fd, reply, sizeof(reply), 0);
|
|
if (len < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to receive netlink response: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Received netlink response: len=%d", (int)len);
|
|
|
|
struct nlmsghdr *nl_hdr = (struct nlmsghdr *)reply;
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Netlink msg: type=%d seq=%d pid=%d",
|
|
nl_hdr->nlmsg_type, nl_hdr->nlmsg_seq, nl_hdr->nlmsg_pid);
|
|
|
|
if (nl_hdr->nlmsg_type == NLMSG_ERROR) {
|
|
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nl_hdr);
|
|
if (err->error < 0) {
|
|
errno = -err->error;
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Netlink error: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
// Fallback: use ip route command
|
|
static int ip_route_cmd(const char *ifname, uint32_t network, uint8_t prefix_len, const char *cmd) {
|
|
struct in_addr addr;
|
|
addr.s_addr = network;
|
|
|
|
char cmdline[256];
|
|
snprintf(cmdline, sizeof(cmdline), "ip route %s %s/%d dev %s >/dev/null 2>&1",
|
|
cmd, ip_to_str(&addr, AF_INET).str, prefix_len, ifname);
|
|
|
|
int ret = system(cmdline);
|
|
return (ret == 0) ? 0 : -1;
|
|
}
|
|
|
|
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index and name are both invalid");
|
|
return -1;
|
|
}
|
|
|
|
network = htonl(network);
|
|
|
|
// Try netlink first
|
|
if (ifindex != 0 && netlink_route(ifindex, network, prefix_len, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Fallback to ip route command
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Netlink failed, trying ip route command for ifindex %u", ifindex);
|
|
const char *dev = ifname ? ifname : "";
|
|
return ip_route_cmd(dev, ntohl(network), prefix_len, "add");
|
|
}
|
|
|
|
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "ifindex and ifname are both invalid");
|
|
return -1;
|
|
}
|
|
|
|
network = htonl(network);
|
|
|
|
// Try netlink first
|
|
if (ifindex != 0 && netlink_route(ifindex, network, prefix_len, RTM_DELROUTE, 0) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Fallback to ip route command
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Netlink failed, trying ip route command for ifindex %u", ifindex);
|
|
const char *dev = ifname ? ifname : "";
|
|
return ip_route_cmd(dev, ntohl(network), prefix_len, "del");
|
|
}
|
|
|
|
int tun_route_flush(const char *ifname) {
|
|
// Use ip route flush as primary method - works reliably
|
|
char cmdline[256];
|
|
snprintf(cmdline, sizeof(cmdline), "ip route flush dev %s >/dev/null 2>&1", ifname);
|
|
int ret = system(cmdline);
|
|
|
|
if (ret != 0) {
|
|
// Fallback: try to list and delete routes one by one
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "ip route flush failed, trying alternative method");
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"ip route show dev %s 2>/dev/null | while read r; do ip route del $r dev %s >/dev/null 2>&1; done",
|
|
ifname, ifname);
|
|
ret = system(cmdline);
|
|
}
|
|
|
|
return (ret == 0) ? 0 : -1;
|
|
}
|
|
|
|
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) {
|
|
int count = 0;
|
|
struct CFG_ROUTE_ENTRY *entry = routes;
|
|
|
|
while (entry) {
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
|
|
if (entry->ip.family == AF_INET) {
|
|
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) {
|
|
count++;
|
|
}
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d routes via ifindex=%lu", count, (unsigned long)ifindex);
|
|
return count;
|
|
}
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
// Windows IP Helper API implementation
|
|
|
|
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
|
|
(void)ifname; // Not used on Windows
|
|
if (ifindex == 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "ifindex is 0");
|
|
return -1;
|
|
}
|
|
|
|
uint32_t dest = htonl(network);
|
|
uint32_t mask = prefix_to_netmask(prefix_len);
|
|
|
|
char cmd[256];
|
|
struct in_addr dest_addr;
|
|
dest_addr.s_addr = dest;
|
|
snprintf(cmd, sizeof(cmd), "netsh interface ip add route %s/%d interface=%lu store=active >NUL 2>&1",
|
|
inet_ntoa(dest_addr), prefix_len, (unsigned long)ifindex);
|
|
int sys_ret = system(cmd);
|
|
if (sys_ret == 0) {
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added route %s/%d via ifindex=%lu",
|
|
inet_ntoa(dest_addr), prefix_len, (unsigned long)ifindex);
|
|
return 0;
|
|
}
|
|
|
|
MIB_IPFORWARDROW route;
|
|
memset(&route, 0, sizeof(route));
|
|
|
|
route.dwForwardDest = dest;
|
|
route.dwForwardMask = mask;
|
|
route.dwForwardPolicy = 0;
|
|
route.dwForwardNextHop = 0;
|
|
route.dwForwardIfIndex = ifindex;
|
|
route.dwForwardType = MIB_IPROUTE_TYPE_INDIRECT;
|
|
route.dwForwardProto = MIB_IPPROTO_NETMGMT;
|
|
route.dwForwardAge = 0;
|
|
route.dwForwardNextHopAS = 0;
|
|
route.dwForwardMetric1 = 1;
|
|
route.dwForwardMetric2 = 0;
|
|
route.dwForwardMetric3 = 0;
|
|
route.dwForwardMetric4 = 0;
|
|
route.dwForwardMetric5 = 0;
|
|
|
|
DWORD ret = CreateIpForwardEntry(&route);
|
|
if (ret != NO_ERROR) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to add route %s/%d: %lu",
|
|
inet_ntoa(dest_addr), prefix_len, ret);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
|
|
(void)ifname; // Not used on Windows
|
|
if (ifindex == 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "ifindex is 0");
|
|
return -1;
|
|
}
|
|
|
|
struct in_addr dest_addr;
|
|
dest_addr.s_addr = htonl(network);
|
|
char cmd[256];
|
|
snprintf(cmd, sizeof(cmd), "netsh interface ip delete route %s/%d interface=%lu store=active >NUL 2>&1",
|
|
inet_ntoa(dest_addr), prefix_len, (unsigned long)ifindex);
|
|
int sys_ret = system(cmd);
|
|
if (sys_ret == 0) {
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted route %s/%d via ifindex=%lu",
|
|
inet_ntoa(dest_addr), prefix_len, (unsigned long)ifindex);
|
|
return 0;
|
|
}
|
|
|
|
MIB_IPFORWARDROW route;
|
|
memset(&route, 0, sizeof(route));
|
|
|
|
route.dwForwardDest = htonl(network);
|
|
route.dwForwardMask = prefix_to_netmask(prefix_len);
|
|
route.dwForwardIfIndex = ifindex;
|
|
|
|
DWORD ret = DeleteIpForwardEntry(&route);
|
|
if (ret != NO_ERROR) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to delete route %s/%d: %lu",
|
|
inet_ntoa(dest_addr), prefix_len, ret);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int tun_route_flush(const char *ifname) {
|
|
PMIB_IPFORWARDTABLE table = NULL;
|
|
DWORD size = 0;
|
|
DWORD ret;
|
|
|
|
DWORD ifindex = if_nametoindex(ifname);
|
|
if (ifindex == 0) {
|
|
return -1;
|
|
}
|
|
|
|
// Get table size
|
|
ret = GetIpForwardTable(NULL, &size, 0);
|
|
if (ret != ERROR_INSUFFICIENT_BUFFER) {
|
|
return -1;
|
|
}
|
|
|
|
table = (PMIB_IPFORWARDTABLE)u_malloc(size);
|
|
if (!table) return -1;
|
|
|
|
ret = GetIpForwardTable(table, &size, 0);
|
|
if (ret != NO_ERROR) {
|
|
u_free(table);
|
|
return -1;
|
|
}
|
|
|
|
// Delete all routes for this interface
|
|
for (DWORD i = 0; i < table->dwNumEntries; i++) {
|
|
if (table->table[i].dwForwardIfIndex == ifindex) {
|
|
DeleteIpForwardEntry(&table->table[i]);
|
|
}
|
|
}
|
|
|
|
u_free(table);
|
|
return 0;
|
|
}
|
|
|
|
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) {
|
|
int count = 0;
|
|
struct CFG_ROUTE_ENTRY *entry = routes;
|
|
|
|
while (entry) {
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
|
|
if (entry->ip.family == AF_INET) {
|
|
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) {
|
|
count++;
|
|
}
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d routes via ifindex=%lu", count, (unsigned long)ifindex);
|
|
return count;
|
|
}
|
|
|
|
#else
|
|
|
|
// BSD / macOS routing socket implementation
|
|
|
|
static int routing_socket_cmd(int ifindex, uint32_t network, uint8_t prefix_len, int cmd) {
|
|
int fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
|
|
if (fd < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create routing socket: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
uint8_t buf[512];
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
|
|
rtm->rtm_msglen = sizeof(struct rt_msghdr);
|
|
rtm->rtm_version = RTM_VERSION;
|
|
rtm->rtm_type = cmd; // RTM_ADD or RTM_DELETE
|
|
rtm->rtm_index = ifindex;
|
|
rtm->rtm_pid = getpid();
|
|
rtm->rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
|
|
rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
|
|
|
|
// Destination address (network)
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)(rtm + 1);
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_len = sizeof(struct sockaddr_in);
|
|
sin->sin_addr.s_addr = htonl(network);
|
|
rtm->rtm_msglen += sizeof(struct sockaddr_in);
|
|
|
|
// Netmask
|
|
sin++;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_len = sizeof(struct sockaddr_in);
|
|
sin->sin_addr.s_addr = prefix_to_netmask(prefix_len);
|
|
rtm->rtm_msglen += sizeof(struct sockaddr_in);
|
|
|
|
// Interface name
|
|
sin++;
|
|
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sin;
|
|
sdl->sdl_family = AF_LINK;
|
|
sdl->sdl_index = ifindex;
|
|
sdl->sdl_len = sizeof(struct sockaddr_dl);
|
|
rtm->rtm_msglen += sizeof(struct sockaddr_dl);
|
|
|
|
if (write(fd, rtm, rtm->rtm_msglen) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to send routing message: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
// Read response
|
|
ssize_t n = read(fd, buf, sizeof(buf));
|
|
if (n >= sizeof(struct rt_msghdr)) {
|
|
if (rtm->rtm_errno != 0) {
|
|
errno = rtm->rtm_errno;
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Routing error: %s", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
// Fallback: use route command
|
|
static int route_cmd(const char *ifname, uint32_t network, uint8_t prefix_len, const char *cmd) {
|
|
struct in_addr addr;
|
|
addr.s_addr = htonl(network);
|
|
|
|
char cmdline[256];
|
|
snprintf(cmdline, sizeof(cmdline), "route %s -net %s/%d -interface %s >/dev/null 2>&1",
|
|
cmd, ip_to_str(&addr, AF_INET).str, prefix_len, ifname);
|
|
|
|
int ret = system(cmdline);
|
|
return (ret == 0) ? 0 : -1;
|
|
}
|
|
|
|
int tun_route_add(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Interface index and name are both invalid");
|
|
return -1;
|
|
}
|
|
|
|
// Try routing socket first
|
|
if (ifindex != 0 && routing_socket_cmd(ifindex, network, prefix_len, RTM_ADD) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Fallback to route command
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Routing socket failed, trying route command for ifindex %u", ifindex);
|
|
const char *dev = ifname ? ifname : "";
|
|
return route_cmd(dev, network, prefix_len, "add");
|
|
}
|
|
|
|
int tun_route_del(uint32_t ifindex, const char *ifname, uint32_t network, uint8_t prefix_len) {
|
|
if (ifindex == 0 && (!ifname || !ifname[0])) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "ifindex and ifname are both invalid");
|
|
return -1;
|
|
}
|
|
|
|
// Try routing socket first
|
|
if (ifindex != 0 && routing_socket_cmd(ifindex, network, prefix_len, RTM_DELETE) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Fallback to route command
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Routing socket failed, trying route command for ifindex %u", ifindex);
|
|
const char *dev = ifname ? ifname : "";
|
|
return route_cmd(dev, network, prefix_len, "delete");
|
|
}
|
|
|
|
int tun_route_flush(const char *ifname) {
|
|
// Use route command to flush routes - more reliable on BSD
|
|
char cmdline[256];
|
|
snprintf(cmdline, sizeof(cmdline),
|
|
"route -n show -interface %s 2>/dev/null | grep -v '^[[:space:]]*#' | "
|
|
"awk '/^[0-9]+\\./{print $1}' | while read r; do route delete -net $r >/dev/null 2>&1; done",
|
|
ifname);
|
|
int ret = system(cmdline);
|
|
|
|
return (ret == 0) ? 0 : -1;
|
|
}
|
|
|
|
int tun_route_del_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) {
|
|
int count = 0;
|
|
struct CFG_ROUTE_ENTRY *entry = routes;
|
|
|
|
while (entry) {
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
|
|
if (entry->ip.family == AF_INET) {
|
|
if (tun_route_del(ifindex, ifname, network, entry->netmask) == 0) {
|
|
count++;
|
|
}
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d routes via ifindex=%lu", count, (unsigned long)ifindex);
|
|
return count;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Platform-independent functions
|
|
|
|
int tun_route_add_all(uint32_t ifindex, const char *ifname, struct CFG_ROUTE_ENTRY *routes) {
|
|
int count = 0;
|
|
struct CFG_ROUTE_ENTRY *entry = routes;
|
|
|
|
while (entry) {
|
|
uint32_t network = ntohl(entry->ip.addr.v4.s_addr);
|
|
if (entry->ip.family == AF_INET) {
|
|
if (tun_route_add(ifindex, ifname, network, entry->netmask) == 0) {
|
|
struct in_addr addr;
|
|
addr.s_addr = entry->ip.addr.v4.s_addr;
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added route %s/%d via %s", ip_to_str(&addr, AF_INET).str, entry->netmask, ifname);
|
|
count++;
|
|
} else {
|
|
struct in_addr addr;
|
|
addr.s_addr = entry->ip.addr.v4.s_addr;
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to add route %s/%d via %s", ip_to_str(&addr, AF_INET).str, entry->netmask, ifname);
|
|
}
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
|
|
return count;
|
|
}
|