From d8090afbfbcba96582dadae0eafa6d9f93843779 Mon Sep 17 00:00:00 2001 From: Abilio Marques Date: Mon, 14 Jun 2021 17:30:16 +0200 Subject: [PATCH] bridge: add support for TCP_USER_TIMEOUT Signed-off-by: Abilio Marques --- man/mosquitto.conf.5.xml | 25 ++++++++++++++++++++++++- mosquitto.conf | 10 ++++++++++ src/bridge.c | 23 +++++++++++++++++++++-- src/conf.c | 19 +++++++++++++++++++ src/mosquitto_broker_internal.h | 7 +++++++ 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 2fc95356..4084027c 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1645,10 +1645,10 @@ 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 @@ -1662,6 +1662,29 @@ openssl dhparam -out dhparam.pem 2048 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 51c72fd3..8b9cf8c8 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -701,6 +701,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 fdeb012c..7b260d76 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -176,7 +176,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 = @@ -192,11 +192,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) { @@ -367,6 +380,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; @@ -517,6 +533,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 78d7b898..c1fee7c4 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1255,6 +1255,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) @@ -1398,6 +1414,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 f8c179c0..aa638aca 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" @@ -530,6 +534,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;