Use edge local addresses for local discovery.
This introduces a new way of doing local discovery: when tinc has local address information for the recipient node, it will send local discovery packets directly to the local address of that node, instead of using broadcast packets. This new way of doing local discovery provides numerous advantages compared to using broadcasts: - No broadcast packets "polluting" the local network; - Reliable even if the sending host has multiple network interfaces (in contrast, broadcasts will only be sent through one unpredictable interface) - Works even if the two hosts are not on the same broadcast domain. One example is a large LAN where the two hosts might be on different local subnets. In fact, thanks to UDP hole punching this might even work if there is a NAT sitting in the middle of the LAN between the two nodes! - Sometimes a node is reachable through its "normal" address, and via a local subnet as well. One might think the local subnet is the best route to the node in this case, but more often than not it's actually worse - one example is where the local segment is a third party VPN running in parallel, or ironically it can be the local segment formed by the tinc VPN itself! Because this new algorithm only checks the addresses for which an edge is already established, it is less likely to fall into these traps.
This commit is contained in:
parent
bfce56d473
commit
e16ade874d
3 changed files with 45 additions and 20 deletions
|
|
@ -144,14 +144,14 @@ static void send_mtu_probe_handler(void *data) {
|
|||
randomize(packet.data + 14, len - 14);
|
||||
packet.len = len;
|
||||
packet.priority = 0;
|
||||
n->status.broadcast = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
|
||||
n->status.send_locally = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
|
||||
|
||||
logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
|
||||
|
||||
send_udppacket(n, &packet);
|
||||
}
|
||||
|
||||
n->status.broadcast = false;
|
||||
n->status.send_locally = false;
|
||||
n->probe_counter = 0;
|
||||
gettimeofday(&n->probe_time, NULL);
|
||||
|
||||
|
|
@ -544,6 +544,18 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
|
|||
return;
|
||||
}
|
||||
|
||||
static void adapt_socket(const sockaddr_t *sa, int *sock) {
|
||||
/* Make sure we have a suitable socket for the chosen address */
|
||||
if(listen_socket[*sock].sa.sa.sa_family != sa->sa.sa_family) {
|
||||
for(int i = 0; i < listen_sockets; i++) {
|
||||
if(listen_socket[i].sa.sa.sa_family == sa->sa.sa_family) {
|
||||
*sock = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock) {
|
||||
/* Latest guess */
|
||||
*sa = &n->address;
|
||||
|
|
@ -582,18 +594,32 @@ static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock
|
|||
*sock = rand() % listen_sockets;
|
||||
}
|
||||
|
||||
/* Make sure we have a suitable socket for the chosen address */
|
||||
if(listen_socket[*sock].sa.sa.sa_family != (*sa)->sa.sa_family) {
|
||||
for(int i = 0; i < listen_sockets; i++) {
|
||||
if(listen_socket[i].sa.sa.sa_family == (*sa)->sa.sa_family) {
|
||||
*sock = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
adapt_socket(*sa, sock);
|
||||
}
|
||||
|
||||
static void choose_broadcast_address(const node_t *n, const sockaddr_t **sa, int *sock) {
|
||||
static void choose_local_address(const node_t *n, const sockaddr_t **sa, int *sock) {
|
||||
/* Pick one of the edges from this node at random, then use its local address. */
|
||||
|
||||
int i = 0;
|
||||
int j = rand() % n->edge_tree->count;
|
||||
edge_t *candidate = NULL;
|
||||
|
||||
for splay_each(edge_t, e, n->edge_tree) {
|
||||
if(i++ == j) {
|
||||
candidate = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate && candidate->local_address.sa.sa_family) {
|
||||
*sa = &candidate->local_address;
|
||||
*sock = rand() % listen_sockets;
|
||||
adapt_socket(*sa, sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* No candidate? Use broadcasts instead. */
|
||||
|
||||
static sockaddr_t broadcast_ipv4 = {
|
||||
.in = {
|
||||
.sin_family = AF_INET,
|
||||
|
|
@ -733,8 +759,8 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
|
|||
const sockaddr_t *sa;
|
||||
int sock;
|
||||
|
||||
if(n->status.broadcast)
|
||||
choose_broadcast_address(n, &sa, &sock);
|
||||
if(n->status.send_locally)
|
||||
choose_local_address(n, &sa, &sock);
|
||||
else
|
||||
choose_udp_address(n, &sa, &sock);
|
||||
|
||||
|
|
@ -785,8 +811,8 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
|
|||
const sockaddr_t *sa;
|
||||
int sock;
|
||||
|
||||
if(to->status.broadcast)
|
||||
choose_broadcast_address(to, &sa, &sock);
|
||||
if(to->status.send_locally)
|
||||
choose_local_address(to, &sa, &sock);
|
||||
else
|
||||
choose_udp_address(to, &sa, &sock);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue