summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@intel.com>2016-08-03 15:23:32 -0300
committerAnas Nashif <nashif@linux.intel.com>2016-09-12 12:25:24 +0000
commit2baa577b4954847ee2a24ae58f8bf9ae8ad65d62 (patch)
treecdc29ebd00761e516c99e508b78485d9c8b88edf /lib
parent3ef2e059c3cbfb62467e759807e37780c03a9bf8 (diff)
lib: Introduce the CoAP implementation for Zephyr
This is a CoAP implementation tailored for Zephyr's requirements, it tries to use memory predictably by exposing its control structures to the user (so the user only pays for the features that are used) and having a lightweight packet representation (the trade-off is that adding options is not as efficient as it could be, if there were more information available). Change-Id: I6f74146c4626a0c554f50b42f163a076e82805ba Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/Kbuild1
-rw-r--r--lib/Kconfig1
-rw-r--r--lib/Makefile2
-rw-r--r--lib/iot/Kbuild1
-rw-r--r--lib/iot/Kconfig21
-rw-r--r--lib/iot/Makefile3
-rw-r--r--lib/iot/zoap/Kbuild7
-rw-r--r--lib/iot/zoap/Kconfig24
-rw-r--r--lib/iot/zoap/Makefile1
-rw-r--r--lib/iot/zoap/zoap.c868
-rw-r--r--lib/iot/zoap/zoap.h386
11 files changed, 1315 insertions, 0 deletions
diff --git a/lib/Kbuild b/lib/Kbuild
index 09d3058ef..d872e63cb 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 088fbf4b0..ff983dbda 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 49d51b0e9..1b02e98ea 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 000000000..08f727b13
--- /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 000000000..a01ee7d39
--- /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 000000000..3fc00a9c3
--- /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 000000000..a427ac27c
--- /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 000000000..808917ecc
--- /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 000000000..64d9ac123
--- /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 000000000..37e677623
--- /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 <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <misc/byteorder.h>
+#include <net/ip_buf.h>
+
+#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 000000000..7a76bce76
--- /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 <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <contiki/ip/uip.h>
+
+#include <misc/slist.h>
+
+/**
+ * @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__ */