Keep track of the largest UDP packet size received from a node.
[tinc] / src / net_packet.c
index 8bf399f..96f8f10 100644 (file)
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
+/* The minimum size of a probe is 14 bytes, but since we normally use CBC mode
+   encryption, we can add a few extra random bytes without increasing the
+   resulting packet size. */
+#define MIN_PROBE_SIZE 18
+
 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];
@@ -90,6 +95,7 @@ static void udp_probe_timeout_handler(void *data) {
 
        logger(DEBUG_TRAFFIC, LOG_INFO, "Too much time has elapsed since last UDP ping response from %s (%s), stopping UDP communication", n->name, n->hostname);
        n->status.udp_confirmed = false;
+       n->maxrecentlen = 0;
        n->mtuprobes = 0;
        n->minmtu = 0;
        n->maxmtu = MTU;
@@ -97,20 +103,22 @@ static void udp_probe_timeout_handler(void *data) {
 
 static void udp_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
        if(!DATA(packet)[0]) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Got UDP probe request %d from %s (%s)", packet->len, n->name, n->hostname);
-
                /* It's a probe request, send back a reply */
 
+               if(!n->status.sptps && !n->status.validkey) {
+                       // But not if we don't have his key.
+                       logger(DEBUG_TRAFFIC, LOG_INFO, "Got UDP probe request from %s (%s) but we don't have his key yet", n->name, n->hostname);
+                       return;
+               }
+
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Got UDP probe request %d from %s (%s)", packet->len, n->name, n->hostname);
+
                /* Type 2 probe replies were introduced in protocol 17.3 */
                if ((n->options >> 24) >= 3) {
                        uint8_t *data = DATA(packet);
                        *data++ = 2;
                        uint16_t len16 = htons(len); memcpy(data, &len16, 2); data += 2;
-                       struct timeval now;
-                       gettimeofday(&now, NULL);
-                       uint32_t sec = htonl(now.tv_sec); memcpy(data, &sec, 4); data += 4;
-                       uint32_t usec = htonl(now.tv_usec); memcpy(data, &usec, 4); data += 4;
-                       packet->len -= 10;
+                       packet->len = MIN_PROBE_SIZE;
                } else {
                        /* Legacy protocol: n won't understand type 2 probe replies. */
                        DATA(packet)[0] = 1;
@@ -144,47 +152,24 @@ static void udp_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
                        timeout_add(&n->udp_ping_timeout, &udp_probe_timeout_handler, n, &(struct timeval){udp_discovery_timeout, 0});
                }
 
-               if(probelen >= n->maxmtu + 1) {
+               if(probelen > n->maxmtu) {
                        logger(DEBUG_TRAFFIC, LOG_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
+                       n->minmtu = probelen;
                        n->maxmtu = MTU;
                        /* Set mtuprobes to 1 so that try_mtu() doesn't reset maxmtu */
                        n->mtuprobes = 1;
                        return;
+               } else if(n->mtuprobes < 0 && probelen == n->maxmtu) {
+                       /* We got a maxmtu sized packet, confirming the PMTU is still valid. */
+                       n->mtuprobes = -1;
                }
 
                /* If applicable, raise the minimum supported MTU */
 
-               if(probelen > n->maxmtu)
-                       probelen = n->maxmtu;
                if(n->minmtu < probelen) {
                        n->minmtu = probelen;
                        try_fix_mtu(n);
                }
-
-               /* Calculate RTT.
-                  The RTT is the time between the MTU probe burst was sent and the first
-                  reply is received.
-                */
-
-               struct timeval now, diff;
-               gettimeofday(&now, NULL);
-               timersub(&now, &n->probe_time, &diff);
-
-               struct timeval probe_timestamp = now;
-               if (DATA(packet)[0] == 2 && packet->len >= 11) {
-                       uint32_t sec; memcpy(&sec, DATA(packet) + 3, 4);
-                       uint32_t usec; memcpy(&usec, DATA(packet) + 7, 4);
-                       probe_timestamp.tv_sec = ntohl(sec);
-                       probe_timestamp.tv_usec = ntohl(usec);
-               }
-               
-               n->probe_counter++;
-
-               if(n->probe_counter == 1) {
-                       n->rtt = diff.tv_sec + diff.tv_usec * 1e-6;
-                       n->probe_time = probe_timestamp;
-                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "%s (%s) RTT %.2f ms, rx packet loss %.2f %%", n->name, n->hostname, n->rtt * 1e3, n->packetloss * 1e2);
-               }
        }
 }
 
@@ -403,6 +388,9 @@ static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                origlen -= MTU/64 + 20;
        }
 
+       if(inpkt->len > n->maxrecentlen)
+               n->maxrecentlen = inpkt->len;
+
        inpkt->priority = 0;
 
        if(!DATA(inpkt)[12] && !DATA(inpkt)[13])
