+static void timeout_handler(void *data) {
+
+ bool close_all_connections = false;
+
+ /*
+ timeout_handler will start after 30 seconds from start of tincd
+ hold information about the elapsed time since last time the handler
+ has been run
+ */
+ long sleep_time = now.tv_sec - last_periodic_run_time.tv_sec;
+ /*
+ It seems that finding sane default value is harder than expected
+ Since we send every second a UDP packet to make holepunching work
+ And default UDP state expire on firewalls is between 15-30 seconds
+ we drop all connections after 60 Seconds - UDPDiscoveryTimeout=30
+ by default
+ */
+ if (sleep_time > 2 * udp_discovery_timeout) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Awaking from dead after %ld seconds of sleep", sleep_time);
+ /*
+ Do not send any packets to tinc after we wake up.
+ The other node probably closed our connection but we still
+ are holding context information to them. This may happen on
+ laptops or any other hardware which can be suspended for some time.
+ Sending any data to node that wasn't expecting it will produce
+ annoying and misleading errors on the other side about failed signature
+ verification and or about missing sptps context
+ */
+ close_all_connections = true;
+ }
+ last_periodic_run_time = now;
+
+ for list_each(connection_t, c, connection_list) {
+ // control connections (eg. tinc ctl) do not have any timeout
+ if(c->status.control)
+ continue;
+
+ if(close_all_connections) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Forcing connection close after sleep time %s (%s)", c->name, c->hostname);
+ terminate_connection(c, c->edge);
+ continue;
+ }
+
+ // Bail out early if we haven't reached the ping timeout for this node yet
+ if(c->last_ping_time + pingtimeout > now.tv_sec)
+ continue;
+
+ // timeout during connection establishing
+ if(!c->edge) {
+ if(c->status.connecting)
+ logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
+ else
+ logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
+
+ terminate_connection(c, c->edge);
+ continue;
+ }
+
+ // helps in UDP holepunching
+ try_tx(c->node, false);
+
+ // timeout during ping
+ if(c->status.pinged) {
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time);
+ terminate_connection(c, c->edge);
+ continue;
+ }
+
+ // check whether we need to send a new ping
+ if(c->last_ping_time + pinginterval <= now.tv_sec)
+ send_ping(c);
+ }
+
+ timeout_set(data, &(struct timeval){1, rand() % 100000});
+}
+
+static void periodic_handler(void *data) {
+ /* Check if there are too many contradicting ADD_EDGE and DEL_EDGE messages.
+ This usually only happens when another node has the same Name as this node.
+ If so, sleep for a short while to prevent a storm of contradicting messages.
+ */
+
+ if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
+ nanosleep(&(struct timespec){sleeptime, 0}, NULL);
+ sleeptime *= 2;
+ if(sleeptime < 0)
+ sleeptime = 3600;
+ } else {
+ sleeptime /= 2;
+ if(sleeptime < 10)
+ sleeptime = 10;
+ }
+
+ contradicting_add_edge = 0;
+ contradicting_del_edge = 0;
+
+ /* If AutoConnect is set, check if we need to make or break connections. */
+
+ if(autoconnect && node_tree->count > 1) {
+ /* Count number of active connections */
+ int nc = 0;
+ for list_each(connection_t, c, connection_list) {
+ if(c->edge)
+ nc++;
+ }
+
+ if(nc < 3) {
+ /* Not enough active connections, try to add one.
+ Choose a random node, if we don't have a connection to it,
+ and we are not already trying to make one, create an
+ outgoing connection to this node.
+ */
+ int count = 0;
+ for splay_each(node_t, n, node_tree) {
+ if(n == myself || n->connection || !(n->status.has_address || n->status.reachable))
+ continue;
+ count++;
+ }
+
+ if(!count)
+ goto end;
+
+ int r = rand() % count;
+
+ for splay_each(node_t, n, node_tree) {
+ if(n == myself || n->connection || !(n->status.has_address || n->status.reachable))