diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 5734b485..820b6bce 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1697,23 +1697,45 @@ openssl dhparam -out dhparam.pem 2048 mqttv311. - idle interval counter - - Set TCP keepalive parameters for this bridge connection. - Use this option to allow you to set a long MQTT - keepalive value, whilst still being able to detect the - connection dropping in a reasonable time. + + Set TCP keepalive parameters for this bridge connection. + Use this option to allow you to set a long MQTT + keepalive value, whilst still being able to detect the + connection dropping in a reasonable time. - This may be useful when bridging to services that bill - for PINGREQ messages. + This may be useful when bridging to services that bill + for PINGREQ messages. Disabled by default. + + timeout + + + It specifies the maximum amount of time (in milliseconds) that + transmitted data may remain unacknowledged at TCP level. + Popular Linux distributions seem to set this time to "up to 20 + minutes with system defaults" (from Linux tcp man page). + The default time is related to tcp_retries2. + Reducing this value helps detecting dropped connections faster. + + + Be aware that when used in combination with TCP Keepalive, it might + change the latter's behavior. + + + You can find more details about this setting at: + + + Only available on Linux. + + + [ true | false ] diff --git a/mosquitto.conf b/mosquitto.conf index 99282299..93814433 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -723,6 +723,16 @@ # Disabled by default. #bridge_tcp_keepalive +# Change the maximum amount of time (in milliseconds) that transmitted data +# may remain unacknowledged at TCP level. +# Popular Linux distributions seem to set this time to "up to 20 minutes with +# system defaults" (from Linux tcp man page). Reducing this value helps +# detecting dropped connections faster. +# When used together with TCP Keepalive, it might change it's behavior. +# More details at: https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/ +# Only available on Linux. +#bridge_tcp_user_timeout + # Set the clientid to use on the local broker. If not defined, this defaults to # 'local.'. If you are bridging a broker to itself, it is important # that local_clientid and clientid do not match. diff --git a/src/bridge.c b/src/bridge.c index 3cfd7cc2..a6cd3fb1 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -177,7 +177,7 @@ static int bridge__set_tcp_keepalive(struct mosquitto *context) unsigned int enabled = 1; bool ret; - if (idle == 0 || interval == 0 || counter == 0) return MOSQ_ERR_SUCCESS; + if(idle == 0 || interval == 0 || counter == 0) return MOSQ_ERR_SUCCESS; #ifdef WIN32 ret = @@ -193,11 +193,24 @@ static int bridge__set_tcp_keepalive(struct mosquitto *context) setsockopt(context->sock, IPPROTO_TCP, TCP_KEEPCNT, (const void*)&counter, sizeof(counter)); #endif - if (ret) return MOSQ_ERR_UNKNOWN; + if(ret) return MOSQ_ERR_UNKNOWN; return MOSQ_ERR_SUCCESS; } +#ifdef WITH_TCP_USER_TIMEOUT +static int bridge__set_tcp_user_timeout(struct mosquitto *context) { + int timeout = context->bridge->tcp_user_timeout; + if(timeout >= 0) { + if(setsockopt(context->sock, IPPROTO_TCP, TCP_USER_TIMEOUT, (char *)&timeout, sizeof(timeout))) { + return MOSQ_ERR_UNKNOWN; + } + } + + return MOSQ_ERR_SUCCESS; +} +#endif + #if defined(__GLIBC__) && defined(WITH_ADNS) static int bridge__connect_step1(struct mosquitto *context) { @@ -373,6 +386,9 @@ int bridge__connect_step3(struct mosquitto *context) } if (bridge__set_tcp_keepalive(context) != MOSQ_ERR_SUCCESS) return MOSQ_ERR_UNKNOWN; +#ifdef WITH_TCP_USER_TIMEOUT + if(bridge__set_tcp_user_timeout(context)) return MOSQ_ERR_UNKNOWN; +#endif if(context->bridge->max_topic_alias != 0){ topic_alias_max.next = NULL; @@ -526,6 +542,9 @@ int bridge__connect(struct mosquitto *context) HASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context); if (bridge__set_tcp_keepalive(context) != MOSQ_ERR_SUCCESS) return MOSQ_ERR_UNKNOWN; +#ifdef WITH_TCP_USER_TIMEOUT + if(bridge__set_tcp_user_timeout(context)) return MOSQ_ERR_UNKNOWN; +#endif if(context->bridge->max_topic_alias){ topic_alias_max.next = NULL; diff --git a/src/conf.c b/src/conf.c index 307922ff..7bf23c9a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1273,6 +1273,22 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload, cur_bridge->tcp_keepalive_counter = (unsigned int)tmp_int; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); +#endif + }else if(!strcmp(token, "bridge_tcp_user_timeout")){ +#if defined(WITH_BRIDGE) && defined(WITH_TCP_USER_TIMEOUT) + if(!cur_bridge){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); + return MOSQ_ERR_INVAL; + } + + if(conf__parse_int(&token, "bridge_tcp_user_timeout", &tmp_int, &saveptr)) return MOSQ_ERR_INVAL; + if(tmp_int < 0) { + log__printf(NULL, MOSQ_LOG_ERR, "Error: invalid TCP user timeout value."); + return MOSQ_ERR_INVAL; + } + cur_bridge->tcp_user_timeout = (unsigned int) tmp_int; +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TCP user timeout support not available."); #endif }else if(!strcmp(token, "bridge_tls_version")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) @@ -1416,6 +1432,9 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload, cur_bridge->clean_start_local = -1; cur_bridge->reload_type = brt_lazy; cur_bridge->max_topic_alias = 10; +#ifdef WITH_TCP_USER_TIMEOUT + cur_bridge->tcp_user_timeout = -1; +#endif }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty connection value in configuration."); return MOSQ_ERR_INVAL; diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 3194a104..a40ade57 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -30,6 +30,10 @@ Contributors: # endif #endif +#ifdef __linux__ +#define WITH_TCP_USER_TIMEOUT +#endif + #include "mosquitto_internal.h" #include "mosquitto_broker.h" #include "mosquitto_plugin.h" @@ -533,6 +537,9 @@ struct mosquitto__bridge{ unsigned int tcp_keepalive_idle; unsigned int tcp_keepalive_interval; unsigned int tcp_keepalive_counter; +#ifdef WITH_TCP_USER_TIMEOUT + unsigned int tcp_user_timeout; +#endif struct mosquitto__bridge_topic *topics; int topic_count; bool topic_remapping;