diff --git a/src/graph.c b/src/graph.c
index e0c48d42..87bb2204 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -226,27 +226,8 @@ void sssp_bfs(void)
 			e->to->via = indirect ? n->via : e->to;
 			e->to->options = e->options;
 
-			if(sockaddrcmp(&e->to->address, &e->address)) {
-				node = avl_unlink(node_udp_tree, e->to);
-				sockaddrfree(&e->to->address);
-				sockaddrcpy(&e->to->address, &e->address);
-
-				if(e->to->hostname)
-					free(e->to->hostname);
-
-				e->to->hostname = sockaddr2hostname(&e->to->address);
-
-				if(node)
-					avl_insert_node(node_udp_tree, node);
-
-				if(e->to->options & OPTION_PMTU_DISCOVERY) {
-					e->to->mtuprobes = 0;
-					e->to->minmtu = 0;
-					e->to->maxmtu = MTU;
-					if(e->to->status.validkey)
-						send_mtu_probe(e->to);
-				}
-			}
+			if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
+				update_node_udp(e->to, &e->address);
 
 			list_insert_tail(todo_list, e->to);
 		}
@@ -269,13 +250,13 @@ void sssp_bfs(void)
 			if(n->status.reachable) {
 				ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"),
 					   n->name, n->hostname);
-				avl_insert(node_udp_tree, n);
 			} else {
 				ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"),
 					   n->name, n->hostname);
-				avl_delete(node_udp_tree, n);
 			}
 
+			/* TODO: only clear status.validkey if node is unreachable? */
+
 			n->status.validkey = false;
 			n->status.waitingforkey = false;
 
diff --git a/src/net.c b/src/net.c
index 0cdc72cc..7f17252d 100644
--- a/src/net.c
+++ b/src/net.c
@@ -414,11 +414,19 @@ int main_loop(void)
 			/* Should we regenerate our key? */
 
 			if(keyexpires < now) {
-				ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key"));
+				avl_node_t *node;
+				node_t *n;
+
+				ifdebug(STATUS) logger(LOG_INFO, _("Expiring symmetric keys"));
+
+				for(node = node_tree->head; node; node = node->next) {
+					n = node->data;
+					if(n->inkey) {
+						free(n->inkey);
+						n->inkey = NULL;
+					}
+				}
 
-				RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength);
-				if(myself->cipher)
-					EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len);
 				send_key_changed(broadcast, myself);
 				keyexpires = now + keylifetime;
 			}
diff --git a/src/net_packet.c b/src/net_packet.c
index 544bbde7..1730023d 100644
--- a/src/net_packet.c
+++ b/src/net_packet.c
@@ -168,6 +168,18 @@ static void receive_packet(node_t *n, vpn_packet_t *packet)
 	route(n, packet);
 }
 
+static bool try_mac(const node_t *n, const vpn_packet_t *inpkt)
+{
+	unsigned char hmac[EVP_MAX_MD_SIZE];
+
+	if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
+		return false;
+
+	HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
+
+	return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
+}
+
 static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
 {
 	vpn_packet_t pkt1, pkt2;
@@ -180,9 +192,15 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
 
 	cp();
 
+	if(!n->inkey) {
+		ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got packet from %s (%s) but he hasn't got our key yet"),
+					n->name, n->hostname);
+		return;
+	}
+
 	/* Check packet length */
 
-	if(inpkt->len < sizeof(inpkt->seqno) + myself->maclength) {
+	if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) {
 		ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"),
 					n->name, n->hostname);
 		return;
@@ -190,12 +208,12 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
 
 	/* Check the message authentication code */
 
