aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2019-08-07 17:17:31 +0000
committerKazuki Yamaguchi <k@rhe.jp>2019-08-07 17:44:26 +0000
commit647a4db4f1a213fd1c9f493f9b4ee59f17c86a87 (patch)
treee06d9e694750f89891ca2a52377ead5d0d59cc3a
downloadulfougretap-647a4db4f1a213fd1c9f493f9b4ee59f17c86a87.tar.gz
ulfougretap: Userspace L2 GRE tunnel over UDP
Designed for OpenVZ servers running on older kernels without Foo-over-UDP.
-rw-r--r--.gitignore20
-rw-r--r--Makefile.am1
-rw-r--r--README32
-rw-r--r--configure.ac30
-rw-r--r--src/Makefile.am5
-rw-r--r--src/common.h72
-rw-r--r--src/main.c150
7 files changed, 310 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..396cee2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+Makefile.in
+Makefile
+autom4te.cache
+/aclocal.m4
+/compile
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.status
+/config.sub
+/configure
+/depcomp
+/install-sh
+/missing
+/stamp-h1
+
+src/.deps
+src/*.o
+src/ulfougretap
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..af437a6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/README b/README
new file mode 100644
index 0000000..b19946b
--- /dev/null
+++ b/README
@@ -0,0 +1,32 @@
+ulfougretap: Userspace L2 GRE tunnel over UDP
+=============================================
+
+Designed for OpenVZ servers running on older kernels without Foo-over-UDP
+support.
+
+USAGE
+-----
+
+$ autoreconf -i && ./configure && make && make install
+$ ulfougretap <dev> <local address> <local port> \
+ <peer address> <peer port>
+
+For example,
+
+ # ulfougretap l2gre0 192.168.1.2 5555 10.0.2.1 5556
+
+Does the same thing as:
+
+ # ip fou add port 5555 ipproto 47 \
+ local 192.168.1.2 peer 10.0.2.1 peer_port 5556
+ # ip link add name l2gre0 type gretap \
+ local 192.168.1.2 remote 10.0.2.1 ttl 64 \
+ encap fou encap-sport 5555 encap-dport 5556
+
+
+BUGS
+----
+
+ - IPv6 transport is currently not supported.
+ - Features of GRE such as Checksum, Key, or Sequence are not supported.
+ - IP fragment is not supported. Not tested actually. You must properly set MTU.
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..9c7c4de
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,30 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT([ulfougretap], [1.0.0], [k@rhe.jp])
+AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_SRCDIR([src/main.c])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_ERROR_AT_LINE
+AC_CHECK_FUNCS([inet_ntoa memset socket strerror strtol])
+
+AC_CONFIG_FILES([Makefile src/Makefile])
+AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..035fc85
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,5 @@
+bin_PROGRAMS = ulfougretap
+
+ulfougretap_CFLAGS = -std=gnu11 -Wall -Wextra -O3 -ggdb3
+
+ulfougretap_SOURCES = main.c common.h
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..ff9d56e
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,72 @@
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#ifndef TUN_DEBUG
+# define TUN_DEBUG 0
+#endif
+
+#define numberof(ary) (sizeof(ary) / sizeof(ary[0]))
+
+#define print_msg(type) do { \
+ va_list args; \
+ va_start(args, format); \
+ fprintf(stderr, type": "); \
+ vfprintf(stderr, format, args); \
+ fprintf(stderr, "\n"); \
+ va_end(args); \
+} while (0)
+
+static inline _Noreturn void fatal(const char *format, ...)
+{
+ print_msg("fatal");
+ exit(1);
+}
+
+static inline void error(const char *format, ...)
+{
+ print_msg("error");
+}
+
+static inline void info(const char *format, ...)
+{
+ print_msg("info");
+}
+
+static inline void debug(const char *format, ...)
+{
+ print_msg("debug");
+}
+
+static inline uint16_t parse_u16(const char *str)
+{
+ long numl = strtol(str, NULL, 10);
+ if (numl <= 0 || numl > 65535)
+ fatal("could not parse port number: %s", str);
+ return (uint16_t)numl;
+}
+
+static inline struct in_addr parse_ipv4(const char *str)
+{
+ struct in_addr tmp;
+ int ret = inet_aton(str, &tmp);
+ if (ret == 0)
+ fatal("could not parse IPv4 address: %s", str);
+ return tmp;
+}
+
+static inline void dumpbin(const uint8_t *buf, size_t x)
+{
+ size_t i;
+ for (i = 0; i < x; i++)
+ {
+ if (i > 0) printf(":");
+ printf("%02X", buf[i]);
+ }
+ printf("\n");
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..393f98b
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,150 @@
+#include "common.h"
+#include <unistd.h>
+#include <errno.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <poll.h>
+
+static int tun_fd, utun_fd;
+static struct sockaddr_in utun_peer;
+
+static void setup_tundev(const char *in_ifname)
+{
+#define DEVNETTUN "/dev/net/tun"
+
+ int fd = open(DEVNETTUN, O_RDWR);
+ if (fd < 0)
+ fatal("setup_tundev: open("DEVNETTUN"): %s", strerror(errno));
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
+ strncpy(ifr.ifr_name, in_ifname, IFNAMSIZ - 1);
+ if (ioctl(fd, TUNSETIFF, &ifr) < 0)
+ fatal("setup_tundev: ioctl(TUNSETIFF): %s", strerror(errno));
+
+ debug("setup_tundev: created tuntap interface: %s", in_ifname);
+
+ tun_fd = fd;
+}
+
+static void setup_utun(struct in_addr local_addr, uint16_t local_port,
+ struct in_addr remote_addr, uint16_t remote_port)
+{
+ int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0)
+ fatal("setup_utun: socket(): %s", strerror(errno));
+
+ struct sockaddr_in local;
+ local.sin_family = AF_INET;
+ local.sin_port = htons(local_port);
+ local.sin_addr = local_addr;
+ if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
+ fatal("setup_utun: bind(port=%d): %s", (int)local_port,
+ strerror(errno));
+
+ debug("setup_utun: created utun socket: %s:%d",
+ inet_ntoa(local_addr), (int)local_port);
+
+ struct sockaddr_in remote;
+ remote.sin_family = AF_INET;
+ remote.sin_port = htons(remote_port);
+ remote.sin_addr = remote_addr;
+
+ utun_peer = remote;
+ utun_fd = fd;
+}
+
+static void read_tundev(void)
+{
+ const ssize_t gre_header_len = 4;
+ uint8_t buf[2048];
+ // GRE header comes first, inside the UDP packet
+ buf[0] = 0;
+ buf[1] = 0;
+ *(uint16_t *)&buf[2] = htons(0x6558);
+
+ // Ethernet frame follows
+ ssize_t nread = read(tun_fd, buf + gre_header_len,
+ sizeof(buf) - gre_header_len);
+ if (nread < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return;
+ fatal("tundev: read(): %s", strerror(errno));
+ }
+
+ ssize_t nsent = sendto(utun_fd, buf, nread + gre_header_len, 0,
+ (struct sockaddr *)&utun_peer,
+ sizeof(utun_peer));
+ if (nsent != nread + gre_header_len)
+ fatal("tundev: sendto(utun): %s", strerror(errno));
+
+ if (TUN_DEBUG > 0)
+ fprintf(stderr, ">");
+}
+
+static void read_utun(void)
+{
+ const ssize_t gre_header_len = 4;
+ uint8_t buf[2048];
+ struct sockaddr_in remote;
+ socklen_t remote_len = sizeof(remote);
+
+ ssize_t nread = recvfrom(utun_fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&remote,
+ &remote_len);
+ if (nread < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return;
+ fatal("utun: read(): %s", strerror(errno));
+ }
+ if (memcmp(&utun_peer, &remote, sizeof(remote))) {
+ debug("utun: packet from unexpected peer; expected=%s:%d, "
+ "actual=%s:%d", inet_ntoa(utun_peer.sin_addr),
+ (int)utun_peer.sin_port, inet_ntoa(remote.sin_addr),
+ (int)remote.sin_port);
+ return;
+ }
+ if (nread < gre_header_len || buf[0] != 0 || buf[1] != 0 ||
+ ntohs(*(uint16_t *)&buf[2]) != 0x6558) {
+ debug("utun: packet in unexpected format");
+ return;
+ }
+
+ ssize_t nwritten = write(tun_fd, buf + gre_header_len,
+ nread - gre_header_len);
+ if (nwritten != nread - gre_header_len)
+ fatal("utun: write(tundev) failed: %s", strerror(errno));
+
+ if (TUN_DEBUG > 0)
+ fprintf(stderr, "<");
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 6)
+ fatal("usage: ulfougretap <dev> <local addr> <local port> " \
+ "<peer addr> <peer port>\n");
+
+ setup_tundev(argv[1]);
+ setup_utun(parse_ipv4(argv[2]), parse_u16(argv[3]),
+ parse_ipv4(argv[4]), parse_u16(argv[5]));
+
+ struct pollfd pollfds[2] = {
+ { .fd = tun_fd, .events = POLLIN, },
+ { .fd = utun_fd, .events = POLLIN, },
+ };
+
+ while (1) {
+ if (poll(pollfds, numberof(pollfds), -1) < 0)
+ fatal("poll(): %s", strerror(errno));
+
+ if (pollfds[0].revents)
+ read_tundev();
+ if (pollfds[1].revents)
+ read_utun();
+ }
+}