blob: ddbc4402e5b290e816fdab78d06bb02c424fff5f [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#if 1
#define SYS_LOG_DOMAIN "zoap-server"
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#define NET_LOG_ENABLED 1
#endif
#include <errno.h>
#include <misc/printk.h>
#include <zephyr.h>
#include <misc/byteorder.h>
#include <misc/util.h>
#include <net/buf.h>
#include <net/nbuf.h>
#include <net/net_ip.h>
#include <net/zoap.h>
#include <net/zoap_link_format.h>
#if defined(CONFIG_NET_L2_BLUETOOTH)
#include <bluetooth/bluetooth.h>
#include <gatt/ipss.h>
#endif
#if defined(CONFIG_NET_L2_IEEE802154)
#include <ieee802154_settings.h>
#endif
#define MY_COAP_PORT 5683
#define STACKSIZE 2000
/* FIXME */
#define BLOCK_WISE_TRANSFER_SIZE_GET 2048
#define ALL_NODES_LOCAL_COAP_MCAST \
{ { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfd } } }
#define MY_IP6ADDR \
{ { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }
#define NUM_OBSERVERS 3
#define NUM_PENDINGS 3
static struct net_context *context;
static const uint8_t plain_text_format;
static struct zoap_observer observers[NUM_OBSERVERS];
static struct zoap_pending pendings[NUM_PENDINGS];
static struct net_context *context;
static struct k_delayed_work observer_work;
static int obs_counter;
static struct zoap_resource *resource_to_notify;
struct k_delayed_work retransmit_work;
static int test_del(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
struct net_buf *buf, *frag;
struct zoap_packet response;
uint8_t tkl, code, type;
const uint8_t *token;
uint16_t id;
int r;
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
type = ZOAP_TYPE_ACK;
} else {
type = ZOAP_TYPE_NON_CON;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_DELETED);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int test_put(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
struct net_buf *buf, *frag;
struct zoap_packet response;
uint8_t *payload, code, type, tkl;
const uint8_t *token;
uint16_t len, id;
int r;
payload = zoap_packet_get_payload(request, &len);
if (!payload) {
return -EINVAL;
}
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
type = ZOAP_TYPE_ACK;
} else {
type = ZOAP_TYPE_NON_CON;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CHANGED);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int test_post(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
static const char * const location_path[] = { "location1",
"location2",
"location3",
NULL };
const char * const *p;
struct net_buf *buf, *frag;
struct zoap_packet response;
uint8_t *payload, code, type, tkl;
const uint8_t *token;
uint16_t len, id;
int r;
payload = zoap_packet_get_payload(request, &len);
if (!payload) {
NET_ERR("Packet without payload\n");
return -EINVAL;
}
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
type = ZOAP_TYPE_ACK;
} else {
type = ZOAP_TYPE_NON_CON;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CREATED);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
for (p = location_path; *p; p++) {
zoap_add_option(&response, ZOAP_OPTION_LOCATION_PATH,
*p, strlen(*p));
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int location_query_post(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
static const char *const location_query[] = { "first=1",
"second=2",
NULL };
const char * const *p;
struct net_buf *buf, *frag;
struct zoap_packet response;
uint8_t *payload, code, type, tkl;
const uint8_t *token;
uint16_t len, id;
int r;
payload = zoap_packet_get_payload(request, &len);
if (!payload) {
NET_ERR("Packet without payload\n");
return -EINVAL;
}
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
type = ZOAP_TYPE_ACK;
} else {
type = ZOAP_TYPE_NON_CON;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CREATED);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
for (p = location_query; *p; p++) {
zoap_add_option(&response, ZOAP_OPTION_LOCATION_QUERY,
*p, strlen(*p));
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int piggyback_get(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
struct net_buf *buf, *frag;
struct zoap_packet response;
const uint8_t *token;
uint8_t *payload, code, type;
uint16_t len, id;
uint8_t tkl;
int r;
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
if (type == ZOAP_TYPE_CON) {
type = ZOAP_TYPE_ACK;
} else {
type = ZOAP_TYPE_NON_CON;
}
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = zoap_add_option(&response, ZOAP_OPTION_CONTENT_FORMAT,
&plain_text_format, sizeof(plain_text_format));
if (r < 0) {
return -EINVAL;
}
payload = zoap_packet_get_payload(&response, &len);
if (!payload) {
return -EINVAL;
}
/* The response that coap-client expects */
r = snprintk((char *) payload, len,
"Type: %u\nCode: %u\nMID: %u\n", type, code, id);
if (r < 0 || r > len) {
return -EINVAL;
}
r = zoap_packet_set_used(&response, r);
if (r) {
return -EINVAL;
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int query_get(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
struct zoap_option options[4];
struct net_buf *buf, *frag;
struct zoap_packet response;
uint8_t *payload, code, type, tkl;
const uint8_t *token;
uint16_t len, id;
int i, r;
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
r = zoap_find_options(request, ZOAP_OPTION_URI_QUERY, options, 4);
if (r < 0) {
return -EINVAL;
}
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("num queries: %d\n", r);
for (i = 0; i < r; i++) {
char str[16];
if (options[i].len + 1 > sizeof(str)) {
NET_INFO("Unexpected length of query: "
"%d (expected %zu)\n",
options[i].len, sizeof(str));
break;
}
memcpy(str, options[i].value, options[i].len);
str[options[i].len] = '\0';
NET_INFO("query[%d]: %s\n", i + 1, str);
}
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, ZOAP_TYPE_ACK);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = zoap_add_option(&response, ZOAP_OPTION_CONTENT_FORMAT,
&plain_text_format, sizeof(plain_text_format));
if (r < 0) {
return -EINVAL;
}
payload = zoap_packet_get_payload(&response, &len);
if (!payload) {
return -EINVAL;
}
/* The response that coap-client expects */
r = snprintk((char *) payload, len,
"Type: %u\nCode: %u\nMID: %u\n", type, code, id);
if (r < 0 || r > len) {
return -EINVAL;
}
r = zoap_packet_set_used(&response, r);
if (r) {
return -EINVAL;
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int separate_get(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
struct net_buf *buf, *frag;
struct zoap_packet response;
struct zoap_pending *pending;
uint8_t *payload, code, type, tkl;
const uint8_t *token;
uint16_t len, id;
int r;
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
if (type == ZOAP_TYPE_NON_CON) {
goto done;
}
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, ZOAP_TYPE_ACK);
zoap_header_set_code(&response, 0);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
if (r < 0) {
return -EINVAL;
}
done:
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
type = ZOAP_TYPE_CON;
} else {
type = ZOAP_TYPE_NON_CON;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = zoap_add_option(&response, ZOAP_OPTION_CONTENT_FORMAT,
&plain_text_format, sizeof(plain_text_format));
if (r < 0) {
return -EINVAL;
}
payload = zoap_packet_get_payload(&response, &len);
if (!payload) {
return -EINVAL;
}
/* The response that coap-client expects */
r = snprintk((char *) payload, len,
"Type: %u\nCode: %u\nMID: %u\n", type, code, id);
if (r < 0 || r > len) {
return -EINVAL;
}
r = zoap_packet_set_used(&response, r);
if (r) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
pending = zoap_pending_next_unused(pendings, NUM_PENDINGS);
if (!pending) {
return -EINVAL;
}
r = zoap_pending_init(pending, &response, from);
if (r) {
return -EINVAL;
}
zoap_pending_cycle(pending);
pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS);
k_delayed_work_submit(&retransmit_work, pending->timeout);
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int large_get(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
static struct zoap_block_context ctx;
struct net_buf *buf, *frag;
struct zoap_packet response;
const uint8_t *token;
uint8_t *payload, code, type;
uint16_t len, id, size;
uint8_t tkl;
int r;
if (ctx.total_size == 0) {
zoap_block_transfer_init(&ctx, ZOAP_BLOCK_64,
BLOCK_WISE_TRANSFER_SIZE_GET);
}
r = zoap_update_from_block(request, &ctx);
if (r < 0) {
return -EINVAL;
}
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, ZOAP_TYPE_ACK);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = zoap_add_option(&response, ZOAP_OPTION_CONTENT_FORMAT,
&plain_text_format, sizeof(plain_text_format));
if (r < 0) {
return -EINVAL;
}
r = zoap_add_block2_option(&response, &ctx);
if (r < 0) {
return -EINVAL;
}
payload = zoap_packet_get_payload(&response, &len);
if (!payload) {
return -EINVAL;
}
size = min(zoap_block_size_to_bytes(ctx.block_size),
ctx.total_size - ctx.current);
if (len < size) {
return -ENOMEM;
}
memset(payload, 'A', size);
r = zoap_packet_set_used(&response, size);
if (r) {
return -EINVAL;
}
r = zoap_next_block(&ctx);
if (!r) {
/* Will return 0 when it's the last block. */
memset(&ctx, 0, sizeof(ctx));
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int large_update_put(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
static struct zoap_block_context ctx;
struct net_buf *buf, *frag;
struct zoap_packet response;
const uint8_t *token;
uint8_t *payload, code, type;
uint16_t len, id;
uint8_t tkl;
int r;
if (ctx.total_size == 0) {
zoap_block_transfer_init(&ctx, ZOAP_BLOCK_64, 0);
}
r = zoap_update_from_block(request, &ctx);
if (r < 0) {
NET_ERR("Invalid block size option from request");
return -EINVAL;
}
NET_INFO("**************\n");
NET_INFO("[ctx] current %u block_size %u total_size %u\n",
ctx.current, zoap_block_size_to_bytes(ctx.block_size),
ctx.total_size);
NET_INFO("**************\n");
payload = zoap_packet_get_payload(request, &len);
if (!payload) {
NET_ERR("Packet without payload\n");
return -EINVAL;
}
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
/* Do something with the payload */
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, ZOAP_TYPE_ACK);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CHANGED);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = zoap_add_block2_option(&response, &ctx);
if (r < 0) {
NET_ERR("Could not add Block2 option to response");
return -EINVAL;
}
r = zoap_add_block1_option(&response, &ctx);
if (r < 0) {
NET_ERR("Could not add Block1 option to response");
return -EINVAL;
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static int large_create_post(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
static struct zoap_block_context ctx;
struct net_buf *buf, *frag;
struct zoap_packet response;
const uint8_t *token;
uint8_t *payload, code, type;
uint16_t len, id;
uint8_t tkl;
int r;
if (ctx.total_size == 0) {
zoap_block_transfer_init(&ctx, ZOAP_BLOCK_32, 0);
}
r = zoap_update_from_block(request, &ctx);
if (r < 0) {
return -EINVAL;
}
payload = zoap_packet_get_payload(request, &len);
if (!payload) {
NET_ERR("Packet without payload\n");
return -EINVAL;
}
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, ZOAP_TYPE_ACK);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTINUE);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
r = zoap_add_block2_option(&response, &ctx);
if (r < 0) {
NET_ERR("Could not add Block2 option to response");
return -EINVAL;
}
r = zoap_add_block1_option(&response, &ctx);
if (r < 0) {
NET_ERR("Could not add Block1 option to response");
return -EINVAL;
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static void update_counter(struct k_work *work)
{
obs_counter++;
if (resource_to_notify) {
zoap_resource_notify(resource_to_notify);
}
k_delayed_work_submit(&observer_work, 5 * MSEC_PER_SEC);
}
static int send_notification_packet(const struct sockaddr *addr, uint16_t age,
socklen_t addrlen, uint16_t id,
const uint8_t *token, uint8_t tkl,
bool is_response)
{
struct zoap_packet response;
struct zoap_pending *pending;
struct net_buf *buf, *frag;
uint8_t *payload, type = ZOAP_TYPE_CON;
uint16_t len;
int r;
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
if (is_response) {
type = ZOAP_TYPE_ACK;
}
zoap_header_set_type(&response, type);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
if (!is_response) {
id = zoap_next_id();
}
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
if (age >= 2) {
zoap_add_option_int(&response, ZOAP_OPTION_OBSERVE, age);
}
r = zoap_add_option(&response, ZOAP_OPTION_CONTENT_FORMAT,
&plain_text_format, sizeof(plain_text_format));
if (r < 0) {
return -EINVAL;
}
payload = zoap_packet_get_payload(&response, &len);
if (!payload) {
return -EINVAL;
}
/* The response that coap-client expects */
r = snprintk((char *) payload, len, "Counter: %d\n", obs_counter);
if (r < 0 || r > len) {
return -EINVAL;
}
r = zoap_packet_set_used(&response, r);
if (r) {
return -EINVAL;
}
if (type == ZOAP_TYPE_CON) {
pending = zoap_pending_next_unused(pendings, NUM_PENDINGS);
if (!pending) {
return -EINVAL;
}
r = zoap_pending_init(pending, &response, addr);
if (r) {
return -EINVAL;
}
zoap_pending_cycle(pending);
pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS);
k_delayed_work_submit(&retransmit_work, pending->timeout);
}
return net_context_sendto(buf, addr, addrlen, NULL, 0, NULL, NULL);
}
static int obs_get(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
struct zoap_observer *observer;
const uint8_t *token;
uint8_t code, type;
uint16_t id;
uint8_t tkl;
bool observe = true;
if (!zoap_request_is_observe(request)) {
observe = false;
goto done;
}
observer = zoap_observer_next_unused(observers, NUM_OBSERVERS);
if (!observer) {
return -ENOMEM;
}
zoap_observer_init(observer, request, from);
zoap_register_observer(resource, observer);
resource_to_notify = resource;
done:
code = zoap_header_get_code(request);
type = zoap_header_get_type(request);
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
NET_INFO("*******\n");
NET_INFO("type: %u code %u id %u\n", type, code, id);
NET_INFO("*******\n");
return send_notification_packet(from, observe ? resource->age : 0,
sizeof(struct sockaddr_in6), id,
token, tkl, true);
}
static void obs_notify(struct zoap_resource *resource,
struct zoap_observer *observer)
{
send_notification_packet(&observer->addr, resource->age,
sizeof(observer->addr), 0,
observer->token, observer->tkl, false);
}
static int core_get(struct zoap_resource *resource,
struct zoap_packet *request,
const struct sockaddr *from)
{
static const char dummy_str[] = "Just a test\n";
struct net_buf *buf, *frag;
struct zoap_packet response;
uint8_t *payload, tkl;
const uint8_t *token;
uint16_t len, id;
int r;
id = zoap_header_get_id(request);
token = zoap_header_get_token(request, &tkl);
buf = net_nbuf_get_tx(context, K_FOREVER);
frag = net_nbuf_get_data(context, K_FOREVER);
net_buf_frag_add(buf, frag);
r = zoap_packet_init(&response, buf);
if (r < 0) {
return -EINVAL;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(&response, 1);
zoap_header_set_type(&response, ZOAP_TYPE_ACK);
zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
zoap_header_set_id(&response, id);
zoap_header_set_token(&response, token, tkl);
payload = zoap_packet_get_payload(&response, &len);
if (!payload) {
return -EINVAL;
}
memcpy(payload, dummy_str, sizeof(dummy_str));
r = zoap_packet_set_used(&response, sizeof(dummy_str));
if (r) {
return -EINVAL;
}
return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
}
static const char * const test_path[] = { "test", NULL };
static const char * const segments_path[] = { "seg1", "seg2", "seg3", NULL };
static const char * const query_path[] = { "query", NULL };
static const char * const separate_path[] = { "separate", NULL };
static const char * const large_path[] = { "large", NULL };
static const char * const location_query_path[] = { "location-query", NULL };
static const char * const large_update_path[] = { "large-update", NULL };
static const char * const large_create_path[] = { "large-create", NULL };
static const char * const obs_path[] = { "obs", NULL };
static const char * const core_1_path[] = { "core1", NULL };
static const char * const core_1_attributes[] = {
"title=\"Core 1\"",
"rt=core1",
NULL };
static const char * const core_2_path[] = { "core2", NULL };
static const char * const core_2_attributes[] = {
"title=\"Core 1\"",
"rt=core1",
NULL };
static struct zoap_resource resources[] = {
{ .get = piggyback_get,
.post = test_post,
.del = test_del,
.put = test_put,
.path = test_path },
{ .get = piggyback_get,
.path = segments_path,
},
{ .get = query_get,
.path = query_path,
},
{ .get = separate_get,
.path = separate_path,
},
{ .path = large_path,
.get = large_get,
},
{ .path = location_query_path,
.post = location_query_post,
},
{ .path = large_update_path,
.put = large_update_put,
},
{ .path = large_create_path,
.post = large_create_post,
},
{ .path = obs_path,
.get = obs_get,
.notify = obs_notify,
},
ZOAP_WELL_KNOWN_CORE_RESOURCE,
{ .get = core_get,
.path = core_1_path,
.user_data = &((struct zoap_core_metadata) {
.attributes = core_1_attributes,
}),
},
{ .get = core_get,
.path = core_2_path,
.user_data = &((struct zoap_core_metadata) {
.attributes = core_2_attributes,
}),
},
{ },
};
static struct zoap_resource *find_resouce_by_observer(
struct zoap_resource *resources, struct zoap_observer *o)
{
struct zoap_resource *r;
for (r = resources; r && r->path; r++) {
sys_snode_t *node;
SYS_SLIST_FOR_EACH_NODE(&r->observers, node) {
if (&o->list == node) {
return r;
}
}
}
return NULL;
}
static void udp_receive(struct net_context *context,
struct net_buf *buf,
int status,
void *user_data)
{
struct zoap_packet request;
struct zoap_pending *pending;
struct sockaddr_in6 from;
int r, header_len;
net_ipaddr_copy(&from.sin6_addr, &NET_IPV6_BUF(buf)->src);
from.sin6_port = NET_UDP_BUF(buf)->src_port;
from.sin6_family = AF_INET6;
/*
* zoap expects that buffer->data starts at the
* beginning of the CoAP header
*/
header_len = net_nbuf_appdata(buf) - buf->frags->data;
net_buf_pull(buf->frags, header_len);
r = zoap_packet_parse(&request, buf);
if (r < 0) {
NET_ERR("Invalid data received (%d)\n", r);
net_nbuf_unref(buf);
return;
}
pending = zoap_pending_received(&request, pendings,
NUM_PENDINGS);
if (pending) {
net_nbuf_unref(buf);
return;
}
if (zoap_header_get_type(&request) == ZOAP_TYPE_RESET) {
struct zoap_resource *r;
struct zoap_observer *o;
o = zoap_find_observer_by_addr(observers, NUM_OBSERVERS,
(struct sockaddr *)&from);
if (!o) {
goto not_found;
}
r = find_resouce_by_observer(resources, o);
if (!r) {
goto not_found;
}
zoap_remove_observer(r, o);
}
not_found:
r = zoap_handle_request(&request, resources,
(const struct sockaddr *) &from);
net_nbuf_unref(buf);
if (r < 0) {
NET_ERR("No handler for such request (%d)\n", r);
return;
}
}
static bool join_coap_multicast_group(void)
{
static struct in6_addr my_addr = MY_IP6ADDR;
static struct sockaddr_in6 mcast_addr = {
.sin6_family = AF_INET6,
.sin6_addr = ALL_NODES_LOCAL_COAP_MCAST,
.sin6_port = htons(MY_COAP_PORT) };
struct net_if_mcast_addr *mcast;
struct net_if_addr *ifaddr;
struct net_if *iface;
iface = net_if_get_default();
if (!iface) {
NET_ERR("Could not get te default interface\n");
return false;
}
#if defined(CONFIG_NET_APP_SETTINGS)
if (net_addr_pton(AF_INET6,
CONFIG_NET_APP_MY_IPV6_ADDR,
&my_addr) < 0) {
NET_ERR("Invalid IPv6 address %s",
CONFIG_NET_APP_MY_IPV6_ADDR);
}
#endif
ifaddr = net_if_ipv6_addr_add(iface, &my_addr, NET_ADDR_MANUAL, 0);
ifaddr->addr_state = NET_ADDR_PREFERRED;
mcast = net_if_ipv6_maddr_add(iface, &mcast_addr.sin6_addr);
if (!mcast) {
NET_ERR("Could not add multicast address to interface\n");
return false;
}
return true;
}
static void retransmit_request(struct k_work *work)
{
struct zoap_pending *pending;
int r;
pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS);
if (!pending) {
return;
}
r = net_context_sendto(pending->buf, &pending->addr,
sizeof(struct sockaddr_in6),
NULL, 0, NULL, NULL);
if (r < 0) {
return;
}
if (!zoap_pending_cycle(pending)) {
zoap_pending_clear(pending);
return;
}
k_delayed_work_submit(&retransmit_work, pending->timeout);
}
void main(void)
{
static struct sockaddr_in6 any_addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ANY_INIT,
.sin6_port = htons(MY_COAP_PORT) };
int r;
#if defined(CONFIG_NET_L2_BLUETOOTH)
if (bt_enable(NULL)) {
NET_ERR("Bluetooth init failed");
return;
}
ipss_init();
ipss_advertise();
#endif
#if defined(CONFIG_NET_L2_IEEE802154)
if (ieee802154_sample_setup()) {
NET_ERR("IEEE 802.15.4 setup failed");
return;
}
#endif
if (!join_coap_multicast_group()) {
NET_ERR("Could not join CoAP multicast group\n");
return;
}
r = net_context_get(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context);
if (r) {
NET_ERR("Could not get an UDP context\n");
return;
}
r = net_context_bind(context, (struct sockaddr *) &any_addr,
sizeof(any_addr));
if (r) {
NET_ERR("Could not bind the context\n");
return;
}
k_delayed_work_init(&retransmit_work, retransmit_request);
k_delayed_work_init(&observer_work, update_counter);
k_delayed_work_submit(&observer_work, 5 * MSEC_PER_SEC);
r = net_context_recv(context, udp_receive, 0, NULL);
if (r) {
NET_ERR("Could not receive in the context\n");
return;
}
}