0263d0016843eea310a90bf7a41e9183d68b0049
[tinc] / protocol_misc.c
1 /*
2     protocol_misc.c -- handle the meta-protocol, miscellaneous functions
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2022 Guus Sliepen <guus@tinc-vpn.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "system.h"
22
23 #include "address_cache.h"
24 #include "connection.h"
25 #include "crypto.h"
26 #include "logger.h"
27 #include "meta.h"
28 #include "net.h"
29 #include "netutl.h"
30 #include "protocol.h"
31 #include "utils.h"
32
33 int maxoutbufsize = 0;
34 int mtu_info_interval = 5;
35 int udp_info_interval = 5;
36
37 bool send_termreq(connection_t *c) {
38         return send_request(c, "%d", TERMREQ);
39 }
40
41 bool termreq_h(connection_t *c, const char *request) {
42         (void)c;
43         (void)request;
44         return false;
45 }
46
47 bool send_ping(connection_t *c) {
48         c->status.pinged = true;
49         c->last_ping_time = now.tv_sec;
50
51         return send_request(c, "%d", PING);
52 }
53
54 bool ping_h(connection_t *c, const char *request) {
55         (void)request;
56         return send_pong(c);
57 }
58
59 bool send_pong(connection_t *c) {
60         return send_request(c, "%d", PONG);
61 }
62
63 bool pong_h(connection_t *c, const char *request) {
64         (void)request;
65         c->status.pinged = false;
66
67         /* Successful connection, reset timeout if this is an outgoing connection. */
68
69         if(c->outgoing && c->outgoing->timeout) {
70                 c->outgoing->timeout = 0;
71                 reset_address_cache(c->outgoing->node->address_cache, &c->address);
72         }
73
74         return true;
75 }
76
77 static bool random_early_drop(connection_t *c) {
78         if(c->outbuf.len > (size_t)maxoutbufsize / 2) {
79                 if((c->outbuf.len - (size_t)maxoutbufsize / 2) > prng((size_t)maxoutbufsize / 2)) {
80                         return true;
81                 }
82         }
83
84         return false;
85 }
86
87 /* Sending and receiving packets via TCP */
88
89 bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
90         /* If there already is a lot of data in the outbuf buffer, discard this packet.
91            We use a very simple Random Early Drop algorithm. */
92
93         if(random_early_drop(c)) {
94                 return true;
95         }
96
97         if(!send_request(c, "%d %d", PACKET, packet->len)) {
98                 return false;
99         }
100
101         return send_meta(c, DATA(packet), packet->len);
102 }
103
104 bool tcppacket_h(connection_t *c, const char *request) {
105         short int len;
106
107         if(sscanf(request, "%*d %hd", &len) != 1 || len < 0) {
108                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
109                        c->hostname);
110                 return false;
111         }
112
113         /* Set reqlen to len, this will tell receive_meta() that a tcppacket is coming. */
114
115         c->tcplen = len;
116
117         return true;
118 }
119
120 bool send_sptps_tcppacket(connection_t *c, const void *packet, size_t len) {
121         /* If there already is a lot of data in the outbuf buffer, discard this packet.
122            We use a very simple Random Early Drop algorithm. */
123
124         if(random_early_drop(c)) {
125                 return true;
126         }
127
128         if(!send_request(c, "%d %lu", SPTPS_PACKET, (unsigned long)len)) {
129                 return false;
130         }
131
132         send_meta_raw(c, packet, len);
133         return true;
134 }
135
136 bool sptps_tcppacket_h(connection_t *c, const char *request) {
137         short int len;
138
139         if(sscanf(request, "%*d %hd", &len) != 1 || len < 0) {
140                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "SPTPS_PACKET", c->name,
141                        c->hostname);
142                 return false;
143         }
144
145         /* Set sptpslen to len, this will tell receive_meta() that a SPTPS packet is coming. */
146
147         c->sptpslen = len;
148
149         return true;
150 }
151
152 /* Transmitting UDP information */
153
154 bool send_udp_info(node_t *from, node_t *to) {
155         /* If there's a static relay in the path, there's no point in sending the message
156            farther than the static relay. */
157         to = (to->via == myself) ? to->nexthop : to->via;
158
159         if(to == NULL) {
160                 logger(DEBUG_ALWAYS, LOG_ERR, "Something went wrong when selecting relay - possible fake UDP_INFO");
161                 return false;
162         }
163
164         /* Skip cases where sending UDP info messages doesn't make sense.
165            This is done here in order to avoid repeating the same logic in multiple callsites. */
166
167         if(to == myself) {
168                 return true;
169         }
170
171         if(!to->status.reachable) {
172                 return true;
173         }
174
175         if(from == myself) {
176                 if(to->connection) {
177                         return true;
178                 }
179
180                 struct timeval elapsed;
181
182                 timersub(&now, &to->udp_info_sent, &elapsed);
183
184                 if(elapsed.tv_sec < udp_info_interval) {
185                         return true;
186                 }
187         }
188
189         if((myself->options | from->options | to->options) & OPTION_TCPONLY) {
190                 return true;
191         }
192
193         if((to->nexthop->options >> 24) < 5) {
194                 return true;
195         }
196
197         char *from_address, *from_port;
198         /* If we're the originator, the address we use is irrelevant
199            because the first intermediate node will ignore it.
200            We use our local address as it somewhat makes sense
201            and it's simpler than introducing an encoding for "null" addresses anyway. */
202         sockaddr2str((from != myself) ? &from->address : &to->nexthop->connection->edge->local_address, &from_address, &from_port);
203
204         bool x = send_request(to->nexthop->connection, "%d %s %s %s %s", UDP_INFO, from->name, to->name, from_address, from_port);
205
206         free(from_address);
207         free(from_port);
208
209         if(from == myself) {
210                 to->udp_info_sent = now;
211         }
212
213         return x;
214 }
215
216 bool udp_info_h(connection_t *c, const char *request) {
217         char from_name[MAX_STRING_SIZE];
218         char to_name[MAX_STRING_SIZE];
219         char from_address[MAX_STRING_SIZE];
220         char from_port[MAX_STRING_SIZE];
221
222         if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING, from_name, to_name, from_address, from_port) != 4) {
223                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "UDP_INFO", c->name, c->hostname);
224                 return false;
225         }
226
227         if(!check_id(from_name) || !check_id(to_name)) {
228                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "UDP_INFO", c->name, c->hostname, "invalid name");
229                 return false;
230         }
231
232         node_t *from = lookup_node(from_name);
233
234         if(!from) {
235                 logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list", "UDP_INFO", c->name, c->hostname, from_name);
236                 return true;
237         }
238
239         if(from != from->via) {
240                 /* Not supposed to happen, as it means the message wandered past a static relay */
241                 logger(DEBUG_PROTOCOL, LOG_WARNING, "Got UDP info message from %s (%s) which we can't reach directly", from->name, from->hostname);
242                 return true;
243         }
244
245         /* If we have a direct edge to "from", we are in a better position
246            to guess its address than it is itself. */
247         if(!from->connection && !from->status.udp_confirmed) {
248                 sockaddr_t from_addr = str2sockaddr(from_address, from_port);
249
250                 if(sockaddrcmp(&from_addr, &from->address)) {
251                         update_node_udp(from, &from_addr);
252                 }
253         }
254
255         node_t *to = lookup_node(to_name);
256
257         if(!to) {
258                 logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list", "UDP_INFO", c->name, c->hostname, to_name);
259                 return true;
260         }
261
262         /* Send our own data (which could be what we just received) up the chain. */
263
264         return send_udp_info(from, to);
265 }
266
267 /* Transmitting MTU information */
268
269 bool send_mtu_info(node_t *from, node_t *to, int mtu) {
270         /* Skip cases where sending MTU info messages doesn't make sense.
271            This is done here in order to avoid repeating the same logic in multiple callsites. */
272
273         if(to == myself) {
274                 return true;
275         }
276
277         if(!to->status.reachable) {
278                 return true;
279         }
280
281         if(from == myself) {
282                 if(to->connection) {
283                         return true;
284                 }
285
286                 struct timeval elapsed;
287
288                 timersub(&now, &to->mtu_info_sent, &elapsed);
289
290                 if(elapsed.tv_sec < mtu_info_interval) {
291                         return true;
292                 }
293         }
294
295         if((to->nexthop->options >> 24) < 6) {
296                 return true;
297         }
298
299         /* We will send the passed-in MTU value, unless we believe ours is better. */
300
301         node_t *via = (from->via == myself) ? from->nexthop : from->via;
302
303         if(from->minmtu == from->maxmtu && from->via == myself) {
304                 /* We have a direct measurement. Override the value entirely.
305                    Note that we only do that if we are sitting as a static relay in the path;
306                    otherwise, we can't guarantee packets will flow through us, and increasing
307                    MTU could therefore end up being too optimistic. */
308                 mtu = from->minmtu;
309         } else if(via->minmtu == via->maxmtu) {
310                 /* Static relay. Ensure packets will make it through the entire relay path. */
311                 mtu = MIN(mtu, via->minmtu);
312         } else if(via->nexthop->minmtu == via->nexthop->maxmtu) {
313                 /* Dynamic relay. Ensure packets will make it through the entire relay path. */
314                 mtu = MIN(mtu, via->nexthop->minmtu);
315         }
316
317         if(from == myself) {
318                 to->mtu_info_sent = now;
319         }
320
321         /* If none of the conditions above match in the steady state, it means we're using TCP,
322            so the MTU is irrelevant. That said, it is still important to honor the MTU that was passed in,
323            because other parts of the relay path might be able to use UDP, which means they care about the MTU. */
324
325         return send_request(to->nexthop->connection, "%d %s %s %d", MTU_INFO, from->name, to->name, mtu);
326 }
327
328 bool mtu_info_h(connection_t *c, const char *request) {
329         char from_name[MAX_STRING_SIZE];
330         char to_name[MAX_STRING_SIZE];
331         int mtu;
332
333         if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" %d", from_name, to_name, &mtu) != 3) {
334                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "MTU_INFO", c->name, c->hostname);
335                 return false;
336         }
337
338         if(mtu < 512) {
339                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "MTU_INFO", c->name, c->hostname, "invalid MTU");
340                 return false;
341         }
342
343         mtu = MIN(mtu, MTU);
344
345         if(!check_id(from_name) || !check_id(to_name)) {
346                 logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "MTU_INFO", c->name, c->hostname, "invalid name");
347                 return false;
348         }
349
350         node_t *from = lookup_node(from_name);
351
352         if(!from) {
353                 logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list", "MTU_INFO", c->name, c->hostname, from_name);
354                 return true;
355         }
356
357         /* If we don't know the current MTU for that node, use the one we received.
358            Even if we're about to make our own measurements, the value we got from downstream nodes should be pretty close
359            so it's a good idea to use it in the mean time. */
360         if(from->mtu != mtu && from->minmtu != from->maxmtu) {
361                 logger(DEBUG_TRAFFIC, LOG_INFO, "Using provisional MTU %d for node %s (%s)", mtu, from->name, from->hostname);
362                 from->mtu = mtu;
363         }
364
365         node_t *to = lookup_node(to_name);
366
367         if(!to) {
368                 logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list", "MTU_INFO", c->name, c->hostname, to_name);
369                 return true;
370         }
371
372         /* Continue passing the MTU value (or a better one if we have it) up the chain. */
373
374         return send_mtu_info(from, to, mtu);
375 }