Support ToS/DiffServ for IPv6 meta and UDP connections.
[tinc] / src / net_packet.c
index 2e47df4..f8c6ac9 100644 (file)
@@ -1,7 +1,7 @@
 /*
     net_packet.c -- Handles in- and outgoing VPN packets
     Copyright (C) 1998-2005 Ivo Timmermans,
-                  2000-2014 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2016 Guus Sliepen <guus@tinc-vpn.org>
                   2010      Timothy Redaelli <timothy@redaelli.eu>
                   2010      Brandon Black <blblack@gmail.com>
 
@@ -64,7 +64,7 @@ static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999
 
 static void send_udppacket(node_t *, vpn_packet_t *);
 
-unsigned replaywin = 16;
+unsigned replaywin = 32;
 bool localdiscovery = true;
 bool udp_discovery = true;
 int udp_discovery_keepalive_interval = 10;
@@ -260,7 +260,7 @@ static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
        if(!n->status.validkey_in || !digest_active(n->indigest) || inpkt->len < sizeof(seqno_t) + digest_length(n->indigest))
                return false;
 
-       return digest_verify(n->indigest, SEQNO(inpkt), inpkt->len - digest_length(n->indigest), DATA(inpkt) + inpkt->len - digest_length(n->indigest));
+       return digest_verify(n->indigest, inpkt->data, inpkt->len - digest_length(n->indigest), inpkt->data + inpkt->len - digest_length(n->indigest));
 #endif
 }
 
@@ -282,13 +282,19 @@ static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                        }
                        return false;
                }
-               inpkt->offset += 2 * sizeof(node_id_t);
                n->status.udppacket = true;
-               bool result = sptps_receive_data(&n->sptps, DATA(inpkt), inpkt->len - 2 * sizeof(node_id_t));
+               bool result = sptps_receive_data(&n->sptps, DATA(inpkt), inpkt->len);
                n->status.udppacket = false;
 
                if(!result) {
-                       logger(DEBUG_TRAFFIC, LOG_ERR, "Got bad packet from %s (%s)", n->name, n->hostname);
+                       /* Uh-oh. It might be that the tunnel is stuck in some corrupted state,
+                          so let's restart SPTPS in case that helps. But don't do that too often
+                          to prevent storms, and because that would make life a little too easy
+                          for external attackers trying to DoS us. */
+                       if(n->last_req_key < now.tv_sec - 10) {
+                               logger(DEBUG_PROTOCOL, LOG_ERR, "Failed to decode raw TCP packet from %s (%s), restarting SPTPS", n->name, n->hostname);
+                               send_req_key(n);
+                       }
                        return false;
                }
                return true;
@@ -428,6 +434,64 @@ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
        receive_packet(c->node, &outpkt);
 }
 
