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

pkg/libcoap: Add in libcoap CoAP library support

libcoap version 4.3.5rc3.

Using Sock interface.

Includes a CoAP client and CoAP server example.

tests/pkg/libcoap: Simple CoAP over DTLS loopback test.
This commit is contained in:
Jon Shallow 2023-08-15 15:46:13 +00:00
parent 821a7e8c9f
commit 84938e4ebb
32 changed files with 2067 additions and 0 deletions

View File

@ -0,0 +1,20 @@
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menu "libcoap-client"
depends on USEPKG_LIBCOAP
config LIBCOAP_CLIENT_URI
string "CoAP URI to connect to"
default "coap://[fe80::405:5aff:fe15:9b7f]/.well-known/core"
config LIBCOAP_USE_PSK
string "Secret to use for PSK communications"
default "secretPSK"
depends on USEMODULE_TINYDTLS
config LIBCOAP_USE_PSK_ID
string "User ID to use for PSK communications"
default "user_abc"
depends on USEMODULE_TINYDTLS
endmenu # libcoap-client

View File

@ -0,0 +1,69 @@
# name of your application
APPLICATION = libcoap-client
# If no BOARD is found in the environment, use this default:
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..
# 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
# Activate ICMPv6 error messages
USEMODULE += gnrc_icmpv6_error
# Specify the mandatory networking module for a IPv6 routing node
USEMODULE += gnrc_ipv6_router_default
# Add a routing protocol
USEMODULE += gnrc_rpl
USEMODULE += auto_init_gnrc_rpl
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_default
USEMODULE += memarray
USEMODULE += ipv4_addr
# a cryptographically secure implementation of PRNG is needed for tinydtls
# Uncomment the following 3 lines for tinydtls support
CFLAGS += -DWITH_RIOT_SOCK
USEPKG += tinydtls
USEMODULE += prng_sha1prng
# libcoap support
USEPKG += libcoap
# Uncomment to enable libcoap OSCORE support
# USEMODULE += libcoap_oscore
# Configure if DNS is required
# USEMODULE += sock_dns
# Support 64 bit ticks
USEMODULE += ztimer64_xtimer_compat
# Add also the shell, some shell commands
USEMODULE += shell
USEMODULE += shell_cmds_default
USEMODULE += ps
USEMODULE += netstats_l2
USEMODULE += netstats_ipv6
USEMODULE += netstats_rpl
# libcoap needs some space
CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\)
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# Change this to 0 show compiler invocation lines by default:
# QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,70 @@
BOARD_INSUFFICIENT_MEMORY := \
airfy-beacon \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-nano \
arduino-uno \
atmega256rfr2-xpro \
atmega328p \
atmega328p-xplained-mini \
atmega8 \
atxmega-a3bu-xplained \
b-l072z-lrwan1 \
blackpill-stm32f103c8 \
blackpill-stm32f103cb \
bluepill-stm32f030c8 \
bluepill-stm32f103c8 \
bluepill-stm32f103cb \
calliope-mini \
cc2650-launchpad \
cc2650stk \
derfmega128 \
hifive1 \
hifive1b \
i-nucleo-lrwan1 \
im880b \
lsn50 \
maple-mini \
microbit \
microduino-corerf \
msb-430 \
msb-430h \
nrf51dk \
nrf51dongle \
nrf6310 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f070rb \
nucleo-f072rb \
nucleo-f103rb \
nucleo-f302r8 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-l073rz \
olimex-msp430-h1611 \
olimex-msp430-h2618 \
opencm904 \
samd10-xmini \
saml10-xpro \
saml11-xpro \
samr21-xpro \
slstk3400a \
spark-core \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32f7508-dk \
stm32g0316-disco \
stm32l0538-disco \
stm32mp157c-dk2 \
telosb \
waspmote-pro \
yunjia-nrf51822 \
z1 \
zigduino \
#

View File

@ -0,0 +1,24 @@
# libcoap client example
This example shows how to configure a client to use libcoap
## Fast configuration (Between RIOT instances):
Preparing the logical interfaces:
sudo ./../../dist/tools/tapsetup/tapsetup --create 2
## Client invocation
For the client:
PORT=tap0 make term
coapc coap://[ip6-address]/some/path
The IP address to connect to needs to be that as returned by libcoap_server,
or that of the tap0 interface, etc.
## Handling the static memory allocation
libcoap for RIOT is using the `sys/memarray` module and therefore there
are certain limits. Said resources are defined in `libcoap/src/coap_mem.c`,
but can be overwritten at compile time.

View File

@ -0,0 +1,8 @@
CONFIG_LIBCOAP_CLIENT_SUPPORT=y
CONFIG_LIBCOAP_CLIENT_URI="coap://[fe80::405:5aff:fe15:9b7f]/.well-known/core"
CONFIG_LIBCOAP_USE_PSK="secretPSK"
CONFIG_LIBCOAP_USE_PSK_ID="user_abc"
# Logging levels are defined in pkg/libcoap using Kconfig

View File