-	if(myself->digest && myself->maclength) {
-		inpkt->len -= myself->maclength;
-		HMAC(myself->digest, myself->key, myself->keylength,
+	if(n->indigest && n->inmaclength) {
+		inpkt->len -= n->inmaclength;
+		HMAC(n->indigest, n->inkey, n->inkeylength,
 			 (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL);
 
-		if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, myself->maclength)) {
+		if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) {
 			ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"),
 					   n->name, n->hostname);
 			return;
@@ -204,13 +222,13 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
 
 	/* Decrypt the packet */
 
-	if(myself->cipher) {
+	if(n->incipher) {
 		outpkt = pkt[nextpkt++];
 
-		if(!EVP_DecryptInit_ex(&packet_ctx, NULL, NULL, NULL, NULL)
-				|| !EVP_DecryptUpdate(&packet_ctx, (unsigned char *) &outpkt->seqno, &outlen,
+		if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL)
+				|| !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen,
 					(unsigned char *) &inpkt->seqno, inpkt->len)
-				|| !EVP_DecryptFinal_ex(&packet_ctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
+				|| !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
 			ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s): %s"),
 						n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
 			return;
@@ -253,10 +271,10 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
 
 	/* Decompress the packet */
 
-	if(myself->compression) {
+	if(n->incompression) {
 		outpkt = pkt[nextpkt++];
 
-		if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, myself->compression)) < 0) {
+		if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
 			ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while uncompressing packet from %s (%s)"),
 				  		 n->name, n->hostname);
 			return;
@@ -315,7 +333,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt)
 				   n->name, n->hostname);
 
 		if(!n->status.waitingforkey)
-			send_req_key(n->nexthop->connection, myself, n);
+			send_req_key(n);
 
 		n->status.waitingforkey = true;
 
@@ -330,6 +348,8 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt)
 				n->name, n->hostname);
 
 		send_tcppacket(n->nexthop->connection, origpkt);
+
+		return;
 	}
 
 	origlen = inpkt->len;
@@ -337,10 +357,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt)
 
 	/* Compress the packet */
 
-	if(n->compression) {
+	if(n->outcompression) {
 		outpkt = pkt[nextpkt++];
 
-		if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->compression)) < 0) {
+		if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
 			ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while compressing packet to %s (%s)"),
 				   n->name, n->hostname);
 			return;
@@ -356,13 +376,13 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt)
 
 	/* Encrypt the packet */
 
-	if(n->cipher) {
+	if(n->outcipher) {
 		outpkt = pkt[nextpkt++];
 
-		if(!EVP_EncryptInit_ex(&n->packet_ctx, NULL, NULL, NULL, NULL)
-				|| !EVP_EncryptUpdate(&n->packet_ctx, (unsigned char *) &outpkt->seqno, &outlen,
+		if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL)
+				|| !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen,
 					(unsigned char *) &inpkt->seqno, inpkt->len)
-				|| !EVP_EncryptFinal_ex(&n->packet_ctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
+				|| !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
 			ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s): %s"),
 						n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
 			goto end;
@@ -374,10 +394,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt)
 
 	/* Add the message authentication code */
 
-	if(n->digest && n->maclength) {
-		HMAC(n->digest, n->key, n->keylength, (unsigned char *) &inpkt->seqno,
+	if(n->outdigest && n->outmaclength) {
+		HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno,
 			 inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL);
-		inpkt->len += n->maclength;
+		inpkt->len += n->outmaclength;
 	}
 
 	/* Determine which socket we have to use */
@@ -476,6 +496,30 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet)
 	}
 }
 
