Improve packet source detection.

When no UDP communication has been done yet, tinc establishes a guess
for the UDP address+port of each node. However, when there are multiple nodes
behind a NAT, tinc will guess the exact same address+port combination
for them, because it doesn't know about the NAT mappings yet. So when
receiving a packet, don't trust that guess unless we have confirmed UDP
communication.

This ensures try_harder() is called in such cases. However, this
function was actually very inefficient, trying to verify packets
multiple times for nodes with multiple edges. Only call try_mac() at
most once per node.
This commit is contained in:
Guus Sliepen 2015-01-12 14:43:32 +01:00
parent ae5b56c03d
commit a95e182d9c

View file

@ -257,7 +257,7 @@ static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
#ifdef DISABLE_LEGACY
return false;
#else
if(!digest_active(n->indigest) || inpkt->len < sizeof(seqno_t) + digest_length(n->indigest))
if(!n->status.validkey_in || !digest_active(n->indigest) || inpkt->len < sizeof(seqno_t) + digest_length(n->indigest))
return false;
return digest_verify(n->indigest, SEQNO(inpkt), inpkt->len - digest_length(n->indigest), DATA(inpkt) + inpkt->len - digest_length(n->indigest));
@ -1263,33 +1263,52 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
}
}
/* We got a packet from some IP address, but we don't know who sent it. Try to
verify the message authentication code against all active session keys.
Since this is actually an expensive operation, we only do a full check once
a minute, the rest of the time we only check against nodes for which we know
an IP address that matches the one from the packet. */
static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
node_t *n = NULL;
node_t *match = NULL;
bool hard = false;
static time_t last_hard_try = 0;
for splay_each(edge_t, e, edge_weight_tree) {
if(!e->to->status.reachable || e->to == myself)
for splay_each(node_t, n, node_tree) {
if(!n->status.reachable || n == myself)
continue;
if(sockaddrcmp_noport(from, &e->address)) {
if((n->status.sptps && !n->sptps.instate) || !n->status.validkey_in)
continue;
bool soft = false;
for splay_each(edge_t, e, n->edge_tree) {
if(!e->reverse)
continue;
if(!sockaddrcmp_noport(from, &e->reverse->address)) {
soft = true;
break;
}
}
if(!soft) {
if(last_hard_try == now.tv_sec)
continue;
hard = true;
}
if(!try_mac(e->to, pkt))
if(!try_mac(n, pkt))
continue;
n = e->to;
match = n;
break;
}
if(hard)
last_hard_try = now.tv_sec;
last_hard_try = now.tv_sec;
return n;
return match;
}
void handle_incoming_vpn_data(void *data, int flags) {
@ -1319,6 +1338,9 @@ void handle_incoming_vpn_data(void *data, int flags) {
node_t *n = lookup_node_udp(&addr);
if(n && !n->status.udp_confirmed)
n = NULL; // Don't believe it if we don't have confirmation yet.
if(!n) {
// It might be from a 1.1 node, which might have a source ID in the packet.
pkt.offset = 2 * sizeof(node_id_t);