Compare commits

..

1 Commits

Author SHA1 Message Date
angt
a3b78a3868 Update mud 2016-03-30 14:36:52 +00:00
43 changed files with 1090 additions and 2032 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: ${{ matrix.os }}
path: ./bin

5
.gitignore vendored
View File

@@ -1,4 +1,4 @@
*.[ios]
*.o
*.log
*.scan
*.cache
@@ -10,7 +10,4 @@ configure
build-aux
.deps
.dirstamp
.static
glorytun
build*
VERSION

5
.gitmodules vendored
View File

@@ -1,8 +1,3 @@
[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-2020, 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,34 +0,0 @@
NAME := glorytun
VERSION := $(shell ./version.sh)
DIST := $(NAME)-$(VERSION)
CC ?= cc
DESTDIR ?=
prefix ?= /usr
Q := @
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)
HDR := argz/argz.h mud/mud.h mud/aegis256/aegis256.h $(wildcard src/*.h)
$(NAME): $(SRC) $(HDR)
@echo "$(NAME)"
$(Q)$(CC) $(FLAGS) -o $(NAME) $(SRC) -lsodium
.PHONY: install
install: $(NAME)
@echo "$(DESTDIR)$(prefix)/bin/$(NAME)"
$(Q)install -m 755 -d $(DESTDIR)$(prefix)/bin
$(Q)install -m 755 -s $(NAME) $(DESTDIR)$(prefix)/bin
.PHONY: clean
clean:
$(Q)rm -f "$(NAME)"
$(Q)rm -f "$(DIST).tar.gz"

View File

@@ -5,39 +5,29 @@ 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
glorytun_CFLAGS += -I$(srcdir)/mud
glorytun_SOURCES += \
mud/mud.h \
mud/mud.c
EXTRA_DIST = \
LICENSE \
Makefile \
README.md \
VERSION \
autogen.sh \
meson.build \
mud/LICENSE \
mud/README.md \
sodium.sh \
systemd \
version.sh

View File

@@ -1,53 +1,23 @@
# Glorytun
# π₁(Glorytun)=ℤ²
Glorytun is a small, simple and secure VPN over [mud](https://github.com/angt/mud).
Small, Simple and Stupid VPN over [mud](https://github.com/angt/mud).
Please visit the [wiki](https://github.com/angt/glorytun/wiki) for how-to guides, tutorials, etc.
#### Work In Progress
## Compatibility
This code will probably format your harddisk!
Glorytun only depends on [libsodium](https://github.com/jedisct1/libsodium) version >= 1.0.4.
Which can be installed on a wide variety of systems.
#### Build and Install
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.
Glorytun depends on [libsodium](https://github.com/jedisct1/libsodium) version >= 1.0.4
and needs an AES-NI capable CPU.
IPv4 and IPv6 are supported.
On Linux you can have both at the same time by binding `::`.
To build and install the latest version:
## Features
The key features of Glorytun come directly from mud:
* **Fast and highly secure**
Glorytun uses a new and very fast AEAD construction called AEGIS-256 if AES-NI is available otherwise ChaCha20-Poly1305 is used.
Of course, you can force the use of ChaCha20-Poly1305 for higher security.
All messages are encrypted, authenticated and timestamped to mitigate a large set of attacks.
This implies that the client and the server must be synchronized, an offset of 10min is accepted by default.
Perfect forward secrecy is also implemented with ECDH over Curve25519. Keys are rotated every hours.
* **Multipath and failover**
Connectivity is now crucial, especially in the SD-WAN world.
This feature allows a TCP connection (and all other protocols) to explore and exploit all available links without being disconnected.
Aggregation should work on all conventional links.
Only very high latency (+500ms) links are not recommended for now.
Backup paths are also supported, they will be used only in case of emergency, it is useful when aggregation is not your priority.
* **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 Next-hop MTU to avoid ICMP black holes.
In asymmetric situations the minimum MTU is selected.
---
$ git clone https://github.com/angt/glorytun --recursive --branch mud
$ cd glorytun
$ ./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 f88a280d2b

View File

@@ -4,18 +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_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,62 +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'),
]
)
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

2
mud

Submodule mud updated: 4a7740f70f...6b3001e6d8

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,118 +0,0 @@
#include "common.h"
#include <sodium.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <inttypes.h>
#include "../argz/argz.h"
#include "../mud/aegis256/aegis256.h"
#define NPUBBYTES 32
#define KEYBYTES 32
#define ABYTES 16
int
gt_bench(int argc, char **argv)
{
struct argz bench_argz[] = {
{"aes|chacha", NULL, NULL, argz_option},
{NULL}};
if (argz(bench_argz, argc, argv))
return 1;
if (sodium_init() == -1) {
gt_log("sodium init failed\n");
return 1;
}
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[1450 + ABYTES];
unsigned char npub[NPUBBYTES];
unsigned char key[KEYBYTES];
memset(buf, 0, sizeof(buf));
randombytes_buf(npub, sizeof(npub));
randombytes_buf(key, sizeof(key));
if (term) {
printf("cipher: %s\n\n", GT_CIPHER(chacha));
printf(" size min mean max \n");
printf("----------------------------------------------------\n");
}
int64_t size = 20;
for (int i = 0; !gt_quit && size <= 1450; i++) {
struct {
int64_t min, mean, max, n;
} mbps = { .n = 0 };
int64_t bytes_max = (int64_t)1 << 24;
while (!gt_quit && mbps.n < 10) {
int64_t bytes = 0;
int64_t base = (int64_t)clock();
while (!gt_quit && bytes <= bytes_max) {
if (chacha) {
crypto_aead_chacha20poly1305_encrypt(
buf, NULL, buf, size, NULL, 0, NULL, npub, key);
} else {
aegis256_encrypt(buf, NULL, buf, size, NULL, 0, npub, key);
}
bytes += size;
}
int64_t dt = (int64_t)clock() - base;
bytes_max = (bytes * (CLOCKS_PER_SEC / 3)) / dt;
int64_t _mbps = (8 * bytes * CLOCKS_PER_SEC) / (dt * 1000 * 1000);
if (!mbps.n++) {
mbps.min = _mbps;
mbps.max = _mbps;
mbps.mean = _mbps;
continue;
}
if (mbps.min > _mbps)
mbps.min = _mbps;
if (mbps.max < _mbps)
mbps.max = _mbps;
mbps.mean += (_mbps - mbps.mean) / mbps.n;
if (term) {
printf("\r %5"PRIi64" %9"PRIi64" Mbps %9"PRIi64" Mbps %9"PRIi64" Mbps",
size, mbps.min, mbps.mean, mbps.max);
fflush(stdout);
}
}
if (term) {
printf("\n");
} else {
printf("bench %s %"PRIi64" %"PRIi64" %"PRIi64" %"PRIi64"\n",
GT_CIPHER(chacha), size, mbps.min, mbps.mean, mbps.max);
}
size += 2 * 5 * 13;
}
return 0;
}

View File

@@ -1,367 +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 || 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(tun_name);
if (ctl_fd == -1) {
char dir[64];
if (ctl_rundir(dir, sizeof(dir))) {
gt_log("couldn't create %s/%s: %s\n",
dir, tun_name, strerror(errno));
} else {
gt_log("couldn't find a writable run/tmp directory\n");
}
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);
__attribute__((aligned(16)))
unsigned char buf[1500];
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 = { 0 };
int update = mud_update(mud);
if (update >= 0) {
if (mud_can_read && tun_can_write) {
} else if (tun_can_read && mud_can_write) {
if (update)
tv.tv_usec = 1000;
} else {
tv.tv_usec = 100000;
}
}
const int ret = select(last_fd, &rfds, &wfds, NULL, update < 0 ? 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);
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,
req.path.beat,
req.path.fixed_rate))
res.ret = errno;
break;
case CTL_CONF:
if (mud_set_conf(mud, &req.conf))
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;
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_BAD:
if (mud_get_bad(mud, &res.bad))
res.ret = errno;
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,26 +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
#define COUNT(x) (sizeof(x)/sizeof(x[0]))
#define ALIGN_SIZE (1<<4)
@@ -44,31 +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; })
#define GT_CIPHER(x) ((x) ? "chacha20poly1305" : "aegis256")
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 **);

203
src/ctl.c
View File

@@ -1,203 +0,0 @@
#include "common.h"
#include "ctl.h"
#include "str.h"
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <libgen.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
char *
ctl_rundir(char *dst, size_t size)
{
if (dst && size)
dst[0] = 0;
const char *fmt[] = {
"/run/user/%u/" PACKAGE_NAME,
"/run/" PACKAGE_NAME ".%u",
"/var/run/" PACKAGE_NAME ".%u",
"/tmp/" PACKAGE_NAME ".%u",
};
for (unsigned i = 0; i < COUNT(fmt); i++) {
char path[128];
int ret = snprintf(dst, size, fmt[i], geteuid());
if ((ret <= 0) ||
((size_t)ret >= size) ||
((size_t)ret >= sizeof(path)))
continue;
memcpy(path, dst, (size_t)ret + 1);
char *p = dirname(path);
if (p && !access(p, W_OK))
return dst;
}
errno = EINTR;
return NULL;
}
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 *file)
{
char dir[64];
if (!ctl_rundir(dir, sizeof(dir)))
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 *file)
{
char dir[64];
DIR *dp = NULL;
if (!ctl_rundir(dir, sizeof(dir)))
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 = socket(AF_UNIX, SOCK_DGRAM, 0);
if (ctl_bind(fd, dir, NULL) ||
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_CONF,
CTL_STATUS,
CTL_PATH_STATUS,
CTL_BAD,
};
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;
unsigned long beat;
unsigned char fixed_rate;
} path;
struct {
char tun_name[64];
long pid;
size_t mtu;
int chacha;
struct sockaddr_storage bind;
struct sockaddr_storage peer;
} status;
struct mud_conf conf;
struct mud_path path_status;
struct mud_bad bad;
};
};
char *ctl_rundir (char *, size_t);
int ctl_create (const char *);
int ctl_connect (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;
}

View File

@@ -1,31 +1,73 @@
#include "common.h"
#include "buffer.h"
#include "ip.h"
#include "str.h"
#include "option.h"
#include "tun.h"
#include "db.h"
#include "state.h"
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <signal.h>
#include <poll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sodium.h>
#include <stdio.h>
#include "../argz/argz.h"
#include "mud.h"
volatile sig_atomic_t gt_alarm;
volatile sig_atomic_t gt_reload;
volatile sig_atomic_t gt_quit;
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
static void
gt_sa_handler(int sig)
static struct {
int timeout;
volatile sig_atomic_t quit;
volatile sig_atomic_t info;
uint8_t key[crypto_generichash_KEYBYTES];
} gt;
static void fd_set_nonblock (int fd)
{
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);
if (ret==-1)
perror("fcntl O_NONBLOCK");
}
static void gt_sa_handler (int sig)
{
switch (sig) {
case SIGALRM:
gt_alarm = 1;
return;
case SIGHUP:
gt_reload = 1; /* FALLTHRU */
default:
gt_quit = 1;
case SIGINT:
case SIGQUIT:
case SIGTERM:
gt.quit = 1;
break;
case SIGUSR1:
gt.info = 1;
break;
}
}
static void
gt_set_signal(void)
static void gt_set_signal (void)
{
struct sigaction sa = {
.sa_flags = 0,
@@ -34,74 +76,333 @@ gt_set_signal(void)
sigemptyset(&sa.sa_mask);
sa.sa_handler = gt_sa_handler;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGALRM, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
}
static int
gt_version(int argc, char **argv)
static ssize_t fd_read (int fd, void *data, size_t size)
{
struct argz version_argz[] = {
{"libsodium", NULL, NULL, argz_option},
{NULL}};
if ((fd==-1) || !size)
return -1;
if (argz(version_argz, argc, argv))
return 1;
ssize_t ret = read(fd, data, size);
if (argz_is_set(version_argz, "libsodium")) {
printf("%s\n", sodium_version_string());
} else {
printf("%s\n", PACKAGE_VERSION);
if (ret==-1) {
if (errno==EAGAIN || errno==EINTR)
return -1;
if (errno)
perror("read");
return 0;
}
return ret;
}
static ssize_t fd_write (int fd, const void *data, size_t size)
{
if ((fd==-1) || !size)
return -1;
ssize_t ret = write(fd, data, size);
if (ret==-1) {
if (errno==EAGAIN || errno==EINTR)
return -1;
if (errno==EPIPE || errno==ECONNRESET)
return 0;
if (errno)
perror("write");
return 0;
}
return ret;
}
static size_t fd_read_all (int fd, void *data, size_t size)
{
size_t done = 0;
while (done<size) {
ssize_t ret = fd_read(fd, (uint8_t *)data+done, size-done);
if (!ret)
break;
if (ret<0) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN,
};
if (!poll(&pollfd, 1, gt.timeout))
break;
continue;
}
done += ret;
}
return done;
}
static size_t fd_write_all (int fd, const void *data, size_t size)
{
size_t done = 0;
while (done<size) {
ssize_t ret = fd_write(fd, (const uint8_t *)data+done, size-done);
if (!ret)
break;
if (ret<0) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLOUT,
};
if (!poll(&pollfd, 1, gt.timeout))
break;
continue;
}
done += ret;
}
return done;
}
static int gt_setup_secretkey (char *keyfile)
{
const size_t size = sizeof(gt.key);
if (str_empty(keyfile)) {
char buf[2*size+1];
randombytes_buf(gt.key, size);
gt_tohex(buf, sizeof(buf), gt.key, size);
state("SECRETKEY", buf);
return 0;
}
int fd;
do {
fd = open(keyfile, O_RDONLY|O_CLOEXEC);
} while (fd==-1 && errno==EINTR);
if (fd==-1) {
perror("open keyfile");
return -1;
}
char key[2*size];
size_t r = fd_read_all(fd, key, sizeof(key));
close(fd);
if (r!=sizeof(key)) {
gt_log("unable to read secret key\n");
return -1;
}
if (gt_fromhex(gt.key, size, key, sizeof(key))) {
gt_log("secret key is not valid\n");
return -1;
}
return 0;
}
int
main(int argc, char **argv)
int main (int argc, char **argv)
{
gt_set_signal();
struct {
char *name;
char *help;
int (*call)(int, char **);
} cmd[] = {
{"show", "show tunnel info", gt_show},
{"bench", "start a crypto bench", gt_bench},
{"bind", "start a new tunnel", gt_bind},
{"set", "change tunnel properties", gt_set},
{"keygen", "generate a new secret key", gt_keygen},
{"path", "manage paths", gt_path},
{"version", "show version", gt_version},
{NULL}};
char *host = NULL;
char *port = "5000";
char *bind_list = NULL;
char *bind_port = "5000";
char *dev = NULL;
char *keyfile = NULL;
char *statefile = NULL;
if (argv[1]) {
for (int k = 0; cmd[k].name; k++) {
if (!str_cmp(cmd[k].name, argv[1]))
return cmd[k].call(argc - 1, argv + 1);
gt.timeout = 5000;
struct option opts[] = {
{ "host", &host, option_str },
{ "port", &port, option_str },
{ "bind", &bind_list, option_str },
{ "bind-port", &bind_port, option_str },
{ "dev", &dev, option_str },
{ "keyfile", &keyfile, option_str },
{ "multiqueue", NULL, option_option },
{ "statefile", &statefile, option_str },
{ "timeout", &gt.timeout, option_long },
{ "version", NULL, option_option },
{ NULL },
};
if (option(opts, argc, argv))
return 1;
if (option_is_set(opts, "version")) {
gt_print(PACKAGE_STRING"\n");
return 0;
}
if (!option_is_set(opts, "keyfile")) {
gt_log("keyfile option must be set\n");
return 1;
}
if (gt.timeout<=0 || gt.timeout>INT_MAX) {
gt_log("bad timeout\n");
return 1;
}
if (sodium_init()==-1) {
gt_log("libsodium initialization has failed\n");
return 1;
}
if (!crypto_aead_aes256gcm_is_available()) {
gt_na("AES-256-GCM");
return 1;
}
if (state_init(statefile))
return 1;
char *tun_name = NULL;
int tun_fd = tun_create(dev, &tun_name, option_is_set(opts, "multiqueue"));
if (tun_fd==-1) {
gt_log("couldn't create tun device\n");
return 1;
}
fd_set_nonblock(tun_fd);
if (gt_setup_secretkey(keyfile))
return 1;
struct mud *mud = mud_create(bind_port);
if (!mud) {
gt_log("couldn't create mud\n");
return 1;
}
mud_set_key(mud, gt.key, sizeof(gt.key));
if (bind_list) {
char tmp[1024];
char *name = &tmp[0];
size_t size = str_cpy(tmp, bind_list, sizeof(tmp)-1);
for (size_t i=0; i<size; i++) {
if (tmp[i]!=',')
continue;
tmp[i] = 0;
if (mud_bind(mud, name))
return 1;
name = &tmp[i+1];
}
if (name[0] && mud_bind(mud, name))
return 1;
}
if (host && mud_peer(mud, host, port))
return 1;
int mud_fd = mud_get_fd(mud);
state("INITIALIZED", tun_name);
fd_set rfds;
FD_ZERO(&rfds);
int started = 0;
unsigned char buf[2048];
while (!gt.quit) {
FD_SET(tun_fd, &rfds);
if (mud_can_pull(mud)) {
FD_SET(mud_fd, &rfds);
} else {
FD_CLR(mud_fd, &rfds);
}
struct timeval timeout = {
.tv_usec = 1000,
};
if _0_(select(mud_fd+1, &rfds, NULL, NULL, &timeout)==-1) {
if (errno==EINTR)
continue;
perror("select");
return 1;
}
if (mud_is_up(mud)) {
if (!started) {
state("STARTED", NULL);
started = 1;
}
} else if (started) {
state("STOPPED", NULL);
started = 0;
}
if (FD_ISSET(tun_fd, &rfds)) {
while (1) {
const ssize_t r = tun_read(tun_fd, buf, sizeof(buf));
if (r<=0)
break;
struct ip_common ic;
if (!ip_get_common(&ic, buf, sizeof(buf)) && ic.size==r)
mud_send(mud, buf, r);
}
}
mud_push(mud);
if (FD_ISSET(mud_fd, &rfds))
mud_pull(mud);
while (1) {
const int r = mud_recv(mud, buf, sizeof(buf));
if (r<=0)
break;
tun_write(tun_fd, buf, r);
}
}
printf("available commands:\n\n");
int len = 0;
for (int k = 0; cmd[k].name; k++)
len = MAX(len, (int)str_len(cmd[k].name, 32));
for (int k = 0; cmd[k].name; k++)
printf(" %-*s %s\n", len, cmd[k].name, cmd[k].help);
printf("\n");
return 1;
return 0;
}

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,229 +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;
}
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: %s\n"
" beat: %"PRIu64" ms\n"
" tx:\n"
" rate: %"PRIu64" bytes/sec\n"
" loss: %"PRIu64" percent\n"
" total: %"PRIu64" packets\n"
" rx:\n"
" rate: %"PRIu64" bytes/sec\n"
" loss: %"PRIu64" percent\n"
" total: %"PRIu64" packets\n"
: "path %s %s"
" %s %"PRIu16" %s %"PRIu16" %s %"PRIu16
" %zu %.3f %.3f"
" %s"
" %"PRIu64
" %"PRIu64" %"PRIu64" %"PRIu64
" %"PRIu64" %"PRIu64" %"PRIu64
"\n",
statestr,
path->ok ? "OK" : "DEGRADED",
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->conf.fixed_rate ? "fixed" : "auto",
path->conf.beat / 1000,
path->tx.rate,
path->tx.loss * 100 / 255,
path->tx.total,
path->rx.rate,
path->rx.loss * 100 / 255,
path->rx.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, enum mud_state 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[] = {
{"fixed|auto", NULL, NULL, argz_option},
{"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},
{"beat", "SECONDS", &req.path.beat, argz_time},
{NULL}};
if (argz(pathz, argc, argv))
return 1;
int fd = ctl_connect(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 = argz_is_set(pathz, "rate")
|| argz_is_set(pathz, "beat");
if (set && !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;
}
if (argz_is_set(ratez, "fixed")) {
req.path.fixed_rate = 3;
} else if (argz_is_set(ratez, "auto")) {
req.path.fixed_rate = 1;
}
int ret;
if (!req.path.addr.ss_family ||
(req.path.state == MUD_EMPTY && !set)) {
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;
}

View File

@@ -1,84 +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_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 << 1) | 1;
return 1;
}
int
gt_set(int argc, char **argv)
{
const char *dev = NULL;
struct ctl_msg req = {
.type = CTL_CONF,
}, res = {0};
struct argz pathz[] = {
{"dev", "NAME", &dev, argz_str},
{"tc", "CS|AF|EF", &req.conf.tc, gt_argz_tc},
{"kxtimeout", "SECONDS", &req.conf.kxtimeout, argz_time},
{"timetolerance", "SECONDS", &req.conf.timetolerance, argz_time},
{"losslimit", "PERCENT", &req.conf.losslimit, argz_percent},
{"keepalive", "SECONDS", &req.conf.keepalive, argz_time},
{NULL}};
if (argz(pathz, argc, argv))
return 1;
int fd = ctl_connect(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 = ctl_reply(fd, &res, &req);
if (ret)
perror("set");
ctl_delete(fd);
return !!ret;
}

View File

@@ -1,161 +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 void
gt_show_bad_line(int term, char *name, uint64_t count,
struct sockaddr_storage *ss)
{
if (!count)
return;
char addr[INET6_ADDRSTRLEN];
gt_toaddr(addr, sizeof(addr), (struct sockaddr *)ss);
printf(term ? "%s:\n"
" count: %"PRIu64"\n"
" last: %s port %"PRIu16"\n"
: "%s"
" %"PRIu64
" %s %"PRIu16
"\n",
name, count, addr[0] ? addr : "-",
gt_get_port((struct sockaddr *)ss));
}
static int
gt_show_bad(int fd)
{
struct ctl_msg res, req = {.type = CTL_BAD};
if (ctl_reply(fd, &res, &req))
return -1;
int term = isatty(1);
gt_show_bad_line(term, "decrypt",
res.bad.decrypt.count, &res.bad.decrypt.addr);
gt_show_bad_line(term, "difftime",
res.bad.difftime.count, &res.bad.difftime.addr);
gt_show_bad_line(term, "keyx",
res.bad.keyx.count, &res.bad.keyx.addr);
return 0;
}
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,
GT_CIPHER(res.status.chacha));
} 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,
GT_CIPHER(res.status.chacha));
}
return 0;
}
int
gt_show(int argc, char **argv)
{
const char *dev = NULL;
struct argz showz[] = {
{"dev", "NAME", &dev, argz_str},
{"bad", NULL, NULL, argz_option},
{NULL}};
if (argz(showz, argc, argv))
return 1;
int fd = ctl_connect(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 = argz_is_set(showz, "bad")
? gt_show_bad(fd)
: gt_show_status(fd);
if (ret == -1)
perror("show");
ctl_delete(fd);
return !!ret;
}

61
src/state.c Normal file
View File

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

4
src/state.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
int state_init (const char *);
void state (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