From 6ef2c79e9abb0bffc8aeb5298c251cebab2b78f8 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 6 Dec 2018 08:49:02 +0000 Subject: [PATCH] Add max_keepalive, for limiting keepalives of MQTT v5 clients. --- ChangeLog.txt | 2 ++ lib/handle_connack.c | 10 ++++++ man/mosquitto.conf.5.xml | 19 +++++++++++ mosquitto.conf | 10 ++++++ src/conf.c | 8 +++++ src/handle_connect.c | 10 ++++-- src/mosquitto_broker_internal.h | 1 + test/broker/12-prop-server-keepalive.py | 42 +++++++++++++++++++++++++ test/broker/Makefile | 1 + test/broker/ptest.py | 1 + 10 files changed, 102 insertions(+), 2 deletions(-) create mode 100755 test/broker/12-prop-server-keepalive.py diff --git a/ChangeLog.txt b/ChangeLog.txt index e2dd2e3a..27be9d46 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,6 +5,8 @@ Broker features: - Improved general support for broker generated client ids. Removed libuuid dependency. - auto_id_prefix now defaults to 'auto-'. +- Add max_keepalive option, to allow a maximum keepalive value to be set for + MQTT v5 clients only. Client library features: - Add mosquitto_subscribe_multiple() for sending subscriptions to multiple diff --git a/lib/handle_connack.c b/lib/handle_connack.c index 98adae1d..7cd65d4e 100644 --- a/lib/handle_connack.c +++ b/lib/handle_connack.c @@ -62,6 +62,16 @@ int handle__connack(struct mosquitto *mosq) } } } + + prop = mosquitto_property_get_property(properties, MQTT_PROP_SERVER_KEEP_ALIVE, false); + if(prop){ + rc = mosquitto_property_read_int16(prop, &mosq->keepalive); + if(rc){ + mosquitto_property_free_all(&properties); + return rc; + } + } + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, reason_code); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_connect){ diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index ee240762..dbfce513 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -435,6 +435,25 @@ Reloaded on reload signal. + + value + + For MQTT v5 clients, it is possible to have the + server send a "server keepalive" value that will + override the keepalive value set by the client. This + is intended to be used as a mechanism to say that the + server will disconnect the client earlier than it + anticipated, and that the client should use the new + keepalive value. The max_keepalive option allows you to + specify that clients may only connect with keepalive + less than or equal to this value, otherwise they will + be sent a server keepalive telling them to use + max_keepalive. This only applies to MQTT v5 clients. + The maximum value allowable, and default value, is + 65535. Do not set below 10 seconds. + Reloaded on reload signal. + + count diff --git a/mosquitto.conf b/mosquitto.conf index d9456a68..cf24e2ed 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -155,6 +155,16 @@ # false. #retain_available true +# For MQTT v5 clients, it is possible to have the server send a "server +# keepalive" value that will override the keepalive value set by the client. +# This is intended to be used as a mechanism to say that the server will +# disconnect the client earlier than it anticipated, and that the client should +# use the new keepalive value. The max_keepalive option allows you to specify +# that clients may only connect with keepalive less than or equal to this +# value, otherwise they will be sent a server keepalive telling them to use +# max_keepalive. This only applies to MQTT v5 clients. The maximum value +# allowable is 65535. Do not set below 10. +#max_keepalive 65535 # ================================================================= # Default listener diff --git a/src/conf.c b/src/conf.c index 68da9929..9053f228 100644 --- a/src/conf.c +++ b/src/conf.c @@ -212,6 +212,7 @@ static void config__init_reload(struct mosquitto_db *db, struct mosquitto__confi } #endif config->log_timestamp = true; + config->max_keepalive = 65535; config->persistence = false; mosquitto__free(config->persistence_location); config->persistence_location = NULL; @@ -1513,6 +1514,13 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_inflight_messages value in configuration."); } + }else if(!strcmp(token, "max_keepalive")){ + if(conf__parse_int(&token, "max_keepalive", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; + if(tmp_int < 10 || tmp_int > 65535){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid max_keepalive value (%d).", tmp_int); + return MOSQ_ERR_INVAL; + } + config->max_keepalive = tmp_int; }else if(!strcmp(token, "max_queued_bytes")){ token = strtok_r(NULL, " ", &saveptr); if(token){ diff --git a/src/handle_connect.c b/src/handle_connect.c index 0c92d6d3..c1634eb7 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -297,11 +297,17 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) rc = 1; goto handle_connect_error; } + if(protocol_version == PROTOCOL_VERSION_v5 && context->keepalive > db->config->max_keepalive){ + context->keepalive = db->config->max_keepalive; + if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_SERVER_KEEP_ALIVE, context->keepalive)){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + } + } if(protocol_version == PROTOCOL_VERSION_v5){ rc = property__read_all(CMD_CONNECT, &context->in_packet, &properties); - if(rc) return rc; - mosquitto_property_free_all(&properties); + if(rc) goto handle_connect_error; } property__process_connect(context, properties); diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 7a1b6bd7..cc0a5edd 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -259,6 +259,7 @@ struct mosquitto__config { bool log_timestamp; char *log_file; FILE *log_fptr; + uint16_t max_keepalive; uint32_t message_size_limit; bool persistence; char *persistence_location; diff --git a/test/broker/12-prop-server-keepalive.py b/test/broker/12-prop-server-keepalive.py new file mode 100755 index 00000000..5073f28c --- /dev/null +++ b/test/broker/12-prop-server-keepalive.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# Test whether sending a non zero session expiry interval in DISCONNECT after +# having sent a zero session expiry interval is treated correctly in MQTT v5. + +from mosq_test_helper import * + +def write_config(filename, port): + with open(filename, 'w') as f: + f.write("port %d\n" % (port)) + f.write("\n") + f.write("max_keepalive 60\n") + +port = mosq_test.get_port(1) +conf_file = os.path.basename(__file__).replace('.py', '.conf') +write_config(conf_file, port) + + +rc = 1 + +keepalive = 61 +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_SERVER_KEEP_ALIVE, 60) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + sock.close() + rc = 0 +finally: + os.remove(conf_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/Makefile b/test/broker/Makefile index db0b971a..0b9bf169 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -140,3 +140,4 @@ endif ./12-prop-subpub-payload-format.py ./12-prop-subpub-content-type.py ./12-prop-assigned-client-identifier.py + ./12-prop-server-keepalive.py diff --git a/test/broker/ptest.py b/test/broker/ptest.py index ff2984fd..a9772aa5 100755 --- a/test/broker/ptest.py +++ b/test/broker/ptest.py @@ -109,6 +109,7 @@ tests = [ (1, './12-prop-subpub-payload-format.py'), (1, './12-prop-subpub-content-type.py'), (1, './12-prop-assigned-client-identifier.py'), + (1, './12-prop-server-keepalive.py'), ] minport = 1888