net: coap: add support for Echo option (RFC 9175)

Resend the request with Echo option and the received Echo
option value when receiving a 4.01 (Unauthorized) response
with the Echo option.

Add missing header file kernel.h to coap_client.h.

Signed-off-by: Juha Ylinen <juha.ylinen@nordicsemi.no>
This commit is contained in:
Juha Ylinen 2023-10-09 16:14:48 +03:00 committed by Carles Cufí
parent 1d9098f3f0
commit 1aef034126
4 changed files with 294 additions and 0 deletions

View File

@ -61,6 +61,7 @@ enum coap_option_num {
COAP_OPTION_PROXY_URI = 35, /**< Proxy-Uri */
COAP_OPTION_PROXY_SCHEME = 39, /**< Proxy-Scheme */
COAP_OPTION_SIZE1 = 60, /**< Size1 */
COAP_OPTION_ECHO = 252, /**< Echo (RFC 9175) */
COAP_OPTION_REQUEST_TAG = 292 /**< Request-Tag (RFC 9175) */
};

View File

@ -20,6 +20,7 @@
*/
#include <zephyr/net/coap.h>
#include <zephyr/kernel.h>
#define MAX_COAP_MSG_LEN (CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE + \
@ -101,6 +102,8 @@ struct coap_client {
uint8_t send_buf[MAX_COAP_MSG_LEN];
uint8_t recv_buf[MAX_COAP_MSG_LEN];
struct coap_client_internal_request requests[CONFIG_COAP_CLIENT_MAX_REQUESTS];
struct coap_option echo_option;
bool send_echo;
};
/** @endcond */

View File

