diff options
| -rw-r--r-- | src/tools/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/tools/ovpn/CMakeLists.txt | 21 | ||||
| -rw-r--r-- | src/tools/ovpn/ovpn.c | 327 | 
3 files changed, 351 insertions, 0 deletions
| diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index b81e5439..d7a4d17a 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -3,3 +3,6 @@ add_subdirectory(ocbr)  add_subdirectory(oecho)  add_subdirectory(oping)  add_subdirectory(operf) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") +  add_subdirectory(ovpn) +endif () diff --git a/src/tools/ovpn/CMakeLists.txt b/src/tools/ovpn/CMakeLists.txt new file mode 100644 index 00000000..f3a2cac8 --- /dev/null +++ b/src/tools/ovpn/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +get_filename_component(CURRENT_SOURCE_PARENT_DIR +  ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) + +include_directories(${CURRENT_SOURCE_PARENT_DIR}) + +set(SOURCE_FILES +  # Add source files here +  ovpn.c +  ) + +add_executable(ovpn ${SOURCE_FILES}) + +target_link_libraries(ovpn LINK_PUBLIC ouroboros-dev) + +install(TARGETS ovpn RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 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); +} | 