@ -0,0 +1,270 @@
/*
* client-coap.c -- RIOT client example
*
* Copyright (C) 2023-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#include <thread.h>
#include <debug.h>
#include <coap3/coap.h>
#include "client-coap.h"
#include <stdio.h>
#include "macros/utils.h"
#ifdef CONFIG_LIBCOAP_CLIENT_URI
#define COAP_CLIENT_URI CONFIG_LIBCOAP_CLIENT_URI
#else /* ! CONFIG_LIBCOAP_CLIENT_URI */
#define COAP_CLIENT_URI "coap://[fe80::405:5aff:fe15:9b7f]/.well-known/core"
#endif /* ! CONFIG_LIBCOAP_CLIENT_URI */
#ifdef CONFIG_LIBCOAP_USE_PSK
#define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK
#else /* ! CONFIG_LIBCOAP_USE_PSK */
#define COAP_USE_PSK NULL
#endif /* ! CONFIG_LIBCOAP_USE_PSK */
#ifdef CONFIG_LIBCOAP_USE_PSK_ID
#define COAP_USE_PSK_ID CONFIG_LIBCOAP_USE_PSK_ID
#else /* ! CONFIG_LIBCOAP_USE_PSK_ID */
#define COAP_USE_PSK_ID NULL
#endif /* ! CONFIG_LIBCOAP_USE_PSK_ID */
static coap_context_t *main_coap_context = NULL;
static coap_optlist_t *optlist = NULL;
static int quit = 0;
static int is_mcast = 0;
#define DEFAULT_WAIT_TIME 15
unsigned int wait_seconds = DEFAULT_WAIT_TIME; /* default timeout in seconds */
static coap_response_t
message_handler(coap_session_t *session,
const coap_pdu_t *sent,
const coap_pdu_t *received,
const coap_mid_t id)
{
const uint8_t *data;
size_t len;
size_t offset;
size_t total;
(void)session;
(void)sent;
(void)id;
if (coap_get_data_large(received, &len, &data, &offset, &total)) {
printf("%*.*s", (int)len, (int)len, (const char *)data);
if (len + offset == total) {
printf("\n");
quit = 1;
}
}
return COAP_RESPONSE_OK;
}
static void
nack_handler(coap_session_t *session COAP_UNUSED,
const coap_pdu_t *sent COAP_UNUSED,
const coap_nack_reason_t reason,
const coap_mid_t id COAP_UNUSED)
{
switch (reason) {
case COAP_NACK_TOO_MANY_RETRIES:
case COAP_NACK_NOT_DELIVERABLE:
case COAP_NACK_RST:
case COAP_NACK_TLS_FAILED:
case COAP_NACK_TLS_LAYER_FAILED:
case COAP_NACK_WS_LAYER_FAILED:
case COAP_NACK_WS_FAILED:
coap_log_err("cannot send CoAP pdu\n");
quit = 1;
break;
case COAP_NACK_ICMP_ISSUE:
case COAP_NACK_BAD_RESPONSE:
default:
break;
}
return;
}
static int
resolve_address(const char *host, const char *service, coap_address_t *dst,
int scheme_hint_bits)
{
uint16_t port = service ? atoi(service) : 0;
int ret = 0;
coap_str_const_t str_host;
coap_addr_info_t *addr_info;
str_host.s = (const uint8_t *)host;
str_host.length = strlen(host);
addr_info = coap_resolve_address_info(&str_host, port, port, port, port,
AF_UNSPEC, scheme_hint_bits,
COAP_RESOLVE_TYPE_REMOTE);
if (addr_info) {
ret = 1;
*dst = addr_info->addr;
is_mcast = coap_is_mcast(dst);
}
coap_free_address_info(addr_info);
return ret;
}
void
client_coap_init(int argc, char **argv)
{
coap_session_t *session = NULL;
coap_pdu_t *pdu;
coap_address_t dst;
coap_mid_t mid;
int len;
coap_uri_t uri;
char portbuf[8];
unsigned int wait_ms = 0;
int result = -1;
#define BUFSIZE 100
unsigned char buf[BUFSIZE];
int res;
const char *coap_uri = COAP_CLIENT_URI;
if (argc > 1) {
coap_uri = argv[1];
}
/* Initialize libcoap library */
coap_startup();
coap_set_log_level(COAP_MAX_LOGGING_LEVEL);
/* Parse the URI */
len = coap_split_uri((const unsigned char *)coap_uri, strlen(coap_uri), &uri);
if (len != 0) {
coap_log_warn("Failed to parse uri %s\n", coap_uri);
goto fail;
}
snprintf(portbuf, sizeof(portbuf), "%d", uri.port);
snprintf((char *)buf, sizeof(buf), "%*.*s", (int)uri.host.length,
(int)uri.host.length, (const char *)uri.host.s);
/* resolve destination address where packet should be sent */
len = resolve_address((const char *)buf, portbuf, &dst, 1 << uri.scheme);
if (len <= 0) {
coap_log_warn("Failed to resolve address %*.*s\n", (int)uri.host.length,
(int)uri.host.length, (const char *)uri.host.s);
goto fail;
}
main_coap_context = coap_new_context(NULL);
if (!main_coap_context) {
coap_log_warn("Failed to initialize context\n");
goto fail;
}
coap_context_set_block_mode(main_coap_context, COAP_BLOCK_USE_LIBCOAP);
if (uri.scheme == COAP_URI_SCHEME_COAP) {
session = coap_new_client_session(main_coap_context, NULL, &dst,
COAP_PROTO_UDP);
}
else if (uri.scheme == COAP_URI_SCHEME_COAP_TCP) {
session = coap_new_client_session(main_coap_context, NULL, &dst,
COAP_PROTO_TCP);
#if defined (COAP_USE_PSK) && defined(COAP_USE_PSK_ID)
}
else {
static coap_dtls_cpsk_t dtls_psk;
static char client_sni[256];
memset(&dtls_psk, 0, sizeof(dtls_psk));
dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
snprintf(client_sni, sizeof(client_sni), "%*.*s", (int)uri.host.length, (int)uri.host.length, uri.host.s);
dtls_psk.client_sni = client_sni;
dtls_psk.psk_info.identity.s = (const uint8_t *)COAP_USE_PSK_ID;
dtls_psk.psk_info.identity.length = strlen(COAP_USE_PSK_ID);
dtls_psk.psk_info.key.s = (const uint8_t *)COAP_USE_PSK;
dtls_psk.psk_info.key.length = strlen(COAP_USE_PSK);
session = coap_new_client_session_psk2(main_coap_context, NULL, &dst,
COAP_PROTO_DTLS, &dtls_psk);
#else /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */
coap_log_err("CONFIG_LIBCOAP_USE_PSK and CONFIG_LIBCOAP_USE_PSK_ID not defined\n");
goto fail;
#endif /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */
}
if (!session) {
coap_log_warn("Failed to create session\n");
goto fail;
}
coap_register_response_handler(main_coap_context, message_handler);
coap_register_nack_handler(main_coap_context, nack_handler);
/* construct CoAP message */
pdu = coap_pdu_init(is_mcast ? COAP_MESSAGE_NON : COAP_MESSAGE_CON,
COAP_REQUEST_CODE_GET,
coap_new_message_id(session),
coap_session_max_pdu_size(session));
if (!pdu) {
coap_log_warn("Failed to create PDU\n");
goto fail;
}
res = coap_uri_into_optlist(&uri, &dst, &optlist, 1);
if (res) {
coap_log_warn("Failed to create options\n");
goto fail;
}
/* Add option list (which will be sorted) to the PDU */
if (optlist) {
res = coap_add_optlist_pdu(pdu, &optlist);
if (res != 1) {
coap_log_warn("Failed to add options to PDU\n");
goto fail;
}
}
if (is_mcast) {
/* Allow for other servers to respond within DEFAULT_LEISURE RFC7252 8.2 */
wait_seconds = coap_session_get_default_leisure(session).integer_part + 1;
}
wait_ms = wait_seconds * 1000;
/* and send the PDU */
mid = coap_send(session, pdu);
if (mid == COAP_INVALID_MID) {
coap_log_warn("Failed to send PDU\n");
goto fail;
}
while (!quit || is_mcast) {
result = coap_io_process(main_coap_context, 1000);
if (result >= 0) {
if (wait_ms > 0) {
if ((unsigned)result >= wait_ms) {
coap_log_info("timeout\n");
break;
} else {
wait_ms -= result;
}
}
}
}
fail:
/* Clean up library usage so client can be run again */
quit = 0;
coap_delete_optlist(optlist);
optlist = NULL;
coap_session_release(session);
session = NULL;
coap_free_context(main_coap_context);
main_coap_context = NULL;
coap_cleanup();
}

