diff --git a/configure.in b/configure.in
index 3cb6a4de..a29a88f4 100644
--- a/configure.in
+++ b/configure.in
@@ -86,6 +86,9 @@ fi
 
 dnl Checks for libraries.
 
+AC_CHECK_LIB(event, event_init,
+  [], [AC_MSG_ERROR(libevent is required)])
+
 dnl Checks for header files.
 dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies.
 
diff --git a/src/connection.c b/src/connection.c
index c4c9fd48..1f2f96cc 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -70,6 +70,7 @@ connection_t *new_connection(void)
 		return NULL;
 
 	gettimeofday(&c->start, NULL);
+	event_set(&c->ev, -1, 0, NULL, NULL);
 
 	return c;
 }
@@ -78,20 +79,14 @@ void free_connection(connection_t *c)
 {
 	cp();
 
-	if(c->hostname)
+	if(c) {
 		free(c->hostname);
-
-	if(c->inkey)
 		free(c->inkey);
-
-	if(c->outkey)
 		free(c->outkey);
-
-	if(c->mychallenge)
 		free(c->mychallenge);
-
-	if(c->hischallenge)
 		free(c->hischallenge);
+		event_del(&c->ev);
+	}
 
 	free(c);
 }
diff --git a/src/connection.h b/src/connection.h
index 087d8f00..8d5537b2 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -26,6 +26,8 @@
 #include <openssl/rsa.h>
 #include <openssl/evp.h>
 
+#include <event.h>
+
 #include "avl_tree.h"
 
 #define OPTION_INDIRECT		0x0001
@@ -60,6 +62,7 @@ typedef struct connection_t {
 	char *hostname;				/* the hostname of its real ip */
 	int protocol_version;		/* used protocol */
 
+	struct event ev;			/* events on this metadata connection */
 	int socket;					/* socket used for this connection */
 	long int options;			/* options for this connection */
 	connection_status_t status;	/* status info */
diff --git a/src/net.c b/src/net.c
index e9c7020a..3a44ce66 100644
--- a/src/net.c
+++ b/src/net.c
@@ -109,10 +109,10 @@ static void purge(void)
 }
 
 /*
-  put all file descriptors in an fd_set array
-  While we're at it, purge stuff that needs to be removed.
+  put all file descriptors into events
+  While we're at it, purge stuf that needs to be removed.
 */
-static int build_fdset(fd_set *readset, fd_set *writeset)
+static int build_fdset(void)
 {
 	avl_node_t *node, *next;
 	connection_t *c;
@@ -120,9 +120,6 @@ static int build_fdset(fd_set *readset, fd_set *writeset)
 
 	cp();
 
-	FD_ZERO(readset);
-	FD_ZERO(writeset);
-
 	for(node = connection_tree->head; node; node = next) {
 		next = node->next;
 		c = node->data;
@@ -132,28 +129,17 @@ static int build_fdset(fd_set *readset, fd_set *writeset)
 			if(!connection_tree->head)
 				purge();
 		} else {
-			FD_SET(c->socket, readset);
+			short events = EV_READ;
 			if(c->outbuflen > 0)
-				FD_SET(c->socket, writeset);
-			if(c->socket > max)
-				max = c->socket;
+				events |= EV_WRITE;
+			event_del(&c->ev);
+			event_set(&c->ev, c->socket, events,
+					  handle_meta_connection_data, c);
+			if (event_add(&c->ev, NULL) < 0)
+				return -1;
 		}
 	}
