Compare commits

...

35 Commits

Author SHA1 Message Date
Adrien Gallouët
0a797b4f5f Try value 42 for mptcp (fallback to 26) 2017-06-07 11:27:44 +02:00
Adrien Gallouët
8cb849fbc3 Update pkg.m4 2017-06-07 11:26:22 +02:00
Adrien Gallouët
13f9f4c896 Fix printf format 2016-07-20 15:32:22 +00:00
Adrien Gallouët
cfd7af9241 Add .build.sh 2016-07-11 11:13:29 +00:00
Adrien Gallouët
c81592fcc5 Don't fd_set_nonblock(-1) 2016-06-23 12:15:01 +00:00
Adrien Gallouët
b4a311cdc8 Check for clock_gettime() 2016-06-15 14:12:21 +00:00
Adrien Gallouët
08617d0017 Fix macosx build 2016-06-15 14:01:20 +00:00
Adrien Gallouët
38cd3b0371 Try to do a more accurate bench 2016-06-15 09:20:18 +00:00
Adrien Gallouët
5944e61dfe Fix bench output 2016-06-15 09:10:57 +00:00
Adrien Gallouët
585b2b08bc Be more verbose on kx errors 2016-06-06 13:37:05 +00:00
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
angt
eefa7722c5 Use timeout in connect() too 2016-01-21 16:22:31 +01:00
angt
17547f555d Add SECRETKEY state 2016-01-18 18:07:35 +01:00
angt
ec9c59ce69 Code cleanup 2016-01-18 18:00:41 +01:00
angt
679927a684 Show device name in INITIALIZED state 2016-01-18 17:58:20 +01:00
angt
2999faf5d7 Move up state_init() 2016-01-18 17:26:42 +01:00
angt
219384b7e5 Remove the old school daemon option 2016-01-18 16:09:29 +01:00
angt
3649e46b03 Remove the absolute path restriction on statefile 2016-01-18 16:02:29 +01:00
angt
88f314bc75 Add state.[ch] 2016-01-18 15:59:18 +01:00
angt
e2b3dc1b46 Use only v[0-9].* tags to get VERSION 2016-01-17 10:56:55 +01:00
angt
1815ea519f Create a tcp_entry only on ACK 2016-01-17 10:18:39 +01:00
angt
33356d5d35 Don't work too much 2016-01-17 09:40:56 +01:00
angt
4a5d07ec45 Merge pull request #11 from jedisct1/notag
Cope with branches that don't have tags
2016-01-16 16:16:05 +01:00
Frank Denis
a50882e1ac Cope with branches that don't have tags
`version.sh` used to return only the hash of the last commit, causing
VERSION_MAJOR to possibly contain non-numeric junk.
2016-01-16 15:37:42 +01:00
angt
b2cb8ebcbe The minimum value for buffer-size option is GT_PKT_MAX 2016-01-15 17:02:11 +01:00
angt
6fd6cf8c4a Merge pull request #10 from jedisct1/socket
Include <sys/socket.h> for AF_INET/AF_INET6 definitions
2016-01-14 17:14:56 +01:00
Frank Denis
f0fc2751e5 Include <sys/socket.h> for AF_INET/AF_INET6 definitions
Required on OpenBSD
2016-01-14 17:11:27 +01:00
13 changed files with 464 additions and 221 deletions

15
.build.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
export CC="gcc -static"
git clone https://github.com/jedisct1/libsodium --depth=1 --branch stable
cd libsodium || exit 1
./autogen.sh && ./configure --enable-minimal --disable-shared --prefix=/usr && make install
cd ..
./autogen.sh && ./configure && make
[ -x glorytun ] || exit 1
mkdir -p deploy
strip -s glorytun
mv glorytun deploy/glorytun-$(cat VERSION)-$(uname -m).bin

View File

@@ -16,7 +16,9 @@ glorytun_SOURCES = \
src/tun.c \ src/tun.c \
src/tun.h \ src/tun.h \
src/db.c \ src/db.c \
src/db.h src/db.h \
src/state.c \
src/state.h
EXTRA_DIST = \ EXTRA_DIST = \
LICENSE \ LICENSE \

View File

@@ -1,4 +1,4 @@
# Glorytun # π₁(Glorytun)=ℤ²
Small, Simple and Stupid TCP VPN. Small, Simple and Stupid TCP VPN.
@@ -8,8 +8,7 @@ This code will probably format your harddisk!
#### Build and Install #### Build and Install
Glorytun depends on [libsodium](https://github.com/jedisct1/libsodium) version >= 1.0.4 Glorytun depends on [libsodium](https://github.com/jedisct1/libsodium) version >= 1.0.4.
and needs an AES-NI capable CPU.
To build and install the latest version: To build and install the latest version:

View File

@@ -16,6 +16,8 @@ AC_PROG_CC_C99
AC_USE_SYSTEM_EXTENSIONS AC_USE_SYSTEM_EXTENSIONS
AC_SEARCH_LIBS([getaddrinfo], [resolv nsl]) AC_SEARCH_LIBS([getaddrinfo], [resolv nsl])
AC_SEARCH_LIBS([socket], [socket]) AC_SEARCH_LIBS([socket], [socket])
AC_CHECK_LIB([rt], [clock_gettime])
AC_CHECK_FUNCS([clock_gettime])
PKG_CHECK_MODULES([libsodium], [libsodium >= 1.0.4]) PKG_CHECK_MODULES([libsodium], [libsodium >= 1.0.4])
AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([Makefile])
AC_OUTPUT AC_OUTPUT

View File

@@ -1,6 +1,6 @@
dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
dnl serial 11 (pkg-config-0.29) # serial 12 (pkg-config-0.29.2)
dnl
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>. dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com> dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
dnl dnl
@@ -41,7 +41,7 @@ dnl
dnl See the "Since" comment for each macro you use to see what version dnl See the "Since" comment for each macro you use to see what version
dnl of the macros you require. dnl of the macros you require.
m4_defun([PKG_PREREQ], m4_defun([PKG_PREREQ],
[m4_define([PKG_MACROS_VERSION], [0.29]) [m4_define([PKG_MACROS_VERSION], [0.29.2])
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, 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])]) [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ ])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 AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no pkg_failed=no
AC_MSG_CHECKING([for $1]) AC_MSG_CHECKING([for $2])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2])

