Compare commits

...

52 Commits

Author SHA1 Message Date
angt
8fa2322314 Add VERSION in the tarball 2016-01-01 12:32:20 +01:00
angt
8982f27220 Update LICENSE 2016-01-01 11:47:17 +01:00
angt
a5f97fcc8c States need EOL 2016-01-01 11:00:29 +01:00
angt
35a9bf27df Add state INITIALIZED 2015-12-31 16:07:36 +01:00
angt
ba0af8cc20 Define VERSION_MAJOR and use it in handshake 2015-12-29 18:31:23 +01:00
angt
ec85be5c6a Code cleanup 2015-12-29 12:59:55 +01:00
angt
fe989851ab Print more debug info 2015-12-29 12:58:39 +01:00
angt
46842dd200 Write state after the close() 2015-12-28 07:18:00 +01:00
angt
e5eb30598d Code cleanup 2015-12-24 17:38:08 +01:00
angt
c81ad0a7c6 One more time 2015-12-24 17:35:01 +01:00
angt
97641d6dda Reserve exclam for very bad message 2015-12-24 17:32:32 +01:00
angt
e48dac775c The statefile option needs an absolute path and a fifo 2015-12-24 17:29:41 +01:00
angt
6282f36ac7 Add statefile option 2015-12-24 16:53:50 +01:00
angt
cf022af4a9 Simplify the db by merging size and mask 2015-12-24 13:13:43 +01:00
angt
597c586657 Don't free data in db_remove() 2015-12-23 22:26:53 +01:00
angt
fc7f9aa0c8 Add a very simple low cost database 2015-12-23 17:11:20 +01:00
angt
7492f977b6 Define a simple proto 2015-12-21 12:34:24 +01:00
angt
38b7333533 Refuse to start the client without keyfile 2015-12-18 16:07:14 +01:00
angt
1ab854f058 Code cleanup 2015-12-18 11:57:20 +01:00
angt
c59def90fb Try to use SO_ACCEPTFILTER to defer accept on non-linux platforms 2015-12-17 23:20:31 +01:00
angt
347a3ecce7 Try to open tunX instead of glorytun by default in Linux 2015-12-17 17:06:04 +01:00
angt
98d13ef510 Print the tun name, not the path 2015-12-17 17:01:31 +01:00
angt
35b95001c4 Warn only when tun_create() fail 2015-12-17 14:07:14 +01:00
angt
75c12b36d6 Code cleanup 2015-12-17 13:59:40 +01:00
angt
db01c8b33f Update README.md 2015-12-17 08:51:07 +01:00
angt
fb4f6f6cb9 Do the md5sum after deploy 2015-12-16 11:27:53 +01:00
angt
a86aea431d Let travis do the md5sum 2015-12-16 08:35:20 +01:00
angt
1a128a6d92 Print readable ip in debug mode 2015-12-15 17:32:03 +01:00
angt
ddae22a3d9 Use a new random secret key without keyfile 2015-12-15 09:07:44 +01:00
angt
893de45272 Accept lower-case in fromhex() 2015-12-15 08:28:10 +01:00
angt
78ba4c9a59 Do not call ip_get_version() again and again 2015-12-14 18:37:50 +01:00
angt
ffa549e444 Fix and cleanup gt_{from,to}hex 2015-12-13 11:26:58 +01:00
angt
6040f17e1c Code cleanup 2015-12-13 11:07:55 +01:00
angt
da30c9110a Do not ask too much to macosx 2015-12-12 13:30:27 +01:00
angt
05de7b8109 Show udp hdr too in debug mode 2015-12-12 13:18:56 +01:00
angt
7cc6d08d7a Use __FAVOR_BSD... 2015-12-12 13:07:51 +01:00
angt
d526a3cfa5 Fix retry when kx fails 2015-12-12 12:19:09 +01:00
angt
0e319b068d Listener should retry accept() by default 2015-12-12 11:05:58 +01:00
angt
c82026cfd7 Update README.md 2015-12-11 17:44:16 +01:00
angt
109f70c208 Secret key must be stored in upper-case hex now 2015-12-11 17:33:35 +01:00
angt
23cdc37ea8 Add gt_tohex() and gt_fromhex() 2015-12-11 16:33:45 +01:00
angt
7688209093 Show tcp hdr in debug 2015-12-11 11:32:22 +01:00
angt
52a3a4b853 Add debug option to show ip_proto 2015-12-10 15:28:45 +01:00
angt
4cf0e7bc68 Function dt_ms() is pure too 2015-12-10 13:19:24 +01:00
angt
f36fde5054 Add ip_get_proto() 2015-12-10 13:17:27 +01:00
angt
e08eb73f98 Remove TCP_INFO 2015-12-10 12:33:54 +01:00
angt
f3143eff83 Do not print error for EPIPE or ECONNRESET on write() 2015-12-09 20:38:49 +01:00
angt
ea1fa120eb Allow IPv6 2015-12-09 20:27:40 +01:00
angt
be29a12842 Deploy only on linux-gcc 2015-12-09 11:25:51 +01:00
angt
113f1ae58d Use file_glob in travis 2015-12-08 18:27:59 +01:00
angt
73fff34bfe Try to deploy with travis 2015-12-08 18:10:39 +01:00
angt
84ae6dae32 Use printf instead of echo -n 2015-12-08 15:16:22 +01:00
14 changed files with 679 additions and 228 deletions

View File

