Allow Cipher and Digest "none".
[tinc] / src / net_packet.c
index 3c3397c..70b6106 100644 (file)
@@ -1,7 +1,7 @@
 /*
     net_packet.c -- Handles in- and outgoing VPN packets
     Copyright (C) 1998-2005 Ivo Timmermans,
-                  2000-2012 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2013 Guus Sliepen <guus@tinc-vpn.org>
                   2010      Timothy Redaelli <timothy@redaelli.eu>
                   2010      Brandon Black <blblack@gmail.com>
 
 
 #include "system.h"
 
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/hmac.h>
-
 #ifdef HAVE_ZLIB
 #include <zlib.h>
 #endif
@@ -48,7 +42,6 @@
 #include "net.h"
 #include "netutl.h"
 #include "protocol.h"
-#include "process.h"
 #include "route.h"
 #include "utils.h"
 #include "xalloc.h"
@@ -62,6 +55,7 @@ static void send_udppacket(node_t *, vpn_packet_t *);
 
 unsigned replaywin = 16;
 bool localdiscovery = false;
+sockaddr_t localdiscovery_address;
 
 #define MAX_SEQNO 1073741824
 
@@ -70,11 +64,15 @@ bool localdiscovery = false;
    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 three, with random sizes between the lower and
-   upper boundaries for the MTU thus far discovered.
+   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.
 
-   In case local discovery is enabled, a fourth packet is added to each batch,
+   In case local discovery is enabled, another packet is added to each batch,
    which will be broadcast to the local network.
+
 */
 
 static void send_mtu_probe_handler(void *data) {
@@ -125,13 +123,18 @@ static void send_mtu_probe_handler(void *data) {
                timeout = pingtimeout;
        }
 
-       for(int i = 0; i < 3 + localdiscovery; i++) {
+       for(int i = 0; i < 4 + localdiscovery; i++) {
                int len;
 
-               if(n->maxmtu <= n->minmtu)
+               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
+               } else {
                        len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu);
+               }
 
                if(len < 64)
                        len = 64;
@@ -140,16 +143,30 @@ static void send_mtu_probe_handler(void *data) {
                memset(packet.data, 0, 14);
                randomize(packet.data + 14, len - 14);
                packet.len = len;
-               if(i >= 3 && n->mtuprobes <= 10)
-                       packet.priority = -1;
-               else
-                       packet.priority = 0;
+               packet.priority = 0;
+               n->status.broadcast = 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_udppacket(n, &packet);
        }
 
+       n->status.broadcast = false;
+       n->probe_counter = 0;
+       gettimeofday(&n->probe_time, NULL);
+
+       /* Calculate the packet loss of incoming traffic by comparing the rate of
+          packets received to the rate with which the sequence number has increased.
+        */
+
+       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;
+
 end:
        timeout_set(&n->mtutimeout, &(struct timeval){timeout, rand() % 100000});
 }
@@ -160,12 +177,25 @@ void send_mtu_probe(node_t *n) {
 }
 
 static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
