summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorWayne Cole <waynecole339@gmail.com>2026-04-26 16:29:30 +0000
committerWayne Cole <waynecole339@gmail.com>2026-04-26 16:29:30 +0000
commitc45c35cf85f3f10dadff964165cff3e5dba5fffc (patch)
tree06005afbdd1d63691ace663e0aacedb152f50889 /main.c
downloadhoodsgate-main.tar.xz
hoodsgate-main.zip
chore: actually vcs this proj, working on it for monthsmain
Diffstat (limited to 'main.c')
-rw-r--r--main.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..39c8d47
--- /dev/null
+++ b/main.c
@@ -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);
+}