Compare commits

..

9 Commits

Author SHA1 Message Date
angt
a3aa6fc4fb Add option bench 2016-06-02 13:44:24 +00:00
angt
6f36424d12 Code cleanup 2016-04-26 06:18:12 +00:00
angt
fa9301da16 Show tun_name in all states and fix state_send() 2016-04-26 06:13:14 +00:00
angt
c154a00358 Use a bigger timeout when buffers are empty 2016-04-12 14:11:16 +00:00
Adrien Gallouët
4246b510d2 Never trust frank :) 2016-03-30 15:41:53 +02:00
Adrien Gallouët
c71db48ae1 Add a nice geek logo for glorytun 2016-03-23 17:44:40 +01:00
Adrien Gallouët
e3cce8aeb9 AES-NI is no longer mandatory 2016-03-18 21:19:24 +01:00
angt
ad0d205ff3 Add option chacha20
Chacha20 will be automatically used if aesni is not available.
2016-03-18 14:43:30 +00:00
angt
030087cb27 Add mptcp option 2016-02-25 14:38:51 +00:00
43 changed files with 2268 additions and 2171 deletions

View File

@@ -1,24 +0,0 @@
name: Build
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
- name: Build glorytun
run: |
git submodule update --init --recursive
./sodium.sh
make prefix=. install
- uses: actions/upload-artifact@v1
with:
name: bin
path: ./bin

3
.gitignore vendored
View File

@@ -10,7 +10,4 @@ configure
build-aux
.deps
.dirstamp
.static
glorytun
build*
VERSION

8
.gitmodules vendored
View File