View File

@@ -49,15 +49,18 @@ static inline size_t buffer_read_size (buffer_t *buffer)
static inline void buffer_shift (buffer_t *buffer) static inline void buffer_shift (buffer_t *buffer)
{ {
if (buffer->read==buffer->data)
return;
if (buffer->read==buffer->write) { if (buffer->read==buffer->write) {
buffer_format(buffer); buffer_format(buffer);
} else { return;
const uint8_t *src = PALIGN_DOWN(buffer->read);
const size_t size = ALIGN(buffer->write-src);
if (buffer->data+size<src) {
memcpy(buffer->data, src, size);
buffer->read -= src-buffer->data;
buffer->write -= src-buffer->data;
}
} }
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

@@ -6,6 +6,7 @@
#include "option.h" #include "option.h"
#include "tun.h" #include "tun.h"
#include "db.h" #include "db.h"
#include "state.h"
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
@@ -13,8 +14,9 @@
#include <signal.h> #include <signal.h>
#include <poll.h> #include <poll.h>
#include <fcntl.h> #include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/stat.h>
#ifndef __FAVOR_BSD #ifndef __FAVOR_BSD
#define __FAVOR_BSD #define __FAVOR_BSD
@@ -35,15 +37,29 @@
#include <sodium.h> #include <sodium.h>
#ifdef __APPLE__
#include <mach/mach_time.h>
#endif
#ifndef O_CLOEXEC #ifndef O_CLOEXEC
#define O_CLOEXEC 0 #define O_CLOEXEC 0
#endif #endif
#define GT_BUFFER_SIZE (64*1024)
#define GT_TIMEOUT (5000)
#define GT_MTU_MAX (1500) #define GT_MTU_MAX (1500)
#define GT_TUNR_SIZE (0x7FFF-16) #define GT_PKT_MAX (32*1024)
#define GT_TUNW_SIZE (0x7FFF) #define GT_TUNR_SIZE (GT_PKT_MAX-16-2)
#define GT_TUNW_SIZE (GT_PKT_MAX)
#define GT_ABYTES (16)
#define GT_KEYBYTES (32)
static struct {
volatile sig_atomic_t quit;
volatile sig_atomic_t info;
long timeout;
int mptcp;
int state_fd;
} gt;
struct fdbuf { struct fdbuf {
int fd; int fd;
@@ -53,15 +69,13 @@ struct fdbuf {
struct crypto_ctx { struct crypto_ctx {
struct { struct {
crypto_aead_aes256gcm_state state; uint8_t key[512] _align_(16);
uint8_t nonce[crypto_aead_aes256gcm_NPUBBYTES]; uint8_t nonce[16];
} write, read; } write, read;
uint8_t skey[crypto_generichash_KEYBYTES]; uint8_t skey[crypto_generichash_KEYBYTES];
int chacha;
}; };
volatile sig_atomic_t gt_close = 0;
volatile sig_atomic_t gt_info = 0;
_pure_ _pure_
static int64_t dt_ms (struct timeval *ta, struct timeval *tb) static int64_t dt_ms (struct timeval *ta, struct timeval *tb)
{ {
@@ -100,12 +114,16 @@ enum sk_opt {
sk_acceptfilter, sk_acceptfilter,
sk_quickack, sk_quickack,
sk_user_timeout, sk_user_timeout,
sk_mptcp_26,
sk_mptcp_42,
}; };
static void sk_set (int fd, enum sk_opt opt, const void *val, socklen_t len) static int sk_set (int fd, enum sk_opt opt, const void *val, socklen_t len)
{ {
if (!val || len<=0) if (!val || len<=0) {
return; errno = EINVAL;
return -1;
}
struct { struct {
const char *name; const char *name;
@@ -156,26 +174,45 @@ static void sk_set (int fd, enum sk_opt opt, const void *val, socklen_t len)
1, IPPROTO_TCP, TCP_USER_TIMEOUT, 1, IPPROTO_TCP, TCP_USER_TIMEOUT,
#endif #endif
}, },
[sk_mptcp_26] = { "MPTCP_ENABLED (26)", 1, IPPROTO_TCP, 26 },
[sk_mptcp_42] = { "MPTCP_ENABLED (42)", 1, IPPROTO_TCP, 42 },
}; };
if (!opts[opt].present) { if (!opts[opt].present) {
gt_na(opts[opt].name); gt_na(opts[opt].name);
return; errno = EINVAL;
return -1;
} }
if (setsockopt(fd, opts[opt].level, opts[opt].option, val, len)==-1) int ret = setsockopt(fd, opts[opt].level, opts[opt].option, val, len);
if (ret==-1) {
int err = errno;
gt_log("couldn't set socket option `%s'\n", opts[opt].name); gt_log("couldn't set socket option `%s'\n", opts[opt].name);
errno = err;
} }
static void sk_set_int (int fd, enum sk_opt opt, int val) return ret;
}
static int sk_set_int (int fd, enum sk_opt opt, int val)
{ {
return sk_set(fd, opt, &val, sizeof(val)); return sk_set(fd, opt, &val, sizeof(val));
} }
static void sk_set_mptcp (int fd)
{
if (sk_set_int(fd, sk_mptcp_42, 1)==-1)
sk_set_int(fd, sk_mptcp_26, 1);
}
static int sk_listen (int fd, struct addrinfo *ai) static int sk_listen (int fd, struct addrinfo *ai)
{ {
sk_set_int(fd, sk_reuseaddr, 1); sk_set_int(fd, sk_reuseaddr, 1);
if (gt.mptcp)
sk_set_mptcp(fd);
if (bind(fd, ai->ai_addr, ai->ai_addrlen)==-1) { if (bind(fd, ai->ai_addr, ai->ai_addrlen)==-1) {
perror("bind"); perror("bind");
return -1; return -1;
@@ -187,7 +224,7 @@ static int sk_listen (int fd, struct addrinfo *ai)
} }
#ifdef __linux__ #ifdef __linux__
sk_set_int(fd, sk_defer_accept, GT_TIMEOUT/1000); sk_set_int(fd, sk_defer_accept, gt.timeout/1000);
#else #else
char data[256] = "dataready"; char data[256] = "dataready";
sk_set(fd, sk_acceptfilter, &data, sizeof(data)); sk_set(fd, sk_acceptfilter, &data, sizeof(data));
@@ -198,11 +235,38 @@ static int sk_listen (int fd, struct addrinfo *ai)
static int sk_connect (int fd, struct addrinfo *ai) static int sk_connect (int fd, struct addrinfo *ai)
{ {
fd_set_nonblock(fd);
if (gt.mptcp)
sk_set_mptcp(fd);
int ret = connect(fd, ai->ai_addr, ai->ai_addrlen); int ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
if (ret==-1 && errno==EINTR) if (ret==-1) {
if (errno==EINTR)
return 0; return 0;
if (errno==EINPROGRESS) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLOUT,
};
if (!poll(&pollfd, 1, gt.timeout))
return -1;
int opt = 0;
socklen_t optlen = sizeof(opt);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &optlen);
if (!opt)
return 0;
errno = opt;
}
}
return ret; return ret;
} }
@@ -230,8 +294,13 @@ static int sk_accept (int fd)
int ret = accept(fd, (struct sockaddr *)&addr, &addr_size); int ret = accept(fd, (struct sockaddr *)&addr, &addr_size);
if (ret==-1 && errno!=EINTR) if (ret==-1) {
if (errno!=EINTR)
perror("accept"); perror("accept");
return -1;
}
fd_set_nonblock(ret);
return ret; return ret;
} }
@@ -264,7 +333,7 @@ static char *sk_get_name (int fd)
return NULL; return NULL;
} }
const char *const strs[] = { const char *strs[] = {
host, ".", port host, ".", port
}; };
@@ -316,10 +385,10 @@ static void gt_sa_handler (int sig)
case SIGINT: case SIGINT:
case SIGQUIT: case SIGQUIT:
case SIGTERM: case SIGTERM:
gt_close = 1; gt.quit = 1;
break; break;
case SIGUSR1: case SIGUSR1:
gt_info = 1; gt.info = 1;
break; break;
} }
} }
@@ -386,15 +455,6 @@ static ssize_t fd_write (int fd, const void *data, size_t size)
return ret; return ret;
} }
static void state_write (int fd, const char *str)
{
if (fd==-1) {
gt_print("state: %s", str);
} else {
fd_write(fd, str, str_len(str));
}
}
static size_t fd_read_all (int fd, void *data, size_t size) static size_t fd_read_all (int fd, void *data, size_t size)
{ {
size_t done = 0; size_t done = 0;
@@ -411,7 +471,7 @@ static size_t fd_read_all (int fd, void *data, size_t size)
.events = POLLIN, .events = POLLIN,
}; };
if (!poll(&pollfd, 1, GT_TIMEOUT)) if (!poll(&pollfd, 1, gt.timeout))
break; break;
continue; continue;
@@ -439,7 +499,7 @@ static size_t fd_write_all (int fd, const void *data, size_t size)
.events = POLLOUT, .events = POLLOUT,
}; };
if (!poll(&pollfd, 1, GT_TIMEOUT)) if (!poll(&pollfd, 1, gt.timeout))
break; break;
continue; continue;
@@ -459,7 +519,7 @@ static int gt_encrypt (struct crypto_ctx *ctx, buffer_t *dst, buffer_t *src)
if (!rs || !ws) if (!rs || !ws)
return 0; return 0;
const size_t size = rs+crypto_aead_aes256gcm_ABYTES; const size_t size = rs+GT_ABYTES;
if (size+2>ws) if (size+2>ws)
return 0; return 0;
@@ -467,14 +527,25 @@ static int gt_encrypt (struct crypto_ctx *ctx, buffer_t *dst, buffer_t *src)
dst->write[0] = 0xFF&(size>>8); dst->write[0] = 0xFF&(size>>8);
dst->write[1] = 0xFF&(size); dst->write[1] = 0xFF&(size);
if (ctx->chacha) {
crypto_aead_chacha20poly1305_encrypt(
dst->write+2, NULL,
src->read, rs,
dst->write, 2,
NULL, ctx->write.nonce,
ctx->write.key);
sodium_increment(ctx->write.nonce, crypto_aead_chacha20poly1305_NPUBBYTES);
} else {
crypto_aead_aes256gcm_encrypt_afternm( crypto_aead_aes256gcm_encrypt_afternm(
dst->write+2, NULL, dst->write+2, NULL,
src->read, rs, src->read, rs,
dst->write, 2, dst->write, 2,
NULL, ctx->write.nonce, NULL, ctx->write.nonce,
(const crypto_aead_aes256gcm_state *)&ctx->write.state); (const crypto_aead_aes256gcm_state *)ctx->write.key);
sodium_increment(ctx->write.nonce, crypto_aead_aes256gcm_NPUBBYTES); sodium_increment(ctx->write.nonce, crypto_aead_aes256gcm_NPUBBYTES);
}
src->read += rs; src->read += rs;
dst->write += size+2; dst->write += size+2;
@@ -490,30 +561,43 @@ static int gt_decrypt (struct crypto_ctx *ctx, buffer_t *dst, buffer_t *src)
if (!rs || !ws) if (!rs || !ws)
return 0; return 0;
if (rs<=2+crypto_aead_aes256gcm_ABYTES) if (rs<=2+GT_ABYTES)
return 0; return 0;
const size_t size = (src->read[0]<<8)|src->read[1]; const size_t size = (src->read[0]<<8)|src->read[1];
if (size-crypto_aead_aes256gcm_ABYTES>ws) if (size-GT_ABYTES>ws)
return 0; return 0;
if (size+2>rs) if (size+2>rs)
return 0; return 0;
if (ctx->chacha) {
if (crypto_aead_chacha20poly1305_decrypt(
dst->write, NULL,
NULL,
src->read+2, size,
src->read, 2,
ctx->read.nonce,
ctx->read.key))
return -1;
sodium_increment(ctx->read.nonce, crypto_aead_chacha20poly1305_NPUBBYTES);
} else {
if (crypto_aead_aes256gcm_decrypt_afternm( if (crypto_aead_aes256gcm_decrypt_afternm(
dst->write, NULL, dst->write, NULL,
NULL, NULL,
src->read+2, size, src->read+2, size,
src->read, 2, src->read, 2,
ctx->read.nonce, ctx->read.nonce,
(const crypto_aead_aes256gcm_state *)&ctx->read.state)) (const crypto_aead_aes256gcm_state *)ctx->read.key))
return -1; return -1;
sodium_increment(ctx->read.nonce, crypto_aead_aes256gcm_NPUBBYTES); sodium_increment(ctx->read.nonce, crypto_aead_aes256gcm_NPUBBYTES);
}
src->read += size+2; src->read += size+2;
dst->write += size-crypto_aead_aes256gcm_ABYTES; dst->write += size-GT_ABYTES;
return 0; return 0;
} }
@@ -619,7 +703,7 @@ int sa_have (struct seq_array *sa, uint32_t seq, uint32_t size)
if (d+size>sa->elem[i].size) { if (d+size>sa->elem[i].size) {
gt_print("sa_have:part\n"); gt_print("sa_have:part\n");
return 1; // XXX 0 return 0; // XXX 0
} }
} }
@@ -751,10 +835,11 @@ static void gt_print_entry (struct tcp_entry *te)
uint8_t *key = &te->key[1]; uint8_t *key = &te->key[1];
size_t size = te->key[0]; size_t size = te->key[0];
char ip0[INET6_ADDRSTRLEN]; char ip0[INET6_ADDRSTRLEN] = {0};
char ip1[INET6_ADDRSTRLEN]; char ip1[INET6_ADDRSTRLEN] = {0};
uint16_t port0, port1; uint16_t port0 = 0;
uint16_t port1 = 0;
switch (size) { switch (size) {
case 8+4: case 8+4:
@@ -881,9 +966,8 @@ static int gt_track (uint8_t **db, struct ip_common *ic, uint8_t *data, int rev)
return 0; return 0;
} }
uint32_t size = ic->size-ic->hdr_size-tcp.th_off*4; if (tcp.th_flags&TH_ACK) {
if (!r_entry) {
if (size && !r_entry) {
r_entry = calloc(1, sizeof(entry)); r_entry = calloc(1, sizeof(entry));
if (!r_entry) if (!r_entry)
@@ -895,29 +979,108 @@ static int gt_track (uint8_t **db, struct ip_common *ic, uint8_t *data, int rev)
free(r_entry); free(r_entry);
return 0; return 0;
} }
gt_print_entry(r_entry);
r_entry->data[1-rev].sa.base = tcp.th_ack;
r_entry->data[rev].sa.base = tcp.th_seq;
} else {
sa_rebase(&r_entry->data[1-rev].sa, tcp.th_ack);
}
} }
if (!r_entry) if (!r_entry)
return 0; return 0;
if (r_entry->data[1-rev].sa.count && (tcp.th_flags&TH_ACK)) uint32_t size = ic->size-ic->hdr_size-tcp.th_off*4;
sa_rebase(&r_entry->data[1-rev].sa, tcp.th_ack);
if (!size) if (!size)
return 0; return 0;
if (r_entry->data[rev].sa.count) { if (sa_have(&r_entry->data[rev].sa, tcp.th_seq, size)) {
if (sa_have(&r_entry->data[rev].sa, tcp.th_seq, size))
r_entry->data[rev].retrans++; r_entry->data[rev].retrans++;
} else { } else {
r_entry->data[rev].sa.base = tcp.th_seq; sa_insert(&r_entry->data[rev].sa, tcp.th_seq, size);
} }
sa_insert(&r_entry->data[rev].sa, tcp.th_seq, size);
return 0; return 0;
} }
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 tv.tv_sec*1000000ULL+tv.tv_nsec/1000ULL;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000000ULL+tv.tv_usec;
#endif
}
static void gt_bench (int chacha)
{
unsigned char npub[crypto_aead_aes256gcm_NPUBBYTES];
memset(npub, 0, sizeof(npub));
unsigned char key[crypto_aead_aes256gcm_KEYBYTES];
memset(key, 1, sizeof(key));
crypto_aead_aes256gcm_state ctx;
if (!chacha)
crypto_aead_aes256gcm_beforenm(&ctx, key);
gt_print("bench: %s\n", chacha?"chacha20poly1305":"aes256gcm");
_align_(16) unsigned char buf[32*1024+crypto_aead_aes256gcm_ABYTES];
size_t bs = 8;
while (!gt.quit && bs<=sizeof(buf)) {
size_t total_size = 0;
unsigned long long total_dt = 0.0;
double mbps = 0.0;
while (!gt.quit) {
unsigned long long now = gt_now();
size_t size = 0;
while (!gt.quit && size<16*1024*1024) {
if (chacha) {
crypto_aead_chacha20poly1305_encrypt(buf, NULL,
buf, bs, NULL, 0, NULL, npub, key);
} else {
crypto_aead_aes256gcm_encrypt_afternm(buf, NULL,
buf, bs, NULL, 0, NULL, npub,
(const crypto_aead_aes256gcm_state *)&ctx);
}
size += bs;
}
total_dt += gt_now()-now;
total_size += size;
double last_mbps = mbps;
mbps = total_size*8.0/total_dt;
double diff = mbps-last_mbps;
if (-0.1<diff && diff<0.1)
break;
}
gt_print("%6zu bytes %9.2f Mbps\n", bs, mbps);
bs *= 2;
}
}
static int gt_setup_secretkey (struct crypto_ctx *ctx, char *keyfile) static int gt_setup_secretkey (struct crypto_ctx *ctx, char *keyfile)
{ {
const size_t size = sizeof(ctx->skey); const size_t size = sizeof(ctx->skey);
@@ -927,8 +1090,7 @@ static int gt_setup_secretkey (struct crypto_ctx *ctx, char *keyfile)
randombytes_buf(ctx->skey, size); randombytes_buf(ctx->skey, size);
gt_tohex(buf, sizeof(buf), ctx->skey, size); gt_tohex(buf, sizeof(buf), ctx->skey, size);
state_send(gt.state_fd, "SECRETKEY", buf);
gt_print("new secret key: %s\n", buf);
return 0; return 0;
} }
@@ -964,17 +1126,16 @@ static int gt_setup_secretkey (struct crypto_ctx *ctx, char *keyfile)
static int gt_setup_crypto (struct crypto_ctx *ctx, int fd, int listener) static int gt_setup_crypto (struct crypto_ctx *ctx, int fd, int listener)
{ {
const uint8_t gt[] = {'G', 'T', VERSION_MAJOR, 0 }; const uint8_t proto[] = {'G', 'T', VERSION_MAJOR, (uint8_t)ctx->chacha };
const size_t size = 96; const size_t size = 96;
const size_t hash_size = 32; const size_t hash_size = 32;
const size_t nonce_size = crypto_aead_aes256gcm_NPUBBYTES;
const size_t public_size = crypto_scalarmult_SCALARBYTES;
uint8_t secret[crypto_scalarmult_SCALARBYTES]; uint8_t secret[crypto_scalarmult_SCALARBYTES];
uint8_t shared[crypto_scalarmult_BYTES]; uint8_t shared[crypto_scalarmult_BYTES];
uint8_t key[crypto_aead_aes256gcm_KEYBYTES];
uint8_t key_r[GT_KEYBYTES];
uint8_t key_w[GT_KEYBYTES];
uint8_t data_r[size], data_w[size]; uint8_t data_r[size], data_w[size];
uint8_t auth_r[hash_size], auth_w[hash_size]; uint8_t auth_r[hash_size], auth_w[hash_size];
@@ -983,12 +1144,11 @@ static int gt_setup_crypto (struct crypto_ctx *ctx, int fd, int listener)
crypto_generichash_state state; crypto_generichash_state state;
memset(data_w, 0, size); memset(data_w, 0, size);
randombytes_buf(data_w, nonce_size);
randombytes_buf(secret, sizeof(secret)); randombytes_buf(secret, sizeof(secret));
crypto_scalarmult_base(&data_w[nonce_size], secret); crypto_scalarmult_base(data_w, secret);
memcpy(&data_w[size-hash_size-sizeof(gt)], gt, sizeof(gt)); memcpy(&data_w[size-hash_size-sizeof(proto)], proto, sizeof(proto));
crypto_generichash(&data_w[size-hash_size], hash_size, crypto_generichash(&data_w[size-hash_size], hash_size,
data_w, size-hash_size, ctx->skey, sizeof(ctx->skey)); data_w, size-hash_size, ctx->skey, sizeof(ctx->skey));
@@ -999,14 +1159,26 @@ static int gt_setup_crypto (struct crypto_ctx *ctx, int fd, int listener)
if (fd_read_all(fd, data_r, size)!=size) if (fd_read_all(fd, data_r, size)!=size)
return -1; return -1;
if (memcmp(&data_r[size-hash_size-sizeof(gt)], gt, sizeof(gt))) if (memcmp(&data_r[size-hash_size-sizeof(proto)], proto, 3)) {
gt_log("bad packet [%02"PRIX8"%02"PRIX8"%02"PRIX8"] !\n",
&data_r[size-hash_size-sizeof(proto)+0],
&data_r[size-hash_size-sizeof(proto)+1],
&data_r[size-hash_size-sizeof(proto)+2]);
return -2; return -2;
}
if (data_r[size-hash_size-sizeof(proto)+3] && !ctx->chacha) {
gt_log("peer wants chacha20\n");
ctx->chacha = 1;
}
crypto_generichash(hash, hash_size, crypto_generichash(hash, hash_size,
data_r, size-hash_size, ctx->skey, sizeof(ctx->skey)); data_r, size-hash_size, ctx->skey, sizeof(ctx->skey));
if (sodium_memcmp(&data_r[size-hash_size], hash, hash_size)) if (sodium_memcmp(&data_r[size-hash_size], hash, hash_size)) {
gt_log("peer sends a bad hash!\n");
return -2; return -2;
}
if (listener && fd_write_all(fd, data_w, size)!=size) if (listener && fd_write_all(fd, data_w, size)!=size)
return -1; return -1;
@@ -1023,32 +1195,43 @@ static int gt_setup_crypto (struct crypto_ctx *ctx, int fd, int listener)
crypto_generichash(hash, hash_size, crypto_generichash(hash, hash_size,
data_w, size, ctx->skey, sizeof(ctx->skey)); data_w, size, ctx->skey, sizeof(ctx->skey));
if (sodium_memcmp(auth_r, hash, hash_size)) if (sodium_memcmp(auth_r, hash, hash_size)) {
gt_log("peer sends a bad hash (challenge-response)!\n");
return -2; return -2;
}
if (crypto_scalarmult(shared, secret, &data_r[nonce_size])) if (crypto_scalarmult(shared, secret, data_r)) {
gt_log("I'm just gonna hurt you really, really, BAD\n");
return -2; return -2;
}
crypto_generichash_init(&state, ctx->skey, sizeof(ctx->skey), sizeof(key)); crypto_generichash_init(&state, ctx->skey, sizeof(ctx->skey), sizeof(key_r));
crypto_generichash_update(&state, shared, sizeof(shared)); crypto_generichash_update(&state, shared, sizeof(shared));
crypto_generichash_update(&state, data_r, size); crypto_generichash_update(&state, data_r, size);
crypto_generichash_update(&state, data_w, size); crypto_generichash_update(&state, data_w, size);
crypto_generichash_final(&state, key, sizeof(key)); crypto_generichash_final(&state, key_r, sizeof(key_r));
crypto_aead_aes256gcm_beforenm(&ctx->read.state, key);
crypto_generichash_init(&state, ctx->skey, sizeof(ctx->skey), sizeof(key)); crypto_generichash_init(&state, ctx->skey, sizeof(ctx->skey), sizeof(key_w));
crypto_generichash_update(&state, shared, sizeof(shared)); crypto_generichash_update(&state, shared, sizeof(shared));
crypto_generichash_update(&state, data_w, size); crypto_generichash_update(&state, data_w, size);
crypto_generichash_update(&state, data_r, size); crypto_generichash_update(&state, data_r, size);
crypto_generichash_final(&state, key, sizeof(key)); crypto_generichash_final(&state, key_w, sizeof(key_w));
crypto_aead_aes256gcm_beforenm(&ctx->write.state, key);
if (ctx->chacha) {
memcpy(ctx->read.key, key_r, sizeof(key_r));
memcpy(ctx->write.key, key_w, sizeof(key_w));
} else {
crypto_aead_aes256gcm_beforenm(&ctx->read.key, key_r);
crypto_aead_aes256gcm_beforenm(&ctx->write.key, key_w);
}
sodium_memzero(secret, sizeof(secret)); sodium_memzero(secret, sizeof(secret));
sodium_memzero(shared, sizeof(shared)); sodium_memzero(shared, sizeof(shared));
sodium_memzero(key, sizeof(key)); sodium_memzero(key_r, sizeof(key_r));
sodium_memzero(key_w, sizeof(key_w));
memcpy(ctx->read.nonce, data_r, nonce_size); memset(ctx->read.nonce, 0, sizeof(ctx->read.nonce));
memcpy(ctx->write.nonce, data_w, nonce_size); memset(ctx->write.nonce, 0, sizeof(ctx->write.nonce));
return 0; return 0;
} }
@@ -1064,7 +1247,7 @@ int main (int argc, char **argv)
char *congestion = NULL; char *congestion = NULL;
char *statefile = NULL; char *statefile = NULL;
long buffer_size = GT_BUFFER_SIZE; long buffer_size = GT_PKT_MAX;
long ka_count = -1; long ka_count = -1;
long ka_idle = -1; long ka_idle = -1;
@@ -1075,7 +1258,7 @@ int main (int argc, char **argv)
long retry_const = 0; long retry_const = 0;
long retry_limit = 1000000; long retry_limit = 1000000;
long user_timeout = 0; gt.timeout = 5000;
struct option ka_opts[] = { struct option ka_opts[] = {
{ "count", &ka_count, option_long }, { "count", &ka_count, option_long },
@@ -1105,9 +1288,11 @@ int main (int argc, char **argv)
{ "buffer-size", &buffer_size, option_long }, { "buffer-size", &buffer_size, option_long },
{ "noquickack", NULL, option_option }, { "noquickack", NULL, option_option },
{ "retry", &retry_opts, option_option }, { "retry", &retry_opts, option_option },
{ "daemon", NULL, option_option },
{ "statefile", &statefile, option_str }, { "statefile", &statefile, option_str },
{ "timeout", &user_timeout, option_long }, { "timeout", &gt.timeout, option_long },
{ "bench", NULL, option_option },
{ "chacha20", NULL, option_option },
{ "mptcp", NULL, option_option },
{ "debug", NULL, option_option }, { "debug", NULL, option_option },
{ "version", NULL, option_option }, { "version", NULL, option_option },
{ NULL }, { NULL },
@@ -1127,9 +1312,28 @@ int main (int argc, char **argv)
const int noquickack = option_is_set(opts, "noquickack"); const int noquickack = option_is_set(opts, "noquickack");
const int debug = option_is_set(opts, "debug"); const int debug = option_is_set(opts, "debug");
if (buffer_size < 2048) { int chacha = option_is_set(opts, "chacha20");
buffer_size = 2048;
gt_log("buffer size must be greater than 2048\n"); gt.mptcp = option_is_set(opts, "mptcp");
if (sodium_init()==-1) {
gt_log("libsodium initialization has failed\n");
return 1;
}
if (!chacha && !crypto_aead_aes256gcm_is_available()) {
gt_na("AES-256-GCM");
chacha = 1;
}
if (option_is_set(opts, "bench")) {
gt_bench(chacha);
return 0;
}
if (buffer_size < GT_PKT_MAX) {
buffer_size = GT_PKT_MAX;
gt_log("buffer size must be greater than or equal to %li\n", buffer_size);
} }
if (!listener) { if (!listener) {
@@ -1142,18 +1346,8 @@ int main (int argc, char **argv)
retry_count = 0; retry_count = 0;
} }
if (statefile && statefile[0]!='/') { if (gt.timeout<=0 || gt.timeout>INT_MAX) {
gt_log("statefile must be an absolute path\n"); 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; return 1;
} }
@@ -1162,10 +1356,17 @@ int main (int argc, char **argv)
if (!ai) if (!ai)
return 1; return 1;
gt.state_fd = state_create(statefile);
if (statefile && gt.state_fd==-1)
return 1;
struct fdbuf tun = { .fd = -1 }; struct fdbuf tun = { .fd = -1 };
struct fdbuf sock = { .fd = -1 }; struct fdbuf sock = { .fd = -1 };
tun.fd = tun_create(dev, option_is_set(opts, "multiqueue")); char *tun_name = NULL;
tun.fd = tun_create(dev, &tun_name, option_is_set(opts, "multiqueue"));
if (tun.fd==-1) { if (tun.fd==-1) {
gt_log("couldn't create tun device\n"); gt_log("couldn't create tun device\n");
@@ -1194,52 +1395,12 @@ int main (int argc, char **argv)
if (gt_setup_secretkey(&ctx, keyfile)) if (gt_setup_secretkey(&ctx, keyfile))
return 1; return 1;
if (option_is_set(opts, "daemon")) {
switch (fork()) {
case -1:
perror("fork");
return 1;
case 0:
if (setsid()==-1)
perror("setsid");
break;
default:
_exit(0);
}
chdir("/");
}
int state_fd = -1;
if (statefile) {
state_fd = open(statefile, O_WRONLY);
if (state_fd==-1) {
if (errno!=EINTR)
perror("open statefile");
return 1;
}
struct stat st = {0};
if (fstat(state_fd, &st)==-1) {
perror("stat statefile");
return 1;
}
if (!S_ISFIFO(st.st_mode)) {
gt_log("`%s' is not a fifo\n", statefile);
return 1;
}
}
long retry = 0; long retry = 0;
uint8_t *db = NULL; uint8_t *db = NULL;
state_write(state_fd, "INITIALIZED\n"); state_send(gt.state_fd, "INITIALIZED", tun_name);
while (!gt_close) { while (!gt.quit) {
if (retry_count>=0 && retry>=retry_count+1) { if (retry_count>=0 && retry>=retry_count+1) {
gt_log("couldn't %s (%d attempt%s)\n", listener?"listen":"connect", gt_log("couldn't %s (%d attempt%s)\n", listener?"listen":"connect",
(int)retry, (retry>1)?"s":""); (int)retry, (retry>1)?"s":"");
@@ -1273,8 +1434,6 @@ int main (int argc, char **argv)
gt_log("%s: connected\n", sockname); gt_log("%s: connected\n", sockname);
fd_set_nonblock(sock.fd);
sk_set_int(sock.fd, sk_nodelay, !delay); sk_set_int(sock.fd, sk_nodelay, !delay);
sk_set_int(sock.fd, sk_keepalive, keepalive); sk_set_int(sock.fd, sk_keepalive, keepalive);
@@ -1289,25 +1448,19 @@ int main (int argc, char **argv)
sk_set_int(sock.fd, sk_keepintvl, ka_interval); sk_set_int(sock.fd, sk_keepintvl, ka_interval);
} }
if (user_timeout>0 && user_timeout<=INT_MAX) sk_set_int(sock.fd, sk_user_timeout, gt.timeout);
sk_set_int(sock.fd, sk_user_timeout, user_timeout);
sk_set(sock.fd, sk_congestion, congestion, str_len(congestion)); sk_set(sock.fd, sk_congestion, congestion, str_len(congestion));
switch (gt_setup_crypto(&ctx, sock.fd, listener)) { ctx.chacha = chacha;
case -2:
gt_log("%s: key exchange could not be verified!\n", sockname); if (gt_setup_crypto(&ctx, sock.fd, listener)) {
goto restart;
case -1:
gt_log("%s: key exchange failed\n", sockname); gt_log("%s: key exchange failed\n", sockname);
goto restart; goto restart;
default:
break;
} }
retry = 0; retry = 0;
state_write(state_fd, "STARTED\n"); state_send(gt.state_fd, "STARTED", tun_name);
fd_set rfds; fd_set rfds;
FD_ZERO(&rfds); FD_ZERO(&rfds);
@@ -1318,7 +1471,7 @@ int main (int argc, char **argv)
buffer_format(&sock.read); buffer_format(&sock.read);
while (1) { while (1) {
if _0_(gt_close) if _0_(gt.quit)
stop_loop |= 1; stop_loop |= 1;
if _0_(stop_loop) { if _0_(stop_loop) {
@@ -1345,9 +1498,12 @@ int main (int argc, char **argv)
} }
struct timeval timeout = { struct timeval timeout = {
.tv_usec = 1000, .tv_usec = 100000,
}; };
if (buffer_read_size(&sock.write))
timeout.tv_usec = 1000;
if _0_(select(sock.fd+1, &rfds, NULL, NULL, &timeout)==-1) { if _0_(select(sock.fd+1, &rfds, NULL, NULL, &timeout)==-1) {
if (errno==EINTR) if (errno==EINTR)
continue; continue;
@@ -1369,7 +1525,7 @@ int main (int argc, char **argv)
const ssize_t r = tun_read(tun.fd, tun.read.write, GT_MTU_MAX); const ssize_t r = tun_read(tun.fd, tun.read.write, GT_MTU_MAX);
if (r<=0) { if (r<=0) {
gt_close |= !r; gt.quit |= !r;
break; break;
} }
@@ -1394,6 +1550,8 @@ int main (int argc, char **argv)
} }
} }
buffer_shift(&sock.write);
if _1_(!stop_loop) if _1_(!stop_loop)
gt_encrypt(&ctx, &sock.write, &tun.read); gt_encrypt(&ctx, &sock.write, &tun.read);
@@ -1416,8 +1574,6 @@ int main (int argc, char **argv)
} }
} }
buffer_shift(&sock.write);
if (FD_ISSET(sock.fd, &rfds)) { if (FD_ISSET(sock.fd, &rfds)) {
if (noquickack) if (noquickack)
sk_set_int(sock.fd, sk_quickack, 0); sk_set_int(sock.fd, sk_quickack, 0);
@@ -1432,7 +1588,6 @@ int main (int argc, char **argv)
} }
} }
while (1) {
buffer_shift(&tun.write); buffer_shift(&tun.write);
if _0_(gt_decrypt(&ctx, &tun.write, &sock.read)) { if _0_(gt_decrypt(&ctx, &tun.write, &sock.read)) {
@@ -1440,6 +1595,7 @@ int main (int argc, char **argv)
goto restart; goto restart;
} }
while (1) {
size_t size = buffer_read_size(&tun.write); size_t size = buffer_read_size(&tun.write);
if (!size) if (!size)
@@ -1465,24 +1621,24 @@ int main (int argc, char **argv)
if (r==ic.size) if (r==ic.size)
tun.write.read += r; tun.write.read += r;
} else { } else {
gt_close |= !r; gt.quit |= !r;
break; break;
} }
} }
} }
restart: restart:
if (sockname) {
free(sockname);
sockname = NULL;
}
if (sock.fd!=-1) { if (sock.fd!=-1) {
close(sock.fd); close(sock.fd);
sock.fd = -1; sock.fd = -1;
} }
state_write(state_fd, "STOPPED\n"); state_send(gt.state_fd, "STOPPED", tun_name);
if (sockname) {
free(sockname);
sockname = NULL;
}
} }
freeaddrinfo(ai); freeaddrinfo(ai);

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

