diff options
Diffstat (limited to 'gre.c')
-rw-r--r-- | gre.c | 87 |
1 files changed, 73 insertions, 14 deletions
@@ -2,7 +2,7 @@ * gre.c - userspace GRE tunnel * * Copyright (C) 2015 - 2017, Xiaoxiao <i@pxx.io> - * Copyright (C) 2019, Mikael Magnusson <mikma@users.sourceforge.net> + * Copyright (C) 2019 - 2020, Mikael Magnusson <mikma@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> +#include <getopt.h> #include <linux/if_tun.h> #include <net/ethernet.h> #include <net/if.h> @@ -42,6 +43,7 @@ static int tun; static int sock; static struct sockaddr_storage remote; static size_t remote_len; +static short type = IFF_TUN; uint8_t buf[4096]; @@ -50,7 +52,7 @@ static void gre_ipv4(const uint8_t *buf, int n); static void gre_ipv6(const uint8_t *buf, int n, const struct sockaddr_in6 *src); static void gre_any(const uint8_t *buf, int n); static int tun_cb(void); -static int tun_new(const char *dev); +static int tun_new(short type, const char *dev); static int setnonblock(int fd); static int runas(const char *user); static int daemonize(void); @@ -60,22 +62,59 @@ int main(int argc, char **argv) { fd_set readset; - if (argc != 4) + while (1) + { + int option_index = 0; + static struct option long_options[] = { + {"tun", no_argument, 0, 'n' }, + {"tap", no_argument, 0, 'p' }, + {0, 0, 0, 0 } + }; + + char c = getopt_long(argc, argv, "np", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'n': + printf("tun\n"); + type = IFF_TUN; + break; + + case 'p': + printf("tap\n"); + type = IFF_TAP; + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + if ( (argc - optind) != 3) { - printf("usage: %s <tun> remote local\n", argv[0]); + printf("usage: %s [-n|--tun|-p|--tap] <tun> remote local\n", argv[0]); return EXIT_FAILURE; } - tun = tun_new(argv[1]); + const char *dev = argv[optind++]; + const char *remote_opt = argv[optind++]; + const char *local_opt = argv[optind++]; + + tun = tun_new(type, dev); if (tun < 0) { - printf("failed to init tun device\n"); + printf("failed to init %s device\n", type == IFF_TAP ? "tap" : "tun"); return EXIT_FAILURE; } struct sockaddr_storage local; size_t local_len = 0; - if (inet_addr_storage(argv[3], &local, &local_len)) + if (inet_addr_storage(local_opt, &local, &local_len)) { fprintf(stderr, "bad local address\n"); return EXIT_FAILURE; @@ -94,7 +133,7 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - if (inet_addr_storage(argv[2], &remote, &remote_len)) + if (inet_addr_storage(remote_opt, &remote, &remote_len)) { fprintf(stderr, "bad remote address\n"); return EXIT_FAILURE; @@ -227,18 +266,38 @@ static int tun_cb(void) return 0; } - buf[0] = 0; - buf[1] = 0; uint16_t proto = ntohs(*(uint16_t *)(buf + 2)); - if (proto != ETHERTYPE_IP && proto != ETHERTYPE_IPV6) + uint8_t *frame; + + if (type == IFF_TAP) { + size_t ethsize = 6 + 6 + 2; + frame = buf + ethsize; + n = n - ethsize; + frame[2] = buf[2]; + frame[3] = buf[3]; + } else { + frame = buf; + } + + frame[0] = 0; + frame[1] = 0; + switch (proto) { + case ETHERTYPE_IP: + case ETHERTYPE_IPV6: + break; + case ETHERTYPE_ARP: + if (type == IFF_TAP) + break; + return 0; + default: return 0; } - sendto(sock, buf, n, 0, (struct sockaddr *)&remote, remote_len); + sendto(sock, frame, n, 0, (struct sockaddr *)&remote, remote_len); return 0; } -static int tun_new(const char *dev) +static int tun_new(short type, const char *dev) { struct ifreq ifr; int fd, err; @@ -251,7 +310,7 @@ static int tun_new(const char *dev) bzero(&ifr, sizeof(struct ifreq)); - ifr.ifr_flags = IFF_TUN; + ifr.ifr_flags = type; if (*dev != '\0') { strncpy(ifr.ifr_name, dev, IFNAMSIZ); |