]> git.meshlink.io Git - meshlink/blob - src/pmtu.c
0d5e8fd528ba59821ce43ed10bba555104924685
[meshlink] / src / pmtu.c
1 /*
2     pmtu.c -- PMTU probing
3     Copyright (C) 2020 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 "crypto.h"
23 #include "logger.h"
24 #include "net.h"
25 #include "netutl.h"
26 #include "node.h"
27 #include "pmtu.h"
28 #include "protocol.h"
29 #include "utils.h"
30
31 /* PMTU probing serves two purposes:
32  *
33  * - establishing a working UDP connection between two peers
34  * - determining the path MTU (PMTU) between two peers
35  *
36  * Establishing a working UDP connection requires NAT hole punching and regular
37  * packets to keep the NAT mappings alive.  For this, we can use very small UDP
38  * packets, and send them rather frequently (once every 10 seconds).  This also
39  * allows us to detect connection loss rather quickly.
40  *
41  * For PMTU discovery, we need to send packets of various size, and determine
42  * which ones are received by the other end.  Once the PMTU is established, we
43  * want to keep monitoring that the discovered PMTU value is still valid.
44  * However, we assume PMTU changes are unlikely, so they do not have to be done
45  * very often.
46  *
47  * To keep track of how far we are in the PMTU probing process, the variable
48  * mtuprobes is used. The meaning of its value is:
49  *
50  * - mtuprobes ==    -4: maxmtu no longer valid, reset minmtu and maxmtu and go to 0
51  * - mtuprobes ==-2..-3: send one maxmtu probe every second
52  * - mtuprobes ==    -1: send one maxmtu and one maxmtu + 1 probe every pinginterval
53  * - mtuprobes == 0..19: initial discovery, send three packets per second, mtuprobes++
54  * - mtuprobes ==    20: fix PMTU, and go to -1
55  *
56  * The first probe is always the maximum MTU supported by the interface,
57  * then a binary search is done until the minimum and maximum converge,
58  * or until 20 packets have been sent.
59  *
60  * After the initial discovery, PMTU probing only sends two packets; one with
61  * the same size as the discovered PMTU, and one which has a size slightly
62  * larger than the currently known PMTU, to test if the PMTU has increased.
63  */
64
65 static void try_fix_mtu(meshlink_handle_t *mesh, node_t *n) {
66         if(n->mtuprobes < 0) {
67                 return;
68         }
69
70         if(n->mtuprobes == 20 || n->minmtu >= n->maxmtu) {
71                 if(n->minmtu > n->maxmtu) {
72                         n->minmtu = n->maxmtu;
73                 } else {
74                         n->maxmtu = n->minmtu;
75                 }
76
77                 n->mtu = n->minmtu;
78                 logger(mesh, MESHLINK_INFO, "Fixing PMTU of %s to %d after %d probes", n->name, n->mtu, n->mtuprobes);
79                 n->mtuprobes = -1;
80         }
81 }
82
83 static void udp_probe_timeout_handler(event_loop_t *loop, void *data) {
84         node_t *n = data;
85         meshlink_handle_t *mesh = loop->data;
86
87         if(!n->status.udp_confirmed) {
88                 return;
89         }
90
91         logger(mesh, MESHLINK_INFO, "Too much time has elapsed since last UDP ping response from %s, stopping UDP communication", n->name);
92         n->status.udp_confirmed = false;
93         n->mtuprobes = 0;
94         n->udpprobes = 0;
95         n->minmtu = 0;
96         n->maxmtu = MTU;
97
98         // If we also have a meta-connection to this node, send a PING on it as well
99         connection_t *c = n->connection;
100
101         if(c && !c->status.pinged) {
102                 send_ping(mesh, c);
103         }
104 }
105
106 static void send_udp_probe_reply(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet, uint16_t len) {
107         if(!n->status.validkey) {
108                 logger(mesh, MESHLINK_INFO, "Trying to send UDP probe reply to %s but we don't have his key yet", n->name);
109                 return;
110         }
111
112         packet->data[0] = 1;
113
114         if(packet->data[1]) {
115                 packet->data[1] = 1;
116                 memcpy(packet->data + 2, &len, 2);
117                 len = MIN_PROBE_SIZE;
118         }
119
120         /* Temporarily set udp_confirmed, so that the reply is sent
121            back exactly the way it came in. */
122
123         bool udp_confirmed = n->status.udp_confirmed;
124         n->status.udp_confirmed = true;
125         logger(mesh, MESHLINK_DEBUG, "Sending UDP reply length %d to %s", packet->len, n->name);
126         n->out_meta += packet->len + SPTPS_DATAGRAM_OVERHEAD;
127         send_udppacket(mesh, n, packet);
128         n->status.udp_confirmed = udp_confirmed;
129 }
130
131 void udp_probe_h(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet, uint16_t len) {
132         if(len < MIN_PROBE_SIZE) {
133                 logger(mesh, MESHLINK_WARNING, "Got too short PMTU probe length %d from %s", packet->len, n->name);
134                 return;
135         }
136
137         n->in_meta += packet->len + SPTPS_DATAGRAM_OVERHEAD;
138
139         if(!packet->data[0]) {
140                 /* It's a probe request, send back a reply */
141                 logger(mesh, MESHLINK_DEBUG, "Got PMTU probe length %d from %s", packet->len, n->name);
142                 send_udp_probe_reply(mesh, n, packet, len);
143                 return;
144         }
145
146         if(packet->data[1]) {
147                 memcpy(&len, packet->data + 2, 2);
148         }
149
150         logger(mesh, MESHLINK_DEBUG, "Got PMTU reply length %d from %s", len, n->name);
151
152         /* It's a valid reply: now we know bidirectional communication
153            is possible using the address and socket that the reply
154            packet used. */
155         if(!n->status.udp_confirmed) {
156                 char *address, *port;
157                 sockaddr2str(&n->address, &address, &port);
158                 send_request(mesh, n->nexthop->connection, NULL, "%d %s %s . -1 -1 -1 0 %s %s", ANS_KEY, n->name, n->name, address, port);
159
160                 free(address);
161                 free(port);
162                 n->status.udp_confirmed = true;
163         }
164
165         n->udpprobes = 0;
166
167         // Reset the UDP ping timer.
168
169         timeout_del(&mesh->loop, &n->udp_ping_timeout);
170         timeout_add(&mesh->loop, &n->udp_ping_timeout, &udp_probe_timeout_handler, n, &(struct timespec) {
171                 30, 0
172         });
173
174         if(len > n->maxmtu) {
175                 logger(mesh, MESHLINK_INFO, "Increase in PMTU to %s detected, restarting PMTU discovery", n->name);
176                 n->minmtu = len;
177                 n->maxmtu = MTU;
178                 /* Set mtuprobes to 1 so that try_pmtu() doesn't reset maxmtu */
179                 n->mtuprobes = 1;
180                 return;
181         } else if(n->mtuprobes < 0 && len == n->maxmtu) {
182                 /* We got a maxmtu sized packet, confirming the PMTU is still valid. */
183                 n->mtuprobes = -1;
184                 n->last_mtu_probe_sent = mesh->loop.now;
185         }
186
187         /* If applicable, raise the minimum supported PMTU */
188
189         if(n->minmtu < len) {
190                 n->minmtu = len;
191                 update_node_pmtu(mesh, n);
192         }
193
194         try_fix_mtu(mesh, n);
195 }
196
197 static void send_udp_probe_packet(meshlink_handle_t *mesh, node_t *n, int len) {
198         if(len < MIN_PROBE_SIZE) {
199                 len = MIN_PROBE_SIZE;
200         }
201
202         vpn_packet_t packet;
203         memset(packet.data, 0, 4);
204         packet.probe = true;
205         packet.data[0] = 0;
206         packet.data[1] = 1;
207         packet.data[2] = 0;
208         packet.data[3] = 0;
209
210         if(len > 4) {
211                 randomize(packet.data + 4, len - 4);
212         }
213
214         packet.len = len;
215
216         logger(mesh, MESHLINK_DEBUG, "Sending UDP probe length %d to %s", len, n->name);
217
218         n->out_meta += packet.len + SPTPS_DATAGRAM_OVERHEAD;
219         send_udppacket(mesh, n, &packet);
220 }
221
222 static void try_udp(meshlink_handle_t *mesh, node_t *n) {
223         /* Probe request */
224
225         if(n->status.udp_confirmed && n->udpprobes < -3) {
226                 /* We lost three UDP probes, UDP status is no longer unconfirmed */
227                 udp_probe_timeout_handler(&mesh->loop, n);
228         }
229
230         struct timespec elapsed;
231
232         timespec_sub(&mesh->loop.now, &n->last_udp_probe_sent, &elapsed);
233
234         int interval;
235
236         if(n->status.udp_confirmed && n->udpprobes >= 0) {
237                 // UDP confirmed and no lost probes, default interval
238                 interval = 10;
239         } else if(n->udpprobes <= -15) {
240                 // Many probes lost in a row, slow probing interval
241                 n->udpprobes = -15;
242                 interval = 10;
243         } else {
244                 // Fast probing if we don't know the state of UDP connectivity yet
245                 interval = 2;
246         }
247
248         if(elapsed.tv_sec >= interval) {
249                 n->last_udp_probe_sent = mesh->loop.now;
250                 send_udp_probe_packet(mesh, n, MIN_PROBE_SIZE);
251                 n->udpprobes--;
252
253                 if(!n->status.udp_confirmed && n->prevedge) {
254                         n->status.broadcast = true;
255                         send_udp_probe_packet(mesh, n, MIN_PROBE_SIZE);
256                         n->status.broadcast = false;
257                 }
258         }
259 }
260
261 static uint16_t choose_initial_maxmtu(meshlink_handle_t *mesh, node_t *n) {
262 #ifdef IP_MTU
263
264         int sock = -1;
265
266         sockaddr_t sa_buf;
267         const sockaddr_t *sa;
268         int sockindex;
269         choose_udp_address(mesh, n, &sa, &sockindex, &sa_buf);
270
271         if(!sa) {
272                 return MTU;
273         }
274
275         sock = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
276
277         if(sock < 0) {
278                 logger(mesh, MESHLINK_ERROR, "Creating MTU assessment socket for %s failed: %s", n->name, sockstrerror(sockerrno));
279                 return MTU;
280         }
281
282         if(connect(sock, &sa->sa, SALEN(sa->sa))) {
283                 logger(mesh, MESHLINK_ERROR, "Connecting MTU assessment socket for %s failed: %s", n->name, sockstrerror(sockerrno));
284                 close(sock);
285                 return MTU;
286         }
287
288         int ip_mtu;
289         socklen_t ip_mtu_len = sizeof(ip_mtu);
290
291         if(getsockopt(sock, IPPROTO_IP, IP_MTU, &ip_mtu, &ip_mtu_len)) {
292                 logger(mesh, MESHLINK_ERROR, "getsockopt(IP_MTU) on %s failed: %s", n->name, sockstrerror(sockerrno));
293                 close(sock);
294                 return MTU;
295         }
296
297         close(sock);
298
299         /* Calculate the maximum SPTPS payload based on the interface MTU */
300         uint16_t mtu = ip_mtu;
301         mtu -= (sa->sa.sa_family == AF_INET6) ? 40 : 20; /* IPv6 or IPv4 */
302         mtu -= 8; /* UDP */
303         mtu -= 21; /* SPTPS */
304
305         if(mtu < 512) {
306                 logger(mesh, MESHLINK_ERROR, "getsockopt(IP_MTU) on %s returned absurdly small value: %d", n->name, ip_mtu);
307                 return MTU;
308         }
309
310         if(mtu > MTU) {
311                 return MTU;
312         }
313
314         logger(mesh, MESHLINK_INFO, "Using system-provided maximum MTU for %s: %hd", n->name, mtu);
315         return mtu;
316
317 #else
318         (void)mesh;
319         (void)n;
320         return MTU;
321 #endif
322 }
323
324 /* This function tries to determines the PMTU of a node.
325    By calling this function repeatedly, n->minmtu will be progressively
326    increased, and at some point, n->mtu will be fixed to n->minmtu.  If the PMTU
327    is already fixed, this function checks if it can be increased.
328 */
329
330 static void try_pmtu(meshlink_handle_t *mesh, node_t *n) {
331         if(!n->status.udp_confirmed) {
332                 n->mtuprobes = 0;
333                 n->minmtu = 0;
334                 n->maxmtu = MTU;
335                 return;
336         }
337
338         struct timespec elapsed;
339
340         timespec_sub(&mesh->loop.now, &n->last_mtu_probe_sent, &elapsed);
341
342         if(n->mtuprobes >= 0) {
343                 /* Fast probing, send three packets per second */
344                 if(n->mtuprobes != 0 && elapsed.tv_sec == 0 && elapsed.tv_nsec < 333333333) {
345                         return;
346                 }
347         } else {
348                 if(n->mtuprobes < -1) {
349                         /* We didn't get an answer to the last probe, try again once every second */
350                         if(elapsed.tv_sec < 1) {
351                                 return;
352                         }
353                 } else {
354                         /* Slow probing, send one packet every pinginterval */
355                         int pinginterval = mesh->dev_class_traits[n->devclass].pinginterval;
356
357                         if(elapsed.tv_sec < pinginterval) {
358                                 return;
359                         }
360                 }
361         }
362
363         n->last_mtu_probe_sent = mesh->loop.now;
364
365         if(n->mtuprobes < -3) {
366                 /* We lost three PMTU probes, restart discovery */
367                 logger(mesh, MESHLINK_INFO, "Decrease in PMTU to %s detected, restarting PMTU discovery", n->name);
368                 n->mtuprobes = 0;
369                 n->minmtu = 0;
370         }
371
372         if(n->mtuprobes < 0) {
373                 /* After the initial discovery, we only send one maxmtu and one
374                    maxmtu + 1 probe to detect PMTU increases. */
375                 send_udp_probe_packet(mesh, n, n->maxmtu);
376
377                 if(n->mtuprobes == -1 && n->maxmtu + 1 < MTU) {
378                         send_udp_probe_packet(mesh, n, n->maxmtu + 1);
379                 }
380
381                 n->mtuprobes--;
382         } else {
383                 /* Starting parameters. */
384                 uint16_t len;
385
386                 if(n->mtuprobes == 0) {
387                         /* First packet is always the maximum MTU size */
388                         n->maxmtu = choose_initial_maxmtu(mesh, n);
389                         len = n->maxmtu;
390                 } else {
391                         if(n->last_mtu_len == n->minmtu) {
392                                 /* The previous probe was succesful, increase the size */
393                                 len = n->minmtu + (n->maxmtu - n->minmtu + 1) / 2;
394                         } else {
395                                 /* The previous probe was unsuccesful, decrease the size */
396                                 len = n->minmtu + (n->last_mtu_len - n->minmtu) / 2;
397                         }
398                 }
399
400                 n->last_mtu_len = len;
401                 send_udp_probe_packet(mesh, n, len);
402                 n->mtuprobes++;
403         }
404
405         try_fix_mtu(mesh, n);
406 }
407
408 /* Keep the connection to the given node alive.
409  * Ensure we have a valid key, and check whether UDP is working.
410  */
411
412 void keepalive(meshlink_handle_t *mesh, node_t *n, bool traffic) {
413         if(!n->status.reachable || !n->status.validkey) {
414                 return;
415         }
416
417         try_udp(mesh, n);
418
419         if(traffic) {
420                 try_pmtu(mesh, n);
421         }
422
423         /* If we want to send traffic but we don't have a working UDP
424          * connection, we are going to forward the traffic to the nexthop, so
425          * try to keep that one alive as well. */
426
427         if(traffic && !n->status.udp_confirmed && n != n->nexthop) {
428                 keepalive(mesh, n->nexthop, traffic);
429         }
430 }
431