From 4f285bb38af149f35a1bd3e730ac6bac7b14fd53 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 14 Jan 2012 20:58:36 +0000 Subject: [PATCH] changing exit helper code to automatically do the network configuration for an exit node (by running sysctl/iptables commands as necessary) --- src/exit/exit.conf | 39 +++++++- src/exit/gnunet-daemon-exit.c | 166 ++++++++++++++++++++++---------- src/exit/gnunet-helper-exit.c | 175 ++++++++++++++++++++++++++++------ 3 files changed, 298 insertions(+), 82 deletions(-) diff --git a/src/exit/exit.conf b/src/exit/exit.conf index 83dfae888..bbf463047 100644 --- a/src/exit/exit.conf +++ b/src/exit/exit.conf @@ -1,12 +1,43 @@ [exit] CONFIG = $DEFAULTCONFIG BINARY = gnunet-daemon-exit + +# IPv6 address for the TUN interface (must be changed as this +# must be within the global IPv6 range of your system!) IPV6ADDR = 1234:1::1 + +# Prefix for our IPv6 subnet on the TUN interface. IPV6PREFIX = 32 + +# IPv4 address to use on our TUN interface (may need to be +# changed to avoid conflicts with existing addresses on your system). IPV4ADDR = 10.10.1.1 + +# Netmask for the IPv4 subnet on the TUN interface. IPV4MASK = 255.255.0.0 -IFNAME = exit-gnunet -ENABLE_UDP = NO -ENABLE_TCP = NO -# MAX_CONNECTIONS = 256 + +# Name of the (virtual) tunnel interface the exit daemon will manage +TUN_IFNAME = exit-gnunet + +# Name of the "real" interface that IPv4 traffic from this system will +# leave from; this is the name of the interface where we need to +# enable NAT on postrouting (typically something like 'eth0' or 'eth1' +# or 'wlan0'). Not needed if EXIT_IPv4 is disabled. +EXIT_IFNAME = eth0 + +# Set this to YES to allow exiting this system via IPv4 to the Internet +EXIT_IPV4 = NO + +# Set this to YES to allow exiting this system via IPv6 to the Internet +EXIT_IPV6 = NO + +# For IPv4-services offered by this peer, we need to at least enable IPv4 +ENABLE_IPV4 = NO + +# For IPv6-services offered by this peer, we need to at least enable IPv6 +ENABLE_IPV6 = NO + + +# Maximum number of concurrent connections this exit supports. +MAX_CONNECTIONS = 256 diff --git a/src/exit/gnunet-daemon-exit.c b/src/exit/gnunet-daemon-exit.c index 2f1865714..9552da155 100644 --- a/src/exit/gnunet-daemon-exit.c +++ b/src/exit/gnunet-daemon-exit.c @@ -277,6 +277,17 @@ static int ipv4_exit; */ static int ipv6_exit; +/** + * Do we support IPv4 at all on the TUN interface? + */ +static int ipv4_enabled; + +/** + * Do we support IPv6 at all on the TUN interface? + */ +static int ipv6_enabled; + + /** * Given IP information about a connection, calculate the respective * hash we would use for the 'connections_map'. @@ -1954,7 +1965,8 @@ run (void *cls, char *const *args GNUNET_UNUSED, GNUNET_APPLICATION_TYPE_END }; unsigned int app_idx; - char *ifname; + char *exit_ifname; + char *tun_ifname; char *ipv6addr; char *ipv6prefix_s; char *ipv4addr; @@ -1963,8 +1975,29 @@ run (void *cls, char *const *args GNUNET_UNUSED, struct in6_addr v6; cfg = cfg_; - ipv4_exit = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_IPV4"); - ipv6_exit = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_IPV6"); + ipv4_exit = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "EXIT_IPV4"); + ipv6_exit = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "EXIT_IPV6"); + ipv4_enabled = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_IPV4"); + ipv6_enabled = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_IPV6"); + if (ipv4_exit && (! ipv4_enabled)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Cannot enable IPv4 exit but disable IPv4 on TUN interface, will use ENABLE_IPv4=YES\n")); + ipv4_enabled = GNUNET_YES; + } + if (ipv6_exit && (! ipv6_enabled)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Cannot enable IPv6 exit but disable IPv6 on TUN interface, will use ENABLE_IPv6=YES\n")); + ipv6_enabled = GNUNET_YES; + } + if (! (ipv4_enabled || ipv6_enabled)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("No useful service enabled. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } app_idx = 0; if (GNUNET_YES == ipv4_exit) { @@ -1985,68 +2018,101 @@ run (void *cls, char *const *args GNUNET_UNUSED, max_connections = 1024; exit_argv[0] = GNUNET_strdup ("exit-gnunet"); if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IFNAME", &ifname)) + GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "TUN_IFNAME", &tun_ifname)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No entry 'IFNAME' in configuration!\n"); + "No entry 'TUN_IFNAME' in configuration!\n"); GNUNET_SCHEDULER_shutdown (); return; } - exit_argv[1] = ifname; - if ( (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6ADDR", - &ipv6addr) || - (1 != inet_pton (AF_INET6, ipv6addr, &v6))) ) + exit_argv[1] = tun_ifname; + if (ipv4_exit) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No valid entry 'IPV6ADDR' in configuration!\n"); - GNUNET_SCHEDULER_shutdown (); - return; + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "EXIT_IFNAME", &exit_ifname)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No entry 'EXIT_IFNAME' in configuration!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + exit_argv[2] = exit_ifname; } - exit_argv[2] = ipv6addr; - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6PREFIX", - &ipv6prefix_s)) + else { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No entry 'IPV6PREFIX' in configuration!\n"); - GNUNET_SCHEDULER_shutdown (); - return; + exit_argv[2] = GNUNET_strdup ("%"); } - exit_argv[3] = ipv6prefix_s; - if ( (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, "exit", - "IPV6PREFIX", - &ipv6prefix)) || - (ipv6prefix >= 127) ) + if (GNUNET_YES == ipv6_enabled) { - GNUNET_SCHEDULER_shutdown (); - return; + if ( (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6ADDR", + &ipv6addr) || + (1 != inet_pton (AF_INET6, ipv6addr, &v6))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No valid entry 'IPV6ADDR' in configuration!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + exit_argv[3] = ipv6addr; + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6PREFIX", + &ipv6prefix_s)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No entry 'IPV6PREFIX' in configuration!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + exit_argv[4] = ipv6prefix_s; + if ( (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, "exit", + "IPV6PREFIX", + &ipv6prefix)) || + (ipv6prefix >= 127) ) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + } + else + { + /* IPv6 explicitly disabled */ + exit_argv[3] = GNUNET_strdup ("-"); + exit_argv[4] = GNUNET_strdup ("-"); } - - if ( (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4ADDR", - &ipv4addr) || - (1 != inet_pton (AF_INET, ipv4addr, &v4))) ) + if (GNUNET_YES == ipv4_enabled) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No valid entry for 'IPV4ADDR' in configuration!\n"); - GNUNET_SCHEDULER_shutdown (); - return; + if ( (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4ADDR", + &ipv4addr) || + (1 != inet_pton (AF_INET, ipv4addr, &v4))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No valid entry for 'IPV4ADDR' in configuration!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + exit_argv[5] = ipv4addr; + if ( (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4MASK", + &ipv4mask) || + (1 != inet_pton (AF_INET, ipv4mask, &v4))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No valid entry 'IPV4MASK' in configuration!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + exit_argv[6] = ipv4mask; } - exit_argv[4] = ipv4addr; - if ( (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4MASK", - &ipv4mask) || - (1 != inet_pton (AF_INET, ipv4mask, &v4))) ) + else { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No valid entry 'IPV4MASK' in configuration!\n"); - GNUNET_SCHEDULER_shutdown (); - return; + /* IPv4 explicitly disabled */ + exit_argv[5] = GNUNET_strdup ("-"); + exit_argv[6] = GNUNET_strdup ("-"); } - exit_argv[5] = ipv4mask; - exit_argv[6] = NULL; + exit_argv[7] = NULL; udp_services = GNUNET_CONTAINER_multihashmap_create (65536); tcp_services = GNUNET_CONTAINER_multihashmap_create (65536); diff --git a/src/exit/gnunet-helper-exit.c b/src/exit/gnunet-helper-exit.c index 84b6e6e65..d1db2a6e6 100644 --- a/src/exit/gnunet-helper-exit.c +++ b/src/exit/gnunet-helper-exit.c @@ -19,16 +19,21 @@ */ /** - * @file exit/gnunet-helper-exit.c - * @brief the helper for exit nodes. Opens a virtual network-interface, - * sends data received on the if to stdout, sends data received on stdin to the - * interface - * @author Philipp Tölke + * @file exit/gnunet-helper-exit.c + * + * @brief the helper for exit nodes. Opens a virtual + * network-interface, sends data received on the if to stdout, sends + * data received on stdin to the interface. The code also enables + * IPv4/IPv6 forwarding and NAT on the current system (the latter on + * an interface specified on the command-line); these changes to the + * network configuration are NOT automatically undone when the program + * is stopped (this is because we cannot be sure that some other + * application didn't enable them before or after us; also, these + * changes should be mostly harmless as it simply turns the system + * into a router). * - * TODO: - * - need to add code to setup ip_forwarding and NAT (for IPv4) so that - * users don't need to ALSO do admin work; this is what will set - * gnunet-helper-exit.c apart from gnunet-helper-vpn.c + * @author Philipp Tölke + * @author Christian Grothoff * * The following list of people have reviewed this code and considered * it safe since the last modification (if you reviewed it, please @@ -54,6 +59,17 @@ */ #define MAX_SIZE 65536 +/** + * Path to 'sysctl' binary. + */ +#define SBIN_SYSCTL "/sbin/sysctl" + +/** + * Path to 'iptables' binary. + */ +#define SBIN_IPTABLES "/sbin/iptables" + + #ifndef _LINUX_IN6_H /** * This is in linux/include/net/ipv6.h, but not always exported... @@ -67,6 +83,58 @@ struct in6_ifreq #endif + +/** + * Run the given command and wait for it to complete. + * + * @param file name of the binary to run + * @param cmd command line arguments (as given to 'execv') + * @return 0 on success, 1 on any error + */ +static int +fork_and_exec (const char *file, + char *const cmd[]) +{ + int status; + pid_t pid; + pid_t ret; + + pid = fork (); + if (-1 == pid) + { + fprintf (stderr, + "fork failed: %s\n", + strerror (errno)); + return 1; + } + if (0 == pid) + { + /* we are the child process */ + (void) execv (file, cmd); + /* can only get here on error */ + fprintf (stderr, + "exec `%s' failed: %s\n", + file, + strerror (errno)); + _exit (1); + } + /* keep running waitpid as long as the only error we get is 'EINTR' */ + while ( (-1 == (ret = waitpid (pid, &status, 0))) && + (errno == EINTR) ); + if (-1 == ret) + { + fprintf (stderr, + "waitpid failed: %s\n", + strerror (errno)); + return 1; + } + if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status)))) + return 1; + /* child process completed and returned success, we're happy */ + return 0; +} + + /** * Creates a tun-interface called dev; * @@ -521,12 +589,13 @@ PROCESS_BUFFER: * Open VPN tunnel interface. * * @param argc must be 6 - * @param argv 0: binary name (gnunet-helper-vpn) - * 1: tunnel interface name (gnunet-vpn) - * 2: IPv6 address (::1) - * 3: IPv6 netmask length in bits (64) - * 4: IPv4 address (1.2.3.4) - * 5: IPv4 netmask (255.255.0.0) + * @param argv 0: binary name ("gnunet-helper-vpn") + * 1: tunnel interface name ("gnunet-vpn") + * 2: IPv4 "physical" interface name ("eth0"), or "%" to not do IPv4 NAT + * 3: IPv6 address ("::1"), or "-" to skip IPv6 + * 4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"] + * 5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4 + * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] */ int main (int argc, char **argv) @@ -535,11 +604,17 @@ main (int argc, char **argv) int fd_tun; int global_ret; - if (6 != argc) + if (7 != argc) { fprintf (stderr, "Fatal: must supply 5 arguments!\n"); return 1; } + if ( (0 == strcmp (argv[3], "-")) && + (0 == strcmp (argv[5], "-")) ) + { + fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n"); + return 1; + } strncpy (dev, argv[1], IFNAMSIZ); dev[IFNAMSIZ - 1] = '\0'; @@ -550,24 +625,66 @@ main (int argc, char **argv) return 1; } + if (0 != strcmp (argv[3], "-")) { - const char *address = argv[2]; - long prefix_len = atol (argv[3]); - - if ((prefix_len < 1) || (prefix_len > 127)) { - fprintf (stderr, "Fatal: prefix_len out of range\n"); - return 1; + const char *address = argv[3]; + long prefix_len = atol (argv[4]); + + if ((prefix_len < 1) || (prefix_len > 127)) + { + fprintf (stderr, "Fatal: prefix_len out of range\n"); + return 1; + } + set_address6 (dev, address, prefix_len); + } + { + char *const sysctl_args[] = + { + "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL + }; + if (0 != fork_and_exec (SBIN_SYSCTL, + sysctl_args)) + { + fprintf (stderr, + "Failed to enable IPv6 forwarding. Will continue anyway.\n"); + } } - - set_address6 (dev, address, prefix_len); } + if (0 != strcmp (argv[5], "-")) { - const char *address = argv[4]; - const char *mask = argv[5]; - - set_address4 (dev, address, mask); + { + const char *address = argv[5]; + const char *mask = argv[6]; + + set_address4 (dev, address, mask); + } + { + char *const sysctl_args[] = + { + "sysctl", "-w", "net.ipv4.ip_forward=1", NULL + }; + if (0 != fork_and_exec (SBIN_SYSCTL, + sysctl_args)) + { + fprintf (stderr, + "Failed to enable IPv4 forwarding. Will continue anyway.\n"); + } + } + if (0 != strcmp (argv[2], "%")) + { + char *const iptables_args[] = + { + "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL + }; + if (0 != fork_and_exec (SBIN_IPTABLES, + iptables_args)) + { + fprintf (stderr, + "Failed to enable IPv4 masquerading (NAT). Will continue anyway.\n"); + } + } } uid_t uid = getuid (); @@ -599,3 +716,5 @@ main (int argc, char **argv) close (fd_tun); return global_ret; } + +/* end of gnunet-helper-exit.c */ -- 2.25.1