+static void send_udp_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;
+
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Sending UDP probe length %d to %s (%s)", len, n->name, n->hostname);
+
+ send_udppacket(n, &packet);
+}
+
+// This function tries to establish a UDP tunnel to a node so that packets can be sent.
+// If a tunnel is already established, it makes sure it stays up.
+// This function makes no guarantees - it is up to the caller to check the node's state to figure out if UDP is usable.
+static void try_udp(node_t *n) {
+ if(!udp_discovery) {
+ return;
+ }
+
+ /* Send gratuitous probe replies to 1.1 nodes. */
+
+ if((n->options >> 24) >= 3 && n->status.udp_confirmed) {
+ struct timeval ping_tx_elapsed;
+ timersub(&now, &n->udp_reply_sent, &ping_tx_elapsed);
+
+ if(ping_tx_elapsed.tv_sec >= udp_discovery_keepalive_interval - 1) {
+ n->udp_reply_sent = now;
+
+ if(n->maxrecentlen) {
+ vpn_packet_t pkt;
+ pkt.len = n->maxrecentlen;
+ pkt.offset = DEFAULT_PACKET_OFFSET;
+ memset(DATA(&pkt), 0, 14);
+ randomize(DATA(&pkt) + 14, MIN_PROBE_SIZE - 14);
+ send_udp_probe_reply(n, &pkt, pkt.len);
+ n->maxrecentlen = 0;
+ }
+ }
+ }
+
+ /* Probe request */
+
+ struct timeval ping_tx_elapsed;
+ timersub(&now, &n->udp_ping_sent, &ping_tx_elapsed);
+
+ 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, 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, MIN_PROBE_SIZE);
+ n->status.send_locally = false;
+ }
+ }
+}
+
+static length_t choose_initial_maxmtu(node_t *n) {
+#ifdef IP_MTU
+
+ int sock = -1;
+
+ const sockaddr_t *sa = NULL;
+ int sockindex;
+ choose_udp_address(n, &sa, &sockindex);
+
+ if(!sa) {
+ return MTU;
+ }
+
+ sock = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
+
+ if(sock < 0) {
+ logger(DEBUG_TRAFFIC, LOG_ERR, "Creating MTU assessment socket for %s (%s) failed: %s", n->name, n->hostname, sockstrerror(sockerrno));
+ return MTU;
+ }
+
+ if(connect(sock, &sa->sa, SALEN(sa->sa))) {
+ logger(DEBUG_TRAFFIC, LOG_ERR, "Connecting MTU assessment socket for %s (%s) failed: %s", n->name, n->hostname, sockstrerror(sockerrno));
+ close(sock);
+ return MTU;
+ }
+
+ int ip_mtu;
+ socklen_t ip_mtu_len = sizeof(ip_mtu);
+
+ if(getsockopt(sock, IPPROTO_IP, IP_MTU, &ip_mtu, &ip_mtu_len)) {
+ logger(DEBUG_TRAFFIC, LOG_ERR, "getsockopt(IP_MTU) on %s (%s) failed: %s", n->name, n->hostname, sockstrerror(sockerrno));
+ close(sock);
+ return MTU;
+ }
+
+ close(sock);
+
+ /* getsockopt(IP_MTU) returns the MTU of the physical interface.
+ We need to remove various overheads to get to the tinc MTU. */
+ length_t mtu = ip_mtu;
+ mtu -= (sa->sa.sa_family == AF_INET6) ? sizeof(struct ip6_hdr) : sizeof(struct ip);
+ mtu -= 8; /* UDP */
+
+ if(n->status.sptps) {
+ mtu -= SPTPS_DATAGRAM_OVERHEAD;
+
+ if((n->options >> 24) >= 4) {
+ mtu -= sizeof(node_id_t) + sizeof(node_id_t);
+ }
+
+#ifndef DISABLE_LEGACY
+ } else {
+ mtu -= digest_length(n->outdigest);
+
+ /* Now it's tricky. We use CBC mode, so the length of the
+ encrypted payload must be a multiple of the blocksize. The
+ sequence number is also part of the encrypted payload, so we
+ must account for it after correcting for the blocksize.
+ Furthermore, the padding in the last block must be at least
+ 1 byte. */
+
+ length_t blocksize = cipher_blocksize(n->outcipher);
+
+ if(blocksize > 1) {
+ mtu /= blocksize;
+ mtu *= blocksize;
+ mtu--;
+ }
+
+ mtu -= 4; // seqno
+#endif
+ }
+
+ if(mtu < 512) {
+ logger(DEBUG_TRAFFIC, LOG_ERR, "getsockopt(IP_MTU) on %s (%s) returned absurdly small value: %d", n->name, n->hostname, ip_mtu);
+ return MTU;
+ }
+
+ if(mtu > MTU) {
+ return MTU;
+ }
+
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Using system-provided maximum tinc MTU for %s (%s): %hd", n->name, n->hostname, mtu);
+ return mtu;
+
+#else
+
+ return MTU;
+
+#endif
+}
+
+/* This function tries to determines the MTU of a node.
+ By calling this function repeatedly, n->minmtu will be progressively
+ increased, and at some point, n->mtu will be fixed to n->minmtu. If the MTU
+ is already fixed, this function checks if it can be increased.
+*/
+
+static void try_mtu(node_t *n) {
+ if(!(n->options & OPTION_PMTU_DISCOVERY)) {
+ return;
+ }
+
+ if(udp_discovery && !n->status.udp_confirmed) {
+ n->maxrecentlen = 0;
+ n->mtuprobes = 0;
+ n->minmtu = 0;
+ n->maxmtu = MTU;
+ return;
+ }
+
+ /* 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 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->mtu_ping_sent, &elapsed);
+
+ if(n->mtuprobes >= 0) {
+ if(n->mtuprobes != 0 && elapsed.tv_sec == 0 && elapsed.tv_usec < 333333) {