Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys/net/nanocoap: Add CoAP over TCP support #21048

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions examples/gcoap/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,16 @@ static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu,
uri_parser_result_t urip;
uri_parser_process(&urip, _last_req_uri, strlen(_last_req_uri));
if (*_proxy_uri) {
gcoap_req_init(pdu, (uint8_t *)pdu->hdr, CONFIG_GCOAP_PDU_BUF_SIZE,
gcoap_req_init(pdu, pdu->buf, CONFIG_GCOAP_PDU_BUF_SIZE,
COAP_METHOD_GET, NULL);
}
else {
gcoap_req_init(pdu, (uint8_t *)pdu->hdr, CONFIG_GCOAP_PDU_BUF_SIZE,
gcoap_req_init(pdu, pdu->buf, CONFIG_GCOAP_PDU_BUF_SIZE,
COAP_METHOD_GET, urip.path);
}

if (msg_type == COAP_TYPE_ACK) {
coap_hdr_set_type(pdu->hdr, COAP_TYPE_CON);
coap_pkt_set_type(pdu, COAP_TYPE_CON);
}
block.blknum++;
coap_opt_add_block2_control(pdu, &block);
Expand All @@ -153,7 +153,7 @@ static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu,

int len = coap_opt_finish(pdu, COAP_OPT_FINISH_NONE);
gcoap_socket_type_t tl = _get_tl(*_proxy_uri ? _proxy_uri : _last_req_uri);
_send((uint8_t *)pdu->hdr, len, remote, memo->context, tl);
_send(pdu->buf, len, remote, memo->context, tl);
}
else {
puts("--- blockwise complete ---");
Expand Down Expand Up @@ -340,7 +340,7 @@ int gcoap_cli_cmd(int argc, char **argv)
}
}

coap_hdr_set_type(pdu.hdr, msg_type);
coap_pkt_set_type(&pdu, msg_type);

size_t paylen = 0;
if (apos < argc) {
Expand Down
36 changes: 27 additions & 9 deletions examples/nanocoap_server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@ BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

NETWORK_STACK ?= gnrc

# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
USEMODULE += netdev_default
USEMODULE += auto_init_gnrc_netif
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_default
USEMODULE += sock_udp
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo

USEMODULE += nanocoap_sock
USEMODULE += nanocoap_resources
USEMODULE += ipv6_addr

ifeq ($(NETWORK_STACK),gnrc)
USEMODULE += auto_init_gnrc_netif
# Specify the mandatory networking modules for IPv6
USEMODULE += gnrc_ipv6_default
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
endif
ifeq ($(NETWORK_STACK),lwip)
USEMODULE += auto_init_lwip_netif
USEMODULE += lwip_ipv6 lwip_ipv6_autoconfig
endif

USEMODULE += xtimer

Expand All @@ -43,7 +50,7 @@ ifneq (,$(filter $(BOARD),$(LOW_MEMORY_BOARDS)))
USEMODULE += prng_minstd
endif

# Enable fileserver for boards with plenty of memory
# Enable fileserver and TCP for boards with plenty of memory
HIGH_MEMORY_BOARDS := native native64 same54-xpro mcb2388

ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS)))
Expand All @@ -61,6 +68,17 @@ ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS)))
ifneq (,$(filter native native64,$(BOARD)))
USEMODULE += vfs_auto_format
endif

# async TCP is not supported on GNRC yet
ifeq ($(NETWORK_STACK),lwip)
USEMODULE += nanocoap_server_tcp
endif
endif

# if nanocaop_server_tcp is used: This app makes use of event_thread
# to run the TCP server
ifneq (,$(filter nanocoap_server_tcp,$(USEMODULE)))
USEMODULE += event_thread
endif

# Change this to 0 show compiler invocation lines by default:
Expand Down
29 changes: 18 additions & 11 deletions examples/nanocoap_server/coap_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
#include <stdio.h>
#include <string.h>

#include "event/callback.h"
#include "event/timeout.h"
#include "event/thread.h"
#include "fmt.h"
#include "net/nanocoap.h"
#include "net/nanocoap_sock.h"
#include "hashes/sha256.h"
#include "kernel_defines.h"

#if MODULE_NANOCOAP_SERVER_SEPARATE
# include "event/thread.h"
# include "event/timeout.h"
# include "event/callback.h"
#endif

/* internal value that can be read/written via CoAP */
static uint8_t internal_value = 0;
Expand All @@ -41,14 +43,14 @@
(uint8_t *)sub_uri, sub_uri_len);
}