@@ -18,3 +18,18 @@ before_script:
script: script:
- ./configure --disable-dependency-tracking && make distcheck - ./configure --disable-dependency-tracking && make distcheck
deploy:
provider: releases
api_key:
secure: Ira1jd3j+17QVFbJ7KlkytTNp0oMZLTqCWPQLxNHLEt32PVY/IPs/16BcvTbFpKrY+Ma7E3KCihfufTVG8T+jRlZ5hFO7HNpoQMgi7L7yZZAGScYO5pnMQvsr8O9hf7jzSi1DjFXgFHznbPJJ3lOSwegcqzSrbZflokiCR50GTLLWewHoPpfs+1gJ5XbtVyRhZlmXhMPC0NEH2sh33X2WdUiKUJUMOnlYC0atPBL+4n12qxnugF9YA9wPK5NHJSLiPHue7I2rHlSrOLX5NkN3vqnEvusiuIFQX1y0NuKtkpOscFD9n1CLHThGLuzMvPlwpdQfzRMVxwjYFGu6Ma5alf64ksFnMKl8Oo0yCXEA7WvHXz+qUai/a1gzYZLTGuasHtjrHN/mgKxM4QRSiHo8t5hj3CczUW+OFglytawZQyGjyUUiKdwx2kvlO96RVDIYEhUD4DeHzHR8rhvDLmlMlGAWqUPXq7d4D+bExqKmqWCCSkhrbubq5B1kChRmgw7/gly4SryqGy+R0xsC4oq9jUUxd7Sl801BIKcYDb5r5gFSw+hEhOzBb+uE2xJYFNO2KZ+eG69lkFeG3oNJMTy8afs8fRRUXq0poQJ39wFWerps3Md7h8Sxs61YLIAKHvLJJMfoMI+AORTcjnnpqujMhgWGWt8wLhJZdxaSycoQkU=
skip_cleanup: true
file_glob: true
file: glorytun-*.tar.gz
on:
tags: true
repo: angt/glorytun
condition: "${TRAVIS_OS_NAME}-${CC} == linux-gcc"
after_deploy:
- md5sum glorytun-*.tar.gz

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015, angt Copyright (c) 2015-2016, angt
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@@ -13,10 +13,13 @@ glorytun_SOURCES = \
src/option.c \ src/option.c \
src/option.h \ src/option.h \
src/tun.c \ src/tun.c \
src/tun.h src/tun.h \
src/db.c \
src/db.h
EXTRA_DIST = \ EXTRA_DIST = \
LICENSE \ LICENSE \
README.md \ README.md \
VERSION \
autogen.sh \ autogen.sh \
version.sh version.sh

View File

@@ -1,8 +1,12 @@
# Glorytun # Glorytun
Small, Simple and Stupid **TCP** VPN. Small, Simple and Stupid TCP VPN.
**Work In Progress:** Do not touch! This code will probably format your harddisk! #### Work In Progress
This code will probably format your harddisk!
#### 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. and needs an AES-NI capable CPU.
@@ -16,7 +20,4 @@ To build and install the latest version:
$ make $ make
# make install # make install
To create and use a new secret key: For feature requests and bug reports, please create an [issue](https://github.com/angt/glorytun/issues).
$ dd if=/dev/urandom of=glorytun.key bs=32 count=1
# glorytun keyfile glorytun.key [...]

View File

@@ -4,6 +4,7 @@ AC_INIT([glorytun],
[https://github.com/angt/glorytun/issues], [https://github.com/angt/glorytun/issues],
[glorytun], [glorytun],
[https://github.com/angt/glorytun]) [https://github.com/angt/glorytun])
AC_DEFINE_UNQUOTED([VERSION_MAJOR], [m4_esyscmd([./version.sh major])])
AC_CONFIG_SRCDIR([src/common.h]) AC_CONFIG_SRCDIR([src/common.h])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])

View File

@@ -18,7 +18,7 @@ static inline void byte_set (void *dst, const char value, size_t size)
static inline void byte_cpy (void *dst, const void *src, size_t size) static inline void byte_cpy (void *dst, const void *src, size_t size)
{ {
if (!dst || !src) if (!dst)
return; return;
char *restrict d = dst; char *restrict d = dst;
@@ -43,18 +43,28 @@ static inline size_t str_cpy (char *restrict dst, const char *restrict src, size
return i; return i;
} }
static inline int str_cmp (const char *restrict sa, const char *restrict sb) _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)
{ {
if (!sa || !sb) if (!sa || !sb)
return 1; return 1;
while (*sa==*sb++) size_t i = 0;
if (!*sa++)
while (sa[i]==sb[i])
if (!sa[i++])
return 0; return 0;
return 1; return i+1;
} }
_pure_
static inline size_t str_len (const char *restrict str) static inline size_t str_len (const char *restrict str)
{ {
if (!str) if (!str)
@@ -111,16 +121,19 @@ static inline void buffer_format (buffer_t *buffer)
buffer->read = buffer->data; buffer->read = buffer->data;
} }
_pure_
static inline size_t buffer_size (buffer_t *buffer) static inline size_t buffer_size (buffer_t *buffer)
{ {
return buffer->end-buffer->data; return buffer->end-buffer->data;
} }
_pure_
static inline size_t buffer_write_size (buffer_t *buffer) static inline size_t buffer_write_size (buffer_t *buffer)
{ {
return buffer->end-buffer->write; return buffer->end-buffer->write;
} }
_pure_
static inline size_t buffer_read_size (buffer_t *buffer) static inline size_t buffer_read_size (buffer_t *buffer)
{ {
return buffer->write-buffer->read; return buffer->write-buffer->read;

View File

@@ -36,5 +36,61 @@ void gt_fatal (const char *fmt, ...)
void gt_na (const char *name) void gt_na (const char *name)
{ {
gt_log("%s is not available on your platform!\n", 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)
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])];
}
*dst = 0;
return 0;
}
_const_
static inline int fromhex (const char c)
{
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;
return -1;
}
int gt_fromhex (uint8_t *dst, size_t dst_size, const char *src, size_t src_size)
{
if _0_(src_size&1)
return -1;
if _0_(dst_size<(src_size/2))
return -1;
for (size_t i=0; i<src_size; i+=2) {
const int a = fromhex(src[i]);
const int b = fromhex(src[i+1]);
if _0_(a==-1 || b==-1)
return -1;
*dst++ = (a<<4)|b;
}
return 0;
} }