View File

@ -0,0 +1,26 @@
/*
* client-coap.h -- RIOT client example
*
* Copyright (C) 2023-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#ifndef CLIENT_COAP_H
#define CLIENT_COAP_H
#ifdef __cplusplus
extern "C" {
#endif
/* Start up the CoAP Client */
void client_coap_init(int argc, char **argv);
#ifdef __cplusplus
}
#endif
#endif /* CLIENT_COAP_H */

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* Copyright (C) 2018 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Example application for libcoap client
*
* @author Raul Fuentes <>
*
* @}
*/
#include <stdio.h>
#include "shell.h"
#include "msg.h"
#include "coap3/coap.h"
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int client_coap_init(int argc, char **argv);
static const shell_command_t shell_commands[] = {
{ "coapc", "Start a libcoap client", client_coap_init },
{ NULL, NULL, NULL }
};
int
main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("RIOT libcoap client testing implementation");
/* start shell */
puts("All up, running the shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
/* should be never reached */
return 0;
}

View File

@ -0,0 +1,22 @@
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menu "libcoap-server"
depends on USEPKG_LIBCOAP
config LIBCOAP_USE_PSK
string "Secret to use for PSK communications"
default "secretPSK"
depends on USEMODULE_TINYDTLS
config LIBCOAP_CLIENT_SUPPORT
bool "Set to y if ongoing proxy support is required"
default n
if LIBCOAP_CLIENT_SUPPORT
config LIBCOAP_USE_PSK_ID
string "User ID to use for ongoing PSK communications"
default "user_abc"
depends on USEMODULE_TINYDTLS
endif # LIBCOAP_CLIENT_SUPPORT
endmenu # libcoap-server

View File

@ -0,0 +1,70 @@
# name of your application
APPLICATION = libcoap-server
# If no BOARD is found in the environment, use this default:
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..
# 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
# Activate ICMPv6 error messages
# USEMODULE += gnrc_icmpv6_error
# Specify the mandatory networking module for a IPv6 routing node
USEMODULE += gnrc_ipv6_router_default
# Add a routing protocol
USEMODULE += gnrc_rpl
USEMODULE += auto_init_gnrc_rpl
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
USEMODULE += shell_cmd_gnrc_udp
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_default
USEMODULE += memarray
USEMODULE += ipv4_addr
# a cryptographically secure implementation of PRNG is needed for tinydtls
# Uncomment the following 3 lines for tinydtls support
CFLAGS += -DWITH_RIOT_SOCK
USEPKG += tinydtls
USEMODULE += prng_sha1prng
# libcoap support
USEPKG += libcoap
# Uncomment to enable libcoap OSCORE support
# USEMODULE += libcoap_oscore
# Configure if DNS is required
USEMODULE += sock_dns
# USEMODULE += xtimer
USEMODULE += ztimer64_xtimer_compat
# Add also the shell, some shell commands
USEMODULE += shell
USEMODULE += shell_cmds_default
USEMODULE += ps
USEMODULE += netstats_l2
USEMODULE += netstats_ipv6
USEMODULE += netstats_rpl
# libcoap needs some space
CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\)
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,70 @@
BOARD_INSUFFICIENT_MEMORY := \
airfy-beacon \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-nano \
arduino-uno \
atmega256rfr2-xpro \
atmega328p \
atmega328p-xplained-mini \
atmega8 \
atxmega-a3bu-xplained \
b-l072z-lrwan1 \
blackpill-stm32f103c8 \
blackpill-stm32f103cb \
bluepill-stm32f030c8 \
bluepill-stm32f103c8 \
bluepill-stm32f103cb \
calliope-mini \
cc2650-launchpad \
cc2650stk \
derfmega128 \
hifive1 \
hifive1b \
i-nucleo-lrwan1 \
im880b \
lsn50 \
maple-mini \
microbit \
microduino-corerf \
msb-430 \
msb-430h \
nrf51dk \
nrf51dongle \
nrf6310 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f070rb \
nucleo-f072rb \
nucleo-f103rb \
nucleo-f302r8 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-l073rz \
olimex-msp430-h1611 \
olimex-msp430-h2618 \
opencm904 \
samd10-xmini \
saml10-xpro \
saml11-xpro \
samr21-xpro \
slstk3400a \
spark-core \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32f7508-dk \
stm32g0316-disco \
stm32l0538-disco \
stm32mp157c-dk2 \
telosb \
waspmote-pro \
yunjia-nrf51822 \
z1 \
zigduino \
#

View File

@ -0,0 +1,25 @@
# libcoap server example
This example shows how to configure a server to use libcoap
## Fast configuration (Between RIOT instances):
Preparing the logical interfaces:
sudo ./../../dist/tools/tapsetup/tapsetup --create 2
## Server invocation
For the server:
PORT=tap0 make term
coaps {stop|start}
The server supports requests for
coap://[ip]/time?ticks
coap://[ip]/.well-known/core
## Handling the static memory allocation
libcoap for RIOT is using the `sys/memarray` module and therefore there
are certain limits. Said resources are defined in `libcoap/src/coap_mem.c`,
but can be overwritten at compile time.

View File

@ -0,0 +1,8 @@
CONFIG_LIBCOAP_SERVER_SUPPORT=y
CONFIG_LIBCOAP_USE_PSK="secretPSK"
# Logging levels are defined in pkg/libcoap using Kconfig
CONFIG_DTLS_PEER_MAX=2
CONFIG_DTLS_HANDSHAKE_MAX=2

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* Copyright (C) 2018 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Example application for libcoap server
*
* @author Raul Fuentes <>
*
* @}
*/
#include <stdio.h>
#include "shell.h"
#include "msg.h"
#include "coap3/coap.h"
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int server_coap_init(int argc, char **argv);
static const shell_command_t shell_commands[] = {
{ "coaps", "Start a libcoap server", server_coap_init },
{ NULL, NULL, NULL }
};
int
main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("RIOT libcoap server testing implementation");
/* start shell */
puts("All up, running the shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
/* should be never reached */
return 0;
}

View File

