diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 7e066bb2..d3c981d8 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -341,10 +341,9 @@ This will allow direct communication using LAN addresses, even if both peers are 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 -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. diff --git a/src/net_packet.c b/src/net_packet.c index f69bf98f..6d417471 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -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); diff --git a/src/node.h b/src/node.h index 1c9f230a..605477e5 100644 --- a/src/node.h +++ b/src/node.h @@ -37,7 +37,7 @@ typedef struct node_status_t { 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;