diff --git a/Makefile.dep b/Makefile.dep index 0e999f9411..3c9a0a1a0d 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -707,6 +707,23 @@ ifneq (,$(filter skald,$(USEMODULE))) USEMODULE += xtimer USEMODULE += random endif + +ifneq (,$(filter rdcli_simple_standalone,$(USEMODULE))) + USEMODULE += rdcli_simple + USEMODULE += xtimer +endif + +ifneq (,$(filter rdcli_simple,$(USEMODULE))) + USEMODULE += rdcli_common + USEMODULE += fmt +endif + +ifneq (,$(filter rdcli_common,$(USEMODULE))) + USEMODULE += fmt + USEMODULE += gcoap + USEMODULE += luid +endif + # always select gpio (until explicit dependencies are sorted out) FEATURES_OPTIONAL += periph_gpio diff --git a/examples/rdcli_simple/Makefile b/examples/rdcli_simple/Makefile new file mode 100644 index 0000000000..d13be99124 --- /dev/null +++ b/examples/rdcli_simple/Makefile @@ -0,0 +1,43 @@ +# name of your application +APPLICATION = rdcli_simple + +# 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)/../.. + +BOARD_INSUFFICIENT_MEMORY := chronos msb-430 msb-430h nucleo-f030 nucleo-l053 \ + nucleo32-f031 nucleo32-f042 nucleo32-l031 \ + stm32f0discovery telosb wsn430-v1_3b wsn430-v1_4 \ + z1 + +# Enable GNRC networking +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_default + +# Run the simple CoRE resource directory +USEMODULE += rdcli_simple_standalone + +# Include the shell for testing purposes +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# 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: +CFLAGS += -DDEVELHELP + +# For debugging and demonstration purposes, we limit the lifetime to the minimal +# allowed value of 60s (see draft-ietf-core-resource-directory-11, Table 2) +RD_LT ?= 60 +# Override this variable to set the RD server address (default is the all nodes +# multicast address) +RD_SERVER ?= IPV6_ADDR_ALL_NODES_LINK_LOCAL + +CFLAGS += -DRDCLI_LT=$(RD_LT) +CFLAGS += -DRDCLI_SERVER_ADDR=$(RD_SERVER) + +include $(RIOTBASE)/Makefile.include diff --git a/examples/rdcli_simple/README.md b/examples/rdcli_simple/README.md new file mode 100644 index 0000000000..6f5740f7fb --- /dev/null +++ b/examples/rdcli_simple/README.md @@ -0,0 +1,20 @@ +CoRE Resource Directory: Simple Registration Example +==================================================== + +This example shows how a node can register with a CoRE resource directory using +the simple registration process as described in +draft-ietf-core-resource-directory-11, section 5.3.2. + +The registration process needs an endpoint name as well as a lifetime for the +registry entry. You can edit these values by overriding `RDCLI_EP` and +`RDCLI_LT`: +``` +CFLAGS="-DRDCLI_LT=\"7200\" -DRDCLI_EP=\"MyNode\"" make all +``` + +Per default, the node is looking for the CoRE RD at the all nodes link-local +multicast address [FF02::1]:5683. To change the target address or port, simply +redefine `RDCLI_SERVER_ADDR` and `RDCLI_SERVER_PORT`, e.g.: +``` +CFLAGS="-DRDCLI_SERVER_ADDR=IPV6_ADDR_ALL_ROUTERS_LINK_LOCAL" make all +``` diff --git a/examples/rdcli_simple/main.c b/examples/rdcli_simple/main.c new file mode 100644 index 0000000000..e1ae4f37f7 --- /dev/null +++ b/examples/rdcli_simple/main.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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 Test application demonstrating the simple registration + * process to a CoRE RD using gcoap + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "shell.h" +#include "net/gcoap.h" +#include "net/rdcli_common.h" + +#define BUFSIZE (64U) + +static char riot_info[BUFSIZE]; + +/* define some dummy CoAP resources */ +static ssize_t text_resp(coap_pkt_t *pdu, uint8_t *buf, size_t len, + const char *text, unsigned format) +{ + gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); + size_t slen = strlen(text); + memcpy(pdu->payload, text, slen); + return gcoap_finish(pdu, slen, format); +} + +static ssize_t handler_info(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx) +{ + (void)ctx; + return text_resp(pdu, buf, len, riot_info, COAP_FORMAT_JSON); +} + +static ssize_t handler_text(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx) +{ + return text_resp(pdu, buf, len, (char *)ctx, COAP_FORMAT_TEXT); +} + +static const coap_resource_t resources[] = { + { "/riot/bar", COAP_GET, handler_text, "foo" }, + { "/riot/foo", COAP_GET, handler_text, "bar" }, + { "/riot/info", COAP_GET, handler_info, NULL } +}; + +static gcoap_listener_t listener = { + .resources = (coap_resource_t *)&resources[0], + .resources_len = sizeof(resources) / sizeof(resources[0]), + .next = NULL +}; + +int main(void) +{ + puts("CoAP simplified RD registration example!\n"); + + /* fill riot info */ + sprintf(riot_info, "{\"ep\":\"%s\",\"lt\":%u}", + rdcli_common_get_ep(), RDCLI_LT); + + /* register resource handlers with gcoap */ + gcoap_register_listener(&listener); + + /* print RD client information */ + puts("RD client information:"); + printf(" ep: %s\n", rdcli_common_get_ep()); + printf(" lt: %is\n", (int)RDCLI_LT); + + /* run the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 15b562684e..ebb54ee0a9 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -64,6 +64,7 @@ PSEUDOMODULES += pktqueue PSEUDOMODULES += printf_float PSEUDOMODULES += prng PSEUDOMODULES += prng_% +PSEUDOMODULES += rdcli_simple_standalone PSEUDOMODULES += saul_adc PSEUDOMODULES += saul_default PSEUDOMODULES += saul_gpio diff --git a/sys/Makefile b/sys/Makefile index cc6d2ab074..f08ec774ae 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -127,6 +127,12 @@ endif ifneq (,$(filter skald,$(USEMODULE))) DIRS += net/skald endif +ifneq (,$(filter rdcli_common,$(USEMODULE))) + DIRS += net/application_layer/rdcli_common +endif +ifneq (,$(filter rdcli_simple,$(USEMODULE))) + DIRS += net/application_layer/rdcli_simple +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE)))) diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index fcff85bec8..6cdaded5b3 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -159,6 +159,16 @@ void auto_init(void) DEBUG("Auto init Skald\n"); skald_init(); #endif +#ifdef MODULE_RDCLI_COMMON + DEBUG("Auto init rdcli_common module\n"); + extern void rdcli_common_init(void); + rdcli_common_init(); +#endif +#ifdef MODULE_RDCLI_SIMPLE_STANDALONE + DEBUG("Auto init rdcli_simple module\n"); + extern void rdcli_simple_run(void); + rdcli_simple_run(); +#endif /* initialize network devices */ #ifdef MODULE_AUTO_INIT_GNRC_NETIF diff --git a/sys/include/net/rdcli_common.h b/sys/include/net/rdcli_common.h new file mode 100644 index 0000000000..2077b3b312 --- /dev/null +++ b/sys/include/net/rdcli_common.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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. + */ + +/** + * @defgroup net_rdcli_common Shared Functions for CoRE RD Clients + * @ingroup net_rdcli + * @{ + * + * @file + * @brief Shared CoRE RD client functions + * + * @author Hauke Petersen + */ + +#ifndef NET_RDCLI_COMMON_H +#define NET_RDCLI_COMMON_H + +#include "net/rdcli_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Export the local endpoint identifier + * + * @note Use rdcli_common_get_ep() for accessing the endpoint identifier + */ +extern char rdcli_ep[]; + +/** + * @brief Generate unique endpoint identifier (ep) + */ +void rdcli_common_init(void); + +/** + * @brief Get the local endpoint identifier + */ +static inline const char *rdcli_common_get_ep(void) +{ + return (const char *)rdcli_ep; +} + +/** + * @brief Add selected query string options to a gcoap request + * + * This function adds: + * - `ep` -> as extracted by rdcli_commont_get_ep() + * - [optional] `lt` -> if defined by RDCLI_LT + * - [optional] 'd' -> if defined by RDCLI_D + * + * @return 0 on success + * @return <0 on error + */ +int rdcli_common_add_qstring(coap_pkt_t *pkt); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_RDCLI_COMMON_H */ +/** @} */ diff --git a/sys/include/net/rdcli_config.h b/sys/include/net/rdcli_config.h new file mode 100644 index 0000000000..1acd5d7578 --- /dev/null +++ b/sys/include/net/rdcli_config.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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. + */ + +/** + * @defgroup net_rdcli_config CoAP Resource Directory Client Configuration + * @ingroup net_rdcli + * @{ + * + * @file + * @brief + * + * @author Hauke Petersen + */ + +#ifndef NET_RDCLI_CONFIG_H +#define NET_RDCLI_CONFIG_H + +#include "net/ipv6/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default lifetime in seconds (the default is 1 day) + */ +#ifndef RDCLI_LT +#define RDCLI_LT (86400UL) +#endif + +/** + * @brief Delay until the RD client starts to try registering (in seconds) + */ +#ifndef RDCLI_STARTUP_DELAY +#define RDCLI_STARTUP_DELAY (3U) +#endif + +/** + * @brief Default client update interval (default is half the lifetime) + */ +#ifndef RDCLI_UPDATE_INTERVAL +#define RDCLI_UPDATE_INTERVAL (RDCLI_LT / 2) +#endif + +/** + * @name Endpoint ID definition + * + * Per default, the endpoint ID (ep) is generated by concatenation of a user + * defined prefix (RDCLI_EP_PREFIX) and a locally unique ID (luid) encoded in + * hexadecimal formatting with the given length of characters + * (RDCLI_EP_SUFFIX_LEN). + * + * Alternatively, the endpoint ID value can be defined at compile time by + * assigning a string value to the RDCLI_ED macro. + * + * @{ + */ +#ifndef RDCLI_EP +/** + * @brief Number of generated hexadecimal characters added to the ep + */ +#define RDCLI_EP_SUFFIX_LEN (16) + +/** + * @brief Default static prefix used for the generated ep + */ +#define RDCLI_EP_PREFIX "RIOT-" +#endif +/** @} */ + +/** + * @brief Default IPv6 address to use when looking for RDs + */ +#ifndef RDCLI_SERVER_ADDR +#define RDCLI_SERVER_ADDR IPV6_ADDR_ALL_NODES_LINK_LOCAL +#endif + +/** + * @brief Default Port to use when looking for RDs + */ +#ifndef RDCLI_SERVER_PORT +#define RDCLI_SERVER_PORT COAP_PORT +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NET_RDCLI_CONFIG_H */ +/** @} */ diff --git a/sys/include/net/rdcli_simple.h b/sys/include/net/rdcli_simple.h new file mode 100644 index 0000000000..1e5d08ffc4 --- /dev/null +++ b/sys/include/net/rdcli_simple.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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. + */ + +/** + * @defgroup net_rdcli_simple Simple CoRE Resource Directory Registration + * @ingroup net_rdcli + * @brief CoAP-based CoRE Resource Directory client supporting the simple + * registration only + * @{ + * + * @file + * @brief Interface for a simple CoRE RD registration + * + * @author Hauke Petersen + */ + +#ifndef NET_RDCLI_SIMPLE_H +#define NET_RDCLI_SIMPLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initiate the node registration by sending an empty CoAP POST message + * to the RD server's /.well-known/core resource + */ +int rdcli_simple_register(void); + +/** + * @brief Spawn a new thread that registers the node and updates the + * registration with all responding RDs using the simple registration + * process + */ +void rdcli_simple_run(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_RDCLI_SIMPLE_H */ +/** @} */ diff --git a/sys/net/application_layer/rdcli_common/Makefile b/sys/net/application_layer/rdcli_common/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/application_layer/rdcli_common/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/rdcli_common/rdcli_common.c b/sys/net/application_layer/rdcli_common/rdcli_common.c new file mode 100644 index 0000000000..d3c2d24ccd --- /dev/null +++ b/sys/net/application_layer/rdcli_common/rdcli_common.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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 net_rdcli_common + * @{ + * + * @file + * @brief Implementation of common functions for CoRE RD clients + * + * @author Hauke Petersen + * + * @} + */ + +#include "fmt.h" +#include "luid.h" + +#include "net/gcoap.h" +#include "net/rdcli_common.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + + +#ifdef RDCLI_EP +#define EP_LEN (sizeof(RDCLI_EP)) +#define BUFSIZE (EP_LEN + 1) +#else +#define PREFIX_LEN (sizeof(RDCLI_EP_PREFIX)) +#define BUFSIZE (PREFIX_LEN + RDCLI_EP_SUFFIX_LEN + 1) +#endif + +char rdcli_ep[BUFSIZE]; + +void rdcli_common_init(void) +{ + size_t pos = 0; + +#ifdef RDCLI_EP + memcpy(rdcli_ep, RDCLI_EP, EP_LEN); + pos += EP_LEN; +#else + uint8_t luid[RDCLI_EP_SUFFIX_LEN / 2]; + + if (PREFIX_LEN) { + memcpy(rdcli_ep, RDCLI_EP_PREFIX, PREFIX_LEN); + pos += PREFIX_LEN - 1; + } + + luid_get(luid, sizeof(luid)); + fmt_bytes_hex(&rdcli_ep[pos], luid, sizeof(luid)); +#endif + + rdcli_ep[pos] = '\0'; +} + +int rdcli_common_add_qstring(coap_pkt_t *pkt) +{ + /* extend the url with some query string options */ + int res = gcoap_add_qstring(pkt, "ep", rdcli_ep); + if (res < 0) { + return res; + } + + /* [optional] set the lifetime parameter */ +#if RDCLI_LT + char lt[11]; + lt[fmt_u32_dec(lt, RDCLI_LT)] = '\0'; + res = gcoap_add_qstring(pkt, "lt", lt); + if (res < 0) { + return res; + } +#endif + + /* [optional] set the domain parameter */ +#ifdef RDCLI_D + res = gcoap_add_qstring(pkt, "d", RDCLI_D); + if (res < 0) { + return res; + } +#endif + + return 0; +} diff --git a/sys/net/application_layer/rdcli_simple/Makefile b/sys/net/application_layer/rdcli_simple/Makefile new file mode 100644 index 0000000000..997bf2fc80 --- /dev/null +++ b/sys/net/application_layer/rdcli_simple/Makefile @@ -0,0 +1,7 @@ +SRC = rdcli_simple.c + +ifneq (,$(filter rdcli_simple_standalone,$(USEMODULE))) + SRC += rdcli_simple_standalone.c +endif + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/rdcli_simple/rdcli_simple.c b/sys/net/application_layer/rdcli_simple/rdcli_simple.c new file mode 100644 index 0000000000..cb86eeef83 --- /dev/null +++ b/sys/net/application_layer/rdcli_simple/rdcli_simple.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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 net_rdcli_simple + * @{ + * + * @file + * @brief Simplified CoAP resource directory client implementation + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "fmt.h" +#include "net/gcoap.h" +#include "net/rdcli_config.h" +#include "net/rdcli_common.h" +#include "net/rdcli_simple.h" + +#define BUFSIZE (128U) + +static coap_pkt_t pkt; +static uint8_t buf[BUFSIZE]; + +/* allocate an UDP endpoint to the RD server */ +static const sock_udp_ep_t remote = { + .family = AF_INET6, + .netif = SOCK_ADDR_ANY_NETIF, + .addr = RDCLI_SERVER_ADDR , + .port = RDCLI_SERVER_PORT +}; + +int rdcli_simple_register(void) +{ + /* build the initial CON packet */ + int res = gcoap_req_init(&pkt, buf, sizeof(buf), COAP_METHOD_POST, + "/.well-known/core"); + if (res < 0) { + return res; + } + /* make packet confirmable */ + coap_hdr_set_type(pkt.hdr, COAP_TYPE_CON); + /* add Uri-Query options */ + res = rdcli_common_add_qstring(&pkt); + if (res < 0) { + return res; + } + /* finish, we don't have any payload */ + ssize_t len = gcoap_finish(&pkt, 0, COAP_FORMAT_LINK); + return (int)gcoap_req_send2(buf, len, &remote, NULL); +} diff --git a/sys/net/application_layer/rdcli_simple/rdcli_simple_standalone.c b/sys/net/application_layer/rdcli_simple/rdcli_simple_standalone.c new file mode 100644 index 0000000000..868250ff19 --- /dev/null +++ b/sys/net/application_layer/rdcli_simple/rdcli_simple_standalone.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * 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 net_rdcli_simple + * @{ + * + * @file + * @brief Standalone extension for the simple RD registration client + * + * @author Hauke Petersen + * + * @} + */ + +#include "log.h" +#include "thread.h" +#include "xtimer.h" +#include "net/rdcli_config.h" +#include "net/rdcli_simple.h" + +#define STACKSIZE (THREAD_STACKSIZE_DEFAULT) +#define PRIO (THREAD_PRIORITY_MAIN - 1) +#define TNAME "rdcli_simple" + +static char _stack[STACKSIZE]; + +static void *reg_runner(void *arg) +{ + (void)arg; + + /* wait some seconds to give the address configuration some time to settle */ + xtimer_sleep(RDCLI_STARTUP_DELAY); + + while (1) { + int res = rdcli_simple_register(); + if (res < 0) { + LOG_ERROR("[rdcli_simple] error: unable to trigger registration\n"); + } + xtimer_sleep(RDCLI_UPDATE_INTERVAL); + } + + return NULL; /* should never be reached */ +} + +void rdcli_simple_run(void) +{ + thread_create(_stack, sizeof(_stack), PRIO, 0, reg_runner, NULL, TNAME); +}