This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

MQTT 128kB packet size

Hello,

I am using NRF9160 running the Serial LTE Modem application in NCS 1.5.0.

When connecting with AWS IoT over a secure MQTT connection the maximum MQTT packet size allowed by AWS IoT is 128kB. When I publish a message of this size from an external device my nrf9160 device outputs the error -128. What is this error? 128 does not appear in errno.h.

How can I allow the maximum packet size of 128kB from AWS IoT to be received by the nrf9160? If this size is not reachable what is the maximum limit and how do I enable that limit?

What is the maximum outgoing limit and how do I enable that limit?

  • Hi,

     

     

    When I publish a message of this size from an external device my nrf9160 device outputs the error -128. What is this error?

     ENOTCONN.

    You get this error because the nRF9160 modem does not support TLS fragments this large, and closes the connection.

     

    128 does not appear in errno.h.

     Yes, it does: https://github.com/eblot/newlib/blob/2a63fa0fd26ffb6603f69d9e369e944fe449c246/newlib/libc/include/sys/errno.h#L161

    Your confusion comes from the fact that Zephyr and NCS supports two different implementations of libc. Zephyr has it's own minimal implementation (which is the default), but it also supports the Newlib implementation which is distributed with the compiler.

    These two implementations uses different values for the error codes.

    The aws_iot sample has enabled the Newlib implementation by setting CONFIG_NEWLIB_LIBC=y in the prj.conf file.

     

    How can I allow the maximum packet size of 128kB from AWS IoT to be received by the nrf9160?

     Realistically, you can't.

    The modem only supports TLS fragments up to ~2kB.

    You can also run the TLS stack on the application core, instead of in the modem. This will let you increase the buffer size, but a buffer size of 128kB would be half the RAM available on the nRF9160 (if you are interested in this, take a look at how it is done in the SLM).

    You can read about how the nRF Asset Tracker works around this limitation here: https://nordicsemiconductor.github.io/asset-tracker-cloud-docs/v1.5.x/docs/aws/LimitingShadowDocumentSize.html

    Best regards,

    Didrik

  • Thank you for the clarifications Didrik. I did not expect it to be easy, but your reply has helped a lot in getting started.

    If half of the RAM is still available while using the SLM then yes that is exactly what I should try and accomplish.
     
    But I am first trying to establish a connection with the MQTT broker using TLS in the application core. I have not been successful and I think it has something to do with credentials. I changed "overlay-native_tls.conf" so it looks like this

    # To build serial LTE modem native TLS socket support
    # add the following to your west build command:
    # -DOVERLAY_CONFIG=overlay-native_tls.conf
    #

    # TLS configuration
    CONFIG_SLM_NATIVE_TLS=y
    CONFIG_MODEM_KEY_MGMT=y
    CONFIG_MBEDTLS=y
    CONFIG_MBEDTLS_LIBRARY=y
    CONFIG_MBEDTLS_TLS_LIBRARY=y
    CONFIG_MBEDTLS_PKCS1_V15=y
    CONFIG_MBEDTLS_ENABLE_HEAP=y
    CONFIG_MBEDTLS_INSTALL_PATH="DUMMY"
    # If larger TLS buffer is required for large CA chain,
    # increase CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN to 4096
    # and CONFIG_MBEDTLS_HEAP_SIZE to 32768
    #CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1280
    #CONFIG_MBEDTLS_HEAP_SIZE=23040
    CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=4096
    CONFIG_MBEDTLS_HEAP_SIZE=32768
    CONFIG_NET_SOCKETS_OFFLOAD_TLS=n
    CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
    CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=2
    # Increase extra FD entry for TLS contexts(2)
    CONFIG_POSIX_MAX_FDS=10
    CONFIG_NORDIC_SECURITY_BACKEND=y
    CONFIG_NRF_SECURITY_ADVANCED=y
    # Enable Socket Logging for debug
    #CONFIG_NET_LOG=y
    #CONFIG_NET_SOCKETS_LOG_LEVEL_DBG=y

    according to the note in the middle of this file about large CA chains (in case that refers to me.) And I build with -DOVERLAY_CONFIG=overlay-native_tls.conf

    The documentation of SLM states I "must" use the overridden %CMNG command to provision credentials. What does this mean exactly? Do I need to issue %CMNG with the write opcode for each of my three credentials every time I start the application?

    This is what I have been trying to do but without success. I was thinking I have not found the correct way to format the certificates but then I discovered that every %CMNG command is returning a simple "ERROR."

    For example if I try to read a CA Root Cert with security tag 24: AT%CMNG=2,24,0 will return ERROR. I have not been able to get a success or even a +CME Error Code

    I have set CFUN=4 like it says to do if I want to write a credential.

    There is a comment in slm_at_cmng.c that says it should be like this: AT#XCMNG, but that doesn't change the outcome.

    For good measure I ran CLAC and AT%CMNG does in fact appear in the list of available commands, AT#XCMNG does not.


    Am I misunderstanding something about the overridden %CMNG command?

    Update: AT%CMNG=2,24,0 fails at the first check in handle_at_xcmng. Line 73 of slm_at_cmng.c. I edited the LOGERR to differentiate it from the others.

    static int handle_at_xcmng(enum at_cmd_type cmd_type)
    {
        int err = -EINVAL;
        uint16_t op, type;
        nrf_sec_tag_t sec_tag;
        uint8_t *content;
        size_t len = CONFIG_AT_CMD_RESPONSE_MAX_LEN;

        switch (cmd_type) {
        case AT_CMD_TYPE_SET_COMMAND:
            if (at_params_valid_count_get(&at_param_list) < 2) {
            LOG_ERR("Parameter missed at first get count");
            return -EINVAL;
        }

    ...

    Update:
    I am struggling to wrap my mind around the full operation of the at_parser, however it appears to me that this AT%CMNG is being treated as a CLAC response, which results in the at_params_list containing just one element, the whole string.

    Testing with other known commands such as AT+CFUN seems to show these commands are not parsed in at_parse_param of at_cmd_parser.c. Special commands of the structure AT#X... are parsed in this function, and it is apparent that the 'X' is specifically meant to differentiate a command subject to this parser from a CLAC response.

    So I don't think this is probably how you should do this but I edited is_clac in at_utils.h to read:

    static bool is_clac(const char *str)
    {
        if (strlen(str) < 4) {
            return false;
        }

        if ((toupper(str[0]) != 'A') || (toupper(str[1]) != 'T')) {
            /* Not an AT command */
            return false;
        }

        if ((toupper(str[2]) != '+') && (toupper(str[2]) != '%')) {
        /* Neither AT+ nor AT% */
        return false;
        }

        if ((toupper(str[2]) == '%') && ((toupper(str[3]) == 'X') ||
            (strlen(str) >= 7) && (toupper(str[3]) == 'C') && (toupper(str[4]) == 'M')
            && (toupper(str[5]) == 'N') && (toupper(str[6]) == 'G'))){
            /* Ignore AT%X to avoid false detect (read resp XCOEX0 etc.) */
            /* Ignore CMNG */
            return false;
        }

        return true;
    }


    And now I am at least getting through to something happening and getting "FAILED! modem_key_mgmt_read() = -22" in my log.

    -----------------------------------------------

    Update:

    It seems that provisioning credentials is now possible with the is_clac edits. I have tried many ways of formatting the credentials but I always get this message when I try to connect to the broker whether I use the URL or the IP directly:

     
     

  • Hi, and sorry for the late reply.

     

    JWizard said:

    The documentation of SLM states I "must" use the overridden %CMNG command to provision credentials. What does this mean exactly?

     Normally, %CMNG writes the credentials to the modem, where they cannot be read by the application (except for the root CA certificates).

    The overridden %CMNG command is used automatically when you enable CONFIG_SLM_NATIVE_TLS, so it is not really something you have to think about, except for %CMNG=1 not being supported in the overridden version.

     

    JWizard said:
    There is a comment in slm_at_cmng.c that says it should be like this: AT#XCMNG, but that doesn't change the outcome.

     I believe AT#XCMNG was used in an earlier version, though I might be mistaken.

     

    JWizard said:
    For good measure I ran CLAC and AT%CMNG does in fact appear in the list of available commands, AT#XCMNG does not.

     Anyway, if it was supported, I would expect it to be listed by #CLAC, not %CLAC.

     

    JWizard said:
    Do I need to issue %CMNG with the write opcode for each of my three credentials every time I start the application?

     No. The credentials are stored in the modem as CA certificates, so that the application can read them out later if it needs them.

     

    JWizard said:
    AT%CMNG=2,24,0 fails at the first check in handle_at_xcmng. Line 73 of slm_at_cmng.c. I edited the LOGERR to differentiate it from the others.

     Yes, I get this as well.

     

    JWizard said:
    Testing with other known commands such as AT+CFUN seems to show these commands are not parsed in at_parse_param of at_cmd_parser.c. Special commands of the structure AT#X... are parsed in this function, and it is apparent that the 'X' is specifically meant to differentiate a command subject to this parser from a CLAC response.

     "normal" AT commands are sent to the modem, where they are parsed and handled. The exception here is %CMNG, which normally would be sent to the modem, but we now want to intercept so that we can do something else with it. AT#-commands on the other hand are implemented by the SLM on the application core, and must therefore be parsed by the application.

    The 'X' is used to differentiate between standard AT commands specified by 3GPP, and custom commands added by us.

     

    JWizard said:
    So I don't think this is probably how you should do this but I edited is_clac in at_utils.h to read:

     I haven't fully wrapped my head around the parser logic, and why CLAC responses must be detected. But it is not unreasonable for is_clac to trigger here, as you wouldn't encounter AT%<non 'X'> anywhere normally, except for this special case where we want to override an AT command, while using a parser not made to handle that scenario.

    I'll discuss the best way to handle this with our developers tomorrow, along with looking into your other problems with using the credentials.

    Best regards,

    Didrik

  • The CLAC problem has been fixed in the fork the SLM developers are working on.

    If I have understood their fix correctly, they do it by only checking for CLAC responses of the second line of the respons, rather than the first.

     

    JWizard said:
    And now I am at least getting through to something happening and getting "FAILED! modem_key_mgmt_read() = -22" in my log.

     Did you write a certificate (with the SLM configured to use native TLS) to that sec_tag first?

    When native TLS is enabled, the SLM remaps the sec_tags, so if the certificates was written without the remapping, you have to map backwards to find the new sec_tag you must use with %CMNG.

    You can find the mapping function here: https://github.com/nrfconnect/sdk-nrf/blob/v1.5.0/applications/serial_lte_modem/src/slm_native_tls.c#L21

    I'll continue to work on using the native TLS stack for connecting to an MQTT broker tomorrow.

  • Thank you Didrik.

    I think I misunderstood the mapping, however, I have tried using the Native TLS version to write the credentials and I could not connect to my Broker. It is possible that I did not format the creds correctly.

    So I took the mapping knowledge and I used a non-native TLS App to write the creds to SecTag = 240.

    I reloaded the NativeTLS application and I do read my type 0 credential with AT%CMNG=2,24,0.

    However It still is not connecting. Interestingly I get this output. The first time I am using the correct port and the second time I used an incorrect port and got different output.

    I tried again this time incrementing the SEC_TAG (per the mapping function) when I use the modem_key_mgmt_write function in a non-native TLS app.

        err = modem_key_mgmt_write(CONFIG_MQTT_TLS_SEC_TAG,
    				   MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
    				   CA_CERTIFICATE,
    				   strlen(CA_CERTIFICATE));
    	if (err) {
    		LOG_ERR("Failed to provision CA certificate: %d", err);
    		return err;
    
            
    	}
            
            err = modem_key_mgmt_write(CONFIG_MQTT_TLS_SEC_TAG+1,
    				   MODEM_KEY_MGMT_CRED_TYPE_PUBLIC_CERT,
    				   CLIENT_PUBLIC_CERTIFICATE,
    				   strlen(CLIENT_PUBLIC_CERTIFICATE));
    	if (err) {
    		LOG_ERR("Failed to provision public certificate: %d", err);
    		return err;
            }
    
            err = modem_key_mgmt_write(CONFIG_MQTT_TLS_SEC_TAG+2,
    				   MODEM_KEY_MGMT_CRED_TYPE_PRIVATE_CERT,
    				   CLIENT_PRIVATE_KEY,
    				   strlen(CLIENT_PRIVATE_KEY));
    	if (err) {
    		LOG_ERR("Failed to provision private key: %d", err);
    		return err;
            }


    Results were the same.

Related