@ -0,0 +1,303 @@
/*
* server-coap.c -- RIOT example
*
* Copyright (C) 2023-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#include "coap_config.h"
#include <coap3/coap.h>
#include "server-coap.h"
#include <stdio.h>
#include "macros/utils.h"
#ifdef CONFIG_LIBCOAP_USE_PSK
#define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK
#else /* CONFIG_LIBCOAP_USE_PSK */
#define COAP_USE_PSK NULL
#endif /* CONFIG_LIBCOAP_USE_PSK */
static volatile int running = 0;
static int quit;
coap_context_t *main_coap_context;
static coap_time_t clock_offset;
/* changeable clock base (see handle_put_time()) */
static coap_time_t my_clock_base = 0;
static coap_resource_t *time_resource = NULL; /* just for testing */
static void
hnd_get_time(coap_resource_t *resource, coap_session_t *session,
const coap_pdu_t *request, const coap_string_t *query,
coap_pdu_t *response)
{
unsigned char buf[40];
size_t len;
coap_tick_t now;
coap_tick_t t;
(void)resource;
(void)session;
(void)request;
/* FIXME: return time, e.g. in human-readable by default and ticks
* when query ?ticks is given. */
/* if my_clock_base was deleted, we pretend to have no such resource */
coap_pdu_set_code(response, my_clock_base ? COAP_RESPONSE_CODE_CONTENT :
COAP_RESPONSE_CODE_NOT_FOUND);
if (my_clock_base) {
coap_add_option(response, COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_safe(buf, sizeof(buf),
COAP_MEDIATYPE_TEXT_PLAIN),
buf);
}
coap_add_option(response, COAP_OPTION_MAXAGE,
coap_encode_var_safe(buf, sizeof(buf), 0x01), buf);
if (my_clock_base) {
/* calculate current time */
coap_ticks(&t);
now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
if (query != NULL
&& coap_string_equal(query, coap_make_str_const("ticks"))) {
/* output ticks */
len = snprintf((char *)buf, sizeof(buf), "%u", (unsigned int)now);
coap_add_data(response, len, buf);
}
}
}
static void
init_coap_resources(coap_context_t *ctx)
{
coap_resource_t *r;
#if 0
r = coap_resource_init(NULL, 0, 0);
coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0);
coap_add_resource(ctx, r);
#endif
/* store clock base to use in /time */
my_clock_base = clock_offset;
r = coap_resource_init(coap_make_str_const("time"), 0);
if (!r) {
goto error;
}
coap_resource_set_get_observable(r, 1);
time_resource = r;
coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
#if 0
coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
#endif
coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
/* coap_add_attr(r, coap_make_str_const("title"),
coap_make_str_const("\"Internal Clock\""), 0); */
coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);
coap_add_resource(ctx, r);
#if 0
if (coap_async_is_supported()) {
r = coap_resource_init(coap_make_str_const("async"), 0);
coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);
coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
coap_add_resource(ctx, r);
}
#endif
return;
error:
coap_log_crit("cannot create resource\n");
}
static int
init_coap_context_endpoints(const char *use_psk)
{
coap_address_t listenaddress;
gnrc_netif_t *netif = gnrc_netif_iter(NULL);
ipv6_addr_t addr;
char addr_str[INET6_ADDRSTRLEN + 8];
int scheme_hint_bits = 1 << COAP_URI_SCHEME_COAP;
coap_addr_info_t *info = NULL;
coap_addr_info_t *info_list = NULL;
coap_str_const_t local;
int have_ep = 0;
/* Get the first address on the interface */
if (gnrc_netif_ipv6_addrs_get(netif, &addr, sizeof(addr)) < 0) {
puts("Unable to get first address of the interface");
return 0;
}
coap_address_init(&listenaddress);
listenaddress.riot.family = AF_INET6;
memcpy(&listenaddress.riot.addr.ipv6, &addr,
sizeof(listenaddress.riot.addr.ipv6));
coap_print_ip_addr(&listenaddress, addr_str, sizeof(addr_str));
coap_log_info("Server IP [%s]\n", addr_str);
main_coap_context = coap_new_context(NULL);
if (!main_coap_context) {
return 0;
}
if (use_psk && coap_dtls_is_supported()) {
coap_dtls_spsk_t setup_data;
/* Need PSK set up before setting up endpoints */
memset(&setup_data, 0, sizeof(setup_data));
setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION;
setup_data.psk_info.key.s = (const uint8_t *)use_psk;
setup_data.psk_info.key.length = strlen(use_psk);
coap_context_set_psk2(main_coap_context, &setup_data);
scheme_hint_bits |= 1 << COAP_URI_SCHEME_COAPS;
}
local.s = (uint8_t *)addr_str;
local.length = strlen(addr_str);
info_list = coap_resolve_address_info(&local, COAP_DEFAULT_PORT,
COAPS_DEFAULT_PORT,
0, 0,
0,
scheme_hint_bits,
COAP_RESOLVE_TYPE_REMOTE);
for (info = info_list; info != NULL; info = info->next) {
coap_endpoint_t *ep;
ep = coap_new_endpoint(main_coap_context, &info->addr, info->proto);
if (!ep) {
coap_log_warn("cannot create endpoint for proto %u\n",
info->proto);
}
else {
have_ep = 1;
}
}
coap_free_address_info(info_list);
if (!have_ep) {
return 0;
}
return 1;
}
void *
server_coap_run(void *arg)
{
(void)arg;
/* Initialize libcoap library */
coap_startup();
coap_set_log_level(COAP_MAX_LOGGING_LEVEL);
if (!init_coap_context_endpoints(COAP_USE_PSK)) {
goto fail;
}
/* Limit the number of idle sessions to save RAM */
coap_context_set_max_idle_sessions(main_coap_context, 2);
clock_offset = 1; /* Need a non-zero value */
init_coap_resources(main_coap_context);
coap_log_info("libcoap server ready\n");
/* Keep on processing ... */
while (quit == 0) {
coap_io_process(main_coap_context, 1000);
}
fail:
/* Clean up library usage so client can be run again */
coap_free_context(main_coap_context);
main_coap_context = NULL;
coap_cleanup();
running = 0;
quit = 0;
coap_log_info("libcoap server stopped\n");
return NULL;
}
static char server_stack[THREAD_STACKSIZE_MAIN +
THREAD_EXTRA_STACKSIZE_PRINTF];
static
void
start_server(void)
{
kernel_pid_t server_pid;
/* Only one instance of the server */
if (running) {
puts("Error: server already running");
return;
}
/* The server is initialized */
server_pid = thread_create(server_stack,
sizeof(server_stack),
THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST,
server_coap_run, NULL, "libcoap_server");
/* Uncommon but better be sure */
if (server_pid == EINVAL) {
puts("ERROR: Thread invalid");
return;
}
if (server_pid == EOVERFLOW) {
puts("ERROR: Thread overflow!");
return;
}
running = 1;
return;
}
static
void
stop_server(void)
{
/* check if server is running at all */
if (running == 0) {
puts("Error: libcoap server is not running");
return;
}
quit = 1;
puts("Stopping server...");
}
void
server_coap_init(int argc, char **argv)
{
if (argc < 2) {
printf("usage: %s start|stop\n", argv[0]);
return;
}
if (strcmp(argv[1], "start") == 0) {
start_server();
}
else if (strcmp(argv[1], "stop") == 0) {
stop_server();
}
else {
printf("Error: invalid command. Usage: %s start|stop\n", argv[0]);
}
return;
}

