summaryrefslogtreecommitdiffhomepage
path: root/gre.c
diff options
context:
space:
mode:
Diffstat (limited to 'gre.c')
-rw-r--r--gre.c87
1 files changed, 73 insertions, 14 deletions
diff --git a/gre.c b/gre.c
index 6ded69d..dff0a99 100644
--- a/gre.c
+++ b/gre.c
@@ -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);