]> git.meshlink.io Git - meshlink-tiny/blob - src/net.c
c05baf77be7d78ff6f80a9d3cd136276c0850264
[meshlink-tiny] / src / net.c
1 /*
2     net.c -- most of the network code
3     Copyright (C) 2014-2017 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 "devtools.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 "sptps.h"
33 #include "xalloc.h"
34
35 #include <assert.h>
36
37 #if !defined(min)
38 static inline int min(int a, int b) {
39         return a < b ? a : b;
40 }
41 #endif
42
43 static const int default_timeout = 5;
44 static const int default_interval = 60;
45
46 /*
47   Terminate a connection:
48   - Mark it as inactive
49   - Kill it with fire
50   - Check if we need to retry making an outgoing connection
51 */
52 void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) {
53         (void)report;
54
55         if(c->status.active) {
56                 logger(mesh, MESHLINK_INFO, "Closing connection with %s", c->name);
57         }
58
59         if(c->node && c->node->connection == c) {
60                 if(c->status.active && mesh->meta_status_cb) {
61                         mesh->meta_status_cb(mesh, (meshlink_node_t *)c->node, false);
62                 }
63
64                 c->node->connection = NULL;
65         }
66
67         c->status.active = false;
68
69         outgoing_t *outgoing = c->outgoing;
70         connection_del(mesh, c);
71
72         /* Check if this was our outgoing connection */
73
74         if(outgoing) {
75                 do_outgoing_connection(mesh, outgoing);
76         }
77 }
78
79 /*
80   Check if the other end is active.
81   If we have sent packets, but didn't receive any,
82   then possibly the other end is dead. We send a
83   PING request over the meta connection. If the other
84   end does not reply in time, we consider them dead
85   and close the connection.
86 */
87 static void timeout_handler(event_loop_t *loop, void *data) {
88         assert(data);
89
90         meshlink_handle_t *mesh = loop->data;
91         logger(mesh, MESHLINK_DEBUG, "timeout_handler()");
92
93         for(connection_t *c = mesh->connection; c; c = NULL) {
94                 int pingtimeout = c->node ? mesh->dev_class_traits[c->node->devclass].pingtimeout : default_timeout;
95                 int pinginterval = c->node ? mesh->dev_class_traits[c->node->devclass].pinginterval : default_interval;
96
97                 if(c->outgoing && !c->status.active && c->outgoing->timeout < 5) {
98                         pingtimeout = 1;
99                 }
100
101                 // Also make sure that if outstanding key requests for the UDP counterpart of a connection has timed out, we restart it.
102                 if(c->node) {
103                         if(c->node->status.waitingforkey && c->node->last_req_key + pingtimeout < mesh->loop.now.tv_sec) {
104                                 send_req_key(mesh, c->node);
105                         }
106                 }
107
108                 if(c->status.active && c->last_key_renewal + 3600 < mesh->loop.now.tv_sec) {
109                         devtool_sptps_renewal_probe((meshlink_node_t *)c->node);
110
111                         if(!sptps_force_kex(&c->sptps)) {
112                                 logger(mesh, MESHLINK_ERROR, "SPTPS key renewal for connection with %s failed", c->name);
113                                 terminate_connection(mesh, c, true);
114                                 continue;
115                         } else {
116                                 c->last_key_renewal = mesh->loop.now.tv_sec;
117                         }
118                 }
119
120                 if(c->last_ping_time + pingtimeout < mesh->loop.now.tv_sec) {
121                         if(c->status.active) {
122                                 if(c->status.pinged) {
123                                         logger(mesh, MESHLINK_INFO, "%s didn't respond to PING in %ld seconds", c->name, (long)mesh->loop.now.tv_sec - c->last_ping_time);
124                                 } else if(c->last_ping_time + pinginterval <= mesh->loop.now.tv_sec) {
125                                         send_ping(mesh, c);
126                                         continue;
127                                 } else {
128                                         continue;
129                                 }
130                         } else {
131                                 if(c->status.connecting) {
132                                         logger(mesh, MESHLINK_WARNING, "Timeout while connecting to %s", c->name);
133                                 } else {
134                                         logger(mesh, MESHLINK_WARNING, "Timeout from %s during authentication", c->name);
135                                 }
136                         }
137
138                         terminate_connection(mesh, c, c->status.active);
139                 }
140         }
141
142         timeout_set(&mesh->loop, data, &(struct timespec) {
143                 1, prng(mesh, TIMER_FUDGE)
144         });
145 }
146
147 static void periodic_handler(event_loop_t *loop, void *data) {
148         meshlink_handle_t *mesh = loop->data;
149
150         /* Check if there are too many contradicting ADD_EDGE and DEL_EDGE messages.
151            This usually only happens when another node has the same Name as this node.
152            If so, sleep for a short while to prevent a storm of contradicting messages.
153         */
154
155         if(mesh->contradicting_del_edge > 100 && mesh->contradicting_add_edge > 100) {
156                 logger(mesh, MESHLINK_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime);
157                 struct timespec ts = {mesh->sleeptime, 0};
158                 nanosleep(&ts, NULL);
159                 mesh->sleeptime *= 2;
160
161                 if(mesh->sleeptime < 0) {
162                         mesh->sleeptime = 3600;
163                 }
164         } else {
165                 mesh->sleeptime /= 2;
166
167                 if(mesh->sleeptime < 10) {
168                         mesh->sleeptime = 10;
169                 }
170         }
171
172         mesh->contradicting_add_edge = 0;
173         mesh->contradicting_del_edge = 0;
174
175         int timeout = default_timeout;
176
177         /* Check if we need to make or break connections. */
178
179         if(mesh->peer && !mesh->connection && !mesh->outgoing) {
180                 logger(mesh, MESHLINK_DEBUG, "Autoconnecting to %s", mesh->peer->name);
181                 mesh->outgoing = xzalloc(sizeof(outgoing_t));
182                 mesh->outgoing->node = mesh->peer;
183                 setup_outgoing_connection(mesh, mesh->outgoing);
184         }
185
186         for(node_t *n = mesh->peer; n; n = NULL) {
187                 if(n->status.dirty) {
188                         if(!node_write_config(mesh, n, false)) {
189                                 logger(mesh, MESHLINK_DEBUG, "Could not update %s", n->name);
190                         }
191                 }
192
193                 if(n->status.reachable && n->status.validkey && n->last_req_key + 3600 < mesh->loop.now.tv_sec) {
194                         logger(mesh, MESHLINK_DEBUG, "SPTPS key renewal for node %s", n->name);
195                         devtool_sptps_renewal_probe((meshlink_node_t *)n);
196
197                         if(!sptps_force_kex(&n->sptps)) {
198                                 logger(mesh, MESHLINK_ERROR, "SPTPS key renewal for node %s failed", n->name);
199                                 n->status.validkey = false;
200                                 sptps_stop(&n->sptps);
201                                 n->status.waitingforkey = false;
202                                 n->last_req_key = -3600;
203                         } else {
204                                 n->last_req_key = mesh->loop.now.tv_sec;
205                         }
206                 }
207         }
208
209         timeout_set(&mesh->loop, data, &(struct timespec) {
210                 timeout, prng(mesh, TIMER_FUDGE)
211         });
212 }
213
214 void handle_meta_connection_data(meshlink_handle_t *mesh, connection_t *c) {
215         if(!receive_meta(mesh, c)) {
216                 terminate_connection(mesh, c, c->status.active);
217                 return;
218         }
219 }
220
221 void retry(meshlink_handle_t *mesh) {
222         /* Reset the reconnection timers for all outgoing connections */
223         for(outgoing_t *outgoing = mesh->outgoing; outgoing; outgoing = NULL) {
224                 outgoing->timeout = 0;
225
226                 if(outgoing->ev.cb) {
227                         timeout_set(&mesh->loop, &outgoing->ev, &(struct timespec) {
228                                 0, 0
229                         });
230                 }
231         }
232
233         /* For active connections, check if their addresses are still valid.
234          * If yes, reset their ping timers, otherwise terminate them. */
235         for(connection_t *c = mesh->connection; c; c = NULL) {
236                 if(!c->status.active) {
237                         continue;
238                 }
239
240                 if(!c->status.pinged) {
241                         c->last_ping_time = -3600;
242                 }
243
244                 sockaddr_t sa;
245                 socklen_t salen = sizeof(sa);
246
247                 if(getsockname(c->socket, &sa.sa, &salen)) {
248                         continue;
249                 }
250
251                 switch(sa.sa.sa_family) {
252                 case AF_INET:
253                         sa.in.sin_port = 0;
254                         break;
255
256                 case AF_INET6:
257                         sa.in6.sin6_port = 0;
258                         break;
259
260                 default:
261                         continue;
262                 }
263
264                 int sock = socket(sa.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
265
266                 if(sock == -1) {
267                         continue;
268                 }
269
270                 if(bind(sock, &sa.sa, salen) && errno == EADDRNOTAVAIL) {
271                         logger(mesh, MESHLINK_DEBUG, "Local address for connection to %s no longer valid, terminating", c->name);
272                         terminate_connection(mesh, c, c->status.active);
273                 }
274
275                 closesocket(sock);
276         }
277
278         /* Kick the ping timeout handler */
279         if(mesh->pingtimer.cb) {
280                 timeout_set(&mesh->loop, &mesh->pingtimer, &(struct timespec) {
281                         0, 0
282                 });
283         }
284 }
285
286 /*
287   this is where it all happens...
288 */
289 void main_loop(meshlink_handle_t *mesh) {
290         timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timespec) {
291                 1, prng(mesh, TIMER_FUDGE)
292         });
293         timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timespec) {
294                 0, 0
295         });
296
297         //Add signal handler
298         mesh->datafromapp.signum = 0;
299         signal_add(&mesh->loop, &mesh->datafromapp, meshlink_send_from_queue, mesh, mesh->datafromapp.signum);
300
301         if(!event_loop_run(&mesh->loop, mesh)) {
302                 logger(mesh, MESHLINK_ERROR, "Error while waiting for input: %s", strerror(errno));
303                 call_error_cb(mesh, MESHLINK_ENETWORK);
304         }
305
306         signal_del(&mesh->loop, &mesh->datafromapp);
307         timeout_del(&mesh->loop, &mesh->periodictimer);
308         timeout_del(&mesh->loop, &mesh->pingtimer);
309 }