Hi everyone,
I am looking at using MQTT v3.1.1 with TLS 1.2, to send data to Azure IoT Hub.
I tested the MQTT Simple sample and it worked perfeclty. So I am modifying this project to enable TLS. To do so I followed those threads:
https://devzone.nordicsemi.com/f/nordic-q-a/44921/nrf9160-tls-and-mqtt
https://devzone.nordicsemi.com/f/nordic-q-a/49339/implementing-tls-with-mqtt-in-nrf9160
Right now I am able to create the TLS socket but I am stucked at the next step which is the connection step.
When defining tls_config->peer_verify = 2; I got back ERROR: mqtt_connect -45.
And when I define it to 0 or 1 there is no response, as if the nRF9160 is stucked somewhere.
My log is the following for peer_verify=2. It is the same for 0/1, but without the error code.
00> ***** Booting Zephyr OS build v2.0.99-ncs1 ***** 00> 00> The MQTT simple sample started 00> 00> Deleting certs sec_tag: 2 00> 00> nrf_inbuilt_key_delete(2, 0) => result=0 00> 00> Deleting certs sec_tag: 2 00> 00> nrf_inbuilt_key_delete(2, 1) => result=0 00> 00> Deleting certs sec_tag: 2 00> 00> nrf_inbuilt_key_delete(2, 2) => result=0 00> 00> Deleting certs sec_tag: 2 00> 00> nrf_inbuilt_key_delete(2, 3) => result=2 00> 00> Deleting certs sec_tag: 2 00> 00> nrf_inbuilt_key_delete(2, 4) => result=2 00> 00> Write ca certs sec_tag: 2 00> 00> Write private cert sec_tag: 2 00> 00> Write public cert sec_tag: 2 00> 00> err_provision = 0 00> 00> LTE Link Connecting ... 00> 00> LTE Link Connected! 00> 00> IPv4 Address found 13.95.15.251 00> 00> ERROR: mqtt_connect -45
It has to be noted that the certificates I used are working in an application on my computer. They are placed here in certificates.h and are using the right formatting.
In my project folder, my prof.conf file is the following:
# # Copyright (c) 2019 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic # # General config CONFIG_PRINTK=y CONFIG_CONSOLE=y CONFIG_LOG=y CONFIG_SERIAL=y CONFIG_STDOUT_CONSOLE=y CONFIG_UART_CONSOLE=n CONFIG_RTT_CONSOLE=y CONFIG_HAS_SEGGER_RTT=y CONFIG_USE_SEGGER_RTT=y # Random Generator CONFIG_TEST_RANDOM_GENERATOR=y # Networking CONFIG_NETWORKING=y CONFIG_NET_NATIVE=n CONFIG_NET_SOCKETS_OFFLOAD=y CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POSIX_NAMES=y # nRF Cloud CONFIG_NRF_CLOUD_PROVISION_CERTIFICATES=y #CONFIG_NRF_CLOUD=y # Modem info #CONFIG_MODEM_INFO=y #TLS CONFIG_SEC_TAG=2 CONFIG_PEER_VERIFY=2 # LTE link control #CONFIG_POWER_OPTIMIZATION_ENABLE=n CONFIG_LTE_LINK_CONTROL=y CONFIG_LTE_AUTO_INIT_AND_CONNECT=n # BSD library CONFIG_BSD_LIBRARY=y # AT Host CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_AT_HOST_LIBRARY=y # MQTT CONFIG_MQTT_LIB=y #CONFIG_MQTT_SOCKET_LIB=y CONFIG_MQTT_LIB_TLS=y #CONFIG_MQTT_MAX_PACKET_LENGTH=2048 # Application #CONFIG_MQTT_PUB_TOPIC="/my/publish/topic" #CONFIG_MQTT_SUB_TOPIC="/my/subscribe/topic" CONFIG_MQTT_CLIENT_ID="mqtt_test" CONFIG_MQTT_BROKER_HOSTNAME="test-aptus.azure-devices.net" CONFIG_MQTT_BROKER_PORT=8883 CONFIG_PROVISION_CERTIFICATES=y CONFIG_CERTIFICATES_FILE="certificates.h" # Main thread CONFIG_MAIN_THREAD_PRIORITY=7 CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=2048
The Kconfig file there is the following:
# # Copyright (c) 2018 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic # menu "MQTT simple sample" config PROVISION_CERTIFICATES bool "Provision of certificate" help Enable run-time provisioning of certificates from the certificates header file selected by using CERTIFICATES_FILE config CERTIFICATES_FILE string "Certificates to use" depends on PROVISION_CERTIFICATES default "certificates.h" config SEC_TAG int "Security tag to use for the connection" default 1 config PEER_VERIFY int "Peer verify parameter for mqtt_client" default 1 help Set to 0 for VERIFY_NONE, 1 for VERIFY_OPTIONAL, and 2 for VERIFY_REQUIRED. config MQTT_PUB_TOPIC string "MQTT publish topic" default "my/publish/topic" config MQTT_SUB_TOPIC string "MQTT subscribe topic" default "my/subscribe/topic" config MQTT_CLIENT_ID string "MQTT Client ID" default "my-client-id" config MQTT_BROKER_HOSTNAME string "MQTT broker hostname" default "mqtt.eclipse.org" config MQTT_BROKER_PORT int "MQTT broker port" default 1883 config MQTT_MESSAGE_BUFFER_SIZE int "MQTT message buffer size" default 128 config MQTT_PAYLOAD_BUFFER_SIZE int "MQTT payload buffer size" default 128 endmenu menu "Zephyr Kernel" source "$ZEPHYR_BASE/Kconfig.zephyr" endmenu
And the main.c is :
/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic */ #include <zephyr.h> #include <stdio.h> #include <uart.h> #include <string.h> #include <net/mqtt.h> #include <net/socket.h> #include <lte_lc.h> #include <certificates.h> #if defined(CONFIG_BSD_LIBRARY) #include "nrf_inbuilt_key.h" #endif #include CONFIG_CERTIFICATES_FILE #if defined(CONFIG_LWM2M_CARRIER) #include <lwm2m_carrier.h> #endif //#define CONFIG_NRF_CLOUD_SEC_TAG 1 #define NRF_CLOUD_HOSTNAME CONFIG_MQTT_BROKER_HOSTNAME //#define NRF_CLOUD_SEC_TAG CONFIG_NRF_CLOUD_SEC_TAG static sec_tag_t sec_tag_list[] = {CONFIG_SEC_TAG}; /* Buffers for MQTT client. */ static u8_t rx_buffer[CONFIG_MQTT_MESSAGE_BUFFER_SIZE]; static u8_t tx_buffer[CONFIG_MQTT_MESSAGE_BUFFER_SIZE]; static u8_t payload_buf[CONFIG_MQTT_PAYLOAD_BUFFER_SIZE]; /* The mqtt client struct */ static struct mqtt_client client; /* MQTT Broker details. */ static struct sockaddr_storage broker; /* Connected flag */ static bool connected; /* File descriptor */ static struct pollfd fds; #if defined(CONFIG_BSD_LIBRARY) /**@brief Recoverable BSD library error. */ void bsd_recoverable_error_handler(uint32_t err) { printk("bsdlib recoverable error: %u\n", (unsigned int)err); } /**@brief Irrecoverable BSD library error. */ void bsd_irrecoverable_error_handler(uint32_t err) { printk("bsdlib irrecoverable error: %u\n", err); __ASSERT_NO_MSG(false); } #endif /* defined(CONFIG_BSD_LIBRARY) */ /**@brief Function to print strings without null-termination */ static void data_print(u8_t *prefix, u8_t *data, size_t len) { char buf[len + 1]; memcpy(buf, data, len); buf[len] = 0; printk("%s%s\n", prefix, buf); } /**@brief Function to publish data on the configured topic */ static int data_publish(struct mqtt_client *c, enum mqtt_qos qos, u8_t *data, size_t len) { struct mqtt_publish_param param; param.message.topic.qos = qos; param.message.topic.topic.utf8 = CONFIG_MQTT_PUB_TOPIC; param.message.topic.topic.size = strlen(CONFIG_MQTT_PUB_TOPIC); param.message.payload.data = data; param.message.payload.len = len; param.message_id = sys_rand32_get(); param.dup_flag = 0; param.retain_flag = 0; data_print("Publishing: ", data, len); printk("to topic: %s len: %u\n", CONFIG_MQTT_PUB_TOPIC, (unsigned int)strlen(CONFIG_MQTT_PUB_TOPIC)); return mqtt_publish(c, ¶m); } /**@brief Function to subscribe to the configured topic */ static int subscribe(void) { struct mqtt_topic subscribe_topic = { .topic = { .utf8 = CONFIG_MQTT_SUB_TOPIC, .size = strlen(CONFIG_MQTT_SUB_TOPIC) }, .qos = MQTT_QOS_1_AT_LEAST_ONCE }; const struct mqtt_subscription_list subscription_list = { .list = &subscribe_topic, .list_count = 1, .message_id = 1234 }; printk("Subscribing to: %s len %u\n", CONFIG_MQTT_SUB_TOPIC, (unsigned int)strlen(CONFIG_MQTT_SUB_TOPIC)); return mqtt_subscribe(&client, &subscription_list); } /**@brief Function to read the published payload. */ static int publish_get_payload(struct mqtt_client *c, size_t length) { u8_t *buf = payload_buf; u8_t *end = buf + length; if (length > sizeof(payload_buf)) { return -EMSGSIZE; } while (buf < end) { int ret = mqtt_read_publish_payload(c, buf, end - buf); if (ret < 0) { int err; if (ret != -EAGAIN) { return ret; } printk("mqtt_read_publish_payload: EAGAIN\n"); err = poll(&fds, 1, K_SECONDS(CONFIG_MQTT_KEEPALIVE)); if (err > 0 && (fds.revents & POLLIN) == POLLIN) { continue; } else { return -EIO; } } if (ret == 0) { return -EIO; } buf += ret; } return 0; } /**@brief MQTT client event handler */ void mqtt_evt_handler(struct mqtt_client *const c, const struct mqtt_evt *evt) { int err; switch (evt->type) { case MQTT_EVT_CONNACK: if (evt->result != 0) { printk("MQTT connect failed %d\n", evt->result); break; } connected = true; printk("[%s:%d] MQTT client connected!\n", __func__, __LINE__); subscribe(); break; case MQTT_EVT_DISCONNECT: printk("[%s:%d] MQTT client disconnected %d\n", __func__, __LINE__, evt->result); connected = false; break; case MQTT_EVT_PUBLISH: { const struct mqtt_publish_param *p = &evt->param.publish; printk("[%s:%d] MQTT PUBLISH result=%d len=%d\n", __func__, __LINE__, evt->result, p->message.payload.len); err = publish_get_payload(c, p->message.payload.len); if (err >= 0) { data_print("Received: ", payload_buf, p->message.payload.len); /* Echo back received data */ data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE, payload_buf, p->message.payload.len); } else { printk("mqtt_read_publish_payload: Failed! %d\n", err); printk("Disconnecting MQTT client...\n"); err = mqtt_disconnect(c); if (err) { printk("Could not disconnect: %d\n", err); } } } break; case MQTT_EVT_PUBACK: if (evt->result != 0) { printk("MQTT PUBACK error %d\n", evt->result); break; } printk("[%s:%d] PUBACK packet id: %u\n", __func__, __LINE__, evt->param.puback.message_id); break; case MQTT_EVT_SUBACK: if (evt->result != 0) { printk("MQTT SUBACK error %d\n", evt->result); break; } printk("[%s:%d] SUBACK packet id: %u\n", __func__, __LINE__, evt->param.suback.message_id); break; default: printk("[%s:%d] default: %d\n", __func__, __LINE__, evt->type); break; } } /**@brief Resolves the configured hostname and * initializes the MQTT broker structure */ static void broker_init(void) { int err; struct addrinfo *result; struct addrinfo *addr; struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM }; err = getaddrinfo(CONFIG_MQTT_BROKER_HOSTNAME, NULL, &hints, &result); if (err) { printk("ERROR: getaddrinfo failed %d\n", err); return; } addr = result; err = -ENOENT; /* Look for address of the broker. */ while (addr != NULL) { /* IPv4 Address. */ if (addr->ai_addrlen == sizeof(struct sockaddr_in)) { struct sockaddr_in *broker4 = ((struct sockaddr_in *)&broker); char ipv4_addr[NET_IPV4_ADDR_LEN]; broker4->sin_addr.s_addr = ((struct sockaddr_in *)addr->ai_addr) ->sin_addr.s_addr; broker4->sin_family = AF_INET; broker4->sin_port = htons(CONFIG_MQTT_BROKER_PORT); inet_ntop(AF_INET, &broker4->sin_addr.s_addr, ipv4_addr, sizeof(ipv4_addr)); printk("IPv4 Address found %s\n", ipv4_addr); break; } else { printk("ai_addrlen = %u should be %u or %u\n", (unsigned int)addr->ai_addrlen, (unsigned int)sizeof(struct sockaddr_in), (unsigned int)sizeof(struct sockaddr_in6)); } addr = addr->ai_next; break; } /* Free the address. */ freeaddrinfo(result); } /**@brief Initialize the MQTT client structure */ static void client_init(struct mqtt_client *client) { mqtt_client_init(client); broker_init(); /* MQTT client configuration */ client->broker = &broker; client->evt_cb = mqtt_evt_handler; client->client_id.utf8 = (u8_t *)CONFIG_MQTT_CLIENT_ID; client->client_id.size = strlen(CONFIG_MQTT_CLIENT_ID); printk("clientId %s \n", CONFIG_MQTT_CLIENT_ID); client->password = NULL; client->user_name = NULL; client->protocol_version = MQTT_VERSION_3_1_1; /* MQTT buffers configuration */ client->rx_buf = rx_buffer; client->rx_buf_size = sizeof(rx_buffer); client->tx_buf = tx_buffer; client->tx_buf_size = sizeof(tx_buffer); /* MQTT transport configuration */ #if defined(CONFIG_MQTT_LIB_TLS) struct mqtt_sec_config *tls_config = &client->transport.tls.config; client->transport.type = MQTT_TRANSPORT_SECURE; tls_config->peer_verify = 2; tls_config->cipher_count = 0; tls_config->cipher_list = NULL; tls_config->sec_tag_count = ARRAY_SIZE(sec_tag_list); tls_config->sec_tag_list = sec_tag_list; tls_config->hostname = CONFIG_MQTT_BROKER_HOSTNAME; #else /* MQTT transport configuration */ client->transport.type = MQTT_TRANSPORT_NON_SECURE; #endif /* defined(CONFIG_MQTT_LIB_TLS) */ } /**@brief Initialize the file descriptor structure used by poll. */ static int fds_init(struct mqtt_client *c) { if (c->transport.type == MQTT_TRANSPORT_NON_SECURE) { fds.fd = c->transport.tcp.sock; } else { #if defined(CONFIG_MQTT_LIB_TLS) fds.fd = c->transport.tls.sock; #else return -ENOTSUP; #endif } fds.events = POLLIN; return 0; } /**@brief Configures modem to provide LTE link. Blocks until link is * successfully established. */ static void modem_configure(void) { #if defined(CONFIG_LTE_LINK_CONTROL) if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { /* Do nothing, modem is already turned on * and connected. */ } else { #if defined(CONFIG_LWM2M_CARRIER) /* Wait for the LWM2M_CARRIER to configure the modem and * start the connection. */ printk("Waitng for carrier registration...\n"); k_sem_take(&carrier_registered, K_FOREVER); printk("Registered!\n"); #else /* defined(CONFIG_LWM2M_CARRIER) */ int err; printk("LTE Link Connecting ...\n"); err = lte_lc_init_and_connect(); __ASSERT(err == 0, "LTE link could not be established."); printk("LTE Link Connected!\n"); #endif /* defined(CONFIG_LWM2M_CARRIER) */ } #endif /* defined(CONFIG_LTE_LINK_CONTROL) */ } /* Provisions root CA certificate using nrf_inbuilt_key API */ static int provision_certificate(void) { #if defined(CONFIG_PROVISION_CERTIFICATES) #if defined(CONFIG_BSD_LIBRARY) { int err; /* Delete certificates */ nrf_sec_tag_t sec_tag = (nrf_sec_tag_t) sec_tag_list[0]; for (nrf_key_mgnt_cred_type_t type = 0; type < 5; type++) { printk("Deleting certs sec_tag: %d\n", sec_tag); err = nrf_inbuilt_key_delete(sec_tag, type); printk("nrf_inbuilt_key_delete(%u, %d) => result=%d\n", sec_tag, type, err); } #if defined(CA_CERTIFICATE) /* Provision CA Certificate. */ printk("Write ca certs sec_tag: %d\n", sec_tag); err = nrf_inbuilt_key_write(sec_tag, NRF_KEY_MGMT_CRED_TYPE_CA_CHAIN, CA_CERTIFICATE, strlen(CA_CERTIFICATE)); if (err) { printk("CA_CERTIFICATE err: %d\n", err); return err; } #endif #if defined (CLIENT_PRIVATE_KEY) /* Provision Private Certificate. */ printk("Write private cert sec_tag: %d\n", sec_tag); err = nrf_inbuilt_key_write( sec_tag, NRF_KEY_MGMT_CRED_TYPE_PRIVATE_CERT, CLIENT_PRIVATE_KEY, strlen(CLIENT_PRIVATE_KEY)); if (err) { printk("CLIENT_PRIVATE_KEY err: %d\n", err); return err; } #endif #if defined(CLIENT_PUBLIC_CERTIFICATE) /* Provision Public Certificate. */ printk("Write public cert sec_tag: %d\n", sec_tag); err = nrf_inbuilt_key_write( sec_tag, NRF_KEY_MGMT_CRED_TYPE_PUBLIC_CERT, CLIENT_PUBLIC_CERTIFICATE, strlen(CLIENT_PUBLIC_CERTIFICATE)); if (err) { printk("CLIENT_PUBLIC_CERTIFICATE err: %d\n",err); return err; } } #endif #else { int err; err = tls_credential_add(CONFIG_SEC_TAG, TLS_CREDENTIAL_CA_CERTIFICATE, NRF_CLOUD_CA_CERTIFICATE, sizeof(NRF_CLOUD_CA_CERTIFICATE)); if (err < 0) { printk("Failed to register ca certificate: %d\n",err); return err; } err = tls_credential_add(CONFIG_SEC_TAG, TLS_CREDENTIAL_PRIVATE_KEY, NRF_CLOUD_CLIENT_PRIVATE_KEY, sizeof(NRF_CLOUD_CLIENT_PRIVATE_KEY)); if (err < 0) { printk("Failed to register private key: %d\n",err); return err; } err = tls_credential_add(CONFIG_SEC_TAG, TLS_CREDENTIAL_SERVER_CERTIFICATE, NRF_CLOUD_CLIENT_PUBLIC_CERTIFICATE, sizeof(NRF_CLOUD_CLIENT_PUBLIC_CERTIFICATE)); if (err < 0) { printk("Failed to register public certificate: %d\n",err); return err; } } #endif /* defined(CONFIG_BSD_LIBRARY) */ #endif /* defined(CONFIG_PROVISION_CERTIFICATES) */ return 0; } void main(void) { int err; printk("The MQTT simple sample started\n"); int err_provision = provision_certificate(); if (err_provision != 0) { printk("ERROR: nct_provision failure %d\n", err_provision); return; } printk("err_provision = %d\n", err_provision); modem_configure(); client_init(&client); err = mqtt_connect(&client); if (err != 0) { printk("ERROR: mqtt_connect %d\n", err); return; } printk("\n CONNECTED\n"); err = fds_init(&client); if (err != 0) { printk("ERROR: fds_init %d\n", err); return; } printk("FDS init done \n"); while (1) { err = poll(&fds, 1, K_SECONDS(CONFIG_MQTT_KEEPALIVE)); if (err < 0) { printk("ERROR: poll %d\n", errno); break; } err = mqtt_live(&client); if (err != 0) { printk("ERROR: mqtt_live %d\n", err); break; } if ((fds.revents & POLLIN) == POLLIN) { err = mqtt_input(&client); if (err != 0) { printk("ERROR: mqtt_input %d\n", err); break; } } if ((fds.revents & POLLERR) == POLLERR) { printk("POLLERR\n"); break; } if ((fds.revents & POLLNVAL) == POLLNVAL) { printk("POLLNVAL\n"); break; } } printk("Disconnecting MQTT client...\n"); err = mqtt_disconnect(&client); if (err) { printk("Could not disconnect MQTT client. Error: %d\n", err); } }
Looking at errno.h, I understand the error -45 as "Operation not supported on socket".
But which operation is not supported ?
How can I fix this ? And why is it hanging out when I use peer-verify=0 or 1 ?
Thank you in advance