View File

@@ -17,9 +17,13 @@
#define _1_(x) (__builtin_expect((x), 1)) #define _1_(x) (__builtin_expect((x), 1))
#define _0_(x) (__builtin_expect((x), 0)) #define _0_(x) (__builtin_expect((x), 0))
#define CLZ(x) (__builtin_clz(x))
#define _printf_(A,B) __attribute__ ((format(printf,A,B))) #define _printf_(A,B) __attribute__ ((format(printf,A,B)))
#define _noreturn_ __attribute__ ((noreturn)) #define _noreturn_ __attribute__ ((noreturn))
#define _unused_ __attribute__ ((unused)) #define _unused_ __attribute__ ((unused))
#define _pure_ __attribute__ ((pure))
#define _const_ __attribute__ ((const))
#define _align_(...) __attribute__ ((aligned(__VA_ARGS__))) #define _align_(...) __attribute__ ((aligned(__VA_ARGS__)))
typedef struct buffer buffer_t; typedef struct buffer buffer_t;
@@ -35,3 +39,6 @@ int gt_print (const char *, ...) _printf_(1,2);
void gt_log (const char *, ...) _printf_(1,2); void gt_log (const char *, ...) _printf_(1,2);
void gt_fatal (const char *, ...) _printf_(1,2) _noreturn_; void gt_fatal (const char *, ...) _printf_(1,2) _noreturn_;
void gt_na (const char *); 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);

153
src/db.c Normal file
View File

@@ -0,0 +1,153 @@
#include "db.h"
#include "common-static.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