-       logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
-
        if(!packet->data[0]) {
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe request %d from %s (%s)", packet->len, n->name, n->hostname);
+
                /* It's a probe request, send back a reply */
 
-               packet->data[0] = 1;
+               /* Type 2 probe replies were introduced in protocol 17.3 */
+               if ((n->options >> 24) == 3) {
+                       uint8_t* data = packet->data;
+                       *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 = data - packet->data;
+               } else {
+                       /* Legacy protocol: n won't understand type 2 probe replies. */
+                       packet->data[0] = 1;
+               }
 
                /* Temporarily set udp_confirmed, so that the reply is sent
                   back exactly the way it came in. */
@@ -175,6 +205,16 @@ static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
                send_udppacket(n, packet);
                n->status.udp_confirmed = udp_confirmed;
        } else {
+               length_t probelen = len;
+               if (packet->data[0] == 2) {
+                       if (len < 3)
+                               logger(DEBUG_TRAFFIC, LOG_WARNING, "Received invalid (too short) MTU probe reply from %s (%s)", n->name, n->hostname);
+                       else {
+                               uint16_t probelen16; memcpy(&probelen16, packet->data + 1, 2); probelen = ntohs(probelen16);
+                       }
+               }
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Got type %d MTU probe reply %d from %s (%s)", packet->data[0], probelen, n->name, n->hostname);
+
                /* It's a valid reply: now we know bidirectional communication
                   is possible using the address and socket that the reply
                   packet used. */
@@ -184,6 +224,13 @@ static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
                /* If we haven't established the PMTU yet, restart the discovery process. */
 
                if(n->mtuprobes > 30) {
+                       if (probelen == n->maxmtu + 8) {
+                               logger(DEBUG_TRAFFIC, LOG_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
+                               n->maxmtu = MTU;
+                               n->mtuprobes = 10;
+                               return;
+                       }
+
                        if(n->minmtu)
                                n->mtuprobes = 30;
                        else
@@ -192,10 +239,40 @@ static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
 
                /* If applicable, raise the minimum supported MTU */
 
-               if(len > n->maxmtu)
-                       len = n->maxmtu;
-               if(n->minmtu < len)
-                       n->minmtu = len;
+               if(probelen > n->maxmtu)
+                       probelen = n->maxmtu;
+               if(n->minmtu < probelen)
+                       n->minmtu = probelen;
+
+               /* Calculate RTT and bandwidth.
+                  The RTT is the time between the MTU probe burst was sent and the first
+                  reply is received. The bandwidth is measured using the time between the
+                  arrival of the first and third probe reply (or type 2 probe requests).
+                */
+
+               struct timeval now, diff;
+               gettimeofday(&now, NULL);
+               timersub(&now, &n->probe_time, &diff);
+
+               struct timeval probe_timestamp = now;
+               if (packet->data[0] == 2 && packet->len >= 11) {
+                       uint32_t sec; memcpy(&sec, packet->data + 3, 4);
+                       uint32_t usec; memcpy(&usec, packet->data + 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;
+               } else if(n->probe_counter == 3) {
+                       struct timeval probe_timestamp_diff;
+                       timersub(&probe_timestamp, &n->probe_time, &probe_timestamp_diff);
+                       n->bandwidth = 2.0 * probelen / (probe_timestamp_diff.tv_sec + probe_timestamp_diff.tv_usec * 1e-6);
+                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "%s (%s) RTT %.2f ms, burst bandwidth %.3f Mbit/s, rx packet loss %.2f %%", n->name, n->hostname, n->rtt * 1e3, n->bandwidth * 8e-6, n->packetloss * 1e2);
+               }
        }
 }
 
@@ -274,10 +351,10 @@ static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
        if(n->status.sptps)
                return sptps_verify_datagram(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
 
-       if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
+       if(!digest_active(n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest))
                return false;
 
-       return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len - n->indigest.maclength, (const char *)&inpkt->seqno + inpkt->len - n->indigest.maclength);
+       return digest_verify(n->indigest, &inpkt->seqno, inpkt->len - digest_length(n->indigest), (const char *)&inpkt->seqno + inpkt->len - digest_length(n->indigest));
 }
 
 static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
@@ -288,19 +365,27 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        size_t outlen;
 
        if(n->status.sptps) {
+               if(!n->sptps.state) {
+                       if(!n->status.waitingforkey) {
+                               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but we haven't exchanged keys yet", n->name, n->hostname);
+                               send_req_key(n);
+                       } else {
+                               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
+                       }
+                       return;
+               }
                sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
                return;
        }
 
-       if(!cipher_active(&n->incipher)) {
-               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
-                                       n->name, n->hostname);
+       if(!n->status.validkey) {
+               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
                return;
        }
 
        /* Check packet length */
 
-       if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
+       if(inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest)) {
                logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got too short packet from %s (%s)",
                                        n->name, n->hostname);
                return;
@@ -308,20 +393,20 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
 
        /* Check the message authentication code */
 
