summaryrefslogtreecommitdiff
path: root/src/tools/ovpn/ovpn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/ovpn/ovpn.c')
-rw-r--r--src/tools/ovpn/ovpn.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/src/tools/ovpn/ovpn.c b/src/tools/ovpn/ovpn.c
new file mode 100644
index 00000000..d5e9d024
--- /dev/null
+++ b/src/tools/ovpn/ovpn.c
@@ -0,0 +1,327 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2018
+ *
+ * Ouroboros VPN
+ *
+ * Dimitri Staessens <dimitri.staessens@ugent.be>
+ * Sander Vrijders <sander.vrijders@ugent.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _POSIX_C_SOURCE 200109L
+
+#include <ouroboros/dev.h>
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <getopt.h>
+
+#define BUF_SIZE 65536
+
+int t_fd;
+int o_fd;
+
+static void usage(void)
+{
+ printf("Usage: ovpn [OPTION]...\n"
+ "Sends TCP/IP traffic over Ouroboros\n\n"
+ " -n, --name Run as client, name of ovpn "
+ "server to connect to\n"
+ " -i, --ip IP address to give to TUN device\n"
+ " -m, --mask Subnet mask to give to TUN device\n"
+ "\n"
+ " --help Display this help text and exit\n");
+}
+
+static int tun_open(char * dev,
+ uint32_t ip,
+ uint32_t mask)
+{
+ struct ifreq ifr;
+ int fd;
+ int s;
+ int ret;
+ char * clonedev = "/dev/net/tun";
+ struct sockaddr_in * addr;
+
+ fd = open(clonedev, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_TUN;
+
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret < 0)
+ goto fail;
+
+ strcpy(dev, ifr.ifr_name);
+
+ /* Now get the i/f up and running. */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ goto fail;
+
+ /* Set IP address. */
+ ifr.ifr_addr.sa_family = AF_INET;
+ addr = (struct sockaddr_in *) &ifr.ifr_addr;
+ addr->sin_addr.s_addr = ip;
+
+ if (ioctl(s, SIOCSIFADDR, &ifr))
+ goto fail_ioctl;
+
+ /* Set subnet mask. */
+ addr->sin_addr.s_addr = mask;
+ if (ioctl(s, SIOCSIFNETMASK, &ifr))
+ goto fail_ioctl;
+
+ /* Bring interface up. */
+ if (ioctl(s, SIOCGIFFLAGS, &ifr))
+ goto fail_ioctl;
+ ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
+ if (ioctl(s, SIOCSIFFLAGS, &ifr))
+ goto fail_ioctl;
+
+ close(s);
+
+ return fd;
+
+ fail_ioctl:
+ close(s);
+ fail:
+ close(fd);
+ return -1;
+}
+
+void * o_reader(void * o)
+{
+ char buf[BUF_SIZE];
+ int len = 0;
+
+ (void) o;
+
+ while (true) {
+ len = flow_read(o_fd, buf, BUF_SIZE);
+ if (len <= 0)
+ continue;
+
+ if (write(t_fd, buf, len))
+ continue;
+ }
+}
+
+void * t_reader(void * o)
+{
+ char buf[BUF_SIZE];
+ int len = 0;
+
+ (void) o;
+
+ while (true) {
+ len = read(t_fd, buf, BUF_SIZE);
+ if (len <= 0)
+ continue;
+
+ if (flow_write(o_fd, buf, len))
+ continue;
+ }
+}
+
+static int check_mask(uint32_t mask)
+{
+ if (mask == 0)
+ return 0;
+
+ return ((~mask & (~mask + 1)) != 0);
+}
+
+int main(int argc,
+ char ** argv)
+{
+ char * name = NULL;
+ uint32_t ip = 0;
+ int32_t mask = -1;
+ char dev[IFNAMSIZ];
+ pthread_t t_thr;
+ pthread_t o_thr;
+ sigset_t sigset;
+ int sig;
+ int c;
+
+ static struct option long_options[] =
+ {{"ip", required_argument, NULL, 'i'},
+ {"mask", required_argument, NULL, 'm'},
+ {"name", optional_argument, NULL, 'n'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGINT);
+ sigaddset(&sigset, SIGQUIT);
+ sigaddset(&sigset, SIGHUP);
+ sigaddset(&sigset, SIGTERM);
+
+ if (geteuid() != 0) {
+ printf("ovpn must be run as root.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt_long(argc, argv, "i:m:n:h",
+ long_options, NULL)) != -1) {
+ switch (c) {
+ case 'i':
+ if (inet_pton(AF_INET, optarg, &ip) != 1) {
+ printf("Invalid IP address: %s.\n\n", optarg);
+ goto fail_usage;
+ }
+ break;
+ case 'm':
+ if (inet_pton(AF_INET, optarg, &mask) != 1 ||
+ check_mask(htonl(mask))) {
+ printf("Invalid subnet mask: %s.\n\n", optarg);
+ goto fail_usage;
+ }
+ break;
+ case 'n':
+ name = optarg;
+ break;
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind < argc) {
+ printf("Unknown arguments specified: ");
+ while (optind < argc)
+ printf("%s ", argv[optind++]);
+ printf("\n\n");
+ goto fail_usage;
+ }
+
+ if (ip == 0) {
+ printf("Please specify an IP address.\n\n");
+ goto fail_usage;
+ }
+
+ if (mask == -1) {
+ printf("Please specify a subnetmask.\n\n");
+ goto fail_usage;
+ }
+
+ if (name != NULL) {
+ printf("Allocating a flow to %s.\n", name);
+
+ o_fd = flow_alloc(name, NULL, NULL);
+ if (o_fd < 0) {
+ printf("Failed to allocate flow.\n");
+ goto fail_alloc;
+ }
+ } else {
+ printf("Waiting for a new flow...\n");
+
+ o_fd = flow_accept(NULL, NULL);
+ if (o_fd < 0) {
+ printf("Failed to accept flow.\n");
+ goto fail_alloc;
+ }
+ }
+
+ printf("Flow allocated.\n");
+
+ t_fd = tun_open(dev, ip, mask);
+ if (t_fd < 0) {
+ printf("Failed to open tunnel device.\n");
+ goto fail_tun;
+ }
+
+ printf("Tunnel device name is %s.\n", dev);
+
+ pthread_sigmask(SIG_BLOCK, &sigset, NULL);
+
+ printf("Starting read/write threads.\n");
+
+ if (pthread_create(&o_thr, NULL, o_reader, NULL))
+ goto fail_thread;
+
+ if (pthread_create(&t_thr, NULL, t_reader, NULL))
+ goto fail_thread2;
+
+ while (true) {
+ if (sigwait(&sigset, &sig) != 0) {
+ printf("Bad signal.\n");
+ continue;
+ }
+
+ printf("Shutting down...\n");
+ break;
+ }
+
+ pthread_cancel(o_thr);
+ pthread_cancel(t_thr);
+
+ pthread_join(o_thr, NULL);
+ pthread_join(t_thr, NULL);
+
+ close(t_fd);
+ flow_dealloc(o_fd);
+
+ pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+
+ exit(EXIT_SUCCESS);
+ fail_usage:
+ usage();
+ exit(EXIT_FAILURE);
+ fail_thread2:
+ pthread_cancel(o_thr);
+ pthread_join(o_thr, NULL);
+ fail_thread:
+ close(t_fd);
+ fail_tun:
+ flow_dealloc(o_fd);
+ fail_alloc:
+ exit(EXIT_FAILURE);
+}