3 Copyright (C) 2020 Guus Sliepen <guus@tinc-vpn.org>
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.
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.
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.
31 /* PMTU probing serves two purposes:
33 * - establishing a working UDP connection between two peers
34 * - determining the path MTU (PMTU) between two peers
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.
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
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:
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
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.
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.
65 static void try_fix_mtu(meshlink_handle_t *mesh, node_t *n) {
66 if(n->mtuprobes < 0) {
70 if(n->mtuprobes == 20 || n->minmtu >= n->maxmtu) {
71 if(n->minmtu > n->maxmtu) {
72 n->minmtu = n->maxmtu;
74 n->maxmtu = n->minmtu;
78 logger(mesh, MESHLINK_INFO, "Fixing PMTU of %s to %d after %d probes", n->name, n->mtu, n->mtuprobes);
83 static void udp_probe_timeout_handler(event_loop_t *loop, void *data) {
85 meshlink_handle_t *mesh = loop->data;
87 if(!n->status.udp_confirmed) {
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;
98 // If we also have a meta-connection to this node, send a PING on it as well
99 connection_t *c = n->connection;
101 if(c && !c->status.pinged) {
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);
114 if(packet->data[1]) {
116 memcpy(packet->data + 2, &len, 2);
117 len = MIN_PROBE_SIZE;
120 /* Temporarily set udp_confirmed, so that the reply is sent
121 back exactly the way it came in. */
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;
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);
137 n->in_meta += packet->len + SPTPS_DATAGRAM_OVERHEAD;
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);
146 if(packet->data[1]) {
147 memcpy(&len, packet->data + 2, 2);
150 logger(mesh, MESHLINK_DEBUG, "Got PMTU reply length %d from %s", len, n->name);
152 /* It's a valid reply: now we know bidirectional communication
153 is possible using the address and socket that the reply
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);
162 n->status.udp_confirmed = true;
167 // Reset the UDP ping timer.
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) {
174 if(len > n->maxmtu) {
175 logger(mesh, MESHLINK_INFO, "Increase in PMTU to %s detected, restarting PMTU discovery", n->name);
178 /* Set mtuprobes to 1 so that try_pmtu() doesn't reset maxmtu */
181 } else if(n->mtuprobes < 0 && len == n->maxmtu) {
182 /* We got a maxmtu sized packet, confirming the PMTU is still valid. */
184 n->last_mtu_probe_sent = mesh->loop.now;
187 /* If applicable, raise the minimum supported PMTU */
189 if(n->minmtu < len) {
191 update_node_pmtu(mesh, n);
194 try_fix_mtu(mesh, n);
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;
203 memset(packet.data, 0, 4);
211 randomize(packet.data + 4, len - 4);
216 logger(mesh, MESHLINK_DEBUG, "Sending UDP probe length %d to %s", len, n->name);
218 n->out_meta += packet.len + SPTPS_DATAGRAM_OVERHEAD;
219 send_udppacket(mesh, n, &packet);
222 static void try_udp(meshlink_handle_t *mesh, node_t *n) {
225 if(n->udpprobes < -3) {
226 /* We lost three UDP probes, UDP status is no longer unconfirmed */
227 udp_probe_timeout_handler(&mesh->loop, n);
230 struct timespec elapsed;
232 timespec_sub(&mesh->loop.now, &n->last_udp_probe_sent, &elapsed);
234 int interval = (n->status.udp_confirmed && n->udpprobes >= 0) ? 10 : 2;
236 if(elapsed.tv_sec >= interval) {
237 n->last_udp_probe_sent = mesh->loop.now;
238 send_udp_probe_packet(mesh, n, MIN_PROBE_SIZE);
240 if(n->status.udp_confirmed) {
244 if(!n->status.udp_confirmed && n->prevedge) {
245 n->status.broadcast = true;
246 send_udp_probe_packet(mesh, n, MIN_PROBE_SIZE);
247 n->status.broadcast = false;
252 static uint16_t choose_initial_maxmtu(meshlink_handle_t *mesh, node_t *n) {
258 const sockaddr_t *sa;
260 choose_udp_address(mesh, n, &sa, &sockindex, &sa_buf);
266 sock = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
269 logger(mesh, MESHLINK_ERROR, "Creating MTU assessment socket for %s failed: %s", n->name, sockstrerror(sockerrno));
273 if(connect(sock, &sa->sa, SALEN(sa->sa))) {
274 logger(mesh, MESHLINK_ERROR, "Connecting MTU assessment socket for %s failed: %s", n->name, sockstrerror(sockerrno));
280 socklen_t ip_mtu_len = sizeof(ip_mtu);
282 if(getsockopt(sock, IPPROTO_IP, IP_MTU, &ip_mtu, &ip_mtu_len)) {
283 logger(mesh, MESHLINK_ERROR, "getsockopt(IP_MTU) on %s failed: %s", n->name, sockstrerror(sockerrno));
290 /* Calculate the maximum SPTPS payload based on the interface MTU */
291 uint16_t mtu = ip_mtu;
292 mtu -= (sa->sa.sa_family == AF_INET6) ? 40 : 20; /* IPv6 or IPv4 */
294 mtu -= 21; /* SPTPS */
297 logger(mesh, MESHLINK_ERROR, "getsockopt(IP_MTU) on %s returned absurdly small value: %d", n->name, ip_mtu);
305 logger(mesh, MESHLINK_INFO, "Using system-provided maximum MTU for %s: %hd", n->name, mtu);
315 /* This function tries to determines the PMTU of a node.
316 By calling this function repeatedly, n->minmtu will be progressively
317 increased, and at some point, n->mtu will be fixed to n->minmtu. If the PMTU
318 is already fixed, this function checks if it can be increased.
321 static void try_pmtu(meshlink_handle_t *mesh, node_t *n) {
322 if(!n->status.udp_confirmed) {
329 struct timespec elapsed;
331 timespec_sub(&mesh->loop.now, &n->last_mtu_probe_sent, &elapsed);
333 if(n->mtuprobes >= 0) {
334 /* Fast probing, send three packets per second */
335 if(n->mtuprobes != 0 && elapsed.tv_sec == 0 && elapsed.tv_nsec < 333333333) {
339 if(n->mtuprobes < -1) {
340 /* We didn't get an answer to the last probe, try again once every second */
341 if(elapsed.tv_sec < 1) {
345 /* Slow probing, send one packet every pinginterval */
346 int pinginterval = mesh->dev_class_traits[n->devclass].pinginterval;
348 if(elapsed.tv_sec < pinginterval) {
354 n->last_mtu_probe_sent = mesh->loop.now;
356 if(n->mtuprobes < -3) {
357 /* We lost three PMTU probes, restart discovery */
358 logger(mesh, MESHLINK_INFO, "Decrease in PMTU to %s detected, restarting PMTU discovery", n->name);
363 if(n->mtuprobes < 0) {
364 /* After the initial discovery, we only send one maxmtu and one
365 maxmtu + 1 probe to detect PMTU increases. */
366 send_udp_probe_packet(mesh, n, n->maxmtu);
368 if(n->mtuprobes == -1 && n->maxmtu + 1 < MTU) {
369 send_udp_probe_packet(mesh, n, n->maxmtu + 1);
374 /* Starting parameters. */
377 if(n->mtuprobes == 0) {
378 /* First packet is always the maximum MTU size */
379 n->maxmtu = choose_initial_maxmtu(mesh, n);
382 if(n->last_mtu_len == n->minmtu) {
383 /* The previous probe was succesful, increase the size */
384 len = n->minmtu + (n->maxmtu - n->minmtu + 1) / 2;
386 /* The previous probe was unsuccesful, decrease the size */
387 len = n->minmtu + (n->last_mtu_len - n->minmtu) / 2;
391 n->last_mtu_len = len;
392 send_udp_probe_packet(mesh, n, len);
396 try_fix_mtu(mesh, n);
399 /* Keep the connection to the given node alive.
400 * Ensure we have a valid key, and check whether UDP is working.
403 void keepalive(meshlink_handle_t *mesh, node_t *n, bool traffic) {
404 if(!n->status.reachable || !n->status.validkey) {
414 /* If we want to send traffic but we don't have a working UDP
415 * connection, we are going to forward the traffic to the nexthop, so
416 * try to keep that one alive as well. */
418 if(traffic && !n->status.udp_confirmed && n != n->nexthop) {
419 keepalive(mesh, n->nexthop, traffic);