@@ -1,8 +0,0 @@
[submodule "mud"]
path = mud
url = https://github.com/angt/mud.git
ignore = dirty
[submodule "argz"]
path = argz
url = https://github.com/angt/argz.git
ignore = dirty

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015-2019, Adrien Gallouët <adrien@gallouet.fr>
Copyright (c) 2015-2016, angt
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1,35 +0,0 @@
NAME := glorytun
VERSION := $(shell ./version.sh)
DIST := $(NAME)-$(VERSION)
DESTDIR ?=
CC ?= gcc
INSTALL ?= install
prefix ?= /usr
CFLAGS ?= -std=c11 -O2 -Wall -fstack-protector-strong
FLAGS := $(CFLAGS) $(LDFLAGS) $(CPPFLAGS)
FLAGS += -DPACKAGE_NAME=\"$(NAME)\" -DPACKAGE_VERSION=\"$(VERSION)\"
FLAGS += -I.static/$(CROSS)/libsodium-stable/src/libsodium/include
FLAGS += -L.static/$(CROSS)/libsodium-stable/src/libsodium/.libs
SRC := argz/argz.c mud/mud.c mud/aegis256/aegis256.c $(wildcard src/*.c)
.PHONY: $(NAME)
$(NAME):
@echo "Building $(NAME)"
@$(CC) $(FLAGS) -o $(NAME) $(SRC) -lsodium -lm
.PHONY: install
install: $(NAME)
@echo "Installing $(NAME)"
@$(INSTALL) -m 755 -d $(DESTDIR)$(prefix)/bin
@$(INSTALL) -m 755 -s $(NAME) $(DESTDIR)$(prefix)/bin
.PHONY: dist
dist:
@echo "Building $(DIST).tar.gz"
@(git --git-dir=.git ls-files --recurse-submodules -- ':!:.*' ':!:**/.*' && echo VERSION) | ( \
tar zcf $(DIST).tar.gz -T- --transform 's:^:$(DIST)/:' || \
tar zcf $(DIST).tar.gz -T- -s ':^:$(DIST)/:' ) 2>/dev/null

View File

@@ -5,37 +5,24 @@ bin_PROGRAMS = glorytun
glorytun_CFLAGS = $(libsodium_CFLAGS)
glorytun_LDADD = $(libsodium_LIBS)
glorytun_SOURCES = \
argz/argz.c \
argz/argz.h \
mud/mud.c \
mud/mud.h \
mud/aegis256/aegis256.c \
mud/aegis256/aegis256.h \
src/bench.c \
src/bind.c \
src/common.c \
src/common.h \
src/ctl.c \
src/ctl.h \
src/iface.c \
src/iface.h \
src/common.c \
src/buffer.h \
src/ip.h \
src/keygen.c \
src/main.c \
src/path.c \
src/set.c \
src/show.c \
src/str.h \
src/main.c \
src/option.c \
src/option.h \
src/tun.c \
src/tun.h
src/tun.h \
src/db.c \
src/db.h \
src/state.c \
src/state.h
EXTRA_DIST = \
LICENSE \
README.md \
VERSION \
autogen.sh \
meson.build \
mud/LICENSE \
mud/README.md \
systemd \
version.sh

135
README.md
View File

@@ -1,133 +1,22 @@
# Glorytun
# π₁(Glorytun)=ℤ²
Glorytun is a small, simple and secure VPN over [mud](https://github.com/angt/mud).
Small, Simple and Stupid TCP VPN.
## Compatibility
#### Work In Progress
Glorytun only depends on [libsodium](https://github.com/jedisct1/libsodium) version >= 1.0.4.
Which can be installed on a wide variety of systems.
This code will probably format your harddisk!
Linux is the platform of choice but the code is standard so it should be easily ported on other posix systems.
It was successfully tested on OpenBSD, FreeBSD and MacOS.
#### Build and Install
IPv4 and IPv6 are supported.
Glorytun depends on [libsodium](https://github.com/jedisct1/libsodium) version >= 1.0.4.
## Features
To build and install the latest version:
The key features of Glorytun come directly from mud:
* **Fast and highly secure**
The use of UDP and [libsodium](https://github.com/jedisct1/libsodium) allows you to secure
your communications without impacting performance.
Glorytun uses AEGIS-256 only if AES-NI is available otherwise ChaCha20Poly1305 is used.
If you are not cpu bounded, you can force the use of ChaCha20Poly1305 for higher security.
All messages are encrypted, authenticated and marked with a timestamp.
Perfect forward secrecy is also implemented with ECDH over Curve25519.
* **Multipath and active failover**
This is the main feature of Glorytun that allows to build an SD-WAN like service.
This allows a TCP connection to explore and exploit multiple links without being disconnected.
Aggregation should work on all conventional links, only very high latency (+500ms) links are not recommended for now.
* **Traffic shaping**
Shaping is very important in network, it allows to keep a low latency without sacrificing the bandwidth.
It also helps the multipath scheduler to make better decisions.
Currently it must be configured by hand, but soon Glorytun will do it for you.
* **Path MTU discovery without ICMP**
Bad MTU configuration is a very common problem in the world of VPN.
As it is critical, Glorytun will try to setup it correctly by guessing its value.
It doesn't rely on ICMP Next-hop MTU to avoid black holes.
In asymmetric situations the minimum MTU is selected.
## Caveats
Glorytun is strongly secure by default and protects against replay attacks,
the clock between the client and the server must be synchronized.
By default, an offset of 10min is accepted.
## Build and Install
You will need `git`, `make`, `gcc` and `libsodium`:
$ sudo apt install git make gcc libsodium-dev # debian based
$ sudo yum install git make gcc libsodium-devel # redhat based
To build and install the latest release from github:
$ git clone https://github.com/angt/glorytun --recursive
$ git clone https://github.com/angt/glorytun
$ cd glorytun
$ sudo make install
This will install the binary in `/usr/bin` by default.
The more classical autotools suite is also available.
## Usage
Just run `glorytun` with no arguments to view the list of available commands:
```
$ glorytun
available commands:
show show tunnel info
bench start a crypto bench
bind start a new tunnel
set change tunnel properties
keygen generate a new secret key
path manage paths
version show version
```
Use the keyword `help` after a command to show its usage.
## Mini HowTo
Glorytun does not touch the configuration of its network interface (except for the MTU),
It is up to the user to do it according to the tools available
on his system (systemd-networkd, netifd, ...).
This also allows a wide variety of configurations.
To start a server:
# (umask 066; glorytun keygen > my_secret_key)
# glorytun bind 0.0.0.0 keyfile my_secret_key &
You should now have an unconfigured network interface (let's say `tun0`).
For example, the simplest setup with `ifconfig`:
# ifconfig tun0 10.0.1.1 pointopoint 10.0.1.2 up
To check if the server is running, simply call `glorytun show`.
It will show you all of the running tunnels.
To start a new client, you need to get the secret key generated for the server.
Then simply call:
# glorytun bind 0.0.0.0 to SERVER_IP keyfile my_secret_key &
# ifconfig tun0 10.0.1.2 pointopoint 10.0.1.1 up
Now you have to setup your path, let's say you have an ADSL link that can do 1Mbit upload and 20Mbit download then call:
# glorytun path up LOCAL_IPADDR rate tx 1mbit rx 20mbit
Again, to check if your path is working, you can watch its status with `glorytun path`.
You should now be able to ping your server with `ping 10.0.1.1`.
If you use systemd-networkd, you can easily setup your tunnels with the helper program `glorytun-setup`.
## Thanks
* @jedisct1 for all his help and the code for MacOS/BSD.
* The team OTB (@bessa, @gregdel, @pouulet, @sduponch and @simon) for all tests and discussions.
* OVH to support this soft :)
---
$ ./autogen.sh
$ ./configure
$ make
# make install
For feature requests and bug reports, please create an [issue](https://github.com/angt/glorytun/issues).

1
argz

Submodule argz deleted from 47ad9daf43

View File

@@ -4,19 +4,18 @@ AC_INIT([glorytun],
[https://github.com/angt/glorytun/issues],
[glorytun],
[https://github.com/angt/glorytun])
AC_DEFINE_UNQUOTED([VERSION_MAJOR], [m4_esyscmd([./version.sh major])])
AC_CONFIG_SRCDIR([src/common.h])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.12 -Wall -Werror foreign tar-ustar subdir-objects])
AM_INIT_AUTOMAKE([1.9 -Wall -Werror foreign tar-ustar subdir-objects])
AM_DEP_TRACK
AM_SILENT_RULES([yes])
AM_PROG_CC_C_O
AC_PROG_CC_C99
AC_USE_SYSTEM_EXTENSIONS
AC_SEARCH_LIBS([getaddrinfo], [resolv nsl])
AC_SEARCH_LIBS([socket], [socket])
AC_SEARCH_LIBS([fmin], [m])
AC_CHECK_LIB([rt], [clock_gettime])
AC_CHECK_FUNCS([clock_gettime])
PKG_CHECK_MODULES([libsodium], [libsodium >= 1.0.4])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

@@ -1,6 +1,6 @@
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
# serial 12 (pkg-config-0.29.2)
dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
dnl serial 11 (pkg-config-0.29)
dnl
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
dnl
@@ -41,7 +41,7 @@ dnl
dnl See the "Since" comment for each macro you use to see what version
dnl of the macros you require.
m4_defun([PKG_PREREQ],
[m4_define([PKG_MACROS_VERSION], [0.29.2])
[m4_define([PKG_MACROS_VERSION], [0.29])
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ
@@ -142,7 +142,7 @@ AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $2])
AC_MSG_CHECKING([for $1])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
@@ -152,11 +152,11 @@ and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
AC_MSG_RESULT([no])
AC_MSG_RESULT([no])
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
else
else
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
@@ -173,7 +173,7 @@ installed software in a non-standard prefix.
_PKG_TEXT])[]dnl
])
elif test $pkg_failed = untried; then
AC_MSG_RESULT([no])
AC_MSG_RESULT([no])
m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full

View File

@@ -1,63 +0,0 @@
project('glorytun', 'c',
version: run_command('./version.sh').stdout(),
license: 'BSD-3-Clause',
default_options : [
'buildtype=debugoptimized',
'c_std=gnu99'
]
)
cc = meson.get_compiler('c')
prefix = get_option('prefix')
bindir = join_paths(prefix, get_option('bindir'))
conf_data = configuration_data()
conf_data.set('prefix', prefix)
conf_data.set('bindir', bindir)
add_global_arguments('-DPACKAGE_VERSION="'+meson.project_version()+'"', language : 'c')
add_global_arguments('-DPACKAGE_NAME="'+meson.project_name()+'"', language : 'c')
executable('glorytun', install: true,
sources: [
'argz/argz.c',
'mud/mud.c',
'mud/aegis256/aegis256.c',
'src/bench.c',
'src/bind.c',
'src/common.c',
'src/ctl.c',
'src/iface.c',
'src/keygen.c',
'src/main.c',
'src/path.c',
'src/set.c',
'src/show.c',
'src/tun.c',
],
dependencies: [
dependency('libsodium', version : '>=1.0.4'),
cc.find_library('m', required : false)
]
)
systemd = dependency('systemd', required: false)
if systemd.found()
systemdutildir = systemd.get_pkgconfig_variable('systemdutildir')
configure_file(
input: 'systemd/glorytun@.service.in',
output: 'glorytun@.service',
configuration: conf_data,
install_dir: join_paths(systemdutildir, 'system')
)
install_data('systemd/glorytun.network',
install_dir: join_paths(systemdutildir, 'network'))
install_data('systemd/glorytun-client.network',
install_dir: join_paths(systemdutildir, 'network'))
install_data('systemd/glorytun-run',
install_dir: bindir)
install_data('systemd/glorytun-setup',
install_dir: bindir)
endif

1
mud

Submodule mud deleted from b59ab48407

View File

@@ -1,29 +0,0 @@
#!/bin/sh
mkdir -p .static
cd .static || exit 1
file=LATEST.tar.gz
url=https://download.libsodium.org/libsodium/releases
dir="$PWD"
[ -f "$file" ] || wget -q "$url/$file" -O "$file"
[ -f "$file" ] || curl -SsfLO "$url/$file"
[ -f "$file" ] || {
echo "Couldn't download $url/$file"
exit 1
}
if [ "$1" ]; then
mkdir -p "$1"
cd "$1" || exit 1
fi
rm -rf libsodium-stable
tar zxf "$dir/$file"
cd libsodium-stable || exit 1
NPROC=$(sysctl -n hw.ncpu || nproc) 2>/dev/null
./configure ${1+--host=$1} --enable-minimal --disable-dependency-tracking --enable-static --disable-shared
make "-j$((NPROC+1))"

View File

@@ -1,164 +0,0 @@
#include "common.h"
#include <math.h>
#include <sodium.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#if defined __APPLE__
#include <mach/mach_time.h>
#endif
#include "../argz/argz.h"
#include "../mud/aegis256/aegis256.h"
#define STR_S(X) (((X) > 1) ? "s" : "")
#define NPUBBYTES 32
#define KEYBYTES 32
#define ABYTES 16
static unsigned long long
gt_now(void)
{
#if defined __APPLE__
static mach_timebase_info_data_t mtid;
if (!mtid.denom)
mach_timebase_info(&mtid);
return (mach_absolute_time() * mtid.numer / mtid.denom) / 1000ULL;
#elif defined CLOCK_MONOTONIC
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
return (unsigned long long)tv.tv_sec * 1000000ULL
+ (unsigned long long)tv.tv_nsec / 1000ULL;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (unsigned long long)tv.tv_sec * 1000000ULL
+ (unsigned long long)tv.tv_usec;
#endif
}
int
gt_bench(int argc, char **argv)
{
unsigned long precision = 10;
size_t bufsize = 64 * 1024;
unsigned long duration = 1000;
struct argz bench_argz[] = {
{"aes|chacha", NULL, NULL, argz_option},
{"precision", "EXPONENT", &precision, argz_ulong},
{"bufsize", "BYTES", &bufsize, argz_bytes},
{"duration", "SECONDS", &duration, argz_time},
{NULL}};
if (argz(bench_argz, argc, argv))
return 1;
if (duration == 0 || bufsize == 0)
return 0;
if (sodium_init() == -1) {
gt_log("sodium init failed\n");
return 1;
}
duration /= 1000;
int term = isatty(1);
int aes = argz_is_set(bench_argz, "aes");
int chacha = argz_is_set(bench_argz, "chacha");
if (!aegis256_is_available()) {
if (aes) {
gt_log("aes is not available on your platform\n");
return 1;
}
chacha = 1;
}
unsigned char *buf = calloc(1, bufsize + ABYTES);
if (!buf) {
perror("calloc");
return 1;
}
unsigned char npub[NPUBBYTES];
unsigned char key[KEYBYTES];
randombytes_buf(npub, sizeof(npub));
randombytes_buf(key, sizeof(key));
if (term) {
printf("\n");
printf(" %-10s %s\n", "bench", chacha ? "chacha20poly1305" : "aegis256");
printf(" %-10s %s\n", "libsodium", sodium_version_string());
printf("\n");
printf(" %-10s 2^(-%lu)\n", "precision", precision);
printf(" %-10s %zu byte%s\n", "bufsize", bufsize, STR_S(bufsize));
printf(" %-10s %lu second%s\n", "duration", duration, STR_S(duration));
printf("\n");
printf("------------------------------------------------------------\n");
printf(" %3s %9s %14s %14s %14s\n", "2^n", "min", "avg", "max", "delta");
printf("------------------------------------------------------------\n");
}
for (int i = 0; !gt_quit && bufsize >> i; i++) {
unsigned long long total_dt = 0ULL;
size_t total_bytes = 0;
double mbps = 0.0;
double mbps_min = INFINITY;
double mbps_max = 0.0;
double mbps_dlt = INFINITY;
while (!gt_quit && mbps_dlt > ldexp(mbps, -(int)precision)) {
unsigned long long now = gt_now();
double mbps_old = mbps;
size_t bytes = 0;
gt_alarm = 0;
alarm((unsigned int)duration);
while (!gt_quit && !gt_alarm) {
if (chacha) {
crypto_aead_chacha20poly1305_encrypt(
buf, NULL, buf, 1ULL << i, NULL, 0, NULL, npub, key);
} else {
aegis256_encrypt(
buf, NULL, buf, 1ULL << i, NULL, 0, npub, key);
}
bytes += 1ULL << i;
}
total_dt += gt_now() - now;
total_bytes += bytes;
mbps = ((double)total_bytes * 8.0) / (double)total_dt;
mbps_min = fmin(mbps_min, mbps);
mbps_max = fmax(mbps_max, mbps);
mbps_dlt = fabs(mbps_old - mbps);
if (term) {
printf("\r %3i %9.2f Mbps %9.2f Mbps %9.2f Mbps %9.2e",
i, mbps_min, mbps, mbps_max, mbps_dlt);
fflush(stdout);
}
}
if (term) {
printf("\n");
} else {
printf("%i %.2f %.2f %.2f\n", i, mbps_min, mbps, mbps_max);
}
}
printf("\n");
free(buf);
return 0;
}

View File

@@ -1,390 +0,0 @@
#include "common.h"
#include "ctl.h"
#include "iface.h"
#include "ip.h"
#include "str.h"
#include "tun.h"
#include <fcntl.h>
#include <stdio.h>
#include <sys/select.h>
#include "../argz/argz.h"
#include "../mud/mud.h"
#include <sodium.h>
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
static int
fd_set_nonblock(int fd)
{
if (fd == -1)
return 0;
int ret;
do {
ret = fcntl(fd, F_GETFL, 0);
} while (ret == -1 && errno == EINTR);
int flags = (ret == -1) ? 0 : ret;
do {
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
} while (ret == -1 && errno == EINTR);
return ret;
}
static int
gt_setup_secretkey(struct mud *mud, const char *keyfile)
{
int fd;
do {
fd = open(keyfile, O_RDONLY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
gt_log("couldn't open %s: %s\n", keyfile, strerror(errno));
return -1;
}
unsigned char key[32];
char buf[2 * sizeof(key)];
size_t size = 0;
while (size < sizeof(buf)) {
ssize_t r = read(fd, &buf[size], sizeof(buf) - size);
if (r <= (ssize_t)0) {
if (r && (errno == EAGAIN || errno == EINTR))
continue;
break;
}
size += (size_t)r;
}
close(fd);
if (size != sizeof(buf)) {
gt_log("couldn't read secret key\n");
return -1;
}
if (gt_fromhex(key, sizeof(key), buf, sizeof(buf))) {
gt_log("secret key is not valid\n");
return -1;
}
mud_set_key(mud, key, sizeof(key));
return 0;
}
static size_t
gt_setup_mtu(struct mud *mud, size_t old, const char *tun_name)
{
size_t mtu = mud_get_mtu(mud);
if (mtu == old)
return mtu;
if (iface_set_mtu(tun_name, mtu) == -1)
gt_log("couldn't setup MTU at %zu on device %s\n", mtu, tun_name);
return mtu;
}
int
gt_bind(int argc, char **argv)
{
struct sockaddr_storage bind_addr = { .ss_family = AF_INET };
struct sockaddr_storage peer_addr = { 0 };
unsigned short bind_port = 5000;
unsigned short peer_port = bind_port;
const char *dev = NULL;
const char *keyfile = NULL;
struct argz toz[] = {
{NULL, "IPADDR", &peer_addr, argz_addr},
{NULL, "PORT", &peer_port, argz_ushort},
{NULL}};
struct argz bindz[] = {
{NULL, "IPADDR", &bind_addr, argz_addr},
{NULL, "PORT", &bind_port, argz_ushort},
{"to", NULL, &toz, argz_option},
{"dev", "NAME", &dev, argz_str},
{"keyfile", "FILE", &keyfile, argz_str},
{"chacha", NULL, NULL, argz_option},
{"persist", NULL, NULL, argz_option},
{NULL}};
if (argz(bindz, argc, argv))
return 1;
if (str_empty(keyfile)) {
gt_log("a keyfile is needed!\n");
return 1;
}
gt_set_port((struct sockaddr *)&bind_addr, bind_port);
gt_set_port((struct sockaddr *)&peer_addr, peer_port);
int chacha = argz_is_set(bindz, "chacha");
int persist = argz_is_set(bindz, "persist");
if (sodium_init() == -1) {
gt_log("couldn't init sodium\n");
return 1;
}
unsigned char hashkey[crypto_shorthash_KEYBYTES];
randombytes_buf(hashkey, sizeof(hashkey));
struct mud *mud = mud_create((struct sockaddr *)&bind_addr);
const int mud_fd = mud_get_fd(mud);
if (mud_fd == -1) {
gt_log("couldn't create mud\n");
return 1;
}
if (gt_setup_secretkey(mud, keyfile))
return 1;
if (!chacha && mud_set_aes(mud)) {
gt_log("AES is not available, enjoy ChaCha20!\n");
chacha = 1;
}
char tun_name[64];
const int tun_fd = tun_create(tun_name, sizeof(tun_name), dev);
if (tun_fd == -1) {
gt_log("couldn't create tun device\n");
return 1;
}
size_t mtu = gt_setup_mtu(mud, 0, tun_name);
if (tun_set_persist(tun_fd, persist) == -1) {
gt_log("couldn't %sable persist mode on device %s\n",
persist ? "en" : "dis", tun_name);
}
if (peer_addr.ss_family) {
if (mud_peer(mud, (struct sockaddr *)&peer_addr)) {
perror("mud_peer");
return 1;
}
}
const int ctl_fd = ctl_create(GT_RUNDIR, tun_name);
if (ctl_fd == -1) {
gt_log("couldn't create "GT_RUNDIR"/%s: %s\n",
tun_name, strerror(errno));
return 1;
}
if (//fd_set_nonblock(tun_fd) ||
//fd_set_nonblock(mud_fd) ||
fd_set_nonblock(ctl_fd)) {
gt_log("couldn't setup non-blocking fds\n");
return 1;
}
const long pid = (long)getpid();
gt_log("running on device %s as pid %li\n", tun_name, pid);
fd_set rfds, wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
int tun_can_read = 0;
int tun_can_write = 0;
int mud_can_read = 0;
int mud_can_write = 0;
int last_fd = MAX(tun_fd, mud_fd);
last_fd = 1 + MAX(last_fd, ctl_fd);
unsigned char buf[4096];
while (!gt_quit) {
if (tun_can_write) {
FD_CLR(tun_fd, &wfds);
} else {
FD_SET(tun_fd, &wfds);
}
if (mud_can_write) {
FD_CLR(mud_fd, &wfds);
} else {
FD_SET(mud_fd, &wfds);
}
if (tun_can_read) {
FD_CLR(tun_fd, &rfds);
} else {
FD_SET(tun_fd, &rfds);
}
if (mud_can_read) {
FD_CLR(mud_fd, &rfds);
} else {
FD_SET(mud_fd, &rfds);
}
FD_SET(ctl_fd, &rfds);
struct timeval tv = {
.tv_usec = 100000,
};
if (mud_can_read && tun_can_write) {
tv.tv_usec = 0;
} else if (tun_can_read && mud_can_write) {
long send_wait = mud_send_wait(mud);
if (send_wait >= 0)
tv.tv_usec = send_wait * 1000;
}
const int ret = select(last_fd, &rfds, &wfds, NULL, &tv);
if (ret == -1) {
if (errno == EBADF) {
perror("select");
break;
}
continue;
}
if (FD_ISSET(tun_fd, &rfds))
tun_can_read = 1;
if (FD_ISSET(tun_fd, &wfds))
tun_can_write = 1;
if (FD_ISSET(mud_fd, &rfds))
mud_can_read = 1;
if (FD_ISSET(mud_fd, &wfds))
mud_can_write = 1;
mtu = gt_setup_mtu(mud, mtu, tun_name);
if (tun_can_read && mud_can_write && !mud_send_wait(mud)) {
struct ip_common ic;
int r = tun_read(tun_fd, buf, sizeof(buf));
if (r > 0 && !ip_get_common(&ic, buf, r)) {
mud_send(mud, buf, (size_t)r, ic.tc);
mud_can_write = 0;
}
tun_can_read = 0;
}
if (mud_can_read && tun_can_write) {
int r = mud_recv(mud, buf, sizeof(buf));
if (r > 0 && ip_is_valid(buf, r)) {
tun_write(tun_fd, buf, (size_t)r);
tun_can_write = 0;
}
mud_can_read = 0;
}
if (FD_ISSET(ctl_fd, &rfds)) {
struct ctl_msg req, res = {.reply = 1};
struct sockaddr_storage ss;
socklen_t sl = sizeof(ss);
ssize_t r = recvfrom(ctl_fd, &req, sizeof(req), 0,
(struct sockaddr *)&ss, &sl);
if (r == (ssize_t)sizeof(req)) {
res.type = req.type;
switch (req.type) {
case CTL_NONE:
break;
case CTL_STATE:
if (mud_set_state(mud, (struct sockaddr *)&req.path.addr,
req.path.state, req.path.rate_tx, req.path.rate_rx))
res.ret = errno;
break;
case CTL_PATH_STATUS:
{
unsigned count = 0;
struct mud_path *paths = mud_get_paths(mud, &count);
if (!paths) {
res.ret = errno;
break;
}
res.ret = EAGAIN;
for (unsigned i = 0; i < count; i++) {
memcpy(&res.path_status, &paths[i], sizeof(struct mud_path));
if (sendto(ctl_fd, &res, sizeof(res), 0,
(const struct sockaddr *)&ss, sl) == -1)
perror("sendto(ctl)");
}
free(paths);
res.ret = 0;
}
break;
case CTL_MTU:
mud_set_mtu(mud, req.mtu);
res.mtu = mtu = gt_setup_mtu(mud, mtu, tun_name);
break;
case CTL_TC:
if (mud_set_tc(mud, req.tc))
res.ret = errno;
break;
case CTL_KXTIMEOUT:
if (mud_set_keyx_timeout(mud, req.ms))
res.ret = errno;
break;
case CTL_TIMETOLERANCE:
if (mud_set_time_tolerance(mud, req.ms))
res.ret = errno;
break;
case CTL_STATUS:
memcpy(res.status.tun_name, tun_name, sizeof(tun_name)); // XXX
res.status.pid = pid;
res.status.mtu = mtu;
res.status.chacha = chacha;
res.status.bind = bind_addr;
res.status.peer = peer_addr;
break;
}
if (sendto(ctl_fd, &res, sizeof(res), 0,
(const struct sockaddr *)&ss, sl) == -1)
perror("sendto(ctl)");
} else if (r == -1 && errno != EAGAIN) {
perror("recvfrom(ctl)");
}
}
}
if (gt_reload && tun_fd >= 0)
tun_set_persist(tun_fd, 1);
mud_delete(mud);
ctl_delete(ctl_fd);
return 0;
}

66
src/buffer.h Normal file
View File

@@ -0,0 +1,66 @@
#pragma once
#include "common.h"
typedef struct buffer buffer_t;
struct buffer {
uint8_t *data;
uint8_t *read;
uint8_t *write;
uint8_t *end;
};
static inline void buffer_setup (buffer_t *buffer, void *data, size_t size)
{
if (!data)
data = malloc(ALIGN(size));
buffer->data = data;
buffer->read = data;
buffer->write = data;
buffer->end = data;
buffer->end += size;
}
static inline void buffer_format (buffer_t *buffer)
{
buffer->write = buffer->data;
buffer->read = buffer->data;
}
_pure_
static inline size_t buffer_size (buffer_t *buffer)
{
return buffer->end-buffer->data;
}
_pure_
static inline size_t buffer_write_size (buffer_t *buffer)
{
return buffer->end-buffer->write;
}
_pure_
static inline size_t buffer_read_size (buffer_t *buffer)
{
return buffer->write-buffer->read;
}
static inline void buffer_shift (buffer_t *buffer)
{
if (buffer->read==buffer->data)
return;
if (buffer->read==buffer->write) {
buffer_format(buffer);
return;
}
const size_t size = buffer_read_size(buffer);
memmove(buffer->data, buffer->read, size);
buffer->read = buffer->data;
buffer->write = buffer->data+size;
}

View File

@@ -1,10 +1,22 @@
#include "common.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdarg.h>
void
gt_log(const char *fmt, ...)
int gt_print (const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = vfprintf(stdout, fmt, ap);
va_end(ap);
if (ret<0)
return 0;
return ret;
}
void gt_log (const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@@ -12,20 +24,34 @@ gt_log(const char *fmt, ...)
va_end(ap);
}
int
gt_tohex(char *dst, size_t dst_size, const uint8_t *src, size_t src_size)
void gt_fatal (const char *fmt, ...)
{
if (_0_(!dst_size))
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
void gt_na (const char *name)
{
gt_log("%s is not available on your platform\n", name);
}
int gt_tohex (char *dst, size_t dst_size, const uint8_t *src, size_t src_size)
{
if _0_(!dst_size)
return -1;
if (_0_(((dst_size - 1) / 2) < src_size))
if _0_(((dst_size-1)/2)<src_size)
return -1;
static const char tbl[] = "0123456789ABCDEF";
for (size_t i = 0; i < src_size; i++) {
*dst++ = tbl[0xF & (src[i] >> 4)];
*dst++ = tbl[0xF & (src[i])];
for (size_t i=0; i<src_size; i++) {
*dst++ = tbl[0xF&(src[i]>>4)];
*dst++ = tbl[0xF&(src[i])];
}
*dst = 0;
@@ -33,84 +59,38 @@ gt_tohex(char *dst, size_t dst_size, const uint8_t *src, size_t src_size)
return 0;
}
_const_ static inline int
fromhex(const char c)
_const_
static inline int fromhex (const char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c>='0' && c<='9')
return c-'0';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c>='A' && c<='F')
return c-'A'+10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c>='a' && c<='f')
return c-'a'+10;
return -1;
}
int
gt_fromhex(uint8_t *dst, size_t dst_size, const char *src, size_t src_size)
int gt_fromhex (uint8_t *dst, size_t dst_size, const char *src, size_t src_size)
{
if (_0_(src_size & 1))
if _0_(src_size&1)
return -1;
if (_0_(dst_size < (src_size / 2)))
if _0_(dst_size<(src_size/2))
return -1;
for (size_t i = 0; i < src_size; i += 2) {
for (size_t i=0; i<src_size; i+=2) {
const int a = fromhex(src[i]);
const int b = fromhex(src[i + 1]);
const int b = fromhex(src[i+1]);
if (_0_(a == -1 || b == -1))
if _0_(a==-1 || b==-1)
return -1;
*dst++ = (uint8_t)((a << 4) | b);
*dst++ = (a<<4)|b;
}
return 0;
}
void
gt_set_port(struct sockaddr *sa, uint16_t port)
{
switch (sa->sa_family) {
case AF_INET:
((struct sockaddr_in *)sa)->sin_port = htons(port);
break;
case AF_INET6:
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
break;
}
}
uint16_t
gt_get_port(struct sockaddr *sa)
{
switch (sa->sa_family) {
case AF_INET:
return ntohs(((struct sockaddr_in *)sa)->sin_port);
case AF_INET6:
return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
}
return 0;
}
int
gt_toaddr(char *str, size_t size, struct sockaddr *sa)
{
if (str)
str[0] = 0;
switch (sa->sa_family) {
case AF_INET:
return -!inet_ntop(AF_INET,
&((struct sockaddr_in *)sa)->sin_addr, str, (socklen_t)size);
case AF_INET6:
return -!inet_ntop(AF_INET6,
&((struct sockaddr_in6 *)sa)->sin6_addr, str, (socklen_t)size);
}
errno = EAFNOSUPPORT;
return -1;
}

View File

@@ -1,30 +1,11 @@
#pragma once
#if defined __linux__ && !defined _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <arpa/inet.h>
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "glorytun"
#endif
#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION "0.0.0"
#endif
#ifndef GT_RUNDIR
#define GT_RUNDIR "/run/" PACKAGE_NAME
#endif
#define COUNT(x) (sizeof(x)/sizeof(x[0]))
#define ALIGN_SIZE (1<<4)
@@ -48,29 +29,10 @@
#define _const_ __attribute__ ((const))
#define _align_(...) __attribute__ ((aligned(__VA_ARGS__)))
#undef MAX
#define MAX(x,y) ({ __typeof__(x) X=(x); __typeof__(y) Y=(y); X > Y ? X : Y; })
#undef MIN
#define MIN(x,y) ({ __typeof__(x) X=(x); __typeof__(y) Y=(y); X < Y ? X : Y; })
extern volatile sig_atomic_t gt_alarm;
extern volatile sig_atomic_t gt_reload;
extern volatile sig_atomic_t gt_quit;
int gt_print (const char *, ...) _printf_(1,2);
void gt_log (const char *, ...) _printf_(1,2);
void gt_fatal (const char *, ...) _printf_(1,2) _noreturn_;
void gt_na (const char *);
int gt_tohex (char *, size_t, const uint8_t *, size_t);
int gt_fromhex (uint8_t *, size_t, const char *, size_t);
void gt_set_port (struct sockaddr *, uint16_t);
uint16_t gt_get_port (struct sockaddr *);
int gt_toaddr (char *, size_t, struct sockaddr *);
int gt_show (int, char **);
int gt_bind (int, char **);
int gt_path (int, char **);
int gt_keygen (int, char **);
int gt_bench (int, char **);
int gt_set (int, char **);

169
src/ctl.c
View File

@@ -1,169 +0,0 @@
#include "common.h"
#include "ctl.h"
#include "str.h"
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
int
ctl_reply(int fd, struct ctl_msg *res, struct ctl_msg *req)
{
if ((send(fd, req, sizeof(struct ctl_msg), 0) == -1) ||
(recv(fd, res, sizeof(struct ctl_msg), 0) == -1))
return -1;
if (res->type != req->type || !res->reply) {
errno = EBADMSG;
return -1;
}
if (res->ret) {
errno = res->ret;
return -1;
}
return 0;
}
static int
ctl_setsun(struct sockaddr_un *dst, const char *dir, const char *file)
{
struct sockaddr_un sun = {
.sun_family = AF_UNIX,
};
int ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dir, file);
if (ret <= 0 || (size_t)ret >= sizeof(sun.sun_path)) {
errno = EINVAL;
return -1;
}
if (dst)
*dst = sun;
return 0;
}
static int
ctl_bind(int fd, const char *dir, const char *file)
{
char name[10] = { [0] = '.' };
struct sockaddr_un sun;
if (str_empty(file)) {
unsigned pid = (unsigned)getpid();
for (size_t i = 1; i < sizeof(name) - 1; i++, pid >>= 4)
name[i] = "uncopyrightables"[pid & 15];
file = name;
}
if (ctl_setsun(&sun, dir, file))
return -1;
if (unlink(sun.sun_path) && errno != ENOENT)
return -1;
return bind(fd, (struct sockaddr *)&sun, sizeof(sun));
}
void
ctl_delete(int fd)
{
struct sockaddr_storage ss = { 0 };
socklen_t sslen = sizeof(ss);
if ((getsockname(fd, (struct sockaddr *)&ss, &sslen) == 0) &&
(ss.ss_family == AF_UNIX))
unlink(((struct sockaddr_un *)&ss)->sun_path);
close(fd);
}
int
ctl_create(const char *dir, const char *file)
{
if (str_empty(dir)) {
errno = EINVAL;
return -1;
}
if (mkdir(dir, 0700) == -1 && errno != EEXIST)
return -1;
int fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (ctl_bind(fd, dir, file)) {
int err = errno;
close(fd);
errno = err;
return -1;
}
return fd;
}
int
ctl_connect(const char *dir, const char *file)
{
DIR *dp = NULL;
if (str_empty(dir)) {
errno = EINVAL;
return -1;
}
if (!file) {
if (dp = opendir(dir), !dp)
return -1;
struct dirent *d = NULL;
while (d = readdir(dp), d) {
if (d->d_name[0] == '.')
continue;
if (file) {
closedir(dp);
return CTL_ERROR_MANY;
}
file = &d->d_name[0];
}
if (!file) {
closedir(dp);
return CTL_ERROR_NONE;
}
}
struct sockaddr_un sun;
const int ret = ctl_setsun(&sun, dir, file);
if (dp) {
int err = errno;
closedir(dp);
errno = err;
}
if (ret)
return -1;
int fd = ctl_create(dir, NULL);
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) {
int err = errno;
ctl_delete(fd);
errno = err;
return -1;
}
return fd;
}

View File

@@ -1,49 +0,0 @@
#pragma once
#include "../mud/mud.h"
#include <sys/socket.h>
#define CTL_ERROR_NONE (-2)
#define CTL_ERROR_MANY (-3)
enum ctl_type {
CTL_NONE = 0,
CTL_STATE,
CTL_STATUS,
CTL_MTU,
CTL_TC,
CTL_KXTIMEOUT,
CTL_TIMETOLERANCE,
CTL_PATH_STATUS,
};
struct ctl_msg {
enum ctl_type type;
int reply, ret;
union {
struct {
struct sockaddr_storage addr;
enum mud_state state;
unsigned long rate_tx;
unsigned long rate_rx;
} path;
struct mud_path path_status;
struct {
char tun_name[64];
long pid;
size_t mtu;
int chacha;
struct sockaddr_storage bind;
struct sockaddr_storage peer;
} status;
size_t mtu;
int tc;
unsigned long ms;
};
};
int ctl_create (const char *, const char *);
int ctl_connect (const char *, const char *);
int ctl_reply (int, struct ctl_msg *, struct ctl_msg *);
void ctl_delete (int);

155
src/db.c Normal file
View File

@@ -0,0 +1,155 @@
#include "common.h"
#include "db.h"
#include "str.h"
#define CBIT(X) (1&(intptr_t)(X))
#define CBIT_PTR(X) (uint8_t *)(1|(intptr_t)(X))
#define CBIT_NODE(X) (struct node *)(1^(intptr_t)(X))
struct node {
uint8_t *child[2];
uint32_t point;
};
_pure_
static inline size_t db_size (const uint8_t *a)
{
return (a[0]?:str_len((char *)a+1))+1;
}
_pure_
static inline size_t db_cmp (const uint8_t *a, const uint8_t *b)
{
const size_t size = a[0];
if (size!=b[0])
return 1;
if (!size) {
size_t i = str_cmp((char *)a+1, (char *)b+1);
return i?i+1:0;
}
for (size_t i=1; i<=size; i++) {
if (a[i]!=b[i])
return i+1;
}
return 0;
}
_pure_
static inline int db_dir (const uint32_t point, uint8_t *data, const size_t size)
{
const size_t pos = point>>8;
if (pos>=size)
return 0;
return ((point|data[pos])&255)==255;
}
uint8_t *db_search (uint8_t **p, uint8_t *data)
{
if _0_(!*p)
return NULL;
uint8_t *r = *p;
const size_t size = db_size(data);
while (CBIT(r)) {
struct node *node = CBIT_NODE(r);
r = node->child[db_dir(node->point, data, size)];
}
if (!db_cmp(r, data))
return r;
return NULL;
}
uint8_t *db_insert (uint8_t **p, uint8_t *data)
{
if _0_(CBIT(data))
return NULL;
if _0_(!*p) {
*p = data;
return data;
}
uint8_t *r = *p;
size_t size = db_size(data);
while (CBIT(r)) {
struct node *node = CBIT_NODE(r);
r = node->child[db_dir(node->point, data, size)];
}
const size_t diff = db_cmp(r, data);
if _0_(!diff)
return r;
const size_t pos = diff-1;
const uint8_t mask = ~((1u<<31)>>CLZ(r[pos]^data[pos]));
const size_t point = (pos<<8)|mask;
while (CBIT(*p)) {
struct node *node = CBIT_NODE(*p);
if (node->point>point)
break;
p = node->child+db_dir(node->point, data, size);
}
struct node *node = malloc(sizeof(struct node));
if _0_(!node)
return NULL;
const int dir = (mask|r[pos])==255;
node->child[dir] = *p;
node->child[1-dir] = data;
node->point = point;
*p = CBIT_PTR(node);
return data;
}
uint8_t *db_remove (uint8_t **p, uint8_t *data)
{
if _0_(!*p)
return NULL;
const size_t size = db_size(data);
uint8_t **p_old = NULL;
struct node *node = NULL;
int dir = 0;
while (CBIT(*p)) {
p_old = p;
node = CBIT_NODE(*p);
dir = db_dir(node->point, data, size);
p = node->child+dir;
}
if _0_(db_cmp(data, *p))
return NULL;
uint8_t *r = *p;
if (p_old) {
*p_old = node->child[1-dir];
free(node);
} else {
*p = NULL;
}
return r;
}

7
src/db.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
uint8_t *db_search (uint8_t **, uint8_t *);
uint8_t *db_insert (uint8_t **, uint8_t *);
uint8_t *db_remove (uint8_t **, uint8_t *);

View File

@@ -1,39 +0,0 @@
#include "common.h"
#include "iface.h"
#include <stdio.h>
#include <net/if.h>
#include <sys/ioctl.h>
int
iface_set_mtu(const char *dev_name, size_t mtu)
{
if (mtu > (size_t)0xFFFF) {
errno = EINVAL;
return -1;
}
struct ifreq ifr = {
.ifr_mtu = (int)mtu,
};
int ret = snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", dev_name);
if (ret <= 0 || (size_t)ret >= sizeof(ifr.ifr_name)) {
errno = EINVAL;
return -1;
}
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
return -1;
ret = ioctl(fd, SIOCSIFMTU, &ifr);
int err = errno;
close(fd);
errno = err;
return ret;
}

View File

@@ -1,3 +0,0 @@
#pragma once
int iface_set_mtu (const char *, size_t);

View File

@@ -3,95 +3,37 @@
#include <stdint.h>
struct ip_common {
uint8_t tc;
uint8_t version;
uint8_t proto;
struct { // data are not reordered
union {
unsigned char v6[16];
struct {
unsigned char zero[10];
unsigned char ff[2];
unsigned char v4[4];
};
};
unsigned char port[2];
} src, dst;
uint8_t hdr_size;
uint16_t size;
};
static inline int
ip_read16(const uint8_t *src)
_pure_
static inline uint8_t ip_get_version (const uint8_t *data, size_t size)
{
return ((int)src[1]) | (((int)src[0]) << 8);
}
static inline uint8_t
ip_get_version(const uint8_t *data, int size)
{
if (size < 20)
if (size<20)
return 0;
return data[0] >> 4;
return data[0]>>4;
}
static inline int
ip_is_valid(const uint8_t *data, int size)
static inline int ip_get_common (struct ip_common *ic, const uint8_t *data, size_t size)
{
switch (ip_get_version(data, size)) {
case 4: return size == ip_read16(&data[2]);
case 6: return size == ip_read16(&data[4]) + 40;
}
ic->version = ip_get_version(data, size);
return 0;
}
static inline int
ip_get_common(struct ip_common *ic, const uint8_t *data, int size)
{
switch (ip_get_version(data, size)) {
switch (ic->version) {
case 4:
ic->tc = data[1];
ic->proto = data[9];
if (size == ip_read16(&data[2])) {
const int hdrsize = (data[0] & 0xF) << 2;
memset(ic->src.zero, 0, sizeof(ic->src.zero));
memset(ic->src.ff, 0xff, sizeof(ic->src.ff));
memcpy(ic->src.v4, &data[12], sizeof(ic->src.v4));
memset(ic->dst.zero, 0, sizeof(ic->dst.zero));
memset(ic->dst.ff, 0xff, sizeof(ic->dst.ff));
memcpy(ic->dst.v4, &data[16], sizeof(ic->dst.v4));
switch (ic->proto) {
case 6: // tcp
case 17: // udp
memcpy(ic->src.port, &data[hdrsize], sizeof(ic->src.port));
memcpy(ic->dst.port, &data[hdrsize + 2], sizeof(ic->dst.port));
break;
default:
memset(ic->src.port, 0, sizeof(ic->src.port));
memset(ic->dst.port, 0, sizeof(ic->dst.port));
}
return 0;
}
break;
ic->hdr_size = (data[0]&0xF)<<2;
ic->size = ((data[2]<<8)|data[3]);
return 0;
case 6:
ic->tc = (uint8_t)((data[0] << 4) | (data[1] >> 4));
ic->proto = data[6];
if (size == ip_read16(&data[4]) + 40) {
memcpy(ic->src.v6, &data[8], sizeof(ic->src.v6));
memcpy(ic->dst.v6, &data[24], sizeof(ic->dst.v6));
switch (ic->proto) {
case 6: // tcp
case 17: // udp
memcpy(ic->src.port, &data[40], sizeof(ic->src.port));
memcpy(ic->dst.port, &data[42], sizeof(ic->dst.port));
break;
default:
memset(ic->src.port, 0, sizeof(ic->src.port));
memset(ic->dst.port, 0, sizeof(ic->dst.port));
}
return 0;
}
break;
ic->hdr_size = 40;
ic->size = ((data[4]<<8)|data[5])+40;
return 0;
}
return 1;
return -1;
}

View File

@@ -1,22 +0,0 @@
#include "common.h"
#include <sodium.h>
#include <stdio.h>
int
gt_keygen(int argc, char **argv)
{
if (sodium_init() == -1) {
gt_log("sodium init failed\n");
return 1;
}
unsigned char key[32];
randombytes_buf(key, sizeof(key));
char buf[2 * sizeof(key) + 1];
gt_tohex(buf, sizeof(buf), key, sizeof(key));
printf("%s\n", buf);
return 0;
}

1611
src/main.c

File diff suppressed because it is too large Load Diff

140
src/option.c Normal file
View File

@@ -0,0 +1,140 @@
#include "common.h"
#include "option.h"
#include "str.h"
int option_str (void *data, int argc, char **argv)
{
if (argc<2 || str_empty(argv[1])) {
gt_print("option `%s' need a string argument\n", argv[0]);
return -1;
}
memcpy(data, &argv[1], sizeof(argv[1]));
return 1;
}
int option_long (void *data, int argc, char **argv)
{
if (argc<2 || str_empty(argv[1])) {
gt_print("option `%s' need an integer argument\n", argv[0]);
return -1;
}
errno = 0;
char *end;
long val = strtol(argv[1], &end, 0);
if (errno || argv[1]==end) {
gt_print("argument `%s' is not a valid integer\n", argv[1]);
return -1;
}
memcpy(data, &val, sizeof(val));
return 1;
}
int option_is_set (struct option *opts, const char *name)
{
for (int k=0; opts[k].name; k++) {
if (!str_cmp(opts[k].name, name))
return opts[k].set;
}
return 0;
}
int option_option (void *data, int argc, char **argv)
{
if (!data)
return 0;
struct option *opts = (struct option *)data;
for (int k=0; opts[k].name; k++)
opts[k].set = 0;
for (int i=1; i<argc; i++) {
int found = 0;
for (int k=0; opts[k].name; k++) {
if (str_cmp(opts[k].name, argv[i]))
continue;
if (opts[k].set) {
gt_print("option `%s' is already set\n", opts[k].name);
return -1;
}
int ret = opts[k].call(opts[k].data, argc-i, &argv[i]);
if (ret<0)
return -1;
opts[k].set = 1;
i += ret;
found = 1;
break;
}
if (!found)
return i-1;
}
return argc;
}
static int option_usage (struct option *opts, int slen)
{
if (!opts)
return 0;
int len = 0;
for (int k=0; opts[k].name; k++) {
if (len>40) {
gt_print("\n%*s", slen, "");
len = 0;
}
len += gt_print(" [%s", opts[k].name);
if (opts[k].call==option_option) {
len += option_usage((struct option *)opts[k].data, slen+len);
} else {
len += gt_print(" ARG");
}
len += gt_print("]");
}
return len;
}
int option (struct option *opts, int argc, char **argv)
{
int ret = option_option(opts, argc, argv);
if (ret==argc)
return 0;
if (ret<0 || ret+1>=argc)
return 1;
gt_print("option `%s' is unknown\n", argv[ret+1]);
int slen = gt_print("usage: %s", argv[0]);
if (slen>40) {
slen = 12;
gt_print("\n%*s", slen, "");
}
option_usage(opts, slen);
gt_print("\n");
return 1;
}

15
src/option.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
struct option {
char *name;
void *data;
int (*call) (void *, int, char **);
int set;
};
int option_option (void *, int, char **);
int option_str (void *, int, char **);
int option_long (void *, int, char **);
int option_is_set (struct option *, const char *);
int option (struct option *, int, char **);

View File

@@ -1,215 +0,0 @@
#include "common.h"
#include "ctl.h"
#include "str.h"
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include "../argz/argz.h"
static void
gt_path_print_status(struct mud_path *path, int term)
{
char bindstr[INET6_ADDRSTRLEN];
char publstr[INET6_ADDRSTRLEN];
char peerstr[INET6_ADDRSTRLEN];
gt_toaddr(bindstr, sizeof(bindstr),
(struct sockaddr *)&path->local_addr);
gt_toaddr(publstr, sizeof(publstr),
(struct sockaddr *)&path->r_addr);
gt_toaddr(peerstr, sizeof(peerstr),
(struct sockaddr *)&path->addr);
const char *statestr = NULL;
switch (path->state) {
case MUD_UP: statestr = "UP"; break;
case MUD_BACKUP: statestr = "BACKUP"; break;
case MUD_DOWN: statestr = "DOWN"; break;
default: return;
}
const char *statusstr = path->ok ? "OK" : "DEGRADED";
printf(term ? "path %s\n"
" status: %s\n"
" bind: %s port %"PRIu16"\n"
" public: %s port %"PRIu16"\n"
" peer: %s port %"PRIu16"\n"
" mtu: %zu bytes\n"
" rtt: %.3f ms\n"
" rttvar: %.3f ms\n"
" rate tx: %"PRIu64" bytes/sec\n"
" rate rx: %"PRIu64" bytes/sec\n"
" total tx: %"PRIu64" packets\n"
" total rx: %"PRIu64" packets\n"
: "path %s %s"
" %s %"PRIu16
" %s %"PRIu16
" %s %"PRIu16
" %zu"
" %.3f %.3f"
" %"PRIu64
" %"PRIu64
" %"PRIu64
" %"PRIu64
"\n",
statestr,
statusstr,
bindstr[0] ? bindstr : "-",
gt_get_port((struct sockaddr *)&path->local_addr),
publstr[0] ? publstr : "-",
gt_get_port((struct sockaddr *)&path->r_addr),
peerstr[0] ? peerstr : "-",
gt_get_port((struct sockaddr *)&path->addr),
path->mtu.ok,
(double)path->rtt.val / 1e3,
(double)path->rtt.var / 1e3,
path->rate_tx,
path->rate_rx,
path->send.total,
path->recv.total);
}
static int
gt_path_cmp_addr(struct sockaddr_storage *a, struct sockaddr_storage *b)
{
if (a->ss_family != b->ss_family)
return 1;
if (a->ss_family == AF_INET) {
struct sockaddr_in *A = (struct sockaddr_in *)a;
struct sockaddr_in *B = (struct sockaddr_in *)b;
return ((memcmp(&A->sin_addr, &B->sin_addr, sizeof(A->sin_addr))));
}
if (a->ss_family == AF_INET6) {
struct sockaddr_in6 *A = (struct sockaddr_in6 *)a;
struct sockaddr_in6 *B = (struct sockaddr_in6 *)b;
return ((memcmp(&A->sin6_addr, &B->sin6_addr, sizeof(A->sin6_addr))));
}
return 1;
}
static int
gt_path_status(int fd, int state, struct sockaddr_storage *addr)
{
struct ctl_msg req = {
.type = CTL_PATH_STATUS,
}, res = {0};
if (send(fd, &req, sizeof(struct ctl_msg), 0) == -1)
return -1;
struct mud_path path[MUD_PATH_MAX];
int count = 0;
while (1) {
if (recv(fd, &res, sizeof(struct ctl_msg), 0) == -1)
return -1;
if (res.type != req.type) {
errno = EBADMSG;
return -1;
}
if (res.ret == EAGAIN) {
memcpy(&path[count], &res.path_status, sizeof(struct mud_path));
count++;
} else if (res.ret) {
errno = res.ret;
return -1;
} else break;
}
int term = isatty(1);
for (int i = 0; i < count; i++) {
if ((state == MUD_EMPTY || path[i].state == state) &&
(!addr->ss_family || !gt_path_cmp_addr(addr, &path[i].local_addr)))
gt_path_print_status(&path[i], term);
}
return 0;
}
int
gt_path(int argc, char **argv)
{
const char *dev = NULL;
struct ctl_msg req = {
.type = CTL_STATE,
.path = {
.state = MUD_EMPTY,
},
}, res = {0};
struct argz ratez[] = {
{"tx", "BYTES/SEC", &req.path.rate_tx, argz_bytes},
{"rx", "BYTES/SEC", &req.path.rate_rx, argz_bytes},
{NULL}};
struct argz pathz[] = {
{NULL, "IPADDR", &req.path.addr, argz_addr},
{"dev", "NAME", &dev, argz_str},
{"up|backup|down", NULL, NULL, argz_option},
{"rate", NULL, &ratez, argz_option},
{NULL}};
if (argz(pathz, argc, argv))
return 1;
int fd = ctl_connect(GT_RUNDIR, dev);
if (fd < 0) {
switch (fd) {
case -1:
perror("path");
break;
case CTL_ERROR_NONE:
gt_log("no device\n");
break;
case CTL_ERROR_MANY:
gt_log("please choose a device\n");
break;
default:
gt_log("couldn't connect\n");
}
return 1;
}
int set_rate = argz_is_set(pathz, "rate");
if (set_rate && !req.path.addr.ss_family) {
gt_log("please specify a path\n");
return 1;
}
if (argz_is_set(pathz, "up")) {
req.path.state = MUD_UP;
} else if (argz_is_set(pathz, "backup")) {
req.path.state = MUD_BACKUP;
} else if (argz_is_set(pathz, "down")) {
req.path.state = MUD_DOWN;
}
int ret;
if (!req.path.addr.ss_family ||
(req.path.state == MUD_EMPTY && !set_rate)) {
ret = gt_path_status(fd, req.path.state, &req.path.addr);
} else {
ret = ctl_reply(fd, &res, &req);
}
if (ret == -1)
perror("path");
ctl_delete(fd);
return !!ret;
}

166
src/set.c
View File

@@ -1,166 +0,0 @@
#include "common.h"
#include "ctl.h"
#include "str.h"
#include <stdio.h>
#include <sys/socket.h>
#include "../argz/argz.h"
static int
gt_set_mtu(int fd, size_t mtu)
{
struct ctl_msg res, req = {
.type = CTL_MTU,
.mtu = mtu,
};
int ret = ctl_reply(fd, &res, &req);
if (ret) {
perror("set mtu");
return 1;
}
printf("mtu set to %zu\n", res.mtu);
return 0;
}
static int
gt_set_kxtimeout(int fd, unsigned long ms)
{
struct ctl_msg res, req = {
.type = CTL_KXTIMEOUT,
.ms = ms,
};
int ret = ctl_reply(fd, &res, &req);
if (ret) {
perror("set kxtimeout");
return 1;
}
return 0;
}
static int
gt_set_timetolerance(int fd, unsigned long ms)
{
struct ctl_msg res, req = {
.type = CTL_TIMETOLERANCE,
.ms = ms,
};
int ret = ctl_reply(fd, &res, &req);
if (ret) {
perror("set timetolerance");
return 1;
}
return 0;
}
static int
gt_set_tc(int fd, int tc)
{
struct ctl_msg res, req = {
.type = CTL_TC,
.tc = tc,
};
int ret = ctl_reply(fd, &res, &req);
if (ret) {
perror("set tc");
return 1;
}
return 0;
}
static int
gt_argz_tc(void *data, int argc, char **argv)
{
if (argc < 1 || !argv[0])
return -1;
int val = 0;
const char *s = argv[0];
if ((s[0] == 'C') && (s[1] == 'S') &&
(s[2] >= '0') && (s[2] <= '7') && !s[3]) {
val = (s[2] - '0') << 3;
} else if ((s[0] == 'A') && (s[1] == 'F') &&
(s[2] >= '1') && (s[2] <= '4') &&
(s[3] >= '1') && (s[3] <= '3') && !s[4]) {
val = ((s[2] - '0') << 3) | ((s[3] - '0') << 1);
} else if ((s[0] == 'E') && (s[1] == 'F') && !s[2]) {
val = 46;
} else return -1;
if (data)
*(int *)data = val;
return 1;
}
int
gt_set(int argc, char **argv)
{
const char *dev = NULL;
size_t mtu;
int tc;
unsigned long kxtimeout;
unsigned long timetolerance;
struct argz pathz[] = {
{"dev", "NAME", &dev, argz_str},
{"mtu", "BYTES", &mtu, argz_bytes},
{"tc", "CS|AF|EF", &tc, gt_argz_tc},
{"kxtimeout", "SECONDS", &kxtimeout, argz_time},
{"timetolerance", "SECONDS", &timetolerance, argz_time},
{NULL}};
if (argz(pathz, argc, argv))
return 1;
int fd = ctl_connect(GT_RUNDIR, dev);
if (fd < 0) {
switch (fd) {
case -1:
perror("set");
break;
case CTL_ERROR_NONE:
gt_log("no device\n");
break;
case CTL_ERROR_MANY:
gt_log("please choose a device\n");
break;
default:
gt_log("couldn't connect\n");
}
return 1;
}
int ret = 0;
if (argz_is_set(pathz, "mtu"))
ret |= gt_set_mtu(fd, mtu);
if (argz_is_set(pathz, "tc"))
ret |= gt_set_tc(fd, tc);
if (argz_is_set(pathz, "kxtimeout"))
ret |= gt_set_kxtimeout(fd, kxtimeout);
if (argz_is_set(pathz, "timetolerance"))
ret |= gt_set_timetolerance(fd, timetolerance);
ctl_delete(fd);
return ret;
}

View File

@@ -1,117 +0,0 @@
#include "common.h"
#include "ctl.h"
#include "str.h"
#include "../argz/argz.h"
#include <stdio.h>
#include <sys/socket.h>
#include <dirent.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
static int
gt_show_status(int fd)
{
struct ctl_msg res, req = {.type = CTL_STATUS};
if (ctl_reply(fd, &res, &req))
return -1;
char bindstr[INET6_ADDRSTRLEN];
char peerstr[INET6_ADDRSTRLEN];
gt_toaddr(bindstr, sizeof(bindstr),
(struct sockaddr *)&res.status.bind);
int server = gt_toaddr(peerstr, sizeof(peerstr),
(struct sockaddr *)&res.status.peer);
int term = isatty(1);
if (server) {
printf(term ? "server %s:\n"
" pid: %li\n"
" bind: %s port %"PRIu16"\n"
" mtu: %zu\n"
" cipher: %s\n"
: "server %s"
" %li"
" %s %"PRIu16
" %zu"
" %s"
"\n",
res.status.tun_name,
res.status.pid,
bindstr[0] ? bindstr : "-",
gt_get_port((struct sockaddr *)&res.status.bind),
res.status.mtu,
res.status.chacha ? "chacha20poly1305" : "aes256gcm");
} else {
printf(term ? "client %s:\n"
" pid: %li\n"
" bind: %s port %"PRIu16"\n"
" peer: %s port %"PRIu16"\n"
" mtu: %zu\n"
" cipher: %s\n"
: "client %s"
" %li"
" %s %"PRIu16
" %s %"PRIu16
" %zu"
" %s"
"\n",
res.status.tun_name,
res.status.pid,
bindstr[0] ? bindstr : "-",
gt_get_port((struct sockaddr *)&res.status.bind),
peerstr[0] ? peerstr : "-",
gt_get_port((struct sockaddr *)&res.status.peer),
res.status.mtu,
res.status.chacha ? "chacha20poly1305" : "aes256gcm");
}
return 0;
}
int
gt_show(int argc, char **argv)
{
const char *dev = NULL;
struct argz showz[] = {
{"dev", "NAME", &dev, argz_str},
{NULL}};
if (argz(showz, argc, argv))
return 1;
int fd = ctl_connect(GT_RUNDIR, dev);
if (fd < 0) {
switch (fd) {
case -1:
perror("show");
break;
case CTL_ERROR_NONE:
gt_log("no device\n");
break;
case CTL_ERROR_MANY:
gt_log("please choose a device\n");
break;
default:
gt_log("couldn't connect\n");
}
return 1;
}
int ret = gt_show_status(fd);
if (ret == -1)
perror("show");
ctl_delete(fd);
return !!ret;
}

62
src/state.c Normal file
View File

@@ -0,0 +1,62 @@
#include "common.h"
#include "state.h"
#include "str.h"
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int state_create (const char *filename)
{
if (str_empty(filename))
return -1;
int fd = open(filename, O_WRONLY);
if (fd==-1) {
if (errno!=EINTR)
perror("open");
return -1;
}
struct stat st = {0};
if (fstat(fd, &st)==-1) {
perror("fstat");
close(fd);
return -1;
}
if (!S_ISFIFO(st.st_mode)) {
gt_log("`%s' is not a fifo\n", filename);
close(fd);
return -1;
}
return fd;
}
void state_send (int fd, const char *state, const char *info)
{
if (str_empty(state))
return;
if (fd==-1) {
gt_print("%s %s\n", state, info);
return;
}
const char *strs[] = { state, " ", info, "\n" };
char *str = str_cat(strs, COUNT(strs));
if (!str) {
perror("str_cat");
return;
}
if (write(fd, str, str_len(str))==-1 && errno!=EINTR)
perror("write");
free(str);
}

4
src/state.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
int state_create (const char *);
void state_send (int, const char *, const char *);

View File

@@ -2,32 +2,72 @@
#include "common.h"
_pure_ static inline int
str_empty(const char *restrict str)
static inline size_t str_cpy (char *restrict dst, const char *restrict src, size_t len)
{
if (!dst || !src)
return 0;
size_t i;
for (i=0; i<len && src[i]; i++)
dst[i] = src[i];
dst[i] = 0;
return i;
}
_pure_
static inline int str_empty (const char *restrict str)
{
return !str || !str[0];
}
_pure_ static inline size_t
str_cmp(const char *restrict sa, const char *restrict sb)
_pure_
static inline size_t str_cmp (const char *restrict sa, const char *restrict sb)
{
if (!sa || !sb)
return 1;
size_t i = 0;
while (sa[i] == sb[i])
while (sa[i]==sb[i])
if (!sa[i++])
return 0;
return i + 1;
return i+1;
}
_pure_ static inline size_t
str_len(const char *restrict str, size_t len)
_pure_
static inline size_t str_len (const char *restrict str)
{
if (!str)
return 0;
return strnlen(str, len);
return strlen(str);
}
static inline char *str_cat (const char **strs, size_t count)
{
size_t size = 1;
for (size_t i=0; i<count; i++)
size += str_len(strs[i]);
char *str = malloc(size);
if (!str)
return NULL;
char *p = str;
for (size_t i=0; i<count; i++) {
size_t len = str_len(strs[i]);
memcpy(p, strs[i], len);
p += len;
}
p[0] = 0;
return str;
}

230
src/tun.c
View File

@@ -1,26 +1,25 @@
#include "common.h"
#include "ip.h"
#include "str.h"
#include "tun.h"
#include <fcntl.h>
#include "tun.h"
#include "str.h"
#include "ip.h"
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <net/if.h>
#ifdef __linux__
#define IFF_TUN 0x0001
#define IFF_NO_PI 0x1000
#define TUNSETIFF _IOW('T', 202, int)
#define TUNSETPERSIST _IOW('T', 203, int)
#include <linux/if.h>
#include <linux/if_tun.h>
#endif
#ifdef __APPLE__
#include <net/if_utun.h>
#include <sys/kern_control.h>
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#endif
#if defined(__APPLE__) || defined(__OpenBSD__)
@@ -29,29 +28,20 @@
#ifdef __APPLE__
static int
tun_create_by_id(char *name, size_t len, unsigned id)
static int tun_create_by_id (char *name, size_t size, unsigned id, _unused_ int mq)
{
int ret = snprintf(name, len, "utun%u", id);
if (ret <= 0 || (size_t)ret >= len) {
errno = EINVAL;
return -1;
}
int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (fd == -1)
if (fd==-1)
return -1;
struct ctl_info ci = {
.ctl_name = UTUN_CONTROL_NAME,
};
struct ctl_info ci;
memset(&ci, 0, sizeof(ci));
str_cpy(ci.ctl_name, UTUN_CONTROL_NAME, sizeof(ci.ctl_name)-1);
if (ioctl(fd, CTLIOCGINFO, &ci)) {
int err = errno;
close(fd);
errno = err;
return -1;
}
@@ -60,164 +50,157 @@ tun_create_by_id(char *name, size_t len, unsigned id)
.sc_len = sizeof(sc),
.sc_family = AF_SYSTEM,
.ss_sysaddr = AF_SYS_CONTROL,
.sc_unit = id + 1,
.sc_unit = id+1,
};
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc))) {
int err = errno;
close(fd);
errno = err;
return -1;
}
snprintf(name, size, "utun%u", id);
return fd;
}
static int
tun_create_by_name(char *name, size_t len, const char *dev_name)
static int tun_create_by_name (char *name, size_t size, char *dev_name, int mq)
{
unsigned id = 0;
if (sscanf(dev_name, "utun%u", &id) != 1) {
errno = EINVAL;
if (sscanf(dev_name, "utun%u", &id)!=1)
return -1;
}
return tun_create_by_id(name, len, id);
return tun_create_by_id(name, size, id, mq);
}
#else /* not __APPLE__ */
#ifdef __linux__
static int
tun_create_by_name(char *name, size_t len, const char *dev_name)
static int tun_create_by_name (char *name, size_t size, char *dev_name, int mq)
{
int ret = snprintf(name, len, "%s", dev_name);
if (ret <= 0 || (size_t)ret >= len) {
errno = EINVAL;
return -1;
}
struct ifreq ifr = {
.ifr_flags = IFF_TUN | IFF_NO_PI,
};
ret = snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", dev_name);
if (ret <= 0 || (size_t)ret >= sizeof(ifr.ifr_name)) {
errno = EINVAL;
return -1;
}
int fd = open("/dev/net/tun", O_RDWR);
if (fd == -1)
if (fd==-1)
return -1;
struct ifreq ifr = {
.ifr_flags = IFF_TUN|IFF_NO_PI,
};
if (mq) {
#ifdef IFF_MULTI_QUEUE
ifr.ifr_flags |= IFF_MULTI_QUEUE;
#endif
}
str_cpy(ifr.ifr_name, dev_name, IFNAMSIZ-1);
if (ioctl(fd, TUNSETIFF, &ifr)) {
int err = errno;
close(fd);
errno = err;
return -1;
}
str_cpy(name, ifr.ifr_name, size-1);
return fd;
}
#else /* not __linux__ not __APPLE__ */
static int
tun_create_by_name(char *name, size_t len, const char *dev_name)
static int tun_create_by_name (char *name, size_t size, char *dev_name, _unused_ int mq)
{
int ret = snprintf(name, len, "/dev/%s", dev_name);
char path[64];
if (ret <= 0 || (size_t)ret >= len) {
errno = EINVAL;
return -1;
}
snprintf(path, sizeof(path), "/dev/%s", dev_name);
str_cpy(name, dev_name, size-1);
return open(tmp, O_RDWR);
return open(path, O_RDWR);
}
#endif /* not __APPLE__ */
static int
tun_create_by_id(char *name, size_t len, unsigned id)
static int tun_create_by_id (char *name, size_t size, unsigned id, int mq)
{
char tmp[64];
int ret = snprintf(tmp, sizeof(tmp), "tun%u", id);
char dev_name[64];
if (ret <= 0 || (size_t)ret >= sizeof(tmp)) {
errno = EINVAL;
return -1;
}
snprintf(dev_name, sizeof(dev_name), "tun%u", id);
return tun_create_by_name(name, len, tmp);
return tun_create_by_name(name, size, dev_name, mq);
}
#endif
int
tun_create(char *name, size_t len, const char *dev_name)
int tun_create (char *dev_name, char **ret_name, int mq)
{
char name[64] = {0};
int fd = -1;
#ifndef IFF_MULTI_QUEUE
if (mq)
gt_na("IFF_MULTI_QUEUE");
#endif
if (str_empty(dev_name)) {
for (unsigned id = 0; id < 32 && fd == -1; id++)
fd = tun_create_by_id(name, len, id);
for (unsigned id=0; id<32 && fd==-1; id++)
fd = tun_create_by_id(name, sizeof(name), id, mq);
} else {
fd = tun_create_by_name(name, len, dev_name);
fd = tun_create_by_name(name, sizeof(name), dev_name, mq);
}
if (fd!=-1 && ret_name)
*ret_name = strdup(name);
return fd;
}
int
tun_read(int fd, void *data, size_t size)
ssize_t tun_read (int fd, void *data, size_t size)
{
if (!size)
return 0;
return -1;
#ifdef GT_BSD_TUN
uint32_t family;
struct iovec iov[2] = {
{
.iov_base = &family,
.iov_len = sizeof(family),
},
{
.iov_base = data,
.iov_len = size,
},
{ .iov_base = &family, .iov_len = sizeof(family) },
{ .iov_base = data, .iov_len = size }
};
int ret = (int)readv(fd, iov, 2);
ssize_t ret = readv(fd, iov, 2);
#else
ssize_t ret = read(fd, data, size);
#endif
if (ret <= 0)
return ret;
if (ret==-1) {
if (errno==EAGAIN || errno==EINTR)
return -1;
if ((size_t)ret <= sizeof(family))
if (errno)
perror("tun read");
return 0;
}
#ifdef GT_BSD_TUN
if (ret<(ssize_t) sizeof(family))
return 0;
return ret - (int)sizeof(family);
return ret-sizeof(family);
#else
return (int)read(fd, data, size);
return ret;
#endif
}
int
tun_write(int fd, const void *data, size_t size)
ssize_t tun_write (int fd, const void *data, size_t size)
{
if (!size)
return 0;
return -1;
#ifdef GT_BSD_TUN
uint32_t family;
switch (ip_get_version(data, (int)size)) {
switch (ip_get_version(data, size)) {
case 4:
family = htonl(AF_INET);
break;
@@ -225,42 +208,35 @@ tun_write(int fd, const void *data, size_t size)
family = htonl(AF_INET6);
break;
default:
errno = EINVAL;
return -1;
}
struct iovec iov[2] = {
{
.iov_base = &family,
.iov_len = sizeof(family),
},
{
.iov_base = (void *)data,
.iov_len = size,
},
{ .iov_base = &family, .iov_len = sizeof(family) },
{ .iov_base = (void *) data, .iov_len = size },
};
int ret = (int)writev(fd, iov, 2);
ssize_t ret = writev(fd, iov, 2);
#else
ssize_t ret = write(fd, data, size);
#endif
if (ret <= 0)
return ret;
if (ret==-1) {
if (errno==EAGAIN || errno==EINTR)
return -1;
if ((size_t)ret <= sizeof(family))
if (errno)
perror("tun write");
return 0;
}
#ifdef GT_BSD_TUN
if (ret<(ssize_t) sizeof(family))
return 0;
return ret - (int)sizeof(family);
return ret-sizeof(family);
#else
return (int)write(fd, data, size);
#endif
}
int
tun_set_persist(int fd, int on)
{
#ifdef TUNSETPERSIST
return ioctl(fd, TUNSETPERSIST, on);
#else
errno = ENOSYS;
return -1;
return ret;
#endif
}

View File

@@ -1,6 +1,7 @@
#pragma once
int tun_create (char *, size_t, const char *);
int tun_read (int, void *, size_t);
int tun_write (int, const void *, size_t);
int tun_set_persist (int, int);
#include <unistd.h>
int tun_create (char *, char **, int);
ssize_t tun_read (int, void *, size_t);
ssize_t tun_write (int, const void *, size_t);

View File

@@ -1,10 +0,0 @@
[Match]
Name=gtc-*
[Network]
Description=Glorytun client device
DHCP=ipv4
[DHCP]
CriticalConnection=yes
RouteTable=200

View File

@@ -1,6 +0,0 @@
#!/bin/sh
exec glorytun bind "$@" \
$BIND $BIND_PORT \
${DEV:+dev "$DEV"} \
${HOST:+to "$HOST" "$PORT"}

View File

@@ -1,75 +0,0 @@
#!/bin/sh
set -e
_ask() {
printf "%s: " "$1"
read -r "$2"
}
_ask "Config filename (tun0)" NAME
NAME=${NAME:-tun0}
DIR="/etc/glorytun/$NAME"
if [ -d "$DIR" ]; then
echo "This config already exit!"
exit 1
fi
_ask "Server ip (enter for server conf)" HOST
if [ -z "$HOST" ]; then
_ask "Bind to port (5000)" BIND_PORT
BIND_PORT=${BIND_PORT:-5000}
else
_ask "Server port (5000)" PORT
PORT=${PORT:-5000}
fi
BIND=0.0.0.0
case "$HOST" in
*:*) BIND=::
esac
_ask "Server key (enter to generate a new one)" KEY
if [ -z "$KEY" ]; then
KEY=$(glorytun keygen)
echo "Your new key: $KEY"
fi
# install files
mkdir -p "$DIR"
cat > "$DIR/env" <<EOF
DEV=gt${HOST:+c}-$NAME
HOST=$HOST
PORT=$PORT
BIND=$BIND
BIND_PORT=$BIND_PORT
OPTIONS=
EOF
( umask 077; echo "$KEY" > "$DIR/key" )
[ "$HOST" ] && cat > "$DIR/post.sh" <<'EOF'
#!/bin/sh
PREF=32765
TABLE=200
# keep the current route to HOST
SRC=$(ip route get "$HOST" | awk '/src/{getline;print $0}' RS=' ')
ip rule add from "$SRC" table main pref "$((PREF-1))" || true
# limit to 100Mbit by default
glorytun path up "$SRC" dev "$DEV" rate rx 12500000 tx 12500000
# forward everything else to the tunnel
ip rule add from all table "$TABLE" pref "$PREF" || true
EOF
[ -f "$DIR/post.sh" ] && chmod u+x "$DIR/post.sh"
# start services
_ask "Start glorytun now ? (enter to skip)" START
case "$START" in y*|Y*)
systemctl start glorytun@"$NAME" ;;
esac