static ssize_t _riot_board_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context)

Check warning on line 46 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
{
(void)context;
return coap_reply_simple(pkt, COAP_CODE_205, buf, len,
COAP_FORMAT_TEXT, (uint8_t*)RIOT_BOARD, strlen(RIOT_BOARD));
}

static ssize_t _riot_block2_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context)

Check warning on line 53 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
{
(void)context;
coap_block_slicer_t slicer;
Expand All @@ -63,7 +65,7 @@

/* Add actual content */
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, block2_intro, sizeof(block2_intro)-1);
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, (uint8_t*)RIOT_VERSION, strlen(RIOT_VERSION));

Check warning on line 68 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
bufpos += coap_blockwise_put_char(&slicer, bufpos, ')');
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, block2_board, sizeof(block2_board)-1);
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, (uint8_t*)RIOT_BOARD, strlen(RIOT_BOARD));
Expand All @@ -81,7 +83,7 @@
buf, len, payload_len, &slicer);
}

static ssize_t _riot_value_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context)

Check warning on line 86 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
{
(void) context;

Expand All @@ -92,7 +94,7 @@
/* read coap method type in packet */
unsigned method_flag = coap_method2flag(coap_get_code_detail(pkt));

switch(method_flag) {

Check warning on line 97 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

keyword 'switch' not followed by a single space
case COAP_GET:
/* write the response buffer with the internal value */
p += fmt_u32_dec(rsp, internal_value);
Expand Down Expand Up @@ -158,7 +160,7 @@
return reply_len;
}

uint8_t *pkt_pos = (uint8_t*)pkt->hdr + reply_len;
uint8_t *pkt_pos = pkt->buf + reply_len;
if (blockwise) {
pkt_pos += coap_opt_put_block1_control(pkt_pos, 0, &block1);
}
Expand All @@ -167,7 +169,7 @@
pkt_pos += fmt_bytes_hex((char *)pkt_pos, digest, sizeof(digest));
}

return pkt_pos - (uint8_t*)pkt->hdr;
return pkt_pos - pkt->buf;
}

NANOCOAP_RESOURCE(echo) {
Expand All @@ -177,7 +179,7 @@
.path = "/riot/board", .methods = COAP_GET, .handler = _riot_board_handler
};
NANOCOAP_RESOURCE(value) {
.path = "/riot/value", .methods = COAP_GET | COAP_PUT | COAP_POST, .handler = _riot_value_handler

Check warning on line 182 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
};
NANOCOAP_RESOURCE(ver) {
.path = "/riot/ver", .methods = COAP_GET, .handler = _riot_block2_handler
Expand All @@ -186,8 +188,8 @@
.path = "/sha256", .methods = COAP_POST, .handler = _sha256_handler
};

/* separate response requires an event thread to execute it */
#ifdef MODULE_EVENT_THREAD
/* separate response is an optional feature */
#ifdef MODULE_NANOCOAP_SERVER_SEPARATE
static nanocoap_server_response_ctx_t _separate_ctx;

static void _send_response(void *ctx)
Expand All @@ -199,12 +201,17 @@
response, sizeof(response));
}

static ssize_t _separate_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_request_ctx_t *context)

Check warning on line 204 in examples/nanocoap_server/coap_handler.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
{
static event_timeout_t event_timeout;
static event_callback_t event_timed = EVENT_CALLBACK_INIT(_send_response, &_separate_ctx);

if (event_timeout_is_pending(&event_timeout) && !sock_udp_ep_equal(context->remote, &_separate_ctx.remote)) {
if (event_timeout_is_pending(&event_timeout)) {
if (nanocoap_is_duplicate_in_separate_ctx(&_separate_ctx, pkt, context)) {
/* no need to check transport: Only UDP can have duplicates */
puts("_separate_handler(): duplicate");
return coap_reply_empty_ack(pkt, buf, len);
}
puts("_separate_handler(): response already scheduled");
return coap_build_reply(pkt, COAP_CODE_SERVICE_UNAVAILABLE, buf, len, 0);
}
Expand All @@ -221,13 +228,13 @@
&event_timed.super);
event_timeout_set(&event_timeout, 1 * MS_PER_SEC);

return coap_build_empty_ack(pkt, (void *)buf);
return coap_reply_empty_ack(pkt, buf, len);
}