@@ -876,12 +864,12 @@ static void try_udp(node_t* n) {
        int interval = n->status.udp_confirmed ? udp_discovery_keepalive_interval : udp_discovery_interval;
 
        if(ping_tx_elapsed.tv_sec >= interval) {
-               send_udp_probe_packet(n, MAX(n->minmtu, 16));
+               send_udp_probe_packet(n, MIN_PROBE_SIZE);
                n->udp_ping_sent = now;
 
                if(localdiscovery && !n->status.udp_confirmed && n->prevedge) {
                        n->status.send_locally = true;
-                       send_udp_probe_packet(n, 16);
+                       send_udp_probe_packet(n, MIN_PROBE_SIZE);
                        n->status.send_locally = false;
                }
        }
@@ -978,6 +966,7 @@ static void try_mtu(node_t *n) {
                return;
 
        if(udp_discovery && !n->status.udp_confirmed) {
+               n->maxrecentlen = 0;
                n->mtuprobes = 0;
                n->minmtu = 0;
                n->maxmtu = MTU;
@@ -986,25 +975,43 @@ static void try_mtu(node_t *n) {
 
        /* mtuprobes == 0..19: initial discovery, send bursts with 1 second interval, mtuprobes++
           mtuprobes ==    20: fix MTU, and go to -1
-          mtuprobes ==    -1: send one >maxmtu probe every pingtimeout */
+          mtuprobes ==    -1: send one maxmtu and one maxmtu+1 probe every pinginterval
+          mtuprobes ==-2..-3: send one maxmtu probe every second
+          mtuprobes ==    -4: maxmtu no longer valid, reset minmtu and maxmtu and go to 0 */
 
        struct timeval elapsed;
-       timersub(&now, &n->probe_sent_time, &elapsed);
+       timersub(&now, &n->mtu_ping_sent, &elapsed);
        if(n->mtuprobes >= 0) {
                if(n->mtuprobes != 0 && elapsed.tv_sec == 0 && elapsed.tv_usec < 333333)
                        return;
        } else {
-               if(elapsed.tv_sec < pingtimeout)
-                       return;
+               if(n->mtuprobes < -1) {
+                       if(elapsed.tv_sec < 1)
+                               return;
+               } else {
+                       if(elapsed.tv_sec < pinginterval)
+                               return;
+               }
        }
 
+       n->mtu_ping_sent = now;
+
        try_fix_mtu(n);
 
+       if(n->mtuprobes < -3) {
+               /* We lost three MTU probes, restart discovery */
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Decrease in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
+               n->mtuprobes = 0;
+               n->minmtu = 0;
+       }
+
        if(n->mtuprobes < 0) {
-               /* After the initial discovery, we only send one >maxmtu probe
-                  to detect PMTU increases. */
-               if(n->maxmtu + 1 < MTU)
+               /* After the initial discovery, we only send one maxmtu and one
+                  maxmtu+1 probe to detect PMTU increases. */
+               send_udp_probe_packet(n, n->maxmtu);
+               if(n->mtuprobes == -1 && n->maxmtu + 1 < MTU)
                        send_udp_probe_packet(n, n->maxmtu + 1);
+               n->mtuprobes--;
        } else {
                /* Before initial discovery begins, set maxmtu to the most likely value.
                   If it's underestimated, we will correct it after initial discovery. */
@@ -1049,23 +1056,6 @@ static void try_mtu(node_t *n) {
                if(n->mtuprobes >= 0)
                        n->mtuprobes++;
        }
-
-       n->probe_counter = 0;
-       n->probe_sent_time = now;
-       n->probe_time = now;
-
-       /* Calculate the packet loss of incoming traffic by comparing the rate of
-          packets received to the rate with which the sequence number has increased.
-          TODO: this is unrelated to PMTU discovery - it should be moved elsewhere.
-        */
-
-       if(n->received > n->prev_received)
-               n->packetloss = 1.0 - (n->received - n->prev_received) / (float)(n->received_seqno - n->prev_received_seqno);
-       else
-               n->packetloss = n->received_seqno <= n->prev_received_seqno;
-
-       n->prev_received_seqno = n->received_seqno;
-       n->prev_received = n->received;
 }
 
 /* These functions try to establish a tunnel to a node (or its relay) so that
@@ -1081,7 +1071,7 @@ static void try_mtu(node_t *n) {
    idle.
 */
 
-static void try_tx_sptps(node_t *n) {
+static void try_tx_sptps(node_t *n, bool mtu) {
        /* If n is a TCP-only neighbor, we'll only use "cleartext" PACKET
           messages anyway, so there's no need for SPTPS at all. */
 
@@ -1104,13 +1094,19 @@ static void try_tx_sptps(node_t *n) {
        /* If we do have a relay, try everything with that one instead. */
 
        if(via != n)
-               return try_tx_sptps(via);
+               return try_tx_sptps(via, mtu);
 
        try_udp(n);
-       try_mtu(n);
+       if(mtu)
+               try_mtu(n);
 }
 
-static void try_tx_legacy(node_t *n) {
+static void try_tx_legacy(node_t *n, bool mtu) {
+       /* Does he have our key? If not, send one. */
+
+       if(!n->status.validkey_in)
+               send_ans_key(n);
+
        /* Check if we already have a key, or request one. */
 
        if(!n->status.validkey) {
@@ -1122,7 +1118,15 @@ static void try_tx_legacy(node_t *n) {
        }
 
        try_udp(n);
-       try_mtu(n);
+       if(mtu)
+               try_mtu(n);
+}
+
+void try_tx(node_t *n, bool mtu) {
+       if(n->status.sptps)
+               try_tx_sptps(n, mtu);
+       else
+               try_tx_legacy(n, mtu);
 }
 
 void send_packet(node_t *n, vpn_packet_t *packet) {
@@ -1155,7 +1159,7 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
 
        if(n->status.sptps) {
                send_sptps_packet(n, packet);
-               try_tx_sptps(n);
+               try_tx_sptps(n, true);
                return;
        }
 
@@ -1175,7 +1179,7 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
        }
 
        send_udppacket(via, packet);
-       try_tx_legacy(via);
+       try_tx_legacy(via, true);
 }
 
 void broadcast_packet(const node_t *from, vpn_packet_t *packet) {