@ -332,6 +332,17 @@ int coap_client_req(struct coap_client *client, int sock, const struct sockaddr
goto out;
}
if (client->send_echo) {
ret = coap_packet_append_option(&internal_req->request, COAP_OPTION_ECHO,
client->echo_option.value, client->echo_option.len);
if (ret < 0) {
LOG_ERR("Failed to append echo option");
k_mutex_unlock(&client->send_mutex);
goto out;
}
client->send_echo = false;
}
ret = coap_client_schedule_poll(client, sock, req, internal_req);
if (ret < 0) {
LOG_ERR("Failed to schedule polling");
@ -604,6 +615,11 @@ struct coap_client_internal_request *get_request_with_token(struct coap_client *
return NULL;
}
static bool find_echo_option(const struct coap_packet *response, struct coap_option *option)
{
return coap_find_options(response, COAP_OPTION_ECHO, option, 1);
}
static int handle_response(struct coap_client *client, const struct coap_packet *response)
{
int ret = 0;
@ -651,6 +667,60 @@ static int handle_response(struct coap_client *client, const struct coap_packet
return 1;
}
/* Received echo option */
if (find_echo_option(response, &client->echo_option)) {
/* Resend request with echo option */
if (response_code == COAP_RESPONSE_CODE_UNAUTHORIZED) {
k_mutex_lock(&client->send_mutex, K_FOREVER);
ret = coap_client_init_request(client, &internal_req->coap_request,
internal_req, false);
if (ret < 0) {
LOG_ERR("Error creating a CoAP request");
k_mutex_unlock(&client->send_mutex);
goto fail;
}
ret = coap_packet_append_option(&internal_req->request, COAP_OPTION_ECHO,
client->echo_option.value,
client->echo_option.len);
if (ret < 0) {
LOG_ERR("Failed to append echo option");
k_mutex_unlock(&client->send_mutex);
goto fail;
}
if (coap_header_get_type(&internal_req->request) == COAP_TYPE_CON) {
ret = coap_pending_init(&internal_req->pending,
&internal_req->request, &client->address,
internal_req->retry_count);
if (ret < 0) {
LOG_ERR("Error creating pending");
k_mutex_unlock(&client->send_mutex);
goto fail;
}
coap_pending_cycle(&internal_req->pending);
}
ret = send_request(client->fd, internal_req->request.data,
internal_req->request.offset, 0, &client->address,
client->socklen);
k_mutex_unlock(&client->send_mutex);
if (ret < 0) {
LOG_ERR("Error sending a CoAP request");
goto fail;
} else {
return 1;
}
} else {
/* Send echo in next request */
client->send_echo = true;
}
}
/* Send ack for CON */
if (response_type == COAP_TYPE_CON) {
/* CON response is always a separate response, respond with empty ACK. */

View File

@ -75,6 +75,89 @@ static ssize_t z_impl_zsock_sendto_custom_fake(int sock, void *buf, size_t len,
last_response_code = ((uint8_t *) buf)[1];
LOG_INF("Latest message ID: %d", last_message_id);
return 1;
}
static ssize_t z_impl_zsock_sendto_custom_fake_echo(int sock, void *buf, size_t len,
int flags, const struct sockaddr *dest_addr,
socklen_t addrlen)
{
uint16_t last_message_id = 0;
struct coap_packet response = {0};
struct coap_option option = {0};
last_message_id |= ((uint8_t *) buf)[2] << 8;
last_message_id |= ((uint8_t *) buf)[3];
if (messages_needing_response[0] == 0) {
messages_needing_response[0] = last_message_id;
} else {
messages_needing_response[1] = last_message_id;
}
last_response_code = ((uint8_t *) buf)[1];
LOG_INF("Latest message ID: %d", last_message_id);
int ret = coap_packet_parse(&response, buf, len, NULL, 0);
if (ret < 0) {
LOG_ERR("Invalid data received");
}
ret = coap_find_options(&response, COAP_OPTION_ECHO, &option, 1);
zassert_equal(ret, 1, "Coap echo option not found, %d", ret);
zassert_mem_equal(option.value, "echo_value", option.len, "Incorrect echo data");
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake;
return 1;
}
static ssize_t z_impl_zsock_sendto_custom_fake_echo_next_req(int sock, void *buf, size_t len,
int flags, const struct sockaddr *dest_addr,
socklen_t addrlen)
{
uint16_t last_message_id = 0;
struct coap_packet response = {0};
struct coap_option option = {0};
last_message_id |= ((uint8_t *) buf)[2] << 8;
last_message_id |= ((uint8_t *) buf)[3];
if (messages_needing_response[0] == 0) {
messages_needing_response[0] = last_message_id;
} else {
messages_needing_response[1] = last_message_id;
}
last_response_code = ((uint8_t *) buf)[1];
LOG_INF("Latest message ID: %d", last_message_id);
int ret = coap_packet_parse(&response, buf, len, NULL, 0);
if (ret < 0) {
LOG_ERR("Invalid data received");
}
ret = coap_header_get_code(&response);
zassert_equal(ret, COAP_METHOD_POST, "Incorrect method, %d", ret);
uint16_t payload_len;
const uint8_t *payload = coap_packet_get_payload(&response, &payload_len);
zassert_mem_equal(payload, "echo testing", payload_len, "Incorrect payload");
ret = coap_find_options(&response, COAP_OPTION_ECHO, &option, 1);
zassert_equal(ret, 1, "Coap echo option not found, %d", ret);
zassert_mem_equal(option.value, "echo_value", option.len, "Incorrect echo data");
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake;
return 1;
}
@ -158,6 +241,66 @@ static ssize_t z_impl_zsock_recvfrom_custom_fake_unmatching(int sock, void *buf,
return sizeof(ack_data);
}
static ssize_t z_impl_zsock_recvfrom_custom_fake_echo(int sock, void *buf, size_t max_len,
int flags, struct sockaddr *src_addr,
socklen_t *addrlen)
{
uint16_t last_message_id = 0;
LOG_INF("Recvfrom");
uint8_t ack_data[] = {0x68, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xda, 0xef, 'e', 'c',
'h', 'o', '_', 'v', 'a', 'l', 'u', 'e'};
if (messages_needing_response[0] != 0) {
last_message_id = messages_needing_response[0];
messages_needing_response[0] = 0;
} else {
last_message_id = messages_needing_response[1];
messages_needing_response[1] = 0;
}
ack_data[2] = (uint8_t) (last_message_id >> 8);
ack_data[3] = (uint8_t) last_message_id;
memcpy(buf, ack_data, sizeof(ack_data));
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_response;
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake_echo;
return sizeof(ack_data);
}
static ssize_t z_impl_zsock_recvfrom_custom_fake_echo_next_req(int sock, void *buf, size_t max_len,
int flags, struct sockaddr *src_addr,
socklen_t *addrlen)
{
uint16_t last_message_id = 0;
LOG_INF("Recvfrom");
uint8_t ack_data[] = {0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xda, 0xef, 'e', 'c',
'h', 'o', '_', 'v', 'a', 'l', 'u', 'e'};
if (messages_needing_response[0] != 0) {
last_message_id = messages_needing_response[0];
messages_needing_response[0] = 0;
} else {
last_message_id = messages_needing_response[1];
messages_needing_response[1] = 0;
}
ack_data[2] = (uint8_t) (last_message_id >> 8);
ack_data[3] = (uint8_t) last_message_id;
memcpy(buf, ack_data, sizeof(ack_data));
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_response;
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake_echo_next_req;
return sizeof(ack_data);
}
static void *suite_setup(void)
{
coap_client_init(&client, NULL);
@ -214,6 +357,83 @@ ZTEST(coap_client, test_get_request)
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
}
ZTEST(coap_client, test_echo_option)
{
int ret = 0;
struct sockaddr address = {0};
struct coap_client_request client_request = {
.method = COAP_METHOD_GET,
.confirmable = true,
.path = test_path,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.cb = coap_callback,
.payload = NULL,
.len = 0
};
client_request.payload = short_payload;
client_request.len = strlen(short_payload);
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_echo;
k_sleep(K_MSEC(1));
LOG_INF("Send request");
ret = coap_client_req(&client, 0, &address, &client_request, -1);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
set_socket_events(ZSOCK_POLLIN);
k_sleep(K_MSEC(5));
k_sleep(K_MSEC(1000));
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
}
ZTEST(coap_client, test_echo_option_next_req)
{
int ret = 0;
struct sockaddr address = {0};
struct coap_client_request client_request = {
.method = COAP_METHOD_GET,
.confirmable = true,
.path = test_path,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.cb = coap_callback,
.payload = NULL,
.len = 0
};
client_request.payload = short_payload;
client_request.len = strlen(short_payload);
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_echo_next_req;
k_sleep(K_MSEC(1));
LOG_INF("Send request");
ret = coap_client_req(&client, 0, &address, &client_request, -1);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
set_socket_events(ZSOCK_POLLIN);
k_sleep(K_MSEC(5));
k_sleep(K_MSEC(1000));
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
char *payload = "echo testing";
client_request.method = COAP_METHOD_POST;
client_request.payload = payload;
client_request.len = strlen(payload);
LOG_INF("Send next request");
ret = coap_client_req(&client, 0, &address, &client_request, -1);
zassert_true(ret >= 0, "Sending request failed, %d", ret);
set_socket_events(ZSOCK_POLLIN);
k_sleep(K_MSEC(5));
k_sleep(K_MSEC(1000));
zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
}
ZTEST(coap_client, test_get_no_path)
{
int ret = 0;