@@ -2,6 +2,7 @@
#include <stdint.h> #include <stdint.h>
_pure_
static inline int ip_get_version (const uint8_t *data, size_t size) static inline int ip_get_version (const uint8_t *data, size_t size)
{ {
if (size<20) // XXX if (size<20) // XXX
@@ -10,17 +11,14 @@ static inline int ip_get_version (const uint8_t *data, size_t size)
return data[0]>>4; return data[0]>>4;
} }
static inline void ip_set_size (uint8_t *data, size_t size) _pure_
static inline ssize_t ip_get_size (const int ip_version, const uint8_t *data, size_t size)
{ {
data[2] = 0xFF&(size>>8); switch (ip_version) {
data[3] = 0xFF&(size);
}
static inline ssize_t ip_get_size (const uint8_t *data, size_t size)
{
switch (ip_get_version(data, size)) {
case 4: case 4:
return (data[2]<<8)|data[3]; return ((data[2]<<8)|data[3]);
case 6:
return ((data[4]<<8)|data[5])+40;
case -1: case -1:
return -1; return -1;
} }
@@ -28,11 +26,31 @@ static inline ssize_t ip_get_size (const uint8_t *data, size_t size)
return 0; return 0;
} }
static inline int ip_get_dscp (const uint8_t *data, size_t size) _pure_
static inline ssize_t ip_get_proto (const int ip_version, const uint8_t *data, size_t size)
{ {
switch (ip_get_version(data, size)) { switch (ip_version) {
case 4: case 4:
return data[1]>>2; return data[9];
case 6:
return data[6];
case -1:
return -1;
}
return 0;
}
_pure_
static inline ssize_t ip_get_hdr_size (const int ip_version, const uint8_t *data, size_t size)
{
switch (ip_version) {
case 4:
return (data[0]&0xF)<<2;
case 6:
return 40;
case -1:
return -1;
} }
return 0; return 0;

View File

@@ -2,11 +2,26 @@
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#include <string.h>
#include <poll.h> #include <poll.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <sys/socket.h> #include <sys/stat.h>
#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#define GT_FAKE_BSD
#endif
#include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef GT_FAKE_BSD
#undef GT_FAKE_BSD
#undef __FAVOR_BSD
#endif
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
@@ -50,6 +65,7 @@ struct crypto_ctx {
volatile sig_atomic_t gt_close = 0; volatile sig_atomic_t gt_close = 0;
volatile sig_atomic_t gt_info = 0; volatile sig_atomic_t gt_info = 0;
_pure_
static int64_t dt_ms (struct timeval *ta, struct timeval *tb) static int64_t dt_ms (struct timeval *ta, struct timeval *tb)
{ {
const int64_t s = ta->tv_sec-tb->tv_sec; const int64_t s = ta->tv_sec-tb->tv_sec;
@@ -84,6 +100,7 @@ enum sk_opt {
sk_keepintvl, sk_keepintvl,
sk_congestion, sk_congestion,
sk_defer_accept, sk_defer_accept,
sk_acceptfilter,
sk_quickack, sk_quickack,
}; };
@@ -129,6 +146,11 @@ static void sk_set (int fd, enum sk_opt opt, const void *val, socklen_t len)
[sk_quickack] = { "TCP_QUICKACK", [sk_quickack] = { "TCP_QUICKACK",
#ifdef TCP_QUICKACK #ifdef TCP_QUICKACK
1, IPPROTO_TCP, TCP_QUICKACK, 1, IPPROTO_TCP, TCP_QUICKACK,
#endif
},
[sk_acceptfilter] = { "SO_ACCEPTFILTER",
#ifdef SO_ACCEPTFILTER
1, SOL_SOCKET, SO_ACCEPTFILTER,
#endif #endif
}, },
}; };
@@ -151,21 +173,23 @@ static int sk_listen (int fd, struct addrinfo *ai)
{ {
sk_set_int(fd, sk_reuseaddr, 1); sk_set_int(fd, sk_reuseaddr, 1);
int ret = bind(fd, ai->ai_addr, ai->ai_addrlen); if (bind(fd, ai->ai_addr, ai->ai_addrlen)==-1) {
if (ret==-1) {
perror("bind"); perror("bind");
return -1; return -1;
} }
ret = listen(fd, 8); if (listen(fd, 8)==-1) {
if (ret==-1) {
perror("listen"); perror("listen");
return -1; return -1;
} }
#ifdef __linux__
sk_set_int(fd, sk_defer_accept, GT_TIMEOUT/1000); sk_set_int(fd, sk_defer_accept, GT_TIMEOUT/1000);
#else
char data[256] = {0};
str_cpy(data, "dataready", sizeof(data)-1);
sk_set(fd, sk_acceptfilter, &data, sizeof(data));
#endif
return 0; return 0;
} }
@@ -245,38 +269,6 @@ static char *sk_get_name (int fd)
return str_cat(strs, COUNT(strs)); return str_cat(strs, COUNT(strs));
} }
#ifdef TCP_INFO
static socklen_t sk_get_info (int fd, struct tcp_info *ti)
{
socklen_t len = sizeof(struct tcp_info);
if (getsockopt(fd, SOL_TCP, TCP_INFO, ti, &len)==-1) {
perror("getsockopt TCP_INFO");
return 0;
}
return len;
}
static void print_tcp_info (const char *name, struct tcp_info *ti)
{
gt_log("%s: tcpinfo"
" rto:%" PRIu32 " ato:%" PRIu32 " snd_mss:%" PRIu32
" rcv_mss:%" PRIu32 " unacked:%" PRIu32 " sacked:%" PRIu32
" lost:%" PRIu32 " retrans:%" PRIu32 " fackets:%" PRIu32
" pmtu:%" PRIu32 " rcv_ssthresh:%" PRIu32 " rtt:%" PRIu32
" rttvar:%" PRIu32 " snd_ssthresh:%" PRIu32 " snd_cwnd:%" PRIu32
" advmss:%" PRIu32 " reordering:%" PRIu32 "\n",
name,
ti->tcpi_rto, ti->tcpi_ato, ti->tcpi_snd_mss,
ti->tcpi_rcv_mss, ti->tcpi_unacked, ti->tcpi_sacked,
ti->tcpi_lost, ti->tcpi_retrans, ti->tcpi_fackets,
ti->tcpi_pmtu, ti->tcpi_rcv_ssthresh, ti->tcpi_rtt,
ti->tcpi_rttvar, ti->tcpi_snd_ssthresh, ti->tcpi_snd_cwnd,
ti->tcpi_advmss, ti->tcpi_reordering);
}
#endif
static struct addrinfo *ai_create (const char *host, const char *port, int listener) static struct addrinfo *ai_create (const char *host, const char *port, int listener)
{ {
if (!port || !port[0]) { if (!port || !port[0]) {
@@ -351,7 +343,7 @@ static void gt_set_signal (void)
static ssize_t fd_read (int fd, void *data, size_t size) static ssize_t fd_read (int fd, void *data, size_t size)
{ {
if (!size) if ((fd==-1) || !size)
return -1; return -1;
ssize_t ret = read(fd, data, size); ssize_t ret = read(fd, data, size);
@@ -371,7 +363,7 @@ static ssize_t fd_read (int fd, void *data, size_t size)
static ssize_t fd_write (int fd, const void *data, size_t size) static ssize_t fd_write (int fd, const void *data, size_t size)
{ {
if (!size) if ((fd==-1) || !size)
return -1; return -1;
ssize_t ret = write(fd, data, size); ssize_t ret = write(fd, data, size);
@@ -380,6 +372,9 @@ static ssize_t fd_write (int fd, const void *data, size_t size)
if (errno==EAGAIN || errno==EINTR) if (errno==EAGAIN || errno==EINTR)
return -1; return -1;
if (errno==EPIPE || errno==ECONNRESET)
return 0;
if (errno) if (errno)
perror("write"); perror("write");
@@ -389,15 +384,15 @@ static ssize_t fd_write (int fd, const void *data, size_t size)
return ret; return ret;
} }
static ssize_t fd_read_all (int fd, void *data, size_t size) static ssize_t fd_write_str (int fd, const char *str)
{
return fd_write(fd, str, str_len(str));
}
static size_t fd_read_all (int fd, void *data, size_t size)
{ {
size_t done = 0; size_t done = 0;
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN,
};
while (done<size) { while (done<size) {
ssize_t ret = fd_read(fd, (uint8_t *)data+done, size-done); ssize_t ret = fd_read(fd, (uint8_t *)data+done, size-done);
@@ -405,8 +400,14 @@ static ssize_t fd_read_all (int fd, void *data, size_t size)
break; break;
if (ret<0) { if (ret<0) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLIN,
};
if (!poll(&pollfd, 1, GT_TIMEOUT)) if (!poll(&pollfd, 1, GT_TIMEOUT))
break; break;
continue; continue;
} }
@@ -416,15 +417,10 @@ static ssize_t fd_read_all (int fd, void *data, size_t size)
return done; return done;
} }
static ssize_t fd_write_all (int fd, const void *data, size_t size) static size_t fd_write_all (int fd, const void *data, size_t size)
{ {
size_t done = 0; size_t done = 0;
struct pollfd pollfd = {
.fd = fd,
.events = POLLOUT,
};
while (done<size) { while (done<size) {
ssize_t ret = fd_write(fd, (const uint8_t *)data+done, size-done); ssize_t ret = fd_write(fd, (const uint8_t *)data+done, size-done);
@@ -432,8 +428,14 @@ static ssize_t fd_write_all (int fd, const void *data, size_t size)
break; break;
if (ret<0) { if (ret<0) {
struct pollfd pollfd = {
.fd = fd,
.events = POLLOUT,
};
if (!poll(&pollfd, 1, GT_TIMEOUT)) if (!poll(&pollfd, 1, GT_TIMEOUT))
break; break;
continue; continue;
} }
@@ -510,32 +512,113 @@ static int gt_decrypt (struct crypto_ctx *ctx, buffer_t *dst, buffer_t *src)
return 0; return 0;
} }
static void dump_ip_header (uint8_t *data, size_t size) _pure_
static inline uint32_t sum16 (uint32_t sum, const uint8_t *data, const size_t size)
{ {
if (size<20) const size_t lim = size&~1u;
return;
const char tbl[] = "0123456789ABCDEF"; for (size_t i=0; i<lim; i+=2)
char hex[41]; sum += (data[i]<<8)|data[i+1];
for (size_t i=0; i<20; i++) { if (size&1)
hex[(i<<1)+0] = tbl[0xF&(data[i]>>4)]; sum += data[size-1]<<8;
hex[(i<<1)+1] = tbl[0xF&(data[i])];
return sum;
} }
hex[40] = 0; _const_
static inline uint16_t sum16_final (uint32_t sum)
{
sum = (sum>>16)+(sum&0xFFFF);
return ~(sum+(sum>>16));
}
gt_log("DUMP(%zu): %s\n", size, hex); static void gt_print_hdr (const int ip_version, uint8_t *data, size_t ip_size)
{
const ssize_t ip_proto = ip_get_proto(ip_version, data, ip_size);
const ssize_t ip_hdr_size = ip_get_hdr_size(ip_version, data, ip_size);
if (ip_proto<0 || ip_hdr_size<=0)
return;
uint32_t sum = (size_t)ip_proto+ip_size-(size_t)ip_hdr_size;
char ip_src[INET6_ADDRSTRLEN];
char ip_dst[INET6_ADDRSTRLEN];
switch (ip_version) {
case 4:
inet_ntop(AF_INET, &data[12], ip_src, sizeof(ip_src));
inet_ntop(AF_INET, &data[16], ip_dst, sizeof(ip_dst));
sum = sum16(sum, &data[12], 2*4);
break;
case 6:
inet_ntop(AF_INET6, &data[9], ip_src, sizeof(ip_src));
inet_ntop(AF_INET6, &data[25], ip_dst, sizeof(ip_dst));
sum = sum16(sum, &data[9], 2*16); // XXX
break;
}
if (ip_proto==IPPROTO_TCP) {
struct tcphdr tcp;
byte_cpy(&tcp, &data[ip_hdr_size], sizeof(tcp));
uint16_t tcp_sum = ntohs(tcp.th_sum);
tcp.th_sum = 0;
sum = sum16(sum, (uint8_t *)&tcp, sizeof(tcp));
sum = sum16(sum, &data[ip_hdr_size+sizeof(tcp)], ip_size-ip_hdr_size-sizeof(tcp));
uint16_t computed_sum = sum16_final(sum);
tcp.th_sport = ntohs(tcp.th_sport);
tcp.th_dport = ntohs(tcp.th_dport);
tcp.th_seq = ntohl(tcp.th_seq);
tcp.th_ack = ntohl(tcp.th_ack);
tcp.th_win = ntohs(tcp.th_win);
gt_print("proto:%zi\tsrc:%s.%u\tdst:%s.%u\tseq:%u\tack:%u\twin:%u\tsize:%zu\tflags:%c%c%c%c%c%c\tsum:%i\n",
ip_proto, ip_src, tcp.th_sport, ip_dst, tcp.th_dport,
tcp.th_seq, tcp.th_ack, tcp.th_win, ip_size-ip_hdr_size+tcp.th_off*4,
(tcp.th_flags&TH_FIN) ?'F':'.',
(tcp.th_flags&TH_SYN) ?'S':'.',
(tcp.th_flags&TH_RST) ?'R':'.',
(tcp.th_flags&TH_PUSH)?'P':'.',
(tcp.th_flags&TH_ACK) ?'A':'.',
(tcp.th_flags&TH_URG) ?'U':'.',
(computed_sum==tcp_sum));
} else if (ip_proto==IPPROTO_UDP) {
struct udphdr udp;
byte_cpy(&udp, &data[ip_hdr_size], sizeof(udp));
udp.uh_sport = ntohs(udp.uh_sport);
udp.uh_dport = ntohs(udp.uh_dport);
udp.uh_ulen = ntohs(udp.uh_ulen);
gt_print("proto:%zi\tsrc:%s.%u\tdst:%s.%u\tsize:%u\n",
ip_proto, ip_src, udp.uh_sport, ip_dst, udp.uh_dport, udp.uh_ulen-8);
} else {
gt_print("proto:%zi\tsrc:%s\tdst:%s\tsize:%zu\n",
ip_proto, ip_src, ip_dst, ip_size);
}
} }
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);
byte_set(ctx->skey, 1, size); if (!keyfile) {
char buf[2*size+1];
randombytes_buf(ctx->skey, size);
gt_tohex(buf, sizeof(buf), ctx->skey, size);
gt_print("new secret key: %s\n", buf);
if (!keyfile)
return 0; return 0;
}
int fd; int fd;
@@ -548,23 +631,33 @@ static int gt_setup_secretkey (struct crypto_ctx *ctx, char *keyfile)
return -1; return -1;
} }
if (fd_read_all(fd, ctx->skey, size)!=size) { char key[2*size];
gt_log("unable to read secret key in `%s'\n", keyfile); size_t r = fd_read_all(fd, key, sizeof(key));
close(fd); close(fd);
if (r!=sizeof(key)) {
gt_log("unable to read secret key\n");
return -1; return -1;
} }
close(fd); if (gt_fromhex(ctx->skey, size, key, sizeof(key))) {
gt_log("secret key is not valid\n");
return -1;
}
return 0; return 0;
} }
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 size_t size = 96;
const size_t hash_size = 32;
const size_t nonce_size = crypto_aead_aes256gcm_NPUBBYTES; const size_t nonce_size = crypto_aead_aes256gcm_NPUBBYTES;
const size_t public_size = crypto_scalarmult_SCALARBYTES; const size_t public_size = crypto_scalarmult_SCALARBYTES;
const size_t hash_size = crypto_generichash_BYTES;
const size_t size = nonce_size + public_size + hash_size;
uint8_t secret[crypto_scalarmult_SCALARBYTES]; uint8_t secret[crypto_scalarmult_SCALARBYTES];
uint8_t shared[crypto_scalarmult_BYTES]; uint8_t shared[crypto_scalarmult_BYTES];
@@ -576,10 +669,14 @@ static int gt_setup_crypto (struct crypto_ctx *ctx, int fd, int listener)
crypto_generichash_state state; crypto_generichash_state state;
byte_set(data_w, 0, size);
randombytes_buf(data_w, nonce_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[nonce_size], secret);
byte_cpy(&data_w[size-hash_size-sizeof(gt)], gt, sizeof(gt));
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));
@@ -589,6 +686,9 @@ 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)))
return -2;
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));
@@ -646,9 +746,10 @@ int main (int argc, char **argv)
char *host = NULL; char *host = NULL;
char *port = "5000"; char *port = "5000";
char *dev = PACKAGE_NAME; char *dev = NULL;
char *keyfile = NULL; char *keyfile = NULL;
char *congestion = NULL; char *congestion = NULL;
char *statefile = NULL;
long buffer_size = GT_BUFFER_SIZE; long buffer_size = GT_BUFFER_SIZE;
@@ -656,8 +757,8 @@ int main (int argc, char **argv)
long ka_idle = -1; long ka_idle = -1;
long ka_interval = -1; long ka_interval = -1;
long retry_count = 0; long retry_count = -1;
long retry_slope = 1000; long retry_slope = 0;
long retry_const = 0; long retry_const = 0;
long retry_limit = 1000000; long retry_limit = 1000000;
@@ -690,6 +791,8 @@ int main (int argc, char **argv)
{ "noquickack", NULL, option_option }, { "noquickack", NULL, option_option },
{ "retry", &retry_opts, option_option }, { "retry", &retry_opts, option_option },
{ "daemon", NULL, option_option }, { "daemon", NULL, option_option },
{ "statefile", &statefile, option_str },
{ "debug", NULL, option_option },
{ "version", NULL, option_option }, { "version", NULL, option_option },
{ NULL }, { NULL },
}; };
@@ -706,14 +809,30 @@ int main (int argc, char **argv)
const int delay = option_is_set(opts, "delay"); const int delay = option_is_set(opts, "delay");
const int keepalive = option_is_set(opts, "keepalive"); const int keepalive = option_is_set(opts, "keepalive");
const int noquickack = option_is_set(opts, "noquickack"); const int noquickack = option_is_set(opts, "noquickack");
const int debug = option_is_set(opts, "debug");
if (buffer_size < 2048) { if (buffer_size < 2048) {
buffer_size = 2048; buffer_size = 2048;
gt_log("buffer size must be greater than 2048!\n"); gt_log("buffer size must be greater than 2048\n");
}
if (!listener) {
if (!option_is_set(opts, "keyfile")) {
gt_log("keyfile option must be set\n");
return 1;
}
if (!option_is_set(opts, "retry"))
retry_count = 0;
}
if (statefile && statefile[0]!='/') {
gt_log("statefile must be an absolute path\n");
return 1;
} }
if (sodium_init()==-1) { if (sodium_init()==-1) {
gt_log("libsodium initialization has failed!\n"); gt_log("libsodium initialization has failed\n");
return 1; return 1;
} }
@@ -722,11 +841,6 @@ int main (int argc, char **argv)
return 1; return 1;
} }
struct crypto_ctx ctx;
if (gt_setup_secretkey(&ctx, keyfile))
return 1;
struct addrinfo *ai = ai_create(host, port, listener); struct addrinfo *ai = ai_create(host, port, listener);
if (!ai) if (!ai)
@@ -737,8 +851,10 @@ int main (int argc, char **argv)
tun.fd = tun_create(dev, option_is_set(opts, "multiqueue")); tun.fd = tun_create(dev, option_is_set(opts, "multiqueue"));
if (tun.fd==-1) if (tun.fd==-1) {
gt_log("couldn't create tun device\n");
return 1; return 1;
}
struct blk *blks = calloc(256, sizeof(struct blk)); struct blk *blks = calloc(256, sizeof(struct blk));
size_t blk_count = 0; size_t blk_count = 0;
@@ -765,6 +881,11 @@ int main (int argc, char **argv)
return 1; return 1;
} }
struct crypto_ctx ctx;
if (gt_setup_secretkey(&ctx, keyfile))
return 1;
if (option_is_set(opts, "daemon")) { if (option_is_set(opts, "daemon")) {
switch (fork()) { switch (fork()) {
case -1: case -1:
@@ -781,36 +902,59 @@ int main (int argc, char **argv)
chdir("/"); 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;
fd_write_str(state_fd, "INITIALIZED\n");
while (!gt_close) { while (!gt_close) {
sock.fd = listener?sk_accept(fd):sk_create(ai, sk_connect); if (retry_count>=0 && retry>=retry_count+1) {
gt_log("couldn't %s (%d attempt%s)\n", listener?"listen":"connect",
if (sock.fd==-1) {
if (retry<LONG_MAX)
retry++;
long usec = retry*retry_slope+retry_const;
if (retry_count>=0 && retry>=retry_count) {
gt_log("couldn't %s (%d attempt%s)\n",
listener?"listen":"connect",
(int)retry, (retry>1)?"s":""); (int)retry, (retry>1)?"s":"");
break; break;
} }
if (retry_slope || retry_const) {
long usec = retry*retry_slope+retry_const;
if (usec>retry_limit) if (usec>retry_limit)
usec = retry_limit; usec = retry_limit;
if (usec<=0) if (usec>0 && usleep(usec)==-1 && errno==EINVAL)
usec = 0;
if (usleep(usec)==-1 && errno==EINVAL)
sleep(usec/1000000); sleep(usec/1000000);
continue;
} }
if (retry<LONG_MAX)
retry++;
sock.fd = listener?sk_accept(fd):sk_create(ai, sk_connect);
if (sock.fd==-1)
continue;
char *sockname = sk_get_name(sock.fd); char *sockname = sk_get_name(sock.fd);
if (!sockname) { if (!sockname) {
@@ -851,7 +995,7 @@ int main (int argc, char **argv)
retry = 0; retry = 0;
gt_log("%s: running\n", sockname); fd_write_str(state_fd, "STARTED\n");
fd_set rfds; fd_set rfds;
FD_ZERO(&rfds); FD_ZERO(&rfds);
@@ -901,17 +1045,6 @@ int main (int argc, char **argv)
// struct timeval now; // struct timeval now;
// gettimeofday(&now, NULL); // gettimeofday(&now, NULL);
#ifdef TCP_INFO
if _0_(gt_info) {
struct tcp_info ti;
if (sk_get_info(sock.fd, &ti))
print_tcp_info(sockname, &ti);
gt_info = 0;
}
#endif
if (FD_ISSET(tun.fd, &rfds)) { if (FD_ISSET(tun.fd, &rfds)) {
while (!blks[blk_write].size) { while (!blks[blk_write].size) {
uint8_t *data = blks[blk_write].data; uint8_t *data = blks[blk_write].data;
@@ -922,20 +1055,22 @@ int main (int argc, char **argv)
break; break;
} }
const ssize_t ip_size = ip_get_size(data, GT_MTU_MAX); const int ip_version = ip_get_version(data, GT_MTU_MAX);
const ssize_t ip_size = ip_get_size(ip_version, data, GT_MTU_MAX);
if _0_(ip_size<=0) if _0_(ip_size<=0)
continue; continue;
if _0_(ip_size!=r) { if _0_(ip_size!=r) {
dump_ip_header(data, r); char tmp[2*GT_MTU_MAX+1];
gt_tohex(tmp, sizeof(tmp), data, r);
if (r>ip_size) gt_log("%s: DUMP %zi %s\n", sockname, r, tmp);
continue; continue;
ip_set_size(data, r);
} }
if _0_(debug)
gt_print_hdr(ip_version, data, ip_size);
blks[blk_write++].size = r; blks[blk_write++].size = r;
blk_count++; blk_count++;
} }
@@ -1009,7 +1144,9 @@ int main (int argc, char **argv)
} }
size_t size = buffer_read_size(&tun.write); size_t size = buffer_read_size(&tun.write);
ssize_t ip_size = ip_get_size(tun.write.read, size);
const int ip_version = ip_get_version(tun.write.read, size);
ssize_t ip_size = ip_get_size(ip_version, tun.write.read, size);
if _0_(!ip_size) { if _0_(!ip_size) {
gt_log("%s: bad packet!\n", sockname); gt_log("%s: bad packet!\n", sockname);
@@ -1022,6 +1159,8 @@ int main (int argc, char **argv)
ssize_t r = tun_write(tun.fd, tun.write.read, ip_size); ssize_t r = tun_write(tun.fd, tun.write.read, ip_size);
if (r>0) { if (r>0) {
if _0_(debug)
gt_print_hdr(ip_version, tun.write.read, ip_size);
tun.write.read += r; tun.write.read += r;
} else { } else {
gt_close |= !r; gt_close |= !r;
@@ -1040,6 +1179,8 @@ int main (int argc, char **argv)
close(sock.fd); close(sock.fd);
sock.fd = -1; sock.fd = -1;
} }
fd_write_str(state_fd, "STOPPED\n");
} }
freeaddrinfo(ai); freeaddrinfo(ai);

154
src/tun.c
View File

@@ -22,61 +22,26 @@
#endif #endif
#if defined(__APPLE__) || defined(__OpenBSD__) #if defined(__APPLE__) || defined(__OpenBSD__)
# define GT_BSD_TUN 1 #define GT_BSD_TUN
#endif #endif
#ifdef __linux__ #ifdef __APPLE__
int tun_create (char *name, int multiqueue)
static int tun_create_by_id (char *name, size_t size, unsigned id, _unused_ int mq)
{ {
int fd = open("/dev/net/tun", O_RDWR);
if (fd<0) {
perror("open /dev/net/tun");
return -1;
}
struct ifreq ifr = {
.ifr_flags = IFF_TUN|IFF_NO_PI,
};
if (multiqueue) {
#ifdef IFF_MULTI_QUEUE
ifr.ifr_flags |= IFF_MULTI_QUEUE;
#else
gt_na("IFF_MULTI_QUEUE");
#endif
}
str_cpy(ifr.ifr_name, name, IFNAMSIZ-1);
int ret = ioctl(fd, TUNSETIFF, &ifr);
if (ret<0) {
perror("ioctl TUNSETIFF");
return -1;
}
gt_log("tun name: %s\n", ifr.ifr_name);
return fd;
}
#elif defined(__APPLE__)
int tun_create (_unused_ char *name, _unused_ int mq)
{
for (unsigned dev_id = 0; dev_id < 32; dev_id++) {
struct ctl_info ci;
byte_set(&ci, 0, sizeof(ci));
str_cpy(ci.ctl_name, UTUN_CONTROL_NAME, sizeof(ci.ctl_name)-1);
int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (fd==-1) if (fd==-1)
return -1; return -1;
if (ioctl(fd, CTLIOCGINFO, &ci)==-1) { struct ctl_info ci;
byte_set(&ci, 0, sizeof(ci));
str_cpy(ci.ctl_name, UTUN_CONTROL_NAME, sizeof(ci.ctl_name)-1);
if (ioctl(fd, CTLIOCGINFO, &ci)) {
close(fd); close(fd);
continue; return -1;
} }
struct sockaddr_ctl sc = { struct sockaddr_ctl sc = {
@@ -84,43 +49,110 @@ int tun_create (_unused_ char *name, _unused_ int mq)
.sc_len = sizeof(sc), .sc_len = sizeof(sc),
.sc_family = AF_SYSTEM, .sc_family = AF_SYSTEM,
.ss_sysaddr = AF_SYS_CONTROL, .ss_sysaddr = AF_SYS_CONTROL,
.sc_unit = dev_id+1, .sc_unit = id+1,
}; };
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc))==-1) { if (connect(fd, (struct sockaddr *)&sc, sizeof(sc))) {
close(fd); close(fd);
continue; return -1;
} }
gt_log("tun name: /dev/utun%u\n", dev_id); snprintf(name, size, "utun%u", id);
return fd; return fd;
} }
return -1; static int tun_create_by_name (char *name, size_t size, char *dev_name, int mq)
}
#else
int tun_create (_unused_ char *name, _unused_ int mq)
{ {
for (unsigned dev_id = 0; dev_id < 32; dev_id++) { unsigned id = 0;
char dev_path[11];
snprintf(dev_path, sizeof(dev_path), "/dev/tun%u", dev_id); if (sscanf(dev_name, "utun%u", &id)!=1)
return -1;
int fd = open(dev_path, O_RDWR); return tun_create_by_id(name, size, id, mq);
}
#else /* not __APPLE__ */
#ifdef __linux__
static int tun_create_by_name (char *name, size_t size, char *dev_name, int mq)
{
int fd = open("/dev/net/tun", O_RDWR);
if (fd==-1) if (fd==-1)
continue; return -1;
gt_log("tun name: %s\n", dev_path); 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)) {
close(fd);
return -1;
}
str_cpy(name, ifr.ifr_name, size-1);
return fd; return fd;
} }
return -1; #else /* not __linux__ not __APPLE__ */
static int tun_create_by_name (char *name, size_t size, char *dev_name, _unused_ int mq)
{
char path[64];
snprintf(path, sizeof(path), "/dev/%s", dev_name);
str_cpy(name, dev_name, size-1);
return open(path, O_RDWR);
} }
#endif /* not __APPLE__ */
static int tun_create_by_id (char *name, size_t size, unsigned id, int mq)
{
char dev_name[64];
snprintf(dev_name, sizeof(dev_name), "tun%u", id);
return tun_create_by_name(name, size, dev_name, mq);
}
#endif #endif
int tun_create (char *dev_name, int mq)
{
char name[64];
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, sizeof(name), id, mq);
} else {
fd = tun_create_by_name(name, sizeof(name), dev_name, mq);
}
if (fd!=-1)
gt_print("tun name: %s\n", name);
return fd;
}
ssize_t tun_read (int fd, void *data, size_t size) ssize_t tun_read (int fd, void *data, size_t size)
{ {
if (!size) if (!size)

View File

@@ -3,7 +3,11 @@
[ -z "${VERSION}" ] && VERSION=`git describe --tags --always 2>/dev/null` \ [ -z "${VERSION}" ] && VERSION=`git describe --tags --always 2>/dev/null` \
&& VERSION=${VERSION#v} && VERSION=${VERSION#v}
[ -z "${VERSION}" ] && VERSION=`basename \`pwd\`` \ [ -z "${VERSION}" ] && VERSION=`cat VERSION 2>/dev/null`
&& VERSION=${VERSION#*-}
echo -n ${VERSION} [ -z "${VERSION}" ] && VERSION=0.0.0
[ "$1" = "major" ] && printf ${VERSION%%.*} \
&& exit 0
printf ${VERSION} | tee VERSION