Move socket handling out of fastd.c

This commit is contained in:
Matthias Schiffer 2013-07-25 14:22:19 +02:00
parent a8304e288b
commit c3dee51736
4 changed files with 205 additions and 172 deletions

View file

@ -27,6 +27,7 @@ add_executable(fastd
random.c random.c
resolve.c resolve.c
shell.c shell.c
socket.c
task.c task.c
protocol_ec25519_fhmqvc.c protocol_ec25519_fhmqvc.c
${FLEX_fastd_config_lex_OUTPUTS} ${FLEX_fastd_config_lex_OUTPUTS}

View file

@ -191,176 +191,6 @@ static void init_sockets(fastd_context_t *ctx) {
ctx->n_socks = ctx->conf->n_bind_addrs; ctx->n_socks = ctx->conf->n_bind_addrs;
} }
static int bind_socket(fastd_context_t *ctx, const fastd_bind_address_t *addr, bool warn) {
int fd = -1;
int af = AF_UNSPEC;
if (addr->addr.sa.sa_family != AF_INET) {
fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (fd >= 0) {
af = AF_INET6;
int val = (addr->addr.sa.sa_family == AF_INET6);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) {
if (warn)
pr_warn_errno(ctx, "setsockopt");
goto error;
}
}
}
if (fd < 0 && addr->addr.sa.sa_family != AF_INET6) {
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0)
exit_errno(ctx, "unable to create socket");
else
af = AF_INET;
}
if (fd < 0)
goto error;
fastd_setfd(ctx, fd, FD_CLOEXEC, 0);
fastd_setfl(ctx, fd, O_NONBLOCK, 0);
int one = 1;
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one))) {
pr_error_errno(ctx, "setsockopt: unable to set IP_PKTINFO");
goto error;
}
if (af == AF_INET6) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one))) {
pr_error_errno(ctx, "setsockopt: unable to set IPV6_RECVPKTINFO");
goto error;
}
}
if (addr->bindtodev) {
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, addr->bindtodev, strlen(addr->bindtodev))) {
if (warn)
pr_warn_errno(ctx, "setsockopt: unable to bind to device");
goto error;
}
}
if (ctx->conf->pmtu.set) {
int pmtu = ctx->conf->pmtu.state ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu))) {
pr_error_errno(ctx, "setsockopt: unable to set PMTU discovery");
goto error;
}
}
fastd_peer_address_t bind_address = addr->addr;
if (bind_address.sa.sa_family == AF_UNSPEC) {
memset(&bind_address, 0, sizeof(bind_address));
bind_address.sa.sa_family = af;
if (af == AF_INET6)
bind_address.in6.sin6_port = addr->addr.in.sin_port;
else
bind_address.in.sin_port = addr->addr.in.sin_port;
}
if (bind(fd, (struct sockaddr*)&bind_address, bind_address.sa.sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
if (warn)
pr_warn_errno(ctx, "bind");
goto error;
}
return fd;
error:
if (fd >= 0) {
if (close(fd))
pr_error_errno(ctx, "close");
}
if (warn) {
if (addr->bindtodev)
pr_warn(ctx, "unable to bind to %B on `%s'", &addr->addr, addr->bindtodev);
else
pr_warn(ctx, "unable to bind to %B", &addr->addr);
}
return -1;
}
static bool set_bound_address(fastd_context_t *ctx, fastd_socket_t *sock) {
fastd_peer_address_t addr = {};
socklen_t len = sizeof(addr);
if (getsockname(sock->fd, &addr.sa, &len) < 0) {
pr_error_errno(ctx, "getsockname");
return false;
}
if (len > sizeof(addr)) {
pr_error(ctx, "getsockname: got strange long address");
return false;
}
sock->bound_addr = calloc(1, sizeof(addr));
*sock->bound_addr = addr;
return true;
}
static bool bind_sockets(fastd_context_t *ctx) {
unsigned i;
for (i = 0; i < ctx->n_socks; i++) {
if (ctx->socks[i].fd >= 0)
continue;
ctx->socks[i].fd = bind_socket(ctx, ctx->socks[i].addr, ctx->socks[i].fd < -1);
if (ctx->socks[i].fd >= 0) {
if (!set_bound_address(ctx, &ctx->socks[i])) {
fastd_socket_close(ctx, &ctx->socks[i]);
continue;
}
fastd_peer_address_t bound_addr = *ctx->socks[i].bound_addr;
if (!ctx->socks[i].addr->addr.sa.sa_family)
bound_addr.sa.sa_family = AF_UNSPEC;
if (ctx->socks[i].addr->bindtodev)
pr_info(ctx, "successfully bound to %B on `%s'", &bound_addr, ctx->socks[i].addr->bindtodev);
else
pr_info(ctx, "successfully bound to %B", &bound_addr);
}
}
if ((ctx->sock_default_v4 && ctx->sock_default_v4->fd < 0) || (ctx->sock_default_v6 && ctx->sock_default_v6->fd < 0))
return false;
return true;
}
fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int af) {
const fastd_bind_address_t any_address = { .addr.sa.sa_family = af };
int fd = bind_socket(ctx, &any_address, true);
if (fd < 0)
return NULL;
fastd_socket_t *sock = malloc(sizeof(fastd_socket_t));
sock->fd = fd;
sock->addr = NULL;
sock->bound_addr = NULL;
sock->peer = peer;
if (!set_bound_address(ctx, sock)) {
fastd_socket_close(ctx, sock);
free(sock);
return NULL;
}
return sock;
}
void fastd_setfd(const fastd_context_t *ctx, int fd, int set, int unset) { void fastd_setfd(const fastd_context_t *ctx, int fd, int set, int unset) {
int flags = fcntl(fd, F_GETFD); int flags = fcntl(fd, F_GETFD);
@ -1206,7 +1036,7 @@ static void maintenance(fastd_context_t *ctx) {
cleanup_peers(ctx); cleanup_peers(ctx);
fastd_peer_eth_addr_cleanup(ctx); fastd_peer_eth_addr_cleanup(ctx);
bind_sockets(ctx); fastd_socket_handle_binds(ctx);
} }
@ -1339,7 +1169,7 @@ int main(int argc, char *argv[]) {
init_sockets(&ctx); init_sockets(&ctx);
if (!bind_sockets(&ctx)) if (!fastd_socket_handle_binds(&ctx))
exit_error(&ctx, "unable to bind default socket"); exit_error(&ctx, "unable to bind default socket");
init_tuntap(&ctx); init_tuntap(&ctx);

View file

@ -305,6 +305,7 @@ void fastd_send(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_pe
void fastd_send_handshake(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer); void fastd_send_handshake(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_buffer_t buffer);
void fastd_handle_receive(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer); void fastd_handle_receive(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer);
bool fastd_socket_handle_binds(fastd_context_t *ctx);
fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int af); fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int af);
void fastd_setfd(const fastd_context_t *ctx, int fd, int set, int unset); void fastd_setfd(const fastd_context_t *ctx, int fd, int set, int unset);

201
src/socket.c Normal file
View file

@ -0,0 +1,201 @@
/*
Copyright (c) 2012-2013, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fastd.h"
#include <fcntl.h>
static int bind_socket(fastd_context_t *ctx, const fastd_bind_address_t *addr, bool warn) {
int fd = -1;
int af = AF_UNSPEC;
if (addr->addr.sa.sa_family != AF_INET) {
fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (fd >= 0) {
af = AF_INET6;
int val = (addr->addr.sa.sa_family == AF_INET6);
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) {
if (warn)
pr_warn_errno(ctx, "setsockopt");
goto error;
}
}
}
if (fd < 0 && addr->addr.sa.sa_family != AF_INET6) {
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0)
exit_errno(ctx, "unable to create socket");
else
af = AF_INET;
}
if (fd < 0)
goto error;
fastd_setfd(ctx, fd, FD_CLOEXEC, 0);
fastd_setfl(ctx, fd, O_NONBLOCK, 0);
int one = 1;
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one))) {
pr_error_errno(ctx, "setsockopt: unable to set IP_PKTINFO");
goto error;
}
if (af == AF_INET6) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one))) {
pr_error_errno(ctx, "setsockopt: unable to set IPV6_RECVPKTINFO");
goto error;
}
}
if (addr->bindtodev) {
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, addr->bindtodev, strlen(addr->bindtodev))) {
if (warn)
pr_warn_errno(ctx, "setsockopt: unable to bind to device");
goto error;
}
}
if (ctx->conf->pmtu.set) {
int pmtu = ctx->conf->pmtu.state ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu))) {
pr_error_errno(ctx, "setsockopt: unable to set PMTU discovery");
goto error;
}
}
fastd_peer_address_t bind_address = addr->addr;
if (bind_address.sa.sa_family == AF_UNSPEC) {
memset(&bind_address, 0, sizeof(bind_address));
bind_address.sa.sa_family = af;
if (af == AF_INET6)
bind_address.in6.sin6_port = addr->addr.in.sin_port;
else
bind_address.in.sin_port = addr->addr.in.sin_port;
}
if (bind(fd, (struct sockaddr*)&bind_address, bind_address.sa.sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
if (warn)
pr_warn_errno(ctx, "bind");
goto error;
}
return fd;
error:
if (fd >= 0) {
if (close(fd))
pr_error_errno(ctx, "close");
}
if (warn) {
if (addr->bindtodev)
pr_warn(ctx, "unable to bind to %B on `%s'", &addr->addr, addr->bindtodev);
else
pr_warn(ctx, "unable to bind to %B", &addr->addr);
}
return -1;
}
static bool set_bound_address(fastd_context_t *ctx, fastd_socket_t *sock) {
fastd_peer_address_t addr = {};
socklen_t len = sizeof(addr);
if (getsockname(sock->fd, &addr.sa, &len) < 0) {
pr_error_errno(ctx, "getsockname");
return false;
}
if (len > sizeof(addr)) {
pr_error(ctx, "getsockname: got strange long address");
return false;
}
sock->bound_addr = calloc(1, sizeof(addr));
*sock->bound_addr = addr;
return true;
}
bool fastd_socket_handle_binds(fastd_context_t *ctx) {
unsigned i;
for (i = 0; i < ctx->n_socks; i++) {
if (ctx->socks[i].fd >= 0)
continue;
ctx->socks[i].fd = bind_socket(ctx, ctx->socks[i].addr, ctx->socks[i].fd < -1);
if (ctx->socks[i].fd >= 0) {
if (!set_bound_address(ctx, &ctx->socks[i])) {
fastd_socket_close(ctx, &ctx->socks[i]);
continue;
}
fastd_peer_address_t bound_addr = *ctx->socks[i].bound_addr;
if (!ctx->socks[i].addr->addr.sa.sa_family)
bound_addr.sa.sa_family = AF_UNSPEC;
if (ctx->socks[i].addr->bindtodev)
pr_info(ctx, "successfully bound to %B on `%s'", &bound_addr, ctx->socks[i].addr->bindtodev);
else
pr_info(ctx, "successfully bound to %B", &bound_addr);
}
}
if ((ctx->sock_default_v4 && ctx->sock_default_v4->fd < 0) || (ctx->sock_default_v6 && ctx->sock_default_v6->fd < 0))
return false;
return true;
}
fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int af) {
const fastd_bind_address_t any_address = { .addr.sa.sa_family = af };
int fd = bind_socket(ctx, &any_address, true);
if (fd < 0)
return NULL;
fastd_socket_t *sock = malloc(sizeof(fastd_socket_t));
sock->fd = fd;
sock->addr = NULL;
sock->bound_addr = NULL;
sock->peer = peer;
if (!set_bound_address(ctx, sock)) {
fastd_socket_close(ctx, sock);
free(sock);
return NULL;
}
return sock;
}