Handle UDP packets from different and ports than advertised.

Previously, tinc used a fixed address and port for each node for UDP packet
exchange.  The port was the one advertised by that node as its listening port.
However, due to NAT the port might be different.  Now, tinc sends a different
session key to each node. This way, the sending node can be determined from
incoming packets by checking the MAC against all session keys. If a match is
found, the address and port for that node are updated.
This commit is contained in:
Guus Sliepen 2009-04-03 01:05:23 +02:00
parent 43fa7283ac
commit 3308d13e7e
9 changed files with 242 additions and 149 deletions

View file

@ -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;