-       if(digest_active(&n->indigest)) {
-               inpkt->len -= n->indigest.maclength;
-               if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
+       if(digest_active(n->indigest)) {
+               inpkt->len -= digest_length(n->indigest);
+               if(!digest_verify(n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
                        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
                        return;
                }
        }
        /* Decrypt the packet */
 
-       if(cipher_active(&n->incipher)) {
+       if(cipher_active(n->incipher)) {
                outpkt = pkt[nextpkt++];
                outlen = MAXSIZE;
 
-               if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
+               if(!cipher_decrypt(n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
                        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
                        return;
                }
@@ -365,6 +450,8 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        if(inpkt->seqno > n->received_seqno)
                n->received_seqno = inpkt->seqno;
 
+       n->received++;
+
        if(n->received_seqno > MAX_SEQNO)
                regenerate_key();
 
@@ -397,6 +484,9 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
 void receive_tcppacket(connection_t *c, const char *buffer, int len) {
        vpn_packet_t outpkt;
 
+       if(len > sizeof outpkt.data)
+               return;
+
        outpkt.len = len;
        if(c->options & OPTION_TCPONLY)
                outpkt.priority = 0;
@@ -412,7 +502,7 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
                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 < time(NULL)) {
+               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;
@@ -523,12 +613,22 @@ static void choose_broadcast_address(const node_t *n, const sockaddr_t **sa, int
        *sock = rand() % listen_sockets;
 
        if(listen_socket[*sock].sa.sa.sa_family == AF_INET6) {
-               broadcast_ipv6.in6.sin6_port = n->prevedge->address.in.sin_port;
-               broadcast_ipv6.in6.sin6_scope_id = listen_socket[*sock].sa.in6.sin6_scope_id;
-               *sa = &broadcast_ipv6;
+               if(localdiscovery_address.sa.sa_family == AF_INET6) {
+                       localdiscovery_address.in6.sin6_port = n->prevedge->address.in.sin_port;
+                       *sa = &localdiscovery_address;
+               } else {
+                       broadcast_ipv6.in6.sin6_port = n->prevedge->address.in.sin_port;
+                       broadcast_ipv6.in6.sin6_scope_id = listen_socket[*sock].sa.in6.sin6_scope_id;
+                       *sa = &broadcast_ipv6;
+               }
        } else {
-               broadcast_ipv4.in.sin_port = n->prevedge->address.in.sin_port;
-               *sa = &broadcast_ipv4;
+               if(localdiscovery_address.sa.sa_family == AF_INET) {
+                       localdiscovery_address.in.sin_port = n->prevedge->address.in.sin_port;
+                       *sa = &localdiscovery_address;
+               } else {
+                       broadcast_ipv4.in.sin_port = n->prevedge->address.in.sin_port;
+                       *sa = &broadcast_ipv4;
+               }
        }
 }
 
@@ -604,11 +704,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
 
        /* Encrypt the packet */
 
-       if(cipher_active(&n->outcipher)) {
+       if(cipher_active(n->outcipher)) {
                outpkt = pkt[nextpkt++];
                outlen = MAXSIZE;
 
-               if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
+               if(!cipher_encrypt(n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
@@ -619,9 +719,13 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
 
        /* Add the message authentication code */
 
-       if(digest_active(&n->outdigest)) {
-               digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len);
-               inpkt->len += digest_length(&n->outdigest);
+       if(digest_active(n->outdigest)) {
+               if(!digest_create(n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len)) {
+                       logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
+                       goto end;
+               }
+
+               inpkt->len += digest_length(n->outdigest);
        }
 
        /* Send the packet */
@@ -629,9 +733,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        const sockaddr_t *sa;
        int sock;
 
-       /* Overloaded use of priority field: -1 means local broadcast */
-
-       if(origpriority == -1 && n->prevedge)
+       if(n->status.broadcast)
                choose_broadcast_address(n, &sa, &sock);
        else
                choose_udp_address(n, &sa, &sock);
@@ -665,15 +767,17 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
 
        /* Send it via TCP if it is a handshake packet, TCPOnly is in use, or this packet is larger than the MTU. */
 
-       if(type >= SPTPS_HANDSHAKE || ((myself->options | to->options) & OPTION_TCPONLY) || (type != PKT_PROBE && len > to->minmtu)) {
+       if(type >= SPTPS_HANDSHAKE || ((myself->options | to->options) & OPTION_TCPONLY) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > to->minmtu)) {
                char buf[len * 4 / 3 + 5];
                b64encode(data, buf, len);
                /* If no valid key is known yet, send the packets using ANS_KEY requests,
                   to ensure we get to learn the reflexive UDP address. */
-               if(!to->status.validkey)
-                       return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, myself->name, to->name, buf, myself->incompression);
-               else
+               if(!to->status.validkey) {
+                       to->incompression = myself->incompression;
+                       return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, myself->name, to->name, buf, to->incompression);
+               } else {
                        return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf);
+               }
        }
 
        /* Otherwise, send the packet via UDP */
@@ -681,10 +785,15 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
        const sockaddr_t *sa;
        int sock;
 
-       choose_udp_address(to, &sa, &sock);
+       if(to->status.broadcast)
+               choose_broadcast_address(to, &sa, &sock);
+       else
+               choose_udp_address(to, &sa, &sock);
 
        if(sendto(listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
                if(sockmsgsize(sockerrno)) {
+                       // Compensate for SPTPS overhead
+                       len -= SPTPS_DATAGRAM_OVERHEAD;
                        if(to->maxmtu >= len)
                                to->maxmtu = len - 1;
                        if(to->mtu >= len)
@@ -853,7 +962,7 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
                                break;
 
                        for splay_each(node_t, n, node_tree)
-                               if(n->status.reachable && ((n->via == myself && n->nexthop == n) || n->via == n))
+                               if(n->status.reachable && n != myself && ((n->via == myself && n->nexthop == n) || n->via == n))
                                        send_packet(n, packet);
                        break;