+static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
+	avl_node_t *node;
+	edge_t *e;
+	node_t *n = NULL;
+
+	for(node = edge_weight_tree->head; node; node = node->next) {
+		e = node->data;
+
+		if(sockaddrcmp_noport(from, &e->address))
+			continue;
+
+		if(!n)
+			n = e->to;
+
+		if(!try_mac(e->to, pkt))
+			continue;
+
+		n = e->to;
+		break;
+	}
+
+	return n;
+}
+
 void handle_incoming_vpn_data(int sock)
 {
 	vpn_packet_t pkt;
@@ -498,11 +542,15 @@ void handle_incoming_vpn_data(int sock)
 	n = lookup_node_udp(&from);
 
 	if(!n) {
-		hostname = sockaddr2hostname(&from);
-		logger(LOG_WARNING, _("Received UDP packet from unknown source %s"),
-			   hostname);
-		free(hostname);
-		return;
+		n = try_harder(&from, &pkt);
+		if(n)
+			update_node_udp(n, &from);
+		else {
+			hostname = sockaddr2hostname(&from);
+			logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
+			free(hostname);
+			return;
+		}
 	}
 
 	receive_udppacket(n, &pkt);
diff --git a/src/net_setup.c b/src/net_setup.c
index 3eb56441..75267794 100644
--- a/src/net_setup.c
+++ b/src/net_setup.c
@@ -349,88 +349,72 @@ bool setup_myself(void)
 	if(get_config_string
 	   (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) {
 		if(!strcasecmp(cipher, "none")) {
-			myself->cipher = NULL;
+			myself->incipher = NULL;
 		} else {
-			myself->cipher = EVP_get_cipherbyname(cipher);
+			myself->incipher = EVP_get_cipherbyname(cipher);
 
-			if(!myself->cipher) {
+			if(!myself->incipher) {
 				logger(LOG_ERR, _("Unrecognized cipher type!"));
 				return false;
 			}
 		}
 	} else
-		myself->cipher = EVP_bf_cbc();
+		myself->incipher = EVP_bf_cbc();
 
-	if(myself->cipher)
-		myself->keylength = myself->cipher->key_len + myself->cipher->iv_len;
+	if(myself->incipher)
+		myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
 	else
-		myself->keylength = 1;
+		myself->inkeylength = 1;
 
 	myself->connection->outcipher = EVP_bf_ofb();
 
-	myself->key = xmalloc(myself->keylength);
-	RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength);
-
 	if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
 		keylifetime = 3600;
 
 	keyexpires = now + keylifetime;
 	
-	if(myself->cipher) {
-		EVP_CIPHER_CTX_init(&packet_ctx);
-		if(!EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len)) {
-			logger(LOG_ERR, _("Error during initialisation of cipher for %s (%s): %s"),
-					myself->name, myself->hostname, ERR_error_string(ERR_get_error(), NULL));
-			return false;
-		}
-
-	}
-
 	/* Check if we want to use message authentication codes... */
 
-	if(get_config_string
-	   (lookup_config(myself->connection->config_tree, "Digest"), &digest)) {
+	if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) {
 		if(!strcasecmp(digest, "none")) {
-			myself->digest = NULL;
+			myself->indigest = NULL;
 		} else {
-			myself->digest = EVP_get_digestbyname(digest);
+			myself->indigest = EVP_get_digestbyname(digest);
 
-			if(!myself->digest) {
+			if(!myself->indigest) {
 				logger(LOG_ERR, _("Unrecognized digest type!"));
 				return false;
 			}
 		}
 	} else
-		myself->digest = EVP_sha1();
+		myself->indigest = EVP_sha1();
 
 	myself->connection->outdigest = EVP_sha1();
 
-	if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"),
-		&myself->maclength)) {
-		if(myself->digest) {
-			if(myself->maclength > myself->digest->md_size) {
+	if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->inmaclength)) {
+		if(myself->indigest) {
+			if(myself->inmaclength > myself->indigest->md_size) {
 				logger(LOG_ERR, _("MAC length exceeds size of digest!"));
 				return false;
-			} else if(myself->maclength < 0) {
+			} else if(myself->inmaclength < 0) {
 				logger(LOG_ERR, _("Bogus MAC length!"));
 				return false;
 			}
 		}
 	} else
-		myself->maclength = 4;
+		myself->inmaclength = 4;
 
 	myself->connection->outmaclength = 0;
 
 	/* Compression */
 
-	if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"),
-		&myself->compression)) {
-		if(myself->compression < 0 || myself->compression > 11) {
+	if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->incompression)) {
+		if(myself->incompression < 0 || myself->incompression > 11) {
 			logger(LOG_ERR, _("Bogus compression level!"));
 			return false;
 		}
 	} else
-		myself->compression = 0;
+		myself->incompression = 0;
 
 	myself->connection->outcompression = 0;
 
diff --git a/src/netutl.c b/src/netutl.c
index 83e19ed8..20648605 100644
--- a/src/netutl.c
+++ b/src/netutl.c
@@ -144,6 +144,39 @@ char *sockaddr2hostname(const sockaddr_t *sa)
 	return str;
 }
 