@@ -47,7 +47,7 @@ static inline size_t str_len (const char *restrict str)
return strlen(str); return strlen(str);
} }
static inline char *str_cat (const char *const strs[], size_t count) static inline char *str_cat (const char **strs, size_t count)
{ {
size_t size = 1; size_t size = 1;

View File

@@ -131,9 +131,9 @@ static int tun_create_by_id (char *name, size_t size, unsigned id, int mq)
#endif #endif
int tun_create (char *dev_name, int mq) int tun_create (char *dev_name, char **ret_name, int mq)
{ {
char name[64]; char name[64] = {0};
int fd = -1; int fd = -1;
#ifndef IFF_MULTI_QUEUE #ifndef IFF_MULTI_QUEUE
@@ -148,8 +148,8 @@ int tun_create (char *dev_name, int mq)
fd = tun_create_by_name(name, sizeof(name), dev_name, mq); fd = tun_create_by_name(name, sizeof(name), dev_name, mq);
} }
if (fd!=-1) if (fd!=-1 && ret_name)
gt_print("tun name: %s\n", name); *ret_name = strdup(name);
return fd; return fd;
} }

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
[ -z "${VERSION}" ] && VERSION=`git describe --tags --always 2>/dev/null` \ [ -z "${VERSION}" ] && VERSION=`git describe --tags --match='v[0-9].*' 2>/dev/null` \
&& VERSION=${VERSION#v} && VERSION=${VERSION#v}
[ -z "${VERSION}" ] && VERSION=`cat VERSION 2>/dev/null` [ -z "${VERSION}" ] && VERSION=`cat VERSION 2>/dev/null`