Merge pull request #10222 from pokgak/pr/add-cord-lc
net: add CoRE RD lookup client implementation
This commit is contained in:
commit
fdcf53e514
29
Makefile.dep
29
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)))
|
||||
|
||||
34
examples/cord_lc/Makefile
Normal file
34
examples/cord_lc/Makefile
Normal file
@ -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
|
||||
34
examples/cord_lc/Makefile.ci
Normal file
34
examples/cord_lc/Makefile.ci
Normal file
@ -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 \
|
||||
#
|
||||
16
examples/cord_lc/README.md
Normal file
16
examples/cord_lc/README.md
Normal file
@ -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.
|
||||
176
examples/cord_lc/cord_lc_cli.c
Normal file
176
examples/cord_lc/cord_lc_cli.c
Normal file
@ -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 <muhammadaimanbin.ismail@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 <server_addr> "
|
||||
"[-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;
|
||||
}
|
||||
47
examples/cord_lc/main.c
Normal file
47
examples/cord_lc/main.c
Normal file
@ -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 <muhammadaimanbin.ismail@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
246
sys/include/net/cord/lc.h
Normal file
246
sys/include/net/cord/lc.h
Normal file
@ -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 <muhammadaimanbin.ismail@haw-hamburg.de>
|
||||
*/
|
||||
|
||||
#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 */
|
||||
/** @} */
|
||||
5
sys/net/application_layer/cord/lc/Makefile
Normal file
5
sys/net/application_layer/cord/lc/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
MODULE = cord_lc
|
||||
|
||||
SRC = cord_lc.c
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
384
sys/net/application_layer/cord/lc/cord_lc.c
Normal file
384
sys/net/application_layer/cord/lc/cord_lc.c
Normal file
@ -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 <muhammadaimanbin.ismail@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user