+int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b)
+{
+	int result;
+
+	cp();
+
+	result = a->sa.sa_family - b->sa.sa_family;
+
+	if(result)
+		return result;
+
+	switch (a->sa.sa_family) {
+		case AF_UNSPEC:
+			return 0;
+
+		case AF_UNKNOWN:
+			return strcmp(a->unknown.address, b->unknown.address);
+
+		case AF_INET:
+			return memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
+
+		case AF_INET6:
+			return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
+
+		default:
+			logger(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"),
+				   a->sa.sa_family);
+			cp_trace();
+			raise(SIGFPE);
+			exit(0);
+	}
+}
+
 int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b)
 {
 	int result;
diff --git a/src/node.c b/src/node.c
index 4ee9ce72..9d359253 100644
--- a/src/node.c
+++ b/src/node.c
@@ -42,16 +42,7 @@ static int node_compare(const node_t *a, const node_t *b)
 
 static int node_udp_compare(const node_t *a, const node_t *b)
 {
-	int result;
-
-	cp();
-
-	result = sockaddrcmp(&a->address, &b->address);
-
-	if(result)
-		return result;
-
-	return (a->name && b->name) ? strcmp(a->name, b->name) : 0;
+       return sockaddrcmp(&a->address, &b->address);
 }
 
 void init_nodes(void)
@@ -78,7 +69,8 @@ node_t *new_node(void)
 
 	n->subnet_tree = new_subnet_tree();
 	n->edge_tree = new_edge_tree();
-	EVP_CIPHER_CTX_init(&n->packet_ctx);
+	EVP_CIPHER_CTX_init(&n->inctx);
+	EVP_CIPHER_CTX_init(&n->outctx);
 	n->mtu = MTU;
 	n->maxmtu = MTU;
 
@@ -89,8 +81,11 @@ void free_node(node_t *n)
 {
 	cp();
 
-	if(n->key)
-		free(n->key);
+	if(n->inkey)
+		free(n->inkey);
+
+	if(n->outkey)
+		free(n->outkey);
 
 	if(n->subnet_tree)
 		free_subnet_tree(n->subnet_tree);
@@ -100,7 +95,8 @@ void free_node(node_t *n)
 
 	sockaddrfree(&n->address);
 
-	EVP_CIPHER_CTX_cleanup(&n->packet_ctx);
+	EVP_CIPHER_CTX_cleanup(&n->inctx);
+	EVP_CIPHER_CTX_cleanup(&n->outctx);
 
 	if(n->mtuevent)
 		event_del(n->mtuevent);
@@ -142,6 +138,7 @@ void node_del(node_t *n)
 	}
 
 	avl_delete(node_tree, n);
+	avl_delete(node_udp_tree, n);
 }
 
 node_t *lookup_node(char *name)
@@ -167,6 +164,25 @@ node_t *lookup_node_udp(const sockaddr_t *sa)
 	return avl_search(node_udp_tree, &n);
 }
 
+void update_node_udp(node_t *n, const sockaddr_t *sa)
+{
+	avl_delete(node_udp_tree, n);
+
+	if(n->hostname)
+		free(n->hostname);
+
+	if(sa) {
+		n->address = *sa;
+		n->hostname = sockaddr2hostname(&n->address);
+		avl_delete(node_udp_tree, n);
+		avl_insert(node_udp_tree, n);
+		logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
+	} else {
+		memset(&n->address, 0, sizeof n->address);
+		logger(LOG_DEBUG, "UDP address of %s cleared", n->name);
+	}
+}
+
 void dump_nodes(void)
 {
 	avl_node_t *node;
@@ -179,8 +195,8 @@ void dump_nodes(void)
 	for(node = node_tree->head; node; node = node->next) {
 		n = node->data;
 		logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"),
-			   n->name, n->hostname, n->cipher ? n->cipher->nid : 0,
-			   n->digest ? n->digest->type : 0, n->maclength, n->compression,
+			   n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0,
+			   n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression,
 			   n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-",
 			   n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu);
 	}
diff --git a/src/node.h b/src/node.h
index 55a1b530..4321c008 100644
--- a/src/node.h
+++ b/src/node.h
@@ -51,15 +51,24 @@ typedef struct node_t {
 
 	node_status_t status;
 
-	const EVP_CIPHER *cipher;		/* Cipher type for UDP packets */
-	char *key;				/* Cipher key and iv */
-	int keylength;				/* Cipher key and iv length */
-	EVP_CIPHER_CTX packet_ctx;		/* Cipher context */
+	const EVP_CIPHER *incipher;		/* Cipher type for UDP packets received from him */
+	char *inkey;				/* Cipher key and iv */
+	int inkeylength;			/* Cipher key and iv length */
+	EVP_CIPHER_CTX inctx;			/* Cipher context */
 	
-	const EVP_MD *digest;			/* Digest type for MAC */
-	int maclength;				/* Length of MAC */
+	const EVP_CIPHER *outcipher;		/* Cipher type for UDP packets sent to him*/
+	char *outkey;				/* Cipher key and iv */
+	int outkeylength;			/* Cipher key and iv length */
+	EVP_CIPHER_CTX outctx;			/* Cipher context */
+	
+	const EVP_MD *indigest;			/* Digest type for MAC of packets received from him */
+	int inmaclength;			/* Length of MAC */
 
-	int compression;			/* Compressionlevel, 0 = no compression */
+	const EVP_MD *outdigest;		/* Digest type for MAC of packets sent to him*/
+	int outmaclength;			/* Length of MAC */
+
+	int incompression;			/* Compressionlevel, 0 = no compression */
+	int outcompression;			/* Compressionlevel, 0 = no compression */
 
 	struct node_t *nexthop;			/* nearest node from us to him */
 	struct node_t *via;			/* next hop for UDP packets */
@@ -93,6 +102,7 @@ extern void node_add(node_t *);
 extern void node_del(node_t *);
 extern node_t *lookup_node(char *);
 extern node_t *lookup_node_udp(const sockaddr_t *);
+extern void update_node_udp(node_t *, const sockaddr_t *);
 extern void dump_nodes(void);
 
 #endif							/* __TINC_NODE_H__ */
diff --git a/src/protocol.h b/src/protocol.h
index 0664d4cf..ac86198e 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -97,9 +97,9 @@ extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
 extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
 extern bool send_add_edge(struct connection_t *, const struct edge_t *);
 extern bool send_del_edge(struct connection_t *, const struct edge_t *);
-extern bool send_key_changed(struct connection_t *, const struct node_t *);
-extern bool send_req_key(struct connection_t *, const struct node_t *, const struct node_t *);
-extern bool send_ans_key(struct connection_t *, const struct node_t *, const struct node_t *);
+extern bool send_key_changed();
+extern bool send_req_key(struct node_t *);
+extern bool send_ans_key(struct node_t *);
 extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *);
 
 /* Request handlers  */
diff --git a/src/protocol_key.c b/src/protocol_key.c
index 06c6d336..5baa5f40 100644
--- a/src/protocol_key.c
+++ b/src/protocol_key.c
@@ -24,6 +24,7 @@
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
+#include <openssl/rand.h>
 
 #include "avl_tree.h"
 #include "connection.h"
@@ -37,7 +38,7 @@
 
 bool mykeyused = false;
 
-bool send_key_changed(connection_t *c, const node_t *n)
+bool send_key_changed()
 {
 	cp();
 
@@ -45,10 +46,10 @@ bool send_key_changed(connection_t *c, const node_t *n)
 	   This reduces unnecessary key_changed broadcasts.
 	 */
 
-	if(n == myself && !mykeyused)
+	if(!mykeyused)
 		return true;
 
-	return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
+	return send_request(broadcast, "%d %lx %s", KEY_CHANGED, random(), myself->name);
 }
 
 bool key_changed_h(connection_t *c)
@@ -86,11 +87,11 @@ bool key_changed_h(connection_t *c)
 	return true;
 }
 
-bool send_req_key(connection_t *c, const node_t *from, const node_t *to)
+bool send_req_key(node_t *to)
 {
 	cp();
 
-	return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
+	return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
 }
 
 bool req_key_h(connection_t *c)
@@ -129,7 +130,7 @@ bool req_key_h(connection_t *c)
 		mykeyused = true;
 		from->received_seqno = 0;
 		memset(from->late, 0, sizeof(from->late));
