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.
and they only ConnectTo a third node outside the NAT,
which normally would prevent the peers from learning each other's LAN address.
.Pp
and they only ConnectTo a third node outside the NAT,
which normally would prevent the peers from learning each other's LAN address.
.Pp
-Currently, local discovery is implemented by sending broadcast packets to the LAN during path MTU discovery.
-This feature may not work in all possible situations.
+Currently, local discovery is implemented by sending some packets to the local address of the node during path MTU discovery. With older nodes that don't transmit their local address, it sends local broadcast packets instead.
.It Va LocalDiscoveryAddress Li = Ar address
.It Va LocalDiscoveryAddress Li = Ar address
-If this variable is specified, local discovery packets are sent to the given
+If this variable is specified, broadcast packets used in local discovery are sent to the given
.Ar address .
.It Va MACExpire Li = Ar seconds Pq 600
This option controls the amount of time MAC addresses are kept before they are removed.
.Ar address .
.It Va MACExpire Li = Ar seconds Pq 600
This option controls the amount of time MAC addresses are kept before they are removed.
randomize(packet.data + 14, len - 14);
packet.len = len;
packet.priority = 0;
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);
}
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);
n->probe_counter = 0;
gettimeofday(&n->probe_time, NULL);
+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;
static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock) {
/* Latest guess */
*sa = &n->address;
*sock = rand() % listen_sockets;
}
*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_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;
-static void choose_broadcast_address(const node_t *n, const sockaddr_t **sa, int *sock) {
+ 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,
static sockaddr_t broadcast_ipv4 = {
.in = {
.sin_family = AF_INET,
const sockaddr_t *sa;
int sock;
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);
else
choose_udp_address(n, &sa, &sock);
const sockaddr_t *sa;
int sock;
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);
else
choose_udp_address(to, &sa, &sock);
unsigned int indirect:1; /* 1 if this node is not directly reachable by us */
unsigned int sptps:1; /* 1 if this node supports SPTPS */
unsigned int udp_confirmed:1; /* 1 if the address is one that we received UDP traffic on */
unsigned int indirect:1; /* 1 if this node is not directly reachable by us */
unsigned int sptps:1; /* 1 if this node supports SPTPS */
unsigned int udp_confirmed:1; /* 1 if the address is one that we received UDP traffic on */
- unsigned int broadcast:1; /* 1 if the next UDP packet should be broadcast to the local network */
+ unsigned int send_locally:1; /* 1 if the next UDP packet should be sent on the local network */
unsigned int unused:23;
} node_status_t;
unsigned int unused:23;
} node_status_t;