summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/fastd.c42
-rw-r--r--src/fastd.h9
-rw-r--r--src/peer.c188
-rw-r--r--src/peer.h7
-rw-r--r--src/queue.c5
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_ */
diff --git a/src/peer.c b/src/peer.c
index 5cce906..83cc1b0 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -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) {
diff --git a/src/peer.h b/src/peer.h
index 64cb912..8a1ca7e 100644
--- a/src/peer.h
+++ b/src/peer.h
@@ -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));