Always call res_init() before getaddrinfo().
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 12 Apr 2015 13:42:48 +0000 (15:42 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 12 Apr 2015 13:42:48 +0000 (15:42 +0200)
Unfortunately, glibc assumes that /etc/resolv.conf is a static file that
never changes. Even on servers, /etc/resolv.conf might be a dynamically
generated file, and we never know when it changes. So just call
res_init() every time, so glibc uses up-to-date nameserver information.

Conflicts:
src/have.h
src/net.c
src/net_setup.c

18 files changed:
bash_completion.d/tinc
doc/tinc.conf.5.in
doc/tinc.texi
src/ethernet.h
src/fsck.c
src/ipv4.h
src/ipv6.h
src/mingw/device.c
src/net.h
src/net_packet.c
src/net_setup.c
src/netutl.c
src/node.h
src/protocol.c
src/protocol.h
src/protocol_key.c
src/protocol_misc.c
src/tincctl.c

index 01629af..ddbc34a 100644 (file)
@@ -4,7 +4,7 @@ _tinc() {
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
        opts="-c -d -D -K -n -o -L -R -U --config --no-detach --debug --net --option --mlock --logfile --pidfile --chroot --user --help --version"
-       confvars="Address AddressFamily BindToAddress BindToInterface Broadcast BroadcastSubnet Cipher ClampMSS Compression ConnectTo DecrementTTL Device DeviceStandby DeviceType Digest DirectOnly Ed25519PrivateKeyFile Ed25519PublicKey Ed25519PublicKeyFile ExperimentalProtocol Forwarding GraphDumpFile Hostnames IffOneQueue IndirectData Interface KeyExpire ListenAddress LocalDiscovery MACExpire MACLength MaxOutputBufferSize MaxTimeout Mode Name PMTU PMTUDiscovery PingInterval PingTimeout Port PriorityInheritance PrivateKeyFile ProcessPriority Proxy PublicKeyFile ReplayWindow StrictSubnets Subnet TCPOnly TunnelServer UDPDiscovery UDPDiscoveryKeepaliveInterval UDPDiscoveryInterval UDPDiscoveryTimeout UDPRcvBuf UDPSndBuf VDEGroup VDEPort Weight"
+       confvars="Address AddressFamily BindToAddress BindToInterface Broadcast BroadcastSubnet Cipher ClampMSS Compression ConnectTo DecrementTTL Device DeviceStandby DeviceType Digest DirectOnly Ed25519PrivateKeyFile Ed25519PublicKey Ed25519PublicKeyFile ExperimentalProtocol Forwarding GraphDumpFile Hostnames IffOneQueue IndirectData Interface KeyExpire ListenAddress LocalDiscovery MACExpire MACLength MaxOutputBufferSize MaxTimeout Mode MTUInfoInterval Name PMTU PMTUDiscovery PingInterval PingTimeout Port PriorityInheritance PrivateKeyFile ProcessPriority Proxy PublicKeyFile ReplayWindow StrictSubnets Subnet TCPOnly TunnelServer UDPDiscovery UDPDiscoveryKeepaliveInterval UDPDiscoveryInterval UDPDiscoveryTimeout UDPInfoInterval UDPRcvBuf UDPSndBuf VDEGroup VDEPort Weight"
        commands="add connect debug del disconnect dump edit export export-all generate-ed25519-keys generate-keys generate-rsa-keys get help import info init invite join log network pcap pid purge reload restart retry set start stop top version"
 
        case ${prev} in
index 9e5c025..71b5ec6 100644 (file)
@@ -500,6 +500,8 @@ The minimum amount of time between sending UDP ping datagrams to try to establis
 .It Va UDPDiscoveryTimeout Li = Ar seconds Pq 30
 If tinc doesn't receive any UDP ping replies over the specified interval,
 it will assume UDP communication is broken and will fall back to TCP.
+.It Va UDPInfoInterval Li = Ar seconds Pq 5
+The minimum amount of time between sending periodic updates about UDP addresses, which are mostly useful for UDP hole punching.
 .It Va UDPRcvBuf Li = Ar bytes Pq 1048576
 Sets the socket receive buffer size for the UDP socket, in bytes.
 If set to zero, the default buffer size will be used by the operating system.
@@ -571,6 +573,8 @@ This option controls the initial path MTU to this node.
 .It Va PMTUDiscovery Li = yes | no Po yes Pc
 When this option is enabled, tinc will try to discover the path MTU to this node.
 After the path MTU has been discovered, it will be enforced on the VPN.
+.It Va MTUInfoInterval Li = Ar seconds Pq 5
+The minimum amount of time between sending periodic updates about relay path MTU. Useful for quickly determining MTU to indirect nodes.
 .It Va Port Li = Ar port Pq 655
 The port number on which this tinc daemon is listening for incoming connections,
 which is used if no port number is specified in an
index 55560f4..05760d9 100644 (file)
@@ -1253,6 +1253,10 @@ The minimum amount of time between sending UDP ping datagrams to try to establis
 If tinc doesn't receive any UDP ping replies over the specified interval,
 it will assume UDP communication is broken and will fall back to TCP.
 
+@cindex UDPInfoInterval
+@item UDPInfoInterval = <seconds> (5)
+The minimum amount of time between sending periodic updates about UDP addresses, which are mostly useful for UDP hole punching.
+
 @cindex UDPRcvBuf
 @item UDPRcvBuf = <bytes> (1048576)
 Sets the socket receive buffer size for the UDP socket, in bytes.
@@ -1331,6 +1335,10 @@ This option controls the initial path MTU to this node.
 When this option is enabled, tinc will try to discover the path MTU to this node.
 After the path MTU has been discovered, it will be enforced on the VPN.
 
+@cindex MTUInfoInterval
+@item MTUInfoInterval = <seconds> (5)
+The minimum amount of time between sending periodic updates about relay path MTU. Useful for quickly determining MTU to indirect nodes.
+
 @cindex Port
 @item Port = <@var{port}> (655)
 This is the port this tinc daemon listens on.
index a8b6420..085e96a 100644 (file)
@@ -50,7 +50,7 @@ struct ether_header {
        uint8_t ether_dhost[ETH_ALEN];
        uint8_t ether_shost[ETH_ALEN];
        uint16_t ether_type;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #endif
 
 #ifndef HAVE_STRUCT_ARPHDR
@@ -60,7 +60,7 @@ struct arphdr {
        uint8_t ar_hln;
        uint8_t ar_pln;
        uint16_t ar_op;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 
 #define ARPOP_REQUEST 1
 #define ARPOP_REPLY 2
@@ -78,7 +78,7 @@ struct  ether_arp {
        uint8_t arp_spa[4];
        uint8_t arp_tha[ETH_ALEN];
        uint8_t arp_tpa[4];
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #define arp_hrd ea_hdr.ar_hrd
 #define arp_pro ea_hdr.ar_pro
 #define arp_hln ea_hdr.ar_hln
index 8df95ec..205a599 100644 (file)
@@ -155,7 +155,11 @@ static void check_conffile(const char *fname, bool server) {
 }
 
 int fsck(const char *argv0) {
+#ifdef HAVE_MINGW
+       int uid = 0;
+#else
        uid_t uid = getuid();
+#endif
 
        // Check that tinc.conf is readable.
 
index 6cb969b..997b88d 100644 (file)
@@ -81,7 +81,7 @@ struct ip {
        uint8_t ip_p;
        uint16_t ip_sum;
        struct in_addr ip_src, ip_dst;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #endif
 
 #ifndef IP_OFFMASK
@@ -143,7 +143,7 @@ struct icmp {
 #define icmp_radv icmp_dun.id_radv
 #define icmp_mask icmp_dun.id_mask
 #define icmp_data icmp_dun.id_data
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #endif
 
 #endif /* __TINC_IPV4_H__ */
index 37d999a..46cf62d 100644 (file)
@@ -36,7 +36,7 @@ struct in6_addr {
                uint16_t u6_addr16[8];
                uint32_t u6_addr32[4];
        } in6_u;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #define s6_addr in6_u.u6_addr8
 #define s6_addr16 in6_u.u6_addr16
 #define s6_addr32 in6_u.u6_addr32
@@ -49,7 +49,7 @@ struct sockaddr_in6 {
        uint32_t sin6_flowinfo;
        struct in6_addr sin6_addr;
        uint32_t sin6_scope_id;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #endif
 
 #ifndef IN6_IS_ADDR_V4MAPPED
@@ -72,7 +72,7 @@ struct ip6_hdr {
        } ip6_ctlun;
        struct in6_addr ip6_src;
        struct in6_addr ip6_dst;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #define ip6_vfc ip6_ctlun.ip6_un2_vfc
 #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
 #define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
@@ -91,7 +91,7 @@ struct icmp6_hdr {
                uint16_t icmp6_un_data16[2];
                uint8_t icmp6_un_data8[4];
        } icmp6_dataun;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #define ICMP6_DST_UNREACH_NOROUTE 0
 #define ICMP6_DST_UNREACH 1
 #define ICMP6_PACKET_TOO_BIG 2
@@ -111,7 +111,7 @@ struct icmp6_hdr {
 struct nd_neighbor_solicit {
        struct icmp6_hdr nd_ns_hdr;
        struct in6_addr nd_ns_target;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #define ND_OPT_SOURCE_LINKADDR 1
 #define ND_OPT_TARGET_LINKADDR 2
 #define nd_ns_type nd_ns_hdr.icmp6_type
@@ -124,7 +124,7 @@ struct nd_neighbor_solicit {
 struct nd_opt_hdr {
        uint8_t nd_opt_type;
        uint8_t nd_opt_len;
-} __attribute__ ((__packed__));
+} __attribute__ ((__gcc_struct__, __packed__));
 #endif
 
 #endif /* __TINC_IPV6_H__ */
index 19719a7..4b821df 100644 (file)
@@ -38,7 +38,9 @@ int device_fd = -1;
 static HANDLE device_handle = INVALID_HANDLE_VALUE;
 static io_t device_read_io;
 static OVERLAPPED device_read_overlapped;
+static OVERLAPPED device_write_overlapped;
 static vpn_packet_t device_read_packet;
+static vpn_packet_t device_write_packet;
 char *device = NULL;
 char *iface = NULL;
 static char *device_info = NULL;
@@ -200,8 +202,12 @@ static void enable_device(void) {
        DWORD len;
        DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
 
-       io_add_event(&device_read_io, device_handle_read, NULL, CreateEvent(NULL, TRUE, FALSE, NULL));
-       device_read_overlapped.hEvent = device_read_io.event;
+       /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
+
+       device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+       io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
        device_issue_read();
 }
 
@@ -210,10 +216,22 @@ static void disable_device(void) {
 
        io_del(&device_read_io);
        CancelIo(device_handle);
+
+       /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
+          To prevent race conditions, make sure the operation is complete
+          before we close the event it's referencing. */
+
+       DWORD len;
+       if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
+       if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
+       device_write_packet.len = 0;
+
        CloseHandle(device_read_overlapped.hEvent);
+       CloseHandle(device_write_overlapped.hEvent);
 
        ULONG status = 0;
-       DWORD len;
        DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
 }
 
@@ -231,12 +249,29 @@ static bool read_packet(vpn_packet_t *packet) {
 
 static bool write_packet(vpn_packet_t *packet) {
        DWORD outlen;
-       OVERLAPPED overlapped = {0};
 
        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(!WriteFile(device_handle, DATA(packet), packet->len, &outlen, &overlapped)) {
+       if(device_write_packet.len > 0) {
+               /* Make sure the previous write operation is finished before we start the next one;
+                  otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
+                  which according to MSDN is a no-no. */
+
+               if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
+                       int log_level = (GetLastError() == ERROR_IO_INCOMPLETE) ? DEBUG_TRAFFIC : DEBUG_ALWAYS;
+                       logger(log_level, LOG_ERR, "Error while checking previous write to %s %s: %s", device_info, device, winerror(GetLastError()));
+                       return false;
+               }
+       }
+
+       /* Copy the packet, since the write operation might still be ongoing after we return. */
+
+       memcpy(&device_write_packet, packet, sizeof *packet);
+
+       if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped))
+               device_write_packet.len = 0;
+       else if (GetLastError() != ERROR_IO_PENDING) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
index cfc44d2..1b0294b 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -143,6 +143,9 @@ extern int udp_discovery_keepalive_interval;
 extern int udp_discovery_interval;
 extern int udp_discovery_timeout;
 
+extern int mtu_info_interval;
+extern int udp_info_interval;
+
 extern listen_socket_t listen_socket[MAXSOCKETS];
 extern int listen_sockets;
 extern io_t unix_socket;
index b05dd16..3284524 100644 (file)
@@ -687,9 +687,7 @@ 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;
 
-       /* 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. */
+       /* 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)) {
                char buf[len * 4 / 3 + 5];
@@ -1137,7 +1135,7 @@ static void try_tx_sptps(node_t *n, bool mtu) {
        /* If we do have a static relay, try everything with that one instead. */
 
        if(via != n)
-               try_tx_sptps(via, mtu);
+               return try_tx_sptps(via, mtu);
 
        /* Otherwise, try to establish UDP connectivity. */
 
@@ -1394,6 +1392,17 @@ skip_harder:
                        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.
+                  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(n != from->via && to->via == myself)
+                       send_udp_info(myself, from);
+
+               /* 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);
@@ -1411,6 +1420,12 @@ skip_harder:
        n->sock = ls - listen_socket;
        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. */
+
+       if(!direct)
+               send_mtu_info(myself, n, MTU);
 }
 
 void handle_device_data(void *data, int flags) {
index fee4159..c4f01b5 100644 (file)
@@ -518,6 +518,9 @@ bool setup_myself_reloadable(void) {
        get_config_int(lookup_config(config_tree, "UDPDiscoveryInterval"), &udp_discovery_interval);
        get_config_int(lookup_config(config_tree, "UDPDiscoveryTimeout"), &udp_discovery_timeout);
 
+       get_config_int(lookup_config(config_tree, "MTUInfoInterval"), &mtu_info_interval);
+       get_config_int(lookup_config(config_tree, "UDPInfoInterval"), &udp_info_interval);
+
        get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
        get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
 
@@ -664,7 +667,7 @@ static bool add_listen_address(char *address, bool bindto) {
        hint.ai_protocol = IPPROTO_TCP;
        hint.ai_flags = AI_PASSIVE;
 
-#ifdef HAVE_DECL_RES_INIT
+#if HAVE_DECL_RES_INIT
        res_init();
 #endif
        int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
index 3ea5d4a..bff734e 100644 (file)
@@ -39,7 +39,7 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
        hint.ai_family = addressfamily;
        hint.ai_socktype = socktype;
 
-#ifdef HAVE_DECL_RES_INIT
+#if HAVE_DECL_RES_INIT
        res_init();
 #endif
        err = getaddrinfo(address, service, &hint, &ai);
index 5fe6dfa..0800337 100644 (file)
@@ -95,6 +95,9 @@ typedef struct node_t {
 
        struct timeval mtu_ping_sent;           /* Last time a MTU probe was sent */
 
+       struct timeval mtu_info_sent;           /* Last time a MTU_INFO message was sent */
+       struct timeval udp_info_sent;           /* Last time a UDP_INFO message was sent */
+
        length_t maxrecentlen;                  /* Maximum size of recently received packets */
 
        length_t mtu;                           /* Maximum size of packets to send to this node */
index 1ec169a..4f7e669 100644 (file)
@@ -41,6 +41,8 @@ static bool (*request_handlers[])(connection_t *, const char *) = {
                add_subnet_h, del_subnet_h,
                add_edge_h, del_edge_h,
                key_changed_h, req_key_h, ans_key_h, tcppacket_h, control_h,
+               NULL, NULL, NULL, /* Not "real" requests (yet) */
+               udp_info_h, mtu_info_h,
 };
 
 /* Request names */
@@ -51,6 +53,7 @@ static char (*request_name[]) = {
                "PING", "PONG",
                "ADD_SUBNET", "DEL_SUBNET",
                "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", "CONTROL",
+               "REQ_PUBKEY", "ANS_PUBKEY", "REQ_SPTPS", "UDP_INFO", "MTU_INFO",
 };
 
 static splay_tree_t *past_request_tree;
index 080d50c..c6a1485 100644 (file)
@@ -26,7 +26,7 @@
 /* Protocol version. Different major versions are incompatible. */
 
 #define PROT_MAJOR 17
-#define PROT_MINOR 4 /* Should not exceed 255! */
+#define PROT_MINOR 6 /* Should not exceed 255! */
 
 /* Silly Windows */
 
@@ -49,6 +49,7 @@ typedef enum request_t {
        CONTROL,
        REQ_PUBKEY, ANS_PUBKEY,
        REQ_SPTPS,
+       UDP_INFO, MTU_INFO,
        LAST                                            /* Guardian for the highest request number */
 } request_t;
 
@@ -107,6 +108,8 @@ extern void send_key_changed(void);
 extern bool send_req_key(struct node_t *);
 extern bool send_ans_key(struct node_t *);
 extern bool send_tcppacket(struct connection_t *, const struct vpn_packet_t *);
+extern bool send_udp_info(struct node_t *, struct node_t *);
+extern bool send_mtu_info(struct node_t *, struct node_t *, int);
 
 /* Request handlers  */
 
@@ -129,5 +132,7 @@ extern bool req_key_h(struct connection_t *, const char *);
 extern bool ans_key_h(struct connection_t *, const char *);
 extern bool tcppacket_h(struct connection_t *, const char *);
 extern bool control_h(struct connection_t *, const char *);
+extern bool udp_info_h(struct connection_t *, const char *);
+extern bool mtu_info_h(struct connection_t *, const char *);
 
 #endif /* __TINC_PROTOCOL_H__ */
index 8cbec1b..8a12b56 100644 (file)
@@ -178,6 +178,7 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
                        from->last_req_key = now.tv_sec;
                        sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record);
                        sptps_receive_data(&from->sptps, buf, len);
+                       send_mtu_info(myself, from, MTU);
                        return true;
                }
 
@@ -194,6 +195,7 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
                                return true;
                        }
                        sptps_receive_data(&from->sptps, buf, len);
+                       send_mtu_info(myself, from, MTU);
                        return true;
                }
 
@@ -236,6 +238,13 @@ bool req_key_h(connection_t *c, const char *request) {
                return true;
        }
 
+       /* If this is a SPTPS packet, see if sending UDP info helps.
+          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(experimental && (reqno == REQ_KEY || reqno == REQ_SPTPS) && to->via == myself)
+               send_udp_info(myself, from);
+
        /* Check if this key request is for us */
 
        if(to == myself) {                      /* Yes */
@@ -408,6 +417,8 @@ bool ans_key_h(connection_t *c, const char *request) {
                        }
                }
 
+               send_mtu_info(myself, from, MTU);
+
                return true;
        }
 
index 713dacf..6fedc08 100644 (file)
 #include "netutl.h"
 #include "protocol.h"
 #include "utils.h"
+#include "xalloc.h"
 
 int maxoutbufsize = 0;
+int mtu_info_interval = 5;
+int udp_info_interval = 5;
 
 /* Status and error notification routines */
 
@@ -149,3 +152,199 @@ bool tcppacket_h(connection_t *c, const char *request) {
 
        return true;
 }
+
+/* Transmitting UDP information */
+
+bool send_udp_info(node_t *from, node_t *to) {
+       /* If there's a static relay in the path, there's no point in sending the message
+          farther than the static relay. */
+       to = (to->via == myself) ? to->nexthop : to->via;
+
+       /* Skip cases where sending UDP info messages doesn't make sense.
+          This is done here in order to avoid repeating the same logic in multiple callsites. */
+
+       if(to == myself)
+               return true;
+
+       if(!to->status.reachable)
+               return true;
+
+       if(from == myself) {
+               if(to->connection)
+                       return true;
+
+               struct timeval elapsed;
+               timersub(&now, &to->udp_info_sent, &elapsed);
+               if(elapsed.tv_sec < udp_info_interval)
+                       return true;
+       }
+
+       if((myself->options | from->options | to->options) & OPTION_TCPONLY)
+               return true;
+
+       if((to->nexthop->options >> 24) < 5)
+               return true;
+
+       char *from_address, *from_port;
+       /* If we're the originator, the address we use is irrelevant
+          because the first intermediate node will ignore it.
+          We use our local address as it somewhat makes sense
+          and it's simpler than introducing an encoding for "null" addresses anyway. */
+       sockaddr2str((from != myself) ? &from->address : &to->nexthop->connection->edge->local_address, &from_address, &from_port);
+
+       bool x = send_request(to->nexthop->connection, "%d %s %s %s %s", UDP_INFO, from->name, to->name, from_address, from_port);
+
+       free(from_address);
+       free(from_port);
+
+       if(from == myself)
+               to->udp_info_sent = now;
+
+       return x;
+}
+
+bool udp_info_h(connection_t *c, const char* request) {
+       char from_name[MAX_STRING_SIZE];
+       char to_name[MAX_STRING_SIZE];
+       char from_address[MAX_STRING_SIZE];
+       char from_port[MAX_STRING_SIZE];
+
+       if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING, from_name, to_name, from_address, from_port) != 4) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "UDP_INFO", c->name, c->hostname);
+               return false;
+       }
+
+       if(!check_id(from_name) || !check_id(to_name)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "UDP_INFO", c->name, c->hostname, "invalid name");
+               return false;
+       }
+
+       node_t *from = lookup_node(from_name);
+       if(!from) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list", "UDP_INFO", c->name, c->hostname, from_name);
+               return true;
+       }
+
+       if(from != from->via) {
+               /* Not supposed to happen, as it means the message wandered past a static relay */
+               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got UDP info message from %s (%s) which we can't reach directly", from->name, from->hostname);
+               return true;
+       }
+
+       /* If we have a direct edge to "from", we are in a better position
+          to guess its address than it is itself. */
+       if(!from->connection && !from->status.udp_confirmed) {
+               sockaddr_t from_addr = str2sockaddr(from_address, from_port);
+               if(sockaddrcmp(&from_addr, &from->address))
+                       update_node_udp(from, &from_addr);
+       }
+
+       node_t *to = lookup_node(to_name);
+       if(!to) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list", "UDP_INFO", c->name, c->hostname, to_name);
+               return true;
+       }
+
+       /* Send our own data (which could be what we just received) up the chain. */
+
+       return send_udp_info(from, to);
+}
+
+/* Transmitting MTU information */
+
+bool send_mtu_info(node_t *from, node_t *to, int mtu) {
+       /* Skip cases where sending MTU info messages doesn't make sense.
+          This is done here in order to avoid repeating the same logic in multiple callsites. */
+
+       if(to == myself)
+               return true;
+
+       if(!to->status.reachable)
+               return true;
+
+       if(from == myself) {
+               if(to->connection)
+                       return true;
+
+               struct timeval elapsed;
+               timersub(&now, &to->mtu_info_sent, &elapsed);
+               if(elapsed.tv_sec < mtu_info_interval)
+                       return true;
+       }
+
+       if((to->nexthop->options >> 24) < 6)
+               return true;
+
+       /* We will send the passed-in MTU value, unless we believe ours is better. */
+
+       node_t *via = (from->via == myself) ? from->nexthop : from->via;
+       if(from->minmtu == from->maxmtu && from->via == myself) {
+               /* We have a direct measurement. Override the value entirely.
+                  Note that we only do that if we are sitting as a static relay in the path;
+                  otherwise, we can't guarantee packets will flow through us, and increasing
+                  MTU could therefore end up being too optimistic. */
+               mtu = from->minmtu;
+       } else if(via->minmtu == via->maxmtu) {
+               /* Static relay. Ensure packets will make it through the entire relay path. */
+               mtu = MIN(mtu, via->minmtu);
+       } else if(via->nexthop->minmtu == via->nexthop->maxmtu) {
+               /* Dynamic relay. Ensure packets will make it through the entire relay path. */
+               mtu = MIN(mtu, via->nexthop->minmtu);
+       }
+
+       if(from == myself)
+               to->mtu_info_sent = now;
+
+       /* If none of the conditions above match in the steady state, it means we're using TCP,
+          so the MTU is irrelevant. That said, it is still important to honor the MTU that was passed in,
+          because other parts of the relay path might be able to use UDP, which means they care about the MTU. */
+
+       return send_request(to->nexthop->connection, "%d %s %s %d", MTU_INFO, from->name, to->name, mtu);
+}
+
+bool mtu_info_h(connection_t *c, const char* request) {
+       char from_name[MAX_STRING_SIZE];
+       char to_name[MAX_STRING_SIZE];
+       int mtu;
+
+       if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" %d", from_name, to_name, &mtu) != 3) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "MTU_INFO", c->name, c->hostname);
+               return false;
+       }
+
+       if(mtu < 512) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "MTU_INFO", c->name, c->hostname, "invalid MTU");
+               return false;
+       }
+
+       mtu = MIN(mtu, MTU);
+
+       if(!check_id(from_name) || !check_id(to_name)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "MTU_INFO", c->name, c->hostname, "invalid name");
+               return false;
+       }
+
+       node_t *from = lookup_node(from_name);
+       if(!from) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list", "MTU_INFO", c->name, c->hostname, from_name);
+               return true;
+       }
+
+       /* If we don't know the current MTU for that node, use the one we received.
+          Even if we're about to make our own measurements, the value we got from downstream nodes should be pretty close
+          so it's a good idea to use it in the mean time. */
+       if(from->mtu != mtu && from->minmtu != from->maxmtu) {
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Using provisional MTU %d for node %s (%s)", mtu, from->name, from->hostname);
+               from->mtu = mtu;
+       }
+
+       node_t *to = lookup_node(to_name);
+       if(!to) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list", "MTU_INFO", c->name, c->hostname, to_name);
+               return true;
+       }
+
+       /* Continue passing the MTU value (or a better one if we have it) up the chain. */
+
+       return send_mtu_info(from, to, mtu);
+}
index b36de76..91f63ef 100644 (file)
@@ -1371,6 +1371,8 @@ const var_t variables[] = {
        {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
        {"UDPDiscoveryInterval", VAR_SERVER},
        {"UDPDiscoveryTimeout", VAR_SERVER},
+       {"MTUInfoInterval", VAR_SERVER},
+       {"UDPInfoInterval", VAR_SERVER},
        {"UDPRcvBuf", VAR_SERVER},
        {"UDPSndBuf", VAR_SERVER},
        {"VDEGroup", VAR_SERVER},