diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 165 |
1 files changed, 165 insertions, 0 deletions
@@ -0,0 +1,165 @@ +/* + * Control Plane Daemon for Tailscale Exit Node Router + * Routes traffic through eth0, Tor, or VPN based on policies + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <netinet/in.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +/* Handle control commands from clients */ +void handle_control_command(int client_fd, const char *cmd) { + char response[1024]; + char command[64], arg1[64], arg2[64], arg3[64]; + + log_event(EVENT_COMMAND_RECEIVED, 0, 0, "Command: %s", cmd); + + // Parse command + int n = sscanf(cmd, "%s %s %s %s", command, arg1, arg2, arg3); + + if (strcmp(command, "add-source-route") == 0 && n >= 3) { + // add-source-route <source_ip> <eth0|tor|vpn> [vpn_server] + struct in_addr addr; + if (inet_aton(arg1, &addr) == 0) { + snprintf(response, sizeof(response), "ERROR: Invalid IP address\n"); + send(client_fd, response, strlen(response), 0); + return; + } + + path_type_t path; + const char *vpn_server = NULL; + + if (strcmp(arg2, "eth0") == 0) + path = PATH_ETH0; + else if (strcmp(arg2, "tor") == 0) + path = PATH_TOR; + else if (strcmp(arg2, "vpn") == 0) { + path = PATH_VPN; + if (n < 4) { + snprintf(response, sizeof(response), + "ERROR: VPN server name required\n"); + send(client_fd, response, strlen(response), 0); + return; + } + vpn_server = arg3; + } else { + snprintf(response, sizeof(response), "ERROR: Invalid path type\n"); + send(client_fd, response, strlen(response), 0); + return; + } + + if (add_routing_policy(addr.s_addr, 0, path, vpn_server) == 0) { + snprintf(response, sizeof(response), "OK: Policy added\n"); + } else { + snprintf(response, sizeof(response), "ERROR: Failed to add policy\n"); + } + + } else if (strcmp(command, "add-mark-route") == 0 && n >= 3) { + // add-mark-route <mark_hex> <eth0|tor|vpn> [vpn_server] + uint32_t mark = strtoul(arg1, NULL, 16); + + path_type_t path; + const char *vpn_server = NULL; + + if (strcmp(arg2, "eth0") == 0) + path = PATH_ETH0; + else if (strcmp(arg2, "tor") == 0) + path = PATH_TOR; + else if (strcmp(arg2, "vpn") == 0) { + path = PATH_VPN; + if (n < 4) { + snprintf(response, sizeof(response), + "ERROR: VPN server name required\n"); + send(client_fd, response, strlen(response), 0); + return; + } + vpn_server = arg3; + } else { + snprintf(response, sizeof(response), "ERROR: Invalid path type\n"); + send(client_fd, response, strlen(response), 0); + return; + } + + if (add_routing_policy(0, mark, path, vpn_server) == 0) { + snprintf(response, sizeof(response), "OK: Mark policy added\n"); + } else { + snprintf(response, sizeof(response), + "ERROR: Failed to add mark policy\n"); + } + + } else if (strcmp(command, "list") == 0) { + snprintf(response, sizeof(response), "Active policies: %d\n", + cp_state.num_policies); + send(client_fd, response, strlen(response), 0); + + for (int i = 0; i < cp_state.num_policies; i++) { + routing_policy_t *p = &cp_state.policies[i]; + if (!p->active) + continue; + + snprintf(response, sizeof(response), + "[%d] src=%s mark=0x%x type=%s table=%d\n", i, + p->source_ip ? inet_ntoa((struct in_addr){p->source_ip}) : "any", + p->mark, + p->path_type == PATH_ETH0 ? "eth0" + : p->path_type == PATH_TOR ? "tor" + : p->vpn_server, + p->routing_table_id); + send(client_fd, response, strlen(response), 0); + } + return; + + } else if (strcmp(command, "status") == 0) { + snprintf(response, sizeof(response), + "Control Plane Status\n" + "Policies: %d/%d\n" + "VPN Configs: %d/%d\n" + "Events logged: %d\n", + cp_state.num_policies, MAX_POLICIES, cp_state.num_vpn_configs, + MAX_VPN_CONFIGS, cp_state.journal.count); + + } else if (strcmp(command, "events") == 0) { + // Query event journal + int max_events = 20; + if (n >= 2) { + max_events = atoi(arg1); + if (max_events > EVENT_JOURNAL_SIZE) + max_events = EVENT_JOURNAL_SIZE; + } + + event_entry_t events[EVENT_JOURNAL_SIZE]; + int count; + get_recent_events(events, max_events, &count); + + snprintf(response, sizeof(response), "Recent %d events:\n", count); + send(client_fd, response, strlen(response), 0); + + for (int i = 0; i < count; i++) { + event_entry_t *e = &events[i]; + snprintf(response, sizeof(response), + "[%lu.%06lu] data1=0x%x data2=0x%x %s\n", e->timestamp / 1000000, + e->timestamp % 1000000, e->data1, e->data2, e->message); + send(client_fd, response, strlen(response), 0); + } + return; + + } else { + snprintf(response, sizeof(response), + "ERROR: Unknown command. Available: add-source-route, " + "add-mark-route, list, status, events\n"); + } + + send(client_fd, response, strlen(response), 0); +} |