+bool receive_tcppacket_sptps(connection_t *c, const char *data, int len) {
+       if (len < sizeof(node_id_t) + sizeof(node_id_t)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got too short TCP SPTPS packet from %s (%s)", c->name, c->hostname);
+               return false;
+       }
+
+       node_t *to = lookup_node_id((node_id_t *)data);
+       data += sizeof(node_id_t); len -= sizeof(node_id_t);
+       if(!to) {
+               logger(DEBUG_PROTOCOL, LOG_ERR, "Got TCP SPTPS packet from %s (%s) with unknown destination ID", c->name, c->hostname);
+               return true;
+       }
+
+       node_t *from = lookup_node_id((node_id_t *)data);
+       data += sizeof(node_id_t); len -= sizeof(node_id_t);
+       if(!from) {
+               logger(DEBUG_PROTOCOL, LOG_ERR, "Got TCP SPTPS packet from %s (%s) with unknown source ID", c->name, c->hostname);
+               return true;
+       }
+
+       if(!to->status.reachable) {
+               /* This can happen in the form of a race condition
+                  if the node just became unreachable. */
+               logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot relay TCP packet from %s (%s) because the destination, %s (%s), is unreachable", from->name, from->hostname, to->name, to->hostname);
+               return true;
+       }
+
+       /* Help the sender reach us over UDP.
+          Note that we only do this if we're the destination or the static relay;
+          otherwise every hop would initiate its own UDP info message, resulting in elevated chatter. */
+       if(to->via == myself)
+               send_udp_info(myself, from);
+
+       /* If we're not the final recipient, relay the packet. */
+
+       if(to != myself) {
+               send_sptps_data(to, from, 0, data, len);
+               try_tx(to, true);
+               return true;
+       }
+
+       /* The packet is for us */
+
+       if(!sptps_receive_data(&from->sptps, data, len)) {
+               /* Uh-oh. It might be that the tunnel is stuck in some corrupted state,
+                  so let's restart SPTPS in case that helps. But don't do that too often
+                  to prevent storms. */
+               if(from->last_req_key < now.tv_sec - 10) {
+                       logger(DEBUG_PROTOCOL, LOG_ERR, "Failed to decode raw TCP packet from %s (%s), restarting SPTPS", from->name, from->hostname);
+                       send_req_key(from);
+               }
+               return true;
+       }
+
+       send_mtu_info(myself, from, MTU);
+       return true;
+}
+
 static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
        if(!n->status.validkey && !n->connection)
                return;
@@ -557,10 +621,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        vpn_packet_t *outpkt;
        int origlen = origpkt->len;
        size_t outlen;
-#if defined(SOL_IP) && defined(IP_TOS)
-       static int priority = 0;
        int origpriority = origpkt->priority;
-#endif
 
        pkt1.offset = DEFAULT_PACKET_OFFSET;
        pkt2.offset = DEFAULT_PACKET_OFFSET;
@@ -655,15 +716,27 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        if(!sa)
                choose_udp_address(n, &sa, &sock);
 
