From 6cf76ca46dc1620431d536839472709895a60a9a Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 7 Aug 2013 21:57:09 +0200 Subject: Use multi-af tun mode on FreeBSD to make IPv6 work on tun --- src/fastd.c | 20 +++----------- src/fastd.h | 2 ++ src/tuntap.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/fastd.c b/src/fastd.c index 2c70007..aa22cc0 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -246,8 +246,7 @@ void fastd_handle_receive(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer fastd_peer_eth_addr_add(ctx, peer, src_addr); } - if (write(ctx->tunfd, buffer.data, buffer.len) < 0) - pr_warn_errno(ctx, "write"); + fastd_tuntap_write(ctx, buffer); if (ctx->conf->mode == MODE_TAP && ctx->conf->forward) { handle_forward(ctx, peer, buffer); @@ -477,20 +476,9 @@ static inline bool handle_tun_tap(fastd_context_t *ctx, fastd_buffer_t buffer) { } static void handle_tun(fastd_context_t *ctx) { - size_t max_len = fastd_max_packet_size(ctx); - fastd_buffer_t buffer = fastd_buffer_alloc(ctx, max_len, ctx->conf->min_encrypt_head_space, ctx->conf->min_encrypt_tail_space); - - ssize_t len = read(ctx->tunfd, buffer.data, max_len); - if (len < 0) { - if (errno == EINTR) { - fastd_buffer_free(buffer); - return; - } - - exit_errno(ctx, "read"); - } - - buffer.len = len; + fastd_buffer_t buffer = fastd_tuntap_read(ctx); + if (!buffer.len) + return; if (handle_tun_tap(ctx, buffer)) return; diff --git a/src/fastd.h b/src/fastd.h index 8ecc141..8201c5c 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -340,6 +340,8 @@ void fastd_config_load_peer_dirs(fastd_context_t *ctx, fastd_config_t *conf); void fastd_config_handle_options(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]); void fastd_tuntap_open(fastd_context_t *ctx); +fastd_buffer_t fastd_tuntap_read(fastd_context_t *ctx); +void fastd_tuntap_write(fastd_context_t *ctx, fastd_buffer_t buffer); void fastd_tuntap_close(fastd_context_t *ctx); void fastd_cap_init(fastd_context_t *ctx); diff --git a/src/tuntap.c b/src/tuntap.c index e94be05..4ee1660 100644 --- a/src/tuntap.c +++ b/src/tuntap.c @@ -84,12 +84,35 @@ void fastd_tuntap_open(fastd_context_t *ctx) { pr_debug(ctx, "tun/tap device initialized."); } +fastd_buffer_t fastd_tuntap_read(fastd_context_t *ctx) { + size_t max_len = fastd_max_packet_size(ctx); + fastd_buffer_t buffer = fastd_buffer_alloc(ctx, max_len, ctx->conf->min_encrypt_head_space, ctx->conf->min_encrypt_tail_space); + + ssize_t len = read(ctx->tunfd, buffer.data, max_len); + if (len < 0) { + if (errno == EINTR) { + fastd_buffer_free(buffer); + return (fastd_buffer_t){}; + } + + exit_errno(ctx, "read"); + } + + buffer.len = len; + return buffer; +} + +void fastd_tuntap_write(fastd_context_t *ctx, fastd_buffer_t buffer) { + if (write(ctx->tunfd, buffer.data, buffer.len) < 0) + pr_warn_errno(ctx, "write"); +} + #elif defined(__FreeBSD__) #include #include -static void get_tap_name(fastd_context_t *ctx) { +static void setup_tap(fastd_context_t *ctx) { struct ifreq ifr = {}; if (ioctl(ctx->tunfd, TAPGIFNAME, &ifr) < 0) @@ -99,6 +122,12 @@ static void get_tap_name(fastd_context_t *ctx) { ctx->ifname = strndup(ifr.ifr_name, IFNAMSIZ-1); } +static void setup_tun(fastd_context_t *ctx) { + int one = 1; + if (ioctl(ctx->tunfd, TUNSIFHEAD, &one) < 0) + exit_errno(ctx, "TUNSIFHEAD ioctl failed"); +} + static void set_tap_mtu(fastd_context_t *ctx) { struct tapinfo tapinfo; @@ -156,7 +185,7 @@ void fastd_tuntap_open(fastd_context_t *ctx) { if (strncmp(ctx->ifname, "tap", 3) != 0) exit_error(ctx, "opened device doesn't to be a tap device"); - get_tap_name(ctx); + setup_tap(ctx); set_tap_mtu(ctx); break; @@ -164,6 +193,7 @@ void fastd_tuntap_open(fastd_context_t *ctx) { if (strncmp(ctx->ifname, "tun", 3) != 0) exit_error(ctx, "opened device doesn't to be a tun device"); + setup_tun(ctx); set_tun_mtu(ctx); break; @@ -174,6 +204,60 @@ void fastd_tuntap_open(fastd_context_t *ctx) { pr_debug(ctx, "tun/tap device initialized."); } +fastd_buffer_t fastd_tuntap_read(fastd_context_t *ctx) { + size_t max_len = fastd_max_packet_size(ctx); + + fastd_buffer_t buffer; + if (ctx->conf->mode == MODE_TUN) + buffer = fastd_buffer_alloc(ctx, max_len+4, ctx->conf->min_encrypt_head_space+12, ctx->conf->min_encrypt_tail_space); + else + buffer = fastd_buffer_alloc(ctx, max_len, ctx->conf->min_encrypt_head_space, ctx->conf->min_encrypt_tail_space); + + ssize_t len = read(ctx->tunfd, buffer.data, max_len); + if (len < 0) { + if (errno == EINTR) { + fastd_buffer_free(buffer); + return (fastd_buffer_t){}; + } + + exit_errno(ctx, "read"); + } + + buffer.len = len; + + if (ctx->conf->mode == MODE_TUN) + fastd_buffer_push_head(ctx, &buffer, 4); + + return buffer; +} + +void fastd_tuntap_write(fastd_context_t *ctx, fastd_buffer_t buffer) { + if (ctx->conf->mode == MODE_TUN) { + uint8_t version = *((uint8_t*)buffer.data) >> 4; + int af; + + switch (version) { + case 4: + af = AF_INET; + break; + + case 6: + af = AF_INET6; + break; + + default: + pr_warn(ctx, "fastd_tuntap_write: unknown IP version %u", version); + return; + } + + fastd_buffer_pull_head(ctx, &buffer, 4); + *((uint32_t*)buffer.data) = htonl(af); + } + + if (write(ctx->tunfd, buffer.data, buffer.len) < 0) + pr_warn_errno(ctx, "write"); +} + #else #error unknown tun/tap implementation -- cgit v1.2.3