Merge branch '1.1' into thkr-1.1-ponyhof

This commit is contained in:
thorkill 2015-04-16 10:44:01 +02:00
commit 0fc873a161
19 changed files with 359 additions and 41 deletions

View 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

View file

@ -470,10 +470,10 @@ and
.Ev REMOTEPORT
are available.
.El
.It Va ReplayWindow Li = Ar bytes Pq 16
.It Va ReplayWindow Li = Ar bytes Pq 32
This is the size of the replay tracking window for each remote node, in bytes.
The window is a bitfield which tracks 1 packet per bit, so for example
the default setting of 16 will track up to 128 packets in the window. In high
the default setting of 32 will track up to 256 packets in the window. In high
bandwidth scenarios, setting this to a higher value can reduce packet loss from
the interaction of replay tracking with underlying real packet loss and/or
reordering. Setting this to zero will disable replay tracking completely and
@ -506,12 +506,16 @@ 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 UDPRcvBuf Li = Ar bytes Pq OS default
.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 unset, the default buffer size will be used by the operating system.
.It Va UDPSndBuf Li = Ar bytes Pq OS default
If set to zero, the default buffer size will be used by the operating system.
Note: this setting can have a significant impact on performance, especially raw throughput.
.It Va UDPSndBuf Li = Ar bytes Pq 1048576
Sets the socket send buffer size for the UDP socket, in bytes.
If unset, the default buffer size will be used by the operating system.
If set to zero, the default buffer size will be used by the operating system.
Note: this setting can have a significant impact on performance, especially raw throughput.
.El
.Sh HOST CONFIGURATION FILES
The host configuration files contain all information needed
@ -575,6 +579,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

View file

@ -1207,10 +1207,10 @@ The environment variables @env{NAME}, @env{NODE}, @env{REMOTEADDRES} and @env{RE
@end table
@cindex ReplayWindow
@item ReplayWindow = <bytes> (16)
@item ReplayWindow = <bytes> (32)
This is the size of the replay tracking window for each remote node, in bytes.
The window is a bitfield which tracks 1 packet per bit, so for example
the default setting of 16 will track up to 128 packets in the window. In high
the default setting of 32 will track up to 256 packets in the window. In high
bandwidth scenarios, setting this to a higher value can reduce packet loss from
the interaction of replay tracking with underlying real packet loss and/or
reordering. Setting this to zero will disable replay tracking completely and
@ -1253,15 +1253,21 @@ 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> (OS default)
@item UDPRcvBuf = <bytes> (1048576)
Sets the socket receive buffer size for the UDP socket, in bytes.
If unset, the default buffer size will be used by the operating system.
If set to zero, the default buffer size will be used by the operating system.
Note: this setting can have a significant impact on performance, especially raw throughput.
@cindex UDPSndBuf
@item UDPSndBuf = <bytes> Pq OS default
@item UDPSndBuf = <bytes> (1048576)
Sets the socket send buffer size for the UDP socket, in bytes.
If unset, the default buffer size will be used by the operating system.
If set to zero, the default buffer size will be used by the operating system.
Note: this setting can have a significant impact on performance, especially raw throughput.
@end table
@ -1329,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.

View 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

View 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.

View 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__ */

View 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__ */

View 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;
@ -175,6 +177,25 @@ static bool setup_device(void) {
return false;
}
/* Get version information from tap device */
{
ULONG info[3] = {0};
DWORD len;
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof info, &info, sizeof info, &len, NULL))
logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
else {
logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
/* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
if(info[0] == 9 && info[1] >= 21)
logger(DEBUG_ALWAYS, LOG_WARNING,
"You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
"Using these drivers with tinc is not recommanded as it can result in poor performance. "
"You might want to revert back to 9.0.0.9 instead.");
}
}
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
@ -200,8 +221,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 +235,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 +268,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;
}

View file

@ -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;

View file

@ -65,7 +65,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;
@ -688,9 +688,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];
@ -1138,7 +1136,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. */
@ -1395,6 +1393,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);
@ -1412,6 +1421,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) {

View file

@ -547,6 +547,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);
@ -696,7 +699,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);
@ -886,14 +889,14 @@ static bool setup_myself(void) {
}
if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
if(udp_rcvbuf <= 0) {
if(udp_rcvbuf < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "UDPRcvBuf cannot be negative!");
return false;
}
}
if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
if(udp_sndbuf <= 0) {
if(udp_sndbuf < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "UDPSndBuf cannot be negative!");
return false;
}

View file

@ -43,8 +43,8 @@
int addressfamily = AF_UNSPEC;
int maxtimeout = 900;
int seconds_till_retry = 5;
int udp_rcvbuf = 0;
int udp_sndbuf = 0;
int udp_rcvbuf = 1024 * 1024;
int udp_sndbuf = 1024 * 1024;
int max_connection_burst = 100;
listen_socket_t listen_socket[MAXSOCKETS];

View 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);

View 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 */

View 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;

View 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;
@ -108,6 +109,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 */
@ -130,5 +133,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__ */

View 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;
}

View file

@ -28,8 +28,11 @@
#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 */
@ -191,3 +194,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);
}

View 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},