summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2012-10-29 22:21:47 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2012-10-29 22:21:47 +0100
commit078b835209bd22b5cf3f497f2b06f3fad0a078ca (patch)
tree2704129741073ce83345cc3305fbf51a988d2842
parentf5462bd63a1f6fc8879341c3eacd60e9824dec24 (diff)
downloadfastd-078b835209bd22b5cf3f497f2b06f3fad0a078ca.tar
fastd-078b835209bd22b5cf3f497f2b06f3fad0a078ca.zip
Add support for multiple binds
-rw-r--r--src/config.c37
-rw-r--r--src/config.y37
-rw-r--r--src/fastd.c242
-rw-r--r--src/fastd.h33
-rw-r--r--src/handshake.c8
-rw-r--r--src/handshake.h2
-rw-r--r--src/peer.c18
-rw-r--r--src/peer.h3
-rw-r--r--src/protocol_ec25519_fhmqvc.c37
-rw-r--r--src/types.h2
10 files changed, 284 insertions, 135 deletions
diff --git a/src/config.c b/src/config.c
index 4eee712..be67e2b 100644
--- a/src/config.c
+++ b/src/config.c
@@ -629,26 +629,34 @@ static void option_bind(fastd_context *ctx, fastd_config *conf, const char *arg)
l = 0;
}
- if (strcmp(addrstr, "any") == 0) {
- conf->bind_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
- conf->bind_addr_in.sin_port = htons(l);
+ fastd_bind_address *addr = calloc(1, sizeof(fastd_bind_address));
+ addr->next = conf->bind_addrs;
+ conf->bind_addrs = addr;
- conf->bind_addr_in6.sin6_addr = in6addr_any;
- conf->bind_addr_in6.sin6_port = htons(l);
+ if (strcmp(addrstr, "any") == 0) {
+ /* nothing to do */
}
else if (arg[0] == '[') {
- conf->bind_addr_in6.sin6_family = AF_INET6;
- if (inet_pton(AF_INET6, addrstr, &conf->bind_addr_in6.sin6_addr) != 1)
+ addr->addr.in6.sin6_family = AF_INET6;
+ addr->addr.in6.sin6_port = htons(l);
+
+ if (inet_pton(AF_INET6, addrstr, &addr->addr.in6.sin6_addr) != 1)
exit_error(ctx, "invalid bind address `%s'", addrstr);
- conf->bind_addr_in6.sin6_port = htons(l);
}
else {
- conf->bind_addr_in.sin_family = AF_INET;
- if (inet_pton(AF_INET, addrstr, &conf->bind_addr_in.sin_addr) != 1)
+ addr->addr.in.sin_family = AF_INET;
+ addr->addr.in.sin_port = htons(l);
+
+ if (inet_pton(AF_INET, addrstr, &addr->addr.in.sin_addr) != 1)
exit_error(ctx, "invalid bind address `%s'", addrstr);
- conf->bind_addr_in.sin_port = htons(l);
}
+ if (!conf->bind_addr_default_v4 && addr->addr.sa.sa_family != AF_INET6)
+ conf->bind_addr_default_v4 = addr;
+
+ if (!conf->bind_addr_default_v6 && addr->addr.sa.sa_family != AF_INET)
+ conf->bind_addr_default_v6 = addr;
+
free(addrstr);
}
@@ -886,6 +894,13 @@ void fastd_config_release(fastd_context *ctx, fastd_config *conf) {
conf->log_files = next;
}
+ while (conf->bind_addrs) {
+ fastd_bind_address *next = conf->bind_addrs->next;
+ free(conf->bind_addrs->bindtodev);
+ free(conf->bind_addrs);
+ conf->bind_addrs = next;
+ }
+
free(conf->ifname);
free(conf->secret);
free(conf->on_up);
diff --git a/src/config.y b/src/config.y
index 2b1af0f..d87c151 100644
--- a/src/config.y
+++ b/src/config.y
@@ -187,21 +187,32 @@ log_level: TOK_FATAL { $$ = LOG_CRIT; }
interface: TOK_STRING { free(conf->ifname); conf->ifname = strdup($1->str); }
;
-bind: TOK_ADDR maybe_port {
- conf->bind_addr_in.sin_family = AF_INET;
- conf->bind_addr_in.sin_addr = $1;
- conf->bind_addr_in.sin_port = htons($2);
+bind_new: {
+ fastd_bind_address *addr = calloc(1, sizeof(fastd_bind_address));
+ addr->next = conf->bind_addrs;
+ conf->bind_addrs = addr;
}
- | TOK_ADDR6 maybe_port {
- conf->bind_addr_in6.sin6_family = AF_INET6;
- conf->bind_addr_in6.sin6_addr = $1;
- conf->bind_addr_in6.sin6_port = htons($2);
+
+bind: bind_new TOK_ADDR maybe_port {
+ conf->bind_addrs->addr.in.sin_family = AF_INET;
+ conf->bind_addrs->addr.in.sin_addr = $2;
+ conf->bind_addrs->addr.in.sin_port = htons($3);
+ if (!conf->bind_addr_default_v4)
+ conf->bind_addr_default_v4 = conf->bind_addrs;
+ }
+ | bind_new TOK_ADDR6 maybe_port {
+ conf->bind_addrs->addr.in6.sin6_family = AF_INET6;
+ conf->bind_addrs->addr.in6.sin6_addr = $2;
+ conf->bind_addrs->addr.in6.sin6_port = htons($3);
+ if (!conf->bind_addr_default_v6)
+ conf->bind_addr_default_v6 = conf->bind_addrs;
}
- | TOK_ANY maybe_port {
- conf->bind_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
- conf->bind_addr_in.sin_port = htons($2);
- conf->bind_addr_in6.sin6_addr = in6addr_any;
- conf->bind_addr_in6.sin6_port = htons($2);
+ | bind_new TOK_ANY maybe_port {
+ conf->bind_addrs->addr.in.sin_port = htons($3);
+ if (!conf->bind_addr_default_v4)
+ conf->bind_addr_default_v4 = conf->bind_addrs;
+ if (!conf->bind_addr_default_v6)
+ conf->bind_addr_default_v6 = conf->bind_addrs;
}
;
diff --git a/src/fastd.c b/src/fastd.c
index 5388384..fc6f016 100644
--- a/src/fastd.c
+++ b/src/fastd.c
@@ -146,62 +146,141 @@ static void crypto_free(fastd_context *ctx) {
#endif
}
+
+static unsigned max_sockets(const fastd_config *conf) {
+ unsigned n = 0;
+ fastd_bind_address *addr;
+
+ for (addr = conf->bind_addrs; addr; addr = addr->next)
+ n++;
+
+ return n;
+}
+
static void init_sockets(fastd_context *ctx) {
- struct sockaddr_in addr_in = ctx->conf->bind_addr_in;
- struct sockaddr_in6 addr_in6 = ctx->conf->bind_addr_in6;
+ static const fastd_bind_address bind_any = {};
- if (addr_in.sin_family == AF_UNSPEC && addr_in6.sin6_family == AF_UNSPEC) {
- if (ctx->conf->peer_dirs || ctx->conf->n_floating || ctx->conf->n_v4 || ctx->conf->n_dynamic || ctx->conf->n_dynamic_v4)
- addr_in.sin_family = AF_INET;
+ const fastd_bind_address *addr = ctx->conf->bind_addrs;
+ const fastd_bind_address *default_v4 = ctx->conf->bind_addr_default_v4;
+ const fastd_bind_address *default_v6 = ctx->conf->bind_addr_default_v6;
- if (ctx->conf->peer_dirs || ctx->conf->n_floating || ctx->conf->n_v6 || ctx->conf->n_dynamic || ctx->conf->n_dynamic_v6)
- addr_in6.sin6_family = AF_INET6;
- }
+ if (!addr)
+ addr = default_v4 = default_v6 = &bind_any;
- if (addr_in.sin_family == AF_UNSPEC && ctx->conf->n_v4)
- pr_warn(ctx, "there are IPv4 peers defined, but bind is explicitly set to IPv6");
+ unsigned n_v4 = 0, n_v6 = 0;
+ bool info_ipv6 = true;
- if (addr_in6.sin6_family == AF_UNSPEC && ctx->conf->n_v6)
- pr_warn(ctx, "there are IPv6 peers defined, but bind is explicitly set to IPv4");
+ ctx->socks = calloc(max_sockets(ctx->conf), sizeof(fastd_socket));
- if (addr_in.sin_family == AF_INET) {
- pr_debug(ctx, "initializing IPv4 socket...");
+ while (addr) {
+ int fd = -1;
+ int af = AF_UNSPEC;
- if ((ctx->sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- exit_errno(ctx, "socket");
+ if (addr->addr.sa.sa_family != AF_INET) {
+ fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ if (info_ipv6) {
+ pr_warn(ctx, "there seems to be no IPv6 support; explicitely bind to an IPv4 address (or 0.0.0.0) to disable this warning");
+ info_ipv6 = false;
+ }
+ }
+ else {
+ af = AF_INET6;
- if (bind(ctx->sockfd, (struct sockaddr*)&addr_in, sizeof(struct sockaddr_in)))
- exit_errno(ctx, "bind");
+ int val = (addr->addr.sa.sa_family == AF_INET6);
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) {
+ 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;
+ }
- pr_debug(ctx, "IPv4 socket initialized.");
- }
- else {
- ctx->sockfd = -1;
- }
+ if (fd < 0)
+ goto error;
+
+ if (addr->bindtodev) {
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, addr->bindtodev, strlen(addr->bindtodev))) {
+ pr_warn_errno(ctx, "setsockopt: unable to bind to device");
+ goto error;
+ }
+ }
+
+ fastd_peer_address bind_address = addr->addr;
- if (addr_in6.sin6_family == AF_INET6) {
- pr_debug(ctx, "initializing IPv6 socket...");
+ if (bind_address.sa.sa_family == AF_UNSPEC) {
+ memset(&bind_address, 0, sizeof(bind_address));
+ bind_address.sa.sa_family = af;
- if ((ctx->sock6fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
- if (ctx->sockfd >= 0)
- pr_warn_errno(ctx, "socket");
+ if (af == AF_INET6)
+ bind_address.in6.sin6_port = addr->addr.in.sin_port;
else
- exit_errno(ctx, "socket");
+ bind_address.in.sin_port = addr->addr.in.sin_port;
+ }
+
+ if (bind(fd, (struct sockaddr*)&bind_address, af == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
+ pr_warn(ctx, "bind");
+ goto error;
+ }
+
+ ctx->socks[ctx->n_socks] = (fastd_socket){fd, addr};
+
+ if (af == AF_INET6) {
+ if (addr == default_v6)
+ ctx->sock_default_v6 = &ctx->socks[ctx->n_socks];
+
+ n_v6++;
+
+ if (addr->addr.sa.sa_family == AF_UNSPEC) {
+ if (addr == default_v4)
+ ctx->sock_default_v4 = &ctx->socks[ctx->n_socks];
+
+ n_v4++;
+ }
}
else {
- int val = 1;
- if (setsockopt(ctx->sock6fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)))
- exit_errno(ctx, "setsockopt");
+ if (addr == default_v4)
+ ctx->sock_default_v4 = &ctx->socks[ctx->n_socks];
- if (bind(ctx->sock6fd, (struct sockaddr*)&addr_in6, sizeof(struct sockaddr_in6)))
- exit_errno(ctx, "bind");
+ n_v4++;
+ }
- pr_debug(ctx, "IPv6 socket initialized.");
+ ctx->n_socks++;
+
+ addr = addr->next;
+ continue;
+
+ error:
+ if (fd >= 0) {
+ if (close(fd))
+ pr_error_errno(ctx, "close");
}
+
+ if (addr->bindtodev)
+ pr_warn(ctx, "unable to bind to %I on `%s'", addr->addr, addr->bindtodev);
+ else
+ pr_warn(ctx, "unable to bind to %I", addr->addr);
+
+ if (addr == default_v4 || addr == default_v6)
+ exit_error(ctx, "unable to bind to default address");
+
+ addr = addr->next;
}
- else {
- ctx->sock6fd = -1;
- }
+
+ if (!ctx->n_socks)
+ exit_error(ctx, "all bind attempts failed");
+
+ if (!n_v4 && ctx->conf->n_v4)
+ pr_warn(ctx, "there are IPv4 peers defined, but there was no successful IPv4 bind");
+
+ if (!n_v6 && ctx->conf->n_v6)
+ pr_warn(ctx, "there are IPv6 peers defined, but there was no successful IPv4 bind");
}
static void init_tuntap(fastd_context *ctx) {
@@ -236,9 +315,7 @@ static void init_tuntap(fastd_context *ctx) {
ctx->ifname = strndup(ifr.ifr_name, IFNAMSIZ);
- int ctl_sock = ctx->sockfd;
- if (ctl_sock < 0)
- ctl_sock = ctx->sock6fd;
+ int ctl_sock = ctx->socks[0].fd;
ifr.ifr_mtu = ctx->conf->mtu;
if (ioctl(ctl_sock, SIOCSIFMTU, &ifr) < 0)
@@ -255,15 +332,13 @@ static void close_tuntap(fastd_context *ctx) {
}
static void close_sockets(fastd_context *ctx) {
- if (ctx->sockfd >= 0) {
- if(close(ctx->sockfd))
+ unsigned i;
+ for (i = 0; i < ctx->n_socks; i++) {
+ if(close(ctx->socks[i].fd))
pr_warn_errno(ctx, "closing IPv4 socket: close");
}
- if (ctx->sock6fd >= 0) {
- if(close(ctx->sock6fd))
- pr_warn_errno(ctx, "closing IPv6 socket: close");
- }
+ free(ctx->socks);
}
static size_t methods_max_packet_size(fastd_context *ctx) {
@@ -347,8 +422,10 @@ static size_t methods_min_decrypt_tail_space(fastd_context *ctx) {
return ret;
}
-static void fastd_send_type(fastd_context *ctx, const fastd_peer_address *address, uint8_t packet_type, fastd_buffer buffer) {
- int sockfd;
+static void fastd_send_type(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, uint8_t packet_type, fastd_buffer buffer) {
+ if (!sock)
+ exit_bug(ctx, "send: sock == NULL");
+
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
@@ -356,13 +433,11 @@ static void fastd_send_type(fastd_context *ctx, const fastd_peer_address *addres
case AF_INET:
msg.msg_name = (void*)&address->in;
msg.msg_namelen = sizeof(struct sockaddr_in);
- sockfd = ctx->sockfd;
break;
case AF_INET6:
msg.msg_name = (void*)&address->in6;
msg.msg_namelen = sizeof(struct sockaddr_in6);
- sockfd = ctx->sock6fd;
break;
default:
@@ -379,7 +454,7 @@ static void fastd_send_type(fastd_context *ctx, const fastd_peer_address *addres
int ret;
do {
- ret = sendmsg(sockfd, &msg, 0);
+ ret = sendmsg(sock->fd, &msg, 0);
} while (ret < 0 && errno == EINTR);
if (ret < 0)
@@ -388,12 +463,12 @@ static void fastd_send_type(fastd_context *ctx, const fastd_peer_address *addres
fastd_buffer_free(buffer);
}
-void fastd_send(fastd_context *ctx, const fastd_peer_address *address, fastd_buffer buffer) {
- fastd_send_type(ctx, address, PACKET_DATA, buffer);
+void fastd_send(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, fastd_buffer buffer) {
+ fastd_send_type(ctx, sock, address, PACKET_DATA, buffer);
}
-void fastd_send_handshake(fastd_context *ctx, const fastd_peer_address *address, fastd_buffer buffer) {
- fastd_send_type(ctx, address, PACKET_HANDSHAKE, buffer);
+void fastd_send_handshake(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, fastd_buffer buffer) {
+ fastd_send_type(ctx, sock, address, PACKET_HANDSHAKE, buffer);
}
void fastd_handle_receive(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) {
@@ -514,7 +589,7 @@ static inline void update_time(fastd_context *ctx) {
}
static inline void send_handshake(fastd_context *ctx, fastd_peer *peer) {
- if (peer->address.sa.sa_family != AF_UNSPEC) {
+ if (peer->address.sa.sa_family && peer->sock) {
if (timespec_diff(&ctx->now, &peer->last_handshake) < ctx->conf->min_handshake_interval*1000
&& fastd_peer_address_equal(&peer->address, &peer->last_handshake_address)) {
pr_debug(ctx, "not sending a handshake to %P as we sent one a short time ago", peer);
@@ -523,7 +598,7 @@ static inline void send_handshake(fastd_context *ctx, fastd_peer *peer) {
pr_debug(ctx, "sending handshake to %P...", peer);
peer->last_handshake = ctx->now;
peer->last_handshake_address = peer->address;
- ctx->conf->protocol->handshake_init(ctx, &peer->address, peer->config);
+ ctx->conf->protocol->handshake_init(ctx, peer->sock, &peer->address, peer->config);
}
}
@@ -603,7 +678,7 @@ static void handle_tun(fastd_context *ctx) {
}
}
-static void handle_socket(fastd_context *ctx, int sockfd) {
+static void handle_socket(fastd_context *ctx, const fastd_socket *sock) {
size_t max_len = PACKET_TYPE_LEN + methods_max_packet_size(ctx);
fastd_buffer buffer = fastd_buffer_alloc(max_len, methods_min_decrypt_head_space(ctx), methods_min_decrypt_tail_space(ctx));
uint8_t *packet_type;
@@ -611,7 +686,7 @@ static void handle_socket(fastd_context *ctx, int sockfd) {
fastd_peer_address recvaddr;
socklen_t recvaddrlen = sizeof(recvaddr);
- ssize_t len = recvfrom(sockfd, buffer.data, buffer.len, 0, (struct sockaddr*)&recvaddr, &recvaddrlen);
+ ssize_t len = recvfrom(sock->fd, buffer.data, buffer.len, 0, (struct sockaddr*)&recvaddr, &recvaddrlen);
if (len < 0) {
if (errno != EINTR)
pr_warn(ctx, "recvfrom: %s", strerror(errno));
@@ -638,7 +713,7 @@ static void handle_socket(fastd_context *ctx, int sockfd) {
break;
case PACKET_HANDSHAKE:
- fastd_handshake_handle(ctx, &recvaddr, peer->config, buffer);
+ fastd_handshake_handle(ctx, sock, &recvaddr, peer->config, buffer);
break;
default:
@@ -651,11 +726,11 @@ static void handle_socket(fastd_context *ctx, int sockfd) {
switch (*packet_type) {
case PACKET_DATA:
fastd_buffer_free(buffer);
- ctx->conf->protocol->handshake_init(ctx, &recvaddr, NULL);
+ ctx->conf->protocol->handshake_init(ctx, sock, &recvaddr, NULL);
break;
case PACKET_HANDSHAKE:
- fastd_handshake_handle(ctx, &recvaddr, NULL, buffer);
+ fastd_handshake_handle(ctx, sock, &recvaddr, NULL, buffer);
break;
default:
@@ -691,7 +766,18 @@ static void handle_resolv_returns(fastd_context *ctx) {
peer->last_resolve_return = ctx->now;
- if (fastd_peer_claim_address(ctx, peer, &resolve_return.addr)) {
+ if (fastd_peer_claim_address(ctx, peer, NULL, &resolve_return.addr)) {
+ if (!peer->sock) {
+ switch(resolve_return.addr.sa.sa_family) {
+ case AF_INET:
+ peer->sock = ctx->sock_default_v4;
+ break;
+
+ case AF_INET6:
+ peer->sock = ctx->sock_default_v6;
+ }
+ }
+
send_handshake(ctx, peer);
}
else {
@@ -705,22 +791,24 @@ static void handle_resolv_returns(fastd_context *ctx) {
}
static void handle_input(fastd_context *ctx) {
- struct pollfd fds[4];
+ struct pollfd fds[ctx->n_socks + 2];
fds[0].fd = ctx->tunfd;
fds[0].events = POLLIN;
- fds[1].fd = ctx->sockfd;
+ fds[1].fd = ctx->resolverfd;
fds[1].events = POLLIN;
- fds[2].fd = ctx->sock6fd;
- fds[2].events = POLLIN;
- fds[3].fd = ctx->resolverfd;
- fds[3].events = POLLIN;
+
+ unsigned i;
+ for (i = 0; i < ctx->n_socks; i++) {
+ fds[i+2].fd = ctx->socks[i].fd;
+ fds[i+2].events = POLLIN;
+ }
int timeout = fastd_task_timeout(ctx);
if (timeout < 0 || timeout > 60000)
timeout = 60000; /* call maintenance at least once a minute */
- int ret = poll(fds, 4, timeout);
+ int ret = poll(fds, ctx->n_socks + 2, timeout);
if (ret < 0) {
if (errno == EINTR)
return;
@@ -731,13 +819,14 @@ static void handle_input(fastd_context *ctx) {
update_time(ctx);
if (fds[0].revents & POLLIN)
- handle_tun(ctx);
+ handle_tun(ctx);
if (fds[1].revents & POLLIN)
- handle_socket(ctx, ctx->sockfd);
- if (fds[2].revents & POLLIN)
- handle_socket(ctx, ctx->sock6fd);
- if (fds[3].revents & POLLIN)
handle_resolv_returns(ctx);
+
+ for (i = 0; i < ctx->n_socks; i++) {
+ if (fds[i+2].revents & POLLIN)
+ handle_socket(ctx, &ctx->socks[i]);
+ }
}
static void cleanup_peers(fastd_context *ctx) {
@@ -875,7 +964,6 @@ int main(int argc, char *argv[]) {
init_log(&ctx);
fastd_reconfigure(&ctx, &conf);
-
}
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
diff --git a/src/fastd.h b/src/fastd.h
index 1694105..cf5aaf1 100644
--- a/src/fastd.h
+++ b/src/fastd.h
@@ -71,8 +71,8 @@ struct _fastd_protocol {
fastd_protocol_config* (*init)(fastd_context *ctx);
void (*peer_configure)(fastd_context *ctx, fastd_peer_config *peer_conf);
- void (*handshake_init)(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf);
- void (*handshake_handle)(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const fastd_handshake *handshake, const fastd_method *method);
+ void (*handshake_init)(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf);
+ void (*handshake_handle)(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const fastd_handshake *handshake, const fastd_method *method);
void (*handle_recv)(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer);
void (*send)(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer);
@@ -131,6 +131,17 @@ struct _fastd_log_fd {
int fd;
};
+struct _fastd_bind_address {
+ fastd_bind_address *next;
+ fastd_peer_address addr;
+ char *bindtodev;
+};
+
+struct _fastd_socket {
+ int fd;
+ const fastd_bind_address *addr;
+};
+
struct _fastd_config {
int log_stderr_level;
int log_syslog_level;
@@ -149,8 +160,10 @@ struct _fastd_config {
char *ifname;
- struct sockaddr_in bind_addr_in;
- struct sockaddr_in6 bind_addr_in6;
+ fastd_bind_address *bind_addrs;
+
+ fastd_bind_address *bind_addr_default_v4;
+ fastd_bind_address *bind_addr_default_v6;
uint16_t mtu;
fastd_mode mode;
@@ -219,8 +232,12 @@ struct _fastd_context {
int resolvewfd;
int tunfd;
- int sockfd;
- int sock6fd;
+
+ unsigned n_socks;
+ fastd_socket *socks;
+
+ fastd_socket *sock_default_v4;
+ fastd_socket *sock_default_v6;
#ifdef USE_CRYPTO_AES128CTR
fastd_crypto_aes128ctr_context *crypto_aes128ctr;
@@ -244,8 +261,8 @@ struct _fastd_string_stack {
};
-void fastd_send(fastd_context *ctx, const fastd_peer_address *address, fastd_buffer buffer);
-void fastd_send_handshake(fastd_context *ctx, const fastd_peer_address *address, fastd_buffer buffer);
+void fastd_send(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, fastd_buffer buffer);
+void fastd_send_handshake(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, fastd_buffer buffer);
void fastd_handle_receive(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer);
void fastd_resolve_peer(fastd_context *ctx, fastd_peer *peer);
diff --git a/src/handshake.c b/src/handshake.c
index f2bdb4c..e823a0e 100644
--- a/src/handshake.c
+++ b/src/handshake.c
@@ -180,7 +180,7 @@ static fastd_string_stack* parse_string_list(uint8_t *data, size_t len) {
return ret;
}
-void fastd_handshake_handle(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, fastd_buffer buffer) {
+void fastd_handshake_handle(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf, fastd_buffer buffer) {
if (buffer.len < sizeof(fastd_packet)) {
pr_warn(ctx, "received a short handshake from %I", address);
goto end_free;
@@ -304,10 +304,10 @@ void fastd_handshake_handle(fastd_context *ctx, const fastd_peer_address *addres
fastd_handshake_add_uint8(ctx, &reply_buffer, RECORD_REPLY_CODE, reply_code);
fastd_handshake_add_uint8(ctx, &reply_buffer, RECORD_ERROR_DETAIL, error_detail);
- fastd_send_handshake(ctx, address, reply_buffer);
+ fastd_send_handshake(ctx, sock, address, reply_buffer);
}
else {
- ctx->conf->protocol->handshake_handle(ctx, address, peer_conf, &handshake, method);
+ ctx->conf->protocol->handshake_handle(ctx, sock, address, peer_conf, &handshake, method);
}
}
else {
@@ -334,7 +334,7 @@ void fastd_handshake_handle(fastd_context *ctx, const fastd_peer_address *addres
goto end_free;
}
- ctx->conf->protocol->handshake_handle(ctx, address, peer_conf, &handshake, method);
+ ctx->conf->protocol->handshake_handle(ctx, sock, address, peer_conf, &handshake, method);
}
else {
const char *error_field_str;
diff --git a/src/handshake.h b/src/handshake.h
index 0db4e2f..7744665 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -71,7 +71,7 @@ struct _fastd_handshake {
fastd_buffer fastd_handshake_new_init(fastd_context *ctx, size_t tail_space);
fastd_buffer fastd_handshake_new_reply(fastd_context *ctx, const fastd_handshake *handshake, const fastd_method *method, size_t tail_space);
-void fastd_handshake_handle(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, fastd_buffer buffer);
+void fastd_handshake_handle(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf, fastd_buffer buffer);
static inline void fastd_handshake_add(fastd_context *ctx, fastd_buffer *buffer, fastd_handshake_record_type type, size_t len, const void *data) {
diff --git a/src/peer.c b/src/peer.c
index 54db531..e29e350 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -176,6 +176,19 @@ static inline void setup_peer(fastd_context *ctx, fastd_peer *peer) {
peer->established = false;
+ switch (peer->address.sa.sa_family) {
+ case AF_INET:
+ peer->sock = ctx->sock_default_v4;
+ break;
+
+ case AF_INET6:
+ peer->sock = ctx->sock_default_v6;
+ break;
+
+ default:
+ peer->sock = NULL;
+ }
+
peer->last_resolve = (struct timespec){0, 0};
peer->last_resolve_return = (struct timespec){0, 0};
peer->seen = (struct timespec){0, 0};
@@ -280,7 +293,7 @@ bool fastd_peer_address_equal(const fastd_peer_address *addr1, const fastd_peer_
return true;
}
-bool fastd_peer_claim_address(fastd_context *ctx, fastd_peer *new_peer, const fastd_peer_address *addr) {
+bool fastd_peer_claim_address(fastd_context *ctx, fastd_peer *new_peer, const fastd_socket *sock, const fastd_peer_address *addr) {
if (addr->sa.sa_family != AF_UNSPEC) {
fastd_peer *peer;
for (peer = ctx->peers; peer; peer = peer->next) {
@@ -304,6 +317,9 @@ bool fastd_peer_claim_address(fastd_context *ctx, fastd_peer *new_peer, const fa
}
new_peer->address = *addr;
+ if (sock)
+ new_peer->sock = sock;
+
return true;
}
diff --git a/src/peer.h b/src/peer.h
index b326ad1..37b5ba2 100644
--- a/src/peer.h
+++ b/src/peer.h
@@ -35,6 +35,7 @@ struct _fastd_peer {
const fastd_peer_config *config;
+ const fastd_socket *sock;
fastd_peer_address address;
bool established;
@@ -87,7 +88,7 @@ void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer);
void fastd_peer_delete(fastd_context *ctx, fastd_peer *peer);
fastd_peer* fastd_peer_add(fastd_context *ctx, fastd_peer_config *conf);
void fastd_peer_set_established(fastd_context *ctx, fastd_peer *peer);
-bool fastd_peer_claim_address(fastd_context *ctx, fastd_peer *peer, const fastd_peer_address *addr);
+bool fastd_peer_claim_address(fastd_context *ctx, fastd_peer *peer, const fastd_socket *sock, const fastd_peer_address *addr);
const fastd_eth_addr* fastd_get_source_address(const fastd_context *ctx, fastd_buffer buffer);
const fastd_eth_addr* fastd_get_dest_address(const fastd_context *ctx, fastd_buffer buffer);
diff --git a/src/protocol_ec25519_fhmqvc.c b/src/protocol_ec25519_fhmqvc.c
index 02920f4..8c96c01 100644
--- a/src/protocol_ec25519_fhmqvc.c
+++ b/src/protocol_ec25519_fhmqvc.c
@@ -228,7 +228,7 @@ static void maintenance(fastd_context *ctx) {
}
}
-static void protocol_handshake_init(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf) {
+static void protocol_handshake_init(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf) {
maintenance(ctx);
fastd_buffer buffer = fastd_handshake_new_init(ctx, 3*(4+PUBLICKEYBYTES) /* sender key, receipient key, handshake key */);
@@ -242,10 +242,10 @@ static void protocol_handshake_init(fastd_context *ctx, const fastd_peer_address
fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, ctx->protocol_state->handshake_key.public_key.p);
- fastd_send_handshake(ctx, address, buffer);
+ fastd_send_handshake(ctx, sock, address, buffer);
}
-static void respond_handshake(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer *peer, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key,
+static void respond_handshake(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer *peer, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key,
const fastd_handshake *handshake, const fastd_method *method) {
pr_debug(ctx, "responding handshake with %P[%I]...", peer, address);
@@ -302,10 +302,10 @@ static void respond_handshake(fastd_context *ctx, const fastd_peer_address *addr
fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p);
fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf);
- fastd_send_handshake(ctx, address, buffer);
+ fastd_send_handshake(ctx, sock, address, buffer);
}
-static bool establish(fastd_context *ctx, fastd_peer *peer, const fastd_method *method, const fastd_peer_address *address, bool initiator,
+static bool establish(fastd_context *ctx, fastd_peer *peer, const fastd_method *method, const fastd_socket *sock, const fastd_peer_address *address, bool initiator,
const ecc_public_key_256 *A, const ecc_public_key_256 *B, const ecc_public_key_256 *X,
const ecc_public_key_256 *Y, const ecc_public_key_256 *sigma, uint64_t serial) {
uint8_t hashinput[5*PUBLICKEYBYTES];
@@ -344,7 +344,7 @@ static bool establish(fastd_context *ctx, fastd_peer *peer, const fastd_method *
fastd_peer_seen(ctx, peer);
- if (!fastd_peer_claim_address(ctx, peer, address)) {
+ if (!fastd_peer_claim_address(ctx, peer, sock, address)) {
pr_warn(ctx, "can't set address %I which is used by a fixed peer", address);
fastd_peer_reset(ctx, peer);
return false;
@@ -362,7 +362,7 @@ static bool establish(fastd_context *ctx, fastd_peer *peer, const fastd_method *
return true;
}
-static void finish_handshake(fastd_context *ctx, const fastd_peer_address *address, fastd_peer *peer, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key,
+static void finish_handshake(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, fastd_peer *peer, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key,
const fastd_handshake *handshake, const fastd_method *method) {
pr_debug(ctx, "finishing handshake with %P[%I]...", peer, address);
@@ -418,7 +418,7 @@ static void finish_handshake(fastd_context *ctx, const fastd_peer_address *addre
memcpy(hashinput+PUBLICKEYBYTES, handshake_key->public_key.p, PUBLICKEYBYTES);
crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, shared_handshake_key);
- if (!establish(ctx, peer, method, address, true, &handshake_key->public_key, peer_handshake_key, &ctx->conf->protocol_config->public_key,
+ if (!establish(ctx, peer, method, sock, address, true, &handshake_key->public_key, peer_handshake_key, &ctx->conf->protocol_config->public_key,
&peer->config->protocol_config->public_key, &sigma, handshake_key->serial))
return;
@@ -430,11 +430,10 @@ static void finish_handshake(fastd_context *ctx, const fastd_peer_address *addre
fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p);
fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf);
- fastd_send_handshake(ctx, address, buffer);
-
+ fastd_send_handshake(ctx, sock, address, buffer);
}
-static void handle_finish_handshake(fastd_context *ctx, const fastd_peer_address *address, fastd_peer *peer, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key,
+static void handle_finish_handshake(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, fastd_peer *peer, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key,
const fastd_handshake *handshake, const fastd_method *method) {
pr_debug(ctx, "handling handshake finish with %P[%I]...", peer, address);
@@ -485,11 +484,11 @@ static void handle_finish_handshake(fastd_context *ctx, const fastd_peer_address
return;
}
- establish(ctx, peer, method, address, false, peer_handshake_key, &handshake_key->public_key, &peer->config->protocol_config->public_key,
+ establish(ctx, peer, method, sock, address, false, peer_handshake_key, &handshake_key->public_key, &peer->config->protocol_config->public_key,
&ctx->conf->protocol_config->public_key, &sigma, handshake_key->serial);
}
-static const fastd_peer_config* match_sender_key(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const unsigned char key[32]) {
+static const fastd_peer_config* match_sender_key(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const unsigned char key[32]) {
if (peer_conf) {
if (memcmp(peer_conf->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0)
return peer_conf;
@@ -523,7 +522,7 @@ static inline bool has_field(const fastd_handshake *handshake, uint8_t type, siz
return (handshake->records[type].length == length);
}
-static void protocol_handshake_handle(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const fastd_handshake *handshake, const fastd_method *method) {
+static void protocol_handshake_handle(fastd_context *ctx, const fastd_socket *sock, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const fastd_handshake *handshake, const fastd_method *method) {
handshake_key *handshake_key;
char *peer_version_name = NULL;
@@ -534,7 +533,7 @@ static void protocol_handshake_handle(fastd_context *ctx, const fastd_peer_addre
return;
}
- peer_conf = match_sender_key(ctx, address, peer_conf, handshake->records[RECORD_SENDER_KEY].data);
+ peer_conf = match_sender_key(ctx, sock, address, peer_conf, handshake->records[RECORD_SENDER_KEY].data);
if (!peer_conf) {
pr_debug(ctx, "ignoring handshake from %I (unknown key or unresolved host)", address);
return;
@@ -589,7 +588,7 @@ static void protocol_handshake_handle(fastd_context *ctx, const fastd_peer_addre
peer->last_handshake_response = ctx->now;
peer->last_handshake_response_address = *address;
- respond_handshake(ctx, address, peer, &ctx->protocol_state->handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake, method);
+ respond_handshake(ctx, sock, address, peer, &ctx->protocol_state->handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake, method);
break;
case 2:
@@ -610,7 +609,7 @@ static void protocol_handshake_handle(fastd_context *ctx, const fastd_peer_addre
pr_verbose(ctx, "received handshake response from %P[%I] using fastd %s", peer, address, peer_version_name);
free(peer_version_name);
- finish_handshake(ctx, address, peer, handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake, method);
+ finish_handshake(ctx, sock, address, peer, handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake, method);
break;
case 3:
@@ -627,7 +626,7 @@ static void protocol_handshake_handle(fastd_context *ctx, const fastd_peer_addre
pr_debug(ctx, "received handshake finish from %P[%I]", peer, address);
- handle_finish_handshake(ctx, address, peer, handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake, method);
+ handle_finish_handshake(ctx, sock, address, peer, handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake, method);
break;
default:
@@ -702,7 +701,7 @@ static void session_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buff
return;
}
- fastd_send(ctx, &peer->address, send_buffer);
+ fastd_send(ctx, peer->sock, &peer->address, send_buffer);
fastd_task_delete_peer_keepalives(ctx, peer);
fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000);
diff --git a/src/types.h b/src/types.h
index f079d3c..13236b6 100644
--- a/src/types.h
+++ b/src/types.h
@@ -44,6 +44,8 @@ typedef enum _fastd_mode {
typedef struct _fastd_buffer fastd_buffer;
typedef union _fastd_peer_address fastd_peer_address;
+typedef struct _fastd_bind_address fastd_bind_address;
+typedef struct _fastd_socket fastd_socket;
typedef struct _fastd_peer_config fastd_peer_config;
typedef struct _fastd_eth_addr fastd_eth_addr;
typedef struct _fastd_peer fastd_peer;