Dear Nordic team,
I am struggling to set up CoAP with DTLS for a Thingsboard CoAP backend. The connection works just fine when using plain UDP, but when I set up a socket with DTLS I run into an error, where the CoAP client request responds with error 127. I think this means that the CoAP client library identifies the socket as already being connected, but I am not sure why. To break this down some more, I've returned to the official "coap_client" sample and adjusted it to communicate with the Thingsboard Cloud backend. I have confirmed that the sample works using plain UDP. The GET request with the specified path (NOTE: access token is redacted below) works flawlessly. This would be the expected log:
[00:02:10.370,086] <inf> coap_client_sample: CoAP response: code: 0x45, payload: {"shared":{"sdlConfigTs":6}}
From there I modified the sample to provision the required certificate (validity checked with CoAP client on my host machine), use port 5684 and then adjust socket type and options. The modified sample is:
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include <stdio.h>
#include <string.h>
#if defined(CONFIG_POSIX_API)
#include <zephyr/posix/arpa/inet.h>
#include <zephyr/posix/netdb.h>
#include <zephyr/posix/sys/socket.h>
#include <zephyr/posix/poll.h>
#else
#include <zephyr/net/socket.h>
#endif /* CONFIG_POSIX_API */
#if CONFIG_MODEM_KEY_MGMT
#include <modem/modem_key_mgmt.h>
#endif
#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/net/coap.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/conn_mgr_connectivity.h>
#include <zephyr/net/conn_mgr_monitor.h>
#include <zephyr/random/random.h>
#include <zephyr/net/coap_client.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_ctrl.h>
#include <zephyr/net/tls_credentials.h>
LOG_MODULE_REGISTER(coap_client_sample, CONFIG_COAP_CLIENT_SAMPLE_LOG_LEVEL);
#define COAP_HOSTNAME "coap.eu.thingsboard.cloud"
#define COAP_PORT 5684
#define COMODO_SEC_TAG 43
static const char comodo_cert[] = {
#include "tb-cloud-root-ca.pem.inc"
IF_ENABLED(CONFIG_TLS_CREDENTIALS, (0x00))
};
BUILD_ASSERT(sizeof(comodo_cert) < KB(4), "Comodo certificate too large");
/**
* @brief Provisions a single CA certificate to a specific security tag.
* @param[in] sec_tag The security tag that the modem will use for the encrypted socket (choose
* different tags for different DNS addresses).
* @param[in] cert_buf The root certificate to provision.
* @param[in] cert_len Length of the certificate.
* @returns 0 or a negative errno on error
*/
static int32_t tls_cert_provision_single(sec_tag_t sec_tag, const char* cert_buf, size_t cert_len)
{
int32_t err;
printk("Processing certificate provisioning for sec tag: %d\n", sec_tag);
#if CONFIG_MODEM_KEY_MGMT
bool exists;
int32_t mismatch;
err = modem_key_mgmt_exists(sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists);
if (err) {
printk("Failed to check for certificate on tag %d, err %d\n", sec_tag, err);
return err;
}
if (exists) {
mismatch = modem_key_mgmt_cmp(
sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert_buf, cert_len);
if (!mismatch) {
printk(
"Certificate on tag %d matches expected content. Skipping.\n", sec_tag);
return 0;
}
printk("Certificate mismatch detected on tag %d. Overwriting...\n", sec_tag);
err = modem_key_mgmt_delete(sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN);
if (err) {
printk(
"Failed to delete old certificate on tag %d, err %d\n", sec_tag, err);
}
}
printk("Writing certificate to modem storage (tag %d)...\n", sec_tag);
err = modem_key_mgmt_write(sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert_buf, cert_len);
if (err) {
printk("Failed to write certificate to tag %d, err %d\n", sec_tag, err);
return err;
}
#else /* CONFIG_MODEM_KEY_MGMT */
err = tls_credential_add(sec_tag, TLS_CREDENTIAL_CA_CERTIFICATE, cert_buf, cert_len);
if (err == -EEXIST) {
printk("CA certificate already exists on application core, sec tag: %d\n", sec_tag);
} else if (err < 0) {
printk("Failed to register CA certificate to app core: %d\n", err);
return err;
}
#endif /* !CONFIG_MODEM_KEY_MGMT */
printk("Successfully provisioned tag %d\n", sec_tag);
return 0;
}
/**
* @brief Provision all certificates to the modem
*/
int32_t tls_cert_provision_all(void)
{
int32_t err = tls_cert_provision_single(COMODO_SEC_TAG, comodo_cert, sizeof(comodo_cert));
if (err) {
printk("Failed to provision Comodo certificate chain\n");
return err;
}
printk("All certificates provisioned successfully.\n");
return 0;
}
/* Macros used to subscribe to specific Zephyr NET management events. */
#define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
#define CONN_LAYER_EVENT_MASK (NET_EVENT_CONN_IF_FATAL_ERROR)
/* Macro called upon a fatal error, reboots the device. */
#define FATAL_ERROR() \
LOG_ERR("Fatal error! Rebooting the device."); \
LOG_PANIC(); \
IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)))
/* Zephyr NET management event callback structures. */
static struct net_mgmt_event_callback l4_cb;
static struct net_mgmt_event_callback conn_cb;
/* Variable used to indicate if network is connected. */
static bool is_connected;
/* Mutex and conditional variable used to signal network connectivity. */
K_MUTEX_DEFINE(network_connected_lock);
K_CONDVAR_DEFINE(network_connected);
static int server_resolve(struct sockaddr_storage *server)
{
int err;
struct addrinfo *result;
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_DGRAM
};
char ipv4_addr[NET_IPV4_ADDR_LEN];
err = getaddrinfo(COAP_HOSTNAME, NULL, &hints, &result);
if (err) {
LOG_ERR("getaddrinfo, error: %d", err);
return err;
}
if (result == NULL) {
LOG_ERR("Address not found");
return -ENOENT;
}
/* IPv4 Address. */
struct sockaddr_in *server4 = ((struct sockaddr_in *)server);
server4->sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
server4->sin_family = AF_INET;
server4->sin_port = htons(COAP_PORT);
inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr, sizeof(ipv4_addr));
LOG_INF("IPv4 Address found %s", ipv4_addr);
/* Free the address. */
freeaddrinfo(result);
return 0;
}
static void wait_for_network(void)
{
k_mutex_lock(&network_connected_lock, K_FOREVER);
if (!is_connected) {
LOG_INF("Waiting for network connectivity");
k_condvar_wait(&network_connected, &network_connected_lock, K_FOREVER);
}
k_mutex_unlock(&network_connected_lock);
}
static void response_cb(const struct coap_client_response_data *data, void *user_data)
{
if (data->result_code >= 0) {
LOG_INF("CoAP response: code: 0x%x, payload: %s",
data->result_code, data->payload);
} else {
LOG_INF("Response received with error code: %d", data->result_code);
}
}
static int periodic_coap_request_loop(void)
{
int err, sock;
struct sockaddr_storage server = { 0 };
struct coap_client coap_client = { 0 };
struct coap_client_request req = {
.method = COAP_METHOD_GET,
.confirmable = true,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.payload = NULL,
.cb = response_cb,
.len = 0,
.path = "/api/v1/ACCESS_TOKEN_REDACTED/attributes?sharedKeys=sdlConfigTs",
};
err = server_resolve(&server);
if (err) {
LOG_ERR("Failed to resolve server name");
return err;
}
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
// sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
LOG_ERR("Failed to create CoAP socket: %d.", -errno);
return -errno;
}
/* Require peer (server) certificate verification */
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};
int verify = REQUIRED;
err = zsock_setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
if (err) {
LOG_ERR("Failed to setup peer verification, errno %d", errno);
return -errno;
}
/* Set the server hostname for SNI / certificate validation */
err = zsock_setsockopt(sock, SOL_TLS, TLS_HOSTNAME, COAP_HOSTNAME, strlen(COAP_HOSTNAME));
if (err) {
LOG_ERR("Failed to setup TLS hostname (%s), errno %d", COAP_HOSTNAME, errno);
return -errno;
}
/* Attach the provisioned CA certificate via its security tag */
sec_tag_t sec_tag_list[] = { COMODO_SEC_TAG };
err = zsock_setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list,
sizeof(sec_tag_t) * ARRAY_SIZE(sec_tag_list));
if (err) {
LOG_ERR("Failed to setup socket security tag, errno %d", errno);
return -errno;
}
LOG_INF("Initializing CoAP client");
err = coap_client_init(&coap_client, NULL);
if (err) {
LOG_ERR("Failed to initialize CoAP client: %d", err);
return err;
}
while (true) {
wait_for_network();
/* Send request */
err = coap_client_req(&coap_client, sock, (struct sockaddr *)&server, &req, NULL);
if (err) {
LOG_ERR("Failed to send request: %d", err);
return err;
}
LOG_INF("CoAP GET request sent sent to %s, resource: %s",
CONFIG_COAP_SAMPLE_SERVER_HOSTNAME, CONFIG_COAP_SAMPLE_RESOURCE);
k_sleep(K_SECONDS(CONFIG_COAP_SAMPLE_REQUEST_INTERVAL_SECONDS));
}
}
static void l4_event_handler(struct net_mgmt_event_callback *cb,
uint64_t event,
struct net_if *iface)
{
switch (event) {
case NET_EVENT_L4_CONNECTED:
LOG_INF("Network connectivity established");
k_mutex_lock(&network_connected_lock, K_FOREVER);
is_connected = true;
k_condvar_signal(&network_connected);
k_mutex_unlock(&network_connected_lock);
break;
case NET_EVENT_L4_DISCONNECTED:
LOG_INF("Network connectivity lost");
k_mutex_lock(&network_connected_lock, K_FOREVER);
is_connected = false;
k_mutex_unlock(&network_connected_lock);
break;
default:
/* Don't care */
return;
}
}
static void connectivity_event_handler(struct net_mgmt_event_callback *cb,
uint64_t event,
struct net_if *iface)
{
if (event == NET_EVENT_CONN_IF_FATAL_ERROR) {
LOG_ERR("NET_EVENT_CONN_IF_FATAL_ERROR");
FATAL_ERROR();
return;
}
}
int main(void)
{
int err;
LOG_INF("The CoAP client sample started");
/* Setup handler for Zephyr NET Connection Manager events and Connectivity layer. */
net_mgmt_init_event_callback(&l4_cb, l4_event_handler, L4_EVENT_MASK);
net_mgmt_add_event_callback(&l4_cb);
net_mgmt_init_event_callback(&conn_cb, connectivity_event_handler, CONN_LAYER_EVENT_MASK);
net_mgmt_add_event_callback(&conn_cb);
/* Bring all network interfaces up.
* Wi-Fi or LTE depending on the board that the sample was built for.
*/
LOG_INF("Bringing network interface up and connecting to the network");
err = conn_mgr_all_if_up(true);
if (err) {
LOG_ERR("conn_mgr_all_if_up, error: %d", err);
FATAL_ERROR();
return err;
}
err = conn_mgr_all_if_connect(true);
if (err) {
LOG_ERR("conn_mgr_all_if_connect, error: %d", err);
FATAL_ERROR();
return err;
}
/* Resend connection status if the sample is built for NATIVE_SIM.
* This is necessary because the network interface is automatically brought up
* at SYS_INIT() before main() is called.
* This means that NET_EVENT_L4_CONNECTED fires before the
* appropriate handler l4_event_handler() is registered.
*/
if (IS_ENABLED(CONFIG_BOARD_NATIVE_SIM)) {
conn_mgr_mon_resend_status();
}
wait_for_network();
err = periodic_coap_request_loop();
if (err) {
LOG_ERR("periodic_coap_request_loop, error: %d", err);
FATAL_ERROR();
return err;
}
return 0;
}
This yields the following logs.
*** Booting nRF Connect SDK v3.4.0-99553055607b *** *** Using Zephyr OS v4.4.0-bf801e4e3d19 *** [00:00:00.254,058] <inf> coap_client_sample: The CoAP client sample started [00:00:00.254,089] <inf> coap_client_sample: Bringing network interface up and connecting to the network [00:00:00.586,791] <inf> coap_client_sample: Waiting for network connectivity [00:00:04.264,648] <inf> coap_client_sample: Network connectivity established [00:00:04.364,440] <inf> coap_client_sample: IPv4 Address found 3.127.76.36 [00:00:04.364,807] <inf> coap_client_sample: Initializing CoAP client [00:00:04.366,149] <err> coap_client_sample: Failed to send request: -127 [00:00:04.366,180] <err> coap_client_sample: periodic_coap_request_loop, error: -127 [00:00:04.366,180] <err> coap_client_sample: Fatal error! Rebooting the device.
I have also tried manually connecting the socket and then passing NULL as the address pointer for the request:
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include <stdio.h>
#include <string.h>
#if defined(CONFIG_POSIX_API)
#include <zephyr/posix/arpa/inet.h>
#include <zephyr/posix/netdb.h>
#include <zephyr/posix/sys/socket.h>
#include <zephyr/posix/poll.h>
#else
#include <zephyr/net/socket.h>
#endif /* CONFIG_POSIX_API */
#if CONFIG_MODEM_KEY_MGMT
#include <modem/modem_key_mgmt.h>
#endif
#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/net/coap.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/conn_mgr_connectivity.h>
#include <zephyr/net/conn_mgr_monitor.h>
#include <zephyr/random/random.h>
#include <zephyr/net/coap_client.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_ctrl.h>
#include <zephyr/net/tls_credentials.h>
LOG_MODULE_REGISTER(coap_client_sample, CONFIG_COAP_CLIENT_SAMPLE_LOG_LEVEL);
#define COAP_HOSTNAME "coap.eu.thingsboard.cloud"
#define COAP_PORT 5684
#define COMODO_SEC_TAG 43
static const char comodo_cert[] = {
#include "tb-cloud-root-ca.pem.inc"
IF_ENABLED(CONFIG_TLS_CREDENTIALS, (0x00))
};
BUILD_ASSERT(sizeof(comodo_cert) < KB(4), "Comodo certificate too large");
/**
* @brief Provisions a single CA certificate to a specific security tag.
* @param[in] sec_tag The security tag that the modem will use for the encrypted socket (choose
* different tags for different DNS addresses).
* @param[in] cert_buf The root certificate to provision.
* @param[in] cert_len Length of the certificate.
* @returns 0 or a negative errno on error
*/
static int32_t tls_cert_provision_single(sec_tag_t sec_tag, const char* cert_buf, size_t cert_len)
{
int32_t err;
printk("Processing certificate provisioning for sec tag: %d\n", sec_tag);
#if CONFIG_MODEM_KEY_MGMT
bool exists;
int32_t mismatch;
err = modem_key_mgmt_exists(sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists);
if (err) {
printk("Failed to check for certificate on tag %d, err %d\n", sec_tag, err);
return err;
}
if (exists) {
mismatch = modem_key_mgmt_cmp(
sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert_buf, cert_len);
if (!mismatch) {
printk(
"Certificate on tag %d matches expected content. Skipping.\n", sec_tag);
return 0;
}
printk("Certificate mismatch detected on tag %d. Overwriting...\n", sec_tag);
err = modem_key_mgmt_delete(sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN);
if (err) {
printk(
"Failed to delete old certificate on tag %d, err %d\n", sec_tag, err);
}
}
printk("Writing certificate to modem storage (tag %d)...\n", sec_tag);
err = modem_key_mgmt_write(sec_tag, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert_buf, cert_len);
if (err) {
printk("Failed to write certificate to tag %d, err %d\n", sec_tag, err);
return err;
}
#else /* CONFIG_MODEM_KEY_MGMT */
err = tls_credential_add(sec_tag, TLS_CREDENTIAL_CA_CERTIFICATE, cert_buf, cert_len);
if (err == -EEXIST) {
printk("CA certificate already exists on application core, sec tag: %d\n", sec_tag);
} else if (err < 0) {
printk("Failed to register CA certificate to app core: %d\n", err);
return err;
}
#endif /* !CONFIG_MODEM_KEY_MGMT */
printk("Successfully provisioned tag %d\n", sec_tag);
return 0;
}
/**
* @brief Provision all certificates to the modem
*/
int32_t tls_cert_provision_all(void)
{
int32_t err = tls_cert_provision_single(COMODO_SEC_TAG, comodo_cert, sizeof(comodo_cert));
if (err) {
printk("Failed to provision Comodo certificate chain\n");
return err;
}
printk("All certificates provisioned successfully.\n");
return 0;
}
/* Macros used to subscribe to specific Zephyr NET management events. */
#define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
#define CONN_LAYER_EVENT_MASK (NET_EVENT_CONN_IF_FATAL_ERROR)
/* Macro called upon a fatal error, reboots the device. */
#define FATAL_ERROR() \
LOG_ERR("Fatal error! Rebooting the device."); \
LOG_PANIC(); \
IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)))
/* Zephyr NET management event callback structures. */
static struct net_mgmt_event_callback l4_cb;
static struct net_mgmt_event_callback conn_cb;
/* Variable used to indicate if network is connected. */
static bool is_connected;
/* Mutex and conditional variable used to signal network connectivity. */
K_MUTEX_DEFINE(network_connected_lock);
K_CONDVAR_DEFINE(network_connected);
static int server_resolve(struct sockaddr_storage *server)
{
int err;
struct addrinfo *result;
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_DGRAM
};
char ipv4_addr[NET_IPV4_ADDR_LEN];
err = getaddrinfo(COAP_HOSTNAME, NULL, &hints, &result);
if (err) {
LOG_ERR("getaddrinfo, error: %d", err);
return err;
}
if (result == NULL) {
LOG_ERR("Address not found");
return -ENOENT;
}
/* IPv4 Address. */
struct sockaddr_in *server4 = ((struct sockaddr_in *)server);
server4->sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
server4->sin_family = AF_INET;
server4->sin_port = htons(COAP_PORT);
inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr, sizeof(ipv4_addr));
LOG_INF("IPv4 Address found %s", ipv4_addr);
/* Free the address. */
freeaddrinfo(result);
return 0;
}
static void wait_for_network(void)
{
k_mutex_lock(&network_connected_lock, K_FOREVER);
if (!is_connected) {
LOG_INF("Waiting for network connectivity");
k_condvar_wait(&network_connected, &network_connected_lock, K_FOREVER);
}
k_mutex_unlock(&network_connected_lock);
}
static void response_cb(const struct coap_client_response_data *data, void *user_data)
{
if (data->result_code >= 0) {
LOG_INF("CoAP response: code: 0x%x, payload: %s",
data->result_code, data->payload);
} else {
LOG_INF("Response received with error code: %d", data->result_code);
}
}
static int periodic_coap_request_loop(void)
{
int err, sock;
struct sockaddr_storage server = { 0 };
struct coap_client coap_client = { 0 };
struct coap_client_request req = {
.method = COAP_METHOD_GET,
.confirmable = true,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.payload = NULL,
.cb = response_cb,
.len = 0,
.path = "/api/v1/ACCESS_TOKEN_REDACTED/attributes?sharedKeys=sdlConfigTs",
};
err = server_resolve(&server);
if (err) {
LOG_ERR("Failed to resolve server name");
return err;
}
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
// sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
LOG_ERR("Failed to create CoAP socket: %d.", -errno);
return -errno;
}
/* Require peer (server) certificate verification */
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};
int verify = REQUIRED;
err = zsock_setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
if (err) {
LOG_ERR("Failed to setup peer verification, errno %d", errno);
return -errno;
}
/* Set the server hostname for SNI / certificate validation */
err = zsock_setsockopt(sock, SOL_TLS, TLS_HOSTNAME, COAP_HOSTNAME, strlen(COAP_HOSTNAME));
if (err) {
LOG_ERR("Failed to setup TLS hostname (%s), errno %d", COAP_HOSTNAME, errno);
return -errno;
}
/* Attach the provisioned CA certificate via its security tag */
sec_tag_t sec_tag_list[] = { COMODO_SEC_TAG };
err = zsock_setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list,
sizeof(sec_tag_t) * ARRAY_SIZE(sec_tag_list));
if (err) {
LOG_ERR("Failed to setup socket security tag, errno %d", errno);
return -errno;
}
socklen_t addr_len = (((struct sockaddr *)&server)->sa_family == AF_INET) ? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6);
err = connect(sock, (struct sockaddr *)&server, addr_len);
if (err < 0) {
LOG_ERR("DTLS transport connect failed, errno %d", errno);
return -errno;
}
LOG_INF("Initializing CoAP client");
err = coap_client_init(&coap_client, NULL);
if (err) {
LOG_ERR("Failed to initialize CoAP client: %d", err);
return err;
}
while (true) {
wait_for_network();
/* Send request */
err = coap_client_req(&coap_client, sock, NULL, &req, NULL);
if (err) {
LOG_ERR("Failed to send request: %d", err);
return err;
}
LOG_INF("CoAP GET request sent sent to %s, resource: %s",
CONFIG_COAP_SAMPLE_SERVER_HOSTNAME, CONFIG_COAP_SAMPLE_RESOURCE);
k_sleep(K_SECONDS(CONFIG_COAP_SAMPLE_REQUEST_INTERVAL_SECONDS));
}
}
static void l4_event_handler(struct net_mgmt_event_callback *cb,
uint64_t event,
struct net_if *iface)
{
switch (event) {
case NET_EVENT_L4_CONNECTED:
LOG_INF("Network connectivity established");
k_mutex_lock(&network_connected_lock, K_FOREVER);
is_connected = true;
k_condvar_signal(&network_connected);
k_mutex_unlock(&network_connected_lock);
break;
case NET_EVENT_L4_DISCONNECTED:
LOG_INF("Network connectivity lost");
k_mutex_lock(&network_connected_lock, K_FOREVER);
is_connected = false;
k_mutex_unlock(&network_connected_lock);
break;
default:
/* Don't care */
return;
}
}
static void connectivity_event_handler(struct net_mgmt_event_callback *cb,
uint64_t event,
struct net_if *iface)
{
if (event == NET_EVENT_CONN_IF_FATAL_ERROR) {
LOG_ERR("NET_EVENT_CONN_IF_FATAL_ERROR");
FATAL_ERROR();
return;
}
}
int main(void)
{
int err;
LOG_INF("The CoAP client sample started");
/* Setup handler for Zephyr NET Connection Manager events and Connectivity layer. */
net_mgmt_init_event_callback(&l4_cb, l4_event_handler, L4_EVENT_MASK);
net_mgmt_add_event_callback(&l4_cb);
net_mgmt_init_event_callback(&conn_cb, connectivity_event_handler, CONN_LAYER_EVENT_MASK);
net_mgmt_add_event_callback(&conn_cb);
/* Bring all network interfaces up.
* Wi-Fi or LTE depending on the board that the sample was built for.
*/
LOG_INF("Bringing network interface up and connecting to the network");
err = conn_mgr_all_if_up(true);
if (err) {
LOG_ERR("conn_mgr_all_if_up, error: %d", err);
FATAL_ERROR();
return err;
}
err = conn_mgr_all_if_connect(true);
if (err) {
LOG_ERR("conn_mgr_all_if_connect, error: %d", err);
FATAL_ERROR();
return err;
}
/* Resend connection status if the sample is built for NATIVE_SIM.
* This is necessary because the network interface is automatically brought up
* at SYS_INIT() before main() is called.
* This means that NET_EVENT_L4_CONNECTED fires before the
* appropriate handler l4_event_handler() is registered.
*/
if (IS_ENABLED(CONFIG_BOARD_NATIVE_SIM)) {
conn_mgr_mon_resend_status();
}
wait_for_network();
err = periodic_coap_request_loop();
if (err) {
LOG_ERR("periodic_coap_request_loop, error: %d", err);
FATAL_ERROR();
return err;
}
return 0;
}
That produces a different application log:
*** Booting nRF Connect SDK v3.4.0-99553055607b *** *** Using Zephyr OS v4.4.0-bf801e4e3d19 *** [00:00:00.385,070] <inf> coap_client_sample: The CoAP client sample started [00:00:00.385,101] <inf> coap_client_sample: Bringing network interface up and connecting to the network [00:00:00.720,092] <inf> coap_client_sample: Waiting for network connectivity [00:00:05.107,818] <inf> coap_client_sample: Network connectivity established [00:00:05.241,302] <inf> coap_client_sample: IPv4 Address found 52.58.111.18 [00:00:07.060,394] <inf> coap_client_sample: Initializing CoAP client [00:00:07.063,079] <inf> coap_client_sample: CoAP GET request sent sent to californium.eclipseprojects.io, resource: obs [00:00:07.161,773] <inf> coap_client_sample: Response received with error code: -106 [00:00:10.102,813] <inf> coap_client_sample: Response received with error code: -106
Is the following definition if the 106 error the one being bubbled up into the callback?
#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */
This leaves me very confused. I also tried to rule out certificate issues by setting the following.
/* Require peer (server) certificate verification */
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};
int verify = NONE;
err = zsock_setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
if (err) {
LOG_ERR("Failed to setup peer verification, errno %d", errno);
return -errno;
}
This yields the same results.
Is the CoAP client library compatible with DTLS and is the intended use to let the library connect the socket or should the socket be manually connected?
Help is greatly appreciated!
Best,
Tom
EDIT: I was able to confirm that the request can be successfully carried out when using the low-level CoAP methods. Therefore, I can rule out certificate or DTLS handshake issues. I am probably misunderstanding something about the high-level CoAP client library...