From aae5265eb5ebe806414f383f99bb765dbdcaee4b Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 24 Jul 2013 18:44:43 +0200 Subject: Allow specifying multiple remote entries for a single peer --- src/config.y | 42 ++++++++----- src/fastd.c | 35 ++++++++--- src/fastd.h | 2 +- src/peer.c | 134 ++++++++++++++++++++++++++++++------------ src/peer.h | 47 +++++++++------ src/protocol_ec25519_fhmqvc.c | 49 +++++++-------- src/resolve.c | 14 ++--- src/types.h | 2 + 8 files changed, 214 insertions(+), 111 deletions(-) diff --git a/src/config.y b/src/config.y index 97d2f97..fde2873 100644 --- a/src/config.y +++ b/src/config.y @@ -397,29 +397,39 @@ peer_statement: TOK_REMOTE peer_remote ';' ; peer_remote: TOK_ADDR4 port { - free(conf->peers->hostname); - conf->peers->hostname = NULL; + fastd_remote_config_t **remote = &conf->peers->remotes; + while (*remote) + remote = &(*remote)->next; - conf->peers->address.in.sin_family = AF_INET; - conf->peers->address.in.sin_addr = $1; - conf->peers->address.in.sin_port = htons($2); - fastd_peer_address_simplify(&conf->peers->address); + *remote = calloc(1, sizeof(fastd_remote_config_t)); + + (*remote)->address.in.sin_family = AF_INET; + (*remote)->address.in.sin_addr = $1; + (*remote)->address.in.sin_port = htons($2); + fastd_peer_address_simplify(&(*remote)->address); } | TOK_ADDR6 port { - free(conf->peers->hostname); - conf->peers->hostname = NULL; + fastd_remote_config_t **remote = &conf->peers->remotes; + while (*remote) + remote = &(*remote)->next; + + *remote = calloc(1, sizeof(fastd_remote_config_t)); - conf->peers->address.in6.sin6_family = AF_INET6; - conf->peers->address.in6.sin6_addr = $1; - conf->peers->address.in6.sin6_port = htons($2); - fastd_peer_address_simplify(&conf->peers->address); + (*remote)->address.in6.sin6_family = AF_INET6; + (*remote)->address.in6.sin6_addr = $1; + (*remote)->address.in6.sin6_port = htons($2); + fastd_peer_address_simplify(&(*remote)->address); } | maybe_af TOK_STRING port maybe_float { - free(conf->peers->hostname); + fastd_remote_config_t **remote = &conf->peers->remotes; + while (*remote) + remote = &(*remote)->next; + + *remote = calloc(1, sizeof(fastd_remote_config_t)); - conf->peers->hostname = strdup($2->str); - conf->peers->address.sa.sa_family = $1; - conf->peers->address.in.sin_port = htons($3); + (*remote)->hostname = strdup($2->str); + (*remote)->address.sa.sa_family = $1; + (*remote)->address.in.sin_port = htons($3); conf->peers->floating = conf->peers->dynamic_float_deprecated = $4; } ; diff --git a/src/fastd.c b/src/fastd.c index fa6ea65..8ba4ea3 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -793,8 +793,13 @@ static inline void schedule_handshake(fastd_context_t *ctx, fastd_peer_t *peer) } static void send_handshake(fastd_context_t *ctx, fastd_peer_t *peer) { - if (!fastd_peer_is_established(peer)) + if (!peer->next_remote) + exit_bug(ctx, "send_handshake: no remote"); + + if (!fastd_peer_is_established(peer)) { + fastd_peer_claim_address(ctx, peer, NULL, NULL, &peer->next_remote->address); fastd_peer_reset_socket(ctx, peer); + } if (!peer->sock) return; @@ -823,14 +828,23 @@ static void handle_tasks(fastd_context_t *ctx) { case TASK_HANDSHAKE: schedule_handshake(ctx, task->peer); - if(!fastd_peer_may_connect(ctx, task->peer)) + if(!fastd_peer_may_connect(ctx, task->peer)) { + task->peer->next_remote = task->peer->remotes; break; - - if (fastd_peer_is_dynamic(task->peer) && !fastd_peer_is_established(task->peer)) - fastd_resolve_peer(ctx, task->peer); + } send_handshake(ctx, task->peer); + if (fastd_peer_is_established(task->peer)) + break; + + task->peer->next_remote = task->peer->next_remote->next; + if (!task->peer->next_remote) + task->peer->next_remote = task->peer->remotes; + + if (fastd_peer_remote_is_dynamic(task->peer->next_remote)) + fastd_resolve_peer(ctx, task->peer, task->peer->next_remote); + break; case TASK_KEEPALIVE: @@ -1069,13 +1083,16 @@ static void handle_resolve_returns(fastd_context_t *ctx) { if (!peer->config) continue; - if (!strequal(peer->config->hostname, hostname)) - continue; + fastd_remote_t *remote; + for (remote = peer->remotes; remote; remote = remote->next) { + if (strequal(remote->config->hostname, hostname) && fastd_peer_remote_matches_dynamic(remote->config, &resolve_return.constraints)) + break; + } - if (!fastd_peer_config_matches_dynamic(peer->config, &resolve_return.constraints)) + if (!remote) continue; - fastd_peer_handle_resolve(ctx, peer, &resolve_return.addr); + fastd_peer_handle_resolve(ctx, peer, remote, &resolve_return.addr); break; } diff --git a/src/fastd.h b/src/fastd.h index a155c80..e3aa76b 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -312,7 +312,7 @@ fastd_socket_t* fastd_socket_open(fastd_context_t *ctx, fastd_peer_t *peer, int void fastd_setfd(const fastd_context_t *ctx, int fd, int set, int unset); void fastd_setfl(const fastd_context_t *ctx, int fd, int set, int unset); -void fastd_resolve_peer(fastd_context_t *ctx, fastd_peer_t *peer); +void fastd_resolve_peer(fastd_context_t *ctx, fastd_peer_t *peer, fastd_remote_t *remote); int fastd_vsnprintf(const fastd_context_t *ctx, char *buffer, size_t size, const char *format, va_list ap); void fastd_logf(const fastd_context_t *ctx, int level, const char *format, ...); diff --git a/src/peer.c b/src/peer.c index 422cd2e..d50fdec 100644 --- a/src/peer.c +++ b/src/peer.c @@ -163,28 +163,27 @@ static void init_handshake(fastd_context_t *ctx, fastd_peer_t *peer) { fastd_task_schedule_handshake(ctx, peer, delay); } -void fastd_peer_handle_resolve(fastd_context_t *ctx, fastd_peer_t *peer, const fastd_peer_address_t *address) { - peer->last_resolve_return = ctx->now; - - if (!fastd_peer_claim_address(ctx, peer, NULL, NULL, address)) - pr_warn(ctx, "resolved address %I for peer %P which is used by a fixed peer", address, peer); +void fastd_peer_handle_resolve(fastd_context_t *ctx, fastd_peer_t *peer, fastd_remote_t *remote, const fastd_peer_address_t *address) { + remote->last_resolve_return = ctx->now; + remote->address = *address; if (peer->state == STATE_RESOLVING) init_handshake(ctx, peer); } static void setup_peer(fastd_context_t *ctx, fastd_peer_t *peer) { - if (!peer->config || peer->config->hostname) - peer->address.sa.sa_family = AF_UNSPEC; - else - peer->address = peer->config->address; - - memset(&peer->local_address, 0, sizeof(peer->local_address)); + peer->address.sa.sa_family = AF_UNSPEC; + peer->local_address.sa.sa_family = AF_UNSPEC; peer->state = STATE_INIT; - peer->last_resolve = (struct timespec){0, 0}; - peer->last_resolve_return = (struct timespec){0, 0}; + fastd_remote_t *remote; + for (remote = peer->remotes; remote; remote = remote->next) { + remote->last_resolve = (struct timespec){0, 0}; + remote->last_resolve_return = (struct timespec){0, 0}; + } + + peer->next_remote = peer->remotes; peer->last_handshake = (struct timespec){0, 0}; peer->last_handshake_address.sa.sa_family = AF_UNSPEC; @@ -195,12 +194,14 @@ static void setup_peer(fastd_context_t *ctx, fastd_peer_t *peer) { if (!peer->protocol_state) ctx->conf->protocol->init_peer_state(ctx, peer); - if (fastd_peer_is_dynamic(peer)) { - peer->state = STATE_RESOLVING; - fastd_resolve_peer(ctx, peer); - } - else if(peer->address.sa.sa_family != AF_UNSPEC) { - init_handshake(ctx, peer); + if(peer->next_remote) { + if (fastd_peer_remote_is_dynamic(peer->next_remote)) { + peer->state = STATE_RESOLVING; + fastd_resolve_peer(ctx, peer, peer->next_remote); + } + else { + init_handshake(ctx, peer); + } } } @@ -245,8 +246,15 @@ fastd_peer_config_t* fastd_peer_config_new(fastd_context_t *ctx, fastd_config_t } void fastd_peer_config_free(fastd_peer_config_t *peer) { + while (peer->remotes) { + fastd_remote_config_t *remote = peer->remotes; + peer->remotes = remote->next; + + free(remote->hostname); + free(remote); + } + free(peer->name); - free(peer->hostname); free(peer->key); free(peer->protocol_config); free(peer); @@ -318,6 +326,35 @@ static inline void reset_peer_address(fastd_context_t *ctx, fastd_peer_t *peer) memset(&peer->address, 0, sizeof(fastd_peer_address_t)); } +bool fastd_peer_owns_address(fastd_context_t *ctx, const fastd_peer_t *peer, const fastd_peer_address_t *addr) { + if (fastd_peer_is_floating(peer)) + return false; + + fastd_remote_config_t *remote; + for (remote = peer->config->remotes; remote; remote = remote->next) { + if (remote->hostname) + continue; + + if (fastd_peer_address_equal(&remote->address, addr)) + return true; + } + + return false; +} + +bool fastd_peer_matches_address(fastd_context_t *ctx, const fastd_peer_t *peer, const fastd_peer_address_t *addr) { + if (fastd_peer_is_floating(peer)) + return true; + + fastd_remote_t *remote; + for (remote = peer->remotes; remote; remote = remote->next) { + if (fastd_peer_address_equal(&remote->address, addr)) + return true; + } + + return false; +} + bool fastd_peer_claim_address(fastd_context_t *ctx, fastd_peer_t *new_peer, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr) { if (remote_addr->sa.sa_family == AF_UNSPEC) { if (fastd_peer_is_established(new_peer)) @@ -354,17 +391,30 @@ bool fastd_peer_claim_address(fastd_context_t *ctx, fastd_peer_t *new_peer, fast return true; } -bool fastd_peer_config_equal(const fastd_peer_config_t *peer1, const fastd_peer_config_t *peer2) { - if (peer1->group != peer2->group) +static bool fastd_remote_configs_equal(const fastd_remote_config_t *remote1, const fastd_remote_config_t *remote2) { + if (!remote1 && !remote2) + return true; + + if (!remote1 || !remote2) + return false; + + if (!fastd_peer_address_equal(&remote1->address, &remote2->address)) + return false; + + if (!strequal(remote1->hostname, remote2->hostname)) return false; - if (!strequal(peer1->hostname, peer2->hostname)) + return fastd_remote_configs_equal(remote1->next, remote2->next); +} + +bool fastd_peer_config_equal(const fastd_peer_config_t *peer1, const fastd_peer_config_t *peer2) { + if (peer1->group != peer2->group) return false; if(peer1->floating != peer2->floating) return false; - if (!fastd_peer_address_equal(&peer1->address, &peer2->address)) + if (!fastd_remote_configs_equal(peer1->remotes, peer2->remotes)) return false; if (!strequal(peer1->key, peer2->key)) @@ -414,7 +464,7 @@ bool fastd_peer_may_connect(fastd_context_t *ctx, fastd_peer_t *peer) { } fastd_peer_t* fastd_peer_add(fastd_context_t *ctx, fastd_peer_config_t *peer_conf) { - fastd_peer_t *peer = malloc(sizeof(fastd_peer_t)); + fastd_peer_t *peer = calloc(1, sizeof(fastd_peer_t)); peer->next = ctx->peers; ctx->peers = peer; @@ -422,9 +472,20 @@ fastd_peer_t* fastd_peer_add(fastd_context_t *ctx, fastd_peer_config_t *peer_con peer->config = peer_conf; peer->group = find_peer_group(ctx->peer_group, peer_conf->group); peer->protocol_config = peer_conf->protocol_config; - peer->protocol_state = NULL; - peer->sock = NULL; - peer->seen = (struct timespec){0, 0}; + + fastd_remote_t **remote = &peer->remotes; + fastd_remote_config_t *remote_config = peer_conf->remotes; + + while (remote_config) { + *remote = calloc(1, sizeof(fastd_remote_t)); + (*remote)->config = remote_config; + + if (!remote_config->hostname) + (*remote)->address = remote_config->address; + + remote = &(*remote)->next; + remote_config = remote_config->next; + } pr_verbose(ctx, "adding peer %P (group `%s')", peer, peer->group->conf->name); @@ -439,15 +500,12 @@ fastd_peer_t* fastd_peer_add_temporary(fastd_context_t *ctx) { if (!ctx->conf->on_verify) exit_bug(ctx, "tried to add temporary peer without on-verify command"); - fastd_peer_t *peer = malloc(sizeof(fastd_peer_t)); + fastd_peer_t *peer = calloc(1, sizeof(fastd_peer_t)); peer->next = ctx->peers_temp; ctx->peers_temp = peer; - peer->config = NULL; peer->group = ctx->peer_group; - peer->protocol_state = NULL; - peer->sock = NULL; peer->seen = ctx->now; pr_debug(ctx, "adding temporary peer"); @@ -513,20 +571,20 @@ const fastd_eth_addr_t* fastd_get_dest_address(const fastd_context_t *ctx, fastd } } -bool fastd_peer_config_matches_dynamic(const fastd_peer_config_t *config, const fastd_peer_address_t *addr) { - if (!config->hostname) +bool fastd_peer_remote_matches_dynamic(const fastd_remote_config_t *remote, const fastd_peer_address_t *addr) { + if (!remote->hostname) return false; - if (config->address.sa.sa_family != AF_UNSPEC && - config->address.sa.sa_family != addr->sa.sa_family) + if (remote->address.sa.sa_family != AF_UNSPEC && + remote->address.sa.sa_family != addr->sa.sa_family) return false; if (addr->sa.sa_family == AF_INET6) { - if (config->address.in.sin_port != addr->in6.sin6_port) + if (remote->address.in.sin_port != addr->in6.sin6_port) return false; } else { - if (config->address.in.sin_port != addr->in.sin_port) + if (remote->address.in.sin_port != addr->in.sin_port) return false; } diff --git a/src/peer.h b/src/peer.h index a363424..1b6303e 100644 --- a/src/peer.h +++ b/src/peer.h @@ -41,11 +41,11 @@ struct fastd_peer { fastd_peer_address_t address; fastd_peer_state_t state; - - struct timespec last_resolve; - struct timespec last_resolve_return; struct timespec seen; + fastd_remote_t *remotes; + fastd_remote_t *next_remote; + struct timespec last_handshake; fastd_peer_address_t last_handshake_address; @@ -64,11 +64,10 @@ struct fastd_peer_config { bool enabled; char *name; - char *hostname; - fastd_peer_address_t address; + fastd_remote_config_t *remotes; + char *key; bool floating; bool dynamic_float_deprecated; - char *key; const fastd_peer_group_config_t *group; fastd_protocol_peer_config_t *protocol_config; @@ -80,6 +79,22 @@ struct fastd_peer_eth_addr { struct timespec seen; }; +struct fastd_remote { + fastd_remote_t *next; + + fastd_remote_config_t *config; + fastd_peer_address_t address; + struct timespec last_resolve; + struct timespec last_resolve_return; +}; + +struct fastd_remote_config { + fastd_remote_config_t *next; + + char *hostname; + fastd_peer_address_t address; +}; + bool fastd_peer_address_equal(const fastd_peer_address_t *addr1, const fastd_peer_address_t *addr2); void fastd_peer_address_simplify(fastd_peer_address_t *addr); @@ -111,7 +126,9 @@ bool fastd_peer_verify_temporary(fastd_context_t *ctx, fastd_peer_t *peer, const void fastd_peer_enable_temporary(fastd_context_t *ctx, fastd_peer_t *peer); void fastd_peer_set_established(fastd_context_t *ctx, fastd_peer_t *peer); bool fastd_peer_may_connect(fastd_context_t *ctx, fastd_peer_t *peer); -void fastd_peer_handle_resolve(fastd_context_t *ctx, fastd_peer_t *peer, const fastd_peer_address_t *address); +void fastd_peer_handle_resolve(fastd_context_t *ctx, fastd_peer_t *peer, fastd_remote_t *remote, const fastd_peer_address_t *address); +bool fastd_peer_owns_address(fastd_context_t *ctx, const fastd_peer_t *peer, const fastd_peer_address_t *addr); +bool fastd_peer_matches_address(fastd_context_t *ctx, const fastd_peer_t *peer, const fastd_peer_address_t *addr); bool fastd_peer_claim_address(fastd_context_t *ctx, fastd_peer_t *peer, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr); void fastd_peer_reset_socket(fastd_context_t *ctx, fastd_peer_t *peer); @@ -123,23 +140,15 @@ static inline bool fastd_peer_allow_unknown(fastd_context_t *ctx) { } static inline bool fastd_peer_config_is_floating(const fastd_peer_config_t *config) { - return ((config->hostname == NULL && config->address.sa.sa_family == AF_UNSPEC) || config->floating); + return (!config->remotes || config->floating); } -static inline bool fastd_peer_config_is_dynamic(const fastd_peer_config_t *config) { - return (config->hostname != NULL); -} - -bool fastd_peer_config_matches_dynamic(const fastd_peer_config_t *config, const fastd_peer_address_t *addr); +bool fastd_peer_remote_matches_dynamic(const fastd_remote_config_t *remote, const fastd_peer_address_t *addr); static inline bool fastd_peer_is_floating(const fastd_peer_t *peer) { return peer->config ? fastd_peer_config_is_floating(peer->config) : true; } -static inline bool fastd_peer_is_dynamic(const fastd_peer_t *peer) { - return peer->config ? fastd_peer_config_is_dynamic(peer->config) : false; -} - static inline bool fastd_peer_is_temporary(const fastd_peer_t *peer) { return (!peer->config); } @@ -154,6 +163,10 @@ static inline bool fastd_peer_is_established(const fastd_peer_t *peer) { } } +static inline bool fastd_peer_remote_is_dynamic(const fastd_remote_t *remote) { + return remote->config->hostname; +} + static inline void fastd_peer_seen(fastd_context_t *ctx, fastd_peer_t *peer) { peer->seen = ctx->now; } diff --git a/src/protocol_ec25519_fhmqvc.c b/src/protocol_ec25519_fhmqvc.c index 487acdc..c9baba4 100644 --- a/src/protocol_ec25519_fhmqvc.c +++ b/src/protocol_ec25519_fhmqvc.c @@ -550,20 +550,31 @@ static void handle_finish_handshake(fastd_context_t *ctx, fastd_socket_t *sock, } static fastd_peer_t* find_sender_key(fastd_context_t *ctx, const fastd_peer_address_t *address, const unsigned char key[32], fastd_peer_t *peers) { - fastd_peer_t *peer; + errno = 0; + + fastd_peer_t *ret = NULL, *peer; + for (peer = peers; peer; peer = peer->next) { - if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) != 0) - continue; + if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) { + if (!fastd_peer_matches_address(ctx, peer, address)) { + errno = EPERM; + return NULL; + } - if (fastd_peer_is_floating(peer)) - return peer; + ret = peer; + continue; + } - errno = EPERM; - return NULL; + if (fastd_peer_owns_address(ctx, peer, address)) { + errno = EPERM; + return NULL; + } } - errno = ENOENT; - return NULL; + if (!ret) + errno = ENOENT; + + return ret; } static fastd_peer_t* match_sender_key(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *address, fastd_peer_t *peer, const unsigned char key[32]) { @@ -573,27 +584,19 @@ static fastd_peer_t* match_sender_key(fastd_context_t *ctx, const fastd_socket_t exit_bug(ctx, "packet without correct peer set on dynamic socket"); if (peer) { - if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) { - if (sock->peer && sock->peer != peer) { - errno = EPERM; - return NULL; - } - + if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) return peer; - } - } - if (peer && !fastd_peer_is_floating(peer) && !fastd_peer_is_dynamic(peer)) { - errno = EPERM; - return NULL; + if (fastd_peer_owns_address(ctx, peer, address)) { + errno = EPERM; + return NULL; + } } peer = find_sender_key(ctx, address, key, ctx->peers); - if (!peer && errno == ENOENT) { - errno = 0; + if (!peer && errno == ENOENT) peer = find_sender_key(ctx, address, key, ctx->peers_temp); - } return peer; } diff --git a/src/resolve.c b/src/resolve.c index 103ba5f..5a18002 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -99,27 +99,27 @@ static void* resolve_peer(void *varg) { return NULL; } -void fastd_resolve_peer(fastd_context_t *ctx, fastd_peer_t *peer) { +void fastd_resolve_peer(fastd_context_t *ctx, fastd_peer_t *peer, fastd_remote_t *remote) { if (!peer->config) exit_bug(ctx, "trying to resolve temporary peer"); - if (timespec_after(&peer->last_resolve, &peer->last_resolve_return)) { + if (timespec_after(&remote->last_resolve, &remote->last_resolve_return)) { pr_debug(ctx, "not resolving %P as there is already a resolve running", peer); return; } - if (timespec_diff(&ctx->now, &peer->last_resolve) < ctx->conf->min_resolve_interval*1000) { + if (timespec_diff(&ctx->now, &remote->last_resolve) < ctx->conf->min_resolve_interval*1000) { /* last resolve was just a few seconds ago */ return; } - pr_verbose(ctx, "resolving host `%s' for peer %P...", peer->config->hostname, peer); + pr_verbose(ctx, "resolving host `%s' for peer %P...", remote->config->hostname, peer); resolv_arg_t *arg = malloc(sizeof(resolv_arg_t)); arg->ctx = ctx; - arg->hostname = strdup(peer->config->hostname); - arg->constraints = peer->config->address; + arg->hostname = strdup(remote->config->hostname); + arg->constraints = remote->config->address; pthread_t thread; if (pthread_create(&thread, NULL, resolve_peer, arg) != 0) { @@ -132,5 +132,5 @@ void fastd_resolve_peer(fastd_context_t *ctx, fastd_peer_t *peer) { } pthread_detach(thread); - peer->last_resolve = ctx->now; + remote->last_resolve = ctx->now; } diff --git a/src/types.h b/src/types.h index 0425db7..7cec771 100644 --- a/src/types.h +++ b/src/types.h @@ -66,6 +66,8 @@ typedef struct fastd_peer_config fastd_peer_config_t; typedef struct fastd_eth_addr fastd_eth_addr_t; typedef struct fastd_peer fastd_peer_t; typedef struct fastd_peer_eth_addr fastd_peer_eth_addr_t; +typedef struct fastd_remote_config fastd_remote_config_t; +typedef struct fastd_remote fastd_remote_t; typedef struct fastd_log_file fastd_log_file_t; typedef struct fastd_log_fd fastd_log_fd_t; -- cgit v1.2.3