View File

@ -0,0 +1,26 @@
/*
* server-coap.h -- RIOT client example
*
* Copyright (C) 2023-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#ifndef SERVER_COAP_H
#define SERVER_COAP_H
#ifdef __cplusplus
extern "C" {
#endif
/* Start up the CoAP Server */
void server_coap_init(int argc, char **argv);
#ifdef __cplusplus
}
#endif
#endif /* SERVER_COAP_H */

View File

@ -7,6 +7,7 @@
menu "Packages"
rsource "flashdb/Kconfig"
rsource "libcoap/Kconfig"
rsource "libfixmath/Kconfig"
rsource "libschc/Kconfig"
rsource "littlefs2/Kconfig"

302
pkg/libcoap/Kconfig Normal file
View File

@ -0,0 +1,302 @@
# Copyright (c) 2019 HAW Hamburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menu "libcoap"
depends on USEPKG_LIBCOAP
choice LIBCOAP_DEBUG_LEVEL
bool "Set CoAP debugging level"
default LIBCOAP_LOG_INFO
help
Set CoAP debugging level
config LIBCOAP_LOG_EMERG
bool "Emergency"
config LIBCOAP_LOG_ALERT
bool "Alert"
config LIBCOAP_LOG_CRIT
bool "Critical"
config LIBCOAP_LOG_ERROR
bool "Error"
config LIBCOAP_LOG_WARNING
bool "Warning"
config LIBCOAP_LOG_NOTICE
bool "Notice"
config LIBCOAP_LOG_INFO
bool "Info"
config LIBCOAP_LOG_DEBUG
bool "Debug"
config LIBCOAP_LOG_OSCORE
bool "OSCORE"
endchoice
config LIBCOAP_MAX_LOGGING_LEVEL
int
default 0 if LIBCOAP_LOG_EMERG
default 1 if LIBCOAP_LOG_ALERT
default 2 if LIBCOAP_LOG_CRIT
default 3 if LIBCOAP_LOG_ERROR
default 4 if LIBCOAP_LOG_WARNING
default 5 if LIBCOAP_LOG_NOTICE
default 6 if LIBCOAP_LOG_INFO
default 7 if LIBCOAP_LOG_DEBUG
default 8 if LIBCOAP_LOG_OSCORE
config LIBCOAP_IPV4_SUPPORT
bool "Enable IPv4 support within CoAP"
default n
help
Enable IPv4 functionality for CoAP.
If this option is disabled, redundant CoAP IPv4 code is removed.
[RIOT sock gnrc does not support this]
config LIBCOAP_IPV6_SUPPORT
bool "Enable IPv6 support within CoAP"
default y
help
Enable IPv6 functionality for CoAP.
If this option is disabled, redundant CoAP IPv6 code is removed.
if USEMODULE_SOCK_TCP
config LIBCOAP_TCP_SUPPORT
bool "Enable TCP support within CoAP"
default n
help
Enable TCP functionality for CoAP. This is required if TLS sessions
are to be used. Note that RIOT TCP support also needs to be enabled.
If this option is disabled, redundant CoAP TCP code is removed.
[RIOT sock gnrc does not support this]
endif # USEMODULE_SOCK_TCP
if USEMODULE_LIBCOAP_OSCORE
config LIBCOAP_OSCORE_SUPPORT
bool "Enable OSCORE support within CoAP"
default y
help
Enable OSCORE functionality for CoAP.
If this option is disabled, redundant CoAP OSCORE code is removed.
endif # MODULE_LIBCOAP_OSCORE
config LIBCOAP_OBSERVE_PERSIST
bool "Enable Observe persist support within CoAP"
default n
help
Enable Observe persist functionality for CoAP.
If this option is disabled, redundant CoAP Observe persist code is removed.
config LIBCOAP_WS_SOCKET
bool "Enable WebSocket support within CoAP"
default n
help
Enable WebSocket functionality for CoAP.
If this option is disabled, redundant CoAP WebSocket code is removed.
config LIBCOAP_Q_BLOCK_SUPPORT
bool "Enable Q-Block (RFC9177) support within CoAP"
default n
help
Enable Q-Block (RFC9177) functionality for CoAP.
If this option is disabled, redundant CoAP Q-Block code is removed.
config LIBCOAP_ASYNC_SUPPORT
bool "Enable separate responses support within CoAP"
default y
help
Enable async separate responses functionality for CoAP.
If this option is disabled, redundent CoAP async separate responses code is removed.
config LIBCOAP_THREAD_SAFE
bool "Enable thread safe support within CoAP"
default n
help
Enable thread safe support within CoAP.
If this option is disabled, libcoap is not thread safe,
config LIBCOAP_THREAD_RECURSIVE_CHECK
bool "Enable thread recursive lock detection if thread safe support is enabled"
depends on LIBCOAP_THREAD_SAFE
default n
help
Enable thread recursive lock detection if thread safe support is enabled.
If this option is disabled, there is no multi thread recursive detection.
config LIBCOAP_CLIENT_SUPPORT
bool "Enable Client functionality within CoAP"
default n
help
Enable client functionality (ability to make requests and receive
responses) for CoAP. If the server is going to act as a proxy, then
this needs to be enabled to support the ongoing session going to
the next hop.
If this option is disabled, redundant CoAP client only code is
removed.
If both this option and LIBCOAP_SERVER_SUPPORT are disabled, then
both are automatically enabled for backwards compatability.
if LIBCOAP_CLIENT_SUPPORT
config LIBCOAP_MAX_LG_CRCVS
int "Max number of client large receives supported"
default 1
help
The maximum number of supported client large receives.
endif # LIBCOAP_CLIENT_SUPPORT
config LIBCOAP_SERVER_SUPPORT
bool "Enable Server functionality within CoAP"
default n
help
Enable server functionality (ability to receive requests and send
responses) for CoAP.
If this option is disabled, redundant CoAP server only code is
removed.
If both this option and LIBCOAP_CLIENT_SUPPORT are disabled, then
both are automatically enabled for backwards compatability.
if LIBCOAP_SERVER_SUPPORT
config LIBCOAP_MAX_ENDPOINTS
int "Max number of endpoints supported"
default 4 if LIBCOAP_TCP_SUPPORT
default 2
help
The maximum number of supported endpoints.
config LIBCOAP_MAX_RESOURCES
int "Max number of resources supported"
default 8
help
The maximum number of supported resources.
config LIBCOAP_MAX_ATTRIBUTE_SIZE
int "Max size of attribute memory allocation"
default 16
help
The maximum size of a supported attribute.
config LIBCOAP_MAX_ATTRIBUTES
int "Max number of resource attributes supported"
default 32
help
The maximum number of supported resource attributes.
config LIBCOAP_MAX_CACHE_KEYS
int "Max number of cache keys supported"
default 2
help
The maximum number of supported cache keys.
config LIBCOAP_MAX_CACHE_ENTRIES
int "Max number of cache entries supported"
default 2
help
The maximum number of supported cache entries.
config LIBCOAP_MAX_LG_SRCVS
int "Max number of server large receives supported"
default 2
help
The maximum number of supported server large receives.
endif # LIBCOAP_SERVER_SUPPORT
config LIBCOAP_PROXY_SUPPORT
bool "Enable Proxy functionality within CoAP"
depends on LIBCOAP_CLIENT_SUPPORT && LIBCOAP_SERVER_SUPPORT
default n
help
Enable Proxy functionality (ability to receive requests, pass
them to an upstream server and send back responses to client)
for CoAP.
If this option is disabled, redundant CoAP proxy only code is
removed.
config LIBCOAP_MAX_LG_XMITS
int "Max number of large transmits supported"
default 2 if LIBCOAP_SERVER_SUPPORT
default 1
help
The maximum number of supported large transmits.
config LIBCOAP_MAX_STRING_SIZE
int "Max size of string memory allocation"
default 64
help
The maximum size of a supported string.
config LIBCOAP_MAX_STRINGS
int "Max number of strings supported"
default 16
help
The maximum number of supported strings.
config LIBCOAP_MAX_PACKETS
int "Max number of packets supported"
default 4
help
The maximum number of supported packets.
config LIBCOAP_MAX_NODES
int "Max number of nodes supported"
default 4
help
The maximum number of supported nodes.
config LIBCOAP_MAX_CONTEXTS
int "Max number of contexts supported"
default 1
help
The maximum number of supported contexts.
config LIBCOAP_MAX_PDUS
int "Max number of PDUs supported"
default 4
help
The maximum number of supported PDUs.
config LIBCOAP_MAX_DTLS_SESSIONS
int "Max number of DTLS sessions supported"
default 2
help
The maximum number of supported DTLS sessions.
config LIBCOAP_MAX_SESSIONS
int "Max number of sessions supported"
default 4
help
The maximum number of supported sessions.
config LIBCOAP_MAX_OPTION_SIZE
int "Max size of option memory allocation"
default 16
help
The maximum size of a supported option.
config LIBCOAP_MAX_OPTIONS
int "Max number of options supported"
default 16
help
The maximum number of supported options.
endmenu # libcoap

