CI: add libgcrypt to sanitizer run
[tinc] / src / autoconnect.c
1 /*
2     autoconnect.c -- automatic connection establishment
3     Copyright (C) 2017-2022 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include "autoconnect.h"
23 #include "connection.h"
24 #include "crypto.h"
25 #include "logger.h"
26 #include "node.h"
27 #include "xalloc.h"
28
29 static void make_new_connection(void) {
30         /* Select a random node we haven't connected to yet. */
31         uint32_t count = 0;
32
33         for splay_each(node_t, n, &node_tree) {
34                 if(n == myself || n->connection || !(n->status.has_address || n->status.reachable)) {
35                         continue;
36                 }
37
38                 count++;
39         }
40
41         if(!count) {
42                 return;
43         }
44
45         uint32_t r = prng(count);
46
47         for splay_each(node_t, n, &node_tree) {
48                 if(n == myself || n->connection || !(n->status.has_address || n->status.reachable)) {
49                         continue;
50                 }
51
52                 if(r) {
53                         --r;
54                         continue;
55                 }
56
57                 bool found = false;
58
59                 for list_each(outgoing_t, outgoing, &outgoing_list) {
60                         if(outgoing->node == n) {
61                                 found = true;
62                                 break;
63                         }
64                 }
65
66                 if(!found) {
67                         logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
68                         outgoing_t *outgoing = xzalloc(sizeof(*outgoing));
69                         outgoing->node = n;
70                         list_insert_tail(&outgoing_list, outgoing);
71                         setup_outgoing_connection(outgoing, false);
72                 }
73
74                 break;
75         }
76 }
77
78 static void connect_to_unreachable(void) {
79         /* Select a random known node. The rationale is that if there are many
80          * reachable nodes, and only a few unreachable nodes, we don't want all
81          * reachable nodes to try to connect to the unreachable ones at the
82          * same time. This way, we back off automatically. Conversely, if there
83          * are only a few reachable nodes, and many unreachable ones, we're
84          * going to try harder to connect to them. */
85
86         uint32_t r = prng(node_tree.count);
87
88         for splay_each(node_t, n, &node_tree) {
89                 if(r--) {
90                         continue;
91                 }
92
93                 /* Is it unreachable and do we know an address for it? If not, return. */
94                 if(n == myself || n->connection || n->status.reachable || !n->status.has_address) {
95                         return;
96                 }
97
98                 /* Are we already trying to make an outgoing connection to it? If so, return. */
99                 for list_each(outgoing_t, outgoing, &outgoing_list) {
100                         if(outgoing->node == n) {
101                                 return;
102                         }
103                 }
104
105                 logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
106                 outgoing_t *outgoing = xzalloc(sizeof(*outgoing));
107                 outgoing->node = n;
108                 list_insert_tail(&outgoing_list, outgoing);
109                 setup_outgoing_connection(outgoing, false);
110
111                 return;
112         }
113 }
114
115 static void drop_superfluous_outgoing_connection(void) {
116         /* Choose a random outgoing connection to a node that has at least one other connection. */
117         uint32_t count = 0;
118
119         for list_each(connection_t, c, &connection_list) {
120                 if(!c->edge || !c->outgoing || !c->node || c->node->edge_tree.count < 2) {
121                         continue;
122                 }
123
124                 count++;
125         }
126
127         if(!count) {
128                 return;
129         }
130
131         uint32_t r = prng(count);
132
133         for list_each(connection_t, c, &connection_list) {
134                 if(!c->edge || !c->outgoing || !c->node || c->node->edge_tree.count < 2) {
135                         continue;
136                 }
137
138                 if(r--) {
139                         continue;
140                 }
141
142                 logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
143                 list_delete(&outgoing_list, c->outgoing);
144                 c->outgoing = NULL;
145                 terminate_connection(c, c->edge);
146                 break;
147         }
148 }
149
150 static void drop_superfluous_pending_connections(void) {
151         for list_each(outgoing_t, o, &outgoing_list) {
152                 /* Only look for connections that are waiting to be retried later. */
153                 bool found = false;
154
155                 for list_each(connection_t, c, &connection_list) {
156                         if(c->outgoing == o) {
157                                 found = true;
158                                 break;
159                         }
160                 }
161
162                 if(found) {
163                         continue;
164                 }
165
166                 logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->node->name);
167                 list_delete_node(&outgoing_list, node);
168         }
169 }
170
171 void do_autoconnect(void) {
172         /* Count number of active connections. */
173         uint32_t nc = 0;
174
175         for list_each(connection_t, c, &connection_list) {
176                 if(c->edge) {
177                         nc++;
178                 }
179         }
180
181         /* Less than 3 connections? Eagerly try to make a new one. */
182         if(nc < 3) {
183                 make_new_connection();
184                 return;
185         }
186
187         /* More than 3 connections? See if we can get rid of a superfluous one. */
188         if(nc > 3) {
189                 drop_superfluous_outgoing_connection();
190         }
191
192         /* Drop pending outgoing connections from the outgoing list. */
193         drop_superfluous_pending_connections();
194
195         /* Check if there are unreachable nodes that we should try to connect to. */
196         connect_to_unreachable();
197 }