Move try_sptps() closer to try_tx().
[tinc] / src / net_packet.c
index c07dd1a..51237d2 100644 (file)
 #include "utils.h"
 #include "xalloc.h"
 
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
 int keylifetime = 0;
 #ifdef HAVE_LZO
 static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
@@ -58,27 +62,21 @@ bool localdiscovery = true;
 
 #define MAX_SEQNO 1073741824
 
-/* mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
-   mtuprobes ==    31: sleep pinginterval seconds
-   mtuprobes ==    32: send 1 burst, sleep pingtimeout second
-   mtuprobes ==    33: no response from other side, restart PMTU discovery process
-
-   Probes are sent in batches of at least three, with random sizes between the
-   lower and upper boundaries for the MTU thus far discovered.
-
-   After the initial discovery, a fourth packet is added to each batch with a
-   size larger than the currently known PMTU, to test if the PMTU has increased.
+static void send_mtu_probe_packet(node_t *n, int len) {
+       vpn_packet_t packet;
+       packet.offset = DEFAULT_PACKET_OFFSET;
+       memset(DATA(&packet), 0, 14);
+       randomize(DATA(&packet) + 14, len - 14);
+       packet.len = len;
+       packet.priority = 0;
 
-   In case local discovery is enabled, another packet is added to each batch,
-   which will be broadcast to the local network.
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
 
-*/
+       send_udppacket(n, &packet);
+}
 
 static void send_mtu_probe_handler(void *data) {
        node_t *n = data;
-       int timeout = 1;
-
-       n->mtuprobes++;
 
        if(!n->status.reachable || !n->status.validkey) {
                logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
@@ -86,6 +84,14 @@ static void send_mtu_probe_handler(void *data) {
                return;
        }
 
+       /* mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
+          mtuprobes ==    31: sleep pinginterval seconds
+          mtuprobes ==    32: send 1 burst, sleep pingtimeout second
+          mtuprobes ==    33: no response from other side, restart PMTU discovery process */
+
+       n->mtuprobes++;
+       int timeout = 1;
+
        if(n->mtuprobes > 32) {
                if(!n->minmtu) {
                        n->mtuprobes = 31;
@@ -122,36 +128,29 @@ static void send_mtu_probe_handler(void *data) {
                timeout = pingtimeout;
        }
 
-       for(int i = 0; i < 4 + localdiscovery; i++) {
-               int len;
+       /* After the initial discovery, a fourth packet is added to each batch with a
+          size larger than the currently known PMTU, to test if the PMTU has increased. */
+       if (n->mtuprobes >= 30 && n->maxmtu + 8 < MTU)
+               send_mtu_probe_packet(n, n->maxmtu + 8);
 
-               if(i == 0) {
-                       if(n->mtuprobes < 30 || n->maxmtu + 8 >= MTU)
-                               continue;
-                       len = n->maxmtu + 8;
-               } else if(n->maxmtu <= n->minmtu) {
-                       len = n->maxmtu;
-               } else {
+       /* Probes are sent in batches of three, with random sizes between the
+          lower and upper boundaries for the MTU thus far discovered. */
+       for (int i = 0; i < 3; i++) {
+               int len = n->maxmtu;
+               if(n->minmtu < n->maxmtu)
                        len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu);
-               }
-
-               if(len < 64)
-                       len = 64;
 
-               vpn_packet_t packet;
-               packet.offset = DEFAULT_PACKET_OFFSET;
-               memset(DATA(&packet), 0, 14);
-               randomize(DATA(&packet) + 14, len - 14);
-               packet.len = len;
-               packet.priority = 0;
-               n->status.send_locally = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
-
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
+               send_mtu_probe_packet(n, MAX(len, 64));
+       }
 
-               send_udppacket(n, &packet);
+       /* In case local discovery is enabled, another packet is added to each batch,
+          which will be broadcast to the local network. */
+       if(localdiscovery && n->mtuprobes <= 10 && n->prevedge) {
+               n->status.send_locally = true;
+               send_mtu_probe_packet(n, 16);
+               n->status.send_locally = false;
        }
 
-       n->status.send_locally = false;
        n->probe_counter = 0;
        gettimeofday(&n->probe_time, NULL);
 
@@ -518,34 +517,8 @@ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
        receive_packet(c->node, &outpkt);
 }
 
-static bool try_sptps(node_t *n) {
-       if(n->status.validkey)
-               return true;
-
-       /* If n is a TCP-only neighbor, we'll only use "cleartext" PACKET
-          messages anyway, so there's no need for SPTPS at all. */
-       if(n->connection && ((myself->options | n->options) & OPTION_TCPONLY))
-               return false;
-
-       logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
-
-       if(!n->status.waitingforkey)
-               send_req_key(n);
-       else if(n->last_req_key + 10 < now.tv_sec) {
-               logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
-               sptps_stop(&n->sptps);
-               n->status.waitingforkey = false;
-               send_req_key(n);
-       }
-
-       return false;
-}
-
 static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
-       /* Note: condition order is as intended - even if we have a direct
-          metaconnection, we want to try SPTPS anyway as it's the only way to
-          get UDP going */
-       if(!try_sptps(n) && !n->connection)
+       if(!n->status.validkey && !n->connection)
                return;
 
        uint8_t type = 0;
@@ -698,14 +671,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
                logger(DEBUG_TRAFFIC, LOG_INFO,
                                   "No valid key known yet for %s (%s), forwarding via TCP",
                                   n->name, n->hostname);
-
-               if(n->last_req_key + 10 <= now.tv_sec) {
-                       send_req_key(n);
-                       n->last_req_key = now.tv_sec;
-               }
-
                send_tcppacket(n->nexthop->connection, origpkt);
-
                return;
        }
 
@@ -809,10 +775,6 @@ static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void
        bool relay_supported = (relay->options >> 24) >= 4;
        bool tcponly = (myself->options | relay->options) & OPTION_TCPONLY;
 
-       /* We don't really need the relay's key, but we need to establish a UDP tunnel with it and discover its MTU. */
-       if (!direct && relay_supported && !tcponly)
-               try_sptps(relay);
-
        /* Send it via TCP if it is a handshake packet, TCPOnly is in use, this is a relay packet that the other node cannot understand, or this packet is larger than the MTU.
           TODO: When relaying, the original sender does not know the end-to-end PMTU (it only knows the PMTU of the first hop).
                 This can lead to scenarios where large packets are sent over UDP to relay, but then relay has no choice but fall back to TCP. */
@@ -953,6 +915,56 @@ bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t
        return true;
 }
 
+// This function tries to get SPTPS keys, if they aren't already known.
+// This function makes no guarantees - it is up to the caller to check the node's state to figure out if the keys are available.
+static void try_sptps(node_t *n) {
+       if(n->status.validkey)
+               return;
+
+       logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
+
+       if(!n->status.waitingforkey)
+               send_req_key(n);
+       else if(n->last_req_key + 10 < now.tv_sec) {
+               logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
+               sptps_stop(&n->sptps);
+               n->status.waitingforkey = false;
+               send_req_key(n);
+       }
+
+       return;
+}
+
+// This function tries to establish a tunnel to a node (or its relay) so that packets can be sent (e.g. get SPTPS keys).
+// If a tunnel is already established, it tries to improve it (e.g. by trying to establish a UDP tunnel instead of TCP).
+// This function makes no guarantees - it is up to the caller to check the node's state to figure out if TCP and/or UDP is usable.
+// By calling this function repeatedly, the tunnel is gradually improved until we hit the wall imposed by the underlying network environment.
+// It is recommended to call this function every time a packet is sent (or intended to be sent) to a node,
+// so that the tunnel keeps improving as packets flow, and then gracefully downgrades itself as it goes idle.
+static void try_tx(node_t *n) {
+       /* If n is a TCP-only neighbor, we'll only use "cleartext" PACKET
+          messages anyway, so there's no need for SPTPS at all. Otherwise, get the keys. */
+       if(n->status.sptps && !(n->connection && ((myself->options | n->options) & OPTION_TCPONLY))) {
+               try_sptps(n);
+               if (!n->status.validkey)
+                       return;
+       }
+
+       node_t *via = (n->via == myself) ? n->nexthop : n->via;
+       
+       if((myself->options | via->options) & OPTION_TCPONLY)
+               return;
+
+       if(!n->status.sptps && !via->status.validkey && via->last_req_key + 10 <= now.tv_sec) {
+               send_req_key(via);
+               via->last_req_key = now.tv_sec;
+       }
+
+       /* If we don't know how to reach "via" yet, then try to reach it through a relay. */
+       if(n->status.sptps && !via->status.udp_confirmed && via->nexthop != via && (via->nexthop->options >> 24) >= 4)
+               try_tx(via->nexthop);
+}
+
 /*
   send a packet to the given vpn ip.
 */
@@ -982,7 +994,7 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
 
        if(n->status.sptps) {
                send_sptps_packet(n, packet);
-               return;
+               goto end;
        }
 
        via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
@@ -996,6 +1008,12 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
                        terminate_connection(via->connection, true);
        } else
                send_udppacket(via, packet);
+
+end:
+       /* Try to improve the tunnel.
+          Note that we do this *after* we send the packet because sending actual packets take priority
+          with regard to the send buffer space and latency. */
+       try_tx(n);
 }
 
 /* Broadcast a packet using the minimum spanning tree */