23
pkg/libcoap/Makefile Normal file
View File

@ -0,0 +1,23 @@
PKG_NAME=libcoap
PKG_URL=https://github.com/obgm/libcoap
PKG_VERSION=0d240530b4beba90cc86c488e17bd0090437a0dd
PKG_LICENSE=BSD-2-Clause
LIBCOAP_BUILD_DIR=$(BINDIR)/pkg/$(PKG_NAME)
LIBCOAP_SOURCE_DIR=$(RIOTBASE)/build/pkg/$(PKG_NAME)
LIBCOAP_INCLUDE_DIR=$(RIOTBASE)/build/pkg/$(PKG_NAME)/include/coap3
include $(RIOTBASE)/pkg/pkg.mk
ifneq (,$(filter libcoap_oscore,$(USEMODULE)))
all: libcoap libcoap_oscore
else
all: libcoap
endif
libcoap:
$(QQ)@cp $(LIBCOAP_SOURCE_DIR)/coap_config.h.riot $(LIBCOAP_SOURCE_DIR)/coap_config.h
$(QQ)"$(MAKE)" -C $(LIBCOAP_SOURCE_DIR)/src -f $(CURDIR)/Makefile.libcoap
libcoap_oscore:
$(QQ)"$(MAKE)" -C $(LIBCOAP_SOURCE_DIR)/src/oscore -f $(CURDIR)/Makefile.oscore

11
pkg/libcoap/Makefile.dep Normal file
View File

@ -0,0 +1,11 @@
USEMODULE += sock_udp
USEMODULE += sock_aux_local
USEMODULE += sock_async_event
ifneq (,$(filter libcoap,$(USEPKG)))
USEMODULE += libcoap
endif
ifneq (,$(filter libcoap_oscore,$(USEPKG)))
USEMODULE += libcoap_oscore
endif

View File

@ -0,0 +1,2 @@
INCLUDES += -I$(PKGDIRBASE)/libcoap \
-I$(PKGDIRBASE)/libcoap/include

View File

@ -0,0 +1,43 @@
MODULE := libcoap
SRC := coap_address.c \
coap_asn1.c \
coap_async.c \
coap_block.c \
coap_cache.c \
coap_debug.c \
coap_dtls.c \
coap_encode.c \
coap_event.c \
coap_gnutls.c\
coap_hashkey.c \
coap_io.c \
coap_io_riot.c\
coap_layers.c\
coap_mbedtls.c\
coap_mem.c \
coap_net.c \
coap_netif.c \
coap_notls.c \
coap_openssl.c\
coap_option.c \
coap_oscore.c \
coap_pdu.c \
coap_prng.c \
coap_proxy.c \
coap_resource.c \
coap_session.c \
coap_sha1.c \
coap_str.c \
coap_subscribe.c \
coap_tcp.c \
coap_threadsafe.c \
coap_tinydtls.c \
coap_uri.c \
coap_ws.c
ifneq (,$(filter libcoap_tinydtls,$(USEMODULE)))
CFLAGS += -DCOAP_WITH_LIBTINYDTLS
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,10 @@
MODULE := libcoap_oscore
SRC := \
oscore.c \
oscore_cbor.c \
oscore_context.c \
oscore_cose.c \
oscore_crypto.c
include $(RIOTBASE)/Makefile.base

17
tests/pkg/libcoap/Kconfig Normal file
View File

@ -0,0 +1,17 @@
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menu "tests_libcoap"
depends on USEPKG_LIBCOAP
config LIBCOAP_USE_PSK
string "Secret to use for PSK communications"
default "secretPSK"
depends on USEMODULE_TINYDTLS
config LIBCOAP_USE_PSK_ID
string "Identifier (user) to use for PSK communications"
default "test_user"
depends on USEMODULE_TINYDTLS
endmenu # tests_libcoap