-
-	for(i = 0; i < listen_sockets; i++) {
-		FD_SET(listen_socket[i].tcp, readset);
-		if(listen_socket[i].tcp > max)
-			max = listen_socket[i].tcp;
-		FD_SET(listen_socket[i].udp, readset);
-		if(listen_socket[i].udp > max)
-			max = listen_socket[i].udp;
-	}
-
-	FD_SET(device_fd, readset);
-	if(device_fd > max)
-		max = device_fd;
-	
-	return max;
+	return 0;
 }
 
 /*
@@ -279,83 +265,59 @@ static void check_dead_connections(void)
 	}
 }
 
-/*
-  check all connections to see if anything
-  happened on their sockets
-*/
-static void check_network_activity(fd_set * readset, fd_set * writeset)
+void handle_meta_connection_data(int fd, short events, void *data)
 {
-	connection_t *c;
-	avl_node_t *node;
-	int result, i;
+	connection_t *c = data;
+	int result;
 	socklen_t len = sizeof(result);
-	vpn_packet_t packet;
 
-	cp();
+	if (c->status.remove)
+		return;
 
-	/* check input from kernel */
-	if(FD_ISSET(device_fd, readset)) {
-		if(read_packet(&packet))
-			route(myself, &packet);
-	}
+	if (events & EV_READ) {
+		if(c->status.connecting) {
+			c->status.connecting = false;
+			getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
 
-	/* check meta connections */
-	for(node = connection_tree->head; node; node = node->next) {
-		c = node->data;
-
-		if(c->status.remove)
-			continue;
-
-		if(FD_ISSET(c->socket, readset)) {
-			if(c->status.connecting) {
-				c->status.connecting = false;
-				getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
-
-				if(!result)
-					finish_connecting(c);
-				else {
-					ifdebug(CONNECTIONS) logger(LOG_DEBUG,
-							   _("Error while connecting to %s (%s): %s"),
-							   c->name, c->hostname, strerror(result));
-					closesocket(c->socket);
-					do_outgoing_connection(c);
-					continue;
-				}
-			}
-
-			if(!receive_meta(c)) {
-				terminate_connection(c, c->status.active);
-				continue;
+			if(!result)
+				finish_connecting(c);
+			else {
+				ifdebug(CONNECTIONS) logger(LOG_DEBUG,
+						   _("Error while connecting to %s (%s): %s"),
+						   c->name, c->hostname, strerror(result));
+				closesocket(c->socket);
+				do_outgoing_connection(c);
+				return;
 			}
 		}
 
-		if(FD_ISSET(c->socket, writeset)) {
-			if(!flush_meta(c)) {
-				terminate_connection(c, c->status.active);
-				continue;
-			}
+		if (!receive_meta(c)) {
+			terminate_connection(c, c->status.active);
+			return;
 		}
 	}
 
-	for(i = 0; i < listen_sockets; i++) {
-		if(FD_ISSET(listen_socket[i].udp, readset))
-			handle_incoming_vpn_data(listen_socket[i].udp);
-
-		if(FD_ISSET(listen_socket[i].tcp, readset))
-			handle_new_meta_connection(listen_socket[i].tcp);
+	if (events & EV_WRITE) {
+		if(!flush_meta(c)) {
+			terminate_connection(c, c->status.active);
+		}
 	}
 }
 
+static void dummy(int a, short b, void *c)
+{
+}
+
 /*
   this is where it all happens...
 */
 int main_loop(void)
 {
-	fd_set readset, writeset;
 	struct timeval tv;
-	int r, maxfd;
+	int r;
 	time_t last_ping_check, last_config_check, last_graph_dump;
 	tevent_t *event;
+	struct event timeout;
 
 	cp();
 
@@ -374,23 +336,30 @@ int main_loop(void)
 		tv.tv_sec = 1;
 		tv.tv_usec = 0;
 
-		maxfd = build_fdset(&readset, &writeset);
-
-		r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
+		/* XXX: libevent transition: old timeout code in this loop */
+		timeout_set(&timeout, dummy, NULL);
+		timeout_add(&timeout, &tv);
 
+		r = build_fdset();
 		if(r < 0) {
-			if(errno != EINTR && errno != EAGAIN) {
-				logger(LOG_ERR, _("Error while waiting for input: %s"),
-					   strerror(errno));
-				cp_trace();
-				dump_connections();
-				return 1;
-			}
-
-			continue;
+			logger(LOG_ERR, _("Error building fdset: %s"), strerror(errno));
+			cp_trace();
+			dump_connections();
+			return 1;
 		}
 
-		check_network_activity(&readset, &writeset);
+		r = event_loop(EVLOOP_ONCE);
+		now = time(NULL);
+		if(r < 0) {
+			logger(LOG_ERR, _("Error while waiting for input: %s"),
+				   strerror(errno));
+			cp_trace();
+			dump_connections();
+			return 1;
+		}
+
+		/* XXX: more libevent transition */
+		timeout_del(&timeout);
 
 		if(do_purge) {
 			purge();
diff --git a/src/net.h b/src/net.h
index d63c0522..d66a1c7a 100644
--- a/src/net.h
+++ b/src/net.h
@@ -24,6 +24,7 @@
 #define __TINC_NET_H__
 
 #include <openssl/evp.h>
+#include <event.h>
 
 #include "ipv6.h"
 
@@ -99,6 +100,8 @@ typedef struct packet_queue_t {
 } packet_queue_t;
 
 typedef struct listen_socket_t {
+	struct event ev_tcp;
+	struct event ev_udp;
 	int tcp;
 	int udp;
 	sockaddr_t sa;
@@ -133,10 +136,10 @@ extern EVP_CIPHER_CTX packet_ctx;
 #include "node.h"
 
 extern void retry_outgoing(outgoing_t *);
-extern void handle_incoming_vpn_data(int);
+extern void handle_incoming_vpn_data(int, short, void *);
 extern void finish_connecting(struct connection_t *);
 extern void do_outgoing_connection(struct connection_t *);
-extern bool handle_new_meta_connection(int);
+extern void handle_new_meta_connection(int, short, void *);
 extern int setup_listen_socket(const sockaddr_t *);
 extern int setup_vpn_in_socket(const sockaddr_t *);
 extern void send_packet(const struct node_t *, vpn_packet_t *);
@@ -151,6 +154,8 @@ extern void terminate_connection(struct connection_t *, bool);
 extern void flush_queue(struct node_t *);
 extern bool read_rsa_public_key(struct connection_t *);
 extern void send_mtu_probe(struct node_t *);
+extern void handle_device_data(int, short, void *);
+extern void handle_meta_connection_data(int, short, void *);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
diff --git a/src/net_packet.c b/src/net_packet.c
index 9e0ba2e8..bb81081d 100644
--- a/src/net_packet.c
+++ b/src/net_packet.c
@@ -484,7 +484,7 @@ void flush_queue(node_t *n)
 	}
 }
 
-void handle_incoming_vpn_data(int sock)
+void handle_incoming_vpn_data(int sock, short events, void *data)
 {
 	vpn_packet_t pkt;
 	char *hostname;
@@ -515,3 +515,11 @@ void handle_incoming_vpn_data(int sock)
 
 	receive_udppacket(n, &pkt);
 }
+
+void handle_device_data(int sock, short events, void *data)
+{
+	vpn_packet_t packet;
+
+	if(read_packet(&packet))
+		route(myself, &packet);
+}
diff --git a/src/net_setup.c b/src/net_setup.c
index 0c399bb6..65965908 100644
--- a/src/net_setup.c
+++ b/src/net_setup.c
@@ -45,6 +45,7 @@
 #include "xalloc.h"
 
 char *myport;
+static struct event device_ev;
 
 bool read_rsa_public_key(connection_t *c)
 {
@@ -447,6 +448,14 @@ bool setup_myself(void)
 	if(!setup_device())
 		return false;
 
+	event_set(&device_ev, device_fd, EV_READ|EV_PERSIST,
+			  handle_device_data, NULL);
+	if (event_add(&device_ev, NULL) < 0) {
+		logger(LOG_ERR, _("event_add failed: %s"), strerror(errno));
+		close_device();
+		return false;
+	}
+
 	/* Run tinc-up script to further initialize the tap interface */
 	asprintf(&envp[0], "NETNAME=%s", netname ? : "");
 	asprintf(&envp[1], "DEVICE=%s", device ? : "");
@@ -492,8 +501,33 @@ bool setup_myself(void)
 		listen_socket[listen_sockets].udp =
 			setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
 
-		if(listen_socket[listen_sockets].udp < 0)
+		if(listen_socket[listen_sockets].udp < 0) {
+			close(listen_socket[listen_sockets].tcp);
 			continue;
+		}
+
+		event_set(&listen_socket[listen_sockets].ev_tcp,
+				  listen_socket[listen_sockets].tcp,
+				  EV_READ|EV_PERSIST,
+				  handle_new_meta_connection, NULL);
+		if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) {
+			logger(LOG_WARNING, _("event_add failed: %s"), strerror(errno));
+			close(listen_socket[listen_sockets].tcp);
+			close(listen_socket[listen_sockets].udp);
+			continue;
+		}
+
+		event_set(&listen_socket[listen_sockets].ev_udp,
+				  listen_socket[listen_sockets].udp,
+				  EV_READ|EV_PERSIST,
+				  handle_incoming_vpn_data, NULL);
+		if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
+			logger(LOG_WARNING, _("event_add failed: %s"), strerror(errno));
+			close(listen_socket[listen_sockets].tcp);
+			close(listen_socket[listen_sockets].udp);
+			event_del(&listen_socket[listen_sockets].ev_tcp);
+			continue;
+		}
 
 		ifdebug(CONNECTIONS) {
 			hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
diff --git a/src/net_socket.c b/src/net_socket.c
index 1db1949c..c727d7b2 100644
--- a/src/net_socket.c
+++ b/src/net_socket.c
@@ -384,7 +384,7 @@ void setup_outgoing_connection(outgoing_t *outgoing)
   accept a new tcp connect and create a
   new connection
 */
-bool handle_new_meta_connection(int sock)
+void handle_new_meta_connection(int sock, short events, void *data)
 {
 	connection_t *c;
 	sockaddr_t sa;
@@ -398,7 +398,6 @@ bool handle_new_meta_connection(int sock)
 	if(fd < 0) {
 		logger(LOG_ERR, _("Accepting a new connection failed: %s"),
 			   strerror(errno));
-		return false;
 	}
 
 	sockaddrunmap(&sa);
@@ -423,8 +422,6 @@ bool handle_new_meta_connection(int sock)
 
 	c->allow_request = ID;
 	send_id(c);
-
-	return true;
 }
 
 void try_outgoing_connections(void)
diff --git a/src/tincd.c b/src/tincd.c
index ed967629..3a359087 100644
--- a/src/tincd.c
+++ b/src/tincd.c
@@ -462,6 +462,11 @@ int main(int argc, char **argv)
 	if(!read_server_config())
 		return 1;
 
+	if(event_init() < 0) {
+		logger(LOG_ERR, _("Error initializing libevent!"));
+		return 1;
+	}
+
 	if(lzo_init() != LZO_E_OK) {
 		logger(LOG_ERR, _("Error initializing LZO compressor!"));
 		return 1;