From 88279904c5d86f0f03275b5ec50f406c76773490 Mon Sep 17 00:00:00 2001 From: pokgak Date: Mon, 29 Apr 2019 15:25:24 +0200 Subject: [PATCH 1/3] sys/coap: add ACCEPT option --- sys/include/net/coap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/include/net/coap.h b/sys/include/net/coap.h index 61f22b4aad..168211d6bd 100644 --- a/sys/include/net/coap.h +++ b/sys/include/net/coap.h @@ -41,6 +41,7 @@ extern "C" { #define COAP_OPT_URI_PATH (11) #define COAP_OPT_CONTENT_FORMAT (12) #define COAP_OPT_URI_QUERY (15) +#define COAP_OPT_ACCEPT (17) #define COAP_OPT_LOCATION_QUERY (20) #define COAP_OPT_BLOCK2 (23) #define COAP_OPT_BLOCK1 (27) From 869052dcd8400333645578e2d03e54b7a9721254 Mon Sep 17 00:00:00 2001 From: Aiman Ismail Date: Tue, 12 Nov 2019 14:52:18 +0100 Subject: [PATCH 2/3] cord: add RD lookup client --- Makefile.dep | 29 +- sys/Makefile | 3 + sys/include/net/cord/lc.h | 246 +++++++++++++ sys/net/application_layer/cord/lc/Makefile | 5 + sys/net/application_layer/cord/lc/cord_lc.c | 384 ++++++++++++++++++++ 5 files changed, 654 insertions(+), 13 deletions(-) create mode 100644 sys/include/net/cord/lc.h create mode 100644 sys/net/application_layer/cord/lc/Makefile create mode 100644 sys/net/application_layer/cord/lc/cord_lc.c diff --git a/Makefile.dep b/Makefile.dep index bc8bb89299..783119f4d3 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -916,9 +916,22 @@ ifneq (,$(filter nimble_%,$(USEMODULE))) USEPKG += nimble endif +ifneq (,$(filter cord_common,$(USEMODULE))) + USEMODULE += fmt + USEMODULE += luid + USEMODULE += gcoap +endif + +ifneq (,$(filter cord_lc cord_ep,$(USEMODULE))) + USEMODULE += core_thread_flags + USEMODULE += cord_common + ifneq (,$(filter shell_commands,$(USEMODULE))) + USEMODULE += sock_util + endif +endif + ifneq (,$(filter cord_epsim,$(USEMODULE))) USEMODULE += cord_common - USEMODULE += gcoap endif ifneq (,$(filter cord_ep_standalone,$(USEMODULE))) @@ -926,18 +939,8 @@ ifneq (,$(filter cord_ep_standalone,$(USEMODULE))) USEMODULE += xtimer endif -ifneq (,$(filter cord_ep,$(USEMODULE))) - USEMODULE += cord_common - USEMODULE += core_thread_flags - USEMODULE += gcoap - ifneq (,$(filter shell_commands,$(USEMODULE))) - USEMODULE += sock_util - endif -endif - -ifneq (,$(filter cord_common,$(USEMODULE))) - USEMODULE += fmt - USEMODULE += luid +ifneq (,$(filter cord_lc,$(USEMODULE))) + USEMODULE += clif endif ifneq (,$(filter tlsf-malloc,$(USEMODULE))) diff --git a/sys/Makefile b/sys/Makefile index f09cef8023..080c08e2d2 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -154,6 +154,9 @@ endif ifneq (,$(filter cord_ep,$(USEMODULE))) DIRS += net/application_layer/cord/ep endif +ifneq (,$(filter cord_lc,$(USEMODULE))) + DIRS += net/application_layer/cord/lc +endif ifneq (,$(filter bluetil_%,$(USEMODULE))) DIRS += net/ble/bluetil endif diff --git a/sys/include/net/cord/lc.h b/sys/include/net/cord/lc.h new file mode 100644 index 0000000000..c348a3bbe6 --- /dev/null +++ b/sys/include/net/cord/lc.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * @defgroup net_cord_lc CoRE RD Lookup Client + * @ingroup net_cord + * @brief Library for using RIOT as CoRE Resource Directory (RD) lookup client + * + * This module implements a CoRE Resource Directory lookup client library, that + * allows RIOT nodes to lookup resources, endpoints and groups with resource + * directories. + * It implements the standard lookup functionality as defined in + * draft-ietf-core-resource-directory-23. + * @see https://tools.ietf.org/html/draft-ietf-core-resource-directory-23 + * + * ## Lookup modes + * + * The module defines two types of lookup for interacting with a RD server: + * + * - raw: result of the lookup is returned as is. No `page` or `count` filter + * is applied by default. Use @ref cord_lc_raw() for this mode. + * - pre-parsed: result of the lookup is parsed and returned in a + * @ref cord_lc_res_t or @ref cord_lc_ep_t depending on the type of the + * lookup. The default `count` filter is set to `1` and `page` filter is + * incremented after each successful call and resets to `0` when lookup result + * is empty. Use @ref cord_lc_res() or cord_lc_ep() for this mode. + * + * ## Limitations + * + * Currently, this module cannot do more than a single request concurrently + * and the request is fully synchronous. The client can only connects to one + * RD server at a time. The client will disconnect when a connection to a new + * RD server is made, regardless of whether the connection attempt is successful + * or not. + * + * @{ + * + * @file + * @brief CoRE Resource Directory lookup interface + * + * @author Aiman Ismail + */ + +#ifndef NET_CORD_LC_H +#define NET_CORD_LC_H + +#include "net/sock/udp.h" +#include "net/nanocoap.h" +#include "clif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return values and error codes used by this module + */ +enum { + CORD_LC_OK = 0, /**< everything went as expected */ + CORD_LC_TIMEOUT = -1, /**< no response from the network */ + CORD_LC_ERR = -2, /**< internal error or invalid reply */ + CORD_LC_OVERFLOW = -3, /**< internal buffers can not handle input */ + CORD_LC_NORSC = -4, /**< lookup interface not found */ +}; + +/** + * @brief RD lookup types + */ +enum { + CORD_LC_RES, /**< Resource lookup type */ + CORD_LC_EP, /**< Endpoint lookup type */ +}; + +/** + * @brief Information about RD server and its lookup interface resources + */ +typedef struct { + const sock_udp_ep_t *remote; /**< Remote endpoint of RD server */ + char *res_lookif; /**< Resource lookup interface */ + char *ep_lookif; /**< Endpoint lookup interface */ + unsigned res_last_page; /**< Page of last resource lookup */ + unsigned ep_last_page; /**< Page of last endpoint lookup */ +} cord_lc_rd_t; + +/** + * @brief Result of lookup + */ +struct cord_lc_result { + clif_t link; /**< Resource link */ + clif_attr_t *attrs; /**< Array of Link Format parameters */ + size_t max_attrs; /**< Max parameters at @p params */ +}; + +typedef struct cord_lc_result cord_lc_res_t; /**< Resource typedef */ +typedef struct cord_lc_result cord_lc_ep_t; /**< Endpoint typedef */ + +/** + * @brief Filters to use for a lookup + */ +typedef struct cord_lc_filter { + clif_attr_t *array; /**< Array of filter(s) */ + size_t len; /**< No. of filters at @p array */ + struct cord_lc_filter *next; /**< Next set of filters */ +} cord_lc_filter_t; + +/** + * @brief Discover the lookup interfaces of a RD server + * + * @param[out] rd Information about RD server at @p remote + * @param[out] buf Buffer to store found lookup interfaces + * @param[in] maxlen Maximum memory available at @p lookif + * @param[in] remote Remote endpoint of RD server + * + * @return bytes used on success + * @return CORD_LC_TIMEOUT if the discovery request times out + * @return CORD_LC_NORSC if no lookup interfaces found for all types + * of lookups + * @return CORD_LC_OVERFLOW if not enough memory available for result + * @return CORD_LC_ERR on any other internal error + */ +int cord_lc_rd_init(cord_lc_rd_t *rd, void *buf, size_t maxlen, + const sock_udp_ep_t *remote); + +/** + * @brief Raw lookup for registered resources/endpoints at a RD server + * + * To specify the number of resources/endpoints to search for, + * `count` and `page` attributes can be used as the filter. + * If the same filter used multiple times with different values, + * only the last filter will be applied. + * + * Content format (e.g. link-format, coral) must be set through + * filters. If none defined, link-format will be used. + * + * @param[in] rd RD server to query + * @param[in] content_format Data format wanted from RD server + * @param[in] lookup_type Lookup type to use + * @param[in] filters Filters for the lookup. Can be NULL. + * @param[out] result Buffer holding the raw response + * @param[in] maxlen Max length of @p result + * + * @return bytes used on success + * @return CORD_LC_NORSC if there is no lookup interface for @p lookup_type + * at @p rd + * @return CORD_LC_TIMEOUT if lookup timed out + * @return CORD_LC_OVERFLOW if not enough memory available for result + * @return CORD_LC_ERR on any other internal error + */ +ssize_t cord_lc_raw(const cord_lc_rd_t *rd, unsigned content_format, + unsigned lookup_type, cord_lc_filter_t *filters, + void *result, size_t maxlen); + +/** + * @brief Get one resource from RD server + * + * Gets only one resource from specified RD server each time called. + * Can be called iteratively to get all the resources on the server. + * If there is no more resource on the server, it will return + * CORD_LC_NORSC. + * + * @param[in] rd RD server to query + * @param[out] result Result link + * @param[in] filters Filters for the lookup + * @param[out] buf Result buffer + * @param[in] maxlen Maximum memory available at @p buf + * @param[in] type Type of resource to query either CORD_LC_EP or + * CORD_LC_RES + * + * @return bytes used on success + * @return CORD_LC_INVALIF if the resource lookup interface at @p rd is invalid + * @return CORD_LC_NORSC if there is no resource (anymore) at @p rd + * @return CORD_LC_TIMEOUT if lookup timed out + * @return CORD_LC_OVERFLOW if not enough memory available for result + * @return CORD_LC_ERR on any other internal error + */ +ssize_t _lookup_result(cord_lc_rd_t *rd, cord_lc_res_t *result, + cord_lc_filter_t *filters, void *buf, size_t maxlen, + unsigned type); + +/** + * @brief Get one resource from RD server + * + * Gets only one resource from specified RD server each time called. + * Can be called iteratively to get all the resources on the server. + * If there is no more resource on the server, it will return + * CORD_LC_NORSC. + * + * @param[in] rd RD server to query + * @param[out] resource Resource link + * @param[in] filters Filters for the lookup + * @param[out] buf Result buffer + * @param[in] maxlen Maximum memory available at @p buf + * + * @return bytes used on success + * @return CORD_LC_INVALIF if the resource lookup interface at @p rd is invalid + * @return CORD_LC_NORSC if there is no resource (anymore) at @p rd + * @return CORD_LC_TIMEOUT if lookup timed out + * @return CORD_LC_OVERFLOW if not enough memory available for result + * @return CORD_LC_ERR on any other internal error + */ +static inline ssize_t cord_lc_res(cord_lc_rd_t *rd, cord_lc_res_t *resource, + cord_lc_filter_t *filters, void *buf, + size_t maxlen) +{ + return _lookup_result(rd, resource, filters, buf, maxlen, CORD_LC_RES); +} + +/** + * @brief Get one endpoint from RD server + * + * Gets only one endpoint from specified RD server each time called. + * Can be called iteratively to get all the endpoints on the server. + * If there is no more endpoint on the server, it will return + * CORD_LC_NORSC. + * + * @param[in] rd RD server to query + * @param[out] endpoint Resource link + * @param[in] filters Filters for the lookup + * @param[out] buf Result buffer + * @param[in] maxlen Maximum memory available at @p buf + * + * @return bytes used on success + * @return CORD_LC_INVALIF if the endpoint lookup interface at @p rd is invalid + * @return CORD_LC_NORSC if there is no endpoints (anymore) at @p rd + * @return CORD_LC_TIMEOUT if lookup timed out + * @return CORD_LC_OVERFLOW if not enough memory available for result + * @return CORD_LC_ERR on any other internal error + */ +static inline ssize_t cord_lc_ep(cord_lc_rd_t *rd, cord_lc_ep_t *endpoint, + cord_lc_filter_t *filters, void *buf, + size_t maxlen) +{ + return _lookup_result(rd, endpoint, filters, buf, maxlen, CORD_LC_EP); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NET_CORD_LC_H */ +/** @} */ diff --git a/sys/net/application_layer/cord/lc/Makefile b/sys/net/application_layer/cord/lc/Makefile new file mode 100644 index 0000000000..ed4441db65 --- /dev/null +++ b/sys/net/application_layer/cord/lc/Makefile @@ -0,0 +1,5 @@ +MODULE = cord_lc + +SRC = cord_lc.c + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/cord/lc/cord_lc.c b/sys/net/application_layer/cord/lc/cord_lc.c new file mode 100644 index 0000000000..7e2bc3c07d --- /dev/null +++ b/sys/net/application_layer/cord/lc/cord_lc.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2017-2018 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. + */ + +/** + * @ingroup net_cord_lc + * @{ + * + * @file + * @brief CoRE Resource Directory lookup implementation + * + * @author Aiman Ismail + * + * @} + */ + +#include + +#include "assert.h" +#include "mutex.h" +#include "thread_flags.h" + +#include "net/gcoap.h" +#include "net/cord/lc.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define FLAG_SUCCESS (0x0001) +#define FLAG_TIMEOUT (0x0002) +#define FLAG_ERR (0x0004) +#define FLAG_OVERFLOW (0x0008) +#define FLAG_NORSC (0x0010) +#define FLAG_MASK (0x00ff) + +#define BUFSIZE (CONFIG_GCOAP_PDU_BUF_SIZE) +#define MAX_EXPECTED_ATTRS (6) + +/* Filter to limit number of link returned by RD server to 1 */ +#define SINGLE_COUNT_FILTER { \ + .key = "count", .key_len = strlen("count"), \ + .value = "1", .value_len = strlen("1") \ + } + +/* Filter to continue request at page_str */ +#define PAGE_FILTER(page_str) { \ + .key = "page", .key_len = strlen("page"), \ + .value = page_str, .value_len = strlen(page_str) \ + } + +/* Default filters that will be appended to existing filters for each request. + * Consists of count=1 and page=last_page */ +#define DEFAULT_FILTERS(page_str) { SINGLE_COUNT_FILTER, PAGE_FILTER(page_str) } + +static void _lock(void); +static int _sync(void); +/* callback for _lookup_raw() request */ +static void _on_lookup(const gcoap_request_memo_t *memo, coap_pkt_t *pdu, + const sock_udp_ep_t *remote); +/* callback for _send_rd_init_req() */ +static void _on_rd_init(const gcoap_request_memo_t *memo, coap_pkt_t *pdu, + const sock_udp_ep_t *remote); +static ssize_t _add_filters_to_lookup(coap_pkt_t *pkt, cord_lc_filter_t *filters); +static int _send_rd_init_req(coap_pkt_t *pkt, const sock_udp_ep_t *remote, + void *buf, size_t maxlen); +/* do a lookup and returns payload of the response */ +static ssize_t _lookup_raw(const cord_lc_rd_t *rd, unsigned content_format, + unsigned lookup_type, cord_lc_filter_t *filters, + void *result, size_t maxlen); + +static char *_result_buf; +static size_t _result_buf_len; +static uint8_t reqbuf[CONFIG_GCOAP_PDU_BUF_SIZE] = {0}; + +static mutex_t _mutex = MUTEX_INIT; +static volatile thread_t *_waiter; + +static void _lock(void) +{ + mutex_lock(&_mutex); + _waiter = sched_active_thread; +} + +static int _sync(void) +{ + thread_flags_t flags = thread_flags_wait_any(FLAG_MASK); + + if (flags & FLAG_ERR) { + return CORD_LC_ERR; + } else if (flags & FLAG_TIMEOUT) { + return CORD_LC_TIMEOUT; + } else if (flags & FLAG_OVERFLOW) { + return CORD_LC_OVERFLOW; + } else if (flags & FLAG_NORSC) { + return CORD_LC_NORSC; + } else { + return CORD_LC_OK; + } +} + +static void _on_lookup(const gcoap_request_memo_t *memo, coap_pkt_t *pdu, + const sock_udp_ep_t *remote) +{ + (void)remote; + + thread_flags_t flag = FLAG_ERR; + + if (memo->state == GCOAP_MEMO_RESP) { + unsigned ct = coap_get_content_type(pdu); + if (ct != COAP_FORMAT_LINK) { + DEBUG("cord_lc: unsupported content format: %u\n", ct); + thread_flags_set((thread_t *)_waiter, flag); + } + if (pdu->payload_len == 0) { + flag = FLAG_NORSC; + thread_flags_set((thread_t *)_waiter, flag); + } + if (pdu->payload_len >= _result_buf_len) { + flag = FLAG_OVERFLOW; + thread_flags_set((thread_t *)_waiter, flag); + } + memcpy(_result_buf, pdu->payload, pdu->payload_len); + memset(_result_buf + pdu->payload_len, 0, + _result_buf_len - pdu->payload_len); + _result_buf_len = pdu->payload_len; + flag = FLAG_SUCCESS; + } else if (memo->state == GCOAP_MEMO_TIMEOUT) { + flag = FLAG_TIMEOUT; + } + + thread_flags_set((thread_t *)_waiter, flag); +} + +static ssize_t _add_filters_to_lookup(coap_pkt_t *pkt, cord_lc_filter_t *filters) +{ + cord_lc_filter_t *f = filters; + while (f) { + for (unsigned i = 0; i < f->len; i++) { + clif_attr_t *kv = &(f->array[i]); + if (coap_opt_add_uri_query2(pkt, kv->key, kv->key_len, kv->value, kv->value_len) == -1) { + return CORD_LC_OVERFLOW; + } + } + f = f->next; + } + return CORD_LC_OK; +} + +static ssize_t _lookup_raw(const cord_lc_rd_t *rd, unsigned content_format, + unsigned lookup_type, cord_lc_filter_t *filters, + void *result, size_t maxlen) +{ + assert(rd->remote); + + int res; + int retval; + coap_pkt_t pkt; + ssize_t pkt_len; + + char *lookif = (lookup_type == CORD_LC_RES) ? rd->res_lookif : rd-> ep_lookif; + res = gcoap_req_init(&pkt, reqbuf, sizeof(reqbuf), COAP_METHOD_GET, lookif); + if (res < 0) { + DEBUG("cord_lc: failed gcoap_req_init()\n"); + return CORD_LC_ERR; + } + + /* save pointer to result */ + _result_buf = result; + _result_buf_len = maxlen; + + /* add filters */ + res = _add_filters_to_lookup(&pkt, filters); + if (res != CORD_LC_OK) { + return res; + } + + /* set packet options */ + if (content_format != COAP_FORMAT_LINK) { + DEBUG("cord_lc: unsupported content format\n"); + return CORD_LC_ERR; + } + coap_hdr_set_type(pkt.hdr, COAP_TYPE_CON); + coap_opt_add_uint(&pkt, COAP_OPT_ACCEPT, content_format); + + pkt_len = gcoap_finish(&pkt, 0, COAP_FORMAT_NONE); + if (pkt_len < 0) { + return CORD_LC_ERR; + } + res = gcoap_req_send(reqbuf, pkt_len, rd->remote, _on_lookup, NULL); + if (res < 0) { + return CORD_LC_ERR; + } + retval = _sync(); + return (retval == CORD_LC_OK) ? (int)_result_buf_len : retval; +} + +static void _on_rd_init(const gcoap_request_memo_t *memo, coap_pkt_t *pdu, + const sock_udp_ep_t *remote) +{ + (void)remote; + + thread_flags_t flag = FLAG_NORSC; + + if (memo->state == GCOAP_MEMO_RESP) { + unsigned ct = coap_get_content_type(pdu); + if (ct != COAP_FORMAT_LINK) { + DEBUG("cord_lc: error payload not in link format: %u\n", ct); + goto end; + } + if (pdu->payload_len == 0) { + DEBUG("cord_lc: error empty payload\n"); + goto end; + } + memcpy(_result_buf, pdu->payload, pdu->payload_len); + _result_buf_len = pdu->payload_len; + _result_buf[_result_buf_len] = '\0'; + flag = FLAG_SUCCESS; + } else if (memo->state == GCOAP_MEMO_TIMEOUT) { + flag = FLAG_TIMEOUT; + } + +end: + if (flag != FLAG_SUCCESS) { + _result_buf = NULL; + _result_buf_len = 0; + } + thread_flags_set((thread_t *)_waiter, flag); +} + +static int _send_rd_init_req(coap_pkt_t *pkt, const sock_udp_ep_t *remote, + void *buf, size_t maxlen) +{ + + int res = gcoap_req_init(pkt, buf, maxlen, COAP_METHOD_GET, "/.well-known/core"); + if (res < 0) { + DEBUG("cord_lc: error gcoap_req_init() %d\n", res); + return CORD_LC_ERR; + } + + coap_hdr_set_type(pkt->hdr, COAP_TYPE_CON); + coap_opt_add_uri_query(pkt, "rt", "core.rd-lookup-*"); + + ssize_t pkt_len = gcoap_finish(pkt, 0, COAP_FORMAT_NONE); + if (pkt_len < 0) { + DEBUG("cord_lc: error gcoap_finish() %zd\n", pkt_len); + return CORD_LC_ERR; + } + + if (!gcoap_req_send(buf, pkt_len, remote, _on_rd_init, NULL)) { + DEBUG("cord_lc: error gcoap_req_send()\n"); + return CORD_LC_ERR; + } + return _sync(); +} + +int cord_lc_rd_init(cord_lc_rd_t *rd, void *buf, size_t maxlen, + const sock_udp_ep_t *remote) +{ + assert(remote); + + coap_pkt_t pkt; + rd->remote = remote; + + _lock(); + memset(buf, '0', maxlen); + _result_buf = buf; + _result_buf_len = maxlen; + + int retval = _send_rd_init_req(&pkt, remote, buf, maxlen); + if (retval != CORD_LC_OK) { + DEBUG("cord_lc: failed to send req %d\n", retval); + goto end; + } + + /* Parse the payload */ + clif_t lookif; + clif_attr_t attrs[MAX_EXPECTED_ATTRS]; + unsigned attrs_used = 0; + size_t parsed_len = 0; + while ((!rd->res_lookif || !rd->ep_lookif) || + (parsed_len != _result_buf_len)) { + + ssize_t ret = clif_decode_link(&lookif, attrs + attrs_used, + ARRAY_SIZE(attrs) - attrs_used, + _result_buf + parsed_len, + _result_buf_len - parsed_len); + if (ret < 0) { + DEBUG("cord_lc: error decoding payload %zd\n", ret); + retval = CORD_LC_ERR; + goto end; + } + + attrs_used += lookif.attrs_len; + parsed_len += ret; + + /* check if we found ep_lookif or res_lookif */ + for (unsigned i = 0; i < lookif.attrs_len; i++) { + clif_attr_t *current_attr = (lookif.attrs + i); + if (current_attr->value == NULL) { + /* skip attributes that have no value */ + continue; + } + + if (!strncmp(current_attr->value, "core.rd-lookup-res", + current_attr->value_len)) { + rd->res_lookif = lookif.target; + rd->res_lookif[lookif.target_len] = '\0'; + } else if (!strncmp(current_attr->value, "core.rd-lookup-ep", + current_attr->value_len)) { + rd->ep_lookif = lookif.target; + rd->ep_lookif[lookif.target_len] = '\0'; + } + } + } + + if (!rd->res_lookif && !rd->ep_lookif) { + DEBUG("cord_lc: no lookup interfaces found\n"); + retval = CORD_LC_NORSC; + } else { + retval = parsed_len; + } +end: + mutex_unlock(&_mutex); + return retval; +} + +ssize_t cord_lc_raw(const cord_lc_rd_t *rd, unsigned content_format, + unsigned lookup_type, cord_lc_filter_t *filters, + void *result, size_t maxlen) +{ + _lock(); + ssize_t retval = _lookup_raw(rd, content_format, lookup_type, filters, + result, maxlen); + mutex_unlock(&_mutex); + return retval; +} + +ssize_t _lookup_result(cord_lc_rd_t *rd, cord_lc_res_t *result, + cord_lc_filter_t *filters, void *buf, size_t maxlen, + unsigned type) +{ + int retval; + + _lock(); + unsigned *page_ptr = (type == CORD_LC_EP) + ? &rd->ep_last_page : &rd->res_last_page; + /* int will always fit in an 12-char array */ + char page_str[12]; + snprintf(page_str, sizeof(page_str), "%u", (*page_ptr)++); + + /* Append given filters to default filters (page, count). + * If same filter are also specified by filters, assume the RD server will + * use the value from last filter in the filter list */ + clif_attr_t default_attrs[] = DEFAULT_FILTERS(page_str); + cord_lc_filter_t *all_filters = &(cord_lc_filter_t) { + .array = default_attrs, + .len = ARRAY_SIZE(default_attrs), + .next = filters, + }; + + retval = _lookup_raw(rd, COAP_FORMAT_LINK, type, all_filters, buf, maxlen); + if (retval < 0) { + if (retval == CORD_LC_NORSC) { + *page_ptr = 0; + } + DEBUG("cord_lc: error ep lookup failed\n"); + mutex_unlock(&_mutex); + return retval; + } + + /* parse the result */ + retval = clif_decode_link(&result->link, result->attrs, result->max_attrs, + buf, retval); + if (retval < 0) { + DEBUG("cord_lc: no endpoint link found\n"); + retval = CORD_LC_ERR; + } + mutex_unlock(&_mutex); + return retval; +} From 99950d1f7c51fd4472a7e2f6abf95171321b4fd1 Mon Sep 17 00:00:00 2001 From: Aiman Ismail Date: Tue, 12 Nov 2019 14:52:47 +0100 Subject: [PATCH 3/3] examples: add RD lookup client example --- examples/cord_lc/Makefile | 34 +++++++ examples/cord_lc/Makefile.ci | 34 +++++++ examples/cord_lc/README.md | 16 +++ examples/cord_lc/cord_lc_cli.c | 176 +++++++++++++++++++++++++++++++++ examples/cord_lc/main.c | 47 +++++++++ 5 files changed, 307 insertions(+) create mode 100644 examples/cord_lc/Makefile create mode 100644 examples/cord_lc/Makefile.ci create mode 100644 examples/cord_lc/README.md create mode 100644 examples/cord_lc/cord_lc_cli.c create mode 100644 examples/cord_lc/main.c diff --git a/examples/cord_lc/Makefile b/examples/cord_lc/Makefile new file mode 100644 index 0000000000..d273c20f87 --- /dev/null +++ b/examples/cord_lc/Makefile @@ -0,0 +1,34 @@ +# name of your application +APPLICATION = cord_lc + +# 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)/../.. + +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_icmpv6_echo + +USEMODULE += cord_lc + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# The default size of GCOAP_PDU_BUF_SIZE can be too small for the response +# from the RD server. Use a larger value if the client drops response packet +# from RD server. +CFLAGS += -DCONFIG_GCOAP_PDU_BUF_SIZE=512 + +# 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 diff --git a/examples/cord_lc/Makefile.ci b/examples/cord_lc/Makefile.ci new file mode 100644 index 0000000000..4f5b594b69 --- /dev/null +++ b/examples/cord_lc/Makefile.ci @@ -0,0 +1,34 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega328p \ + chronos \ + derfmega128 \ + hifive1 \ + hifive1b \ + i-nucleo-lrwan1 \ + im880b \ + mega-xplained \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + wsn430-v1_3b \ + wsn430-v1_4 \ + z1 \ + # diff --git a/examples/cord_lc/README.md b/examples/cord_lc/README.md new file mode 100644 index 0000000000..40fc32e296 --- /dev/null +++ b/examples/cord_lc/README.md @@ -0,0 +1,16 @@ +CoRE Resource Directory: Lookup Example +========================================= + +This example application demonstrates the usage of RIOT's Resource Directory +(RD) lookup module, called `cord_lc`. This module supports the lookup of +resources and endpoints as defined in +[draft-ietf-core-resource-directory-20](https://tools.ietf.org/html/draft-ietf-core-resource-directory-23). +The lookup can be done in two modes: a) raw: result of the lookup is returned +as is. b) pre-parsed: result of the lookup is parsed and only one link is +returned for each call. + +Usage +===== +The examples includes a shell command that you can use to interact with a given +RD, called `cord_lc`. Simply use that shell command without parameters for +more information on its usage. \ No newline at end of file diff --git a/examples/cord_lc/cord_lc_cli.c b/examples/cord_lc/cord_lc_cli.c new file mode 100644 index 0000000000..b07ca26b28 --- /dev/null +++ b/examples/cord_lc/cord_lc_cli.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Shell command for the cord_lc module + * + * @author Aiman Ismail + * + * @} + */ + +#include +#include + +#include "net/cord/config.h" +#include "net/cord/lc.h" +#include "net/gcoap.h" +#include "net/sock/util.h" + +static cord_lc_rd_t rd; +static sock_udp_ep_t remote; +static char rdbuf[2 * CONFIG_NANOCOAP_URI_MAX] = {0}; +static unsigned rd_initialized = 0; + +static int make_sock_ep(sock_udp_ep_t *ep, const char *addr) +{ + ep->port = 0; + if (sock_udp_str2ep(ep, addr) < 0) { + return -1; + } + /* if netif not specified in addr */ + if ((ep->netif == SOCK_ADDR_ANY_NETIF) && (gnrc_netif_numof() == 1)) { + /* assign the single interface found in gnrc_netif_numof() */ + ep->netif = (uint16_t)gnrc_netif_iter(NULL)->pid; + } + ep->family = AF_INET6; + if (ep->port == 0) { + ep->port = COAP_PORT; + } + return 0; +} + +/** + * Parses main arguments for filters. + * Returns number of parsed filters + */ +static void _parse_filters(clif_attr_t *filters, size_t filter_count, + char **argv) { + for (unsigned i = 0; i < filter_count; i++) { + clif_attr_t *f = &filters[i]; + f->key = argv[i]; + char *key_end = memchr(argv[i], '=', strlen(argv[i])); + if (!key_end) { + f->key_len = strlen(f->key); + f->value = NULL; + f->value_len = 0; + } + else { + f->key_len = key_end - f->key; + f->value = key_end + 1; + f->value_len = strlen(f->value); + } + } +} + +static void _print_lookup_result(struct cord_lc_result *res) { + printf("Found resource/endpoint\n"); + printf("Target: %.*s\n", res->link.target_len, res->link.target); + for (unsigned i = 0; i < res->link.attrs_len; i++) { + clif_attr_t *p = &(res->link.attrs[i]); + printf("'%.*s': '%.*s'\n", p->key_len, p->key, p->value_len, p->value); + } +} + +static void _print_usage(void) { + puts("usage: cord_lc " + "[-r] { resource | endpoint } [key=value]\n" + "Options:\n" + " -r get raw result\n" + "example: cord_lc [2001:db8:3::dead:beef]:5683 -r resource count=1 page=2\n" + "example: cord_lc [2001:db8:3::dead:beef]:5683 endpoint"); +} + +int cord_lc_cli_cmd(int argc, char **argv) { + char bufpool[1024] = {0}; + int raw_mode = 0; + + raw_mode = argc > 3 && !strcmp(argv[2], "-r"); + if (argc < 3 || (raw_mode && argc < 4)) { + _print_usage(); + return -1; + } + + if (!rd_initialized) { + int ret = make_sock_ep(&remote, argv[1]); + if (ret < 0) { + printf("error: unable to parse address\n"); + return -1; + } + puts("Performing lookup now, this may take a short while..."); + ret = cord_lc_rd_init(&rd, rdbuf, sizeof(rdbuf), &remote); + if (ret < 0) { + printf("error initializing RD server %d", ret); + return -1; + } + rd_initialized = 1; + } + + /* parse filters */ + unsigned filter_start = raw_mode ? 4 : 3; + size_t filter_count = argc - filter_start; + clif_attr_t filter_array[filter_count]; + if (filter_count > 0) { + _parse_filters(filter_array, filter_count, &argv[filter_start]); + } + cord_lc_filter_t filters = { + .array = filter_array, + .len = filter_count, + .next = NULL, + }; + + int retval = 0; + if (raw_mode) { + if (!strcmp(argv[3], "resource")) { + retval = cord_lc_raw(&rd, COAP_FORMAT_LINK, CORD_LC_RES, &filters, + bufpool, sizeof(bufpool)); + } else if (!strcmp(argv[3], "endpoint")) { + retval = cord_lc_raw(&rd, COAP_FORMAT_LINK, CORD_LC_EP, &filters, + bufpool, sizeof(bufpool)); + } else { + _print_usage(); + return -1; + } + if (retval < 0) { + printf("Error during lookup %d\n", retval); + return -1; + } + printf("Lookup result:\n%.*s\n", retval, bufpool); + } else if (!strcmp(argv[2], "resource")) { + cord_lc_res_t resource; + clif_attr_t attrs[5]; + resource.attrs = attrs; + resource.max_attrs = ARRAY_SIZE(attrs); + retval = + cord_lc_res(&rd, &resource, &filters, bufpool, sizeof(bufpool)); + if (retval < 0) { + printf("Error during lookup %d\n", retval); + return -1; + } + _print_lookup_result(&resource); + } else if (!strcmp(argv[2], "endpoint")) { + cord_lc_ep_t endpoint; + clif_attr_t attrs[5]; + endpoint.attrs = attrs; + endpoint.max_attrs = ARRAY_SIZE(attrs); + retval = cord_lc_ep(&rd, &endpoint, &filters, bufpool, sizeof(bufpool)); + if (retval < 0) { + printf("Error during lookup %d\n", retval); + return -1; + } + _print_lookup_result(&endpoint); + } else { + _print_usage(); + return -1; + } + return 0; +} diff --git a/examples/cord_lc/main.c b/examples/cord_lc/main.c new file mode 100644 index 0000000000..f8a8a49ec3 --- /dev/null +++ b/examples/cord_lc/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief CoRE Resource Directory lookup (cord_lc) example + * + * @author Aiman Ismail + * + * @} + */ + +#include + +#include "shell.h" +#include "msg.h" + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +extern int cord_lc_cli_cmd(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "cord_lc", "Cord LC example", cord_lc_cli_cmd }, + { 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("CoRE RD lookup client example!\n"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}