View File

@@ -1,14 +0,0 @@
[Match]
Name=gt-*
[Network]
Description=Glorytun server device
Address=0.0.0.0/24
DHCPServer=yes
IPMasquerade=yes
[DHCPServer]
PoolOffset=2
PoolSize=1
EmitDNS=yes
DNS=9.9.9.9

View File

@@ -1,14 +0,0 @@
[Unit]
Description=Glorytun on %I
After=network.target
[Service]
Type=simple
Restart=always
EnvironmentFile=/etc/glorytun/%i/env
ExecStart=@bindir@/glorytun-run keyfile /etc/glorytun/%i/key $OPTIONS
ExecStartPost=-/etc/glorytun/%i/post.sh
CapabilityBoundingSet=CAP_NET_ADMIN
[Install]
WantedBy=multi-user.target

View File

@@ -1,15 +1,13 @@
#!/bin/sh
export GIT_DIR=.git
export GIT_WORK_TREE=.
[ -z "${VERSION}" ] && VERSION=`git describe --tags --match='v[0-9].*' 2>/dev/null` \
&& VERSION=${VERSION#v}
[ -z "$VERSION" ] && VERSION="$(git describe --tags --match='v[0-9].*' 2>/dev/null)" \
&& VERSION="${VERSION#v}"
[ -z "${VERSION}" ] && VERSION=`cat VERSION 2>/dev/null`
[ -z "$VERSION" ] && VERSION="$(git rev-parse HEAD 2>/dev/null)"
[ -z "${VERSION}" ] && VERSION=0.0.0
[ -z "$VERSION" ] && VERSION="$(cat VERSION 2>/dev/null)"
[ "$1" = "major" ] && printf ${VERSION%%.*} \
&& exit 0
[ -z "$VERSION" ] && VERSION="0.0.0"
printf "%s" "$VERSION" | tee VERSION
printf ${VERSION} | tee VERSION