diff options
-rw-r--r-- | src/fastd.c | 42 | ||||
-rw-r--r-- | src/fastd.h | 9 | ||||
-rw-r--r-- | src/peer.c | 188 | ||||
-rw-r--r-- | src/peer.h | 7 | ||||
-rw-r--r-- | src/queue.c | 5 |
5 files changed, 218 insertions, 33 deletions
diff --git a/src/fastd.c b/src/fastd.c index 6db09b6..fc443a6 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -36,7 +36,6 @@ #include <linux/if_tun.h> #include <net/if.h> #include <poll.h> -#include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -122,6 +121,11 @@ static void init_socket(fastd_context *ctx) { static void default_config(fastd_config *conf) { conf->loglevel = LOG_DEBUG; + + conf->peer_stale_time = 300; + conf->peer_stale_time_temp = 30; + conf->eth_addr_stale_time = 300; + conf->ifname = NULL; memset(&conf->bind_addr_in, 0, sizeof(struct sockaddr_in)); @@ -398,7 +402,7 @@ static void handle_tasks(fastd_context *ctx) { const fastd_eth_addr *src_addr = fastd_get_source_address(ctx, task->handle_recv.buffer); if (fastd_eth_addr_is_unicast(src_addr)) - fastd_peer_add_eth_addr(ctx, task->peer, src_addr); + fastd_peer_eth_addr_add(ctx, task->peer, src_addr); } write(ctx->tunfd, task->handle_recv.buffer.data, task->handle_recv.buffer.len); @@ -522,10 +526,12 @@ static void handle_socket(fastd_context *ctx, int sockfd) { if (peer) { switch (packet_type) { case PACKET_DATA: + peer->seen = ctx->now; ctx->conf->method->handle_recv(ctx, peer, buffer); break; case PACKET_HANDSHAKE: + peer->seen = ctx->now; fastd_handshake_handle(ctx, peer, buffer); break; @@ -566,7 +572,12 @@ static void handle_input(fastd_context *ctx) { fds[2].fd = ctx->sock6fd; fds[2].events = POLLIN; - int ret = poll(fds, 3, fastd_task_timeout(ctx)); + int timeout = fastd_task_timeout(ctx); + + if (timeout < 0 || timeout > 60000) + timeout = 60000; /* call maintenance at least once a minute */ + + int ret = poll(fds, 3, timeout); if (ret < 0) exit_errno(ctx, "poll"); @@ -580,6 +591,29 @@ static void handle_input(fastd_context *ctx) { handle_socket(ctx, ctx->sock6fd); } +static void cleanup_peers(fastd_context *ctx) { + fastd_peer *peer, *next; + + for (peer = ctx->peers; peer; peer = next) { + next = peer->next; + + if (fastd_peer_is_temporary(peer)) { + if (timespec_diff(&ctx->now, &peer->seen) > ctx->conf->peer_stale_time_temp*1000) + fastd_peer_delete(ctx, peer); + } + else if (fastd_peer_is_established(peer)) { + if (timespec_diff(&ctx->now, &peer->seen) > ctx->conf->peer_stale_time*1000) + fastd_peer_reset(ctx, peer); + } + } +} + +static void maintenance(fastd_context *ctx) { + cleanup_peers(ctx); + + fastd_peer_eth_addr_cleanup(ctx); +} + int main(int argc, char *argv[]) { fastd_context ctx; @@ -599,6 +633,8 @@ int main(int argc, char *argv[]) { while (1) { handle_tasks(&ctx); handle_input(&ctx); + + maintenance(&ctx); } return 0; diff --git a/src/fastd.h b/src/fastd.h index e4bef44..9ce15e7 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -68,6 +68,10 @@ struct _fastd_method { struct _fastd_config { fastd_loglevel loglevel; + unsigned peer_stale_time; + unsigned peer_stale_time_temp; + unsigned eth_addr_stale_time; + char *ifname; struct sockaddr_in bind_addr_in; @@ -147,4 +151,9 @@ static inline size_t fastd_max_packet_size(const fastd_context *ctx) { } } +/* returns (tp1 - tp2) in milliseconds */ +static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) { + return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6; +} + #endif /* _FASTD_FASTD_H_ */ @@ -25,6 +25,8 @@ */ +#define _GNU_SOURCE + #include "peer.h" #include "task.h" @@ -49,6 +51,66 @@ const fastd_eth_addr* fastd_get_dest_address(const fastd_context *ctx, fastd_buf } } +static inline void reset_peer(fastd_context *ctx, fastd_peer *peer) { + int i, deleted = 0; + for (i = 0; i < ctx->n_eth_addr; i++) { + if (ctx->eth_addr[i].peer == peer) { + deleted++; + } + else if (deleted) { + ctx->eth_addr[i-deleted] = ctx->eth_addr[i]; + } + } + + ctx->n_eth_addr -= deleted; + + fastd_task_delete_peer(ctx, peer); + +} + +static inline void setup_peer(fastd_context *ctx, fastd_peer *peer) { + if (fastd_peer_is_temporary(peer)) { + exit_fatal(ctx, "tried to reset temporary peer"); + } + + peer->address = peer->config->address; + peer->state = STATE_WAIT; + peer->seen = (struct timespec){0, 0}; + + if (!fastd_peer_is_floating(peer)) + fastd_task_schedule_handshake(ctx, peer, 0); +} + +void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer) { + if (is_debug(ctx)) { + char buf[INET6_ADDRSTRLEN] = ""; + + switch (peer->address.sa.sa_family) { + case AF_UNSPEC: + pr_debug(ctx, "resetting peer <floating>"); + break; + + case AF_INET: + if (inet_ntop(AF_INET, &peer->address.in.sin_addr, buf, sizeof(buf))) + pr_debug(ctx, "resetting peer %s:%u", buf, ntohs(peer->address.in.sin_port)); + break; + + case AF_INET6: + if (inet_ntop(AF_INET6, &peer->address.in6.sin6_addr, buf, sizeof(buf))) + pr_debug(ctx, "resetting peer [%s]:%u", buf, ntohs(peer->address.in6.sin6_port)); + break; + + default: + exit_bug(ctx, "unsupported address family"); + } + } + + reset_peer(ctx, peer); + setup_peer(ctx, peer); +} + + + static fastd_peer* add_peer(fastd_context *ctx) { fastd_peer *peer = malloc(sizeof(fastd_peer)); @@ -63,26 +125,27 @@ static fastd_peer* add_peer(fastd_context *ctx) { fastd_peer* fastd_peer_add(fastd_context *ctx, fastd_peer_config *peer_conf) { fastd_peer *peer = add_peer(ctx); + peer->config = peer_conf; - peer->address = peer_conf->address; - peer->state = STATE_WAIT; + + setup_peer(ctx, peer); if (is_debug(ctx)) { char buf[INET6_ADDRSTRLEN] = ""; switch (peer->address.sa.sa_family) { case AF_UNSPEC: - pr_debug(ctx, "added peer <floating>"); + pr_debug(ctx, "adding peer <floating>"); break; case AF_INET: if (inet_ntop(AF_INET, &peer->address.in.sin_addr, buf, sizeof(buf))) - pr_debug(ctx, "added peer %s:%u", buf, ntohs(peer->address.in.sin_port)); + pr_debug(ctx, "adding peer %s:%u", buf, ntohs(peer->address.in.sin_port)); break; case AF_INET6: if (inet_ntop(AF_INET6, &peer->address.in6.sin6_addr, buf, sizeof(buf))) - pr_debug(ctx, "added peer [%s]:%u", buf, ntohs(peer->address.in6.sin6_port)); + pr_debug(ctx, "adding peer [%s]:%u", buf, ntohs(peer->address.in6.sin6_port)); break; default: @@ -90,9 +153,6 @@ fastd_peer* fastd_peer_add(fastd_context *ctx, fastd_peer_config *peer_conf) { } } - if (!fastd_peer_is_floating(peer)) - fastd_task_schedule_handshake(ctx, peer, 0); - return peer; } @@ -105,6 +165,7 @@ fastd_peer* fastd_peer_add_temp(fastd_context *ctx, const fastd_peer_address *ad peer->config = NULL; peer->address = *address; peer->state = STATE_TEMP; + peer->seen = ctx->now; if (is_debug(ctx)) { char buf[INET6_ADDRSTRLEN] = ""; @@ -112,12 +173,12 @@ fastd_peer* fastd_peer_add_temp(fastd_context *ctx, const fastd_peer_address *ad switch (peer->address.sa.sa_family) { case AF_INET: if (inet_ntop(AF_INET, &peer->address.in.sin_addr, buf, sizeof(buf))) - pr_debug(ctx, "added peer %s:%u (temporary)", buf, ntohs(peer->address.in.sin_port)); + pr_debug(ctx, "adding peer %s:%u (temporary)", buf, ntohs(peer->address.in.sin_port)); break; case AF_INET6: if (inet_ntop(AF_INET6, &peer->address.in6.sin6_addr, buf, sizeof(buf))) - pr_debug(ctx, "added peer [%s]:%u (temporary)", buf, ntohs(peer->address.in6.sin6_port)); + pr_debug(ctx, "adding peer [%s]:%u (temporary)", buf, ntohs(peer->address.in6.sin6_port)); break; default: @@ -129,10 +190,56 @@ fastd_peer* fastd_peer_add_temp(fastd_context *ctx, const fastd_peer_address *ad } fastd_peer* fastd_peer_merge(fastd_context *ctx, fastd_peer *perm_peer, fastd_peer *temp_peer) { - pr_debug(ctx, "merging peers"); + if (is_debug(ctx)) { + char buf[INET6_ADDRSTRLEN]; + char *str_perm = NULL, *str_temp = NULL; + + switch (perm_peer->address.sa.sa_family) { + case AF_UNSPEC: + str_perm = strdup("<floating>"); + break; + + case AF_INET: + if (inet_ntop(AF_INET, &perm_peer->address.in.sin_addr, buf, sizeof(buf))) + asprintf(&str_perm, "%s:%u", buf, ntohs(perm_peer->address.in.sin_port)); + break; + + case AF_INET6: + if (inet_ntop(AF_INET6, &perm_peer->address.in6.sin6_addr, buf, sizeof(buf))) + asprintf(&str_perm, "[%s]:%u", buf, ntohs(perm_peer->address.in.sin_port)); + break; + + default: + exit_bug(ctx, "unsupported address family"); + } + + switch (temp_peer->address.sa.sa_family) { + case AF_UNSPEC: + str_temp = strdup("<floating>"); + break; + + case AF_INET: + if (inet_ntop(AF_INET, &temp_peer->address.in.sin_addr, buf, sizeof(buf))) + asprintf(&str_temp, "%s:%u", buf, ntohs(temp_peer->address.in.sin_port)); + break; + + case AF_INET6: + if (inet_ntop(AF_INET6, &temp_peer->address.in6.sin6_addr, buf, sizeof(buf))) + asprintf(&str_temp, "[%s]:%u", buf, ntohs(temp_peer->address.in.sin_port)); + break; + + default: + exit_bug(ctx, "unsupported address family"); + } + + pr_debug(ctx, "merging peer %s into %s", str_temp, str_perm); + free(str_temp); + free(str_perm); + } perm_peer->address = temp_peer->address; perm_peer->state = fastd_peer_is_established(temp_peer) ? STATE_ESTABLISHED : STATE_WAIT; + perm_peer->seen = temp_peer->seen; int i; for (i = 0; i < ctx->n_eth_addr; i++) { @@ -147,18 +254,30 @@ fastd_peer* fastd_peer_merge(fastd_context *ctx, fastd_peer *perm_peer, fastd_pe } void fastd_peer_delete(fastd_context *ctx, fastd_peer *peer) { - int i, deleted = 0; + if (is_debug(ctx)) { + char buf[INET6_ADDRSTRLEN]; - for (i = 0; i < ctx->n_eth_addr; i++) { - if (ctx->eth_addr[i].peer == peer) { - deleted++; - } - else if (deleted) { - ctx->eth_addr[i-deleted] = ctx->eth_addr[i]; + switch (peer->address.sa.sa_family) { + case AF_UNSPEC: + pr_debug(ctx, "deleting peer <floating>"); + break; + + case AF_INET: + if (inet_ntop(AF_INET, &peer->address.in.sin_addr, buf, sizeof(buf))) + pr_debug(ctx, "deleting peer %s:%u%s", buf, ntohs(peer->address.in.sin_port), fastd_peer_is_temporary(peer) ? " (temporary)" : ""); + break; + + case AF_INET6: + if (inet_ntop(AF_INET6, &peer->address.in6.sin6_addr, buf, sizeof(buf))) + pr_debug(ctx, "deleting peer [%s]:%u%s", buf, ntohs(peer->address.in6.sin6_port), fastd_peer_is_temporary(peer) ? " (temporary)" : ""); + break; + + default: + exit_bug(ctx, "unsupported address family"); } } - ctx->n_eth_addr -= deleted; + reset_peer(ctx, peer); fastd_peer **cur_peer; for (cur_peer = &ctx->peers; *cur_peer; cur_peer = &(*cur_peer)->next) { @@ -168,8 +287,6 @@ void fastd_peer_delete(fastd_context *ctx, fastd_peer *peer) { } } - fastd_task_delete_peer(ctx, peer); - free(peer); } @@ -182,11 +299,11 @@ static inline int fastd_peer_eth_addr_cmp(const fastd_peer_eth_addr *addr1, cons } static inline fastd_peer_eth_addr* peer_get_by_addr(fastd_context *ctx, const fastd_eth_addr *addr) { - return bsearch(((uint8_t*)addr)-offsetof(fastd_peer_eth_addr, addr), ctx->eth_addr, ctx->n_eth_addr, sizeof(fastd_peer_eth_addr), + return bsearch(container_of(addr, fastd_peer_eth_addr, addr), ctx->eth_addr, ctx->n_eth_addr, sizeof(fastd_peer_eth_addr), (int (*)(const void *, const void *))fastd_peer_eth_addr_cmp); } -void fastd_peer_add_eth_addr(fastd_context *ctx, fastd_peer *peer, const fastd_eth_addr *addr) { +void fastd_peer_eth_addr_add(fastd_context *ctx, fastd_peer *peer, const fastd_eth_addr *addr) { int min = 0, max = ctx->n_eth_addr; while (max > min) { @@ -195,6 +312,7 @@ void fastd_peer_add_eth_addr(fastd_context *ctx, fastd_peer *peer, const fastd_e if (cmp == 0) { ctx->eth_addr[cur].peer = peer; + ctx->eth_addr[cur].seen = ctx->now; return; /* We're done here. */ } else if (cmp < 0) { @@ -219,7 +337,29 @@ void fastd_peer_add_eth_addr(fastd_context *ctx, fastd_peer *peer, const fastd_e for (i = ctx->n_eth_addr-1; i > min+1; i--) ctx->eth_addr[i] = ctx->eth_addr[i-1]; - ctx->eth_addr[min] = (fastd_peer_eth_addr){ *addr, peer }; + ctx->eth_addr[min] = (fastd_peer_eth_addr){ *addr, peer, ctx->now }; + + pr_debug(ctx, "Learned new MAC address %02x:%02x:%02x:%02x:%02x:%02x", + addr->data[0], addr->data[1], addr->data[2], addr->data[3], addr->data[4], addr->data[5]); +} + +void fastd_peer_eth_addr_cleanup(fastd_context *ctx) { + int i, deleted = 0; + + for (i = 0; i < ctx->n_eth_addr; i++) { + if (timespec_diff(&ctx->now, &ctx->eth_addr[i].seen) > ctx->conf->eth_addr_stale_time*1000) { + deleted++; + pr_debug(ctx, "MAC address %02x:%02x:%02x:%02x:%02x:%02x not seen for more than %u seconds, removing", + ctx->eth_addr[i].addr.data[0], ctx->eth_addr[i].addr.data[1], ctx->eth_addr[i].addr.data[2], + ctx->eth_addr[i].addr.data[3], ctx->eth_addr[i].addr.data[4], ctx->eth_addr[i].addr.data[5], + ctx->conf->eth_addr_stale_time); + } + else if (deleted) { + ctx->eth_addr[i-deleted] = ctx->eth_addr[i]; + } + } + + ctx->n_eth_addr -= deleted; } fastd_peer *fastd_peer_find_by_eth_addr(fastd_context *ctx, const fastd_eth_addr *addr) { @@ -46,6 +46,8 @@ struct _fastd_peer { fastd_peer_state state; uint8_t last_req_id; + + struct timespec seen; }; struct _fastd_peer_config { @@ -61,12 +63,14 @@ struct _fastd_eth_addr { struct _fastd_peer_eth_addr { fastd_eth_addr addr; fastd_peer *peer; + struct timespec seen; }; 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); +void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer); fastd_peer* fastd_peer_add(fastd_context *ctx, fastd_peer_config *conf); fastd_peer* fastd_peer_add_temp(fastd_context *ctx, const fastd_peer_address *address); fastd_peer* fastd_peer_merge(fastd_context *ctx, fastd_peer *perm_peer, fastd_peer *temp_peer); @@ -108,7 +112,8 @@ static inline bool fastd_eth_addr_is_unicast(const fastd_eth_addr *addr) { return ((addr->data[0] & 1) == 0); } -void fastd_peer_add_eth_addr(fastd_context *ctx, fastd_peer *peer, const fastd_eth_addr *addr); +void fastd_peer_eth_addr_add(fastd_context *ctx, fastd_peer *peer, const fastd_eth_addr *addr); +void fastd_peer_eth_addr_cleanup(fastd_context *ctx); fastd_peer* fastd_peer_find_by_eth_addr(fastd_context *ctx, const fastd_eth_addr *addr); #endif /* _FASTD_PEER_H_ */ diff --git a/src/queue.c b/src/queue.c index d950e33..4b5a3f0 100644 --- a/src/queue.c +++ b/src/queue.c @@ -31,11 +31,6 @@ #include <stdint.h> -/* returns (tp1 - tp2) in milliseconds */ -static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) { - return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6; -} - static inline bool after(const struct timespec *tp1, const struct timespec *tp2) { return (tp1->tv_sec > tp2->tv_sec || (tp1->tv_sec == tp2->tv_sec && tp1->tv_nsec > tp2->tv_nsec)); |