]> git.meshlink.io Git - meshlink/blob - src/net.c
Remove reload_configuration().
[meshlink] / src / net.c
1 /*
2     net.c -- most of the network code
3     Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
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 "utils.h"
23 #include "conf.h"
24 #include "connection.h"
25 #include "graph.h"
26 #include "logger.h"
27 #include "meshlink_internal.h"
28 #include "meta.h"
29 #include "net.h"
30 #include "netutl.h"
31 #include "protocol.h"
32 #include "xalloc.h"
33
34 /* Purge edges of unreachable nodes. Use carefully. */
35
36 // TODO: remove
37 void purge(void) {
38         logger(DEBUG_PROTOCOL, LOG_DEBUG, "Purging unreachable nodes");
39
40         /* Remove all edges owned by unreachable nodes. */
41
42         for splay_each(node_t, n, mesh->nodes) {
43                 if(!n->status.reachable) {
44                         logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Purging node %s (%s)", n->name, n->hostname);
45
46                         for splay_each(edge_t, e, n->edge_tree) {
47                                 send_del_edge(mesh->everyone, e);
48                                 edge_del(e);
49                         }
50                 }
51         }
52
53         /* Check if anyone else claims to have an edge to an unreachable node. If not, delete node. */
54
55         for splay_each(node_t, n, mesh->nodes) {
56                 if(!n->status.reachable) {
57                         for splay_each(edge_t, e, mesh->edges)
58                                 if(e->to == n)
59                                         return;
60                 }
61         }
62 }
63
64 /*
65   Terminate a connection:
66   - Mark it as inactive
67   - Remove the edge representing this connection
68   - Kill it with fire
69   - Check if we need to retry making an outgoing connection
70 */
71 void terminate_connection(connection_t *c, bool report) {
72         logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname);
73
74         c->status.active = false;
75
76         if(c->node && c->node->connection == c)
77                 c->node->connection = NULL;
78
79         if(c->edge) {
80                 if(report)
81                         send_del_edge(mesh->everyone, c->edge);
82
83                 edge_del(c->edge);
84                 c->edge = NULL;
85
86                 /* Run MST and SSSP algorithms */
87
88                 graph();
89
90                 /* If the node is not reachable anymore but we remember it had an edge to us, clean it up */
91
92                 if(report && !c->node->status.reachable) {
93                         edge_t *e;
94                         e = lookup_edge(c->node, mesh->self);
95                         if(e) {
96                                 send_del_edge(mesh->everyone, e);
97                                 edge_del(e);
98                         }
99                 }
100         }
101
102         outgoing_t *outgoing = c->outgoing;
103         connection_del(c);
104
105         /* Check if this was our outgoing connection */
106
107         if(outgoing)
108                 do_outgoing_connection(mesh, outgoing);
109
110 #ifndef HAVE_MINGW
111         /* Clean up dead proxy processes */
112
113         while(waitpid(-1, NULL, WNOHANG) > 0);
114 #endif
115 }
116
117 /*
118   Check if the other end is active.
119   If we have sent packets, but didn't receive any,
120   then possibly the other end is dead. We send a
121   PING request over the meta connection. If the other
122   end does not reply in time, we consider them dead
123   and close the connection.
124 */
125 static void timeout_handler(event_loop_t *loop, void *data) {
126         for list_each(connection_t, c, mesh->connections) {
127                 if(c->last_ping_time + mesh->pingtimeout <= mesh->loop.now.tv_sec) {
128                         if(c->status.active) {
129                                 if(c->status.pinged) {
130                                         logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)mesh->loop.now.tv_sec - c->last_ping_time);
131                                 } else if(c->last_ping_time + mesh->pinginterval <= mesh->loop.now.tv_sec) {
132                                         send_ping(c);
133                                         continue;
134                                 } else {
135                                         continue;
136                                 }
137                         } else {
138                                 if(c->status.connecting)
139                                         logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
140                                 else
141                                         logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
142                         }
143                         terminate_connection(c, c->status.active);
144                 }
145         }
146
147         timeout_set(&mesh->loop, data, &(struct timeval){mesh->pingtimeout, rand() % 100000});
148 }
149
150 static void periodic_handler(event_loop_t *loop, void *data) {
151         /* Check if there are too many contradicting ADD_EDGE and DEL_EDGE messages.
152            This usually only happens when another node has the same Name as this node.
153            If so, sleep for a short while to prevent a storm of contradicting messages.
154         */
155
156         if(mesh->contradicting_del_edge > 100 && mesh->contradicting_add_edge > 100) {
157                 logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime);
158                 usleep(mesh->sleeptime * 1000000LL);
159                 mesh->sleeptime *= 2;
160                 if(mesh->sleeptime < 0)
161                         mesh->sleeptime = 3600;
162         } else {
163                 mesh->sleeptime /= 2;
164                 if(mesh->sleeptime < 10)
165                         mesh->sleeptime = 10;
166         }
167
168         mesh->contradicting_add_edge = 0;
169         mesh->contradicting_del_edge = 0;
170
171         /* If AutoConnect is set, check if we need to make or break connections. */
172
173         if(autoconnect && mesh->nodes->count > 1) {
174                 /* Count number of active connections */
175                 int nc = 0;
176                 for list_each(connection_t, c, mesh->connections) {
177                         if(c->status.active)
178                                 nc++;
179                 }
180
181                 if(nc < autoconnect) {
182                         /* Not enough active connections, try to add one.
183                            Choose a random node, if we don't have a connection to it,
184                            and we are not already trying to make one, create an
185                            outgoing connection to this node.
186                         */
187                         int r = rand() % mesh->nodes->count;
188                         int i = 0;
189
190                         for splay_each(node_t, n, mesh->nodes) {
191                                 if(i++ != r)
192                                         continue;
193
194                                 if(n->connection)
195                                         break;
196
197                                 bool found = false;
198
199                                 for list_each(outgoing_t, outgoing, mesh->outgoings) {
200                                         if(!strcmp(outgoing->name, n->name)) {
201                                                 found = true;
202                                                 break;
203                                         }
204                                 }
205
206                                 if(!found) {
207                                         logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
208                                         outgoing_t *outgoing = xzalloc(sizeof *outgoing);
209                                         outgoing->name = xstrdup(n->name);
210                                         list_insert_tail(mesh->outgoings, outgoing);
211                                         setup_outgoing_connection(mesh, outgoing);
212                                 }
213                                 break;
214                         }
215                 } else if(nc > autoconnect) {
216                         /* Too many active connections, try to remove one.
217                            Choose a random outgoing connection to a node
218                            that has at least one other connection.
219                         */
220                         int r = rand() % nc;
221                         int i = 0;
222
223                         for list_each(connection_t, c, mesh->connections) {
224                                 if(!c->status.active)
225                                         continue;
226
227                                 if(i++ != r)
228                                         continue;
229
230                                 if(!c->outgoing || !c->node || c->node->edge_tree->count < 2)
231                                         break;
232
233                                 logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
234                                 list_delete(mesh->outgoings, c->outgoing);
235                                 c->outgoing = NULL;
236                                 terminate_connection(c, c->status.active);
237                                 break;
238                         }
239                 }
240
241                 if(nc >= autoconnect) {
242                         /* If we have enough active connections,
243                            remove any pending outgoing connections.
244                         */
245                         for list_each(outgoing_t, o, mesh->outgoings) {
246                                 bool found = false;
247                                 for list_each(connection_t, c, mesh->connections) {
248                                         if(c->outgoing == o) {
249                                                 found = true;
250                                                 break;
251                                         }
252                                 }
253                                 if(!found) {
254                                         logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->name);
255                                         list_delete_node(mesh->outgoings, node);
256                                 }
257                         }
258                 }
259         }
260
261         timeout_set(&mesh->loop, data, &(struct timeval){5, rand() % 100000});
262 }
263
264 void handle_meta_connection_data(connection_t *c) {
265         if (!receive_meta(c)) {
266                 terminate_connection(c, c->status.active);
267                 return;
268         }
269 }
270
271 void retry(void) {
272         /* Reset the reconnection timers for all outgoing connections */
273         for list_each(outgoing_t, outgoing, mesh->outgoings) {
274                 outgoing->timeout = 0;
275                 if(outgoing->ev.cb)
276                         timeout_set(&mesh->loop, &outgoing->ev, &(struct timeval){0, 0});
277         }
278
279         /* Check for outgoing connections that are in progress, and reset their ping timers */
280         for list_each(connection_t, c, mesh->connections) {
281                 if(c->outgoing && !c->node)
282                         c->last_ping_time = 0;
283         }
284
285         /* Kick the ping timeout handler */
286         timeout_set(&mesh->loop, &mesh->pingtimer, &(struct timeval){0, 0});
287 }
288
289 /*
290   this is where it all happens...
291 */
292 int main_loop(void) {
293         timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timeval){mesh->pingtimeout, rand() % 100000});
294         timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timeval){mesh->pingtimeout, rand() % 100000});
295
296         if(!event_loop_run(&mesh->loop)) {
297                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno));
298                 return 1;
299         }
300
301         timeout_del(&mesh->loop, &mesh->periodictimer);
302         timeout_del(&mesh->loop, &mesh->pingtimer);
303
304         return 0;
305 }