+       if(priorityinheritance && origpriority != listen_socket[sock].priority) {
+               listen_socket[sock].priority = origpriority;
+               switch(sa->sa.sa_family) {
 #if defined(SOL_IP) && defined(IP_TOS)
-       if(priorityinheritance && origpriority != priority
-          && listen_socket[n->sock].sa.sa.sa_family == AF_INET) {
-               priority = origpriority;
-               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
-               if(setsockopt(listen_socket[n->sock].udp.fd, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
-                       logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
-       }
+               case AF_INET:
+                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting IPv4 outgoing packet priority to %d", origpriority);
+                       if(setsockopt(listen_socket[sock].udp.fd, SOL_IP, IP_TOS, &origpriority, sizeof origpriority)) /* SO_PRIORITY doesn't seem to work */
+                               logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
+                       break;
 #endif
+#if defined(IPPROTO_IPV6) & defined(IPV6_TCLASS)
+               case AF_INET6:
+                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting IPv6 outgoing packet priority to %d", origpriority);
+                       if(setsockopt(listen_socket[sock].udp.fd, IPPROTO_IPV6, IPV6_TCLASS, &origpriority, sizeof origpriority)) /* SO_PRIORITY doesn't seem to work */
+                               logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
 
        if(sendto(listen_socket[sock].udp.fd, SEQNO(inpkt), inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
                if(sockmsgsize(sockerrno)) {
@@ -681,7 +754,7 @@ end:
 #endif
 }
 
-static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void *data, size_t len) {
+bool send_sptps_data(node_t *to, node_t *from, int type, const void *data, size_t len) {
        node_t *relay = (to->via != myself && (type == PKT_PROBE || (len - SPTPS_DATAGRAM_OVERHEAD) <= to->via->minmtu)) ? to->via : to->nexthop;
        bool direct = from == myself && to == relay;
        bool relay_supported = (relay->options >> 24) >= 4;
@@ -690,15 +763,25 @@ static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void
        /* 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. */
 
        if(type == SPTPS_HANDSHAKE || tcponly || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) {
+               if(type != SPTPS_HANDSHAKE && (to->nexthop->connection->options >> 24) >= 7) {
+                       char buf[len + sizeof to->id + sizeof from->id]; char* buf_ptr = buf;
+                       memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id;
+                       memcpy(buf_ptr, &from->id, sizeof from->id); buf_ptr += sizeof from->id;
+                       memcpy(buf_ptr, data, len); buf_ptr += len;
+                       logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s) (TCP)", from->name, from->hostname, to->name, to->hostname, to->nexthop->name, to->nexthop->hostname);
+                       return send_sptps_tcppacket(to->nexthop->connection, buf, sizeof buf);
+               }
+
                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(from == myself && !to->status.validkey) {
+               /* If this is a handshake packet, use ANS_KEY instead of REQ_KEY, for two reasons:
+                   - We don't want intermediate nodes to switch to UDP to relay these packets;
+                   - ANS_KEY allows us to learn the reflexive UDP address. */
+               if(type == SPTPS_HANDSHAKE) {
                        to->incompression = myself->incompression;
                        return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, from->name, to->name, buf, to->incompression);
                } else {
-                       return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, REQ_SPTPS, buf);
+                       return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, SPTPS_PACKET, buf);
                }
        }
 
@@ -725,7 +808,7 @@ static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void
                choose_local_address(relay, &sa, &sock);
        if(!sa)
                choose_udp_address(relay, &sa, &sock);
-       logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s)", from->name, from->hostname, to->name, to->hostname, relay->name, relay->hostname);
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s) (UDP)", from->name, from->hostname, to->name, to->hostname, relay->name, relay->hostname);
        if(sendto(listen_socket[sock].udp.fd, buf, buf_ptr - buf, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
                if(sockmsgsize(sockerrno)) {
                        // Compensate for SPTPS overhead
@@ -744,10 +827,6 @@ static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void
        return true;
 }
 
-bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
-       return send_sptps_data_priv(handle, myself, type, data, len);
-}
-
 bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) {
        node_t *from = handle;
 
@@ -1127,15 +1206,13 @@ static void try_tx_sptps(node_t *n, bool mtu) {
 
        node_t *via = (n->via == myself) ? n->nexthop : n->via;
 
-       /* If the static relay doesn't support SPTPS, everything goes via TCP anyway. */
-
-       if((via->options >> 24) < 4)
-               return;
-
-       /* If we do have a static relay, try everything with that one instead. */
+       /* If we do have a static relay, try everything with that one instead, if it supports relaying. */
 
-       if(via != n)
-               try_tx_sptps(via, mtu);
+       if(via != n) {
+               if((via->options >> 24) < 4)
+                       return;
+               return try_tx(via, mtu);
+       }
 
        /* Otherwise, try to establish UDP connectivity. */
 
@@ -1147,7 +1224,7 @@ static void try_tx_sptps(node_t *n, bool mtu) {
           while we try to establish direct connectivity. */
 
        if(!n->status.udp_confirmed && n != n->nexthop && (n->nexthop->options >> 24) >= 4)
-               try_tx_sptps(n->nexthop, mtu);
+               try_tx(n->nexthop, mtu);
 }
 
 static void try_tx_legacy(node_t *n, bool mtu) {
@@ -1172,6 +1249,8 @@ static void try_tx_legacy(node_t *n, bool mtu) {
 }
 
 void try_tx(node_t *n, bool mtu) {
+       if(!n->status.reachable)
+               return;
        if(n->status.sptps)
                try_tx_sptps(n, mtu);
        else
@@ -1182,8 +1261,12 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
        // If it's for myself, write it to the tun/tap device.
 
        if(n == myself) {
-               if(overwrite_mac)
+               if(overwrite_mac) {
                         memcpy(DATA(packet), mymac.x, ETH_ALEN);
+                        // Use an arbitrary fake source address.
+                        memcpy(DATA(packet) + ETH_ALEN, DATA(packet), ETH_ALEN);
+                        DATA(packet)[ETH_ALEN * 2 - 1] ^= 0xFF;
+               }
                n->out_packets++;
                n->out_bytes += packet->len;
                devops.write(packet);
@@ -1208,7 +1291,7 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
 
        if(n->status.sptps) {
                send_sptps_packet(n, packet);
-               try_tx_sptps(n, true);
+               try_tx(n, true);
                return;
        }
 
@@ -1228,7 +1311,7 @@ void send_packet(node_t *n, vpn_packet_t *packet) {
        }
 
        send_udppacket(via, packet);
-       try_tx_legacy(via, true);
+       try_tx(via, true);
 }
 
 void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
@@ -1286,7 +1369,7 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
                if(!n->status.reachable || n == myself)
                        continue;
 
-               if((n->status.sptps && !n->sptps.instate) || !n->status.validkey_in)
+               if(!n->status.validkey_in && !(n->status.sptps && n->sptps.instate))
                        continue;
 
                bool soft = false;
@@ -1319,42 +1402,27 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
        return match;
 }
 
-void handle_incoming_vpn_data(void *data, int flags) {
-       listen_socket_t *ls = data;
-       vpn_packet_t pkt;
+static void handle_incoming_vpn_packet(listen_socket_t *ls, vpn_packet_t *pkt, sockaddr_t *addr) {
        char *hostname;
        node_id_t nullid = {};
-       sockaddr_t addr = {};
-       socklen_t addrlen = sizeof addr;
        node_t *from, *to;
        bool direct = false;
 
-       pkt.offset = 0;
-       int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
-
-       if(len <= 0 || len > MAXSIZE) {
-               if(!sockwouldblock(sockerrno))
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
-               return;
-       }
-
-       pkt.len = len;
-
-       sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */
+       sockaddrunmap(addr); /* Some braindead IPv6 implementations do stupid things. */
 
        // Try to figure out who sent this packet.
 
-       node_t *n = lookup_node_udp(&addr);
+       node_t *n = lookup_node_udp(addr);
 
        if(n && !n->status.udp_confirmed)
                n = NULL; // Don't believe it if we don't have confirmation yet.
 
        if(!n) {
                // It might be from a 1.1 node, which might have a source ID in the packet.
-               pkt.offset = 2 * sizeof(node_id_t);
-               from = lookup_node_id(SRCID(&pkt));
-               if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) {
-                       if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)))
+               pkt->offset = 2 * sizeof(node_id_t);
+               from = lookup_node_id(SRCID(pkt));
+               if(from && !memcmp(DSTID(pkt), &nullid, sizeof nullid) && from->status.sptps) {
+                       if(sptps_verify_datagram(&from->sptps, DATA(pkt), pkt->len - 2 * sizeof(node_id_t)))
                                n = from;
                        else
                                goto skip_harder;
@@ -1362,36 +1430,49 @@ void handle_incoming_vpn_data(void *data, int flags) {
        }
 
        if(!n) {
-               pkt.offset = 0;
-               n = try_harder(&addr, &pkt);
+               pkt->offset = 0;
+               n = try_harder(addr, pkt);
        }
 
 skip_harder:
        if(!n) {
                if(debug_level >= DEBUG_PROTOCOL) {
-                       hostname = sockaddr2hostname(&addr);
+                       hostname = sockaddr2hostname(addr);
                        logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
                        free(hostname);
                }
                return;
        }
 
+       pkt->offset = 0;
+
        if(n->status.sptps) {
-               pkt.offset = 2 * sizeof(node_id_t);
+               bool relay_enabled = (n->options >> 24) >= 4;
+               if (relay_enabled) {
+                       pkt->offset = 2 * sizeof(node_id_t);
+                       pkt->len -= pkt->offset;
+               }
 
-               if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) {
+               if(!memcmp(DSTID(pkt), &nullid, sizeof nullid) || !relay_enabled) {
                        direct = true;
                        from = n;
                        to = myself;
                } else {
-                       from = lookup_node_id(SRCID(&pkt));
-                       to = lookup_node_id(DSTID(&pkt));
+                       from = lookup_node_id(SRCID(pkt));
+                       to = lookup_node_id(DSTID(pkt));
                }
                if(!from || !to) {
                        logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
                        return;
                }
 
+               if(!to->status.reachable) {
+                       /* This can happen in the form of a race condition
+                          if the node just became unreachable. */
+                       logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot relay packet from %s (%s) because the destination, %s (%s), is unreachable", from->name, from->hostname, to->name, to->hostname);
+                       return;
+               }
+
                /* The packet is supposed to come from the originator or its static relay
                   (i.e. with no dynamic relays in between).
                   If it did not, "help" the static relay by sending it UDP info.
@@ -1404,8 +1485,8 @@ skip_harder:
                /* If we're not the final recipient, relay the packet. */
 
                if(to != myself) {
-                       send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t));
-                       try_tx_sptps(n, true);
+                       send_sptps_data(to, from, 0, DATA(pkt), pkt->len);
+                       try_tx(to, true);
                        return;
                }
        } else {
@@ -1413,13 +1494,12 @@ skip_harder:
                from = n;
        }
 
-       pkt.offset = 0;
-       if(!receive_udppacket(from, &pkt))
+       if(!receive_udppacket(from, pkt))
                return;
 
        n->sock = ls - listen_socket;
-       if(direct && sockaddrcmp(&addr, &n->address))
-               update_node_udp(n, &addr);
+       if(direct && sockaddrcmp(addr, &n->address))
+               update_node_udp(n, addr);
 
        /* If the packet went through a relay, help the sender find the appropriate MTU
           through the relay path. */
@@ -1428,6 +1508,67 @@ skip_harder:
                send_mtu_info(myself, n, MTU);
 }
 
+void handle_incoming_vpn_data(void *data, int flags) {
+       listen_socket_t *ls = data;
+
+#ifdef HAVE_RECVMMSG
+#define MAX_MSG 64
+       static int num = MAX_MSG;
+       static vpn_packet_t pkt[MAX_MSG];
+       static sockaddr_t addr[MAX_MSG];
+       static struct mmsghdr msg[MAX_MSG];
+       static struct iovec iov[MAX_MSG];
+
+       for(int i = 0; i < num; i++) {
+               pkt[i].offset = 0;
+
+               iov[i] = (struct iovec){
+                       .iov_base = DATA(&pkt[i]),
+                       .iov_len = MAXSIZE,
+               };
+
+               msg[i].msg_hdr = (struct msghdr){
+                       .msg_name = &addr[i].sa,
+                       .msg_namelen = sizeof addr[i],
+                       .msg_iov = &iov[i],
+                       .msg_iovlen = 1,
+               };
+       }
+
+       num = recvmmsg(ls->udp.fd, msg, MAX_MSG, MSG_DONTWAIT, NULL);
+
+       if(num < 0) {
+               if(!sockwouldblock(sockerrno))
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
+               return;
+       }
+
+       for(int i = 0; i < num; i++) {
+               pkt[i].len = msg[i].msg_len;
+               if(pkt[i].len <= 0 || pkt[i].len > MAXSIZE)
+                       continue;
+               handle_incoming_vpn_packet(ls, &pkt[i], &addr[i]);
+       }
+#else
+       vpn_packet_t pkt;
+       sockaddr_t addr = {};
+       socklen_t addrlen = sizeof addr;
+
+       pkt.offset = 0;
+       int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
+
+       if(len <= 0 || len > MAXSIZE) {
+               if(!sockwouldblock(sockerrno))
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
+               return;
+       }
+
+       pkt.len = len;
+
+       handle_incoming_vpn_packet(ls, &pkt, &addr);
+#endif
+}
+
 void handle_device_data(void *data, int flags) {
        vpn_packet_t packet;
        packet.offset = DEFAULT_PACKET_OFFSET;