View File

@ -0,0 +1,35 @@
# Include common testing definitions
include ../Makefile.pkg_common
# This test depends on tap device setup not being set
PORT=
# 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
USEMODULE += gnrc_netif_single
# Additional networking modules that can be dropped if not needed
USEMODULE += netutils
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_default
USEMODULE += memarray
# a cryptographically secure implementation of PRNG is needed for tinydtls
CFLAGS += -DWITH_RIOT_SOCK
CFLAGS += -DDTLS_PEER_MAX=2 -DDTLS_HANDSHAKE_MAX=2 -DNETQ_MAXCNT=5
USEPKG += tinydtls
USEMODULE += prng_sha1prng
# libcoap support
USEPKG += libcoap
USEMODULE += ztimer64_xtimer_compat
# libcoap needs some space
CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,70 @@
BOARD_INSUFFICIENT_MEMORY := \
airfy-beacon \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-nano \
arduino-uno \
atmega256rfr2-xpro \
atmega328p \
atmega328p-xplained-mini \
atmega8 \
atxmega-a3bu-xplained \
b-l072z-lrwan1 \
blackpill-stm32f103c8 \
blackpill-stm32f103cb \
bluepill-stm32f030c8 \
bluepill-stm32f103c8 \
bluepill-stm32f103cb \
calliope-mini \
cc2650-launchpad \
cc2650stk \
derfmega128 \
hifive1 \
hifive1b \
i-nucleo-lrwan1 \
im880b \
lsn50 \
maple-mini \
microbit \
microduino-corerf \
msb-430 \
msb-430h \
nrf51dk \
nrf51dongle \
nrf6310 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f070rb \
nucleo-f072rb \
nucleo-f103rb \
nucleo-f302r8 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-l073rz \
olimex-msp430-h1611 \
olimex-msp430-h2618 \
opencm904 \
samd10-xmini \
saml10-xpro \
saml11-xpro \
samr21-xpro \
slstk3400a \
spark-core \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32f7508-dk \
stm32g0316-disco \
stm32l0538-disco \
stm32mp157c-dk2 \
telosb \
waspmote-pro \
yunjia-nrf51822 \
z1 \
zigduino \
#

View File

@ -0,0 +1,7 @@
# libcoap test example
This test is to check that the libcoap client and server logic talk to
each other.
THe client request generates a CoAP request, which is sent to the server
who responds back to the client.

View File

@ -0,0 +1,11 @@
CONFIG_LIBCOAP_SERVER_SUPPORT=y
CONFIG_LIBCOAP_CLIENT_SUPPORT=y
CONFIG_LIBCOAP_USE_PSK="secretPSK"
CONFIG_LIBCOAP_USE_PSK_ID="test_user"
# Logging levels are defined in pkg/libcoap using Kconfig
CONFIG_LIBCOAP_LOG_ALERT=y
CONFIG_DTLS_PEER_MAX=2
CONFIG_DTLS_HANDSHAKE_MAX=2

View File

