2023-09-21 17:25:22 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2023 Basalte bv
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_DECLARE(net_coap, CONFIG_COAP_LOG_LEVEL);
|
|
|
|
|
|
|
|
#include <zephyr/net/socket.h>
|
|
|
|
|
|
|
|
#include <zephyr/init.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/net/coap.h>
|
|
|
|
#include <zephyr/net/coap_link_format.h>
|
2023-11-29 16:53:09 +01:00
|
|
|
#include <zephyr/net/coap_mgmt.h>
|
2023-09-21 17:25:22 +02:00
|
|
|
#include <zephyr/net/coap_service.h>
|
2025-03-20 15:16:43 +01:00
|
|
|
#include <zephyr/sys/fdtable.h>
|
|
|
|
#include <zephyr/zvfs/eventfd.h>
|
2023-09-21 17:25:22 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
|
|
|
|
/* Lowest priority cooperative thread */
|
|
|
|
#define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
|
|
|
|
#else
|
|
|
|
#define THREAD_PRIORITY K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define ADDRLEN(sock) \
|
|
|
|
(((struct sockaddr *)sock)->sa_family == AF_INET ? \
|
|
|
|
sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))
|
|
|
|
|
|
|
|
/* Shortened defines */
|
|
|
|
#define MAX_OPTIONS CONFIG_COAP_SERVER_MESSAGE_OPTIONS
|
|
|
|
#define MAX_PENDINGS CONFIG_COAP_SERVICE_PENDING_MESSAGES
|
|
|
|
#define MAX_OBSERVERS CONFIG_COAP_SERVICE_OBSERVERS
|
2024-07-03 14:29:25 +02:00
|
|
|
#define MAX_POLL_FD CONFIG_ZVFS_POLL_MAX
|
2023-09-21 17:25:22 +02:00
|
|
|
|
2024-07-03 14:29:25 +02:00
|
|
|
BUILD_ASSERT(CONFIG_ZVFS_POLL_MAX > 0, "CONFIG_ZVFS_POLL_MAX can't be 0");
|
2023-09-21 17:25:22 +02:00
|
|
|
|
|
|
|
static K_MUTEX_DEFINE(lock);
|
2025-03-20 15:16:43 +01:00
|
|
|
static int control_sock;
|
2023-09-21 17:25:22 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC)
|
|
|
|
K_MEM_SLAB_DEFINE_STATIC(pending_data, CONFIG_COAP_SERVER_MESSAGE_SIZE,
|
|
|
|
CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC_BLOCKS, 4);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline void *coap_server_alloc(size_t len)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC)
|
|
|
|
void *ptr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (len > CONFIG_COAP_SERVER_MESSAGE_SIZE) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = k_mem_slab_alloc(&pending_data, &ptr, K_NO_WAIT);
|
|
|
|
if (ret < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
#elif defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_SYSTEM_HEAP)
|
|
|
|
return k_malloc(len);
|
|
|
|
#else
|
|
|
|
ARG_UNUSED(len);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void coap_server_free(void *ptr)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC)
|
|
|
|
k_mem_slab_free(&pending_data, ptr);
|
|
|
|
#elif defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_SYSTEM_HEAP)
|
|
|
|
k_free(ptr);
|
|
|
|
#else
|
|
|
|
ARG_UNUSED(ptr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int coap_service_remove_observer(const struct coap_service *service,
|
|
|
|
struct coap_resource *resource,
|
|
|
|
const struct sockaddr *addr,
|
|
|
|
const uint8_t *token, uint8_t tkl)
|
|
|
|
{
|
|
|
|
struct coap_observer *obs;
|
|
|
|
|
2023-11-10 12:23:51 +01:00
|
|
|
if (tkl > 0 && addr != NULL) {
|
|
|
|
/* Prefer addr+token to find the observer */
|
|
|
|
obs = coap_find_observer(service->data->observers, MAX_OBSERVERS, addr, token, tkl);
|
|
|
|
} else if (tkl > 0) {
|
2024-06-21 18:37:21 +10:00
|
|
|
/* Then try to find the observer by token */
|
2023-09-21 17:25:22 +02:00
|
|
|
obs = coap_find_observer_by_token(service->data->observers, MAX_OBSERVERS, token,
|
|
|
|
tkl);
|
|
|
|
} else if (addr != NULL) {
|
|
|
|
obs = coap_find_observer_by_addr(service->data->observers, MAX_OBSERVERS, addr);
|
|
|
|
} else {
|
|
|
|
/* Either a token or an address is required */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obs == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resource == NULL) {
|
|
|
|
COAP_SERVICE_FOREACH_RESOURCE(service, it) {
|
|
|
|
if (coap_remove_observer(it, obs)) {
|
|
|
|
memset(obs, 0, sizeof(*obs));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (coap_remove_observer(resource, obs)) {
|
|
|
|
memset(obs, 0, sizeof(*obs));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int coap_server_process(int sock_fd)
|
|
|
|
{
|
2024-01-31 10:17:01 +01:00
|
|
|
static uint8_t buf[CONFIG_COAP_SERVER_MESSAGE_SIZE];
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
struct sockaddr client_addr;
|
|
|
|
socklen_t client_addr_len = sizeof(client_addr);
|
|
|
|
struct coap_service *service = NULL;
|
|
|
|
struct coap_packet request;
|
|
|
|
struct coap_pending *pending;
|
|
|
|
struct coap_option options[MAX_OPTIONS] = { 0 };
|
|
|
|
uint8_t opt_num = MAX_OPTIONS;
|
|
|
|
uint8_t type;
|
|
|
|
ssize_t received;
|
|
|
|
int ret;
|
2025-01-09 17:05:30 +01:00
|
|
|
int flags = ZSOCK_MSG_DONTWAIT;
|
2023-09-21 17:25:22 +02:00
|
|
|
|
2025-01-09 17:05:30 +01:00
|
|
|
if (IS_ENABLED(CONFIG_COAP_SERVER_TRUNCATE_MSGS)) {
|
|
|
|
flags |= ZSOCK_MSG_TRUNC;
|
|
|
|
}
|
|
|
|
|
|
|
|
received = zsock_recvfrom(sock_fd, buf, sizeof(buf), flags, &client_addr, &client_addr_len);
|
2023-09-21 17:25:22 +02:00
|
|
|
|
|
|
|
if (received < 0) {
|
|
|
|
if (errno == EWOULDBLOCK) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_ERR("Failed to process client request (%d)", -errno);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2025-01-09 17:05:30 +01:00
|
|
|
ret = coap_packet_parse(&request, buf, MIN(received, sizeof(buf)), options, opt_num);
|
2023-09-21 17:25:22 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed To parse coap message (%d)", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
/* Find the active service */
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (svc->data->sock_fd == sock_fd) {
|
|
|
|
service = svc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (service == NULL) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
type = coap_header_get_type(&request);
|
|
|
|
|
2025-01-09 17:05:30 +01:00
|
|
|
if (received > sizeof(buf)) {
|
|
|
|
/* The message was truncated and can't be processed further */
|
|
|
|
struct coap_packet response;
|
|
|
|
uint8_t token[COAP_TOKEN_MAX_LEN];
|
|
|
|
uint8_t tkl = coap_header_get_token(&request, token);
|
|
|
|
uint16_t id = coap_header_get_id(&request);
|
|
|
|
|
|
|
|
if (type == COAP_TYPE_CON) {
|
|
|
|
type = COAP_TYPE_ACK;
|
|
|
|
} else {
|
|
|
|
type = COAP_TYPE_NON_CON;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = coap_packet_init(&response, buf, sizeof(buf), COAP_VERSION_1, type, tkl,
|
|
|
|
token, COAP_RESPONSE_CODE_REQUEST_TOO_LARGE, id);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to init response (%d)", ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = coap_append_option_int(&response, COAP_OPTION_SIZE1,
|
|
|
|
CONFIG_COAP_SERVER_MESSAGE_SIZE);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to add SIZE1 option (%d)", ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = coap_service_send(service, &response, &client_addr, client_addr_len, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to reply \"Request Entity Too Large\" (%d)", ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
pending = coap_pending_received(&request, service->data->pending, MAX_PENDINGS);
|
|
|
|
if (pending) {
|
|
|
|
uint8_t token[COAP_TOKEN_MAX_LEN];
|
|
|
|
uint8_t tkl;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case COAP_TYPE_RESET:
|
|
|
|
tkl = coap_header_get_token(&request, token);
|
|
|
|
coap_service_remove_observer(service, NULL, &client_addr, token, tkl);
|
|
|
|
__fallthrough;
|
|
|
|
case COAP_TYPE_ACK:
|
|
|
|
coap_server_free(pending->data);
|
|
|
|
coap_pending_clear(pending);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_WRN("Unexpected pending type %d", type);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto unlock;
|
|
|
|
} else if (type == COAP_TYPE_ACK || type == COAP_TYPE_RESET) {
|
|
|
|
LOG_WRN("Unexpected type %d without pending packet", type);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_COAP_SERVER_WELL_KNOWN_CORE) &&
|
|
|
|
coap_header_get_code(&request) == COAP_METHOD_GET &&
|
|
|
|
coap_uri_path_match(COAP_WELL_KNOWN_CORE_PATH, options, opt_num)) {
|
|
|
|
uint8_t well_known_buf[CONFIG_COAP_SERVER_MESSAGE_SIZE];
|
|
|
|
struct coap_packet response;
|
|
|
|
|
|
|
|
ret = coap_well_known_core_get_len(service->res_begin,
|
|
|
|
COAP_SERVICE_RESOURCE_COUNT(service),
|
|
|
|
&request, &response,
|
|
|
|
well_known_buf, sizeof(well_known_buf));
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to build well known core for %s (%d)", service->name, ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2023-12-14 15:53:28 +01:00
|
|
|
ret = coap_service_send(service, &response, &client_addr, client_addr_len, NULL);
|
2023-09-21 17:25:22 +02:00
|
|
|
} else {
|
|
|
|
ret = coap_handle_request_len(&request, service->res_begin,
|
|
|
|
COAP_SERVICE_RESOURCE_COUNT(service),
|
|
|
|
options, opt_num, &client_addr, client_addr_len);
|
|
|
|
|
2023-11-29 10:02:10 +01:00
|
|
|
/* Translate errors to response codes */
|
|
|
|
switch (ret) {
|
|
|
|
case -ENOENT:
|
|
|
|
ret = COAP_RESPONSE_CODE_NOT_FOUND;
|
|
|
|
break;
|
|
|
|
case -ENOTSUP:
|
|
|
|
ret = COAP_RESPONSE_CODE_BAD_REQUEST;
|
|
|
|
break;
|
|
|
|
case -EPERM:
|
|
|
|
ret = COAP_RESPONSE_CODE_NOT_ALLOWED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
/* Shortcut for replying a code without a body */
|
|
|
|
if (ret > 0 && type == COAP_TYPE_CON) {
|
|
|
|
/* Minimal sized ack buffer */
|
|
|
|
uint8_t ack_buf[COAP_TOKEN_MAX_LEN + 4U];
|
|
|
|
struct coap_packet ack;
|
|
|
|
|
|
|
|
ret = coap_ack_init(&ack, &request, ack_buf, sizeof(ack_buf), (uint8_t)ret);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to init ACK (%d)", ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2023-12-14 15:53:28 +01:00
|
|
|
ret = coap_service_send(service, &ack, &client_addr, client_addr_len, NULL);
|
2023-09-21 17:25:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
(void)k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void coap_server_retransmit(void)
|
|
|
|
{
|
|
|
|
struct coap_pending *pending;
|
|
|
|
int64_t remaining;
|
|
|
|
int64_t now = k_uptime_get();
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
(void)k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
|
|
|
|
COAP_SERVICE_FOREACH(service) {
|
|
|
|
if (service->data->sock_fd < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pending = coap_pending_next_to_expire(service->data->pending, MAX_PENDINGS);
|
|
|
|
if (pending == NULL) {
|
|
|
|
/* No work to be done */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the pending request has expired */
|
|
|
|
remaining = pending->t0 + pending->timeout - now;
|
|
|
|
if (remaining > 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (coap_pending_cycle(pending)) {
|
|
|
|
ret = zsock_sendto(service->data->sock_fd, pending->data, pending->len, 0,
|
|
|
|
&pending->addr, ADDRLEN(&pending->addr));
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to send pending retransmission for %s (%d)",
|
|
|
|
service->name, ret);
|
|
|
|
}
|
|
|
|
__ASSERT_NO_MSG(ret == pending->len);
|
|
|
|
} else {
|
|
|
|
LOG_WRN("Packet retransmission failed for %s", service->name);
|
|
|
|
|
|
|
|
coap_service_remove_observer(service, NULL, &pending->addr, NULL, 0U);
|
|
|
|
coap_server_free(pending->data);
|
|
|
|
coap_pending_clear(pending);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)k_mutex_unlock(&lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int coap_server_poll_timeout(void)
|
|
|
|
{
|
|
|
|
struct coap_pending *pending;
|
|
|
|
int64_t result = INT64_MAX;
|
|
|
|
int64_t remaining;
|
|
|
|
int64_t now = k_uptime_get();
|
|
|
|
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (svc->data->sock_fd < -1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pending = coap_pending_next_to_expire(svc->data->pending, MAX_PENDINGS);
|
|
|
|
if (pending == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
remaining = pending->t0 + pending->timeout - now;
|
|
|
|
if (result > remaining) {
|
|
|
|
result = remaining;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == INT64_MAX) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MAX(result, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void coap_server_update_services(void)
|
|
|
|
{
|
2025-03-20 15:16:43 +01:00
|
|
|
if (zvfs_eventfd_write(control_sock, 1)) {
|
2024-02-19 12:42:58 +01:00
|
|
|
LOG_ERR("Failed to notify server thread (%d)", errno);
|
|
|
|
}
|
2023-09-21 17:25:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool coap_service_in_section(const struct coap_service *service)
|
|
|
|
{
|
|
|
|
STRUCT_SECTION_START_EXTERN(coap_service);
|
|
|
|
STRUCT_SECTION_END_EXTERN(coap_service);
|
|
|
|
|
|
|
|
return STRUCT_SECTION_START(coap_service) <= service &&
|
|
|
|
STRUCT_SECTION_END(coap_service) > service;
|
|
|
|
}
|
|
|
|
|
2025-04-24 19:30:07 +03:00
|
|
|
static inline void coap_service_raise_event(const struct coap_service *service, uint64_t mgmt_event)
|
2023-11-29 16:53:09 +01:00
|
|
|
{
|
|
|
|
#if defined(CONFIG_NET_MGMT_EVENT_INFO)
|
|
|
|
const struct net_event_coap_service net_event = {
|
|
|
|
.service = service,
|
|
|
|
};
|
|
|
|
|
|
|
|
net_mgmt_event_notify_with_info(mgmt_event, NULL, (void *)&net_event, sizeof(net_event));
|
|
|
|
#else
|
|
|
|
ARG_UNUSED(service);
|
|
|
|
|
|
|
|
net_mgmt_event_notify(mgmt_event, NULL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
int coap_service_start(const struct coap_service *service)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
uint8_t af;
|
|
|
|
socklen_t len;
|
|
|
|
struct sockaddr_storage addr_storage;
|
|
|
|
union {
|
|
|
|
struct sockaddr *addr;
|
|
|
|
struct sockaddr_in *addr4;
|
|
|
|
struct sockaddr_in6 *addr6;
|
|
|
|
} addr_ptrs = {
|
|
|
|
.addr = (struct sockaddr *)&addr_storage,
|
|
|
|
};
|
2025-04-28 11:00:22 +02:00
|
|
|
int proto = IPPROTO_UDP;
|
2023-09-21 17:25:22 +02:00
|
|
|
|
|
|
|
if (!coap_service_in_section(service)) {
|
|
|
|
__ASSERT_NO_MSG(false);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
|
|
|
|
if (service->data->sock_fd >= 0) {
|
|
|
|
ret = -EALREADY;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the default address (in6addr_any / INADDR_ANY are all 0) */
|
|
|
|
addr_storage = (struct sockaddr_storage){0};
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6) && service->host != NULL &&
|
|
|
|
zsock_inet_pton(AF_INET6, service->host, &addr_ptrs.addr6->sin6_addr) == 1) {
|
|
|
|
/* if a literal IPv6 address is provided as the host, use IPv6 */
|
|
|
|
af = AF_INET6;
|
|
|
|
len = sizeof(struct sockaddr_in6);
|
|
|
|
|
|
|
|
addr_ptrs.addr6->sin6_family = AF_INET6;
|
|
|
|
addr_ptrs.addr6->sin6_port = htons(*service->port);
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4) && service->host != NULL &&
|
|
|
|
zsock_inet_pton(AF_INET, service->host, &addr_ptrs.addr4->sin_addr) == 1) {
|
|
|
|
/* if a literal IPv4 address is provided as the host, use IPv4 */
|
|
|
|
af = AF_INET;
|
|
|
|
len = sizeof(struct sockaddr_in);
|
|
|
|
|
|
|
|
addr_ptrs.addr4->sin_family = AF_INET;
|
|
|
|
addr_ptrs.addr4->sin_port = htons(*service->port);
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
|
|
|
/* prefer IPv6 if both IPv6 and IPv4 are supported */
|
|
|
|
af = AF_INET6;
|
|
|
|
len = sizeof(struct sockaddr_in6);
|
|
|
|
|
|
|
|
addr_ptrs.addr6->sin6_family = AF_INET6;
|
|
|
|
addr_ptrs.addr6->sin6_port = htons(*service->port);
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
|
|
|
af = AF_INET;
|
|
|
|
len = sizeof(struct sockaddr_in);
|
|
|
|
|
|
|
|
addr_ptrs.addr4->sin_family = AF_INET;
|
|
|
|
addr_ptrs.addr4->sin_port = htons(*service->port);
|
|
|
|
} else {
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2025-04-28 11:00:22 +02:00
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
|
|
if (service->sec_tag_list != NULL) {
|
|
|
|
proto = IPPROTO_DTLS_1_2;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
service->data->sock_fd = zsock_socket(af, SOCK_DGRAM, proto);
|
2023-09-21 17:25:22 +02:00
|
|
|
if (service->data->sock_fd < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2025-04-28 11:00:22 +02:00
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
|
|
if (service->sec_tag_list != NULL) {
|
|
|
|
int role = TLS_DTLS_ROLE_SERVER;
|
|
|
|
|
|
|
|
ret = zsock_setsockopt(service->data->sock_fd, SOL_TLS, TLS_SEC_TAG_LIST,
|
|
|
|
service->sec_tag_list, service->sec_tag_list_size);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = zsock_setsockopt(service->data->sock_fd, SOL_TLS, TLS_DTLS_ROLE,
|
|
|
|
&role, sizeof(role));
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2025-03-20 15:16:43 +01:00
|
|
|
ret = zsock_fcntl(service->data->sock_fd, ZVFS_F_SETFL, ZVFS_O_NONBLOCK);
|
2023-09-21 17:25:22 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = zsock_bind(service->data->sock_fd, addr_ptrs.addr, len);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*service->port == 0) {
|
|
|
|
/* ephemeral port - read back the port number */
|
|
|
|
len = sizeof(addr_storage);
|
|
|
|
ret = zsock_getsockname(service->data->sock_fd, addr_ptrs.addr, &len);
|
|
|
|
if (ret < 0) {
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (af == AF_INET6) {
|
|
|
|
*service->port = addr_ptrs.addr6->sin6_port;
|
|
|
|
} else {
|
|
|
|
*service->port = addr_ptrs.addr4->sin_port;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
coap_server_update_services();
|
|
|
|
|
2023-11-29 16:53:09 +01:00
|
|
|
coap_service_raise_event(service, NET_EVENT_COAP_SERVICE_STARTED);
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
close:
|
|
|
|
(void)zsock_close(service->data->sock_fd);
|
|
|
|
service->data->sock_fd = -1;
|
|
|
|
|
|
|
|
k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int coap_service_stop(const struct coap_service *service)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!coap_service_in_section(service)) {
|
|
|
|
__ASSERT_NO_MSG(false);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
|
|
|
|
if (service->data->sock_fd < 0) {
|
2023-11-29 16:53:09 +01:00
|
|
|
k_mutex_unlock(&lock);
|
|
|
|
return -EALREADY;
|
2023-09-21 17:25:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Closing a socket will trigger a poll event */
|
|
|
|
ret = zsock_close(service->data->sock_fd);
|
|
|
|
service->data->sock_fd = -1;
|
|
|
|
|
|
|
|
k_mutex_unlock(&lock);
|
|
|
|
|
2023-11-29 16:53:09 +01:00
|
|
|
coap_service_raise_event(service, NET_EVENT_COAP_SERVICE_STOPPED);
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-11-29 14:58:12 +01:00
|
|
|
int coap_service_is_running(const struct coap_service *service)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!coap_service_in_section(service)) {
|
|
|
|
__ASSERT_NO_MSG(false);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
|
|
|
|
ret = (service->data->sock_fd < 0) ? 0 : 1;
|
|
|
|
|
|
|
|
k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
int coap_service_send(const struct coap_service *service, const struct coap_packet *cpkt,
|
2023-12-14 15:53:28 +01:00
|
|
|
const struct sockaddr *addr, socklen_t addr_len,
|
|
|
|
const struct coap_transmission_parameters *params)
|
2023-09-21 17:25:22 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!coap_service_in_section(service)) {
|
|
|
|
__ASSERT_NO_MSG(false);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
|
|
|
|
if (service->data->sock_fd < 0) {
|
|
|
|
(void)k_mutex_unlock(&lock);
|
|
|
|
return -EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we should start with retransmits, if creating a pending message fails we still
|
|
|
|
* try to send.
|
|
|
|
*/
|
|
|
|
if (coap_header_get_type(cpkt) == COAP_TYPE_CON) {
|
|
|
|
struct coap_pending *pending = coap_pending_next_unused(service->data->pending,
|
|
|
|
MAX_PENDINGS);
|
|
|
|
|
|
|
|
if (pending == NULL) {
|
|
|
|
LOG_WRN("No pending message available for %s", service->name);
|
|
|
|
goto send;
|
|
|
|
}
|
|
|
|
|
2023-12-14 15:53:28 +01:00
|
|
|
ret = coap_pending_init(pending, cpkt, addr, params);
|
2023-09-21 17:25:22 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_WRN("Failed to init pending message for %s (%d)", service->name, ret);
|
|
|
|
goto send;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Replace tracked data with our allocated copy */
|
|
|
|
pending->data = coap_server_alloc(pending->len);
|
|
|
|
if (pending->data == NULL) {
|
|
|
|
LOG_WRN("Failed to allocate pending message data for %s", service->name);
|
|
|
|
coap_pending_clear(pending);
|
|
|
|
goto send;
|
|
|
|
}
|
|
|
|
memcpy(pending->data, cpkt->data, pending->len);
|
|
|
|
|
|
|
|
coap_pending_cycle(pending);
|
|
|
|
|
|
|
|
/* Trigger event in receive loop to schedule retransmit */
|
|
|
|
coap_server_update_services();
|
|
|
|
}
|
|
|
|
|
|
|
|
send:
|
|
|
|
(void)k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
ret = zsock_sendto(service->data->sock_fd, cpkt->data, cpkt->offset, 0, addr, addr_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to send CoAP message (%d)", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
__ASSERT_NO_MSG(ret == cpkt->offset);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int coap_resource_send(const struct coap_resource *resource, const struct coap_packet *cpkt,
|
2023-12-14 15:53:28 +01:00
|
|
|
const struct sockaddr *addr, socklen_t addr_len,
|
|
|
|
const struct coap_transmission_parameters *params)
|
2023-09-21 17:25:22 +02:00
|
|
|
{
|
|
|
|
/* Find owning service */
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (COAP_SERVICE_HAS_RESOURCE(svc, resource)) {
|
2023-12-14 15:53:28 +01:00
|
|
|
return coap_service_send(svc, cpkt, addr, addr_len, params);
|
2023-09-21 17:25:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int coap_resource_parse_observe(struct coap_resource *resource, const struct coap_packet *request,
|
|
|
|
const struct sockaddr *addr)
|
|
|
|
{
|
|
|
|
const struct coap_service *service = NULL;
|
|
|
|
int ret;
|
2023-11-10 12:23:51 +01:00
|
|
|
uint8_t token[COAP_TOKEN_MAX_LEN];
|
|
|
|
uint8_t tkl;
|
2023-09-21 17:25:22 +02:00
|
|
|
|
|
|
|
if (!coap_packet_is_request(request)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = coap_get_option_int(request, COAP_OPTION_OBSERVE);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find owning service */
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (COAP_SERVICE_HAS_RESOURCE(svc, resource)) {
|
|
|
|
service = svc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (service == NULL) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2023-11-10 12:23:51 +01:00
|
|
|
tkl = coap_header_get_token(request, token);
|
|
|
|
if (tkl == 0) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2023-09-21 17:25:22 +02:00
|
|
|
(void)k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
struct coap_observer *observer;
|
|
|
|
|
2023-11-10 12:23:51 +01:00
|
|
|
/* RFC7641 section 4.1 - Check if the current observer already exists */
|
|
|
|
observer = coap_find_observer(service->data->observers, MAX_OBSERVERS, addr, token,
|
|
|
|
tkl);
|
|
|
|
if (observer != NULL) {
|
|
|
|
/* Client refresh */
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* New client */
|
2023-09-21 17:25:22 +02:00
|
|
|
observer = coap_observer_next_unused(service->data->observers, MAX_OBSERVERS);
|
|
|
|
if (observer == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
coap_observer_init(observer, request, addr);
|
|
|
|
coap_register_observer(resource, observer);
|
|
|
|
} else if (ret == 1) {
|
|
|
|
ret = coap_service_remove_observer(service, resource, addr, token, tkl);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_WRN("Failed to remove observer (%d)", ret);
|
2025-05-28 09:35:02 +02:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
/* Observer not found */
|
|
|
|
ret = -ENOENT;
|
2023-09-21 17:25:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
(void)k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int coap_resource_remove_observer(struct coap_resource *resource,
|
|
|
|
const struct sockaddr *addr,
|
|
|
|
const uint8_t *token, uint8_t token_len)
|
|
|
|
{
|
|
|
|
const struct coap_service *service = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Find owning service */
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (COAP_SERVICE_HAS_RESOURCE(svc, resource)) {
|
|
|
|
service = svc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (service == NULL) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)k_mutex_lock(&lock, K_FOREVER);
|
|
|
|
ret = coap_service_remove_observer(service, resource, addr, token, token_len);
|
|
|
|
(void)k_mutex_unlock(&lock);
|
|
|
|
|
|
|
|
if (ret == 1) {
|
|
|
|
/* An observer was found and removed */
|
|
|
|
return 0;
|
|
|
|
} else if (ret == 0) {
|
|
|
|
/* No matching observer found */
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* An error occurred */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int coap_resource_remove_observer_by_addr(struct coap_resource *resource,
|
|
|
|
const struct sockaddr *addr)
|
|
|
|
{
|
|
|
|
return coap_resource_remove_observer(resource, addr, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int coap_resource_remove_observer_by_token(struct coap_resource *resource,
|
|
|
|
const uint8_t *token, uint8_t token_len)
|
|
|
|
{
|
|
|
|
return coap_resource_remove_observer(resource, NULL, token, token_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void coap_server_thread(void *p1, void *p2, void *p3)
|
|
|
|
{
|
|
|
|
struct zsock_pollfd sock_fds[MAX_POLL_FD];
|
|
|
|
int sock_nfds;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ARG_UNUSED(p1);
|
|
|
|
ARG_UNUSED(p2);
|
|
|
|
ARG_UNUSED(p3);
|
|
|
|
|
2025-03-20 15:16:43 +01:00
|
|
|
control_sock = zvfs_eventfd(0, ZVFS_EFD_NONBLOCK);
|
|
|
|
if (control_sock < 0) {
|
|
|
|
LOG_ERR("Failed to create event fd (%d)", -errno);
|
2023-09-21 17:25:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (svc->flags & COAP_SERVICE_AUTOSTART) {
|
|
|
|
ret = coap_service_start(svc);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to autostart service %s (%d)", svc->name, ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
sock_nfds = 0;
|
|
|
|
COAP_SERVICE_FOREACH(svc) {
|
|
|
|
if (svc->data->sock_fd < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (sock_nfds >= MAX_POLL_FD) {
|
|
|
|
LOG_ERR("Maximum active CoAP services reached (%d), "
|
2024-07-03 14:29:25 +02:00
|
|
|
"increase CONFIG_ZVFS_POLL_MAX to support more.",
|
2023-09-21 17:25:22 +02:00
|
|
|
MAX_POLL_FD);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sock_fds[sock_nfds].fd = svc->data->sock_fd;
|
|
|
|
sock_fds[sock_nfds].events = ZSOCK_POLLIN;
|
|
|
|
sock_fds[sock_nfds].revents = 0;
|
|
|
|
sock_nfds++;
|
|
|
|
}
|
|
|
|
|
2025-03-20 15:16:43 +01:00
|
|
|
/* Add event FD to allow wake up */
|
2023-09-21 17:25:22 +02:00
|
|
|
if (sock_nfds < MAX_POLL_FD) {
|
2025-03-20 15:16:43 +01:00
|
|
|
sock_fds[sock_nfds].fd = control_sock;
|
2023-09-21 17:25:22 +02:00
|
|
|
sock_fds[sock_nfds].events = ZSOCK_POLLIN;
|
|
|
|
sock_fds[sock_nfds].revents = 0;
|
|
|
|
sock_nfds++;
|
|
|
|
}
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(sock_nfds > 0);
|
|
|
|
|
|
|
|
ret = zsock_poll(sock_fds, sock_nfds, coap_server_poll_timeout());
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Poll error (%d)", -errno);
|
|
|
|
k_msleep(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < sock_nfds; ++i) {
|
|
|
|
/* Check the wake up event */
|
2025-03-20 15:16:43 +01:00
|
|
|
if (sock_fds[i].fd == control_sock &&
|
2023-09-21 17:25:22 +02:00
|
|
|
sock_fds[i].revents & ZSOCK_POLLIN) {
|
2025-03-20 15:16:43 +01:00
|
|
|
zvfs_eventfd_t tmp;
|
2023-09-21 17:25:22 +02:00
|
|
|
|
2025-03-20 15:16:43 +01:00
|
|
|
zvfs_eventfd_read(sock_fds[i].fd, &tmp);
|
2023-09-21 17:25:22 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if socket can receive/was closed first */
|
|
|
|
if (sock_fds[i].revents & ZSOCK_POLLIN) {
|
|
|
|
coap_server_process(sock_fds[i].fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock_fds[i].revents & ZSOCK_POLLERR) {
|
|
|
|
LOG_ERR("Poll error on %d", sock_fds[i].fd);
|
|
|
|
}
|
|
|
|
if (sock_fds[i].revents & ZSOCK_POLLHUP) {
|
2025-04-29 16:43:51 +02:00
|
|
|
LOG_DBG("Poll hup on %d", sock_fds[i].fd);
|
2023-09-21 17:25:22 +02:00
|
|
|
}
|
|
|
|
if (sock_fds[i].revents & ZSOCK_POLLNVAL) {
|
|
|
|
LOG_ERR("Poll invalid on %d", sock_fds[i].fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process retransmits */
|
|
|
|
coap_server_retransmit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
K_THREAD_DEFINE(coap_server_id, CONFIG_COAP_SERVER_STACK_SIZE,
|
|
|
|
coap_server_thread, NULL, NULL, NULL,
|
|
|
|
THREAD_PRIORITY, 0, 0);
|