1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

gcoap: multi-transport support for listeners

This commit is contained in:
Martine Lenders 2021-07-28 16:41:43 +02:00
parent 90eae8e0a4
commit 287bfdc10d
No known key found for this signature in database
GPG Key ID: 2134D77A5336DD80
4 changed files with 129 additions and 49 deletions

View File

@ -75,6 +75,7 @@ static const char *_link_params[] = {
static gcoap_listener_t _listener = {
&_resources[0],
ARRAY_SIZE(_resources),
GCOAP_SOCKET_TYPE_UNDEF,
_encode_link,
NULL,
NULL

View File

@ -704,6 +704,36 @@ typedef int (*gcoap_request_matcher_t)(gcoap_listener_t *listener,
const coap_resource_t **resource,
coap_pkt_t *pdu);
/**
* @brief CoAP socket types
*
* May be used as flags for @ref gcoap_listener_t, but must be used numerically
* with @ref gcoap_req_send_tl().
*/
typedef enum {
GCOAP_SOCKET_TYPE_UNDEF = 0x0, /**< undefined */
GCOAP_SOCKET_TYPE_UDP = 0x1, /**< Unencrypted UDP transport */
GCOAP_SOCKET_TYPE_DTLS = 0x2, /**< DTLS-over-UDP transport */
} gcoap_socket_type_t;
/**
* @brief CoAP socket to handle multiple transport types
*/
typedef struct {
gcoap_socket_type_t type; /**< Type of stored socket */
union {
sock_udp_t *udp;
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
sock_dtls_t *dtls;
#endif
} socket; /**< Stored socket */
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket.
Used for exchanging a session between
functions. */
#endif
} gcoap_socket_t;
/**
* @brief A modular collection of resources for a server
*/
@ -711,6 +741,16 @@ struct gcoap_listener {
const coap_resource_t *resources; /**< First element in the array of
* resources; must order alphabetically */
size_t resources_len; /**< Length of array */
/**
* @brief Transport type for the listener
*
* Any transport supported by the implementation can be set as a flag.
* If @ref GCOAP_SOCKET_TYPE_UNDEF is set, the listener listens on all
* supported transports. If non of the transports beyond UDP are compiled in
* (i.e. no usage of modules `gcoap_dtls`, ...) this will be ignored and
* @ref GCOAP_SOCKET_TYPE_UDP assumed.
*/
gcoap_socket_type_t tl_type;
gcoap_link_encoder_t link_encoder; /**< Writes a link for a resource */
struct gcoap_listener *next; /**< Next listener in list */
@ -750,33 +790,6 @@ typedef struct {
size_t pdu_len; /**< Length of pdu_buf */
} gcoap_resend_t;
/**
* @brief Coap socket types
*/
typedef enum {
GCOAP_SOCKET_TYPE_UNDEF = 0,
GCOAP_SOCKET_TYPE_UDP,
GCOAP_SOCKET_TYPE_DTLS
} gcoap_socket_type_t;
/**
* @brief Coap socket to handle multiple transport types
*/
typedef struct {
gcoap_socket_type_t type; /**< Type of stored socket */
union {
sock_udp_t *udp;
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
sock_dtls_t *dtls;
#endif
} socket; /**< Stored socket */
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket.
Used for exchanging a session between
functions. */
#endif
} gcoap_socket_t;
/**
* @brief Memo to handle a response for a request
*/
@ -918,8 +931,9 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len,
* @param[in] resp_handler Callback when response received, may be NULL
* @param[in] context User defined context passed to the response handler
* @param[in] tl_type The transport type to use for send. When
* GCOAP_SOCKET_TYPE_UNDEF is selected, the highest
* available (by value) will be selected.
* @ref GCOAP_SOCKET_TYPE_UNDEF is selected, the highest
* available (by value) will be selected. Only single
* types are allowed, not a combination of them.
*
* @return length of the packet
* @return -ENOTCONN, if DTLS was used and session establishment failed
@ -940,7 +954,7 @@ ssize_t gcoap_req_send_tl(const uint8_t *buf, size_t len,
* @param[in] resp_handler Callback when response received, may be NULL
* @param[in] context User defined context passed to the response handler
*
* @note The highest supported (by value) coap_socket_type_t will be selected
* @note The highest supported (by value) gcoap_socket_type_t will be selected
* as transport type.
*
* @return length of the packet
@ -1037,6 +1051,35 @@ uint8_t gcoap_op_state(void);
* @brief Get the resource list, currently only `CoRE Link Format`
* (COAP_FORMAT_LINK) supported
*
* @deprecated Will be an alias for @ref gcoap_get_resource_list after the
* 2022.01 release. Will be removed after the 2022.04 release.
*
* If @p buf := NULL, nothing will be written but the size of the resulting
* resource list is computed and returned.
*
* @param[out] buf output buffer to write resource list into, my be NULL
* @param[in] maxlen length of @p buf, ignored if @p buf is NULL
* @param[in] cf content format to use for the resource list, currently
* only COAP_FORMAT_LINK supported
* @param[in] tl_type Transport type to get the list for.
* @ref GCOAP_SOCKET_TYPE_UNDEF for all transport types.
* If non of the transports beyond UDP are compiled in
* (i.e. usage of modules no `gcoap_dtls`, ...) this will
* be ignored and @ref GCOAP_SOCKET_TYPE_UDP assumed.
*
* @todo add support for `JSON CoRE Link Format`
* @todo add support for 'CBOR CoRE Link Format`
*
* @return the number of bytes written to @p buf
* @return -1 on error
*/
int gcoap_get_resource_list_tl(void *buf, size_t maxlen, uint8_t cf,
gcoap_socket_type_t tl_type);
/**
* @brief Get the resource list for all transports,
* currently only `CoRE Link Format` (COAP_FORMAT_LINK) supported
*
* If @p buf := NULL, nothing will be written but the size of the resulting
* resource list is computed and returned.
*
@ -1051,7 +1094,10 @@ uint8_t gcoap_op_state(void);
* @return the number of bytes written to @p buf
* @return -1 on error
*/
int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf);
static inline int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf)
{
return gcoap_get_resource_list_tl(buf, maxlen, cf, GCOAP_SOCKET_TYPE_UNDEF);
}
/**
* @brief Writes a resource in CoRE Link Format to a provided buffer.

View File

@ -208,6 +208,13 @@ typedef struct {
BITFIELD(opt_crit, CONFIG_NANOCOAP_NOPTS_MAX); /**< unhandled critical option */
#ifdef MODULE_GCOAP
uint32_t observe_value; /**< observe value */
/**
* @brief transport the packet was received over
* @see @ref gcoap_socket_type_t for values.
* @note @ref gcoap_socket_type_t can not be used, as this would
* cyclically include the @ref net_gcoap header.
*/
uint32_t tl_type;
#endif
} coap_pkt_t;

View File

@ -66,7 +66,8 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
static void _expire_request(gcoap_request_memo_t *memo);
static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *pdu,
const sock_udp_ep_t *remote, bool by_mid);
static int _find_resource(coap_pkt_t *pdu,
static int _find_resource(gcoap_socket_type_t tl_type,
coap_pkt_t *pdu,
const coap_resource_t **resource_ptr,
gcoap_listener_t **listener_ptr);
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote);
@ -92,6 +93,7 @@ const coap_resource_t _default_resources[] = {
static gcoap_listener_t _default_listener = {
&_default_resources[0],
ARRAY_SIZE(_default_resources),
GCOAP_SOCKET_TYPE_UNDEF,
NULL,
NULL,
_request_matcher_default
@ -130,7 +132,7 @@ static sock_udp_t _sock_udp;
#if IS_USED(MODULE_GCOAP_DTLS)
/* DTLS variables and definitions */
#define SOCK_DTLS_CLIENT_TAG (2)
static sock_udp_t _sock_dtls_base;
static sock_dtls_t _sock_dtls;
static kernel_pid_t _auth_waiting_thread;
@ -147,11 +149,7 @@ static void *_event_loop(void *arg)
memset(&local, 0, sizeof(sock_udp_ep_t));
local.family = AF_INET6;
local.netif = SOCK_ADDR_ANY_NETIF;
if (IS_USED(MODULE_GCOAP_DTLS)) {
local.port = CONFIG_GCOAPS_PORT;
} else {
local.port = CONFIG_GCOAP_PORT;
}
local.port = CONFIG_GCOAP_PORT;
int res = sock_udp_create(&_sock_udp, &local, NULL, 0);
if (res < 0) {
DEBUG("gcoap: cannot create sock: %d\n", res);
@ -159,20 +157,25 @@ static void *_event_loop(void *arg)
}
event_queue_init(&_queue);
sock_udp_event_init(&_sock_udp, &_queue, _on_sock_udp_evt, NULL);
if (IS_USED(MODULE_GCOAP_DTLS)) {
#if IS_USED(MODULE_GCOAP_DTLS)
if (sock_dtls_create(&_sock_dtls, &_sock_udp,
local.port = CONFIG_GCOAPS_PORT;
if (sock_udp_create(&_sock_dtls_base, &local, NULL, 0)) {
DEBUG("gcoap: error creating DTLS transport sock\n");
return 0;
}
if (sock_dtls_create(&_sock_dtls, &_sock_dtls_base,
CREDMAN_TAG_EMPTY,
SOCK_DTLS_1_2, SOCK_DTLS_SERVER) < 0) {
DEBUG("gcoap: error creating DTLS sock");
sock_udp_close(&_sock_udp);
DEBUG("gcoap: error creating DTLS sock\n");
sock_udp_close(&_sock_dtls_base);
return 0;
}
sock_dtls_event_init(&_sock_dtls, &_queue, _on_sock_dtls_evt,
NULL);
#endif
} else {
sock_udp_event_init(&_sock_udp, &_queue, _on_sock_udp_evt, NULL);
}
event_loop(&_queue);
@ -537,7 +540,7 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
gcoap_observe_memo_t *memo = NULL;
gcoap_observe_memo_t *resource_memo = NULL;
switch (_find_resource(pdu, &resource, &listener)) {
switch (_find_resource(sock->type, pdu, &resource, &listener)) {
case GCOAP_RESOURCE_WRONG_METHOD:
return gcoap_response(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED);
case GCOAP_RESOURCE_NO_PATH:
@ -631,6 +634,7 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf,
return -1;
}
pdu->tl_type = (uint32_t)sock->type;
ssize_t pdu_len = resource->handler(pdu, buf, len, resource->context);
if (pdu_len < 0) {
pdu_len = gcoap_response(pdu, buf, len,
@ -689,6 +693,7 @@ static int _request_matcher_default(gcoap_listener_t *listener,
/*
* Searches listener registrations for the resource matching the path in a PDU.
*
* param[in] tl_type -- transport the request for the resource came over.
* param[in] pdu -- the PDU to check the resource for
* param[out] resource_ptr -- found resource
* param[out] listener_ptr -- listener for found resource
@ -697,7 +702,8 @@ static int _request_matcher_default(gcoap_listener_t *listener,
* code didn't match and `GCOAP_RESOURCE_NO_PATH` if no matching
* resource was found.
*/
static int _find_resource(coap_pkt_t *pdu,
static int _find_resource(gcoap_socket_type_t tl_type,
coap_pkt_t *pdu,
const coap_resource_t **resource_ptr,
gcoap_listener_t **listener_ptr)
{
@ -708,8 +714,17 @@ static int _find_resource(coap_pkt_t *pdu,
while (listener) {
const coap_resource_t *resource;
int res = listener->request_matcher(listener, &resource, pdu);
int res;
/* only makes sense to check if non-UDP transports are supported,
* so check if module is used first. */
if (IS_USED(MODULE_GCOAP_DTLS) &&
(listener->tl_type != GCOAP_SOCKET_TYPE_UNDEF) &&
!(listener->tl_type & tl_type)) {
listener = listener->next;
continue;
}
res = listener->request_matcher(listener, &resource, pdu);
/* check next resource on mismatch */
if (res == GCOAP_RESOURCE_NO_PATH) {
listener = listener->next;
@ -826,8 +841,9 @@ static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t le
coap_opt_add_format(pdu, COAP_FORMAT_LINK);
ssize_t plen = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD);
plen += gcoap_get_resource_list(pdu->payload, (size_t)pdu->payload_len,
COAP_FORMAT_LINK);
plen += gcoap_get_resource_list_tl(pdu->payload, (size_t)pdu->payload_len,
COAP_FORMAT_LINK,
(gcoap_socket_type_t)pdu->tl_type);
return plen;
}
@ -1332,7 +1348,8 @@ uint8_t gcoap_op_state(void)
return count;
}
int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf)
int gcoap_get_resource_list_tl(void *buf, size_t maxlen, uint8_t cf,
gcoap_socket_type_t tl_type)
{
assert(cf == COAP_FORMAT_LINK);
@ -1351,6 +1368,15 @@ int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf)
if (!listener->link_encoder) {
continue;
}
/* only makes sense to check if non-UDP transports are supported,
* so check if module is used first. */
if (IS_USED(MODULE_GCOAP_DTLS) &&
(tl_type != GCOAP_SOCKET_TYPE_UNDEF) &&
(listener->tl_type != GCOAP_SOCKET_TYPE_UNDEF) &&
((listener->tl_type & GCOAP_SOCKET_TYPE_UDP) != (tl_type & GCOAP_SOCKET_TYPE_UDP)) &&
((listener->tl_type & GCOAP_SOCKET_TYPE_DTLS) != (tl_type & GCOAP_SOCKET_TYPE_DTLS))) {
continue;
}
ctx.link_pos = 0;
for (; ctx.link_pos < listener->resources_len; ctx.link_pos++) {