@ -0,0 +1,327 @@
/*
* libcoap-test.c -- RIOT example
*
* Copyright (C) 2023-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#include "coap_config.h"
#include <coap3/coap.h>
#include "libcoap-test.h"
#include <stdio.h>
#include "macros/utils.h"
#ifdef CONFIG_LIBCOAP_USE_PSK
#define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK
#else /* CONFIG_LIBCOAP_USE_PSK */
#define COAP_USE_PSK NULL
#endif /* CONFIG_LIBCOAP_USE_PSK */
#ifdef CONFIG_LIBCOAP_USE_PSK_ID
#define COAP_USE_PSK_ID CONFIG_LIBCOAP_USE_PSK_ID
#else /* CONFIG_LIBCOAP_USE_PSK_ID */
#define COAP_USE_PSK_ID NULL
#endif /* CONFIG_LIBCOAP_USE_PSK_ID */
static int quit;
#define INDEX "This is a DTLS loopback test client/server made with libcoap\n"
static void
hnd_get_index(coap_resource_t *resource, coap_session_t *session,
const coap_pdu_t *request, const coap_string_t *query,
coap_pdu_t *response)
{
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
0x7fff, 0, strlen(INDEX),
(const uint8_t *)INDEX, NULL, NULL);
}
static void
init_coap_resources(coap_context_t *ctx)
{
coap_resource_t *r;
r = coap_resource_init(NULL, 0);
if (r == NULL) {
goto error;
}
coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
coap_add_resource(ctx, r);
return;
error:
coap_log_crit("cannot create resource\n");
}
static int
init_coap_endpoints(coap_context_t *ctx, const char *use_psk)
{
char addr_str[INET6_ADDRSTRLEN + 8];
int scheme_hint_bits = 1 << COAP_URI_SCHEME_COAP;
coap_addr_info_t *info = NULL;
coap_addr_info_t *info_list = NULL;
coap_str_const_t local;
int have_ep = 0;
if (use_psk && coap_dtls_is_supported()) {
coap_dtls_spsk_t setup_data;
/* Need PSK set up before setting up endpoints */
memset(&setup_data, 0, sizeof(setup_data));
setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION;
setup_data.psk_info.key.s = (const uint8_t *)use_psk;
setup_data.psk_info.key.length = strlen(use_psk);
coap_context_set_psk2(ctx, &setup_data);
scheme_hint_bits |= 1 << COAP_URI_SCHEME_COAPS;
}
local.s = (uint8_t *)"::1";
local.length = strlen(addr_str);
info_list = coap_resolve_address_info(&local, COAP_DEFAULT_PORT,
COAPS_DEFAULT_PORT,
0, 0,
0,
scheme_hint_bits,
COAP_RESOLVE_TYPE_REMOTE);
for (info = info_list; info != NULL; info = info->next) {
coap_endpoint_t *ep;
ep = coap_new_endpoint(ctx, &info->addr, info->proto);
if (!ep) {
coap_log_warn("cannot create endpoint for proto %u\n",
info->proto);
}
else {
have_ep = 1;
}
}
coap_free_address_info(info_list);
if (!have_ep) {
return 0;
}
return 1;
}
static int
coap_server_init(coap_context_t *ctx)
{
if (!init_coap_endpoints(ctx, COAP_USE_PSK)) {
return 0;
}
init_coap_resources(ctx);
return 1;
}
static int
resolve_address(const char *host, const char *service, coap_address_t *dst,
int scheme_hint_bits)
{
uint16_t port = service ? atoi(service) : 0;
int ret = 0;
coap_str_const_t str_host;
coap_addr_info_t *addr_info;
str_host.s = (const uint8_t *)host;
str_host.length = strlen(host);
addr_info = coap_resolve_address_info(&str_host, port, port, port, port,
AF_UNSPEC, scheme_hint_bits,
COAP_RESOLVE_TYPE_REMOTE);
if (addr_info) {
ret = 1;
*dst = addr_info->addr;
}
coap_free_address_info(addr_info);
return ret;
}
static coap_response_t
message_handler(coap_session_t *session,
const coap_pdu_t *sent,
const coap_pdu_t *received,
const coap_mid_t id)
{
const uint8_t *data;
size_t len;
size_t offset;
size_t total;
(void)session;
(void)sent;
(void)id;
if (coap_get_data_large(received, &len, &data, &offset, &total)) {
printf("%*.*s", (int)len, (int)len, (const char *)data);
if (len + offset == total) {
printf("\n");
quit = 1;
}
}
return COAP_RESPONSE_OK;
}
static int
coap_client_init(coap_context_t *ctx)
{
coap_session_t *session = NULL;
coap_pdu_t *pdu;
coap_address_t dst;
coap_mid_t mid;
int len;
coap_uri_t uri;
char portbuf[8];
coap_optlist_t *optlist = NULL;
#define BUFSIZE 100
unsigned char buf[BUFSIZE];
int res;
const char *coap_uri = "coaps://[::1]";
/* Parse the URI */
len = coap_split_uri((const unsigned char *)coap_uri, strlen(coap_uri), &uri);
if (len != 0) {
coap_log_warn("Failed to parse uri %s\n", coap_uri);
goto fail;
}
snprintf(portbuf, sizeof(portbuf), "%d", uri.port);
snprintf((char *)buf, sizeof(buf), "%*.*s", (int)uri.host.length,
(int)uri.host.length, (const char *)uri.host.s);
/* resolve destination address where packet should be sent */
len = resolve_address((const char *)buf, portbuf, &dst, 1 << uri.scheme);
if (len <= 0) {
coap_log_warn("Failed to resolve address %*.*s\n", (int)uri.host.length,
(int)uri.host.length, (const char *)uri.host.s);
goto fail;
}
if (uri.scheme == COAP_URI_SCHEME_COAP) {
session = coap_new_client_session(ctx, NULL, &dst,
COAP_PROTO_UDP);
}
else if (uri.scheme == COAP_URI_SCHEME_COAP_TCP) {
goto fail;
}
else if (uri.scheme == COAP_URI_SCHEME_COAPS_TCP) {
goto fail;
}
else if (uri.scheme == COAP_URI_SCHEME_COAPS) {
#if defined (COAP_USE_PSK) && defined(COAP_USE_PSK_ID)
static coap_dtls_cpsk_t dtls_psk;
static char client_sni[256];
memset(client_sni, 0, sizeof(client_sni));
memset(&dtls_psk, 0, sizeof(dtls_psk));
dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
if (uri.host.length) {
memcpy(client_sni, uri.host.s,
MIN(uri.host.length, sizeof(client_sni) - 1));
}
else {
memcpy(client_sni, "localhost", 9);
}
dtls_psk.client_sni = client_sni;
dtls_psk.psk_info.identity.s = (const uint8_t *)COAP_USE_PSK_ID;
dtls_psk.psk_info.identity.length = strlen(COAP_USE_PSK_ID);
dtls_psk.psk_info.key.s = (const uint8_t *)COAP_USE_PSK;
dtls_psk.psk_info.key.length = strlen(COAP_USE_PSK);
session = coap_new_client_session_psk2(ctx, NULL, &dst,
COAP_PROTO_DTLS, &dtls_psk);
#else /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */
coap_log_err("CONFIG_LIBCOAP_USE_PSK and CONFIG_LIBCOAP_USE_PSK_ID not defined\n");
goto fail;
#endif /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */
}
if (!session) {
coap_log_warn("Failed to create session\n");
goto fail;
}
coap_register_response_handler(ctx, message_handler);
/* construct CoAP message */
pdu = coap_pdu_init(COAP_MESSAGE_CON,
COAP_REQUEST_CODE_GET,
coap_new_message_id(session),
coap_session_max_pdu_size(session));
if (!pdu) {
coap_log_warn("Failed to create PDU\n");
goto fail;
}
res = coap_uri_into_optlist(&uri, &dst, &optlist, 1);
if (res != 1) {
coap_log_warn("Failed to create options\n");
goto fail;
}
/* Add option list (which will be sorted) to the PDU */
if (optlist) {
res = coap_add_optlist_pdu(pdu, &optlist);
if (res != 1) {
coap_log_warn("Failed to add options to PDU\n");
goto fail;
}
}
/* and send the PDU */
mid = coap_send(session, pdu);
if (mid == COAP_INVALID_MID) {
coap_log_warn("Failed to send PDU\n");
goto fail;
}
coap_delete_optlist(optlist);
return 1;
fail:
coap_delete_optlist(optlist);
return 0;
}
void
libcoap_test_run(void)
{
coap_context_t *coap_context;
/* Initialize libcoap library */
coap_startup();
coap_set_log_level(COAP_MAX_LOGGING_LEVEL);
coap_context = coap_new_context(NULL);
if (!coap_context) {
goto fail;
}
coap_context_set_block_mode(coap_context, COAP_BLOCK_USE_LIBCOAP);
/* Set up server logic */
coap_server_init(coap_context);
coap_log_info("libcoap test server ready\n");
/* Set up and initiate client logic */
coap_client_init(coap_context);
coap_log_info("libcoap test client started\n");
/* Keep on processing until response is back in ... */
while (quit == 0) {
coap_io_process(coap_context, 1000);
}
fail:
/* Clean up library usage so client can be run again */
coap_free_context(coap_context);
coap_cleanup();
coap_log_info("libcoap test stopped\n");
return;
}

View File

@ -0,0 +1,26 @@
/*
* libcoap-test.h -- RIOT client example
*
* Copyright (C) 2023-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#ifndef LIBCOAP_TEST_H
#define LIBCOAP_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
/* Start up the CoAP Server */
void libcoap_test_run(void);
#ifdef __cplusplus
}
#endif
#endif /* LIBCOAP_TEST_H */

43
tests/pkg/libcoap/main.c Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* Copyright (C) 2018 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Example test application for libcoap
*
* @author Raul Fuentes <>
*
* @}
*/
#include <stdio.h>
#include "shell.h"
#include "msg.h"
#include "libcoap-test.h"
#include "coap3/coap.h"
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
int
main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
libcoap_test_run();
return 0;
}

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run
def testfunc(child):
child.expect_exact("This is a DTLS loopback test client/server made with libcoap\r\n")
if __name__ == "__main__":
sys.exit(run(testfunc))