summaryrefslogtreecommitdiffstats
path: root/src/poll.c
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2014-04-20 02:48:25 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2014-04-20 02:48:25 +0200
commitab4ca17ba3dfc92932834b09afc83cf7fe002a14 (patch)
treeddaad23777006ce39a3fda055fad7efe5cd09289 /src/poll.c
parent8c705b9cd4c7866227f09af2a859744e47602ba4 (diff)
downloadfastd-ab4ca17ba3dfc92932834b09afc83cf7fe002a14.tar
fastd-ab4ca17ba3dfc92932834b09afc83cf7fe002a14.zip
Linux: use epoll to handle great numbers of peers with less overhead
Diffstat (limited to 'src/poll.c')
-rw-r--r--src/poll.c219
1 files changed, 171 insertions, 48 deletions
diff --git a/src/poll.c b/src/poll.c
index 31e69bf..150f2ca 100644
--- a/src/poll.c
+++ b/src/poll.c
@@ -28,6 +28,174 @@
#include "async.h"
#include "peer.h"
+#ifdef USE_EPOLL
+
+#include <sys/epoll.h>
+
+#endif
+
+
+static inline bool handle_tun_tap(fastd_context_t *ctx, fastd_buffer_t buffer) {
+ if (ctx->conf->mode != MODE_TAP)
+ return false;
+
+ if (buffer.len < ETH_HLEN) {
+ pr_debug(ctx, "truncated packet on tap interface");
+ fastd_buffer_free(buffer);
+ return true;
+ }
+
+ fastd_eth_addr_t dest_addr = fastd_get_dest_address(ctx, buffer);
+ if (!fastd_eth_addr_is_unicast(dest_addr))
+ return false;
+
+ fastd_peer_t *peer = fastd_peer_find_by_eth_addr(ctx, dest_addr);
+
+ if (!peer)
+ return false;
+
+ ctx->conf->protocol->send(ctx, peer, buffer);
+ return true;
+}
+
+static void handle_tun(fastd_context_t *ctx) {
+ fastd_buffer_t buffer = fastd_tuntap_read(ctx);
+ if (!buffer.len)
+ return;
+
+ if (handle_tun_tap(ctx, buffer))
+ return;
+
+ /* TUN mode or multicast packet */
+ fastd_send_all(ctx, NULL, buffer);
+}
+
+static inline int handshake_timeout(fastd_context_t *ctx) {
+ if (!ctx->handshake_queue.next)
+ return -1;
+
+ fastd_peer_t *peer = container_of(ctx->handshake_queue.next, fastd_peer_t, handshake_entry);
+
+ int diff_msec = timespec_diff(&peer->next_handshake, &ctx->now);
+ if (diff_msec < 0)
+ return 0;
+ else
+ return diff_msec;
+}
+
+
+#ifdef USE_EPOLL
+
+#include <fcntl.h>
+#include <sys/epoll.h>
+
+
+void fastd_poll_init(fastd_context_t *ctx) {
+ ctx->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (ctx->epoll_fd < 0)
+ exit_errno(ctx, "epoll_create1");
+
+ struct epoll_event event = {
+ .events = EPOLLIN,
+ .data.ptr = &ctx->async_rfd,
+ };
+ if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, ctx->async_rfd, &event) < 0)
+ exit_errno(ctx, "epoll_ctl");
+}
+
+void fastd_poll_free(fastd_context_t *ctx) {
+ if (close(ctx->epoll_fd))
+ pr_warn_errno(ctx, "closing EPOLL: close");
+}
+
+
+void fastd_poll_set_fd_tuntap(fastd_context_t *ctx) {
+ struct epoll_event event = {
+ .events = EPOLLIN,
+ .data.ptr = &ctx->tunfd,
+ };
+ if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, ctx->tunfd, &event) < 0)
+ exit_errno(ctx, "epoll_ctl");
+}
+
+void fastd_poll_set_fd_sock(fastd_context_t *ctx, size_t i) {
+ struct epoll_event event = {
+ .events = EPOLLIN,
+ .data.ptr = &ctx->socks[i],
+ };
+ if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, ctx->socks[i].fd, &event) < 0)
+ exit_errno(ctx, "epoll_ctl");
+}
+
+void fastd_poll_set_fd_peer(fastd_context_t *ctx, size_t i) {
+ fastd_peer_t *peer = VECTOR_INDEX(ctx->peers, i);
+
+ if (!peer->sock || !fastd_peer_is_socket_dynamic(peer))
+ return;
+
+ struct epoll_event event = {
+ .events = EPOLLIN,
+ .data.ptr = peer->sock,
+ };
+ if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, peer->sock->fd, &event) < 0)
+ exit_errno(ctx, "epoll_ctl");
+}
+
+void fastd_poll_add_peer(fastd_context_t *ctx UNUSED) {
+}
+
+void fastd_poll_delete_peer(fastd_context_t *ctx UNUSED, size_t i UNUSED) {
+}
+
+
+void fastd_poll_handle(fastd_context_t *ctx) {
+ int maintenance_timeout = timespec_diff(&ctx->next_maintenance, &ctx->now);
+
+ if (maintenance_timeout < 0)
+ maintenance_timeout = 0;
+
+ int timeout = handshake_timeout(ctx);
+ if (timeout < 0 || timeout > maintenance_timeout)
+ timeout = maintenance_timeout;
+
+ fastd_update_time(ctx);
+
+ struct epoll_event events[16];
+ int ret = epoll_wait(ctx->epoll_fd, events, 16, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ return;
+
+ exit_errno(ctx, "epoll_wait");
+ }
+
+ size_t i;
+ for (i = 0; i < (size_t)ret; i++) {
+ if (events[i].data.ptr == &ctx->tunfd) {
+ if (events[i].events & EPOLLIN)
+ handle_tun(ctx);
+ }
+ else if (events[i].data.ptr == &ctx->async_rfd) {
+ if (events[i].events & EPOLLIN)
+ fastd_async_handle(ctx);
+ }
+ else {
+ fastd_socket_t *sock = events[i].data.ptr;
+
+ if (events[i].events & (EPOLLERR|EPOLLHUP)) {
+ if (sock->peer)
+ fastd_peer_reset_socket(ctx, sock->peer);
+ else
+ fastd_socket_error(ctx, sock);
+ }
+ else if (events[i].events & EPOLLIN) {
+ fastd_receive(ctx, sock);
+ }
+ }
+ }
+}
+
+#else
void fastd_poll_init(fastd_context_t *ctx) {
VECTOR_ALLOC(ctx->pollfds, 2 + ctx->n_socks);
@@ -91,54 +259,6 @@ void fastd_poll_delete_peer(fastd_context_t *ctx, size_t i) {
}
-static inline bool handle_tun_tap(fastd_context_t *ctx, fastd_buffer_t buffer) {
- if (ctx->conf->mode != MODE_TAP)
- return false;
-
- if (buffer.len < ETH_HLEN) {
- pr_debug(ctx, "truncated packet on tap interface");
- fastd_buffer_free(buffer);
- return true;
- }
-
- fastd_eth_addr_t dest_addr = fastd_get_dest_address(ctx, buffer);
- if (!fastd_eth_addr_is_unicast(dest_addr))
- return false;
-
- fastd_peer_t *peer = fastd_peer_find_by_eth_addr(ctx, dest_addr);
-
- if (!peer)
- return false;
-
- ctx->conf->protocol->send(ctx, peer, buffer);
- return true;
-}
-
-static void handle_tun(fastd_context_t *ctx) {
- fastd_buffer_t buffer = fastd_tuntap_read(ctx);
- if (!buffer.len)
- return;
-
- if (handle_tun_tap(ctx, buffer))
- return;
-
- /* TUN mode or multicast packet */
- fastd_send_all(ctx, NULL, buffer);
-}
-
-static inline int handshake_timeout(fastd_context_t *ctx) {
- if (!ctx->handshake_queue.next)
- return -1;
-
- fastd_peer_t *peer = container_of(ctx->handshake_queue.next, fastd_peer_t, handshake_entry);
-
- int diff_msec = timespec_diff(&peer->next_handshake, &ctx->now);
- if (diff_msec < 0)
- return 0;
- else
- return diff_msec;
-}
-
void fastd_poll_handle(fastd_context_t *ctx) {
int maintenance_timeout = timespec_diff(&ctx->next_maintenance, &ctx->now);
@@ -186,3 +306,6 @@ void fastd_poll_handle(fastd_context_t *ctx) {
}
}
}
+
+#endif
+