-int setup_vpn_in_socket(int port)
-{
- int nfd, flags;
- struct sockaddr_in a;
- const int one = 1;
-cp
- if((nfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- {
- close(nfd);
- syslog(LOG_ERR, _("Creating socket failed: %m"));
- return -1;
- }
-
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-
- flags = fcntl(nfd, F_GETFL);
- if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
- {
- close(nfd);
- syslog(LOG_ERR, _("System call `%s' failed: %m"),
- "fcntl");
- return -1;
- }
-
- memset(&a, 0, sizeof(a));
- a.sin_family = AF_INET;
- a.sin_port = htons(port);
- a.sin_addr.s_addr = htonl(INADDR_ANY);
-
- if(bind(nfd, (struct sockaddr *)&a, sizeof(struct sockaddr)))
- {
- close(nfd);
- syslog(LOG_ERR, _("Can't bind to port %hd/udp: %m"), port);
- return -1;
- }
-cp
- return nfd;
-}
-
-int setup_outgoing_socket(connection_t *c)
-{
- int flags;
- struct sockaddr_in a;
- int option;
-cp
- if(debug_lvl >= DEBUG_CONNECTIONS)
- syslog(LOG_INFO, _("Trying to connect to %s (%s)"), c->name, c->hostname);
-
- c->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+/*
+ Check if the other end is active.
+ If we have sent packets, but didn't receive any,
+ then possibly the other end is dead. We send a
+ PING request over the meta connection. If the other
+ end does not reply in time, we consider them dead
+ and close the connection.
+*/
+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);
+ }