-		send_ans_key(c, myself, from);
+		send_ans_key(from);
 	} else {
 		if(tunnelserver)
 			return false;
@@ -140,27 +141,39 @@ bool req_key_h(connection_t *c)
 			return true;
 		}
 
-		send_req_key(to->nexthop->connection, from, to);
+		send_request(to->nexthop->connection, "%s", c->buffer);
 	}
 
 	return true;
 }
 
-bool send_ans_key(connection_t *c, const node_t *from, const node_t *to)
+bool send_ans_key(node_t *to)
 {
 	char *key;
 
 	cp();
 
-	key = alloca(2 * from->keylength + 1);
-	bin2hex(from->key, key, from->keylength);
-	key[from->keylength * 2] = '\0';
+	if(!to->inkey) {
+		to->incipher = myself->incipher;
+		to->inkeylength = myself->inkeylength;
+		to->indigest = myself->indigest;
+		to->incompression = myself->incompression;
+		to->inkey = xmalloc(to->inkeylength);
 
-	return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
-						from->name, to->name, key,
-						from->cipher ? from->cipher->nid : 0,
-						from->digest ? from->digest->type : 0, from->maclength,
-						from->compression);
+		RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
+		if(to->incipher)
+			EVP_DecryptInit_ex(&packet_ctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
+	}
+
+	key = alloca(2 * to->inkeylength + 1);
+	bin2hex(to->inkey, key, to->inkeylength);
+	key[to->outkeylength * 2] = '\0';
+
+	return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
+			myself->name, to->name, key,
+			to->incipher ? to->incipher->nid : 0,
+			to->indigest ? to->indigest->type : 0, to->inmaclength,
+			to->incompression);
 }
 
 bool ans_key_h(connection_t *c)
@@ -214,13 +227,13 @@ bool ans_key_h(connection_t *c)
 
 	/* Update our copy of the origin's packet key */
 
-	if(from->key)
-		free(from->key);
+	if(from->outkey)
+		free(from->outkey);
 
-	from->key = xstrdup(key);
-	from->keylength = strlen(key) / 2;
-	hex2bin(from->key, from->key, from->keylength);
-	from->key[from->keylength] = '\0';
+	from->outkey = xstrdup(key);
+	from->outkeylength = strlen(key) / 2;
+	hex2bin(from->outkey, from->outkey, from->outkeylength);
+	from->outkey[from->outkeylength] = '\0';
 
 	from->status.validkey = true;
 	from->status.waitingforkey = false;
@@ -229,41 +242,41 @@ bool ans_key_h(connection_t *c)
 	/* Check and lookup cipher and digest algorithms */
 
 	if(cipher) {
-		from->cipher = EVP_get_cipherbynid(cipher);
+		from->outcipher = EVP_get_cipherbynid(cipher);
 
-		if(!from->cipher) {
+		if(!from->outcipher) {
 			logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
 				   from->hostname);
 			return false;
 		}
 
-		if(from->keylength != from->cipher->key_len + from->cipher->iv_len) {
+		if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) {
 			logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
 				   from->hostname);
 			return false;
 		}
 	} else {
-		from->cipher = NULL;
+		from->outcipher = NULL;
 	}
 
-	from->maclength = maclength;
+	from->outmaclength = maclength;
 
 	if(digest) {
-		from->digest = EVP_get_digestbynid(digest);
+		from->outdigest = EVP_get_digestbynid(digest);
 
-		if(!from->digest) {
+		if(!from->outdigest) {
 			logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
 				   from->hostname);
 			return false;
 		}
 
-		if(from->maclength > from->digest->md_size || from->maclength < 0) {
+		if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) {
 			logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
 				   from->name, from->hostname);
 			return false;
 		}
 	} else {
-		from->digest = NULL;
+		from->outdigest = NULL;
 	}
 
 	if(compression < 0 || compression > 11) {
@@ -271,10 +284,10 @@ bool ans_key_h(connection_t *c)
 		return false;
 	}
 	
-	from->compression = compression;
+	from->outcompression = compression;
 
-	if(from->cipher)
-		if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) {
+	if(from->outcipher)
+		if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) {
 			logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
 					from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
 			return false;