/* * Control Plane Daemon for Tailscale Exit Node Router * Routes traffic through eth0, Tor, or VPN based on policies */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 [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 [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); }