NANOCOAP_RESOURCE(separate) {
.path = "/separate", .methods = COAP_GET, .handler = _separate_handler,
};
#endif /* MODULE_EVENT_THREAD */
#endif /* MODULE_NANOCOAP_SERVER_SEPARATE */

/* we can also include the fileserver module */
#ifdef MODULE_NANOCOAP_FILESERVER
Expand Down
16 changes: 15 additions & 1 deletion examples/nanocoap_server/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@
#include "net/nanocoap_sock.h"
#include "xtimer.h"

#if MODULE_NANOCOAP_SERVER_TCP
# include "event/thread.h"
#endif

#define COAP_INBUF_SIZE (256U)

#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];

#if MODULE_NANOCOAP_SERVER_TCP
static nanocoap_tcp_server_ctx_t tcp_ctx;
#endif

int main(void)
{
puts("RIOT nanocoap example application");
Expand All @@ -42,10 +50,16 @@ int main(void)
netifs_print_ipv6("\", \"");
puts("\"]}");

#if MODULE_NANOCOAP_SERVER_TCP
nanocoap_server_tcp(&tcp_ctx, EVENT_PRIO_MEDIUM, NULL);
#endif

#if MODULE_NANOCOAP_UDP
/* initialize nanocoap server instance */
uint8_t buf[COAP_INBUF_SIZE];
sock_udp_ep_t local = { .port=COAP_PORT, .family=AF_INET6 };
nanocoap_server(&local, buf, sizeof(buf));
nanocoap_server_udp(&local, buf, sizeof(buf));
#endif

/* should be never reached */
return 0;
Expand Down
3 changes: 3 additions & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ PSEUDOMODULES += nanocoap_%
PSEUDOMODULES += nanocoap_fileserver_callback
PSEUDOMODULES += nanocoap_fileserver_delete
PSEUDOMODULES += nanocoap_fileserver_put
PSEUDOMODULES += nanocoap_token_ext
PSEUDOMODULES += nanocoap_tcp
PSEUDOMODULES += nanocoap_udp
PSEUDOMODULES += netdev_default
PSEUDOMODULES += netdev_ieee802154_%
PSEUDOMODULES += netdev_ieee802154_rx_timestamp
Expand Down
52 changes: 52 additions & 0 deletions pkg/lwip/contrib/sock/tcp/lwip_sock_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include "lwip/api.h"
#include "lwip/opt.h"

#define ENABLE_DEBUG 0
#include "debug.h"

static inline void _tcp_sock_init(sock_tcp_t *sock, struct netconn *conn,
sock_tcp_queue_t *queue)
{
Expand Down Expand Up @@ -85,6 +88,9 @@ int sock_tcp_listen(sock_tcp_queue_t *queue, const sock_tcp_ep_t *local,
queue->array = queue_array;
queue->len = queue_len;
queue->used = 0;
/* This wipe is important: We cancel pending events in async event API when
* reusing the socket. If the memory would be uninitialized, bad things
* would happen */
memset(queue->array, 0, sizeof(sock_tcp_t) * queue_len);
mutex_unlock(&queue->mutex);

Expand Down Expand Up @@ -125,6 +131,14 @@ void sock_tcp_disconnect(sock_tcp_t *sock)
}
}

#ifdef SOCK_HAS_ASYNC_CTX
/* Cancel any pending event in the event queue */
if (sock->base.async_ctx.queue) {
event_cancel(sock->base.async_ctx.queue,
&sock->base.async_ctx.event.super);
}
#endif

mutex_unlock(&sock->mutex);
memset(&sock->mutex, 0, sizeof(mutex_t));
}
Expand Down Expand Up @@ -240,6 +254,16 @@ int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock,
for (unsigned short i = 0; i < queue->len; i++) {
sock_tcp_t *s = &queue->array[i];
if (s->base.conn == NULL) {
#ifdef SOCK_HAS_ASYNC_CTX
/* If there still is an event pending, we cannot just wipe
* its memory but have to remove the event from the list
* first. We rely here sock_tcp_listen to zero-initialize
* the sockets. */
if (s->base.async_ctx.queue) {
event_cancel(s->base.async_ctx.queue,
&s->base.async_ctx.event.super);
}
#endif
_tcp_sock_init(s, tmp, queue);
queue->used++;
assert(queue->used > 0);
Expand Down Expand Up @@ -293,6 +317,9 @@ int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock,
ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len,
uint32_t timeout)
{
DEBUG("sock_tcp_read(sock, data, max_len=%u, timeout=%" PRIu32 ")\n",
(unsigned)max_len, timeout);

struct pbuf *buf;
ssize_t recvd = 0;
ssize_t res = 0;
Expand Down Expand Up @@ -322,6 +349,7 @@ ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len,

if ((timeout == 0) && !mbox_avail(&sock->base.conn->recvmbox.mbox)) {
mutex_unlock(&sock->mutex);
DEBUG_PUTS("sock_tcp_read(): -EAGAIN");
return -EAGAIN;
}

Expand All @@ -333,6 +361,7 @@ ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len,
else {
err_t err;
if ((err = netconn_recv_tcp_pbuf(sock->base.conn, &buf)) < 0) {
DEBUG("sock_tcp_read(): %d", (int)err);
switch (err) {
case ERR_ABRT:
res = -ECONNABORTED;
Expand Down Expand Up @@ -400,6 +429,17 @@ ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len,
#endif
netconn_set_nonblocking(sock->base.conn, false);
mutex_unlock(&sock->mutex);

DEBUG("sock_tcp_read(): %d\n", (int)res);
if (ENABLE_DEBUG && (res > 0)) {
DEBUG(" ");
unsigned bytes_to_print = (res > 8) ? 8 : res;
for (unsigned i = 0; i < bytes_to_print; i++) {
DEBUG(" %02X", (unsigned)((uint8_t *)data)[i]);
}
DEBUG_PUTS((res > 8) ? "..." : "");
}

return res;
}

Expand All @@ -408,6 +448,16 @@ ssize_t sock_tcp_write(sock_tcp_t *sock, const void *data, size_t len)
struct netconn *conn;
int res = 0;

DEBUG("sock_tcp_write(sock, data, %u)\n", (unsigned)len);
if (ENABLE_DEBUG) {
DEBUG(" ");
unsigned bytes_to_print = (len > 8) ? 8 : len;
for (unsigned i = 0; i < bytes_to_print; i++) {
DEBUG(" %02X", (unsigned)((uint8_t *)data)[i]);
}
DEBUG_PUTS((len > 8) ? "..." : "");
}

assert(sock != NULL);
assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */
mutex_lock(&sock->mutex);
Expand All @@ -423,6 +473,8 @@ ssize_t sock_tcp_write(sock_tcp_t *sock, const void *data, size_t len)
NULL) so we can leave the mutex */
res = lwip_sock_send(conn, data, len, 0, NULL, NETCONN_TCP);

DEBUG("sock_tcp_write(): %d\n", (int)res);

return res;
}

Expand Down
19 changes: 18 additions & 1 deletion sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ ifneq (,$(filter dhcpv6_relay,$(USEMODULE)))
DEFAULT_MODULE += auto_init_dhcpv6_relay
USEMODULE += event
USEMODULE += sock_async_event
USEMODULE += sock_async
USEMODULE += sock_udp
endif

Expand Down Expand Up @@ -534,13 +535,24 @@ ifneq (,$(filter nanocoap_server_separate,$(USEMODULE)))
USEMODULE += sock_aux_local
endif

ifneq (,$(filter nanocoap_server_tcp,$(USEMODULE)))
USEMODULE += nanocoap_server
USEMODULE += nanocoap_tcp
USEMODULE += sock_async_event
endif

ifneq (,$(filter nanocoap_server,$(USEMODULE)))
USEMODULE += nanocoap_resources
USEMODULE += nanocoap_sock
endif

ifneq (,$(filter nanocoap_sock,$(USEMODULE)))
USEMODULE += sock_udp
ifneq (,$(filter nanocoap_udp,$(USEMODULE)))
USEMODULE += sock_udp
endif
ifneq (,$(filter nanocoap_tcp,$(USEMODULE)))
USEMODULE += sock_tcp
endif
USEMODULE += sock_util
USEMODULE += ztimer_msec
endif
Expand All @@ -565,6 +577,11 @@ ifneq (,$(filter nanocoap_vfs,$(USEMODULE)))
USEMODULE += vfs
endif

# default to UDP transport if none is selected
ifeq (,$(filter nanocoap_udp nanocoap_tcp nanocoap_dtls,$(USEMODULE)))
USEMODULE += nanocoap_udp
endif

ifneq (,$(filter nanocoap_%,$(USEMODULE)))
USEMODULE += nanocoap
endif
Expand Down
Loading
Loading