diff --git a/lib/Kbuild b/lib/Kbuild index 09d3058ef79..d872e63cb0e 100644 --- a/lib/Kbuild +++ b/lib/Kbuild @@ -1 +1,2 @@ obj-y += libc/ +obj-y += iot/ \ No newline at end of file diff --git a/lib/Kconfig b/lib/Kconfig index 088fbf4b06d..ff983dbda3a 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -19,3 +19,4 @@ menu "Cryptography" source "lib/crypto/tinycrypt/Kconfig" endmenu +source "lib/iot/Kconfig" diff --git a/lib/Makefile b/lib/Makefile index 49d51b0e941..1b02e98ea1f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,3 +6,5 @@ ifdef CONFIG_NEWLIB_LIBC ZEPHYRINCLUDE += $(TOOLCHAIN_CFLAGS) ALL_LIBS += m c endif + +include $(srctree)/lib/iot/Makefile diff --git a/lib/iot/Kbuild b/lib/iot/Kbuild new file mode 100644 index 00000000000..08f727b136c --- /dev/null +++ b/lib/iot/Kbuild @@ -0,0 +1 @@ +obj-$(CONFIG_ZOAP) += zoap/ \ No newline at end of file diff --git a/lib/iot/Kconfig b/lib/iot/Kconfig new file mode 100644 index 00000000000..a01ee7d3960 --- /dev/null +++ b/lib/iot/Kconfig @@ -0,0 +1,21 @@ +# +# Copyright (c) 2016 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +menu "IoT Protocols" + +source "lib/iot/zoap/Kconfig" + +endmenu diff --git a/lib/iot/Makefile b/lib/iot/Makefile new file mode 100644 index 00000000000..3fc00a9c3ae --- /dev/null +++ b/lib/iot/Makefile @@ -0,0 +1,3 @@ +ifdef CONFIG_ZOAP +include $(srctree)/lib/iot/zoap/Makefile +endif diff --git a/lib/iot/zoap/Kbuild b/lib/iot/zoap/Kbuild new file mode 100644 index 00000000000..a427ac27cfe --- /dev/null +++ b/lib/iot/zoap/Kbuild @@ -0,0 +1,7 @@ +subdir-ccflags-y +=-I$(srctree)/lib/iot/zoap +ccflags-y += -I${srctree}/net/ip/contiki +ccflags-y += -I${srctree}/net/ip/contiki/os/lib +ccflags-y += -I${srctree}/net/ip/contiki/os +ccflags-y += -I${srctree}/net/ip + +obj-y := zoap.o diff --git a/lib/iot/zoap/Kconfig b/lib/iot/zoap/Kconfig new file mode 100644 index 00000000000..808917eccf7 --- /dev/null +++ b/lib/iot/zoap/Kconfig @@ -0,0 +1,24 @@ +# Kconfig - Zoap, CoAP implementation for Zephyr + +# +# Copyright (c) 2015 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +config ZOAP + bool + prompt "CoAP Support" + default n + help + This option enables the Zoap implementation of CoAP. diff --git a/lib/iot/zoap/Makefile b/lib/iot/zoap/Makefile new file mode 100644 index 00000000000..64d9ac1231d --- /dev/null +++ b/lib/iot/zoap/Makefile @@ -0,0 +1 @@ +ZEPHYRINCLUDE += -I$(srctree)/lib/iot/zoap diff --git a/lib/iot/zoap/zoap.c b/lib/iot/zoap/zoap.c new file mode 100644 index 00000000000..37e677623ec --- /dev/null +++ b/lib/iot/zoap/zoap.c @@ -0,0 +1,868 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "zoap.h" + +struct option_context { + uint8_t *buf; + int delta; + int used; /* size used of options */ + int buflen; +}; + +#define COAP_VERSION 1 + +#define COAP_MARKER 0xFF + +#define BASIC_HEADER_SIZE 4 + +static uint8_t coap_option_header_get_delta(uint8_t buf) +{ + return (buf & 0xF0) >> 4; +} + +static uint8_t coap_option_header_get_len(uint8_t buf) +{ + return buf & 0x0F; +} + +static void coap_option_header_set_delta(uint8_t *buf, uint8_t delta) +{ + *buf |= (delta & 0xF) << 4; +} + +static void coap_option_header_set_len(uint8_t *buf, uint8_t len) +{ + *buf |= (len & 0xF); +} + +static int decode_delta(int num, const uint8_t *buf, int16_t buflen, + uint16_t *decoded) +{ + int hdrlen = 0; + + switch (num) { + case 13: + if (buflen < 1) { + return -EINVAL; + } + + num = *buf + 13; + hdrlen += 1; + break; + case 14: + if (buflen < 2) { + return -EINVAL; + } + + num = sys_be16_to_cpu((uint16_t)*buf) + 269; + hdrlen += 2; + break; + case 15: + return -EINVAL; + } + + *decoded = num; + + return hdrlen; +} + +static int coap_parse_option(const struct zoap_packet *pkt, + struct option_context *context, + uint8_t **value, uint16_t *vlen) +{ + uint16_t delta, len; + int r; + + if (context->buflen < 1) { + return 0; + } + + /* This indicates that options have ended */ + if (context->buf[0] == COAP_MARKER) { + return 0; + } + + delta = coap_option_header_get_delta(context->buf[0]); + len = coap_option_header_get_len(context->buf[0]); + context->buf += 1; + context->used += 1; + context->buflen -= 1; + + /* In case 'delta' doesn't fit the option fixed header. */ + r = decode_delta(delta, context->buf, context->buflen, &delta); + if (r < 0) { + return -EINVAL; + } + + context->buf += r; + context->used += r; + context->buflen -= r; + + /* In case 'len' doesn't fit the option fixed header. */ + r = decode_delta(len, context->buf, context->buflen, &len); + if (r < 0) { + return -EINVAL; + } + + if (context->buflen < r + len) { + return -EINVAL; + } + + if (value) { + *value = context->buf + r; + } + + if (vlen) { + *vlen = len; + } + + context->buf += r + len; + context->used += r + len; + context->buflen -= r + len; + + context->delta += delta; + + return context->used; +} + +static int coap_parse_options(struct zoap_packet *pkt, unsigned int offset) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + struct option_context context = { + .delta = 0, + .used = 0, + .buflen = ip_buf_appdatalen(buf) - offset, + .buf = &appdata[offset] }; + + while (true) { + int r = coap_parse_option(pkt, &context, NULL, NULL); + + if (r < 0) { + return -EINVAL; + } + + if (r == 0) { + break; + } + } + return context.used; +} + +static uint8_t coap_header_get_tkl(const struct zoap_packet *pkt) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + return appdata[0] & 0xF; +} + +static int coap_get_header_len(const struct zoap_packet *pkt) +{ + struct net_buf *buf = pkt->buf; + unsigned int hdrlen; + uint8_t tkl; + + hdrlen = BASIC_HEADER_SIZE; + + if (ip_buf_appdatalen(buf) < hdrlen) { + return -EINVAL; + } + + tkl = coap_header_get_tkl(pkt); + + /* Token lenghts 9-15 are reserved. */ + if (tkl > 8) { + return -EINVAL; + } + + if (net_buf_tailroom(buf) < hdrlen + tkl) { + return -EINVAL; + } + + return hdrlen + tkl; +} + +int zoap_packet_parse(struct zoap_packet *pkt, struct net_buf *buf) +{ + int optlen, hdrlen; + + if (!buf) { + return -EINVAL; + } + + memset(pkt, 0, sizeof(*pkt)); + pkt->buf = buf; + + hdrlen = coap_get_header_len(pkt); + if (hdrlen < 0) { + return -EINVAL; + } + + optlen = coap_parse_options(pkt, hdrlen); + if (optlen < 0) { + return -EINVAL; + } + + if (ip_buf_appdatalen(buf) < hdrlen + optlen) { + return -EINVAL; + } + + if (ip_buf_appdatalen(buf) <= hdrlen + optlen + 1) { + pkt->start = NULL; + return 0; + } + + pkt->start = ip_buf_appdata(buf) + hdrlen + optlen + 1; + + return 0; +} + +static int delta_encode(int num, uint8_t *value, uint8_t *buf, size_t buflen) +{ + uint16_t v; + + if (num < 13) { + *value = num; + return 0; + } + + if (num < 269) { + if (buflen < 1) { + return -EINVAL; + } + + *value = 13; + *buf = num - 13; + return 1; + } + + if (buflen < 2) { + return -EINVAL; + } + + *value = 14; + + v = sys_cpu_to_be16(num - 269); + memcpy(buf, &v, sizeof(v)); + + return 2; +} + +static int coap_option_encode(struct option_context *context, uint16_t code, + const void *value, uint16_t len) +{ + int delta, offset, r; + uint8_t data; + + delta = code - context->delta; + + offset = 1; + + r = delta_encode(delta, &data, context->buf + offset, + context->buflen - offset); + + if (r < 0) { + return -EINVAL; + } + + offset += r; + coap_option_header_set_delta(context->buf, data); + + r = delta_encode(len, &data, context->buf + offset, + context->buflen - offset); + if (r < 0) { + return -EINVAL; + } + + offset += r; + coap_option_header_set_len(context->buf, data); + + if (context->buflen < offset + len) { + return -EINVAL; + } + + memcpy(context->buf + offset, value, len); + + return offset + len; +} + +int zoap_packet_init(struct zoap_packet *pkt, + struct net_buf *buf) +{ + if (!buf) { + return -EINVAL; + } + + if (net_buf_tailroom(buf) < BASIC_HEADER_SIZE) { + return -ENOMEM; + } + + memset(pkt, 0, sizeof(*pkt)); + memset(ip_buf_appdata(buf), 0, net_buf_tailroom(buf)); + + pkt->buf = buf; + ip_buf_appdatalen(buf) = BASIC_HEADER_SIZE; + + return 0; +} + +int zoap_pending_init(struct zoap_pending *pending, + const struct zoap_packet *request) +{ + memset(pending, 0, sizeof(*pending)); + memcpy(&pending->request, request, sizeof(*request)); + + /* FIXME: keeping a reference to the original net_buf is necessary? */ + + return 0; +} + +struct zoap_pending *zoap_pending_next_unused( + struct zoap_pending *pendings, size_t len) +{ + struct zoap_pending *p; + size_t i; + + for (i = 0, p = pendings; i < len; i++, p++) { + struct zoap_packet *pkt = &p->request; + + if (p->timeout == 0 && !pkt->buf) { + return p; + } + } + + return NULL; +} + +struct zoap_reply *zoap_reply_next_unused( + struct zoap_reply *replies, size_t len) +{ + struct zoap_reply *r; + size_t i; + + for (i = 0, r = replies; i < len; i++, r++) { + if (!r->reply) { + return r; + } + } + + return NULL; +} + +static bool match_response(const struct zoap_packet *request, + const struct zoap_packet *response) +{ + const uint8_t *req_token, *resp_token; + uint8_t req_tkl, resp_tkl; + + if (zoap_header_get_id(request) != zoap_header_get_id(response)) { + return false; + } + + req_token = zoap_header_get_token(request, &req_tkl); + resp_token = zoap_header_get_token(response, &resp_tkl); + + if (req_tkl != resp_tkl) { + return false; + } + + return req_tkl == 0 || memcmp(req_token, resp_token, req_tkl) == 0; +} + +struct zoap_pending *zoap_pending_received( + const struct zoap_packet *response, + struct zoap_pending *pendings, size_t len) +{ + struct zoap_pending *p; + size_t i; + + for (i = 0, p = pendings; i < len; i++, p++) { + struct zoap_packet *req = &p->request; + + if (!p->timeout) { + continue; + } + + if (!match_response(req, response)) { + continue; + } + + zoap_pending_clear(p); + return p; + } + + return NULL; +} + +struct zoap_pending *zoap_pending_next_to_expire( + struct zoap_pending *pendings, size_t len) +{ + struct zoap_pending *p, *found = NULL; + size_t i; + + for (i = 0, p = pendings; i < len; i++, p++) { + if (p->timeout && (!found || found->timeout < p->timeout)) { + found = p; + } + } + + return found; +} + +#define LAST_TIMEOUT (2345 * 4) + +static uint16_t next_timeout(uint16_t previous) +{ + switch (previous) { + case 0: + return 2345; + case 2345: + return 2345 * 2; + case (2345 * 2): + return LAST_TIMEOUT; + case LAST_TIMEOUT: + return LAST_TIMEOUT; + } + + return 2345; +} + +bool zoap_pending_cycle(struct zoap_pending *pending) +{ + uint16_t old = pending->timeout; + + pending->timeout = next_timeout(pending->timeout); + + return old != pending->timeout; +} + +void zoap_pending_clear(struct zoap_pending *pending) +{ + pending->timeout = 0; +} + +static bool uri_path_eq(const struct zoap_packet *pkt, + const char * const *path) +{ + struct zoap_option options[16]; + uint16_t count = 16; + int i, r; + + r = zoap_find_options(pkt, ZOAP_OPTION_URI_PATH, options, count); + if (r < 0) { + return false; + } + + count = r; + + for (i = 0; i < count && path[i]; i++) { + size_t len; + + len = strlen(path[i]); + + if (options[i].len != len) { + return false; + } + + if (strncmp(options[i].value, path[i], len)) { + return false; + } + } + + return i == count && !path[i]; +} + +static zoap_method_t method_from_code(const struct zoap_resource *resource, + uint8_t code) +{ + switch (code) { + case ZOAP_METHOD_GET: + return resource->get; + case ZOAP_METHOD_POST: + return resource->post; + case ZOAP_METHOD_PUT: + return resource->put; + case ZOAP_METHOD_DELETE: + return resource->del; + default: + return NULL; + } +} + +int zoap_handle_request(struct zoap_packet *pkt, + struct zoap_resource *resources, + const void *from) +{ + struct zoap_resource *resource; + + for (resource = resources; resource && resource->path; resource++) { + zoap_method_t method; + uint8_t code; + + /* FIXME: deal with hierarchical resources */ + if (!uri_path_eq(pkt, resource->path)) { + continue; + } + + code = zoap_header_get_code(pkt); + method = method_from_code(resource, code); + + if (!method) { + return 0; + } + + return method(resource, pkt, from); + } + + return -ENOENT; +} + +struct zoap_reply *zoap_response_received( + const struct zoap_packet *response, const void *from, + struct zoap_reply *replies, size_t len) +{ + struct zoap_reply *r; + size_t i; + + for (i = 0, r = replies; i < len; i++, r++) { + const uint8_t *token; + uint8_t tkl; + + token = zoap_header_get_token(response, &tkl); + + if (r->tkl != tkl) { + continue; + } + + if (tkl > 0 && memcmp(r->token, token, tkl)) { + continue; + } + + r->reply(response, r, from); + return r; + } + + return NULL; +} + +void zoap_reply_init(struct zoap_reply *reply, + const struct zoap_packet *request) +{ + const uint8_t *token; + uint8_t tkl; + + token = zoap_header_get_token(request, &tkl); + + if (tkl > 0) { + memcpy(reply->token, token, tkl); + } + reply->tkl = tkl; +} + +void zoap_reply_clear(struct zoap_reply *reply) +{ + reply->reply = NULL; +} + +uint8_t *zoap_packet_get_payload(struct zoap_packet *pkt, uint16_t *len) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + if (len) { + *len = 0; + } + + if (!pkt->start) { + if (ip_buf_appdatalen(buf) + 1 > net_buf_tailroom(buf)) { + return NULL; + } + + appdata[ip_buf_appdatalen(buf)] = COAP_MARKER; + ip_buf_appdatalen(buf) += 1; + + pkt->start = appdata + ip_buf_appdatalen(buf); + } + + if (len) { + *len = net_buf_tailroom(buf) - ip_buf_appdatalen(buf); + } + + return pkt->start; +} + +int zoap_packet_set_used(struct zoap_packet *pkt, uint16_t len) +{ + struct net_buf *buf = pkt->buf; + + if (ip_buf_appdatalen(buf) + len > net_buf_tailroom(buf)) { + return -ENOMEM; + } + + ip_buf_appdatalen(buf) += len; + + return 0; +} + +int zoap_add_option(struct zoap_packet *pkt, uint16_t code, + const void *value, uint16_t len) +{ + struct net_buf *buf = pkt->buf; + struct option_context context = { .delta = 0, + .used = 0 }; + int r, offset; + + if (pkt->start) { + return -EINVAL; + } + + offset = coap_get_header_len(pkt); + if (offset < 0) { + return -EINVAL; + } + + /* We check for options in all the 'used' space. */ + context.buflen = ip_buf_appdatalen(buf) - offset; + context.buf = ip_buf_appdata(buf) + offset; + + while (context.delta <= code) { + r = coap_parse_option(pkt, &context, NULL, NULL); + if (r < 0) { + return -ENOENT; + } + + if (r == 0) { + break; + } + + /* If the new option code is out of order. */ + if (code < context.delta) { + return -EINVAL; + } + } + /* We can now add options using all the available space. */ + context.buflen = net_buf_tailroom(buf) - (offset + context.used); + + r = coap_option_encode(&context, code, value, len); + if (r < 0) { + return -EINVAL; + } + + ip_buf_appdatalen(buf) += r; + + return 0; +} + +int zoap_find_options(const struct zoap_packet *pkt, uint16_t code, + struct zoap_option *options, uint16_t veclen) +{ + struct net_buf *buf = pkt->buf; + struct option_context context = { .delta = 0, + .used = 0 }; + int hdrlen, count = 0; + uint16_t len; + + hdrlen = coap_get_header_len(pkt); + if (hdrlen < 0) { + return -EINVAL; + } + + context.buflen = ip_buf_appdatalen(buf) - hdrlen; + context.buf = (uint8_t *)ip_buf_appdata(buf) + hdrlen; + + while (context.delta <= code && count < veclen) { + int used = coap_parse_option(pkt, &context, + (uint8_t **)&options[count].value, + &len); + options[count].len = len; + if (used < 0) { + return -ENOENT; + } + + if (used == 0) { + break; + } + + if (code != context.delta) { + continue; + } + + count++; + } + + return count; +} + +uint8_t zoap_header_get_version(const struct zoap_packet *pkt) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + uint8_t byte = appdata[0]; + + return (byte & 0xC0) >> 6; +} + +uint8_t zoap_header_get_type(const struct zoap_packet *pkt) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + uint8_t byte = appdata[0]; + + return (byte & 0x30) >> 4; +} + +uint8_t coap_header_get_code(const struct zoap_packet *pkt) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + return appdata[1]; +} + +const uint8_t *zoap_header_get_token(const struct zoap_packet *pkt, + uint8_t *len) +{ + struct net_buf *buf = pkt->buf; + uint8_t tkl = coap_header_get_tkl(pkt); + + if (len) { + *len = 0; + } + + if (tkl == 0) { + return NULL; + } + + if (len) { + *len = tkl; + } + + return (uint8_t *)ip_buf_appdata(buf) + BASIC_HEADER_SIZE; +} + +uint8_t zoap_header_get_code(const struct zoap_packet *pkt) +{ + uint8_t code = coap_header_get_code(pkt); + + switch (code) { + /* Methods are encoded in the code field too */ + case ZOAP_METHOD_GET: + case ZOAP_METHOD_POST: + case ZOAP_METHOD_PUT: + case ZOAP_METHOD_DELETE: + + /* All the defined response codes */ + case ZOAP_RESPONSE_CODE_OK: + case ZOAP_RESPONSE_CODE_CREATED: + case ZOAP_RESPONSE_CODE_DELETED: + case ZOAP_RESPONSE_CODE_VALID: + case ZOAP_RESPONSE_CODE_CHANGED: + case ZOAP_RESPONSE_CODE_CONTENT: + case ZOAP_RESPONSE_CODE_BAD_REQUEST: + case ZOAP_RESPONSE_CODE_UNAUTHORIZED: + case ZOAP_RESPONSE_CODE_BAD_OPTION: + case ZOAP_RESPONSE_CODE_FORBIDDEN: + case ZOAP_RESPONSE_CODE_NOT_FOUND: + case ZOAP_RESPONSE_CODE_NOT_ALLOWED: + case ZOAP_RESPONSE_CODE_NOT_ACCEPTABLE: + case ZOAP_RESPONSE_CODE_PRECONDITION_FAILED: + case ZOAP_RESPONSE_CODE_REQUEST_TOO_LARGE: + case ZOAP_RESPONSE_CODE_INTERNAL_ERROR: + case ZOAP_RESPONSE_CODE_NOT_IMPLEMENTED: + case ZOAP_RESPONSE_CODE_BAD_GATEWAY: + case ZOAP_RESPONSE_CODE_SERVICE_UNAVAILABLE: + case ZOAP_RESPONSE_CODE_GATEWAY_TIMEOUT: + case ZOAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED: + case ZOAP_CODE_EMPTY: + return code; + default: + return ZOAP_CODE_EMPTY; + } +} + +uint16_t zoap_header_get_id(const struct zoap_packet *pkt) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + return sys_get_be16(&appdata[2]); +} + +void zoap_header_set_version(struct zoap_packet *pkt, uint8_t ver) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + appdata[0] |= (ver & 0x3) << 6; +} + +void zoap_header_set_type(struct zoap_packet *pkt, uint8_t type) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + appdata[0] |= (type & 0x3) << 4; +} + +int zoap_header_set_token(struct zoap_packet *pkt, const uint8_t *token, + uint8_t tokenlen) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + if (net_buf_tailroom(buf) < BASIC_HEADER_SIZE + tokenlen) { + return -EINVAL; + } + + if (tokenlen > 8) { + return -EINVAL; + } + + ip_buf_appdatalen(buf) += tokenlen; + appdata[0] |= tokenlen & 0xF; + + memcpy(ip_buf_appdata(buf) + BASIC_HEADER_SIZE, token, tokenlen); + + return 0; +} + +void zoap_header_set_code(struct zoap_packet *pkt, uint8_t code) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + appdata[1] = code; +} + +void zoap_header_set_id(struct zoap_packet *pkt, uint16_t id) +{ + struct net_buf *buf = pkt->buf; + uint8_t *appdata = ip_buf_appdata(buf); + + sys_put_be16(id, &appdata[2]); +} diff --git a/lib/iot/zoap/zoap.h b/lib/iot/zoap/zoap.h new file mode 100644 index 00000000000..7a76bce7621 --- /dev/null +++ b/lib/iot/zoap/zoap.h @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * + * @brief CoAP implementation for Zephyr. + */ + +#ifndef __ZOAP_H__ +#define __ZOAP_H__ + +#include +#include +#include + +#include + +#include + +/** + * @brief Set of CoAP packet options we are aware of. + * + * Users may add options other than these to their packets, provided + * they know how to format them correctly. The only restriction is + * that all options must be added to a packet in numeric order. + * + * Refer to RFC 7252, section 12.2 for more information. + */ +enum zoap_option_num { + ZOAP_OPTION_IF_MATCH = 1, + ZOAP_OPTION_URI_HOST = 3, + ZOAP_OPTION_ETAG = 4, + ZOAP_OPTION_IF_NONE_MATCH = 5, + ZOAP_OPTION_OBSERVE = 6, + ZOAP_OPTION_URI_PORT = 7, + ZOAP_OPTION_LOCATION_PATH = 8, + ZOAP_OPTION_URI_PATH = 11, + ZOAP_OPTION_CONTENT_FORMAT = 12, + ZOAP_OPTION_MAX_AGE = 14, + ZOAP_OPTION_URI_QUERY = 15, + ZOAP_OPTION_ACCEPT = 17, + ZOAP_OPTION_LOCATION_QUERY = 20, + ZOAP_OPTION_PROXY_URI = 35, + ZOAP_OPTION_PROXY_SCHEME = 39 +}; + +/** + * @brief Available request methods. + * + * To be used with zoap_header_set_code() when creating a request + * or a response. + */ +enum zoap_method { + ZOAP_METHOD_GET = 1, + ZOAP_METHOD_POST = 2, + ZOAP_METHOD_PUT = 3, + ZOAP_METHOD_DELETE = 4, +}; + +#define ZOAP_REQUEST_MASK 0x07 + +/** + * @brief CoAP packets may be of one of these types. + */ +enum zoap_msgtype { + /** + * Confirmable message. + * + * The packet is a request or response the destination end-point must + * acknowledge. + */ + ZOAP_TYPE_CON = 0, + /** + * Non-confirmable message. + * + * The packet is a request or response that doesn't + * require acknowledgements. + */ + ZOAP_TYPE_NON_CON = 1, + /** + * Acknowledge. + * + * Response to a confirmable message. + */ + ZOAP_TYPE_ACK = 2, + /** + * Reset. + * + * Rejecting a packet for any reason is done by sending a message + * of this type. + */ + ZOAP_TYPE_RESET = 3 +}; + +#define zoap_make_response_code(clas, det) ((clas << 5) | (det)) + +/** + * @brief Set of response codes available for a response packet. + * + * To be used with zoap_header_set_code() when creating a response. + */ +enum zoap_response_code { + ZOAP_RESPONSE_CODE_OK = zoap_make_response_code(2, 0), + ZOAP_RESPONSE_CODE_CREATED = zoap_make_response_code(2, 1), + ZOAP_RESPONSE_CODE_DELETED = zoap_make_response_code(2, 2), + ZOAP_RESPONSE_CODE_VALID = zoap_make_response_code(2, 3), + ZOAP_RESPONSE_CODE_CHANGED = zoap_make_response_code(2, 4), + ZOAP_RESPONSE_CODE_CONTENT = zoap_make_response_code(2, 5), + ZOAP_RESPONSE_CODE_BAD_REQUEST = zoap_make_response_code(4, 0), + ZOAP_RESPONSE_CODE_UNAUTHORIZED = zoap_make_response_code(4, 1), + ZOAP_RESPONSE_CODE_BAD_OPTION = zoap_make_response_code(4, 2), + ZOAP_RESPONSE_CODE_FORBIDDEN = zoap_make_response_code(4, 3), + ZOAP_RESPONSE_CODE_NOT_FOUND = zoap_make_response_code(4, 4), + ZOAP_RESPONSE_CODE_NOT_ALLOWED = zoap_make_response_code(4, 5), + ZOAP_RESPONSE_CODE_NOT_ACCEPTABLE = zoap_make_response_code(4, 6), + ZOAP_RESPONSE_CODE_PRECONDITION_FAILED = zoap_make_response_code(4, 12), + ZOAP_RESPONSE_CODE_REQUEST_TOO_LARGE = zoap_make_response_code(4, 13), + ZOAP_RESPONSE_CODE_INTERNAL_ERROR = zoap_make_response_code(5, 0), + ZOAP_RESPONSE_CODE_NOT_IMPLEMENTED = zoap_make_response_code(5, 1), + ZOAP_RESPONSE_CODE_BAD_GATEWAY = zoap_make_response_code(5, 2), + ZOAP_RESPONSE_CODE_SERVICE_UNAVAILABLE = zoap_make_response_code(5, 3), + ZOAP_RESPONSE_CODE_GATEWAY_TIMEOUT = zoap_make_response_code(5, 4), + ZOAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED = zoap_make_response_code(5, 5) +}; + +#define ZOAP_CODE_EMPTY (0) + +struct zoap_packet; +struct zoap_pending; +struct zoap_reply; +struct zoap_resource; + +/** + * Type of the callback being called when a resource's method is invoked by + * remote entity. + */ +typedef int (*zoap_method_t)(struct zoap_resource *resource, + struct zoap_packet *request, + const void *from); + +/** + * @brief Description of CoAP resource. + * + * CoAP servers often want to register resources, so that clients can act on + * them, by fetching their state or requesting updates to them. + */ +struct zoap_resource { + zoap_method_t get, post, put, del; + const char * const *path; + void *user_data; + int age; +}; + +/** + * Representation of a CoAP packet. + */ +struct zoap_packet { + struct net_buf *buf; + uint8_t *start; /* Start of the payload */ +}; + +/** + * Helper function to be called when a response matches the + * a pending request. + */ +typedef int (*zoap_reply_t)(const struct zoap_packet *response, + struct zoap_reply *reply, const void *from); + +/** + * Represents a request awaiting for an acknowledgment (ACK). + */ +struct zoap_pending { + struct zoap_packet request; + uint16_t timeout; +}; + +/** + * Represents the handler for the reply of a request, it is also used when + * observing resources. + */ +struct zoap_reply { + zoap_reply_t reply; + void *user_data; + uint8_t token[8]; + uint8_t tkl; +}; + +/** + * Indicates that a reply is expected for @a request. + */ +void zoap_reply_init(struct zoap_reply *reply, + const struct zoap_packet *request); + +/** + * Represents the value of a CoAP option. + * + * To be used with zoap_find_options(). + */ +struct zoap_option { + uint8_t *value; + uint16_t len; +}; + +/** + * Parses the CoAP packet in @a buf, validating it and initializing @a pkt. + * @a buf must remain valid while @a pkt is used. Used when receiving packets. + */ +int zoap_packet_parse(struct zoap_packet *pkt, struct net_buf *buf); + +/** + * Creates a new CoAP packet from a net_buf. @a buf must remain valid while + * @a pkt is used. Used when creating packets to be sent. + */ +int zoap_packet_init(struct zoap_packet *pkt, struct net_buf *buf); + +/** + * Initialize a pending request with a request. The request's fields are + * copied into the pending struct, so @a request doesn't have to live for as + * long as the pending struct lives, but net_buf needs to live for at least + * that long. + */ +int zoap_pending_init(struct zoap_pending *pending, + const struct zoap_packet *request); + +/** + * Returns the next available pending struct, that can be used to track + * the retransmission status of a request. + */ +struct zoap_pending *zoap_pending_next_unused( + struct zoap_pending *pendings, size_t len); + +/** + * Returns the next available reply struct, so it can be used to track replies + * and notifications received. + */ +struct zoap_reply *zoap_reply_next_unused( + struct zoap_reply *replies, size_t len); + +/** + * After a response is received, clear all pending retransmissions related to + * that response. + */ +struct zoap_pending *zoap_pending_received( + const struct zoap_packet *response, + struct zoap_pending *pendings, size_t len); + +/** + * After a response is received, clear all pending retransmissions related to + * that response. + */ +struct zoap_reply *zoap_response_received( + const struct zoap_packet *response, const void *from, + struct zoap_reply *replies, size_t len); + +/** + * Returns the next pending about to expire, pending->timeout informs how many + * ms to next expiration. + */ +struct zoap_pending *zoap_pending_next_to_expire( + struct zoap_pending *pendings, size_t len); + +/** + * After a request is sent, user may want to cycle the pending retransmission + * so the timeout is updated. Returns false if this is the last + * retransmission. + */ +bool zoap_pending_cycle(struct zoap_pending *pending); + +/** + * Cancels the pending retransmission, so it again becomes available. + */ +void zoap_pending_clear(struct zoap_pending *pending); + +/** + * Cancels awaiting for this reply, so it becomes available again. + */ +void zoap_reply_clear(struct zoap_reply *reply); + +/** + * When a request is received, call the appropriate methods of the + * matching resources. + */ +int zoap_handle_request(struct zoap_packet *pkt, + struct zoap_resource *resources, + const void *from); + +/** + * Returns a pointer to the start of the payload, and how much memory + * is available (to the payload), it will also insert the + * COAP_MARKER (0xFF). + */ +uint8_t *zoap_packet_get_payload(struct zoap_packet *pkt, uint16_t *len); + +/** + * Sets how much space was used by the payload. + */ +int zoap_packet_set_used(struct zoap_packet *pkt, uint16_t len); + +/** + * Adds an option to the packet. Note that options must be added + * in numeric order of their codes. + */ +int zoap_add_option(struct zoap_packet *pkt, uint16_t code, + const void *value, uint16_t len); + +/** + * Return the values associated with the option of value @a code. + */ +int zoap_find_options(const struct zoap_packet *pkt, uint16_t code, + struct zoap_option *options, uint16_t veclen); + + +/** + * Returns the version present in a CoAP packet. + */ +uint8_t zoap_header_get_version(const struct zoap_packet *pkt); + +/** + * Returns the type of the packet present in the CoAP packet. + */ +uint8_t zoap_header_get_type(const struct zoap_packet *pkt); + +/** + * Returns the token associated with a CoAP packet. + */ +const uint8_t *zoap_header_get_token(const struct zoap_packet *pkt, + uint8_t *len); + +/** + * Returns the code present in the header of a CoAP packet. + */ +uint8_t zoap_header_get_code(const struct zoap_packet *pkt); + +/** + * Returns the message id associated with a CoAP packet. + */ +uint16_t zoap_header_get_id(const struct zoap_packet *pkt); + +/** + * Sets the CoAP version present in the CoAP header of a packet. + */ +void zoap_header_set_version(struct zoap_packet *pkt, uint8_t ver); + +/** + * Sets the type of a CoAP message. + */ +void zoap_header_set_type(struct zoap_packet *pkt, uint8_t type); + +/** + * Sets the token present in the CoAP header of a packet. + */ +int zoap_header_set_token(struct zoap_packet *pkt, const uint8_t *token, + uint8_t tokenlen); + +/** + * Sets the code present in the header of a CoAP packet. + */ +void zoap_header_set_code(struct zoap_packet *pkt, uint8_t code); + +/** + * Sets the message id associated with a CoAP packet. + */ +void zoap_header_set_id(struct zoap_packet *pkt, uint16_t id); + +static inline uint16_t zoap_next_id(void) +{ + static uint16_t message_id; + + return ++message_id; +} + +#endif /* __ZOAP_H__ */