diff --git a/doc/api/networking.rst b/doc/api/networking.rst index 4f06fcb2b6f62..871714b55fe96 100644 --- a/doc/api/networking.rst +++ b/doc/api/networking.rst @@ -136,7 +136,7 @@ MQTT 3.1.1 CoAP ==== -.. doxygengroup:: zoap +.. doxygengroup:: coap :project: Zephyr DNS Resolve diff --git a/doc/subsystems/networking/overview.rst b/doc/subsystems/networking/overview.rst index 7366736060e61..c82abd39b0132 100644 --- a/doc/subsystems/networking/overview.rst +++ b/doc/subsystems/networking/overview.rst @@ -63,10 +63,10 @@ can be disabled if not needed. A sample :ref:`mqtt-publisher-sample` client application for MQTT v3.1.1 is implemented. -* **CoAP** Constrained Application Protocol (RFC 7252) is supported. Both - Both :ref:`zoap-client-sample` and :ref:`zoap-server-sample` sample - applications are implemented. A :ref:`coap-client-sample` and - :ref:`coap-server-sample` using DTLS (Datagram Transport Layer Security) +* **CoAP** Constrained Application Protocol (RFC 7252) is supported. + Both :ref:`coap-client-sample` and :ref:`coap-server-sample` sample + applications are implemented. A :ref:`coaps-client-sample` and + :ref:`coaps-server-sample` using DTLS (Datagram Transport Layer Security) (RFC 6347) are also implemented. * **LWM2M** OMA Lightweight Machine-to-Machine Protocol (V1.0 Feb 2017) is diff --git a/doc/zephyr.doxyfile b/doc/zephyr.doxyfile index a13588243cabc..03a5597bdbae0 100644 --- a/doc/zephyr.doxyfile +++ b/doc/zephyr.doxyfile @@ -107,7 +107,7 @@ INPUT = \ ../include/arch/arm/cortex_m \ ../include/arch/nios2/ \ ../lib/libc/minimal/include/ \ - ../include/net/zoap.h \ + ../include/net/coap.h \ ../include/net/dns_resolve.h \ ../tests/ztest/include/ INPUT_ENCODING = UTF-8 diff --git a/ext/lib/crypto/mbedtls/configs/config-lwm2m.h b/ext/lib/crypto/mbedtls/configs/config-lwm2m.h new file mode 100644 index 0000000000000..d16a5d1060003 --- /dev/null +++ b/ext/lib/crypto/mbedtls/configs/config-lwm2m.h @@ -0,0 +1,97 @@ +/* + * Minimal configuration for DTLS 1.2 with PSK and AES-CCM ciphersuites + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +/* + * Minimal configuration for TLS 1.2 with PSK and AES-CCM ciphersuites + * Distinguishing features: + * - no bignum, no PK, no X509 + * - fully modern and secure (provided the pre-shared keys have high entropy) + * - very low record overhead with CCM-8 + * - optimized for low RAM usage + * + * See README.txt for usage instructions. + */ +#ifndef MBEDTLS_CONFIG_H +#define MBEDTLS_CONFIG_H + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES +#define MBEDTLS_PLATFORM_PRINTF_ALT + +#if defined(CONFIG_MBEDTLS_TEST) +#define MBEDTLS_SELF_TEST +#define MBEDTLS_DEBUG_C +#else +#define MBEDTLS_ENTROPY_C +#endif + + +/* mbed TLS feature support */ +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_PROTO_DTLS +#define MBEDTLS_SSL_DTLS_ANTI_REPLAY +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY + + +/* mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_CCM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_MD_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SSL_COOKIE_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C + +/* Save RAM at the expense of ROM */ +#define MBEDTLS_AES_ROM_TABLES + +/* Save some RAM by adjusting to your exact needs */ +#define MBEDTLS_PSK_MAX_LEN 16 /* 128-bits keys are generally enough */ + +#if defined(CONFIG_MBEDTLS_DEBUG) +#define MBEDTLS_ERROR_C +#define MBEDTLS_DEBUG_C +#define MBEDTLS_SSL_DEBUG_ALL +#define MBEDTLS_SSL_ALL_ALERT_MESSAGES +#endif + +/* + * Use only CCM_8 ciphersuites, and + * save ROM and a few bytes of RAM by specifying our own ciphersuite list + */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8 + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1500 + +#include "mbedtls/check_config.h" + +#endif /* MBEDTLS_CONFIG_H */ diff --git a/include/net/coap.h b/include/net/coap.h new file mode 100644 index 0000000000000..6a7c173c8986c --- /dev/null +++ b/include/net/coap.h @@ -0,0 +1,814 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief CoAP implementation for Zephyr. + */ + +#ifndef __COAP_H__ +#define __COAP_H__ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief COAP library + * @defgroup COAP Library + * @{ + */ + +/** + * @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 coap_option_num { + COAP_OPTION_IF_MATCH = 1, + COAP_OPTION_URI_HOST = 3, + COAP_OPTION_ETAG = 4, + COAP_OPTION_IF_NONE_MATCH = 5, + COAP_OPTION_OBSERVE = 6, + COAP_OPTION_URI_PORT = 7, + COAP_OPTION_LOCATION_PATH = 8, + COAP_OPTION_URI_PATH = 11, + COAP_OPTION_CONTENT_FORMAT = 12, + COAP_OPTION_MAX_AGE = 14, + COAP_OPTION_URI_QUERY = 15, + COAP_OPTION_ACCEPT = 17, + COAP_OPTION_LOCATION_QUERY = 20, + COAP_OPTION_BLOCK2 = 23, + COAP_OPTION_BLOCK1 = 27, + COAP_OPTION_SIZE2 = 28, + COAP_OPTION_PROXY_URI = 35, + COAP_OPTION_PROXY_SCHEME = 39, + COAP_OPTION_SIZE1 = 60, +}; + +/** + * @brief Available request methods. + * + * To be used when creating a request or a response. + */ +enum coap_method { + COAP_METHOD_GET = 1, + COAP_METHOD_POST = 2, + COAP_METHOD_PUT = 3, + COAP_METHOD_DELETE = 4, +}; + +#define COAP_REQUEST_MASK 0x07 + +/** + * @brief CoAP packets may be of one of these types. + */ +enum coap_msgtype { + /** + * Confirmable message. + * + * The packet is a request or response the destination end-point must + * acknowledge. + */ + COAP_TYPE_CON = 0, + /** + * Non-confirmable message. + * + * The packet is a request or response that doesn't + * require acknowledgements. + */ + COAP_TYPE_NON_CON = 1, + /** + * Acknowledge. + * + * Response to a confirmable message. + */ + COAP_TYPE_ACK = 2, + /** + * Reset. + * + * Rejecting a packet for any reason is done by sending a message + * of this type. + */ + COAP_TYPE_RESET = 3 +}; + +#define coap_make_response_code(clas, det) ((clas << 5) | (det)) + +/** + * @brief Set of response codes available for a response packet. + * + * To be used when creating a response. + */ +enum coap_response_code { + COAP_RESPONSE_CODE_OK = coap_make_response_code(2, 0), + COAP_RESPONSE_CODE_CREATED = coap_make_response_code(2, 1), + COAP_RESPONSE_CODE_DELETED = coap_make_response_code(2, 2), + COAP_RESPONSE_CODE_VALID = coap_make_response_code(2, 3), + COAP_RESPONSE_CODE_CHANGED = coap_make_response_code(2, 4), + COAP_RESPONSE_CODE_CONTENT = coap_make_response_code(2, 5), + COAP_RESPONSE_CODE_CONTINUE = coap_make_response_code(2, 31), + COAP_RESPONSE_CODE_BAD_REQUEST = coap_make_response_code(4, 0), + COAP_RESPONSE_CODE_UNAUTHORIZED = coap_make_response_code(4, 1), + COAP_RESPONSE_CODE_BAD_OPTION = coap_make_response_code(4, 2), + COAP_RESPONSE_CODE_FORBIDDEN = coap_make_response_code(4, 3), + COAP_RESPONSE_CODE_NOT_FOUND = coap_make_response_code(4, 4), + COAP_RESPONSE_CODE_NOT_ALLOWED = coap_make_response_code(4, 5), + COAP_RESPONSE_CODE_NOT_ACCEPTABLE = coap_make_response_code(4, 6), + COAP_RESPONSE_CODE_INCOMPLETE = coap_make_response_code(4, 8), + COAP_RESPONSE_CODE_PRECONDITION_FAILED = coap_make_response_code(4, 12), + COAP_RESPONSE_CODE_REQUEST_TOO_LARGE = coap_make_response_code(4, 13), + COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT = + coap_make_response_code(4, 15), + COAP_RESPONSE_CODE_INTERNAL_ERROR = coap_make_response_code(5, 0), + COAP_RESPONSE_CODE_NOT_IMPLEMENTED = coap_make_response_code(5, 1), + COAP_RESPONSE_CODE_BAD_GATEWAY = coap_make_response_code(5, 2), + COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE = coap_make_response_code(5, 3), + COAP_RESPONSE_CODE_GATEWAY_TIMEOUT = coap_make_response_code(5, 4), + COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED = + coap_make_response_code(5, 5) +}; + +#define COAP_CODE_EMPTY (0) + +struct coap_observer; +struct coap_packet; +struct coap_pending; +struct coap_reply; +struct coap_resource; + +/** + * @typedef coap_method_t + * @brief Type of the callback being called when a resource's method is + * invoked by the remote entity. + */ +typedef int (*coap_method_t)(struct coap_resource *resource, + struct coap_packet *request); + +/** + * @typedef coap_notify_t + * @brief Type of the callback being called when a resource's has observers + * to be informed when an update happens. + */ +typedef void (*coap_notify_t)(struct coap_resource *resource, + struct coap_observer *observer); + +/** + * @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 coap_resource { + /** Which function to be called for each CoAP method */ + coap_method_t get, post, put, del; + coap_notify_t notify; + const char * const *path; + void *user_data; + sys_slist_t observers; + int age; +}; + +/** + * @brief Represents a remote device that is observing a local resource. + */ +struct coap_observer { + sys_snode_t list; + struct sockaddr addr; + u8_t token[8]; + u8_t tkl; +}; + +/** + * @brief Representation of a CoAP packet. + */ +struct coap_packet { + struct net_pkt *pkt; + struct net_buf *frag; /* Where CoAP header resides */ + u16_t offset; /* Where CoAP header starts.*/ + u8_t hdr_len; /* CoAP header length */ + u8_t opt_len; /* Total options length (delta + len + value) */ + u16_t last_delta; /* Used only when preparing CoAP packet */ +}; + +/** + * @typedef coap_reply_t + * @brief Helper function to be called when a response matches the + * a pending request. + */ +typedef int (*coap_reply_t)(const struct coap_packet *response, + struct coap_reply *reply, + const struct sockaddr *from); + +/** + * @brief Represents a request awaiting for an acknowledgment (ACK). + */ +struct coap_pending { + struct net_pkt *pkt; + struct sockaddr addr; + s32_t timeout; + u16_t id; +}; + +/** + * @brief Represents the handler for the reply of a request, it is + * also used when observing resources. + */ +struct coap_reply { + coap_reply_t reply; + void *user_data; + int age; + u8_t token[8]; + u16_t id; + u8_t tkl; +}; + +/** + * @brief Indicates that the remote device referenced by @a addr, with + * @a request, wants to observe a resource. + * + * @param observer Observer to be initialized + * @param request Request on which the observer will be based + * @param addr Address of the remote device + */ +void coap_observer_init(struct coap_observer *observer, + const struct coap_packet *request, + const struct sockaddr *addr); + +/** + * @brief After the observer is initialized, associate the observer + * with an resource. + * + * @param resource Resource to add an observer + * @param observer Observer to be added + * + * @return true if this is the first observer added to this resource. + */ +bool coap_register_observer(struct coap_resource *resource, + struct coap_observer *observer); + +/** + * @brief Remove this observer from the list of registered observers + * of that resource. + * + * @param resource Resource in which to remove the observer + * @param observer Observer to be removed + */ +void coap_remove_observer(struct coap_resource *resource, + struct coap_observer *observer); + +/** + * @brief Returns the observer that matches address @a addr. + * + * @param observers Pointer to the array of observers + * @param len Size of the array of observers + * @param addr Address of the endpoint observing a resource + * + * @return A pointer to a observer if a match is found, NULL + * otherwise. + */ +struct coap_observer *coap_find_observer_by_addr( + struct coap_observer *observers, size_t len, + const struct sockaddr *addr); + +/** + * @brief Returns the next available observer representation. + * + * @param observers Pointer to the array of observers + * @param len Size of the array of observers + * + * @return A pointer to a observer if there's an available observer, + * NULL otherwise. + */ +struct coap_observer *coap_observer_next_unused( + struct coap_observer *observers, size_t len); + +/** + * @brief Indicates that a reply is expected for @a request. + * + * @param reply Reply structure to be initialized + * @param request Request from which @a reply will be based + */ +void coap_reply_init(struct coap_reply *reply, + const struct coap_packet *request); + +/** + * @brief Represents the value of a CoAP option. + * + * To be used with coap_find_options(). + */ +struct coap_option { + u16_t delta; + +#if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN) + u16_t len; + u8_t value[CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE]; +#else + u8_t len; + u8_t value[12]; +#endif +}; + +/** + * @brief Parses the CoAP packet in @a pkt, validating it and + * initializing @a cpkt. @a pkt must remain valid while @a cpkt is used. + * + * @param cpkt Packet to be initialized from received @a pkt. + * @param pkt Network Packet containing a CoAP packet, its @a data pointer is + * positioned on the start of the CoAP packet. + * @param options Parse options and cache its details. + * @param opt_num Number of options + * + * @return 0 in case of success or negative in case of error. + */ +int coap_packet_parse(struct coap_packet *cpkt, struct net_pkt *pkt, + struct coap_option *options, u8_t opt_num); + +/** + * @brief Creates a new CoAP packet from a net_pkt. @a pkt must remain + * valid while @a cpkt is used. + * + * @param cpkt New packet to be initialized using the storage from @a + * pkt. + * @param pkt Network Packet that will contain a CoAP packet + * @param ver CoAP header version + * @param type CoAP header type + * @param tokenlen CoAP header token length + * @param token CoAP header token + * @param code CoAP header code + * @param id CoAP header message id + * + * @return 0 in case of success or negative in case of error. + */ +int coap_packet_init(struct coap_packet *cpkt, struct net_pkt *pkt, + u8_t ver, u8_t type, u8_t tokenlen, + u8_t *token, u8_t code, u16_t id); + +/** + * @brief 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_pkt needs to live for at least that long. + * + * @param pending Structure representing the waiting for a + * confirmation message, initialized with data from @a request + * @param request Message waiting for confirmation + * @param addr Address to send the retransmission + * + * @return 0 in case of success or negative in case of error. + */ +int coap_pending_init(struct coap_pending *pending, + const struct coap_packet *request, + const struct sockaddr *addr); + +/** + * @brief Returns the next available pending struct, that can be used + * to track the retransmission status of a request. + * + * @param pendings Pointer to the array of #coap_pending structures + * @param len Size of the array of #coap_pending structures + * + * @return pointer to a free #coap_pending structure, NULL in case + * none could be found. + */ +struct coap_pending *coap_pending_next_unused( + struct coap_pending *pendings, size_t len); + +/** + * @brief Returns the next available reply struct, so it can be used + * to track replies and notifications received. + * + * @param replies Pointer to the array of #coap_reply structures + * @param len Size of the array of #coap_reply structures + * + * @return pointer to a free #coap_reply structure, NULL in case + * none could be found. + */ +struct coap_reply *coap_reply_next_unused( + struct coap_reply *replies, size_t len); + +/** + * @brief After a response is received, clear all pending + * retransmissions related to that response. + * + * @param response The received response + * @param pendings Pointer to the array of #coap_reply structures + * @param len Size of the array of #coap_reply structures + * + * @return pointer to the associated #coap_pending structure, NULL in + * case none could be found. + */ +struct coap_pending *coap_pending_received( + const struct coap_packet *response, + struct coap_pending *pendings, size_t len); + +/** + * @brief After a response is received, call coap_reply_t handler + * registered in #coap_reply structure + * + * @param response A response received + * @param from Address from which the response was received + * @param replies Pointer to the array of #coap_reply structures + * @param len Size of the array of #coap_reply structures + * + * @return Pointer to the reply matching the packet received, NULL if + * none could be found. + */ +struct coap_reply *coap_response_received( + const struct coap_packet *response, + const struct sockaddr *from, + struct coap_reply *replies, size_t len); + +/** + * @brief Returns the next pending about to expire, pending->timeout + * informs how many ms to next expiration. + * + * @param pendings Pointer to the array of #coap_pending structures + * @param len Size of the array of #coap_pending structures + * + * @return The next #coap_pending to expire, NULL if none is about to + * expire. + */ +struct coap_pending *coap_pending_next_to_expire( + struct coap_pending *pendings, size_t len); + +/** + * @brief After a request is sent, user may want to cycle the pending + * retransmission so the timeout is updated. + * + * @param pending Pending representation to have its timeout updated + * + * @return false if this is the last retransmission. + */ +bool coap_pending_cycle(struct coap_pending *pending); + +/** + * @brief Cancels the pending retransmission, so it again becomes + * available. + * + * @param pending Pending representation to be canceled + */ +void coap_pending_clear(struct coap_pending *pending); + +/** + * @brief Cancels awaiting for this reply, so it becomes available + * again. + * + * @param reply The reply to be canceled + */ +void coap_reply_clear(struct coap_reply *reply); + +/** + * @brief When a request is received, call the appropriate methods of + * the matching resources. + * + * @param cpkt Packet received + * @param resources Array of known resources + * @param options Parsed options from coap_packet_parse() + * @param opt_num Number of options + * + * @return 0 in case of success or negative in case of error. + */ +int coap_handle_request(struct coap_packet *cpkt, + struct coap_resource *resources, + struct coap_option *options, + u8_t opt_num); + +/** + * @brief Indicates that this resource was updated and that the @a + * notify callback should be called for every registered observer. + * + * @param resource Resource that was updated + * + * @return 0 in case of success or negative in case of error. + */ +int coap_resource_notify(struct coap_resource *resource); + +/** + * @brief Returns if this request is enabling observing a resource. + * + * @param request Request to be checked + * + * @return True if the request is enabling observing a resource, False + * otherwise + */ +bool coap_request_is_observe(const struct coap_packet *request); + +/** + * @brief Append payload marker to CoAP packet + * + * @param cpkt Packet to append the payload marker (0xFF) + * + * @return 0 in case of success or negative in case of error. + */ +int coap_packet_append_payload_marker(struct coap_packet *cpkt); + +/** + * @brief Append payload to CoAP packet + * + * @param cpkt Packet to append the payload + * @param payload CoAP packet payload + * @param payload_len CoAP packet payload len + * + * @return 0 in case of success or negative in case of error. + */ +int coap_packet_append_payload(struct coap_packet *cpkt, u8_t *payload, + u16_t payload_len); + +/** + * @brief Appends an option to the packet. + * + * Note: options must be added in numeric order of their codes. Otherwise + * error will be returned. + * TODO: Add support for placing options according to its delta value. + * + * @param cpkt Packet to be updated + * @param code Option code to add to the packet, see #coap_option_num + * @param value Pointer to the value of the option, will be copied to the packet + * @param len Size of the data to be added + * + * @return 0 in case of success or negative in case of error. + */ +int coap_packet_append_option(struct coap_packet *cpkt, u16_t code, + const u8_t *value, u16_t len); + +/** + * @brief Converts an option to its integer representation. + * + * Assumes that the number is encoded in the network byte order in the + * option. + * + * @param option Pointer to the option value, retrieved by + * coap_find_options() + * + * @return The integer representation of the option + */ +unsigned int coap_option_value_to_int(const struct coap_option *option); + +/** + * @brief Appends an integer value option to the packet. + * + * The option must be added in numeric order of their codes, and the + * least amount of bytes will be used to encode the value. + * + * @param cpkt Packet to be updated + * @param code Option code to add to the packet, see #coap_option_num + * @param val Integer value to be added + * + * @return 0 in case of success or negative in case of error. + */ +int coap_append_option_int(struct coap_packet *cpkt, u16_t code, + unsigned int val); + +/** + * @brief Return the values associated with the option of value @a + * code. + * + * @param cpkt CoAP packet representation + * @param code Option number to look for + * @param options Array of #coap_option where to store the value + * of the options found + * @param veclen Number of elements in the options array + * + * @return The number of options found in packet matching code, + * negative on error. + */ +int coap_find_options(const struct coap_packet *cpkt, u16_t code, + struct coap_option *options, u16_t veclen); + +/** + * Represents the size of each block that will be transferred using + * block-wise transfers [RFC7959]: + * + * Each entry maps directly to the value that is used in the wire. + * + * https://tools.ietf.org/html/rfc7959 + */ +enum coap_block_size { + COAP_BLOCK_16, + COAP_BLOCK_32, + COAP_BLOCK_64, + COAP_BLOCK_128, + COAP_BLOCK_256, + COAP_BLOCK_512, + COAP_BLOCK_1024, +}; + +/** + * @brief Helper for converting the enumeration to the size expressed + * in bytes. + * + * @param block_size The block size to be converted + * + * @return The size in bytes that the block_size represents + */ +static inline u16_t coap_block_size_to_bytes( + enum coap_block_size block_size) +{ + return (1 << (block_size + 4)); +} + +/** + * @brief Represents the current state of a block-wise transaction. + */ +struct coap_block_context { + size_t total_size; + size_t current; + enum coap_block_size block_size; +}; + +/** + * @brief Initializes the context of a block-wise transfer. + * + * @param ctx The context to be initialized + * @param block_size The size of the block + * @param total_size The total size of the transfer, if known + * + * @return 0 in case of success or negative in case of error. + */ +int coap_block_transfer_init(struct coap_block_context *ctx, + enum coap_block_size block_size, + size_t total_size); + +/** + * @brief Append BLOCK1 option to the packet. + * + * @param cpkt Packet to be updated + * @param ctx Block context from which to retrieve the + * information for the Block1 option + * + * @return 0 in case of success or negative in case of error. + */ +int coap_append_block1_option(struct coap_packet *cpkt, + struct coap_block_context *ctx); + +/** + * @brief Append BLOCK2 option to the packet. + * + * @param cpkt Packet to be updated + * @param ctx Block context from which to retrieve the + * information for the Block2 option + * + * @return 0 in case of success or negative in case of error. + */ +int coap_append_block2_option(struct coap_packet *cpkt, + struct coap_block_context *ctx); + +/** + * @brief Append SIZE1 option to the packet. + * + * @param cpkt Packet to be updated + * @param ctx Block context from which to retrieve the + * information for the Size1 option + * + * @return 0 in case of success or negative in case of error. + */ +int coap_append_size1_option(struct coap_packet *cpkt, + struct coap_block_context *ctx); + +/** + * @brief Append SIZE2 option to the packet. + * + * @param cpkt Packet to be updated + * @param ctx Block context from which to retrieve the + * information for the Size2 option + * + * @return 0 in case of success or negative in case of error. + */ +int coap_append_size2_option(struct coap_packet *cpkt, + struct coap_block_context *ctx); + +/** + * @brief Retrieves BLOCK{1,2} and SIZE{1,2} from @a cpkt and updates + * @a ctx accordingly. + * + * @param cpkt Packet in which to look for block-wise transfers options + * @param ctx Block context to be updated + * + * @return 0 in case of success or negative in case of error. + */ +int coap_update_from_block(const struct coap_packet *cpkt, + struct coap_block_context *ctx); + +/** + * @brief Updates @a ctx so after this is called the current entry + * indicates the correct offset in the body of data being + * transferred. + * + * @param cpkt Packet in which to look for block-wise transfers options + * @param ctx Block context to be updated + * + * @return The offset in the block-wise transfer, 0 if the transfer + * has finished. + */ +size_t coap_next_block(const struct coap_packet *cpkt, + struct coap_block_context *ctx); + +/** + * @brief Returns the version present in a CoAP packet. + * + * @param cpkt CoAP packet representation + * + * @return the CoAP version in packet + */ +u8_t coap_header_get_version(const struct coap_packet *cpkt); + +/** + * @brief Returns the type of the CoAP packet. + * + * @param cpkt CoAP packet representation + * + * @return the type of the packet + */ +u8_t coap_header_get_type(const struct coap_packet *cpkt); + +/** + * @brief Returns the token (if any) in the CoAP packet. + * + * @param cpkt CoAP packet representation + * @param token Where to store the token + * + * @return Token length in the CoAP packet. + */ +u8_t coap_header_get_token(const struct coap_packet *cpkt, u8_t *token); + +/** + * @brief Returns the code of the CoAP packet. + * + * @param cpkt CoAP packet representation + * + * @return the code present in the packet + */ +u8_t coap_header_get_code(const struct coap_packet *cpkt); + +/** + * @brief Returns the message id associated with the CoAP packet. + * + * @param cpkt CoAP packet representation + * + * @return the message id present in the packet + */ +u16_t coap_header_get_id(const struct coap_packet *cpkt); + +/** + * @brief Helper to generate message ids + * + * @return a new message id + */ +static inline u16_t coap_next_id(void) +{ + static u16_t message_id; + + return ++message_id; +} + +/** + * @brief Returns the fragment pointer and offset where payload starts + * in the CoAP packet. + * + * @param cpkt CoAP packet representation + * @param offset Stores the offset value where payload starts + * @param len Total length of CoAP payload + * + * @return the net_buf fragment pointer and offset value if payload exists + * NULL pointer and offset set to 0 in case there is no payload + * NULL pointer and offset value 0xffff incase of an error + */ +struct net_buf *coap_packet_get_payload(const struct coap_packet *cpkt, + u16_t *offset, + u16_t *len); + + +/** + * @brief Returns a randomly generated array of 8 bytes, that can be + * used as a message's token. + * + * @return a 8-byte pseudo-random token. + */ +u8_t *coap_next_token(void); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __COAP_H__ */ diff --git a/include/net/coap_link_format.h b/include/net/coap_link_format.h new file mode 100644 index 0000000000000..f8440ff718214 --- /dev/null +++ b/include/net/coap_link_format.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief CoAP implementation for Zephyr. + */ + +#ifndef __LINK_FORMAT_H__ +#define __LINK_FORMAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup coap COAP Library + * @{ + */ +/** + * This resource should be added before all other resources that should be + * included in the responses of the .well-known/core resource. + */ +#define COAP_WELL_KNOWN_CORE_PATH \ + ((const char * const[]) { ".well-known", "core", NULL }) + +int coap_well_known_core_get(struct coap_resource *resource, + struct coap_packet *request, + struct coap_packet *response, + struct net_pkt *pkt); + +/** + * In case you want to add attributes to the resources included in the + * 'well-known/core' "virtual" resource, the 'user_data' field should point + * to a valid coap_core_metadata structure. + */ +struct coap_core_metadata { + const char * const *attributes; + void *user_data; +}; + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LINK_FORMAT_H__ */ diff --git a/include/net/http_parser.h b/include/net/http_parser.h index ea31fff128cba..95cb5b1b046d5 100644 --- a/include/net/http_parser.h +++ b/include/net/http_parser.h @@ -24,10 +24,12 @@ extern "C" { #endif +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 #define HTTP_PARSER_VERSION_MINOR 7 #define HTTP_PARSER_VERSION_PATCH 1 +#endif #include #if defined(_WIN32) && !defined(__MINGW32__) && \ @@ -47,6 +49,7 @@ typedef unsigned __int64 u64_t; #include #endif +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) /* Maximium header size allowed. If the macro is not defined * before including this header then the default is used. To * change the maximum header size, define the macro in the build @@ -227,6 +230,7 @@ struct http_parser_settings { http_cb on_chunk_header; http_cb on_chunk_complete; }; +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ enum http_parser_url_fields { @@ -261,6 +265,7 @@ struct http_parser_url { }; +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) /* Returns the library version. Bits 16-23 contain the major version number, * bits 8-15 the minor version number and bits 0-7 the patch level. * Usage example: @@ -305,6 +310,7 @@ const char *http_errno_name(enum http_errno err); /* Return a string description of the given error */ const char *http_errno_description(enum http_errno err); +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ /* Initialize all http_parser_url members to 0 */ void http_parser_url_init(struct http_parser_url *u); @@ -313,11 +319,13 @@ void http_parser_url_init(struct http_parser_url *u); int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u); +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) /* Pause or un-pause the parser; a nonzero value pauses */ void http_parser_pause(struct http_parser *parser, int paused); /* Checks if this is the final chunk of the body. */ int http_body_is_final(const struct http_parser *parser); +#endif #ifdef __cplusplus } diff --git a/include/net/lwm2m.h b/include/net/lwm2m.h index ae730e093dcbe..7044dfef4e990 100644 --- a/include/net/lwm2m.h +++ b/include/net/lwm2m.h @@ -7,8 +7,9 @@ #ifndef __LWM2M_H__ #define __LWM2M_H__ +#include #include -#include +#include /* LWM2M Objects defined by OMA */ @@ -26,6 +27,43 @@ #define IPSO_OBJECT_TEMP_SENSOR_ID 3303 #define IPSO_OBJECT_LIGHT_CONTROL_ID 3311 +struct lwm2m_ctx; +struct lwm2m_message; + +/* Establish a message timeout callback type */ +typedef void (*lwm2m_message_timeout_cb_t)(struct lwm2m_message *msg); + +/** + * Internal LwM2M message structure + * + * @details Structure to track in-flight messages. + * + * Users should not need to manipulate this structure directly. + * This definition is here so that an LwM2M context can have a + * pool of messages for the library to use. + */ +struct lwm2m_message { + /** LwM2M context related to this message */ + struct lwm2m_ctx *ctx; + + /** ZoAP packet data related to this message */ + struct coap_packet cpkt; + + /** Message configuration */ + u8_t *token; + coap_reply_t reply_cb; + lwm2m_message_timeout_cb_t message_timeout_cb; + u16_t mid; + u8_t type; + u8_t code; + u8_t tkl; + + /** ZoAP transmission handling structures */ + struct coap_pending *pending; + struct coap_reply *reply; + u8_t send_attempts; +}; + /** * LwM2M context structure * @@ -49,10 +87,27 @@ struct lwm2m_ctx { net_pkt_get_pool_func_t data_pool; #endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ - /** Private ZoAP and networking structures */ - struct zoap_pending pendings[CONFIG_LWM2M_ENGINE_MAX_PENDING]; - struct zoap_reply replies[CONFIG_LWM2M_ENGINE_MAX_REPLIES]; + /** Private CoAP and networking structures */ + struct coap_pending pendings[CONFIG_LWM2M_ENGINE_MAX_PENDING]; + struct coap_reply replies[CONFIG_LWM2M_ENGINE_MAX_REPLIES]; + struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES]; struct k_delayed_work retransmit_work; + +#if defined(CONFIG_NET_APP_DTLS) + /** Pre-Shared Key Information*/ + unsigned char *client_psk; + size_t client_psk_len; + char *client_psk_id; + size_t client_psk_id_len; + + /** DTLS support structures */ + char *cert_host; + u8_t *dtls_result_buf; + size_t dtls_result_buf_len; + struct k_mem_pool *dtls_pool; + k_thread_stack_t dtls_stack; + size_t dtls_stack_len; +#endif }; /* callback can return 1 if handled (don't update value) */ @@ -119,9 +174,15 @@ int lwm2m_device_add_err(u8_t error_code); #define RESULT_UPDATE_FAILED 8 #define RESULT_UNSUP_PROTO 9 +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT) void lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb); lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void); +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT) +void lwm2m_firmware_set_update_cb(lwm2m_engine_exec_cb_t cb); +lwm2m_engine_exec_cb_t lwm2m_firmware_get_update_cb(void); +#endif +#endif /* LWM2M Engine */ @@ -189,8 +250,26 @@ int lwm2m_engine_start(struct lwm2m_ctx *client_ctx, /* LWM2M RD Client */ +/* Client events */ +enum lwm2m_rd_client_event { + LWM2M_RD_CLIENT_EVENT_NONE, + LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_FAILURE, + LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_COMPLETE, + LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE, + LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE, + LWM2M_RD_CLIENT_EVENT_REG_UPDATE_FAILURE, + LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE, + LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE, + LWM2M_RD_CLIENT_EVENT_DISCONNECT +}; + +/* Event callback */ +typedef void (*lwm2m_ctx_event_cb_t)(struct lwm2m_ctx *ctx, + enum lwm2m_rd_client_event event); + int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, char *peer_str, u16_t peer_port, - const char *ep_name); + const char *ep_name, + lwm2m_ctx_event_cb_t event_cb); #endif /* __LWM2M_H__ */ diff --git a/include/net/zoap.h b/include/net/zoap.h index e3d3e98631c6b..840d38e2f6b45 100644 --- a/include/net/zoap.h +++ b/include/net/zoap.h @@ -30,6 +30,10 @@ extern "C" { * @{ */ +/** + * @deprecated This library is deprecated. + */ + /** * @brief Set of CoAP packet options we are aware of. * @@ -39,6 +43,10 @@ extern "C" { * * Refer to RFC 7252, section 12.2 for more information. */ + +/** + * @deprecated This enum is deprecated. + */ enum zoap_option_num { ZOAP_OPTION_IF_MATCH = 1, ZOAP_OPTION_URI_HOST = 3, @@ -67,6 +75,10 @@ enum zoap_option_num { * To be used with zoap_header_set_code() when creating a request * or a response. */ + +/** + * @deprecated This enum is deprecated. + */ enum zoap_method { ZOAP_METHOD_GET = 1, ZOAP_METHOD_POST = 2, @@ -74,11 +86,19 @@ enum zoap_method { ZOAP_METHOD_DELETE = 4, }; + +/** + * @deprecated This macro is deprecated. + */ + #define ZOAP_REQUEST_MASK 0x07 /** * @brief CoAP packets may be of one of these types. */ +/** + * @deprecated This enum is deprecated. + */ enum zoap_msgtype { /** * Confirmable message. @@ -109,6 +129,9 @@ enum zoap_msgtype { ZOAP_TYPE_RESET = 3 }; +/** + * @deprecated This macro is deprecated. + */ #define zoap_make_response_code(clas, det) ((clas << 5) | (det)) /** @@ -116,6 +139,9 @@ enum zoap_msgtype { * * To be used with zoap_header_set_code() when creating a response. */ +/** + * @deprecated This enum is deprecated. + */ enum zoap_response_code { ZOAP_RESPONSE_CODE_OK = zoap_make_response_code(2, 0), ZOAP_RESPONSE_CODE_CREATED = zoap_make_response_code(2, 1), @@ -143,6 +169,9 @@ enum zoap_response_code { ZOAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED = zoap_make_response_code(5, 5) }; +/** + * @deprecated This macro is deprecated. + */ #define ZOAP_CODE_EMPTY (0) struct zoap_observer; @@ -156,6 +185,9 @@ struct zoap_resource; * @brief Type of the callback being called when a resource's method is * invoked by the remote entity. */ +/** + * @deprecated This callback is deprecated. + */ typedef int (*zoap_method_t)(struct zoap_resource *resource, struct zoap_packet *request, const struct sockaddr *from); @@ -165,6 +197,9 @@ typedef int (*zoap_method_t)(struct zoap_resource *resource, * @brief Type of the callback being called when a resource's has observers * to be informed when an update happens. */ +/** + * @deprecated This callback is deprecated. + */ typedef void (*zoap_notify_t)(struct zoap_resource *resource, struct zoap_observer *observer); @@ -174,6 +209,9 @@ typedef void (*zoap_notify_t)(struct zoap_resource *resource, * CoAP servers often want to register resources, so that clients can act on * them, by fetching their state or requesting updates to them. */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_resource { /** Which function to be called for each CoAP method */ zoap_method_t get, post, put, del; @@ -187,6 +225,9 @@ struct zoap_resource { /** * @brief Represents a remote device that is observing a local resource. */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_observer { sys_snode_t list; struct sockaddr addr; @@ -197,6 +238,9 @@ struct zoap_observer { /** * @brief Representation of a CoAP packet. */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_packet { struct net_pkt *pkt; u8_t *start; /* Start of the payload */ @@ -208,6 +252,9 @@ struct zoap_packet { * @brief Helper function to be called when a response matches the * a pending request. */ +/** + * @deprecated This callback is deprecated. + */ typedef int (*zoap_reply_t)(const struct zoap_packet *response, struct zoap_reply *reply, const struct sockaddr *from); @@ -215,6 +262,9 @@ typedef int (*zoap_reply_t)(const struct zoap_packet *response, /** * @brief Represents a request awaiting for an acknowledgment (ACK). */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_pending { struct net_pkt *pkt; struct sockaddr addr; @@ -226,6 +276,9 @@ struct zoap_pending { * @brief Represents the handler for the reply of a request, it is * also used when observing resources. */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_reply { zoap_reply_t reply; void *user_data; @@ -243,6 +296,9 @@ struct zoap_reply { * @param request Request on which the observer will be based * @param addr Address of the remote device */ +/** + * @deprecated This api is deprecated. + */ void zoap_observer_init(struct zoap_observer *observer, const struct zoap_packet *request, const struct sockaddr *addr); @@ -256,6 +312,9 @@ void zoap_observer_init(struct zoap_observer *observer, * * @return true if this is the first observer added to this resource. */ +/** + * @deprecated This api is deprecated. + */ bool zoap_register_observer(struct zoap_resource *resource, struct zoap_observer *observer); @@ -266,6 +325,9 @@ bool zoap_register_observer(struct zoap_resource *resource, * @param resource Resource in which to remove the observer * @param observer Observer to be removed */ +/** + * @deprecated This api is deprecated. + */ void zoap_remove_observer(struct zoap_resource *resource, struct zoap_observer *observer); @@ -279,6 +341,9 @@ void zoap_remove_observer(struct zoap_resource *resource, * @return A pointer to a observer if a match is found, NULL * otherwise. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_observer *zoap_find_observer_by_addr( struct zoap_observer *observers, size_t len, const struct sockaddr *addr); @@ -292,6 +357,9 @@ struct zoap_observer *zoap_find_observer_by_addr( * @return A pointer to a observer if there's an available observer, * NULL otherwise. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_observer *zoap_observer_next_unused( struct zoap_observer *observers, size_t len); @@ -301,6 +369,9 @@ struct zoap_observer *zoap_observer_next_unused( * @param reply Reply structure to be initialized * @param request Request from which @a reply will be based */ +/** + * @deprecated This api is deprecated. + */ void zoap_reply_init(struct zoap_reply *reply, const struct zoap_packet *request); @@ -309,6 +380,9 @@ void zoap_reply_init(struct zoap_reply *reply, * * To be used with zoap_find_options(). */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_option { u8_t *value; u16_t len; @@ -324,6 +398,9 @@ struct zoap_option { * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_packet_parse(struct zoap_packet *zpkt, struct net_pkt *pkt); /** @@ -336,6 +413,9 @@ int zoap_packet_parse(struct zoap_packet *zpkt, struct net_pkt *pkt); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_packet_init(struct zoap_packet *zpkt, struct net_pkt *pkt); /** @@ -352,6 +432,9 @@ int zoap_packet_init(struct zoap_packet *zpkt, struct net_pkt *pkt); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_pending_init(struct zoap_pending *pending, const struct zoap_packet *request, const struct sockaddr *addr); @@ -366,6 +449,9 @@ int zoap_pending_init(struct zoap_pending *pending, * @return pointer to a free #zoap_pending structure, NULL in case * none could be found. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_pending *zoap_pending_next_unused( struct zoap_pending *pendings, size_t len); @@ -379,6 +465,9 @@ struct zoap_pending *zoap_pending_next_unused( * @return pointer to a free #zoap_reply structure, NULL in case * none could be found. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_reply *zoap_reply_next_unused( struct zoap_reply *replies, size_t len); @@ -393,6 +482,9 @@ struct zoap_reply *zoap_reply_next_unused( * @return pointer to the associated #zoap_pending structure, NULL in * case none could be found. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_pending *zoap_pending_received( const struct zoap_packet *response, struct zoap_pending *pendings, size_t len); @@ -409,6 +501,9 @@ struct zoap_pending *zoap_pending_received( * @return Pointer to the reply matching the packet received, NULL if * none could be found. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_reply *zoap_response_received( const struct zoap_packet *response, const struct sockaddr *from, @@ -424,6 +519,9 @@ struct zoap_reply *zoap_response_received( * @return The next #zoap_pending to expire, NULL if none is about to * expire. */ +/** + * @deprecated This api is deprecated. + */ struct zoap_pending *zoap_pending_next_to_expire( struct zoap_pending *pendings, size_t len); @@ -435,6 +533,9 @@ struct zoap_pending *zoap_pending_next_to_expire( * * @return false if this is the last retransmission. */ +/** + * @deprecated This api is deprecated. + */ bool zoap_pending_cycle(struct zoap_pending *pending); /** @@ -443,6 +544,9 @@ bool zoap_pending_cycle(struct zoap_pending *pending); * * @param pending Pending representation to be canceled */ +/** + * @deprecated This api is deprecated. + */ void zoap_pending_clear(struct zoap_pending *pending); /** @@ -451,6 +555,9 @@ void zoap_pending_clear(struct zoap_pending *pending); * * @param reply The reply to be canceled */ +/** + * @deprecated This api is deprecated. + */ void zoap_reply_clear(struct zoap_reply *reply); /** @@ -463,6 +570,9 @@ void zoap_reply_clear(struct zoap_reply *reply); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_handle_request(struct zoap_packet *zpkt, struct zoap_resource *resources, const struct sockaddr *from); @@ -475,6 +585,9 @@ int zoap_handle_request(struct zoap_packet *zpkt, * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_resource_notify(struct zoap_resource *resource); /** @@ -485,6 +598,9 @@ int zoap_resource_notify(struct zoap_resource *resource); * @return True if the request is enabling observing a resource, False * otherwise */ +/** + * @deprecated This api is deprecated. + */ bool zoap_request_is_observe(const struct zoap_packet *request); /** @@ -498,6 +614,9 @@ bool zoap_request_is_observe(const struct zoap_packet *request); * * @return pointer to the start of the payload, NULL in case of error. */ +/** + * @deprecated This api is deprecated. + */ u8_t *zoap_packet_get_payload(struct zoap_packet *zpkt, u16_t *len); /** @@ -512,6 +631,9 @@ u8_t *zoap_packet_get_payload(struct zoap_packet *zpkt, u16_t *len); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_packet_set_used(struct zoap_packet *zpkt, u16_t len); /** @@ -526,6 +648,9 @@ int zoap_packet_set_used(struct zoap_packet *zpkt, u16_t len); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_add_option(struct zoap_packet *zpkt, u16_t code, const void *value, u16_t len); @@ -540,6 +665,9 @@ int zoap_add_option(struct zoap_packet *zpkt, u16_t code, * * @return The integer representation of the option */ +/** + * @deprecated This api is deprecated. + */ unsigned int zoap_option_value_to_int(const struct zoap_option *option); /** @@ -554,6 +682,9 @@ unsigned int zoap_option_value_to_int(const struct zoap_option *option); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_add_option_int(struct zoap_packet *zpkt, u16_t code, unsigned int val); @@ -570,6 +701,9 @@ int zoap_add_option_int(struct zoap_packet *zpkt, u16_t code, * @return The number of options found in packet matching code, * negative on error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_find_options(const struct zoap_packet *zpkt, u16_t code, struct zoap_option *options, u16_t veclen); @@ -581,6 +715,9 @@ int zoap_find_options(const struct zoap_packet *zpkt, u16_t code, * * https://tools.ietf.org/html/rfc7959 */ +/** + * @deprecated This enum is deprecated. + */ enum zoap_block_size { ZOAP_BLOCK_16, ZOAP_BLOCK_32, @@ -599,6 +736,9 @@ enum zoap_block_size { * * @return The size in bytes that the block_size represents */ +/** + * @deprecated This api is deprecated. + */ static inline u16_t zoap_block_size_to_bytes( enum zoap_block_size block_size) { @@ -608,6 +748,9 @@ static inline u16_t zoap_block_size_to_bytes( /** * @brief Represents the current state of a block-wise transaction. */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_block_context { size_t total_size; size_t current; @@ -623,6 +766,9 @@ struct zoap_block_context { * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_block_transfer_init(struct zoap_block_context *ctx, enum zoap_block_size block_size, size_t total_size); @@ -636,6 +782,9 @@ int zoap_block_transfer_init(struct zoap_block_context *ctx, * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_add_block1_option(struct zoap_packet *zpkt, struct zoap_block_context *ctx); @@ -648,6 +797,9 @@ int zoap_add_block1_option(struct zoap_packet *zpkt, * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_add_block2_option(struct zoap_packet *zpkt, struct zoap_block_context *ctx); @@ -660,6 +812,9 @@ int zoap_add_block2_option(struct zoap_packet *zpkt, * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_add_size1_option(struct zoap_packet *zpkt, struct zoap_block_context *ctx); @@ -672,6 +827,9 @@ int zoap_add_size1_option(struct zoap_packet *zpkt, * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_add_size2_option(struct zoap_packet *zpkt, struct zoap_block_context *ctx); @@ -684,6 +842,9 @@ int zoap_add_size2_option(struct zoap_packet *zpkt, * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_update_from_block(const struct zoap_packet *zpkt, struct zoap_block_context *ctx); @@ -698,6 +859,9 @@ int zoap_update_from_block(const struct zoap_packet *zpkt, * @return The offset in the block-wise transfer, 0 if the transfer * has finished. */ +/** + * @deprecated This api is deprecated. + */ size_t zoap_next_block(const struct zoap_packet *zpkt, struct zoap_block_context *ctx); @@ -708,6 +872,9 @@ size_t zoap_next_block(const struct zoap_packet *zpkt, * * @return the CoAP version in packet */ +/** + * @deprecated This api is deprecated. + */ u8_t zoap_header_get_version(const struct zoap_packet *zpkt); /** @@ -717,6 +884,9 @@ u8_t zoap_header_get_version(const struct zoap_packet *zpkt); * * @return the type of the packet */ +/** + * @deprecated This api is deprecated. + */ u8_t zoap_header_get_type(const struct zoap_packet *zpkt); /** @@ -727,6 +897,9 @@ u8_t zoap_header_get_type(const struct zoap_packet *zpkt); * * @return pointer to the start of the token in the CoAP packet. */ +/** + * @deprecated This api is deprecated. + */ const u8_t *zoap_header_get_token(const struct zoap_packet *zpkt, u8_t *len); @@ -737,6 +910,9 @@ const u8_t *zoap_header_get_token(const struct zoap_packet *zpkt, * * @return the code present in the packet */ +/** + * @deprecated This api is deprecated. + */ u8_t zoap_header_get_code(const struct zoap_packet *zpkt); /** @@ -746,6 +922,9 @@ u8_t zoap_header_get_code(const struct zoap_packet *zpkt); * * @return the message id present in the packet */ +/** + * @deprecated This api is deprecated. + */ u16_t zoap_header_get_id(const struct zoap_packet *zpkt); /** @@ -754,6 +933,9 @@ u16_t zoap_header_get_id(const struct zoap_packet *zpkt); * @param zpkt CoAP packet representation * @param ver The CoAP version to set in the packet */ +/** + * @deprecated This api is deprecated. + */ void zoap_header_set_version(struct zoap_packet *zpkt, u8_t ver); /** @@ -762,6 +944,9 @@ void zoap_header_set_version(struct zoap_packet *zpkt, u8_t ver); * @param zpkt CoAP packet representation * @param type The packet type to set */ +/** + * @deprecated This api is deprecated. + */ void zoap_header_set_type(struct zoap_packet *zpkt, u8_t type); /** @@ -773,6 +958,9 @@ void zoap_header_set_type(struct zoap_packet *zpkt, u8_t type); * * @return 0 in case of success or negative in case of error. */ +/** + * @deprecated This api is deprecated. + */ int zoap_header_set_token(struct zoap_packet *zpkt, const u8_t *token, u8_t tokenlen); @@ -782,6 +970,9 @@ int zoap_header_set_token(struct zoap_packet *zpkt, const u8_t *token, * @param zpkt CoAP packet representation * @param code The code set in the packet */ +/** + * @deprecated This api is deprecated. + */ void zoap_header_set_code(struct zoap_packet *zpkt, u8_t code); /** @@ -790,6 +981,9 @@ void zoap_header_set_code(struct zoap_packet *zpkt, u8_t code); * @param zpkt CoAP packet representation * @param id The message id to set in the packet */ +/** + * @deprecated This api is deprecated. + */ void zoap_header_set_id(struct zoap_packet *zpkt, u16_t id); /** @@ -797,6 +991,9 @@ void zoap_header_set_id(struct zoap_packet *zpkt, u16_t id); * * @return a new message id */ +/** + * @deprecated This api is deprecated. + */ static inline u16_t zoap_next_id(void) { static u16_t message_id; @@ -810,6 +1007,9 @@ static inline u16_t zoap_next_id(void) * * @return a 8-byte pseudo-random token. */ +/** + * @deprecated This api is deprecated. + */ u8_t *zoap_next_token(void); /** diff --git a/include/net/zoap_link_format.h b/include/net/zoap_link_format.h index ce4ec577ab776..7970fe459da29 100644 --- a/include/net/zoap_link_format.h +++ b/include/net/zoap_link_format.h @@ -22,9 +22,15 @@ extern "C" { * @{ */ +/** + * @deprecated This macro is deprecated. + */ #define _ZOAP_WELL_KNOWN_CORE_PATH \ ((const char * const[]) { ".well-known", "core", NULL }) +/** + * @deprecated This api is deprecated. + */ int _zoap_well_known_core_get(struct zoap_resource *resource, struct zoap_packet *request, const struct sockaddr *from); @@ -33,6 +39,9 @@ int _zoap_well_known_core_get(struct zoap_resource *resource, * This resource should be added before all other resources that should be * included in the responses of the .well-known/core resource. */ +/** + * @deprecated This macro is deprecated. + */ #define ZOAP_WELL_KNOWN_CORE_RESOURCE \ { .get = _zoap_well_known_core_get, \ .path = _ZOAP_WELL_KNOWN_CORE_PATH, \ @@ -43,6 +52,9 @@ int _zoap_well_known_core_get(struct zoap_resource *resource, * 'well-known/core' "virtual" resource, the 'user_data' field should point * to a valid zoap_core_metadata structure. */ +/** + * @deprecated This struct is deprecated. + */ struct zoap_core_metadata { const char * const *attributes; void *user_data; diff --git a/samples/net/zoap_client/Makefile b/samples/net/coap_client/Makefile similarity index 100% rename from samples/net/zoap_client/Makefile rename to samples/net/coap_client/Makefile diff --git a/samples/net/zoap_client/README.rst b/samples/net/coap_client/README.rst similarity index 98% rename from samples/net/zoap_client/README.rst rename to samples/net/coap_client/README.rst index 081301f84f177..04f44113c10ab 100644 --- a/samples/net/zoap_client/README.rst +++ b/samples/net/coap_client/README.rst @@ -1,4 +1,4 @@ -.. _zoap-client-sample: +.. _coap-client-sample: CoAP client ########### diff --git a/samples/net/zoap_client/prj.conf b/samples/net/coap_client/prj.conf similarity index 93% rename from samples/net/zoap_client/prj.conf rename to samples/net/coap_client/prj.conf index 614dbe3886620..363603a8e12e6 100644 --- a/samples/net/zoap_client/prj.conf +++ b/samples/net/coap_client/prj.conf @@ -7,4 +7,4 @@ CONFIG_SYS_LOG=y CONFIG_SYS_LOG_SHOW_COLOR=y CONFIG_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_ZOAP=y +CONFIG_COAP=y diff --git a/samples/net/zoap_client/prj_bt.conf b/samples/net/coap_client/prj_bt.conf similarity index 97% rename from samples/net/zoap_client/prj_bt.conf rename to samples/net/coap_client/prj_bt.conf index 5911d18a58084..ac7f224f1c8ef 100644 --- a/samples/net/zoap_client/prj_bt.conf +++ b/samples/net/coap_client/prj_bt.conf @@ -6,7 +6,7 @@ CONFIG_SYS_LOG=y CONFIG_SYS_LOG_SHOW_COLOR=y CONFIG_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_NET_L2_BT=y CONFIG_NET_L2_BT_ZEP1656=y CONFIG_NET_DEBUG_L2_BT=y diff --git a/samples/net/zoap_client/sample.yaml b/samples/net/coap_client/sample.yaml similarity index 100% rename from samples/net/zoap_client/sample.yaml rename to samples/net/coap_client/sample.yaml diff --git a/samples/net/zoap_client/src/Makefile b/samples/net/coap_client/src/Makefile similarity index 84% rename from samples/net/zoap_client/src/Makefile rename to samples/net/coap_client/src/Makefile index d799baf118d18..e631cb66382f9 100644 --- a/samples/net/zoap_client/src/Makefile +++ b/samples/net/coap_client/src/Makefile @@ -3,4 +3,4 @@ ccflags-y +=-I${ZEPHYR_BASE}/samples/net/common/ ccflags-y +=-DNET_TESTING_SERVER=1 endif -obj-y = zoap-client.o +obj-y = coap-client.o diff --git a/samples/net/zoap_client/src/zoap-client.c b/samples/net/coap_client/src/coap-client.c similarity index 71% rename from samples/net/zoap_client/src/zoap-client.c rename to samples/net/coap_client/src/coap-client.c index 585d373b77219..9c256c0a2a906 100644 --- a/samples/net/zoap_client/src/zoap-client.c +++ b/samples/net/coap_client/src/coap-client.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #define MY_COAP_PORT 5683 @@ -32,8 +32,8 @@ static const struct sockaddr_in6 mcast_addr = { static struct net_context *context; -struct zoap_pending pendings[NUM_PENDINGS]; -struct zoap_reply replies[NUM_REPLIES]; +struct coap_pending pendings[NUM_PENDINGS]; +struct coap_reply replies[NUM_REPLIES]; struct k_delayed_work retransmit_work; #if defined(CONFIG_NET_MGMT_EVENT) @@ -52,8 +52,8 @@ static void msg_dump(const char *s, u8_t *data, unsigned len) printk("(%u bytes)\n", len); } -static int resource_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static int resource_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { struct net_pkt *pkt = response->pkt; @@ -63,48 +63,46 @@ static int resource_reply_cb(const struct zoap_packet *response, return 0; } +static void get_from_ip_addr(struct coap_packet *cpkt, + struct sockaddr_in6 *from) +{ + struct net_udp_hdr hdr, *udp_hdr; + + udp_hdr = net_udp_get_hdr(cpkt->pkt, &hdr); + if (!udp_hdr) { + return; + } + + net_ipaddr_copy(&from->sin6_addr, &NET_IPV6_HDR(cpkt->pkt)->src); + from->sin6_port = udp_hdr->src_port; + from->sin6_family = AF_INET6; +} + static void udp_receive(struct net_context *context, struct net_pkt *pkt, int status, void *user_data) { - struct zoap_pending *pending; - struct zoap_reply *reply; - struct zoap_packet response; + struct coap_pending *pending; + struct coap_reply *reply; + struct coap_packet response; struct sockaddr_in6 from; - struct net_udp_hdr hdr, *udp_hdr; - int header_len, r; - - /* - * zoap expects that buffer->data starts at the - * beginning of the CoAP header - */ - header_len = net_pkt_appdata(pkt) - pkt->frags->data; - net_buf_pull(pkt->frags, header_len); + int r; - r = zoap_packet_parse(&response, pkt); + r = coap_packet_parse(&response, pkt, NULL, 0); if (r < 0) { printk("Invalid data received (%d)\n", r); return; } - pending = zoap_pending_received(&response, pendings, + pending = coap_pending_received(&response, pendings, NUM_PENDINGS); if (pending) { /* If necessary cancel retransmissions */ } - net_ipaddr_copy(&from.sin6_addr, &NET_IPV6_HDR(pkt)->src); - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - if (!udp_hdr) { - printk("Invalid UDP data received\n"); - return; - } - - from.sin6_port = udp_hdr->src_port; - - reply = zoap_response_received(&response, + get_from_ip_addr(&response, &from); + reply = coap_response_received(&response, (const struct sockaddr *) &from, replies, NUM_REPLIES); if (!reply) { @@ -115,10 +113,10 @@ static void udp_receive(struct net_context *context, static void retransmit_request(struct k_work *work) { - struct zoap_pending *pending; + struct coap_pending *pending; int r; - pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS); + pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); if (!pending) { return; } @@ -129,8 +127,8 @@ static void retransmit_request(struct k_work *work) return; } - if (!zoap_pending_cycle(pending)) { - zoap_pending_clear(pending); + if (!coap_pending_cycle(pending)) { + coap_pending_clear(pending); return; } @@ -142,9 +140,9 @@ static void event_iface_up(struct net_mgmt_event_callback *cb, { static struct sockaddr_in6 any_addr = { .sin6_addr = IN6ADDR_ANY_INIT, .sin6_family = AF_INET6 }; - struct zoap_packet request; - struct zoap_pending *pending; - struct zoap_reply *reply; + struct coap_packet request; + struct coap_pending *pending; + struct coap_reply *reply; const char * const *p; struct net_pkt *pkt; struct net_buf *frag; @@ -186,56 +184,51 @@ static void event_iface_up(struct net_mgmt_event_callback *cb, net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&request, pkt); + r = coap_packet_init(&request, pkt, 1, COAP_TYPE_CON, + 8, coap_next_token(), + COAP_METHOD_GET, coap_next_id()); if (r < 0) { return; } - /* FIXME: Could be that zoap_packet_init() sets some defaults */ - zoap_header_set_version(&request, 1); - zoap_header_set_type(&request, ZOAP_TYPE_CON); - zoap_header_set_code(&request, ZOAP_METHOD_GET); - zoap_header_set_id(&request, zoap_next_id()); - zoap_header_set_token(&request, zoap_next_token(), 8); - /* Enable observing the resource. */ - r = zoap_add_option(&request, ZOAP_OPTION_OBSERVE, - &observe, sizeof(observe)); + r = coap_packet_append_option(&request, COAP_OPTION_OBSERVE, + &observe, sizeof(observe)); if (r < 0) { printk("Unable add option to request.\n"); return; } for (p = test_path; p && *p; p++) { - r = zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - *p, strlen(*p)); + r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, + *p, strlen(*p)); if (r < 0) { printk("Unable add option to request.\n"); return; } } - pending = zoap_pending_next_unused(pendings, NUM_PENDINGS); + pending = coap_pending_next_unused(pendings, NUM_PENDINGS); if (!pending) { printk("Unable to find a free pending to track " "retransmissions.\n"); return; } - r = zoap_pending_init(pending, &request, + r = coap_pending_init(pending, &request, (struct sockaddr *) &mcast_addr); if (r < 0) { printk("Unable to initialize a pending retransmission.\n"); return; } - reply = zoap_reply_next_unused(replies, NUM_REPLIES); + reply = coap_reply_next_unused(replies, NUM_REPLIES); if (!reply) { printk("No resources for waiting for replies.\n"); return; } - zoap_reply_init(reply, &request); + coap_reply_init(reply, &request); reply->reply = resource_reply_cb; r = net_context_sendto(pkt, (struct sockaddr *) &mcast_addr, @@ -246,7 +239,7 @@ static void event_iface_up(struct net_mgmt_event_callback *cb, return; } - zoap_pending_cycle(pending); + coap_pending_cycle(pending); k_delayed_work_submit(&retransmit_work, pending->timeout); diff --git a/samples/net/zoap_server/Makefile b/samples/net/coap_server/Makefile similarity index 100% rename from samples/net/zoap_server/Makefile rename to samples/net/coap_server/Makefile diff --git a/samples/net/zoap_server/README.rst b/samples/net/coap_server/README.rst similarity index 96% rename from samples/net/zoap_server/README.rst rename to samples/net/coap_server/README.rst index 556484f324966..46fc3ebc7d1f6 100644 --- a/samples/net/zoap_server/README.rst +++ b/samples/net/coap_server/README.rst @@ -1,4 +1,4 @@ -.. _zoap-server-sample: +.. _coap-server-sample: CoAP Server ########### @@ -27,7 +27,7 @@ The sample exports the following resources: /large-update These resources allow a good part of the ETSI test cases to be run -against zoap-server. +against coap-server. Building And Running ******************** diff --git a/samples/net/zoap_server/prj.conf b/samples/net/coap_server/prj.conf similarity index 95% rename from samples/net/zoap_server/prj.conf rename to samples/net/coap_server/prj.conf index e073b4a7c26dc..d9c6f81285191 100644 --- a/samples/net/zoap_server/prj.conf +++ b/samples/net/coap_server/prj.conf @@ -6,7 +6,7 @@ CONFIG_SYS_LOG=y CONFIG_SYS_LOG_SHOW_COLOR=y CONFIG_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_NET_APP_SETTINGS=y CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5 diff --git a/samples/net/zoap_server/prj_bt.conf b/samples/net/coap_server/prj_bt.conf similarity index 96% rename from samples/net/zoap_server/prj_bt.conf rename to samples/net/coap_server/prj_bt.conf index 00817826a1b5a..9aaf4e8819e57 100644 --- a/samples/net/zoap_server/prj_bt.conf +++ b/samples/net/coap_server/prj_bt.conf @@ -17,4 +17,4 @@ CONFIG_SYS_LOG=y CONFIG_SYS_LOG_SHOW_COLOR=y CONFIG_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_ZOAP=y +CONFIG_COAP=y diff --git a/samples/net/zoap_server/prj_cc2520.conf b/samples/net/coap_server/prj_cc2520.conf similarity index 96% rename from samples/net/zoap_server/prj_cc2520.conf rename to samples/net/coap_server/prj_cc2520.conf index e63cd48e3d22d..669621bc0fe6c 100644 --- a/samples/net/zoap_server/prj_cc2520.conf +++ b/samples/net/coap_server/prj_cc2520.conf @@ -7,7 +7,7 @@ CONFIG_NET_LOG=y CONFIG_NET_UDP=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_IEEE802154_CC2520=y -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_NET_APP_SETTINGS=y CONFIG_NET_APP_IEEE802154_CHANNEL=20 CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" diff --git a/samples/net/zoap_server/sample.yaml b/samples/net/coap_server/sample.yaml similarity index 100% rename from samples/net/zoap_server/sample.yaml rename to samples/net/coap_server/sample.yaml diff --git a/samples/net/zoap_server/src/Makefile b/samples/net/coap_server/src/Makefile similarity index 92% rename from samples/net/zoap_server/src/Makefile rename to samples/net/coap_server/src/Makefile index 2b65cdce545c3..abbe66864e969 100644 --- a/samples/net/zoap_server/src/Makefile +++ b/samples/net/coap_server/src/Makefile @@ -3,7 +3,7 @@ ccflags-y +=-I${ZEPHYR_BASE}/samples/net/common/ ccflags-y +=-DNET_TESTING_SERVER=1 endif -obj-y = zoap-server.o +obj-y = coap-server.o ifeq ($(CONFIG_NET_L2_IEEE802154), y) ccflags-y +=-I${ZEPHYR_BASE}/samples/net/common/ diff --git a/samples/net/coap_server/src/coap-server.c b/samples/net/coap_server/src/coap-server.c new file mode 100644 index 0000000000000..4d092f99f08f3 --- /dev/null +++ b/samples/net/coap_server/src/coap-server.c @@ -0,0 +1,1310 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if 1 +#define SYS_LOG_DOMAIN "coap-server" +#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 u8_t plain_text_format; + +static struct coap_observer observers[NUM_OBSERVERS]; + +static struct coap_pending pendings[NUM_PENDINGS]; + +static struct net_context *context; + +static struct k_delayed_work observer_work; + +static int obs_counter; + +static struct coap_resource *resource_to_notify; + +struct k_delayed_work retransmit_work; + +static void get_from_ip_addr(struct coap_packet *cpkt, + struct sockaddr_in6 *from) +{ + struct net_udp_hdr hdr, *udp_hdr; + + udp_hdr = net_udp_get_hdr(cpkt->pkt, &hdr); + if (!udp_hdr) { + return; + } + + net_ipaddr_copy(&from->sin6_addr, &NET_IPV6_HDR(cpkt->pkt)->src); + from->sin6_port = udp_hdr->src_port; + from->sin6_family = AF_INET6; +} + +static int well_known_core_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct coap_packet response; + struct sockaddr_in6 from; + struct net_pkt *pkt; + struct net_buf *frag; + int r; + + NET_DBG(""); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_well_known_core_get(resource, request, &response, pkt); + if (r < 0) { + net_pkt_unref(response.pkt); + return r; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(response.pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(response.pkt); + } + + return r; +} + +static int test_del(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t tkl, code, type; + u8_t token[8]; + u16_t id; + int r; + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + if (type == COAP_TYPE_CON) { + type = COAP_TYPE_ACK; + } else { + type = COAP_TYPE_NON_CON; + } + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_DELETED, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int test_put(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t code, type, tkl; + u8_t token[8]; + u16_t id; + int r; + + /* TODO: Check for payload, empty payload is an error case. */ + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + if (type == COAP_TYPE_CON) { + type = COAP_TYPE_ACK; + } else { + type = COAP_TYPE_NON_CON; + } + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CHANGED, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int test_post(struct coap_resource *resource, + struct coap_packet *request) +{ + static const char * const location_path[] = { "location1", + "location2", + "location3", + NULL }; + const char * const *p; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t code, type, tkl; + u8_t token[8]; + u16_t id; + int r; + + /* TODO: Check for payload, empty payload is an error case. */ + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + if (type == COAP_TYPE_CON) { + type = COAP_TYPE_ACK; + } else { + type = COAP_TYPE_NON_CON; + } + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CREATED, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + for (p = location_path; *p; p++) { + r = coap_packet_append_option(&response, + COAP_OPTION_LOCATION_PATH, + *p, strlen(*p)); + if (r < 0) { + return -EINVAL; + } + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int location_query_post(struct coap_resource *resource, + struct coap_packet *request) +{ + static const char *const location_query[] = { "first=1", + "second=2", + NULL }; + const char * const *p; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t code, type, tkl; + u8_t token[8]; + u16_t id; + int r; + + /* TODO: Check for payload, empty payload is an error case. */ + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + if (type == COAP_TYPE_CON) { + type = COAP_TYPE_ACK; + } else { + type = COAP_TYPE_NON_CON; + } + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CREATED, id); + if (r < 0) { + return -EINVAL; + } + + for (p = location_query; *p; p++) { + r = coap_packet_append_option(&response, + COAP_OPTION_LOCATION_QUERY, + *p, strlen(*p)); + if (r < 0) { + return -EINVAL; + } + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int piggyback_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t token[8]; + u8_t payload[40], code, type; + u16_t id; + u8_t tkl; + int r; + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + if (type == COAP_TYPE_CON) { + type = COAP_TYPE_ACK; + } else { + type = COAP_TYPE_NON_CON; + } + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload_marker(&response); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + /* The response that coap-client expects */ + r = snprintk((char *) payload, sizeof(payload), + "Type: %u\nCode: %u\nMID: %u\n", type, code, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)payload, + strlen(payload)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int query_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct coap_option options[4]; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t payload[40], code, type, tkl; + u8_t token[8]; + u16_t id; + int i, r; + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + r = coap_find_options(request, COAP_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"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, (u8_t *) token, + COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload_marker(&response); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + /* The response that coap-client expects */ + r = snprintk((char *) payload, sizeof(payload), + "Type: %u\nCode: %u\nMID: %u\n", type, code, id); + if (r < 0) { + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)payload, + strlen(payload)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int separate_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + struct coap_pending *pending; + u8_t payload[40], code, type, tkl; + u8_t token[8]; + u16_t id; + int r; + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + if (type == COAP_TYPE_NON_CON) { + goto done; + } + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, (u8_t *)token, 0, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + +done: + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + if (type == COAP_TYPE_CON) { + type = COAP_TYPE_CON; + } else { + type = COAP_TYPE_NON_CON; + } + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload_marker(&response); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + /* The response that coap-client expects */ + r = snprintk((char *) payload, sizeof(payload), + "Type: %u\nCode: %u\nMID: %u\n", type, code, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)payload, + strlen(payload)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + if (type == COAP_TYPE_CON) { + pending = coap_pending_next_unused(pendings, NUM_PENDINGS); + if (!pending) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_pending_init(pending, &response, + (const struct sockaddr *)&from); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + coap_pending_cycle(pending); + pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); + + k_delayed_work_submit(&retransmit_work, pending->timeout); + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int large_get(struct coap_resource *resource, + struct coap_packet *request) +{ + static struct coap_block_context ctx; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t token[8], code, type; + u8_t payload[64]; + u16_t size; + u16_t id; + u8_t tkl; + int r; + + if (ctx.total_size == 0) { + coap_block_transfer_init(&ctx, COAP_BLOCK_64, + BLOCK_WISE_TRANSFER_SIZE_GET); + } + + r = coap_update_from_block(request, &ctx); + if (r < 0) { + return -EINVAL; + } + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, (u8_t *) token, + COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + return -EINVAL; + } + + r = coap_append_block2_option(&response, &ctx); + if (r < 0) { + return -EINVAL; + } + + r = coap_packet_append_payload_marker(&response); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + size = min(coap_block_size_to_bytes(ctx.block_size), + ctx.total_size - ctx.current); + + memset(payload, 'A', size); + + r = coap_packet_append_payload(&response, (u8_t *)payload, size); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_next_block(&response, &ctx); + if (!r) { + /* Will return 0 when it's the last block. */ + memset(&ctx, 0, sizeof(ctx)); + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int large_update_put(struct coap_resource *resource, + struct coap_packet *request) +{ + static struct coap_block_context ctx; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t token[8]; + u16_t offset; + u16_t id; + u8_t payload; + u8_t code; + u8_t type; + u8_t tkl; + int r; + + frag = net_frag_skip(request->frag, request->offset, &offset, + request->hdr_len + request->opt_len); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + + frag = net_frag_read_u8(frag, offset, &offset, &payload); + if (frag && offset != 0xffff) { + NET_ERR("packet has payload, wrong"); + return -EINVAL; + } + + if (ctx.total_size == 0) { + coap_block_transfer_init(&ctx, COAP_BLOCK_64, 0); + } + + r = coap_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, coap_block_size_to_bytes(ctx.block_size), + ctx.total_size); + NET_INFO("**************\n"); + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + /* Do something with the payload */ + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, (u8_t *) token, + COAP_RESPONSE_CODE_CHANGED, id); + if (r < 0) { + return -EINVAL; + } + + r = coap_append_block2_option(&response, &ctx); + if (r < 0) { + NET_ERR("Could not add Block2 option to response"); + return -EINVAL; + } + + r = coap_append_block1_option(&response, &ctx); + if (r < 0) { + NET_ERR("Could not add Block1 option to response"); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static int large_create_post(struct coap_resource *resource, + struct coap_packet *request) +{ + static struct coap_block_context ctx; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t token[8]; + u8_t code, type; + u16_t id; + u8_t tkl; + int r; + + if (ctx.total_size == 0) { + coap_block_transfer_init(&ctx, COAP_BLOCK_32, 0); + } + + r = coap_update_from_block(request, &ctx); + if (r < 0) { + return -EINVAL; + } + + /* TODO: Check for payload, empty payload is an error case. */ + + get_from_ip_addr(request, &from); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CONTINUE, id); + if (r < 0) { + return -EINVAL; + } + + r = coap_append_block2_option(&response, &ctx); + if (r < 0) { + NET_ERR("Could not add Block2 option to response"); + return -EINVAL; + } + + r = coap_append_block1_option(&response, &ctx); + if (r < 0) { + NET_ERR("Could not add Block1 option to response"); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); +} + +static void update_counter(struct k_work *work) +{ + obs_counter++; + + if (resource_to_notify) { + coap_resource_notify(resource_to_notify); + } + + k_delayed_work_submit(&observer_work, 5 * MSEC_PER_SEC); +} + +static int send_notification_packet(const struct sockaddr *addr, u16_t age, + socklen_t addrlen, u16_t id, + const u8_t *token, u8_t tkl, + bool is_response) +{ + struct coap_packet response; + struct coap_pending *pending; + struct net_pkt *pkt; + struct net_buf *frag; + char payload[14]; + u8_t type = COAP_TYPE_CON; + int r; + + if (is_response) { + type = COAP_TYPE_ACK; + } + + if (!is_response) { + id = coap_next_id(); + } + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, type, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + if (age >= 2) { + coap_append_option_int(&response, COAP_OPTION_OBSERVE, age); + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload_marker(&response); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + /* The response that coap-client expects */ + r = snprintk((char *) payload, sizeof(payload), + "Counter: %d\n", obs_counter); + if (r < 0) { + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)payload, + strlen(payload)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + if (type == COAP_TYPE_CON) { + pending = coap_pending_next_unused(pendings, NUM_PENDINGS); + if (!pending) { + return -EINVAL; + } + + r = coap_pending_init(pending, &response, addr); + if (r) { + return -EINVAL; + } + + coap_pending_cycle(pending); + pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); + + k_delayed_work_submit(&retransmit_work, pending->timeout); + } + + return net_context_sendto(pkt, addr, addrlen, NULL, 0, NULL, NULL); +} + +static int obs_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct coap_observer *observer; + struct sockaddr_in6 from; + u8_t token[8]; + u8_t code, type; + u16_t id; + u8_t tkl; + bool observe = true; + + get_from_ip_addr(request, &from); + + if (!coap_request_is_observe(request)) { + observe = false; + goto done; + } + + observer = coap_observer_next_unused(observers, NUM_OBSERVERS); + if (!observer) { + return -ENOMEM; + } + + coap_observer_init(observer, request, + (const struct sockaddr *)&from); + + coap_register_observer(resource, observer); + + resource_to_notify = resource; + +done: + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + NET_INFO("*******\n"); + NET_INFO("type: %u code %u id %u\n", type, code, id); + NET_INFO("*******\n"); + + return send_notification_packet((const struct sockaddr *)&from, + observe ? resource->age : 0, + sizeof(struct sockaddr_in6), id, + token, tkl, true); +} + +static void obs_notify(struct coap_resource *resource, + struct coap_observer *observer) +{ + send_notification_packet(&observer->addr, resource->age, + sizeof(observer->addr), 0, + observer->token, observer->tkl, false); +} + +static int core_get(struct coap_resource *resource, + struct coap_packet *request) +{ + static const char dummy_str[] = "Just a test\n"; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + u8_t tkl; + u8_t token[8]; + u16_t id; + int r; + + get_from_ip_addr(request, &from); + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, (u8_t *)token, + COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + return -EINVAL; + } + + r = coap_packet_append_payload_marker(&response); + if (r) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)dummy_str, + sizeof(dummy_str)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + return net_context_sendto(pkt, (const struct sockaddr *)&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 coap_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, + }, + { .get = well_known_core_get, + .path = COAP_WELL_KNOWN_CORE_PATH, + }, + { .get = well_known_core_get, + .path = COAP_WELL_KNOWN_CORE_PATH, + }, + { .get = core_get, + .path = core_1_path, + .user_data = &((struct coap_core_metadata) { + .attributes = core_1_attributes, + }), + }, + { .get = core_get, + .path = core_2_path, + .user_data = &((struct coap_core_metadata) { + .attributes = core_2_attributes, + }), + }, + { }, +}; + +static struct coap_resource *find_resouce_by_observer( + struct coap_resource *resources, struct coap_observer *o) +{ + struct coap_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_pkt *pkt, + int status, + void *user_data) +{ + struct coap_packet request; + struct coap_pending *pending; + struct sockaddr_in6 from; + struct coap_option options[16] = { 0 }; + u8_t opt_num = 16; + int r; + + r = coap_packet_parse(&request, pkt, options, opt_num); + if (r < 0) { + NET_ERR("Invalid data received (%d)\n", r); + net_pkt_unref(pkt); + return; + } + + get_from_ip_addr(&request, &from); + pending = coap_pending_received(&request, pendings, + NUM_PENDINGS); + if (pending) { + net_pkt_unref(pkt); + return; + } + + if (coap_header_get_type(&request) == COAP_TYPE_RESET) { + struct coap_resource *r; + struct coap_observer *o; + + o = coap_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; + } + + coap_remove_observer(r, o); + } + +not_found: + r = coap_handle_request(&request, resources, options, opt_num); + if (r < 0) { + NET_ERR("No handler for such request (%d)\n", r); + } + + net_pkt_unref(pkt); +} + +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); + if (!ifaddr) { + NET_ERR("Could not add unicast address to interface"); + return false; + } + + 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 coap_pending *pending; + int r; + + pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); + if (!pending) { + return; + } + + r = net_context_sendto(pending->pkt, &pending->addr, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + return; + } + + if (!coap_pending_cycle(pending)) { + coap_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 (!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; + } +} diff --git a/samples/net/zoap_server/src/ieee802154_settings.c b/samples/net/coap_server/src/ieee802154_settings.c similarity index 100% rename from samples/net/zoap_server/src/ieee802154_settings.c rename to samples/net/coap_server/src/ieee802154_settings.c diff --git a/samples/net/coaps_client/README.rst b/samples/net/coaps_client/README.rst index cadd12efe1692..a4c1e9ad9239a 100644 --- a/samples/net/coaps_client/README.rst +++ b/samples/net/coaps_client/README.rst @@ -1,4 +1,4 @@ -.. _coap-client-sample: +.. _coaps-client-sample: CoAP over DTLS sample client ############################ diff --git a/samples/net/coaps_client/prj.conf b/samples/net/coaps_client/prj.conf index c37694e773870..4fc6728ae02c4 100644 --- a/samples/net/coaps_client/prj.conf +++ b/samples/net/coaps_client/prj.conf @@ -20,7 +20,7 @@ CONFIG_MBEDTLS=y CONFIG_MBEDTLS_BUILTIN=y CONFIG_MBEDTLS_CFG_FILE="config-coap.h" -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_NET_APP_SETTINGS=y CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::3" diff --git a/samples/net/coaps_client/src/coaps_client.c b/samples/net/coaps_client/src/coaps_client.c index 391727e42aa10..b18d85ef997ec 100644 --- a/samples/net/coaps_client/src/coaps_client.c +++ b/samples/net/coaps_client/src/coaps_client.c @@ -41,7 +41,7 @@ #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" -#include +#include #if defined(MBEDTLS_DEBUG_C) #include "mbedtls/debug.h" @@ -66,12 +66,12 @@ const char *pers = "mini_client"; static unsigned char payload[128]; #define NUM_REPLIES 3 -struct zoap_reply replies[NUM_REPLIES]; +struct coap_reply replies[NUM_REPLIES]; -#define ZOAP_BUF_SIZE 128 +#define COAP_BUF_SIZE 128 -NET_PKT_TX_SLAB_DEFINE(zoap_pkt_slab, 4); -NET_BUF_POOL_DEFINE(zoap_data_pool, 4, ZOAP_BUF_SIZE, 0, NULL); +NET_PKT_TX_SLAB_DEFINE(coap_pkt_slab, 4); +NET_BUF_POOL_DEFINE(coap_data_pool, 4, COAP_BUF_SIZE, 0, NULL); static const char *const test_path[] = { "test", NULL }; @@ -95,8 +95,8 @@ static void msg_dump(const char *s, u8_t *data, unsigned int len) printk("(%u bytes)\n", len); } -static int resource_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static int resource_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { @@ -184,8 +184,8 @@ void dtls_client(void) int ret; struct udp_context ctx; struct dtls_timing_context timer; - struct zoap_packet request, zpkt; - struct zoap_reply *reply; + struct coap_packet request, zpkt; + struct coap_reply *reply; struct net_pkt *pkt; struct net_buf *frag; u8_t observe = 0; @@ -283,53 +283,48 @@ void dtls_client(void) /* Write to server */ retry: - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); if (!pkt) { goto exit; } - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); if (!frag) { goto exit; } net_pkt_frag_add(pkt, frag); - ret = zoap_packet_init(&request, pkt); + ret = coap_packet_init(&request, pkt, 1, COAP_TYPE_CON, + 0, NULL, COAP_METHOD_GET, coap_next_id()); if (ret < 0) { goto exit; } - zoap_header_set_version(&request, 1); - zoap_header_set_type(&request, ZOAP_TYPE_CON); - zoap_header_set_code(&request, ZOAP_METHOD_GET); - zoap_header_set_id(&request, zoap_next_id()); - zoap_header_set_token(&request, zoap_next_token(), 0); - /* Enable observing the resource. */ - ret = zoap_add_option(&request, ZOAP_OPTION_OBSERVE, - &observe, sizeof(observe)); + ret = coap_packet_append_option(&request, COAP_OPTION_OBSERVE, + &observe, sizeof(observe)); if (ret < 0) { mbedtls_printf("Unable add option to request.\n"); goto exit; } for (p = test_path; p && *p; p++) { - ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - *p, strlen(*p)); + ret = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, + *p, strlen(*p)); if (ret < 0) { mbedtls_printf("Unable add option/path to request.\n"); goto exit; } } - reply = zoap_reply_next_unused(replies, NUM_REPLIES); + reply = coap_reply_next_unused(replies, NUM_REPLIES); if (!reply) { mbedtls_printf("No resources for waiting for replies.\n"); goto exit; } - zoap_reply_init(reply, &request); + coap_reply_init(reply, &request); reply->reply = resource_reply_cb; len = frag->len; @@ -346,24 +341,24 @@ void dtls_client(void) goto exit; } - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); if (!pkt) { mbedtls_printf("Could not get packet from pool\n"); goto exit; } - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); if (!frag) { mbedtls_printf("Could not get frag from pool\n"); goto exit; } net_pkt_frag_add(pkt, frag); - len = ZOAP_BUF_SIZE - 1; - memset(frag->data, 0, ZOAP_BUF_SIZE); + len = COAP_BUF_SIZE - 1; + memset(frag->data, 0, COAP_BUF_SIZE); do { - ret = mbedtls_ssl_read(&ssl, frag->data, ZOAP_BUF_SIZE - 1); + ret = mbedtls_ssl_read(&ssl, frag->data, COAP_BUF_SIZE - 1); } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE); @@ -390,13 +385,13 @@ void dtls_client(void) len = ret; frag->len = len; - ret = zoap_packet_parse(&zpkt, pkt); + ret = coap_packet_parse(&zpkt, pkt, NULL, 0); if (ret) { mbedtls_printf("Could not parse packet\n"); goto exit; } - reply = zoap_response_received(&zpkt, NULL, replies, NUM_REPLIES); + reply = coap_response_received(&zpkt, NULL, replies, NUM_REPLIES); if (!reply) { mbedtls_printf("No handler for response (%d)\n", ret); } diff --git a/samples/net/coaps_server/README.rst b/samples/net/coaps_server/README.rst index 15bf27f096e15..bd997d1b63e35 100644 --- a/samples/net/coaps_server/README.rst +++ b/samples/net/coaps_server/README.rst @@ -1,4 +1,4 @@ -.. _coap-server-sample: +.. _coaps-server-sample: CoAP over DTLS sample server ############################ diff --git a/samples/net/coaps_server/prj.conf b/samples/net/coaps_server/prj.conf index a2f54a84f69c8..5a322a4efeb2f 100644 --- a/samples/net/coaps_server/prj.conf +++ b/samples/net/coaps_server/prj.conf @@ -16,7 +16,7 @@ CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5 CONFIG_NET_MAX_CONTEXTS=10 -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_MBEDTLS=y CONFIG_MBEDTLS_BUILTIN=y diff --git a/samples/net/coaps_server/src/coaps_server.c b/samples/net/coaps_server/src/coaps_server.c index 70cb19a36a243..9ef76547cb266 100644 --- a/samples/net/coaps_server/src/coaps_server.c +++ b/samples/net/coaps_server/src/coaps_server.c @@ -37,7 +37,7 @@ #include #include -#include +#include #include "udp.h" #include "udp_cfg.h" @@ -58,10 +58,10 @@ static unsigned char heap[8192]; #endif -#define ZOAP_BUF_SIZE 128 +#define COAP_BUF_SIZE 128 -NET_PKT_TX_SLAB_DEFINE(zoap_pkt_slab, 4); -NET_BUF_POOL_DEFINE(zoap_data_pool, 4, ZOAP_BUF_SIZE, 0, NULL); +NET_PKT_TX_SLAB_DEFINE(coap_pkt_slab, 4); +NET_BUF_POOL_DEFINE(coap_data_pool, 4, COAP_BUF_SIZE, 0, NULL); /* * Hardcoded values for server host and port @@ -76,45 +76,43 @@ const char psk_id[] = "Client_identity\0"; static mbedtls_ssl_context *curr_ctx; -static int send_response(struct zoap_packet *request, u8_t response_code) +static int send_response(struct coap_packet *request, u8_t response_code) { struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; + struct coap_packet response; u8_t code, type; u16_t id; int r; - code = zoap_header_get_code(request); - type = zoap_header_get_type(request); - id = zoap_header_get_id(request); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); printk("*******\n"); printk("type: %u code %u id %u\n", type, code, id); printk("*******\n"); - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); if (!pkt) { return -ENOMEM; } - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); if (!frag) { + net_pkt_unref(pkt); return -ENOMEM; } net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, response_code, id); if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - zoap_header_set_version(&response, 1); - zoap_header_set_type(&response, ZOAP_TYPE_ACK); - zoap_header_set_code(&response, response_code); - zoap_header_set_id(&response, id); - do { r = mbedtls_ssl_write(curr_ctx, frag->data, frag->len); } while (r == MBEDTLS_ERR_SSL_WANT_READ @@ -129,79 +127,80 @@ static int send_response(struct zoap_packet *request, u8_t response_code) return r; } -static int test_del(struct zoap_resource *resource, - struct zoap_packet *request, const struct sockaddr *from) +static int test_del(struct coap_resource *resource, + struct coap_packet *request) { - return send_response(request, ZOAP_RESPONSE_CODE_DELETED); + return send_response(request, COAP_RESPONSE_CODE_DELETED); } -static int test_put(struct zoap_resource *resource, - struct zoap_packet *request, const struct sockaddr *from) +static int test_put(struct coap_resource *resource, + struct coap_packet *request) { - return send_response(request, ZOAP_RESPONSE_CODE_CHANGED); + return send_response(request, COAP_RESPONSE_CODE_CHANGED); } -static int test_post(struct zoap_resource *resource, - struct zoap_packet *request, const struct sockaddr *from) +static int test_post(struct coap_resource *resource, + struct coap_packet *request) { - return send_response(request, ZOAP_RESPONSE_CODE_CREATED); + return send_response(request, COAP_RESPONSE_CODE_CREATED); } -static int piggyback_get(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from) +static int piggyback_get(struct coap_resource *resource, + struct coap_packet *request) { struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, code, type; - u16_t len, id; + struct coap_packet response; + u8_t payload[40], code, type; + u16_t id; int r; - code = zoap_header_get_code(request); - type = zoap_header_get_type(request); - id = zoap_header_get_id(request); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); printk("*******\n"); printk("type: %u code %u id %u\n", type, code, id); printk("*******\n"); - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); if (!pkt) { return -ENOMEM; } - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); if (!frag) { + net_pkt_unref(pkt); return -ENOMEM; } net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - 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); - - payload = zoap_packet_get_payload(&response, &len); - if (!payload) { + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); 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) { + r = snprintk((char *)payload, sizeof(payload), + "Type: %u\nCode: %u\nMID: %u\n", type, code, id); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - r = zoap_packet_set_used(&response, r); - if (r) { + r = coap_packet_append_payload(&response, (u8_t *)payload, + strlen(payload)); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } @@ -219,22 +218,22 @@ static int piggyback_get(struct zoap_resource *resource, return r; } -static int query_get(struct zoap_resource *resource, - struct zoap_packet *request, const struct sockaddr *from) +static int query_get(struct coap_resource *resource, + struct coap_packet *request) { - struct zoap_option options[4]; + struct coap_option options[4]; struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, code, type; - u16_t len, id; + struct coap_packet response; + u8_t payload[40], code, type; + u16_t id; int i, r; - code = zoap_header_get_code(request); - type = zoap_header_get_type(request); - id = zoap_header_get_id(request); + code = coap_header_get_code(request); + type = coap_header_get_type(request); + id = coap_header_get_id(request); - r = zoap_find_options(request, ZOAP_OPTION_URI_QUERY, options, 4); + r = coap_find_options(request, COAP_OPTION_URI_QUERY, options, 4); if (r <= 0) { return -EINVAL; } @@ -261,43 +260,44 @@ static int query_get(struct zoap_resource *resource, printk("*******\n"); - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); if (!pkt) { return -ENOMEM; } - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); if (!frag) { + net_pkt_unref(pkt); return -ENOMEM; } net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); if (r < 0) { + net_pkt_unref(pkt); 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); - - payload = zoap_packet_get_payload(&response, &len); - if (!payload) { + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); 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) { + r = snprintk((char *)payload, sizeof(payload), + "Type: %u\nCode: %u\nMID: %u\n", type, code, id); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - r = zoap_packet_set_used(&response, r); - if (r) { + r = coap_packet_append_payload(&response, (u8_t *)payload, + strlen(payload)); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } @@ -321,7 +321,7 @@ static const char *const segments_path[] = { "seg1", "seg2", "seg3", NULL }; static const char *const query_path[] = { "query", NULL }; -static struct zoap_resource resources[] = { +static struct coap_resource resources[] = { {.get = piggyback_get, .post = test_post, .del = test_del, @@ -419,9 +419,11 @@ void dtls_server(void) int len, ret = 0; struct udp_context ctx; struct dtls_timing_context timer; - struct zoap_packet zpkt; + struct coap_packet zpkt; struct net_pkt *pkt; struct net_buf *frag; + struct coap_option options[16]; + u8_t opt_num = 16; mbedtls_ssl_cookie_ctx cookie_ctx; mbedtls_entropy_context entropy; @@ -553,21 +555,21 @@ void dtls_server(void) do { /* Read the request */ - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); if (!pkt) { mbedtls_printf("Could not get packet from slab\n"); goto exit; } - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); if (!frag) { mbedtls_printf("Could not get frag from pool\n"); goto exit; } net_pkt_frag_add(pkt, frag); - len = ZOAP_BUF_SIZE - 1; - memset(frag->data, 0, ZOAP_BUF_SIZE); + len = COAP_BUF_SIZE - 1; + memset(frag->data, 0, COAP_BUF_SIZE); ret = mbedtls_ssl_read(&ssl, frag->data, len); if (ret == MBEDTLS_ERR_SSL_WANT_READ || @@ -598,14 +600,13 @@ void dtls_server(void) len = ret; frag->len = len; - ret = zoap_packet_parse(&zpkt, pkt); + ret = coap_packet_parse(&zpkt, pkt, options, opt_num); if (ret) { mbedtls_printf("Could not parse packet\n"); goto exit; } - ret = zoap_handle_request(&zpkt, resources, - (const struct sockaddr *)&ssl); + ret = coap_handle_request(&zpkt, resources, options, opt_num); if (ret < 0) { mbedtls_printf("No handler for such request (%d)\n", ret); diff --git a/samples/net/leds_demo/prj.conf b/samples/net/leds_demo/prj.conf index be2d0dbdd0650..708b60c247755 100644 --- a/samples/net/leds_demo/prj.conf +++ b/samples/net/leds_demo/prj.conf @@ -7,6 +7,6 @@ CONFIG_SYS_LOG=y CONFIG_SYS_LOG_SHOW_COLOR=y CONFIG_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_NET_APP_SETTINGS=y CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" diff --git a/samples/net/leds_demo/prj_802154.conf b/samples/net/leds_demo/prj_802154.conf index 083e365e49777..ce29dda54faf3 100644 --- a/samples/net/leds_demo/prj_802154.conf +++ b/samples/net/leds_demo/prj_802154.conf @@ -8,6 +8,6 @@ CONFIG_NET_LOG=y CONFIG_NET_UDP=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_IEEE802154_CC2520=y -CONFIG_ZOAP=y +CONFIG_COAP=y CONFIG_NET_APP_SETTINGS=y CONFIG_NET_APP_IEEE802154_CHANNEL=20 diff --git a/samples/net/leds_demo/src/leds-demo.c b/samples/net/leds_demo/src/leds-demo.c index b42b884146c8d..c82970a246b17 100644 --- a/samples/net/leds_demo/src/leds-demo.c +++ b/samples/net/leds_demo/src/leds-demo.c @@ -5,7 +5,7 @@ */ #if 1 -#define SYS_LOG_DOMAIN "zoap-server" +#define SYS_LOG_DOMAIN "coap-server" #define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG #define NET_LOG_ENABLED 1 #endif @@ -26,8 +26,8 @@ #include -#include -#include +#include +#include #define MY_COAP_PORT 5683 @@ -56,6 +56,54 @@ static const char led_toggle_off[] = "LED Toggle OFF\n"; static bool fake_led; +static void get_from_ip_addr(struct coap_packet *cpkt, + struct sockaddr_in6 *from) +{ + struct net_udp_hdr hdr, *udp_hdr; + + udp_hdr = net_udp_get_hdr(cpkt->pkt, &hdr); + if (!udp_hdr) { + return; + } + + net_ipaddr_copy(&from->sin6_addr, &NET_IPV6_HDR(cpkt->pkt)->src); + + from->sin6_port = udp_hdr->src_port; + from->sin6_family = AF_INET6; +} + +static int well_known_core_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct coap_packet response; + struct sockaddr_in6 from; + struct net_pkt *pkt; + struct net_buf *frag; + int r; + + NET_DBG(""); + + pkt = net_pkt_get_tx(context, K_FOREVER); + frag = net_pkt_get_data(context, K_FOREVER); + net_pkt_frag_add(pkt, frag); + + r = coap_well_known_core_get(resource, request, &response, pkt); + if (r < 0) { + net_pkt_unref(response.pkt); + return r; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(response.pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(response.pkt); + } + + return r; +} + static bool read_led(void) { u32_t led = 0; @@ -83,19 +131,18 @@ static void write_led(bool led) gpio_pin_write(led0, LED_PIN, !led); } -static int led_get(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from) +static int led_get(struct coap_resource *resource, + struct coap_packet *request) { struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; + struct sockaddr_in6 from; + struct coap_packet response; const char *str; - u8_t *payload; u16_t len, id; int r; - id = zoap_header_get_id(request); + id = coap_header_get_id(request); pkt = net_pkt_get_tx(context, K_FOREVER); if (!pkt) { @@ -109,38 +156,35 @@ static int led_get(struct zoap_resource *resource, net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); 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); - - payload = zoap_packet_get_payload(&response, &len); - if (!payload) { - return -EINVAL; - } - if (read_led()) { str = led_on; - r = sizeof(led_on); + len = sizeof(led_on); } else { str = led_off; - r = sizeof(led_off); + len = sizeof(led_off); } - memcpy(payload, str, r); + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } - r = zoap_packet_set_used(&response, r); - if (r) { + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - r = net_context_sendto(pkt, from, sizeof(struct sockaddr_in6), + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), NULL, 0, NULL, NULL); if (r < 0) { net_pkt_unref(pkt); @@ -149,26 +193,42 @@ static int led_get(struct zoap_resource *resource, return r; } -static int led_post(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from) +static int led_post(struct coap_resource *resource, + struct coap_packet *request) { struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; + struct sockaddr_in6 from; + struct coap_packet response; const char *str; - u8_t *payload; - u16_t len, id; + u8_t payload; + u8_t len; + u16_t id; + u16_t offset; u32_t led; int r; - payload = zoap_packet_get_payload(request, &len); - if (!payload) { - printk("packet without payload"); + led = 0; + frag = net_frag_skip(request->frag, request->offset, &offset, + request->hdr_len + request->opt_len); + if (!frag && offset == 0xffff) { return -EINVAL; } - id = zoap_header_get_id(request); + frag = net_frag_read_u8(frag, offset, &offset, &payload); + if (!frag && offset == 0xffff) { + printk("packet without payload, so toggle the led"); + led = read_led(); + led = !led; + } else { + if (payload == 0x31) { + led = 1; + } + } + + write_led(led); + + id = coap_header_get_id(request); pkt = net_pkt_get_tx(context, K_FOREVER); if (!pkt) { @@ -182,44 +242,35 @@ static int led_post(struct zoap_resource *resource, net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); 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); - - payload = zoap_packet_get_payload(&response, &len); - if (!payload) { - return -EINVAL; - } - - led = read_led(); - - led = !led; - - write_led(led); - if (led) { str = led_toggle_on; - r = sizeof(led_toggle_on); + len = sizeof(led_toggle_on); } else { str = led_toggle_off; - r = sizeof(led_toggle_off); + len = sizeof(led_toggle_off); } - memcpy(payload, str, r); + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } - r = zoap_packet_set_used(&response, r); - if (r) { + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - r = net_context_sendto(pkt, from, sizeof(struct sockaddr_in6), + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), NULL, 0, NULL, NULL); if (r < 0) { net_pkt_unref(pkt); @@ -228,31 +279,42 @@ static int led_post(struct zoap_resource *resource, return r; } -static int led_put(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from) +static int led_put(struct coap_resource *resource, + struct coap_packet *request) { struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; + struct sockaddr_in6 from; + struct coap_packet response; const char *str; - u8_t *payload; - u16_t len, id; + u8_t payload; + u8_t len; + u16_t id; + u16_t offset; u32_t led; int r; - payload = zoap_packet_get_payload(request, &len); - if (!payload) { - printk("packet without payload"); + led = 0; + frag = net_frag_skip(request->frag, request->offset, &offset, + request->hdr_len + request->opt_len); + if (!frag && offset == 0xffff) { return -EINVAL; } - led = 0; - if (len > 0 && payload[0] == '1') { - led = 1; + frag = net_frag_read_u8(frag, offset, &offset, &payload); + if (!frag && offset == 0xffff) { + printk("packet without payload, so toggle the led"); + led = read_led(); + led = !led; + } else { + if (payload == 0x31) { + led = 1; + } } - id = zoap_header_get_id(request); + write_led(led); + + id = coap_header_get_id(request); pkt = net_pkt_get_tx(context, K_FOREVER); if (!pkt) { @@ -266,40 +328,35 @@ static int led_put(struct zoap_resource *resource, net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CHANGED, id); 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); - - payload = zoap_packet_get_payload(&response, &len); - if (!payload) { - return -EINVAL; - } - - write_led(led); - if (led) { str = led_on; - r = sizeof(led_on); + len = sizeof(led_on); } else { str = led_off; - r = sizeof(led_off); + len = sizeof(led_off); } - memcpy(payload, str, r); + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } - r = zoap_packet_set_used(&response, r); - if (r) { + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - r = net_context_sendto(pkt, from, sizeof(struct sockaddr_in6), + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), NULL, 0, NULL, NULL); if (r < 0) { net_pkt_unref(pkt); @@ -308,19 +365,18 @@ static int led_put(struct zoap_resource *resource, return r; } -static int dummy_get(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from) +static int dummy_get(struct coap_resource *resource, + struct coap_packet *request) { static const char dummy_str[] = "Just a test\n"; struct net_pkt *pkt; struct net_buf *frag; - struct zoap_packet response; - u8_t *payload; - u16_t len, id; + struct sockaddr_in6 from; + struct coap_packet response; + u16_t id; int r; - id = zoap_header_get_id(request); + id = coap_header_get_id(request); pkt = net_pkt_get_tx(context, K_FOREVER); if (!pkt) { @@ -334,30 +390,28 @@ static int dummy_get(struct zoap_resource *resource, net_pkt_frag_add(pkt, frag); - r = zoap_packet_init(&response, pkt); + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); 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); - - payload = zoap_packet_get_payload(&response, &len); - if (!payload) { + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - memcpy(payload, dummy_str, sizeof(dummy_str)); - - r = zoap_packet_set_used(&response, sizeof(dummy_str)); - if (r) { + r = coap_packet_append_payload(&response, (u8_t *)dummy_str, + sizeof(dummy_str)); + if (r < 0) { + net_pkt_unref(pkt); return -EINVAL; } - r = net_context_sendto(pkt, from, sizeof(struct sockaddr_in6), + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), NULL, 0, NULL, NULL); if (r < 0) { net_pkt_unref(pkt); @@ -378,19 +432,24 @@ static const char * const dummy_attributes[] = { "rt=dummy", NULL }; -static struct zoap_resource resources[] = { - ZOAP_WELL_KNOWN_CORE_RESOURCE, +static struct coap_resource resources[] = { + { .get = well_known_core_get, + .post = NULL, + .put = NULL, + .path = COAP_WELL_KNOWN_CORE_PATH, + .user_data = NULL, + }, { .get = led_get, .post = led_post, .put = led_put, .path = led_default_path, - .user_data = &((struct zoap_core_metadata) { + .user_data = &((struct coap_core_metadata) { .attributes = led_default_attributes, }), }, { .get = dummy_get, .path = dummy_path, - .user_data = &((struct zoap_core_metadata) { + .user_data = &((struct coap_core_metadata) { .attributes = dummy_attributes, }), }, @@ -402,46 +461,24 @@ static void udp_receive(struct net_context *context, int status, void *user_data) { - struct zoap_packet request; - struct sockaddr_in6 from; - struct net_udp_hdr hdr, *udp_hdr; - int r, header_len; - - net_ipaddr_copy(&from.sin6_addr, &NET_IPV6_HDR(pkt)->src); - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - if (!udp_hdr) { - printk("Invalid UDP data received\n"); - net_pkt_unref(pkt); - return; - } - - from.sin6_port = udp_hdr->src_port; - from.sin6_family = AF_INET6; - - /* - * zoap expects that buffer->data starts at the - * beginning of the CoAP header - */ - header_len = net_pkt_appdata(pkt) - pkt->frags->data; - net_buf_pull(pkt->frags, header_len); + struct coap_packet request; + struct coap_option options[16] = { 0 }; + u8_t opt_num = 16; + int r; - r = zoap_packet_parse(&request, pkt); + r = coap_packet_parse(&request, pkt, options, opt_num); if (r < 0) { NET_ERR("Invalid data received (%d)\n", r); net_pkt_unref(pkt); return; } - r = zoap_handle_request(&request, resources, - (const struct sockaddr *) &from); - - net_pkt_unref(pkt); - + r = coap_handle_request(&request, resources, options, opt_num); if (r < 0) { NET_ERR("No handler for such request (%d)\n", r); - return; } + + net_pkt_unref(pkt); } static bool join_coap_multicast_group(void) diff --git a/samples/net/lwm2m_client/Makefile b/samples/net/lwm2m_client/Makefile index df07b43961752..7af342361d3fb 100644 --- a/samples/net/lwm2m_client/Makefile +++ b/samples/net/lwm2m_client/Makefile @@ -7,7 +7,7 @@ # BOARD ?= qemu_x86 -CONF_FILE ?= prj_$(BOARD).conf +CONF_FILE ?= prj.conf include $(ZEPHYR_BASE)/Makefile.inc include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack diff --git a/samples/net/lwm2m_client/README.rst b/samples/net/lwm2m_client/README.rst index 3f928d5019295..a0522646f53f3 100644 --- a/samples/net/lwm2m_client/README.rst +++ b/samples/net/lwm2m_client/README.rst @@ -31,14 +31,14 @@ Requirements Building and Running ******************** -There are configuration files for various hardware setups in the +There are configuration files for various setups in the samples/net/lwm2m_client directory: -- :file:`prj_frdm_k64f.conf` - Use this for FRDM-K64F board with built-in ethernet. +- :file:`prj.conf` + This is the standard default config. -- :file:`prj_qemu_x86.conf` - Use this for x86 QEMU. +- :file:`prj_dtls.conf` + Use this for enabling DTLS support. Build the lwm2m-client sample application like this: @@ -49,7 +49,7 @@ Build the lwm2m-client sample application like this: BOARD= The easiest way to setup this sample application is to build and run it -via QEMU using the configuration :file:`prj_qemu_x86.conf`. +via QEMU using the default configuration :file:`prj.conf`. This requires a small amount of setup described in :ref:`networking_with_qemu`. Download and run the latest build of the Leshan Demo Server: diff --git a/samples/net/lwm2m_client/prj_qemu_x86.conf b/samples/net/lwm2m_client/prj.conf similarity index 82% rename from samples/net/lwm2m_client/prj_qemu_x86.conf rename to samples/net/lwm2m_client/prj.conf index 84fa4dabb2de6..e97e9ff98ccbe 100644 --- a/samples/net/lwm2m_client/prj_qemu_x86.conf +++ b/samples/net/lwm2m_client/prj.conf @@ -17,11 +17,11 @@ CONFIG_INIT_STACKS=y CONFIG_PRINTK=y CONFIG_NET_STATISTICS=y CONFIG_NET_BUF_DATA_SIZE=384 -CONFIG_NET_PKT_RX_COUNT=50 -CONFIG_NET_PKT_TX_COUNT=50 -CONFIG_NET_BUF_RX_COUNT=50 -CONFIG_NET_BUF_TX_COUNT=50 -CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_PKT_RX_COUNT=10 +CONFIG_NET_PKT_TX_COUNT=10 +CONFIG_NET_BUF_RX_COUNT=10 +CONFIG_NET_BUF_TX_COUNT=10 +CONFIG_NET_MAX_CONTEXTS=3 CONFIG_NET_CONTEXT_NET_PKT_POOL=y CONFIG_NET_SHELL=y @@ -31,7 +31,6 @@ CONFIG_NET_APP_NEED_IPV4=y CONFIG_NET_APP_SETTINGS=y CONFIG_LWM2M=y -CONFIG_LWM2M_RD_CLIENT_INSTANCE_COUNT=2 CONFIG_LWM2M_IPSO_SUPPORT=y CONFIG_LWM2M_IPSO_TEMP_SENSOR=y CONFIG_LWM2M_IPSO_LIGHT_CONTROL=y @@ -40,5 +39,3 @@ CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" - -CONFIG_NET_SLIP_TAP=y diff --git a/samples/net/lwm2m_client/prj_frdm_k64f.conf b/samples/net/lwm2m_client/prj_dtls.conf similarity index 73% rename from samples/net/lwm2m_client/prj_frdm_k64f.conf rename to samples/net/lwm2m_client/prj_dtls.conf index d159f59f06f64..3a0c882daf6ca 100644 --- a/samples/net/lwm2m_client/prj_frdm_k64f.conf +++ b/samples/net/lwm2m_client/prj_dtls.conf @@ -17,11 +17,11 @@ CONFIG_INIT_STACKS=y CONFIG_PRINTK=y CONFIG_NET_STATISTICS=y CONFIG_NET_BUF_DATA_SIZE=384 -CONFIG_NET_PKT_RX_COUNT=50 -CONFIG_NET_PKT_TX_COUNT=50 -CONFIG_NET_BUF_RX_COUNT=50 -CONFIG_NET_BUF_TX_COUNT=50 -CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_PKT_RX_COUNT=10 +CONFIG_NET_PKT_TX_COUNT=10 +CONFIG_NET_BUF_RX_COUNT=10 +CONFIG_NET_BUF_TX_COUNT=10 +CONFIG_NET_MAX_CONTEXTS=3 CONFIG_NET_CONTEXT_NET_PKT_POOL=y CONFIG_NET_SHELL=y @@ -29,9 +29,16 @@ CONFIG_NET_SHELL=y CONFIG_NET_APP_NEED_IPV6=y CONFIG_NET_APP_NEED_IPV4=y CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_DTLS=y + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=8192 +CONFIG_MBEDTLS_CFG_FILE="config-lwm2m.h" CONFIG_LWM2M=y -CONFIG_LWM2M_RD_CLIENT_INSTANCE_COUNT=2 +CONFIG_LWM2M_PEER_PORT=5684 CONFIG_LWM2M_IPSO_SUPPORT=y CONFIG_LWM2M_IPSO_TEMP_SENSOR=y CONFIG_LWM2M_IPSO_LIGHT_CONTROL=y @@ -40,5 +47,3 @@ CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" - -CONFIG_NET_L2_ETHERNET=y diff --git a/samples/net/lwm2m_client/src/lwm2m-client.c b/samples/net/lwm2m_client/src/lwm2m-client.c index c95f449176d13..49fba71a4149b 100644 --- a/samples/net/lwm2m_client/src/lwm2m-client.c +++ b/samples/net/lwm2m_client/src/lwm2m-client.c @@ -54,6 +54,36 @@ static struct device *led_dev; static u32_t led_state; static struct lwm2m_ctx client; + +#if defined(CONFIG_NET_APP_DTLS) +#if !defined(CONFIG_NET_APP_TLS_STACK_SIZE) +#define CONFIG_NET_APP_TLS_STACK_SIZE 30000 +#endif /* CONFIG_NET_APP_TLS_STACK_SIZE */ + +#define HOSTNAME "localhost" /* for cert verification if that is enabled */ + +/* The result buf size is set to large enough so that we can receive max size + * buf back. Note that mbedtls needs also be configured to have equal size + * value for its buffer size. See MBEDTLS_SSL_MAX_CONTENT_LEN option in DTLS + * config file. + */ +#define RESULT_BUF_SIZE 1500 + +NET_APP_TLS_POOL_DEFINE(dtls_pool, 10); + +/* "000102030405060708090a0b0c0d0e0f" */ +static unsigned char client_psk[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f +}; + +static char client_psk_id[] = "Client_identity"; + +static u8_t dtls_result[RESULT_BUF_SIZE]; +NET_STACK_DEFINE(NET_APP_DTLS, net_app_dtls_stack, + CONFIG_NET_APP_TLS_STACK_SIZE, CONFIG_NET_APP_TLS_STACK_SIZE); +#endif /* CONFIG_NET_APP_DTLS */ + static struct k_sem quit_lock; #if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) @@ -147,12 +177,23 @@ static int device_factory_default_cb(u16_t obj_inst_id) return 1; } +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT) static int firmware_update_cb(u16_t obj_inst_id) { SYS_LOG_DBG("UPDATE"); + + /* TODO: kick off update process */ + + /* If success, set the update result as RESULT_SUCCESS. + * In reality, it should be set at function lwm2m_setup() + */ + lwm2m_engine_set_u8("5/0/3", STATE_IDLE); + lwm2m_engine_set_u8("5/0/5", RESULT_SUCCESS); return 1; } +#endif +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT) static int firmware_block_received_cb(u16_t obj_inst_id, u8_t *data, u16_t data_len, bool last_block, size_t total_size) @@ -161,6 +202,7 @@ static int firmware_block_received_cb(u16_t obj_inst_id, data_len, last_block); return 1; } +#endif static int lwm2m_setup(void) { @@ -204,10 +246,12 @@ static int lwm2m_setup(void) /* setup FIRMWARE object */ - lwm2m_engine_register_post_write_callback("5/0/0", - firmware_block_received_cb); +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT) lwm2m_firmware_set_write_cb(firmware_block_received_cb); - lwm2m_engine_register_exec_callback("5/0/2", firmware_update_cb); +#endif +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT) + lwm2m_firmware_set_update_cb(firmware_update_cb); +#endif /* setup TEMP SENSOR object */ @@ -227,6 +271,50 @@ static int lwm2m_setup(void) return 0; } +static void rd_client_event(struct lwm2m_ctx *client, + enum lwm2m_rd_client_event client_event) +{ + switch (client_event) { + + case LWM2M_RD_CLIENT_EVENT_NONE: + /* do nothing */ + break; + + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_FAILURE: + SYS_LOG_DBG("Bootstrap failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_COMPLETE: + SYS_LOG_DBG("Bootstrap complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE: + SYS_LOG_DBG("Registration failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE: + SYS_LOG_DBG("Registration complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_REG_UPDATE_FAILURE: + SYS_LOG_DBG("Registration update failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE: + SYS_LOG_DBG("Registration update complete"); + break; + + case LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE: + SYS_LOG_DBG("Deregister failure!"); + break; + + case LWM2M_RD_CLIENT_EVENT_DISCONNECT: + SYS_LOG_DBG("Disconnected"); + break; + + } +} + void main(void) { int ret; @@ -249,12 +337,27 @@ void main(void) client.data_pool = data_udp_pool; #endif +#if defined(CONFIG_NET_APP_DTLS) + client.client_psk = client_psk; + client.client_psk_len = 16; + client.client_psk_id = client_psk_id; + client.client_psk_id_len = strlen(client_psk_id); + client.cert_host = HOSTNAME; + client.dtls_pool = &dtls_pool; + client.dtls_result_buf = dtls_result; + client.dtls_result_buf_len = RESULT_BUF_SIZE; + client.dtls_stack = net_app_dtls_stack; + client.dtls_stack_len = K_THREAD_STACK_SIZEOF(net_app_dtls_stack); +#endif /* CONFIG_NET_APP_DTLS */ + #if defined(CONFIG_NET_IPV6) ret = lwm2m_rd_client_start(&client, CONFIG_NET_APP_PEER_IPV6_ADDR, - CONFIG_LWM2M_PEER_PORT, CONFIG_BOARD); + CONFIG_LWM2M_PEER_PORT, CONFIG_BOARD, + rd_client_event); #elif defined(CONFIG_NET_IPV4) ret = lwm2m_rd_client_start(&client, CONFIG_NET_APP_PEER_IPV4_ADDR, - CONFIG_LWM2M_PEER_PORT, CONFIG_BOARD); + CONFIG_LWM2M_PEER_PORT, CONFIG_BOARD, + rd_client_event); #else SYS_LOG_ERR("LwM2M client requires IPv4 or IPv6."); ret = -EPROTONOSUPPORT; diff --git a/samples/net/rpl-node/0001-Added-CoAP-support-for-Sparrow-Border-Router.patch b/samples/net/rpl-node/0001-Added-CoAP-support-for-Sparrow-Border-Router.patch new file mode 100644 index 0000000000000..9d2481c68fac9 --- /dev/null +++ b/samples/net/rpl-node/0001-Added-CoAP-support-for-Sparrow-Border-Router.patch @@ -0,0 +1,272 @@ +From cb38f06f84e8d3c1e021265d40a21bc16b5a8a44 Mon Sep 17 00:00:00 2001 +From: Ravi kumar Veeramally +Date: Fri, 9 Jun 2017 13:08:52 +0300 +Subject: [PATCH] Added CoAP support for Sparrow Border Router + +Sparrow border router communicates with only TLV messages. But +there are other implementations which does not support TLV. +Support added with generic CoAP based commands to retrieve +RPL and IPv6 information with CoAP commands. Also supported +LED's control methods. + +Signed-off-by: Ravi kumar Veeramally +--- + examples/sparrow/wsdemoserver.py | 4 +- + examples/sparrow/wspcoap.py | 81 +++++++++++++++++++++++++++ + examples/sparrow/wspnodes.py | 38 +++++++------ + examples/sparrow/www/index.html | 12 ++++ + products/sparrow-border-router/project-conf.h | 3 + + tools/sparrow/deviceserver.py | 1 + + 6 files changed, 120 insertions(+), 19 deletions(-) + create mode 100644 examples/sparrow/wspcoap.py + +diff --git a/examples/sparrow/wsdemoserver.py b/examples/sparrow/wsdemoserver.py +index 8db1db7..67127fa 100755 +--- a/examples/sparrow/wsdemoserver.py ++++ b/examples/sparrow/wsdemoserver.py +@@ -40,7 +40,7 @@ DEBUG = 0 + + import sys, subprocess, thread, string, tlvlib, socket, binascii + from SimpleWebSocketServer import WebSocket, SimpleWebSocketServer +-import json, deviceserver, struct, wspserial, wsptlvs, wspnodes ++import json, deviceserver, struct, wspserial, wsptlvs, wspnodes, wspcoap + import httpd + + # Some global vaiables +@@ -304,5 +304,5 @@ if __name__ == "__main__": + print "Starting demo server" + setup_state() + server = SimpleWebSocketServer('', 8001, DemoSocket) +- plugins = plugins + [wspserial.SerialCommands(), wsptlvs.TLVCommands(), wspnodes.NodeCommands()] ++ plugins = plugins + [wspserial.SerialCommands(), wsptlvs.TLVCommands(), wspnodes.NodeCommands(), wspcoap.CoAPCommands()] + server.serveforever() +diff --git a/examples/sparrow/wspcoap.py b/examples/sparrow/wspcoap.py +new file mode 100644 +index 0000000..a1b2899 +--- /dev/null ++++ b/examples/sparrow/wspcoap.py +@@ -0,0 +1,81 @@ ++import wsplugin, thread, subprocess, json, re, os.path ++ ++def coap_is_supported(): ++ return os.path.isfile("/home/rveerama/src/libcoap-develop/examples/coap-client") ++ ++def coap_get(ws, uri): ++ ws.stop = False ++ p=subprocess.Popen(["/home/rveerama/src/libcoap-develop/examples/coap-client", "-m", "get", uri], ++ stdout=subprocess.PIPE, stderr=subprocess.PIPE) ++ data = "" ++ try: ++ while not ws.stop: ++ line = p.stdout.readline() ++ if line == '': break ++ data = data + line ++ except Exception as e: ++ print e ++ print "CoAP Unexpected error:", sys.exc_info()[0] ++ p.terminate() ++ return data ++ ++# This assumes that data is ascii! ++def coap_put(ws, uri, data): ++ ws.stop = False ++ p=subprocess.Popen(["/home/rveerama/src/libcoap-develop/examples/coap-client", "-e" + data, ++ "-m", "put", uri], ++ stdout=subprocess.PIPE, stderr=subprocess.PIPE) ++ line = "" ++ try: ++ while not ws.stop: ++ line = p.stdout.readline() ++ if line == '': break ++ print line ++ except Exception as e: ++ print e ++ print "CoAP Unexpected error:", sys.exc_info()[0] ++ p.terminate() ++ return line ++ ++def coap_check(ws): ++ if not coap_is_supported(): ++ print "Error: can not find libcoap at /home/rveerama/src/libcoap-develop/examples/coap-client" ++ ws.sendMessage(json.dumps({"error":"Can not find libcoap. Please install libcoap in the server and try again."})) ++ return False ++ return True ++ ++# The plugin class ++class CoAPCommands(wsplugin.DemoPlugin): ++ ++ def get_commands(self): ++ return ["coapled", "coaptemp"] ++ ++ def handle_command(self, wsdemo, cmds): ++ if cmds[0] == "coapled": ++ if coap_check(wsdemo): ++ ip = cmds[1] ++ led = cmds[2] ++ on = cmds[3] ++ thread.start_new_thread(coapled, (wsdemo, ip, led, on)) ++ return True ++ elif cmds[0] == "coaptemp": ++ if coap_check(wsdemo): ++ ip = cmds[1] ++ thread.start_new_thread(coaptemp, (wsdemo, ip)) ++ return True ++ return False ++ ++# Toggle a LED on a Yanzi IoT-U10 node (or other node with LEDs) ++def coapled(ws, ip, led, on): ++ coap_put(ws, "coap://[" + ip + "]/led/" + led, on) ++ ++# Read the temperature from a Yanzi IoT-U10 node ++def coaptemp(ws, ip): ++ temperature = coap_get(ws, "coap://[" + ip + "]/temperature") ++ print "\t",temperature ++ # get the temperature from the coap response ++ m = re.search("Temperature .+: (.+)", temperature) ++ if m: ++ ws.sendMessage(json.dumps({"temp":m.group(1),"address":ip})) ++ else: ++ ws.sendMessage(json.dumps({"error":"Failed to fetch temperature via CoAP"})) +diff --git a/examples/sparrow/wspnodes.py b/examples/sparrow/wspnodes.py +index 3f5dc13..abfd1ae 100644 +--- a/examples/sparrow/wspnodes.py ++++ b/examples/sparrow/wspnodes.py +@@ -28,6 +28,7 @@ + # + + import socket, binascii, wsplugin, tlvlib, thread, deviceserver, json, struct, urllib2, time ++import wspcoap + + RANK_DIVISOR = 128.0 + +@@ -102,37 +103,40 @@ class NodeCommands(wsplugin.DemoPlugin): + node = 2 + nodes[endfix] = 1 + for device in devs: +- rpl = device.nstats_rpl +- parent = None ++ rpl = wspcoap.coap_get(ws, "coap://[" + device.address + "]/rpl-info") + if rpl is None: + rank = "unknown" + else: +- rank = rpl.dag_rank() / RANK_DIVISOR +- parent = rpl.parent_as_string() ++ parent1 = rpl.split('\n', 4)[1] ++ rank1 = rpl.split('\n', 4)[2] ++ rank = rank1.split('-', 2)[1], ++ parent = parent1.split('-', 2)[1] + addr = socket.inet_pton(socket.AF_INET6, device.address) + endfix = binascii.hexlify(addr[-4:]) + # First we add the parent as level - second round we will + # change this to "level" instead +- topology["nodes"] = topology["nodes"] + [{"id":node, "label":"N" + str(node), "title":"Rank " + str(rank) + "
" + device.address, +- "level":-1, "parent":parent, "address":endfix}] ++ topology["nodes"] = topology["nodes"] + [{"id":node, "label":"N" + str(node), ++ "title":"Rank " + str(rank) + "
" + device.address, ++ "level":-1, "parent":str(parent), "address":endfix}] + nodes[endfix] = node + node = node + 1 + +- changed = True +- i = 0 + edges = [] +- while changed and i < 10: +- changed = False ++ i = 0 ++ while i < 3: + i = i + 1 + for n in topology["nodes"]: + if n["level"] is -1: +- p = topology["nodes"][nodes[n["parent"]] - 1] +- # add an edge +- edges = edges + [{"from":p["id"],"to":n["id"]}] +- if p["level"] is not -1: +- n["level"] = p["level"] + 1 +- #print "level should be ", p["level"] + 1 +- changed = True ++ parent = socket.inet_pton(socket.AF_INET6, n["parent"]) ++ endfix = binascii.hexlify(parent[-4:]) ++ for k in topology["nodes"]: ++ if k["address"] == endfix: ++ # add an edge ++ edges = edges + [{"from":k["id"],"to":n["id"]}] ++ if k["level"] is not -1: ++ n["level"] = k["level"] + 1 ++ break ++ + topology["edges"] = edges + print "Sending json: ",json.dumps({'topology':topology}) + ws.sendMessage(json.dumps({'topology':topology})) +diff --git a/examples/sparrow/www/index.html b/examples/sparrow/www/index.html +index 685fb64..631f597 100644 +--- a/examples/sparrow/www/index.html ++++ b/examples/sparrow/www/index.html +@@ -292,6 +292,9 @@ function getTypeButtons(type, address) { + ' ' + + ' ' + + ''; ++ } else { ++ buttons = buttons + ++ ' '; + } + return buttons; + } +@@ -305,6 +308,14 @@ function temp_read(address) { + doSend("tlvtemp " + address); + } + ++function coap_led_control(address, led, val) { ++ doSend("coapled " + address + " " + led + " " + val); ++} ++ ++function coap_temp_read(address) { ++ doSend("coaptemp " + address); ++} ++ + function handleEvent(json) { + if (json.event.type == "discovery") { + addr = json.event.address.replace(new RegExp(":", 'g'), "\\:"); +@@ -563,6 +574,7 @@ function updateRSSI(rssi) { +
  • Javascript-surface-plot by Greg Ross (New BSD License) +
  • Vis.js for Network Topology Graphs (Apache 2.0 and MIT License) +
  • SimpleWebSocketServer - Python library by Dave Pallot (MIT License) ++
  • libcoap - a CoAP library by Olaf Bergmann (BSD License) + + + +diff --git a/products/sparrow-border-router/project-conf.h b/products/sparrow-border-router/project-conf.h +index 0921f13..1a8f32e 100644 +--- a/products/sparrow-border-router/project-conf.h ++++ b/products/sparrow-border-router/project-conf.h +@@ -88,6 +88,8 @@ + #undef WEBSERVER_CONF_CFS_CONNS + #define WEBSERVER_CONF_CFS_CONNS 2 + ++#define WEBSERVER 1 ++ + #define CMD_CONF_OUTPUT border_router_cmd_output + #define CMD_CONF_ERROR border_router_cmd_error + +@@ -100,6 +102,7 @@ + /* Configure DAO routes to have a lifetime of 30 x 60 seconds */ + #define RPL_CONF_DEFAULT_LIFETIME_UNIT 60 + #define RPL_CONF_DEFAULT_LIFETIME 30 ++#define RPL_CONF_WITH_DAO_ACK 1 + + #undef NBR_TABLE_CONF_MAX_NEIGHBORS + #define NBR_TABLE_CONF_MAX_NEIGHBORS 1000 +diff --git a/tools/sparrow/deviceserver.py b/tools/sparrow/deviceserver.py +index 754e872..3796ad5 100755 +--- a/tools/sparrow/deviceserver.py ++++ b/tools/sparrow/deviceserver.py +@@ -595,6 +595,7 @@ class DeviceServer: + + p = re.compile(" ([a-fA-F0-9:]+)(/| prefixlen )") + m = p.search(output) ++ return default_host + if m: + return m.group(1) + else: +-- +2.11.0 + diff --git a/samples/net/rpl-node/Makefile b/samples/net/rpl-node/Makefile new file mode 100644 index 0000000000000..7d0723ff32823 --- /dev/null +++ b/samples/net/rpl-node/Makefile @@ -0,0 +1,15 @@ +# Makefile - RPL node test application + +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +BOARD ?= quark_se_c1000_devboard +CONF_FILE ?= prj.conf + +include $(ZEPHYR_BASE)/Makefile.inc +include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack + +# QEMU_EXTRA_FLAGS = -serial unix:/tmp/bt-server-bredr diff --git a/samples/net/rpl-node/README.rst b/samples/net/rpl-node/README.rst new file mode 100644 index 0000000000000..27663824dcb7d --- /dev/null +++ b/samples/net/rpl-node/README.rst @@ -0,0 +1,73 @@ +.. _rpl-node-sample: + +RPL node +########### + +Overview +******** + +This sample builds a simple RPL node and shows how to join into an RPL +mesh network. + +This sample assumes that your chosen platform has networking support. +Some code configuration adjustments may be needed. + +The sample will listen for RPL multicast messages and joins with the RPL +Border Router node in DAG network. + +The sample exports the following resources through a CoAP server role: + +.. code-block:: none + + /led + /ipv6/neighbors + /rpl-info + /rpl-info/parent + /rpl-info/rank + /rpl-info/link-metric + +These resources allow you to toggle an on-board LED (if available) and build +the RPL mesh network topology from node RPL information. + +Building And Running +******************** + +If you're using a Sparrow border router, follow the steps below to build and +run Sparrow BR. (Sparrow has its own TLV mechanism to build topology that +Zephyr doesn't support.) A patch is provided in the sample folder to to support +building topology with CoAP-based responses. + +Running Sparrow BR +================== + +.. code-block:: console + + git clone https://github.com/sics-iot/sparrow.git + cd sparrow + git am 0001-Added-CoAP-support-for-Sparrow-Border-Router.patch + cd products/sparrow-border-router + sudo make connect-high PORT=/dev/ttyACM0 + +If your PC is using an http proxy, you should unset it for this sample. +Wait until the border router is up and running. The python script used below +will run a web-based UI. + +.. code-block:: console + + cd examples/sparrow + ./wsdemoserver.py + +Wait until you see "Connected" message on console. Unset proxy in browser +and open 127.0.0.1:8000. + +Running RPL node +================ + +To build and run RPL node, follow the below steps to build and install +it on IEEE 802.15.4 radio supported board. + +.. code-block:: console + + $ make pristine and make flash + +Wait until the RPL node joins with Border-Router and updates the list in the web UI. diff --git a/samples/net/rpl-node/prj.conf b/samples/net/rpl-node/prj.conf new file mode 100644 index 0000000000000..02fde29c40b03 --- /dev/null +++ b/samples/net/rpl-node/prj.conf @@ -0,0 +1,88 @@ +CONFIG_ARC_INIT=n + +CONFIG_NETWORKING=y + +CONFIG_RANDOM_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y + +CONFIG_NET_PKT_RX_COUNT=15 +CONFIG_NET_PKT_TX_COUNT=15 +CONFIG_NET_BUF_RX_COUNT=25 +CONFIG_NET_BUF_TX_COUNT=25 + +CONFIG_NET_IPV6=y +CONFIG_NET_IPV6_RA_RDNSS=n +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 +CONFIG_NET_IF_IPV6_PREFIX_COUNT=5 + +CONFIG_NET_IPV6_ND=n +CONFIG_NET_IPV6_DAD=y +CONFIG_NET_IPV6_MLD=n + +CONFIG_NET_UDP=y + +CONFIG_NET_6LO=y +CONFIG_NET_6LO_CONTEXT=y +CONFIG_NET_MAX_6LO_CONTEXTS=3 + +CONFIG_NET_LOG=y +CONFIG_SYS_LOG_SHOW_COLOR=y +CONFIG_SYS_LOG_NET_LEVEL=4 +CONFIG_INIT_STACKS=y +CONFIG_PRINTK=y + +CONFIG_NET_BUF_LOG=n +CONFIG_SYS_LOG_NET_BUF_LEVEL=1 + +CONFIG_GPIO=y + +CONFIG_COAP=y +CONFIG_COAP_WELL_KNOWN_BLOCK_WISE=y +CONFIG_COAP_WELL_KNOWN_BLOCK_WISE_SIZE=64 +CONFIG_NET_DEBUG_COAP=y + +CONFIG_NET_RPL=y +CONFIG_NET_RPL_PROBING=y +CONFIG_NET_RPL_STATS=y +CONFIG_NET_RPL_MIN_HOP_RANK_INC=128 + +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_L2_IEEE802154_FRAGMENT=y +CONFIG_IEEE802154_CC2520=y +CONFIG_IEEE802154_CC2520_AUTO_ACK=n + +# IP core debug +CONFIG_NET_DEBUG_CORE=y +CONFIG_NET_DEBUG_IPV6_NBR_CACHE=y +CONFIG_NET_DEBUG_IPV6=y +CONFIG_NET_DEBUG_NET_BUF=n +CONFIG_NET_DEBUG_UTILS=n +CONFIG_NET_DEBUG_IF=y +CONFIG_NET_DEBUG_ICMPV6=y +CONFIG_NET_DEBUG_CONN=n +CONFIG_NET_DEBUG_UDP=n +CONFIG_NET_DEBUG_CONTEXT=n +CONFIG_NET_DEBUG_ROUTE=y +CONFIG_NET_DEBUG_6LO=n +CONFIG_NET_DEBUG_RPL=y + +CONFIG_NET_STATISTICS=n +CONFIG_NET_STATISTICS_PERIODIC_OUTPUT=n + +CONFIG_NET_SHELL=y + +# L2 Debug +CONFIG_NET_DEBUG_L2_IEEE802154=n +CONFIG_NET_DEBUG_L2_IEEE802154_FRAGMENT=n +CONFIG_SYS_LOG_IEEE802154_DRIVER_LEVEL=0 + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_IEEE802154_CHANNEL=18 +CONFIG_NET_APP_IEEE802154_DEV_NAME="cc2520" + +CONFIG_IEEE802154_CC2520_RANDOM_MAC=n +CONFIG_IEEE802154_CC2520_MAC4=0x00 +CONFIG_IEEE802154_CC2520_MAC5=0x00 +CONFIG_IEEE802154_CC2520_MAC6=0x00 +CONFIG_IEEE802154_CC2520_MAC7=0x03 diff --git a/samples/net/rpl-node/src/Makefile b/samples/net/rpl-node/src/Makefile new file mode 100644 index 0000000000000..5887fb498172b --- /dev/null +++ b/samples/net/rpl-node/src/Makefile @@ -0,0 +1,9 @@ +ccflags-y += -I${ZEPHYR_BASE}/subsys/net/ip +obj-y = main.o + +ifeq ($(CONFIG_NET_L2_IEEE802154), y) +ccflags-y +=-I${ZEPHYR_BASE}/samples/net/common/ +ifeq ($(CONFIG_NET_APP_SETTINGS), y) +obj-y += ../../common/ieee802154_settings.o +endif +endif diff --git a/samples/net/rpl-node/src/main.c b/samples/net/rpl-node/src/main.c new file mode 100644 index 0000000000000..0668a8e93537d --- /dev/null +++ b/samples/net/rpl-node/src/main.c @@ -0,0 +1,1165 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if 1 +#define SYS_LOG_DOMAIN "rpl-node" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include <6lo.h> +#include + +#include + +#include +#include + +#if defined(CONFIG_NET_L2_IEEE802154) +#include +#endif + +#define MY_COAP_PORT 5683 + +#define ALL_NODES_LOCAL_COAP_MCAST \ + { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfd } } } + +#if defined(LED0_GPIO_PORT) +#define LED_GPIO_NAME LED0_GPIO_PORT +#define LED_PIN LED0_GPIO_PIN +#else +#define LED_GPIO_NAME "(fail)" +#define LED_PIN 0 +#endif + +#define RPL_MAX_REPLY 75 + +#define PKT_WAIT_TIME K_SECONDS(1) + +static struct net_context *context; +static struct device *led0; +static const u8_t plain_text_format; + +/* LED */ +static const char led_on[] = "LED0 ON"; +static const char led_off[] = "LED0 OFF"; +static const char led_toggle_on[] = "LED Toggle ON"; +static const char led_toggle_off[] = "LED Toggle OFF"; + +static bool fake_led; + +/* RPL */ +static const char rpl_parent[] = "RPL Parent:"; +static const char rpl_no_parent[] = "No parent yet"; + +static const char rpl_rank[] = "RPL Rank:"; +static const char rpl_no_rank[] = "No rank yet"; + +static const char rpl_link[] = "Link Metric:"; +static const char rpl_no_link[] = "No link metric yet"; + +static const char ipv6_no_nbr[] = "No IPv6 Neighbors"; + +static void get_from_ip_addr(struct coap_packet *cpkt, + struct sockaddr_in6 *from) +{ + struct net_udp_hdr hdr, *udp_hdr; + + udp_hdr = net_udp_get_hdr(cpkt->pkt, &hdr); + if (!udp_hdr) { + return; + } + + net_ipaddr_copy(&from->sin6_addr, &NET_IPV6_HDR(cpkt->pkt)->src); + from->sin6_port = udp_hdr->src_port; + from->sin6_family = AF_INET6; +} + +static void send_error_response(struct coap_resource *resource, + struct coap_packet *request, + struct sockaddr_in6 *from) +{ + struct net_context *context; + struct coap_packet response; + struct net_pkt *pkt; + struct net_buf *frag; + u16_t id; + int r; + + id = coap_header_get_id(request); + context = net_pkt_context(request->pkt); + + pkt = net_pkt_get_tx(context, PKT_WAIT_TIME); + if (!pkt) { + return; + } + + frag = net_pkt_get_data(context, PKT_WAIT_TIME); + if (!frag) { + net_pkt_unref(pkt); + return; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_BAD_REQUEST, id); + if (r < 0) { + net_pkt_unref(pkt); + return; + } + + r = net_context_sendto(pkt, (const struct sockaddr *)from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } +} + +static int well_known_core_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct coap_packet response; + struct sockaddr_in6 from; + struct net_pkt *pkt; + struct net_buf *frag; + int r; + + NET_DBG(""); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_well_known_core_get(resource, request, &response, pkt); + if (r < 0) { + net_pkt_unref(response.pkt); + send_error_response(resource, request, &from); + + return r; + } + + get_from_ip_addr(request, &from); + + r = net_context_sendto(response.pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(response.pkt); + } + + return r; +} + +static bool read_led(void) +{ + u32_t led = 0; + int r; + + if (!led0) { + return fake_led; + } + + r = gpio_pin_read(led0, LED_PIN, &led); + if (r < 0) { + return false; + } + + return led; +} + +static void write_led(bool led) +{ + if (!led0) { + fake_led = led; + return; + } + + gpio_pin_write(led0, LED_PIN, led); +} + +static int led_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + const char *str; + u16_t len, id; + int r; + + NET_DBG(""); + + id = coap_header_get_id(request); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + if (read_led()) { + str = led_on; + len = sizeof(led_on); + } else { + str = led_off; + len = sizeof(led_off); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +static int led_post(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + const char *str; + u16_t offset; + u16_t len; + u16_t id; + u32_t led; + u8_t payload; + int r; + + NET_DBG(""); + + led = 0; + + frag = net_frag_skip(request->frag, request->offset, &offset, + request->hdr_len + request->opt_len); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + + frag = net_frag_read_u8(frag, offset, &offset, &payload); + if (!frag && offset == 0xffff) { + printk("packet without payload, so toggle the led"); + + led = read_led(); + led = !led; + } else { + if (payload == 0x31) { + led = 1; + } + } + + write_led(led); + + id = coap_header_get_id(request); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + if (led) { + str = led_toggle_on; + len = sizeof(led_toggle_on); + } else { + str = led_toggle_off; + len = sizeof(led_toggle_off); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +static int led_put(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + const char *str; + u16_t offset; + u16_t len; + u16_t id; + u32_t led; + u8_t payload; + int r; + + NET_DBG(""); + + led = 0; + + frag = net_frag_skip(request->frag, request->offset, &offset, + request->hdr_len + request->opt_len); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + + frag = net_frag_read_u8(frag, offset, &offset, &payload); + if (!frag && offset == 0xffff) { + printk("packet without payload, so toggle the led"); + + led = read_led(); + led = !led; + } else { + if (payload == 0x31) { + led = 1; + } + } + + write_led(led); + + id = coap_header_get_id(request); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_RESPONSE_CODE_CHANGED, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + if (led) { + str = led_on; + len = sizeof(led_on); + } else { + str = led_off; + len = sizeof(led_off); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +struct ipv6_nbr_str { + char str[(sizeof("xx:xx:xx:xx:xx:xx:xx:xx") * + CONFIG_NET_IPV6_MAX_NEIGHBORS) + + CONFIG_NET_IPV6_MAX_NEIGHBORS]; + u8_t len; +}; + +static void ipv6_nbr_cb(struct net_nbr *nbr, void *user_data) +{ + struct ipv6_nbr_str *nbr_str = user_data; + char temp[sizeof("xx:xx:xx:xx:xx:xx:xx:xx") + sizeof("\n")]; + + snprintk(temp, sizeof(temp), "%s\n", + net_sprint_ipv6_addr(&net_ipv6_nbr_data(nbr)->addr)); + + memcpy(nbr_str->str + nbr_str->len, temp, strlen(temp)); + nbr_str->len += strlen(temp); +} + +/* IPv6 Neighbors */ +static int ipv6_neighbors_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + struct ipv6_nbr_str nbr_str; + u8_t token[8]; + const char *str; + u16_t len, id; + u8_t tkl; + int r; + + NET_DBG(""); + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, (u8_t *)token); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + nbr_str.len = 0; + net_ipv6_nbr_foreach(ipv6_nbr_cb, &nbr_str); + + if (nbr_str.len) { + str = nbr_str.str; + len = nbr_str.len; + } else { + str = ipv6_no_nbr; + len = strlen(ipv6_no_nbr); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +/* RPL Information */ +static int rpl_info_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_rpl_instance *rpl; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + struct in6_addr *parent; + struct net_nbr *nbr; + const char *str; + u8_t token[8]; + u16_t len, id; + u16_t out_len; + u8_t tkl; + int r; + char out[RPL_MAX_REPLY]; + + NET_DBG(""); + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, (u8_t *)token); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + rpl = net_rpl_get_default_instance(); + if (rpl && rpl->current_dag && rpl->current_dag->preferred_parent) { + nbr = net_rpl_get_nbr(rpl->current_dag->preferred_parent); + } else { + nbr = NULL; + } + + /* Write all RPL info in JSON format */ + snprintk(out, sizeof(out) - 1, "parent-"); + out_len = strlen(out); + + if (!rpl || !rpl->current_dag || !rpl->current_dag->preferred_parent) { + snprintk(&out[out_len], sizeof(out) - out_len - 1, "None"); + } else { + parent = net_rpl_get_parent_addr(net_pkt_iface(pkt), + rpl->current_dag->preferred_parent); + snprintk(&out[out_len], sizeof(out) - out_len - 1, "%s", + net_sprint_ipv6_addr(parent)); + } + + out_len = strlen(out); + + snprintk(&out[out_len], sizeof(out) - out_len - 1, "\nrank-"); + out_len = strlen(out); + + if (!rpl || !rpl->current_dag) { + snprintk(&out[out_len], sizeof(out) - out_len - 1, "inf"); + } else { + snprintk(&out[out_len], sizeof(out) - out_len - 1, "%u.%02u", + rpl->current_dag->rank / NET_RPL_MC_ETX_DIVISOR, + (100 * (rpl->current_dag->rank % + NET_RPL_MC_ETX_DIVISOR)) / + NET_RPL_MC_ETX_DIVISOR); + } + + out_len = strlen(out); + + snprintk(&out[out_len], sizeof(out) - out_len - 1, "\nlinkmetric-"); + out_len = strlen(out); + + if (!nbr) { + snprintk(&out[out_len], sizeof(out) - out_len - 1, "inf"); + } else { + snprintk(&out[out_len], sizeof(out) - out_len - 1, "%u.%02u", + (net_ipv6_nbr_data(nbr)->link_metric) / + NET_RPL_MC_ETX_DIVISOR, + (100 * ((net_ipv6_nbr_data(nbr)->link_metric) % + NET_RPL_MC_ETX_DIVISOR)) / + NET_RPL_MC_ETX_DIVISOR); + } + + out_len = strlen(out); + + str = out; + len = out_len; + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +/* RPL Parent */ +static int rpl_parent_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_rpl_instance *rpl; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + struct in6_addr *parent; + const char *str; + u8_t token[8]; + u16_t len, id; + u8_t tkl; + int r; + char out[RPL_MAX_REPLY]; + + NET_DBG(""); + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, (u8_t *)token); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + rpl = net_rpl_get_default_instance(); + if (!rpl || !rpl->current_dag || !rpl->current_dag->preferred_parent) { + str = rpl_no_parent; + len = sizeof(rpl_no_parent); + + } else { + parent = net_rpl_get_parent_addr(net_pkt_iface(pkt), + rpl->current_dag->preferred_parent); + snprintk(out, sizeof(out), "%s %s", rpl_parent, + net_sprint_ipv6_addr(parent)); + str = out; + len = strlen(out); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +/* RPL Rank */ +static int rpl_rank_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_rpl_instance *rpl; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + const char *str; + u8_t token[8]; + u16_t len, id; + u8_t tkl; + int r; + char out[RPL_MAX_REPLY]; + + NET_DBG(""); + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, (u8_t *)token); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + rpl = net_rpl_get_default_instance(); + if (!rpl || !rpl->current_dag) { + str = rpl_no_rank; + len = sizeof(rpl_no_rank); + + } else { + snprintk(out, sizeof(out), "%s %u.%02u", rpl_rank, + rpl->current_dag->rank / NET_RPL_MC_ETX_DIVISOR, + (100 * (rpl->current_dag->rank % + NET_RPL_MC_ETX_DIVISOR)) / + NET_RPL_MC_ETX_DIVISOR); + str = out; + len = strlen(out); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +/* RPL Link Metric */ +static int rpl_link_metric_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct net_rpl_instance *rpl; + struct net_pkt *pkt; + struct net_buf *frag; + struct sockaddr_in6 from; + struct coap_packet response; + struct net_nbr *nbr; + const char *str; + u8_t token[8]; + u16_t len, id; + u8_t tkl; + int r; + char out[RPL_MAX_REPLY]; + + NET_DBG(""); + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, (u8_t *)token); + + pkt = net_pkt_get_tx(context, K_FOREVER); + if (!pkt) { + return -ENOMEM; + } + + frag = net_pkt_get_data(context, K_FOREVER); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, + &plain_text_format, + sizeof(plain_text_format)); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + rpl = net_rpl_get_default_instance(); + if (rpl && rpl->current_dag && + rpl->current_dag->preferred_parent) { + nbr = net_rpl_get_nbr(rpl->current_dag->preferred_parent); + if (nbr) { + snprintk(out, sizeof(out), "%s %u.%02u", rpl_link, + (net_ipv6_nbr_data(nbr)->link_metric) / + NET_RPL_MC_ETX_DIVISOR, + (100 * ((net_ipv6_nbr_data(nbr)->link_metric) % + NET_RPL_MC_ETX_DIVISOR)) / + NET_RPL_MC_ETX_DIVISOR); + + str = out; + len = strlen(out); + } else { + str = rpl_no_link; + len = sizeof(rpl_no_link); + } + } else { + str = rpl_no_link; + len = sizeof(rpl_no_link); + } + + r = coap_packet_append_payload_marker(&response); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + r = coap_packet_append_payload(&response, (u8_t *)str, len); + if (r < 0) { + net_pkt_unref(pkt); + return -EINVAL; + } + + get_from_ip_addr(request, &from); + r = net_context_sendto(pkt, (const struct sockaddr *)&from, + sizeof(struct sockaddr_in6), + NULL, 0, NULL, NULL); + if (r < 0) { + net_pkt_unref(pkt); + } + + return r; +} + +static const char * const led_default_path[] = { "led", "0", NULL }; +static const char * const led_default_attributes[] = { + "title=\"LED0: led=toggle ?len=0..\"", + "rt=\"Text\"", + NULL }; + +static const char * const ipv6_neighbors_default_path[] = { "ipv6", + "neighbors", + NULL }; +static const char * const ipv6_neighbors_default_attributes[] = { + "title=\"IPv6 Neighbors\"", + "rt=Text", + NULL }; + +static const char * const rpl_info_default_path[] = { "rpl-info", NULL }; +static const char * const rpl_info_default_attributes[] = { + "title=\"RPL Information\"", + "rt=Text", + NULL }; + +static const char * const rpl_parent_default_path[] = { "rpl-info", + "parent", + NULL }; +static const char * const rpl_parent_default_attributes[] = { + "title=\"RPL Parent\"", + "rt=Text", + NULL }; + +static const char * const rpl_rank_default_path[] = { "rpl-info", + "rank", + NULL }; +static const char * const rpl_rank_default_attributes[] = { + "title=\"RPL Rank\"", + "rt=Text", + NULL }; + +static const char * const rpl_link_default_path[] = { "rpl-info", + "link-metric", + NULL }; +static const char * const rpl_link_default_attributes[] = { + "title=\"RPL Link Metric\"", + "rt=Text", + NULL }; + +static struct coap_resource resources[] = { + { .get = well_known_core_get, + .post = NULL, + .put = NULL, + .path = COAP_WELL_KNOWN_CORE_PATH, + .user_data = NULL, + }, + { .get = led_get, + .post = led_post, + .put = led_put, + .path = led_default_path, + .user_data = &((struct coap_core_metadata) { + .attributes = led_default_attributes, + }), + }, + { .get = ipv6_neighbors_get, + .post = NULL, + .put = NULL, + .path = ipv6_neighbors_default_path, + .user_data = &((struct coap_core_metadata) { + .attributes = ipv6_neighbors_default_attributes, + }), + }, + { .get = rpl_info_get, + .post = NULL, + .put = NULL, + .path = rpl_info_default_path, + .user_data = &((struct coap_core_metadata) { + .attributes = rpl_info_default_attributes, + }), + }, + { .get = rpl_parent_get, + .post = NULL, + .put = NULL, + .path = rpl_parent_default_path, + .user_data = &((struct coap_core_metadata) { + .attributes = rpl_parent_default_attributes, + }), + }, + { .get = rpl_rank_get, + .post = NULL, + .put = NULL, + .path = rpl_rank_default_path, + .user_data = &((struct coap_core_metadata) { + .attributes = rpl_rank_default_attributes, + }), + }, + { .get = rpl_link_metric_get, + .post = NULL, + .put = NULL, + .path = rpl_link_default_path, + .user_data = &((struct coap_core_metadata) { + .attributes = rpl_link_default_attributes, + }), + }, + { }, +}; + +static void udp_receive(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data) +{ + struct coap_packet request; + struct coap_option options[16] = { 0 }; + u8_t opt_num = 16; + int r; + + r = coap_packet_parse(&request, pkt, options, opt_num); + if (r < 0) { + NET_ERR("Invalid data received (%d)\n", r); + goto end; + } + + r = coap_handle_request(&request, resources, options, opt_num); + if (r < 0) { + NET_ERR("No handler for such request (%d)\n", r); + } + +end: + net_pkt_unref(pkt); +} + +static bool join_coap_multicast_group(void) +{ + 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 *iface; + + iface = net_if_get_default(); + if (!iface) { + NET_ERR("Could not get te default interface\n"); + return false; + } + + 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 init_app(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_IEEE802154) + if (ieee802154_sample_setup()) { + NET_ERR("IEEE 802.15.4 setup failed"); + return; + } +#endif + + led0 = device_get_binding(LED_GPIO_NAME); + if (led0) { + gpio_pin_configure(led0, LED_PIN, GPIO_DIR_OUT); + } else { + NET_WARN("Failed to bind '%s'" + "fake_led will provide dummpy replies", + LED_GPIO_NAME); + } + + if (!join_coap_multicast_group()) { + NET_ERR("Could not join CoAP multicast group"); + return; + } + + r = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context); + if (r) { + NET_ERR("Could not get an UDP context"); + return; + } + + r = net_context_bind(context, (struct sockaddr *) &any_addr, + sizeof(any_addr)); + if (r) { + NET_ERR("Could not bind the context"); + return; + } + + r = net_context_recv(context, udp_receive, 0, NULL); + if (r) { + NET_ERR("Could not receive in the context"); + } +} + +void main(void) +{ + NET_DBG("Start Demo"); + + init_app(); +} diff --git a/samples/net/rpl-node/testcase.ini b/samples/net/rpl-node/testcase.ini new file mode 100644 index 0000000000000..9deee8c5b5e8a --- /dev/null +++ b/samples/net/rpl-node/testcase.ini @@ -0,0 +1,5 @@ +[test] +tags = net zoap +build_only = true +arch_whitelist = x86 +platform_exclude = quark_d2000_crb # not enough RAM diff --git a/samples/net/zoap_server/src/zoap-server.c b/samples/net/zoap_server/src/zoap-server.c deleted file mode 100644 index b251672ce0590..0000000000000 --- a/samples/net/zoap_server/src/zoap-server.c +++ /dev/null @@ -1,1289 +0,0 @@ -/* - * 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 -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#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 u8_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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - u8_t tkl, code, type; - const u8_t *token; - u16_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, code, type, tkl; - const u8_t *token; - u16_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, code, type, tkl; - const u8_t *token; - u16_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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++) { - r = zoap_add_option(&response, ZOAP_OPTION_LOCATION_PATH, - *p, strlen(*p)); - if (r < 0) { - return -EINVAL; - } - } - - return net_context_sendto(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, code, type, tkl; - const u8_t *token; - u16_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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++) { - r = zoap_add_option(&response, ZOAP_OPTION_LOCATION_QUERY, - *p, strlen(*p)); - if (r < 0) { - return -EINVAL; - } - } - - return net_context_sendto(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - const u8_t *token; - u8_t *payload, code, type; - u16_t len, id; - u8_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, code, type, tkl; - const u8_t *token; - u16_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - struct zoap_pending *pending; - u8_t *payload, code, type, tkl; - const u8_t *token; - u16_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; - } - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, from, sizeof(struct sockaddr_in6), - NULL, 0, NULL, NULL); - if (r < 0) { - return -EINVAL; - } - -done: - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - const u8_t *token; - u8_t *payload, code, type; - u16_t len, id, size; - u8_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(&response, &ctx); - if (!r) { - /* Will return 0 when it's the last block. */ - memset(&ctx, 0, sizeof(ctx)); - } - - return net_context_sendto(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - const u8_t *token; - u8_t *payload, code, type; - u16_t len, id; - u8_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 */ - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - const u8_t *token; - u8_t *payload, code, type; - u16_t len, id; - u8_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"); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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, u16_t age, - socklen_t addrlen, u16_t id, - const u8_t *token, u8_t tkl, - bool is_response) -{ - struct zoap_packet response; - struct zoap_pending *pending; - struct net_pkt *pkt; - struct net_buf *frag; - u8_t *payload, type = ZOAP_TYPE_CON; - u16_t len; - int r; - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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) { - r = zoap_add_option_int(&response, ZOAP_OPTION_OBSERVE, age); - if (r < 0) { - return -EINVAL; - } - } - - 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(pkt, 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 u8_t *token; - u8_t code, type; - u16_t id; - u8_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_pkt *pkt; - struct net_buf *frag; - struct zoap_packet response; - u8_t *payload, tkl; - const u8_t *token; - u16_t len, id; - int r; - - id = zoap_header_get_id(request); - token = zoap_header_get_token(request, &tkl); - - pkt = net_pkt_get_tx(context, K_FOREVER); - frag = net_pkt_get_data(context, K_FOREVER); - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - 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(pkt, 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_pkt *pkt, - int status, - void *user_data) -{ - struct zoap_packet request; - struct zoap_pending *pending; - struct sockaddr_in6 from; - struct net_udp_hdr hdr, *udp_hdr; - int r, header_len; - - net_ipaddr_copy(&from.sin6_addr, &NET_IPV6_HDR(pkt)->src); - - udp_hdr = net_udp_get_hdr(pkt, &hdr); - if (!udp_hdr) { - printk("Invalid UDP data received\n"); - net_pkt_unref(pkt); - return; - } - - from.sin6_port = udp_hdr->src_port; - from.sin6_family = AF_INET6; - - /* - * zoap expects that buffer->data starts at the - * beginning of the CoAP header - */ - header_len = net_pkt_appdata(pkt) - pkt->frags->data; - net_buf_pull(pkt->frags, header_len); - - r = zoap_packet_parse(&request, pkt); - if (r < 0) { - NET_ERR("Invalid data received (%d)\n", r); - net_pkt_unref(pkt); - return; - } - - pending = zoap_pending_received(&request, pendings, - NUM_PENDINGS); - if (pending) { - net_pkt_unref(pkt); - 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_pkt_unref(pkt); - - 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); - if (!ifaddr) { - NET_ERR("Could not add unicast address to interface"); - return false; - } - - 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->pkt, &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 (!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; - } -} diff --git a/subsys/net/lib/Kbuild b/subsys/net/lib/Kbuild index cc297ac8d350a..9104db2eccee1 100644 --- a/subsys/net/lib/Kbuild +++ b/subsys/net/lib/Kbuild @@ -1,5 +1,6 @@ obj-$(CONFIG_NET_SOCKETS) += sockets/ obj-$(CONFIG_ZOAP) += zoap/ +obj-$(CONFIG_COAP) += coap/ obj-$(CONFIG_DNS_RESOLVER) += dns/ obj-$(CONFIG_MQTT_LIB) += mqtt/ obj-$(CONFIG_HTTP) += http/ diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index ea638f4d6d280..18b188f2b5e38 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -8,6 +8,8 @@ menu "Network Protocols" source "subsys/net/lib/zoap/Kconfig" +source "subsys/net/lib/coap/Kconfig" + source "subsys/net/lib/dns/Kconfig" source "subsys/net/lib/mqtt/Kconfig" diff --git a/subsys/net/lib/Makefile b/subsys/net/lib/Makefile index d90e6d6b24976..af0f21b6e704d 100644 --- a/subsys/net/lib/Makefile +++ b/subsys/net/lib/Makefile @@ -6,6 +6,10 @@ ifdef CONFIG_ZOAP include $(srctree)/subsys/net/lib/zoap/Makefile endif +ifdef CONFIG_COAP +include $(srctree)/subsys/net/lib/coap/Makefile +endif + ifdef CONFIG_DNS_RESOLVER include $(srctree)/subsys/net/lib/dns/Makefile endif diff --git a/subsys/net/lib/coap/Kbuild b/subsys/net/lib/coap/Kbuild new file mode 100644 index 0000000000000..825f8bfcc0abe --- /dev/null +++ b/subsys/net/lib/coap/Kbuild @@ -0,0 +1,4 @@ +subdir-ccflags-y +=-I$(srctree)/subsys/net/lib/coap +ccflags-y += -I${srctree}/net/ip + +obj-y := coap.o coap_link_format.o diff --git a/subsys/net/lib/coap/Kconfig b/subsys/net/lib/coap/Kconfig new file mode 100644 index 0000000000000..15914994778cd --- /dev/null +++ b/subsys/net/lib/coap/Kconfig @@ -0,0 +1,70 @@ +# Kconfig - CoAP implementation for Zephyr + +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config COAP + bool + prompt "CoAP Support" + default n + help + This option enables the CoAP implementation. + +# This setting is only used by unit test. Do not enable it in applications +config COAP_TEST_API_ENABLE + bool "Enable test API for CoAP unit tests" + default n + depends on COAP + help + Do not enable this for normal use. + +config COAP_WELL_KNOWN_BLOCK_WISE + bool + prompt "CoAP ./well-known/core services block wise support" + default n + depends on COAP + help + This option enables the block wise support of CoAP response + to ./well-known/core request. Without this option all resource's + information will be sent in a single IP packet (can be multiple + fragments depends on MTU size). This will be useful in mesh kind + of networks. + +config COAP_WELL_KNOWN_BLOCK_WISE_SIZE + int + prompt "CoAP ./well-known/core services block wise support" + default 32 + depends on COAP_WELL_KNOWN_BLOCK_WISE + help + Maximum size of CoAP block. Valid values are 16, 32, 64, 128, + 256, 512 and 1024. + +config COAP_EXTENDED_OPTIONS_LEN + bool "Support for CoAP extended options" + default n + depends on COAP + help + This option enables the parsing of extended CoAP options length. + CoAP extended options length can be 2 byte value, which + requires more memory. User can save memory by disabling this. + That means only length of maximum 12 bytes are supported by default. + Enable this if length field going to bigger that 12. + +config COAP_EXTENDED_OPTIONS_LEN_VALUE + int "CoAP extended options length value" + default 13 + depends on COAP_EXTENDED_OPTIONS_LEN + help + This option specifies the maximum value of length field when + COAP_EXTENDED_OPTIONS_LEN is enabled. Define the value according to + user requirement. + +config NET_DEBUG_COAP + bool "Debug COAP" + default n + depends on COAP && NET_LOG + help + Enables CoaP output debug messages diff --git a/subsys/net/lib/coap/Makefile b/subsys/net/lib/coap/Makefile new file mode 100644 index 0000000000000..ff88030c66204 --- /dev/null +++ b/subsys/net/lib/coap/Makefile @@ -0,0 +1 @@ +ZEPHYRINCLUDE += -I$(srctree)/subsys/net/lib/coap diff --git a/subsys/net/lib/coap/coap.c b/subsys/net/lib/coap/coap.c new file mode 100644 index 0000000000000..f6cd750e65aad --- /dev/null +++ b/subsys/net/lib/coap/coap.c @@ -0,0 +1,1445 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_NET_DEBUG_COAP) +#define SYS_LOG_DOMAIN "coap" +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct option_context { + u16_t delta; + u16_t offset; + struct net_buf *frag; +}; + +#define COAP_VERSION 1 + +#define COAP_MARKER 0xFF + +#define BASIC_HEADER_SIZE 4 + +#define PKT_WAIT_TIME K_SECONDS(1) + +static u8_t option_header_get_delta(u8_t opt) +{ + return (opt & 0xF0) >> 4; +} + +static u8_t option_header_get_len(u8_t opt) +{ + return opt & 0x0F; +} + +static void option_header_set_delta(u8_t *opt, u8_t delta) +{ + *opt = (delta & 0xF) << 4; +} + +static void option_header_set_len(u8_t *opt, u8_t len) +{ + *opt |= (len & 0xF); +} + +static int get_coap_packet_len(struct net_pkt *pkt) +{ + u16_t len; + + /* TODO: verify with smaller packets */ + len = net_pkt_get_len(pkt) + - net_pkt_ip_hdr_len(pkt) + - net_pkt_ipv6_ext_len(pkt) + - NET_UDPH_LEN; + + return len; +} + +static int decode_delta(u16_t num, struct option_context *context, + u16_t *decoded) +{ + int hdrlen = 0; + + if (num == 13) { + u8_t val; + + context->frag = net_frag_read_u8(context->frag, context->offset, + &context->offset, &val); + if (!context->frag && context->offset == 0xffff) { + return -EINVAL; + } + + num = val + 13; + hdrlen += 1; + } else if (num == 14) { + u16_t val; + + context->frag = net_frag_read_be16(context->frag, + context->offset, + &context->offset, &val); + if (!context->frag && context->offset == 0xffff) { + return -EINVAL; + } + + num = val + 269; + hdrlen += 2; + } else if (num == 15) { + return -EINVAL; + } + + *decoded = num; + + return hdrlen; +} + +static int parse_option(const struct coap_packet *cpkt, + struct option_context *context, + struct coap_option *option) +{ + u16_t opt_len; + u16_t delta; + u16_t len; + u8_t opt; + int r; + + context->frag = net_frag_read_u8(context->frag, context->offset, + &context->offset, &opt); + if (!context->frag && context->offset == 0xffff) { + return -EINVAL; + } + + opt_len = 1; + + /* This indicates that options have ended */ + if (context->offset == 0 || opt == COAP_MARKER) { + return 0; + } + + delta = option_header_get_delta(opt); + len = option_header_get_len(opt); + + /* In case 'delta' doesn't fit the option fixed header. */ + r = decode_delta(delta, context, &delta); + if (r < 0) { + return -EINVAL; + } + + opt_len += r; + + /* In case 'len' doesn't fit the option fixed header. */ + r = decode_delta(len, context, &len); + if (r < 0) { + return -EINVAL; + } + + opt_len += r + len; + + if (option) { + if (delta) { + option->delta = context->delta + delta; + } else { + option->delta = context->delta; + } + + option->len = len; + context->frag = net_frag_read(context->frag, context->offset, + &context->offset, len, + &option->value[0]); + if (!context->frag && context->offset == 0xffff) { + return -EINVAL; + } + + } else { + context->frag = net_frag_skip(context->frag, context->offset, + &context->offset, len); + if (!context->frag && context->offset == 0xffff) { + return -EINVAL; + } + } + + context->delta += delta; + + return opt_len; +} + +static int parse_options(const struct coap_packet *cpkt, + struct coap_option *options, u8_t opt_num) +{ + struct option_context context = { + .delta = 0, + .frag = NULL, + .offset = 0 + }; + u8_t num; + u16_t opt_len; + int coap_len; + + coap_len = get_coap_packet_len(cpkt->pkt); + if (coap_len < 0) { + return -EINVAL; + } + + /* Skip CoAP header */ + context.frag = net_frag_skip(cpkt->frag, cpkt->offset, + &context.offset, cpkt->hdr_len); + if (!context.frag && context.offset == 0xffff) { + return -EINVAL; + } + + coap_len -= cpkt->hdr_len; + num = 0; + opt_len = 0; + + while (true) { + struct coap_option *option; + int r; + + if (opt_len >= coap_len) { + break; + } + + option = num < opt_num ? &options[num++] : NULL; + r = parse_option(cpkt, &context, option); + if (r < 0) { + return r; + } + + opt_len += r; + + /* Reached End of Options (0xFF), + * r is zero, so increase opt_len + 1. + */ + if (r == 0) { + opt_len++; + break; + } + } + + return opt_len; +} + +static u8_t get_header_tkl(const struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t offset; + u8_t tkl; + + frag = net_frag_read_u8(cpkt->frag, cpkt->offset, &offset, &tkl); + + return tkl & 0xF; +} + +static u16_t get_pkt_len(const struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t len; + + len = cpkt->frag->len - cpkt->offset; + + for (frag = cpkt->frag->frags; frag; frag = frag->frags) { + len += frag->len; + } + + return len; +} + +static int get_header_len(struct coap_packet *cpkt) +{ + u8_t tkl; + u16_t len; + int hdrlen; + + len = get_pkt_len(cpkt); + + hdrlen = BASIC_HEADER_SIZE; + + if (len < hdrlen) { + return -EINVAL; + } + + tkl = get_header_tkl(cpkt); + + /* Token lenghts 9-15 are reserved. */ + if (tkl > 8) { + return -EINVAL; + } + + if (len < hdrlen + tkl) { + return -EINVAL; + } + + cpkt->hdr_len = hdrlen + tkl; + + return 0; +} + +int coap_packet_parse(struct coap_packet *cpkt, struct net_pkt *pkt, + struct coap_option *options, u8_t opt_num) +{ + int ret; + + if (!cpkt || !pkt || !pkt->frags) { + return -EINVAL; + } + + cpkt->pkt = pkt; + cpkt->hdr_len = 0; + cpkt->opt_len = 0; + + cpkt->frag = net_frag_skip(pkt->frags, 0, &cpkt->offset, + net_pkt_ip_hdr_len(pkt) + + NET_UDPH_LEN + + net_pkt_ipv6_ext_len(pkt)); + if (!cpkt->frag && cpkt->offset == 0xffff) { + return -EINVAL; + } + + ret = get_header_len(cpkt); + if (ret < 0) { + return -EINVAL; + } + + ret = parse_options(cpkt, options, opt_num); + if (ret < 0) { + return -EINVAL; + } + + cpkt->opt_len = ret; + + return 0; +} + +int coap_packet_init(struct coap_packet *cpkt, struct net_pkt *pkt, + u8_t ver, u8_t type, u8_t tokenlen, + u8_t *token, u8_t code, u16_t id) +{ + u8_t hdr; + bool res; + + if (!cpkt || !pkt || !pkt->frags) { + return -EINVAL; + } + + memset(cpkt, 0, sizeof(*cpkt)); + cpkt->pkt = pkt; + cpkt->frag = pkt->frags; + cpkt->offset = 0; + cpkt->last_delta = 0; + + hdr = (ver & 0x3) << 6; + hdr |= (type & 0x3) << 4; + hdr |= tokenlen & 0xF; + + net_pkt_append_u8(pkt, hdr); + net_pkt_append_u8(pkt, code); + net_pkt_append_be16(pkt, id); + + if (token && tokenlen) { + res = net_pkt_append_all(pkt, tokenlen, token, PKT_WAIT_TIME); + if (!res) { + return -ENOMEM; + } + } + + /* Header length : (version + type + tkl) + code + id + [token] */ + cpkt->hdr_len = 1 + 1 + 2 + tokenlen; + + return 0; +} + +int coap_pending_init(struct coap_pending *pending, + const struct coap_packet *request, + const struct sockaddr *addr) +{ + memset(pending, 0, sizeof(*pending)); + pending->id = coap_header_get_id(request); + memcpy(&pending->addr, addr, sizeof(*addr)); + + /* Will increase the reference count when the pending is cycled */ + pending->pkt = request->pkt; + + return 0; +} + +struct coap_pending *coap_pending_next_unused( + struct coap_pending *pendings, size_t len) +{ + struct coap_pending *p; + size_t i; + + for (i = 0, p = pendings; i < len; i++, p++) { + if (p->timeout == 0 && !p->pkt) { + return p; + } + } + + return NULL; +} + +struct coap_reply *coap_reply_next_unused( + struct coap_reply *replies, size_t len) +{ + struct coap_reply *r; + size_t i; + + for (i = 0, r = replies; i < len; i++, r++) { + if (!r->reply) { + return r; + } + } + + return NULL; +} + +static inline bool is_addr_unspecified(const struct sockaddr *addr) +{ + if (addr->sa_family == AF_UNSPEC) { + return true; + } + + if (addr->sa_family == AF_INET6) { + return net_is_ipv6_addr_unspecified( + &(net_sin6(addr)->sin6_addr)); + } else if (addr->sa_family == AF_INET) { + return net_sin(addr)->sin_addr.s4_addr32[0] == 0; + } + + return false; +} + +struct coap_observer *coap_observer_next_unused( + struct coap_observer *observers, size_t len) +{ + struct coap_observer *o; + size_t i; + + for (i = 0, o = observers; i < len; i++, o++) { + if (is_addr_unspecified(&o->addr)) { + return o; + } + } + + return NULL; +} + +struct coap_pending *coap_pending_received( + const struct coap_packet *response, + struct coap_pending *pendings, size_t len) +{ + struct coap_pending *p; + u16_t resp_id = coap_header_get_id(response); + size_t i; + + for (i = 0, p = pendings; i < len; i++, p++) { + if (!p->timeout) { + continue; + } + + if (resp_id != p->id) { + continue; + } + + coap_pending_clear(p); + return p; + } + + return NULL; +} + +struct coap_pending *coap_pending_next_to_expire( + struct coap_pending *pendings, size_t len) +{ + struct coap_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 s32_t next_timeout(s32_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 coap_pending_cycle(struct coap_pending *pending) +{ + s32_t old = pending->timeout; + bool cont; + + pending->timeout = next_timeout(pending->timeout); + + /* If the timeout changed, it's not the last, continue... */ + cont = (old != pending->timeout); + if (cont) { + /* When it it is the last retransmission, the buffer + * will be destroyed when it is transmitted. + */ + net_pkt_ref(pending->pkt); + } + + return cont; +} + +void coap_pending_clear(struct coap_pending *pending) +{ + pending->timeout = 0; + net_pkt_unref(pending->pkt); + pending->pkt = NULL; +} + +static bool uri_path_eq(const struct coap_packet *cpkt, + const char * const *path, + struct coap_option *options, + u8_t opt_num) +{ + u8_t i; + u8_t j = 0; + + for (i = 0; i < opt_num && path[j]; i++) { + if (options[i].delta != COAP_OPTION_URI_PATH) { + continue; + } + + if (options[i].len != strlen(path[j])) { + return false; + } + + if (memcmp(options[i].value, path[j], options[i].len)) { + return false; + } + + j++; + } + + if (path[j]) { + return false; + } + + for (; i < opt_num; i++) { + if (options[i].delta == COAP_OPTION_URI_PATH) { + return false; + } + } + + return true; +} + +static coap_method_t method_from_code(const struct coap_resource *resource, + u8_t code) +{ + switch (code) { + case COAP_METHOD_GET: + return resource->get; + case COAP_METHOD_POST: + return resource->post; + case COAP_METHOD_PUT: + return resource->put; + case COAP_METHOD_DELETE: + return resource->del; + default: + return NULL; + } +} + +static bool is_request(const struct coap_packet *cpkt) +{ + u8_t code = coap_header_get_code(cpkt); + + return !(code & ~COAP_REQUEST_MASK); +} + +int coap_handle_request(struct coap_packet *cpkt, + struct coap_resource *resources, + struct coap_option *options, + u8_t opt_num) +{ + struct coap_resource *resource; + + if (!is_request(cpkt)) { + return 0; + } + + /* FIXME: deal with hierarchical resources */ + for (resource = resources; resource && resource->path; resource++) { + coap_method_t method; + u8_t code; + + if (!uri_path_eq(cpkt, resource->path, options, opt_num)) { + continue; + } + + code = coap_header_get_code(cpkt); + method = method_from_code(resource, code); + if (!method) { + return 0; + } + + return method(resource, cpkt); + } + + return -ENOENT; +} + +unsigned int coap_option_value_to_int(const struct coap_option *option) +{ + switch (option->len) { + case 0: + return 0; + case 1: + return option->value[0]; + case 2: + return (option->value[1] << 0) | (option->value[0] << 8); + case 3: + return (option->value[2] << 0) | (option->value[1] << 8) | + (option->value[0] << 16); + case 4: + return (option->value[2] << 0) | (option->value[2] << 8) | + (option->value[1] << 16) | (option->value[0] << 24); + default: + return 0; + } + + return 0; +} + +static int get_observe_option(const struct coap_packet *cpkt) +{ + struct coap_option option = {}; + u16_t count = 1; + int r; + + r = coap_find_options(cpkt, COAP_OPTION_OBSERVE, &option, count); + if (r <= 0) { + return -ENOENT; + } + + return coap_option_value_to_int(&option); +} + +struct coap_reply *coap_response_received( + const struct coap_packet *response, + const struct sockaddr *from, + struct coap_reply *replies, size_t len) +{ + struct coap_reply *r; + u8_t token[8]; + u16_t id; + u8_t tkl; + size_t i; + + id = coap_header_get_id(response); + tkl = coap_header_get_token(response, (u8_t *)token); + + for (i = 0, r = replies; i < len; i++, r++) { + int age; + + if ((r->id == 0) && (r->tkl == 0)) { + continue; + } + + /* Piggybacked must match id when token is empty */ + if ((r->id != id) && (tkl == 0)) { + continue; + } + + if (tkl > 0 && memcmp(r->token, token, tkl)) { + continue; + } + + age = get_observe_option(response); + if (age > 0) { + /* age == 2 means that the notifications wrapped, + * or this is the first one + */ + if (r->age > age && age != 2) { + continue; + } + + r->age = age; + } + + r->reply(response, r, from); + return r; + } + + return NULL; +} + +void coap_reply_init(struct coap_reply *reply, + const struct coap_packet *request) +{ + u8_t token[8]; + u8_t tkl; + int age; + + reply->id = coap_header_get_id(request); + tkl = coap_header_get_token(request, (u8_t *)&token); + + if (tkl > 0) { + memcpy(reply->token, token, tkl); + } + + reply->tkl = tkl; + + age = get_observe_option(request); + + /* It means that the request enabled observing a resource */ + if (age == 0) { + reply->age = 2; + } +} + +void coap_reply_clear(struct coap_reply *reply) +{ + reply->id = 0; + reply->tkl = 0; + reply->reply = NULL; +} + +int coap_resource_notify(struct coap_resource *resource) +{ + struct coap_observer *o; + + resource->age++; + + if (!resource->notify) { + return -ENOENT; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&resource->observers, o, list) { + resource->notify(resource, o); + } + + return 0; +} + +bool coap_request_is_observe(const struct coap_packet *request) +{ + return get_observe_option(request) == 0; +} + +void coap_observer_init(struct coap_observer *observer, + const struct coap_packet *request, + const struct sockaddr *addr) +{ + u8_t token[8]; + u8_t tkl; + + tkl = coap_header_get_token(request, (u8_t *)&token); + + if (tkl > 0) { + memcpy(observer->token, token, tkl); + } + + observer->tkl = tkl; + + net_ipaddr_copy(&observer->addr, addr); +} + +bool coap_register_observer(struct coap_resource *resource, + struct coap_observer *observer) +{ + bool first; + + sys_slist_append(&resource->observers, &observer->list); + + first = resource->age == 0; + if (first) { + resource->age = 2; + } + + return first; +} + +void coap_remove_observer(struct coap_resource *resource, + struct coap_observer *observer) +{ + sys_slist_find_and_remove(&resource->observers, &observer->list); +} + +static bool sockaddr_equal(const struct sockaddr *a, + const struct sockaddr *b) +{ + /* FIXME: Should we consider ipv6-mapped ipv4 addresses as equal to + * ipv4 addresses? + */ + if (a->sa_family != b->sa_family) { + return false; + } + + if (a->sa_family == AF_INET) { + const struct sockaddr_in *a4 = net_sin(a); + const struct sockaddr_in *b4 = net_sin(b); + + if (a4->sin_port != b4->sin_port) { + return false; + } + + return net_ipv4_addr_cmp(&a4->sin_addr, &b4->sin_addr); + } + + if (b->sa_family == AF_INET6) { + const struct sockaddr_in6 *a6 = net_sin6(a); + const struct sockaddr_in6 *b6 = net_sin6(b); + + if (a6->sin6_scope_id != b6->sin6_scope_id) { + return false; + } + + if (a6->sin6_port != b6->sin6_port) { + return false; + } + + return net_ipv6_addr_cmp(&a6->sin6_addr, &b6->sin6_addr); + } + + /* Invalid address family */ + return false; +} + +struct coap_observer *coap_find_observer_by_addr( + struct coap_observer *observers, size_t len, + const struct sockaddr *addr) +{ + size_t i; + + for (i = 0; i < len; i++) { + struct coap_observer *o = &observers[i]; + + if (sockaddr_equal(&o->addr, addr)) { + return o; + } + } + + return NULL; +} + +int coap_packet_append_payload_marker(struct coap_packet *cpkt) +{ + return net_pkt_append_u8(cpkt->pkt, COAP_MARKER) ? 0 : -EINVAL; +} + +int coap_packet_append_payload(struct coap_packet *cpkt, u8_t *payload, + u16_t payload_len) +{ + bool status; + + status = net_pkt_append_all(cpkt->pkt, payload_len, payload, + PKT_WAIT_TIME); + + return status ? 0 : -EINVAL; +} + +static u8_t encode_extended_option(u16_t num, u8_t *opt, u16_t *ext) +{ + if (num < 13) { + *opt = num; + *ext = 0; + + return 0; + } else if (num < 269) { + *opt = 13; + *ext = num - 13; + + return 1; + } + + *opt = 14; + *ext = num - 269; + + return 2; +} + +static int encode_option(struct coap_packet *cpkt, u16_t code, + const u8_t *value, u16_t len) +{ + u16_t delta_ext; /* Extended delta */ + u16_t len_ext; /* Extended length */ + u8_t opt; /* delta | len */ + u8_t opt_delta; + u8_t opt_len; + u8_t delta_size; + u8_t len_size; + bool res; + + delta_size = encode_extended_option(code, &opt_delta, &delta_ext); + len_size = encode_extended_option(len, &opt_len, &len_ext); + + option_header_set_delta(&opt, opt_delta); + option_header_set_len(&opt, opt_len); + + net_pkt_append_u8(cpkt->pkt, opt); + + if (delta_size == 1) { + net_pkt_append_u8(cpkt->pkt, (u8_t) delta_ext); + } else if (delta_size == 2) { + net_pkt_append_be16(cpkt->pkt, delta_ext); + } + + if (len_size == 1) { + net_pkt_append_u8(cpkt->pkt, (u8_t) len_ext); + } else if (delta_size == 2) { + net_pkt_append_be16(cpkt->pkt, len_ext); + } + + if (len && value) { + res = net_pkt_append_all(cpkt->pkt, len, value, PKT_WAIT_TIME); + if (!res) { + return -EINVAL; + } + } + + return (1 + delta_size + len_size + len); +} + +/* TODO Add support for inserting options in proper place + * and modify other option's delta accordingly. + */ +int coap_packet_append_option(struct coap_packet *cpkt, u16_t code, + const u8_t *value, u16_t len) +{ + struct net_buf *frag; + u16_t offset; + int r; + + if (!cpkt) { + return -EINVAL; + } + + if (len && !value) { + return -EINVAL; + } + + if (len > 13) { +#if !defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN) + NET_ERR("Enable CONFIG_COAP_EXTENDED_OPTIONS_LEN to support " + "if length is more than 13"); + return -EINVAL; +#endif + } + + if (code < cpkt->last_delta) { + NET_ERR("Options should be in ascending order"); + return -EINVAL; + } + + /* Skip CoAP packet header */ + frag = net_frag_skip(cpkt->frag, cpkt->offset, &offset, cpkt->hdr_len); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + + /* Calculate delta, if this option is not the first one */ + if (cpkt->opt_len) { + code = (code == cpkt->last_delta) ? 0 : + code - cpkt->last_delta; + } + + r = encode_option(cpkt, code, value, len); + if (r < 0) { + return -EINVAL; + } + + cpkt->opt_len += r; + cpkt->last_delta += code; + + return 0; +} + +int coap_append_option_int(struct coap_packet *cpkt, u16_t code, + unsigned int val) +{ + u8_t data[4], len; + + if (val == 0) { + data[0] = 0; + len = 0; + } else if (val < 0xFF) { + data[0] = (u8_t) val; + len = 1; + } else if (val < 0xFFFF) { + sys_put_be16(val, data); + len = 2; + } else if (val < 0xFFFFFF) { + sys_put_be16(val, data); + data[2] = val >> 16; + len = 3; + } else { + sys_put_be32(val, data); + len = 4; + } + + return coap_packet_append_option(cpkt, code, data, len); +} + +int coap_find_options(const struct coap_packet *cpkt, u16_t code, + struct coap_option *options, u16_t veclen) +{ + struct option_context context = { + .delta = 0, + .frag = NULL, + .offset = 0 + }; + u16_t opt_len; + int coap_len; + int count = 0; + int r; + + if (!cpkt || !cpkt->pkt || !cpkt->pkt->frags) { + return -EINVAL; + } + + if (!cpkt->hdr_len) { + return -EINVAL; + } + + coap_len = get_coap_packet_len(cpkt->pkt); + if (coap_len < 0) { + return -EINVAL; + } + + coap_len -= cpkt->hdr_len; + opt_len = 0; + + /* Skip CoAP header */ + context.frag = net_frag_skip(cpkt->frag, cpkt->offset, + &context.offset, cpkt->hdr_len); + if (!context.frag && context.offset == 0xffff) { + return -EINVAL; + } + + while (context.delta <= code && count < veclen) { + if (opt_len >= coap_len) { + break; + } + + r = parse_option(cpkt, &context, &options[count]); + if (r < 0) { + return -ENOENT; + } + + opt_len += r; + + if (r == 0) { + break; + } + + if (code != options[count].delta) { + continue; + } + + count++; + } + + return count; +} + +u8_t coap_header_get_version(const struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t offset; + u8_t version; + + frag = net_frag_read_u8(cpkt->frag, cpkt->offset, &offset, &version); + if (!frag && offset == 0xffff) { + return 0; + } + + return (version & 0xC0) >> 6; +} + +u8_t coap_header_get_type(const struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t offset; + u8_t type; + + frag = net_frag_read_u8(cpkt->frag, cpkt->offset, &offset, &type); + if (!frag && offset == 0xffff) { + return 0; + } + + return (type & 0x30) >> 4; +} + +static u8_t __coap_header_get_code(const struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t offset; + u8_t code; + + frag = net_frag_skip(cpkt->frag, cpkt->offset, &offset, 1); + frag = net_frag_read_u8(frag, offset, &offset, &code); + if (!frag && offset == 0xffff) { + return 0; + } + + return code; +} + +u8_t coap_header_get_token(const struct coap_packet *cpkt, u8_t *token) +{ + struct net_buf *frag; + u16_t offset; + u8_t tkl; + + if (!cpkt || !token) { + return 0; + } + + tkl = get_header_tkl(cpkt); + if (!tkl) { + return 0; + } + + frag = net_frag_skip(cpkt->frag, cpkt->offset, &offset, + BASIC_HEADER_SIZE); + frag = net_frag_read(frag, offset, &offset, tkl, token); + if (!frag && offset == 0xffff) { + return 0; + } + + return tkl; +} + +u8_t coap_header_get_code(const struct coap_packet *cpkt) +{ + u8_t code = __coap_header_get_code(cpkt); + + switch (code) { + /* Methods are encoded in the code field too */ + case COAP_METHOD_GET: + case COAP_METHOD_POST: + case COAP_METHOD_PUT: + case COAP_METHOD_DELETE: + + /* All the defined response codes */ + case COAP_RESPONSE_CODE_OK: + case COAP_RESPONSE_CODE_CREATED: + case COAP_RESPONSE_CODE_DELETED: + case COAP_RESPONSE_CODE_VALID: + case COAP_RESPONSE_CODE_CHANGED: + case COAP_RESPONSE_CODE_CONTENT: + case COAP_RESPONSE_CODE_CONTINUE: + case COAP_RESPONSE_CODE_BAD_REQUEST: + case COAP_RESPONSE_CODE_UNAUTHORIZED: + case COAP_RESPONSE_CODE_BAD_OPTION: + case COAP_RESPONSE_CODE_FORBIDDEN: + case COAP_RESPONSE_CODE_NOT_FOUND: + case COAP_RESPONSE_CODE_NOT_ALLOWED: + case COAP_RESPONSE_CODE_NOT_ACCEPTABLE: + case COAP_RESPONSE_CODE_INCOMPLETE: + case COAP_RESPONSE_CODE_PRECONDITION_FAILED: + case COAP_RESPONSE_CODE_REQUEST_TOO_LARGE: + case COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT: + case COAP_RESPONSE_CODE_INTERNAL_ERROR: + case COAP_RESPONSE_CODE_NOT_IMPLEMENTED: + case COAP_RESPONSE_CODE_BAD_GATEWAY: + case COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE: + case COAP_RESPONSE_CODE_GATEWAY_TIMEOUT: + case COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED: + case COAP_CODE_EMPTY: + return code; + default: + return COAP_CODE_EMPTY; + } +} + +u16_t coap_header_get_id(const struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t offset; + u16_t id; + + frag = net_frag_skip(cpkt->frag, cpkt->offset, &offset, 2); + frag = net_frag_read_be16(frag, offset, &offset, &id); + if (!frag && offset == 0xffff) { + return 0; + } + + return id; +} + +struct net_buf *coap_packet_get_payload(const struct coap_packet *cpkt, + u16_t *offset, u16_t *len) +{ + struct net_buf *frag; + int r; + + if (!cpkt || !offset) { + return NULL; + } + + frag = NULL; + *offset = 0xffff; + *len = 0; + + r = get_coap_packet_len(cpkt->pkt); + if (r < 0) { + return frag; + } + + frag = net_frag_skip(cpkt->frag, cpkt->offset, offset, + cpkt->hdr_len + cpkt->opt_len); + *len = r - cpkt->hdr_len - cpkt->opt_len; + + return frag; +} + + +int coap_block_transfer_init(struct coap_block_context *ctx, + enum coap_block_size block_size, + size_t total_size) +{ + ctx->block_size = block_size; + ctx->total_size = total_size; + ctx->current = 0; + + return 0; +} + +#define GET_BLOCK_SIZE(v) (((v) & 0x7)) +#define GET_MORE(v) (!!((v) & 0x08)) +#define GET_NUM(v) ((v) >> 4) + +#define SET_BLOCK_SIZE(v, b) (v |= ((b) & 0x07)) +#define SET_MORE(v, m) ((v) |= (m) ? 0x08 : 0x00) +#define SET_NUM(v, n) ((v) |= ((n) << 4)) + +int coap_append_block1_option(struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + u16_t bytes = coap_block_size_to_bytes(ctx->block_size); + unsigned int val = 0; + int r; + + if (is_request(cpkt)) { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_MORE(val, ctx->current + bytes < ctx->total_size); + SET_NUM(val, ctx->current / bytes); + } else { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_NUM(val, ctx->current / bytes); + } + + r = coap_append_option_int(cpkt, COAP_OPTION_BLOCK1, val); + + return r; +} + +int coap_append_block2_option(struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + int r, val = 0; + u16_t bytes = coap_block_size_to_bytes(ctx->block_size); + + if (is_request(cpkt)) { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_NUM(val, ctx->current / bytes); + } else { + SET_BLOCK_SIZE(val, ctx->block_size); + SET_MORE(val, ctx->current + bytes < ctx->total_size); + SET_NUM(val, ctx->current / bytes); + } + + r = coap_append_option_int(cpkt, COAP_OPTION_BLOCK2, val); + + return r; +} + +int coap_append_size1_option(struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + return coap_append_option_int(cpkt, COAP_OPTION_SIZE1, ctx->total_size); +} + +int coap_append_size2_option(struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + return coap_append_option_int(cpkt, COAP_OPTION_SIZE2, ctx->total_size); +} + +static int get_block_option(const struct coap_packet *cpkt, u16_t code) +{ + struct coap_option option; + unsigned int val; + int count = 1; + + count = coap_find_options(cpkt, code, &option, count); + if (count <= 0) { + return -ENOENT; + } + + val = coap_option_value_to_int(&option); + + return val; +} + +static int update_descriptive_block(struct coap_block_context *ctx, + int block, int size) +{ + size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4); + + if (block == -ENOENT) { + return 0; + } + + if (size && ctx->total_size && ctx->total_size != size) { + return -EINVAL; + } + + if (ctx->current > 0 && GET_BLOCK_SIZE(block) > ctx->block_size) { + return -EINVAL; + } + + if (ctx->total_size && new_current > ctx->total_size) { + return -EINVAL; + } + + if (size) { + ctx->total_size = size; + } + ctx->current = new_current; + ctx->block_size = min(GET_BLOCK_SIZE(block), ctx->block_size); + + return 0; +} + +static int update_control_block1(struct coap_block_context *ctx, + int block, int size) +{ + size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4); + + if (block == -ENOENT) { + return 0; + } + + if (new_current != ctx->current) { + return -EINVAL; + } + + if (GET_BLOCK_SIZE(block) > ctx->block_size) { + return -EINVAL; + } + + ctx->block_size = GET_BLOCK_SIZE(block); + ctx->total_size = size; + + return 0; +} + +static int update_control_block2(struct coap_block_context *ctx, + int block, int size) +{ + size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4); + + if (block == -ENOENT) { + return 0; + } + + if (GET_MORE(block)) { + return -EINVAL; + } + + if (GET_NUM(block) > 0 && GET_BLOCK_SIZE(block) != ctx->block_size) { + return -EINVAL; + } + + ctx->current = new_current; + ctx->block_size = min(GET_BLOCK_SIZE(block), ctx->block_size); + + return 0; +} + +int coap_update_from_block(const struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + int r, block1, block2, size1, size2; + + block1 = get_block_option(cpkt, COAP_OPTION_BLOCK1); + block2 = get_block_option(cpkt, COAP_OPTION_BLOCK2); + size1 = get_block_option(cpkt, COAP_OPTION_SIZE1); + size2 = get_block_option(cpkt, COAP_OPTION_SIZE2); + + size1 = size1 == -ENOENT ? 0 : size1; + size2 = size2 == -ENOENT ? 0 : size2; + + if (is_request(cpkt)) { + r = update_control_block2(ctx, block2, size2); + if (r) { + return r; + } + + return update_descriptive_block(ctx, block1, size1); + } + + r = update_control_block1(ctx, block1, size1); + if (r) { + return r; + } + + return update_descriptive_block(ctx, block2, size2); +} + +size_t coap_next_block(const struct coap_packet *cpkt, + struct coap_block_context *ctx) +{ + int block; + + if (is_request(cpkt)) { + block = get_block_option(cpkt, COAP_OPTION_BLOCK1); + } else { + block = get_block_option(cpkt, COAP_OPTION_BLOCK2); + } + + if (!GET_MORE(block)) { + return 0; + } + + ctx->current += coap_block_size_to_bytes(ctx->block_size); + + return ctx->current; +} + +u8_t *coap_next_token(void) +{ + static u32_t rand[2]; + + rand[0] = sys_rand32_get(); + rand[1] = sys_rand32_get(); + + return (u8_t *) rand; +} diff --git a/subsys/net/lib/coap/coap_link_format.c b/subsys/net/lib/coap/coap_link_format.c new file mode 100644 index 0000000000000..4b1c58a3203ac --- /dev/null +++ b/subsys/net/lib/coap/coap_link_format.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_NET_DEBUG_COAP) +#define SYS_LOG_DOMAIN "coap" +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#define PKT_WAIT_TIME K_SECONDS(1) + +static bool match_path_uri(const char * const *path, + const char *uri, u16_t len) +{ + const char * const *p = NULL; + int i, j, k, plen; + + if (!path) { + return false; + } + + if (len <= 1 || uri[0] != '/') { + return false; + } + + p = path; + plen = *p ? strlen(*p) : 0; + j = 0; + + if (plen == 0) { + return false; + } + + /* Go through uri and try to find a matching path */ + for (i = 1; i < len; i++) { + while (*p) { + plen = strlen(*p); + + k = i; + + for (j = 0; j < plen; j++) { + if (uri[k] == '*') { + if ((k + 1) == len) { + return true; + } + } + + if (uri[k] != (*p)[j]) { + goto next; + } + + k++; + } + + if (i == (k - 1) && j == plen) { + return true; + } + + if (k == len && j == plen) { + return true; + } + +next: + p++; + } + } + + /* Did we find the resource or not */ + if (i == len && !*p) { + return false; + } + + return true; +} + +static bool match_attributes(const char * const *attributes, + const struct coap_option *query) +{ + const char * const *attr; + + /* FIXME: deal with the case when there are multiple options in a + * query, for example: 'rt=lux temperature', if I want to list + * resources with resource type lux or temperature. + */ + for (attr = attributes; attr && *attr; attr++) { + u16_t attr_len = strlen(*attr); + + if (query->len != attr_len) { + continue; + } + + if (!strncmp((char *) query->value, *attr, attr_len)) { + return true; + } + } + + return false; +} + +static bool match_queries_resource(const struct coap_resource *resource, + const struct coap_option *query, + int num_queries) +{ + struct coap_core_metadata *meta = resource->user_data; + const char * const *attributes = NULL; + const int href_len = strlen("href"); + + if (num_queries == 0) { + return true; + } + + if (meta && meta->attributes) { + attributes = meta->attributes; + } + + if (!attributes) { + return false; + } + + if (query->len > href_len + 1 && + !strncmp((char *) query->value, "href", href_len)) { + /* The stuff after 'href=' */ + const char *uri = (char *) query->value + href_len + 1; + u16_t uri_len = query->len - (href_len + 1); + + return match_path_uri(resource->path, uri, uri_len); + } + + return match_attributes(attributes, query); +} + +#if defined(CONFIG_COAP_WELL_KNOWN_BLOCK_WISE) + +#define MAX_BLOCK_WISE_TRANSFER_SIZE 2048 + +enum coap_block_size default_block_size(void) +{ + switch (CONFIG_COAP_WELL_KNOWN_BLOCK_WISE_SIZE) { + case 16: + return COAP_BLOCK_16; + case 32: + return COAP_BLOCK_32; + case 64: + return COAP_BLOCK_64; + case 128: + return COAP_BLOCK_128; + case 256: + return COAP_BLOCK_256; + case 512: + return COAP_BLOCK_512; + case 1024: + return COAP_BLOCK_1024; + } + + return COAP_BLOCK_64; +} + +static bool append_to_net_pkt(struct net_pkt *pkt, const char *str, u16_t len, + u16_t *remaining, size_t *offset, size_t current) +{ + u16_t pos = 0; + bool res; + + if (!*remaining) { + return true; + } + + if (*offset < current) { + pos = current - *offset; + + if (len >= pos) { + len -= pos; + *offset += pos; + } else { + *offset += len; + return true; + } + } + + if (len > *remaining) { + len = *remaining; + } + + res = net_pkt_append_all(pkt, len, str + pos, PKT_WAIT_TIME); + + *remaining -= len; + *offset += len; + + return res; +} + +static int format_uri(const char * const *path, struct net_pkt *pkt, + u16_t *remaining, size_t *offset, + size_t current, bool *more) +{ + static const char prefix[] = "", 1, remaining, offset, current); + if (!res) { + return -ENOMEM; + } + + if (!*remaining) { + *more = true; + return 0; + } + + *more = false; + + return 0; +} + +static int format_attributes(const char * const *attributes, + struct net_pkt *pkt, + u16_t *remaining, size_t *offset, + size_t current, bool *more) +{ + const char * const *attr; + bool res; + + if (!attributes) { + goto terminator; + } + + for (attr = attributes; *attr; ) { + int attr_len = strlen(*attr); + + res = append_to_net_pkt(pkt, *attr, attr_len, + remaining, offset, current); + if (!res) { + return -ENOMEM; + } + + if (!*remaining) { + *more = true; + return 0; + } + + attr++; + if (!*attr) { + continue; + } + + res = append_to_net_pkt(pkt, ";", 1, + remaining, offset, current); + if (!res) { + return -ENOMEM; + } + + if (!*remaining) { + *more = true; + return 0; + } + } + +terminator: + res = append_to_net_pkt(pkt, ";", 1, remaining, offset, current); + if (!res) { + return -ENOMEM; + } + + if (!*remaining) { + *more = true; + return 0; + } + + *more = false; + + return 0; +} + +static int format_resource(const struct coap_resource *resource, + struct net_pkt *pkt, + u16_t *remaining, size_t *offset, + size_t current, bool *more) +{ + struct coap_core_metadata *meta = resource->user_data; + const char * const *attributes = NULL; + int r; + + r = format_uri(resource->path, pkt, remaining, offset, current, more); + if (r < 0) { + return r; + } + + if (!*remaining) { + *more = true; + return 0; + } + + if (meta && meta->attributes) { + attributes = meta->attributes; + } + + return format_attributes(attributes, pkt, remaining, offset, current, + more); +} + +int clear_more_flag(struct coap_packet *cpkt) +{ + struct net_buf *frag; + u16_t offset; + u8_t opt; + u8_t delta; + u8_t len; + + frag = net_frag_skip(cpkt->frag, 0, &offset, cpkt->hdr_len); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + + delta = 0; + /* Note: coap_well_known_core_get() added Option (delta and len) witho + * out any extended options so parsing will not consider at the moment. + */ + while (1) { + frag = net_frag_read_u8(frag, offset, &offset, &opt); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + + delta += ((opt & 0xF0) >> 4); + len = (opt & 0xF); + + if (delta == COAP_OPTION_BLOCK2) { + break; + } + + frag = net_frag_skip(frag, offset, &offset, len); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + } + + /* As per RFC 7959 Sec 2.2 : NUM filed can be on 0-3 bytes. + * Skip NUM field to update M bit. + */ + if (len > 1) { + frag = net_frag_skip(frag, offset, &offset, len - 1); + if (!frag && offset == 0xffff) { + return -EINVAL; + } + } + + frag->data[offset] = frag->data[offset] & 0xF7; + + return 0; +} + +int coap_well_known_core_get(struct coap_resource *resource, + struct coap_packet *request, + struct coap_packet *response, + struct net_pkt *pkt) +{ + static struct coap_block_context ctx; + struct coap_option query; + unsigned int num_queries; + size_t offset; + u8_t token[8]; + u16_t remaining; + u16_t id; + u8_t tkl; + u8_t format; + int r; + bool more = false; + + if (ctx.total_size == 0) { + /* We have to iterate through resources and it's attributes, + * total size is unknown, so initialize it to + * MAX_BLOCK_WISE_TRANSFER_SIZE and update it according to + * offset. + */ + coap_block_transfer_init(&ctx, default_block_size(), + MAX_BLOCK_WISE_TRANSFER_SIZE); + } + + r = coap_update_from_block(request, &ctx); + if (r < 0) { + goto end; + } + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + /* Per RFC 6690, Section 4.1, only one (or none) query parameter may be + * provided, use the first if multiple. + */ + r = coap_find_options(request, COAP_OPTION_URI_QUERY, &query, 1); + if (r < 0) { + goto end; + } + + num_queries = r; + + r = coap_packet_init(response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + goto end; + } + + format = 40; /* application/link-format */ + + r = coap_packet_append_option(response, COAP_OPTION_CONTENT_FORMAT, + &format, sizeof(format)); + if (r < 0) { + goto end; + } + + r = coap_append_block2_option(response, &ctx); + if (r < 0) { + goto end; + } + + r = coap_packet_append_payload_marker(response); + if (r < 0) { + goto end; + } + + offset = 0; + remaining = coap_block_size_to_bytes(ctx.block_size); + + while (resource++ && resource->path) { + if (!remaining) { + more = true; + break; + } + + if (!match_queries_resource(resource, &query, num_queries)) { + continue; + } + + r = format_resource(resource, pkt, &remaining, &offset, + ctx.current, &more); + if (r < 0) { + goto end; + } + } + + /* Offset is the total size now, but block2 option is already + * appended. So update only 'more' flag. + */ + if (!more) { + ctx.total_size = offset; + r = clear_more_flag(response); + } + +end: + /* So it's a last block, reset context */ + if (!more) { + memset(&ctx, 0, sizeof(ctx)); + } + + return r; +} + +#else + +static int format_uri(const char * const *path, struct net_pkt *pkt) +{ + const char * const *p; + char *prefix = "'); + + return 0; +} + +static int format_attributes(const char * const *attributes, + struct net_pkt *pkt) +{ + const char * const *attr; + bool res; + + if (!attributes) { + goto terminator; + } + + for (attr = attributes; *attr; ) { + res = net_pkt_append_all(pkt, strlen(*attr), (u8_t *) *attr, + PKT_WAIT_TIME); + if (!res) { + return -ENOMEM; + } + + attr++; + if (*attr) { + net_pkt_append_u8(pkt, (u8_t) ';'); + } + } + +terminator: + net_pkt_append_u8(pkt, (u8_t) ';'); + + return 0; +} + +static int format_resource(const struct coap_resource *resource, + struct net_pkt *pkt) +{ + struct coap_core_metadata *meta = resource->user_data; + const char * const *attributes = NULL; + int r; + + r = format_uri(resource->path, pkt); + if (r < 0) { + return r; + } + + if (meta && meta->attributes) { + attributes = meta->attributes; + } + + return format_attributes(attributes, pkt); +} + +int coap_well_known_core_get(struct coap_resource *resource, + struct coap_packet *request, + struct coap_packet *response, + struct net_pkt *pkt) +{ + struct coap_option query; + u8_t token[8]; + u16_t id; + u8_t tkl; + u8_t format; + u8_t num_queries; + int r; + + if (!resource || !request || !response || !pkt) { + return -EINVAL; + } + + id = coap_header_get_id(request); + tkl = coap_header_get_token(request, token); + + /* Per RFC 6690, Section 4.1, only one (or none) query parameter may be + * provided, use the first if multiple. + */ + r = coap_find_options(request, COAP_OPTION_URI_QUERY, &query, 1); + if (r < 0) { + return r; + } + + num_queries = r; + + r = coap_packet_init(response, pkt, 1, COAP_TYPE_ACK, + tkl, token, COAP_RESPONSE_CODE_CONTENT, id); + if (r < 0) { + return r; + } + + format = 40; /* application/link-format */ + r = coap_packet_append_option(response, COAP_OPTION_CONTENT_FORMAT, + &format, sizeof(format)); + if (r < 0) { + return -EINVAL; + } + + r = coap_packet_append_payload_marker(response); + if (r < 0) { + return -EINVAL; + } + + while (resource++ && resource->path) { + if (!match_queries_resource(resource, &query, num_queries)) { + continue; + } + + r = format_resource(resource, pkt); + if (r < 0) { + return r; + } + } + + return 0; +} +#endif + +/* Exposing some of the APIs to ZoAP unit tests in tests/net/lib/coap */ +#if defined(CONFIG_COAP_TEST_API_ENABLE) +bool _coap_match_path_uri(const char * const *path, + const char *uri, u16_t len) +{ + return match_path_uri(path, uri, len); +} +#endif diff --git a/subsys/net/lib/http/Kconfig b/subsys/net/lib/http/Kconfig index 2bc269f5b1a22..8ac28a7b9770d 100644 --- a/subsys/net/lib/http/Kconfig +++ b/subsys/net/lib/http/Kconfig @@ -65,6 +65,13 @@ config HTTP_PARSER This parser requires some string-related routines commonly provided by a libc implementation. +config HTTP_PARSER_URL_ONLY + bool "HTTP URL parser only support" + default n + select HTTP_PARSER + help + This option only enables URL parser of the http_parser library. + config HTTP_PARSER_STRICT bool "HTTP strict parsing" default n diff --git a/subsys/net/lib/http/http_parser.c b/subsys/net/lib/http/http_parser.c index eab6ca065b292..56cf2de990caf 100644 --- a/subsys/net/lib/http/http_parser.c +++ b/subsys/net/lib/http/http_parser.c @@ -71,6 +71,7 @@ do { \ } \ } while (0) +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) /* Don't allow the total size of the HTTP headers (including the status * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect * embedders against denial-of-service attacks where the attacker feeds @@ -169,6 +170,7 @@ s8_t unhex[256] = { -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ #ifdef HTTP_PARSER_STRICT @@ -284,6 +286,7 @@ enum state { s_message_done }; +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) #define PARSING_HEADER(state) (state <= s_headers_done) enum header_states { @@ -311,6 +314,7 @@ enum header_states { h_connection_close, h_connection_upgrade }; +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ enum http_host_state { s_http_host_dead = 1, @@ -327,6 +331,7 @@ enum http_host_state { s_http_host_port }; +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) static inline int cb_notify(struct http_parser *parser, enum state *current_state, http_cb cb, int cb_error, size_t *parsed, size_t already_parsed) @@ -382,6 +387,7 @@ int cb_data(struct http_parser *parser, http_data_cb cb, int cb_error, return 0; } +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ /* Macros for character classes; depends on strict-mode */ @@ -416,6 +422,7 @@ int cb_data(struct http_parser *parser, http_data_cb cb, int cb_error, (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') #endif +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) /** * Verify that a char is a valid visible (printable) US-ASCII * character or %x80-FF @@ -493,6 +500,7 @@ static struct { }; int http_message_needs_eof(const struct http_parser *parser); +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ /* Our URL parser. * @@ -655,6 +663,7 @@ static enum state parse_url_char(enum state s, const char ch) return s_dead; } +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) static int parser_header_state(struct http_parser *parser, char ch, char c) { @@ -2624,6 +2633,7 @@ const char *http_errno_description(enum http_errno err) return http_strerror_tab[err].description; } +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ static enum http_host_state http_parse_host_char(enum http_host_state s, const char ch) @@ -2906,6 +2916,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, return 0; } +#if !defined(CONFIG_HTTP_PARSER_URL_ONLY) void http_parser_pause(struct http_parser *parser, int paused) { /* Users should only be pausing/unpausing a parser that is not in an @@ -2933,3 +2944,4 @@ unsigned long http_parser_version(void) HTTP_PARSER_VERSION_MINOR * 0x00100 | HTTP_PARSER_VERSION_PATCH * 0x00001; } +#endif /* !CONFIG_HTTP_PARSER_URL_ONLY */ diff --git a/subsys/net/lib/lwm2m/Kconfig b/subsys/net/lib/lwm2m/Kconfig index 0b736cf2e78a1..5a3cf6731396d 100644 --- a/subsys/net/lib/lwm2m/Kconfig +++ b/subsys/net/lib/lwm2m/Kconfig @@ -7,7 +7,8 @@ menuconfig LWM2M bool "OMA LWM2M protocol stack" default n - select ZOAP + select COAP + select COAP_EXTENDED_OPTIONS_LEN select NET_APP_CLIENT help This option adds logic for managing OMA LWM2M data @@ -29,6 +30,12 @@ config LWM2M_ENGINE_STACK_SIZE Set the stack size for the LWM2M library engine (used for handling OBSERVE and NOTIFY events) +config LWM2M_ENGINE_MAX_MESSAGES + int "LWM2M engine max. message object" + default 5 + help + Set the maximum message objects for the LWM2M library client + config LWM2M_ENGINE_MAX_PENDING int "LWM2M engine max. pending objects" default 5 @@ -44,7 +51,7 @@ config LWM2M_ENGINE_MAX_REPLIES config LWM2M_ENGINE_MAX_OBSERVER int "Maximum # of observable LWM2M resources" default 50 - range 10 200 + range 5 200 help This value sets the maximum number of resources which can be added to the observe notification list. @@ -52,6 +59,7 @@ config LWM2M_ENGINE_MAX_OBSERVER config LWM2M_ENGINE_DEFAULT_LIFETIME int "LWM2M engine default server connection lifetime" default 30 + range 15 65535 help Set the default lifetime (in seconds) for the LWM2M library engine @@ -64,7 +72,7 @@ config LWM2M_LOCAL_PORT config LWM2M_SECURITY_INSTANCE_COUNT int "Maximum # of LWM2M Security object instances" - default 3 + default 1 range 1 10 help This setting establishes the total count of LWM2M Security instances @@ -72,7 +80,7 @@ config LWM2M_SECURITY_INSTANCE_COUNT config LWM2M_SERVER_INSTANCE_COUNT int "Maximum # of LWM2M Server object instances" - default 3 + default 1 range 1 10 help This setting establishes the total count of LWM2M Server instances @@ -93,7 +101,7 @@ config LWM2M_RD_CLIENT_STACK_SIZE config LWM2M_RD_CLIENT_INSTANCE_COUNT int "Maximum # of LWM2M RD client instances" - default 2 + default 1 help This setting establishes the total count of LWM2M RD client instances available. @@ -123,22 +131,53 @@ config LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT bool "Firmware Update object pull support" default y depends on LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT + select HTTP_PARSER_URL_ONLY help Include support for pulling a file from a remote server via block transfer and "FIRMWARE PACKAGE URI" resource. This option adds another UDP context and packet handling. -config LWM2M_FIRMWARE_UPDATE_PULL_COAP_BLOCK_SIZE - int "Firmware Update object pull CoAP block size" - depends on LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT +config LWM2M_COAP_BLOCK_SIZE + int "LWM2M CoAP block-wise transfer size" default 256 default 64 if NET_L2_BT default 64 if NET_L2_IEEE802154 range 16 1024 help - CoAP block size used by firmware pull when performing block-wise + CoAP block size used by LWM2M when performing block-wise transfers. Possible values: 16, 32, 64, 128, 256, 512 and 1024. +config LWM2M_NUM_BLOCK1_CONTEXT + int "Maximum # of LWM2M block1 contexts" + default 3 + help + This value sets up the maximum number of block1 contexts for + CoAP block-wise transfer we can handle at the same time. + +config LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT + bool "Firmware Update object pull via CoAP-CoAP/HTTP proxy support" + depends on LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT + default n + help + Include support for pulling firmware file via a CoAP-CoAP/HTTP proxy. + +if LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT + +config LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_ADDR + string "CoAP proxy network address" + help + Network address of the CoAP proxy server. + +config LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_URI_PATH + string "CoAP URI path element used by the proxy" + default "coap2http" + help + CoAP URI path element exported by the CoAP proxy server. + Defaults to coap2http, which is the URI path used by the + Californium CoAP-HTTP proxy. + +endif # LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT + config LWM2M_RW_JSON_SUPPORT bool "support for JSON writer" default y diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index f7514a3e1ac6a..c8794c3509481 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -16,8 +16,6 @@ * * - Use server / security object instance 0 for initial connection * - Add DNS support for security uri parsing - * - Block-transfer support / Large response messages - * (use Block2 to limit message size to 64 bytes for 6LOWPAN compat.) * - BOOTSTRAP/DTLS cleanup * - Handle WRITE_ATTRIBUTES (pmin=10&pmax=60) * - Handle Resource ObjLink type @@ -39,7 +37,7 @@ #include #include #include -#include +#include #include #include "lwm2m_object.h" @@ -79,11 +77,17 @@ #define BUF_ALLOC_TIMEOUT K_SECONDS(1) +#if defined(CONFIG_NET_APP_DTLS) +#define INSTANCE_INFO "Zephyr DTLS LwM2M-client" +#endif + +#define MAX_TOKEN_LEN 8 + struct observe_node { sys_snode_t node; struct lwm2m_ctx *ctx; struct lwm2m_obj_path path; - u8_t token[8]; + u8_t token[MAX_TOKEN_LEN]; s64_t event_timestamp; s64_t last_timestamp; u32_t min_period_sec; @@ -100,6 +104,24 @@ static sys_slist_t engine_obj_list; static sys_slist_t engine_obj_inst_list; static sys_slist_t engine_observer_list; +#define NUM_BLOCK1_CONTEXT CONFIG_LWM2M_NUM_BLOCK1_CONTEXT + +/* TODO: figure out what's correct value */ +#define TIMEOUT_BLOCKWISE_TRANSFER K_SECONDS(30) + +#define GET_BLOCK_NUM(v) ((v) >> 4) +#define GET_BLOCK_SIZE(v) (((v) & 0x7)) +#define GET_MORE(v) (!!((v) & 0x08)) + +struct block_context { + struct coap_block_context ctx; + s64_t timestamp; + u8_t token[8]; + u8_t tkl; +}; + +static struct block_context block1_contexts[NUM_BLOCK1_CONTEXT]; + /* periodic / notify / observe handling stack */ static K_THREAD_STACK_DEFINE(engine_thread_stack, CONFIG_LWM2M_ENGINE_STACK_SIZE); @@ -138,14 +160,116 @@ static char *sprint_token(const u8_t *token, u8_t tkl) static char buf[32]; int pos = 0; - for (i = 0; i < tkl; i++) { - pos += snprintf(&buf[pos], 31 - pos, "%x", token[i]); + if (token && tkl != LWM2M_MSG_TOKEN_LEN_SKIP) { + for (i = 0; i < tkl; i++) { + pos += snprintf(&buf[pos], 31 - pos, "%x", token[i]); + } + buf[pos] = '\0'; + } else if (tkl == LWM2M_MSG_TOKEN_LEN_SKIP) { + strcpy(buf, "[skip-token]"); + } else { + strcpy(buf, "[no-token]"); } - buf[pos] = '\0'; + return buf; } #endif +/* block-wise transfer functions */ + +enum coap_block_size lwm2m_default_block_size(void) +{ + switch (CONFIG_LWM2M_COAP_BLOCK_SIZE) { + case 16: + return COAP_BLOCK_16; + case 32: + return COAP_BLOCK_32; + case 64: + return COAP_BLOCK_64; + case 128: + return COAP_BLOCK_128; + case 256: + return COAP_BLOCK_256; + case 512: + return COAP_BLOCK_512; + case 1024: + return COAP_BLOCK_1024; + } + + return COAP_BLOCK_256; +} + +static int +init_block_ctx(const u8_t *token, u8_t tkl, struct block_context **ctx) +{ + int i; + s64_t timestamp; + + *ctx = NULL; + timestamp = k_uptime_get(); + for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) { + if (block1_contexts[i].tkl == 0) { + *ctx = &block1_contexts[i]; + break; + } + + if (timestamp - block1_contexts[i].timestamp > + TIMEOUT_BLOCKWISE_TRANSFER) { + *ctx = &block1_contexts[i]; + /* TODO: notify application for block + * transfer timeout + */ + break; + } + } + + if (*ctx == NULL) { + SYS_LOG_ERR("Cannot find free block context"); + return -ENOMEM; + } + + (*ctx)->tkl = tkl; + memcpy((*ctx)->token, token, tkl); + coap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0); + (*ctx)->timestamp = timestamp; + + return 0; +} + +static int +get_block_ctx(const u8_t *token, u8_t tkl, struct block_context **ctx) +{ + int i; + + *ctx = NULL; + + for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) { + if (block1_contexts[i].tkl == tkl && + memcmp(token, block1_contexts[i].token, tkl) == 0) { + *ctx = &block1_contexts[i]; + /* refresh timestmap */ + (*ctx)->timestamp = k_uptime_get(); + break; + } + } + + if (*ctx == NULL) { + SYS_LOG_ERR("Cannot find block context"); + return -ENOENT; + } + + return 0; +} + +static void free_block_ctx(struct block_context *ctx) +{ + if (ctx == NULL) { + return; + } + + ctx->tkl = 0; +} + /* observer functions */ int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id) @@ -178,7 +302,7 @@ int lwm2m_notify_observer_path(struct lwm2m_obj_path *path) path->res_id); } -static int engine_add_observer(struct net_app_ctx *app_ctx, +static int engine_add_observer(struct lwm2m_message *msg, const u8_t *token, u8_t tkl, struct lwm2m_obj_path *path, u16_t format) @@ -188,8 +312,19 @@ static int engine_add_observer(struct net_app_ctx *app_ctx, struct sockaddr *addr; int i; + if (!msg || !msg->ctx) { + SYS_LOG_ERR("valid lwm2m message is required"); + return -EINVAL; + } + + if (!token || (tkl == 0 || tkl > MAX_TOKEN_LEN)) { + SYS_LOG_ERR("token(%p) and token length(%u) must be valid.", + token, tkl); + return -EINVAL; + } + /* remote addr */ - addr = &app_ctx->default_ctx->remote; + addr = &msg->ctx->net_app_ctx.default_ctx->remote; /* check if object exists */ if (!get_engine_obj(path->obj_id)) { @@ -226,7 +361,7 @@ static int engine_add_observer(struct net_app_ctx *app_ctx, /* make sure this observer doesn't exist already */ SYS_SLIST_FOR_EACH_CONTAINER(&engine_observer_list, obs, node) { - if (&obs->ctx->net_app_ctx == app_ctx && + if (obs->ctx == msg->ctx && memcmp(&obs->path, path, sizeof(*path)) == 0) { /* quietly update the token information */ memcpy(obs->token, token, tkl); @@ -255,8 +390,7 @@ static int engine_add_observer(struct net_app_ctx *app_ctx, /* copy the values and add it to the list */ observe_node_data[i].used = true; - observe_node_data[i].ctx = CONTAINER_OF(app_ctx, - struct lwm2m_ctx, net_app_ctx); + observe_node_data[i].ctx = msg->ctx; memcpy(&observe_node_data[i].path, path, sizeof(*path)); memcpy(observe_node_data[i].token, token, tkl); observe_node_data[i].tkl = tkl; @@ -283,7 +417,7 @@ static int engine_remove_observer(const u8_t *token, u8_t tkl) struct observe_node *obs, *found_obj = NULL; sys_snode_t *prev_node = NULL; - if (!token || tkl == 0) { + if (!token || (tkl == 0 || tkl > MAX_TOKEN_LEN)) { SYS_LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl); return -EINVAL; @@ -506,6 +640,20 @@ int lwm2m_delete_obj_inst(u16_t obj_id, u16_t obj_inst_id) /* utility functions */ +static int get_option_int(const struct coap_packet *cpkt, u8_t opt) +{ + struct coap_option option = {}; + u16_t count = 1; + int r; + + r = coap_find_options(cpkt, opt, &option, count); + if (r <= 0) { + return -ENOENT; + } + + return coap_option_value_to_int(&option); +} + static void engine_clear_context(struct lwm2m_engine_context *context) { if (context->in) { @@ -538,7 +686,7 @@ static u16_t atou16(u8_t *buf, u16_t buflen, u16_t *len) return val; } -static void zoap_options_to_path(struct zoap_option *opt, int options_count, +static void coap_options_to_path(struct coap_option *opt, int options_count, struct lwm2m_obj_path *path) { u16_t len; @@ -571,100 +719,190 @@ static void zoap_options_to_path(struct zoap_option *opt, int options_count, } } -int lwm2m_init_message(struct net_app_ctx *app_ctx, struct zoap_packet *zpkt, - struct net_pkt **pkt, u8_t type, u8_t code, u16_t mid, - const u8_t *token, u8_t tkl) +static struct lwm2m_message *find_msg(struct coap_pending *pending, + struct coap_reply *reply, + struct lwm2m_message *messages, + size_t len) +{ + size_t i; + + if (!messages || (!pending && !reply)) { + return NULL; + } + + for (i = 0; i < len; i++) { + if (messages[i].ctx && messages[i].pending == pending) { + return &messages[i]; + } + + if (messages[i].ctx && messages[i].reply == reply) { + return &messages[i]; + } + } + + return NULL; +} + +struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx) +{ + size_t i; + + for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) { + if (!client_ctx->messages[i].ctx) { + client_ctx->messages[i].ctx = client_ctx; + return &client_ctx->messages[i]; + } + } + + return NULL; +} + +void lwm2m_release_message(struct lwm2m_message *msg) +{ + if (!msg) { + return; + } + + if (msg->pending) { + coap_pending_clear(msg->pending); + msg->pending = NULL; + } + + if (msg->reply) { + /* make sure we want to clear the reply */ + coap_reply_clear(msg->reply); + msg->reply = NULL; + } + + memset(msg, 0, sizeof(*msg)); +} + +int lwm2m_init_message(struct lwm2m_message *msg) { + struct net_pkt *pkt; + struct net_app_ctx *app_ctx; struct net_buf *frag; - int r; + u8_t tokenlen = 0; + u8_t *token = NULL; + int r = 0; + + if (!msg || !msg->ctx) { + SYS_LOG_ERR("LwM2M message is invalid."); + return -EINVAL; + } - *pkt = net_app_get_net_pkt(app_ctx, AF_UNSPEC, BUF_ALLOC_TIMEOUT); - if (!*pkt) { + app_ctx = &msg->ctx->net_app_ctx; + pkt = net_app_get_net_pkt(app_ctx, AF_UNSPEC, BUF_ALLOC_TIMEOUT); + if (!pkt) { SYS_LOG_ERR("Unable to get TX packet, not enough memory."); return -ENOMEM; } - frag = net_app_get_net_buf(app_ctx, *pkt, + frag = net_app_get_net_buf(app_ctx, pkt, BUF_ALLOC_TIMEOUT); if (!frag) { SYS_LOG_ERR("Unable to get DATA buffer, not enough memory."); - net_pkt_unref(*pkt); - *pkt = NULL; - return -ENOMEM; + r = -ENOMEM; + goto cleanup; } - r = zoap_packet_init(zpkt, *pkt); + /* + * msg->tkl == 0 is for a new TOKEN + * msg->tkl == LWM2M_MSG_TOKEN_LEN_SKIP means dont set + */ + if (msg->tkl == 0) { + tokenlen = 0; + token = coap_next_token(); + } else if (msg->token && msg->tkl != LWM2M_MSG_TOKEN_LEN_SKIP) { + tokenlen = msg->tkl; + token = msg->token; + } + + r = coap_packet_init(&msg->cpkt, pkt, 1, msg->type, + tokenlen, token, msg->code, + (msg->mid > 0 ? msg->mid : coap_next_id())); if (r < 0) { - SYS_LOG_ERR("zoap packet init error (err:%d)", r); - return r; + SYS_LOG_ERR("coap packet init error (err:%d)", r); + goto cleanup; } - /* FIXME: Could be that zoap_packet_init() sets some defaults */ - zoap_header_set_version(zpkt, 1); - zoap_header_set_type(zpkt, type); - zoap_header_set_code(zpkt, code); + if (msg->type == COAP_TYPE_CON) { + msg->pending = coap_pending_next_unused( + msg->ctx->pendings, + CONFIG_LWM2M_ENGINE_MAX_PENDING); + if (!msg->pending) { + SYS_LOG_ERR("Unable to find a free pending to track " + "retransmissions."); + r = -ENOMEM; + goto cleanup; + } - if (mid > 0) { - zoap_header_set_id(zpkt, mid); - } else { - zoap_header_set_id(zpkt, zoap_next_id()); - } + r = coap_pending_init(msg->pending, &msg->cpkt, + &app_ctx->default_ctx->remote); + if (r < 0) { + SYS_LOG_ERR("Unable to initialize a pending " + "retransmission (err:%d).", r); + goto cleanup; + } - /* tkl == 0 is for a new TOKEN, tkl == -1 means dont set */ - if (token && tkl > 0) { - zoap_header_set_token(zpkt, token, tkl); - } else if (tkl == 0) { - zoap_header_set_token(zpkt, zoap_next_token(), 8); + if (msg->reply_cb) { + msg->reply = coap_reply_next_unused( + msg->ctx->replies, + CONFIG_LWM2M_ENGINE_MAX_REPLIES); + if (!msg->reply) { + SYS_LOG_ERR("No resources for " + "waiting for replies."); + r = -ENOMEM; + goto cleanup; + } + + coap_reply_init(msg->reply, &msg->cpkt); + msg->reply->reply = msg->reply_cb; + } } return 0; + +cleanup: + lwm2m_release_message(msg); + if (pkt) { + net_pkt_unref(pkt); + } + + return r; } -struct zoap_pending *lwm2m_init_message_pending(struct lwm2m_ctx *client_ctx, - struct zoap_packet *zpkt) +int lwm2m_send_message(struct lwm2m_message *msg) { - struct zoap_pending *pending = NULL; int ret; - pending = zoap_pending_next_unused(client_ctx->pendings, - CONFIG_LWM2M_ENGINE_MAX_PENDING); - if (!pending) { - SYS_LOG_ERR("Unable to find a free pending to track " - "retransmissions."); - return NULL; + if (!msg || !msg->ctx) { + SYS_LOG_ERR("LwM2M message is invalid."); + return -EINVAL; } - ret = zoap_pending_init(pending, zpkt, - &client_ctx->net_app_ctx.default_ctx->remote); + msg->send_attempts++; + ret = net_app_send_pkt(&msg->ctx->net_app_ctx, msg->cpkt.pkt, + &msg->ctx->net_app_ctx.default_ctx->remote, + NET_SOCKADDR_MAX_SIZE, K_NO_WAIT, NULL); if (ret < 0) { - SYS_LOG_ERR("Unable to initialize a pending " - "retransmission (err:%d).", ret); - pending->pkt = NULL; - return NULL; + return ret; } - return pending; -} - -void lwm2m_init_message_cleanup(struct net_pkt *pkt, - struct zoap_pending *pending, - struct zoap_reply *reply) -{ - if (pending) { - zoap_pending_clear(pending); - /* don't unref attached pkt twice */ - if (!pending->pkt) { - pkt = NULL; + if (msg->type == COAP_TYPE_CON) { + if (msg->send_attempts > 1) { + return 0; } - } - if (reply) { - zoap_reply_clear(reply); + coap_pending_cycle(msg->pending); + k_delayed_work_submit(&msg->ctx->retransmit_work, + msg->pending->timeout); + } else { + /* if we're not expecting an ACK, free up the msg data */ + lwm2m_release_message(msg); } - if (pkt) { - net_pkt_unref(pkt); - } + return 0; } u16_t lwm2m_get_rd_data(u8_t *client_data, u16_t size) @@ -1582,6 +1820,12 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, void *data_ptr = NULL; size_t data_len = 0; size_t len = 0; + size_t total_size = 0; + int ret = 0; + u8_t tkl = 0; + u8_t token[8]; + bool last_block = true; + struct block_context *block_ctx = NULL; if (!obj_inst || !res || !obj_field || !context) { return -EINVAL; @@ -1591,13 +1835,18 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, data_ptr = res->data_ptr; data_len = res->data_len; + /* setup data_ptr/data_len for OPAQUE when it has none setup */ + if (obj_field->data_type == LWM2M_RES_TYPE_OPAQUE && + data_ptr == NULL && data_len == 0) { + data_ptr = in->inbuf; + data_len = in->insize; + } + /* allow user to override data elements via callback */ if (res->pre_write_cb) { data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, &data_len); } - /* TODO: check for block transfer fields here */ - if (data_ptr && data_len > 0) { switch (obj_field->data_type) { @@ -1685,14 +1934,30 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, } if (res->post_write_cb) { + /* Get block1 option for checking MORE block flag */ + ret = get_option_int(in->in_cpkt, COAP_OPTION_BLOCK1); + if (ret >= 0) { + last_block = !GET_MORE(ret); + + /* Get block_ctx for total_size (might be zero) */ + tkl = coap_header_get_token(in->in_cpkt, token); + if (token != NULL && + !get_block_ctx(token, tkl, &block_ctx)) { + total_size = block_ctx->ctx.total_size; + } + } + /* ignore return value here */ - res->post_write_cb(obj_inst->obj_inst_id, data_ptr, len, - false, 0); + ret = res->post_write_cb(obj_inst->obj_inst_id, data_ptr, len, + last_block, total_size); + if (ret >= 0) { + ret = 0; + } } NOTIFY_OBSERVER_PATH(path); - return 0; + return ret; } static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj, @@ -1748,15 +2013,88 @@ static int lwm2m_delete_handler(struct lwm2m_engine_obj *obj, context->path->obj_inst_id); } +static int get_coap_packet_len(struct net_pkt *pkt) +{ + struct net_buf *frag; + u16_t offset; + u16_t len; + + frag = net_frag_read_be16(pkt->frags, + net_pkt_ip_hdr_len(pkt) + + net_pkt_ipv6_ext_len(pkt) + + 4, &offset, &len); + if (!frag && offset == 0xffff) { + return -1; + } + + return len - NET_UDPH_LEN; +} + +#define COAP_MARKER 0xFF + +/* TODO: Remove in favor of using net_buf pointer to payload */ +u8_t *coap_packet_get_payload_ptr(struct coap_packet *cpkt, u16_t *len, + bool start_marker) +{ + u8_t *appdata = cpkt->pkt->frags->data; + u16_t appdatalen = cpkt->pkt->frags->len; + u8_t *start = NULL; /* Start of the payload */ + s32_t payload_size = 0; + + if (!cpkt || !len) { + return NULL; + } + + *len = 0; + if (start_marker) { + if (appdatalen + 1 >= net_buf_tailroom(cpkt->pkt->frags)) { + return NULL; + } + + appdata[appdatalen] = COAP_MARKER; + cpkt->pkt->frags->len += 1; + start = appdata + appdatalen + 1; + payload_size = appdata + net_buf_tailroom(cpkt->pkt->frags) - start; + } else { + payload_size = get_coap_packet_len(cpkt->pkt); + payload_size -= (cpkt->hdr_len + cpkt->opt_len); + if (payload_size > 0) { + start = appdata; + start += net_pkt_ip_hdr_len(cpkt->pkt); + start += net_pkt_ipv6_ext_len(cpkt->pkt); + start += NET_UDPH_LEN; + start += cpkt->hdr_len + cpkt->opt_len; + } else { + payload_size = 0; + } + } + + *len = payload_size; + return start; +} + +/* TODO: Remove in favor of using net_pkt APIs to add data to payload */ +int coap_packet_set_used(struct coap_packet *cpkt, u16_t len) +{ + if ((cpkt->pkt->frags->len + len) > + net_buf_tailroom(cpkt->pkt->frags)) { + return -ENOMEM; + } + + cpkt->pkt->frags->len += len; + + return 0; +} + /* - * ZoAP API needs to create the net_pkt buffer in the correct order. + * CoAP API needs to create the net_pkt buffer in the correct order. * This function performs last minute verification that outbuf is initialized. */ static void outbuf_init_check(struct lwm2m_output_context *out) { if (!out->outbuf) { - out->outbuf = zoap_packet_get_payload(out->out_zpkt, - &out->outsize); + out->outbuf = coap_packet_get_payload_ptr(out->out_cpkt, + &out->outsize, true); } } @@ -1883,16 +2221,19 @@ static int do_discover_op(struct lwm2m_engine_context *context) { struct lwm2m_output_context *out = context->out; struct lwm2m_engine_obj_inst *obj_inst; - int i = 0; + int i = 0, ret; /* set output content-format */ - zoap_add_option_int(out->out_zpkt, - ZOAP_OPTION_CONTENT_FORMAT, - LWM2M_FORMAT_APP_LINK_FORMAT); + ret = coap_append_option_int(out->out_cpkt, + COAP_OPTION_CONTENT_FORMAT, + LWM2M_FORMAT_APP_LINK_FORMAT); + if (ret < 0) { + return ret; + } /* init the outbuffer */ - out->outbuf = zoap_packet_get_payload(out->out_zpkt, - &out->outsize); + out->outbuf = coap_packet_get_payload_ptr(out->out_cpkt, + &out->outsize, true); /* ,**;ct=40 */ memcpy(out->outbuf, DISCOVER_PREFACE, strlen(DISCOVER_PREFACE)); @@ -1942,8 +2283,6 @@ int lwm2m_get_or_create_engine_obj(struct lwm2m_engine_context *context, return ret; } - zoap_header_set_code(context->in->in_zpkt, - ZOAP_RESPONSE_CODE_CREATED); /* set created flag to one */ if (created) { *created = 1; @@ -1980,29 +2319,14 @@ static int do_write_op(struct lwm2m_engine_obj *obj, } } -static int get_observe_option(const struct zoap_packet *zpkt) -{ - struct zoap_option option = {}; - u16_t count = 1; - int r; - - r = zoap_find_options(zpkt, ZOAP_OPTION_OBSERVE, &option, count); - if (r <= 0) { - return -ENOENT; - } - - return zoap_option_value_to_int(&option); -} - -static int handle_request(struct net_app_ctx *app_ctx, - struct zoap_packet *request, - struct zoap_packet *response) +static int handle_request(struct coap_packet *request, + struct lwm2m_message *msg) { int r; u8_t code; - struct zoap_option options[4]; + struct coap_option options[4]; struct lwm2m_engine_obj *obj; - const u8_t *token; + u8_t token[8]; u8_t tkl = 0; u16_t format, accept; struct lwm2m_input_context in; @@ -2011,6 +2335,9 @@ static int handle_request(struct net_app_ctx *app_ctx, struct lwm2m_engine_context context; int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */ bool discover = false; + struct block_context *block_ctx = NULL; + size_t block_offset = 0; + enum coap_block_size block_size; /* setup engine context */ memset(&context, 0, sizeof(struct lwm2m_engine_context)); @@ -2019,16 +2346,16 @@ static int handle_request(struct net_app_ctx *app_ctx, context.path = &path; engine_clear_context(&context); - /* set ZoAP request / response */ - in.in_zpkt = request; - out.out_zpkt = response; + /* set CoAP request / message */ + in.in_cpkt = request; + out.out_cpkt = &msg->cpkt; /* set default reader/writer */ in.reader = &plain_text_reader; out.writer = &plain_text_writer; /* parse the URL path into components */ - r = zoap_find_options(in.in_zpkt, ZOAP_OPTION_URI_PATH, options, 4); + r = coap_find_options(in.in_cpkt, COAP_OPTION_URI_PATH, options, 4); if (r > 0) { /* check for .well-known/core URI query (DISCOVER) */ if (r == 2 && @@ -2038,24 +2365,24 @@ static int handle_request(struct net_app_ctx *app_ctx, strncmp(options[1].value, "core", 4) == 0)) { discover = true; } else { - zoap_options_to_path(options, r, &path); + coap_options_to_path(options, r, &path); } } /* read Content Format */ - r = zoap_find_options(in.in_zpkt, ZOAP_OPTION_CONTENT_FORMAT, + r = coap_find_options(in.in_cpkt, COAP_OPTION_CONTENT_FORMAT, options, 1); if (r > 0) { - format = zoap_option_value_to_int(&options[0]); + format = coap_option_value_to_int(&options[0]); } else { SYS_LOG_DBG("No content-format given. Assume text plain."); format = LWM2M_FORMAT_PLAIN_TEXT; } /* read Accept */ - r = zoap_find_options(in.in_zpkt, ZOAP_OPTION_ACCEPT, options, 1); + r = coap_find_options(in.in_cpkt, COAP_OPTION_ACCEPT, options, 1); if (r > 0) { - accept = zoap_option_value_to_int(&options[0]); + accept = coap_option_value_to_int(&options[0]); } else { SYS_LOG_DBG("No accept option given. Assume OMA TLV."); accept = LWM2M_FORMAT_OMA_TLV; @@ -2063,7 +2390,7 @@ static int handle_request(struct net_app_ctx *app_ctx, /* TODO: Handle bootstrap deleted -- re-add when DTLS support ready */ - code = zoap_header_get_code(in.in_zpkt); + code = coap_header_get_code(in.in_cpkt); /* find registered obj */ obj = get_engine_obj(path.obj_id); @@ -2076,9 +2403,9 @@ static int handle_request(struct net_app_ctx *app_ctx, accept = select_writer(&out, accept); /* set the operation */ - switch (code & ZOAP_REQUEST_MASK) { + switch (code & COAP_REQUEST_MASK) { - case ZOAP_METHOD_GET: + case COAP_METHOD_GET: if (discover || format == LWM2M_FORMAT_APP_LINK_FORMAT) { context.operation = LWM2M_OP_DISCOVER; accept = LWM2M_FORMAT_APP_LINK_FORMAT; @@ -2086,60 +2413,98 @@ static int handle_request(struct net_app_ctx *app_ctx, context.operation = LWM2M_OP_READ; } /* check for observe */ - observe = get_observe_option(in.in_zpkt); - zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CONTENT); + observe = get_option_int(in.in_cpkt, COAP_OPTION_OBSERVE); + msg->code = COAP_RESPONSE_CODE_CONTENT; break; - case ZOAP_METHOD_POST: + case COAP_METHOD_POST: if (path.level < 2) { /* write/create a object instance */ context.operation = LWM2M_OP_CREATE; } else { context.operation = LWM2M_OP_EXECUTE; } - zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CHANGED); + msg->code = COAP_RESPONSE_CODE_CHANGED; break; - case ZOAP_METHOD_PUT: + case COAP_METHOD_PUT: context.operation = LWM2M_OP_WRITE; - zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CHANGED); + msg->code = COAP_RESPONSE_CODE_CHANGED; break; - case ZOAP_METHOD_DELETE: + case COAP_METHOD_DELETE: context.operation = LWM2M_OP_DELETE; - zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_DELETED); + msg->code = COAP_RESPONSE_CODE_DELETED; break; default: break; } - /* set response token */ - token = zoap_header_get_token(in.in_zpkt, &tkl); + /* setup response token */ + tkl = coap_header_get_token(in.in_cpkt, token); if (tkl) { - zoap_header_set_token(out.out_zpkt, token, tkl); + msg->tkl = tkl; + msg->token = token; } in.inpos = 0; - in.inbuf = zoap_packet_get_payload(in.in_zpkt, &in.insize); + in.inbuf = coap_packet_get_payload_ptr(in.in_cpkt, &in.insize, false); + + /* Check for block transfer */ + r = get_option_int(in.in_cpkt, COAP_OPTION_BLOCK1); + if (r > 0) { + /* RFC7252: 4.6. Message Size */ + block_size = GET_BLOCK_SIZE(r); + if (GET_MORE(r) && + coap_block_size_to_bytes(block_size) > in.insize) { + SYS_LOG_DBG("Trailing payload is discarded!"); + r = -EFBIG; + goto error; + } + + if (GET_BLOCK_NUM(r) == 0) { + r = init_block_ctx(token, tkl, &block_ctx); + } else { + r = get_block_ctx(token, tkl, &block_ctx); + } + + if (r < 0) { + goto error; + } - /* TODO: check for block transfer? */ + /* 0 will be returned if it's the last block */ + block_offset = coap_next_block(in.in_cpkt, &block_ctx->ctx); + } + + /* Handle blockwise 1 (Part 1): Set response code / free context */ + if (block_ctx) { + if (block_offset > 0) { + msg->code = COAP_RESPONSE_CODE_CONTINUE; + } else { + /* Free context when finished */ + free_block_ctx(block_ctx); + } + } + + /* render CoAP packet header */ + lwm2m_init_message(msg); switch (context.operation) { case LWM2M_OP_READ: if (observe == 0) { /* add new observer */ - if (token) { - r = zoap_add_option_int(out.out_zpkt, - ZOAP_OPTION_OBSERVE, 1); + if (msg->token) { + r = coap_append_option_int(out.out_cpkt, + COAP_OPTION_OBSERVE, + 1); if (r) { SYS_LOG_ERR("OBSERVE option error: %d", r); } - r = engine_add_observer(app_ctx, - token, tkl, &path, + r = engine_add_observer(msg, token, tkl, &path, accept); if (r < 0) { SYS_LOG_ERR("add OBSERVE error: %d", r); @@ -2149,7 +2514,7 @@ static int handle_request(struct net_app_ctx *app_ctx, } } else if (observe == 1) { /* use token from this request */ - token = zoap_header_get_token(in.in_zpkt, &tkl); + tkl = coap_header_get_token(in.in_cpkt, token); /* remove observer */ r = engine_remove_observer(token, tkl); if (r < 0) { @@ -2158,8 +2523,9 @@ static int handle_request(struct net_app_ctx *app_ctx, } /* set output content-format */ - r = zoap_add_option_int(out.out_zpkt, - ZOAP_OPTION_CONTENT_FORMAT, accept); + r = coap_append_option_int(out.out_cpkt, + COAP_OPTION_CONTENT_FORMAT, + accept); if (r > 0) { SYS_LOG_ERR("Error setting response content-format: %d", r); @@ -2194,60 +2560,70 @@ static int handle_request(struct net_app_ctx *app_ctx, return -EINVAL; } - if (r == 0) { - /* TODO: Handle blockwise 1 */ + if (r) { + goto error; + } - if (out.outlen > 0) { - SYS_LOG_DBG("replying with %u bytes", out.outlen); - zoap_packet_set_used(out.out_zpkt, out.outlen); - } else { - SYS_LOG_DBG("no data in reply"); + /* Handle blockwise 1 (Part 2): Append BLOCK1 option */ + if (block_ctx) { + if (block_offset > 0) { + /* More to come, ack with correspond block # */ + r = coap_append_block1_option(out.out_cpkt, + &block_ctx->ctx); + if (r) { + /* report as internal server error */ + SYS_LOG_ERR("Fail adding block1 option: %d", r); + r = -EINVAL; + goto error; + } } + } + + if (out.outlen > 0) { + SYS_LOG_DBG("replying with %u bytes", out.outlen); + coap_packet_set_used(out.out_cpkt, out.outlen); } else { + SYS_LOG_DBG("no data in reply"); + } + + return 0; + +error: + if (r < 0) { + /* TODO: reset CoAP packet and empty payload */ if (r == -ENOENT) { - zoap_header_set_code(out.out_zpkt, - ZOAP_RESPONSE_CODE_NOT_FOUND); - r = 0; + msg->code = COAP_RESPONSE_CODE_NOT_FOUND; } else if (r == -EPERM) { - zoap_header_set_code(out.out_zpkt, - ZOAP_RESPONSE_CODE_NOT_ALLOWED); - r = 0; + msg->code = COAP_RESPONSE_CODE_NOT_ALLOWED; } else if (r == -EEXIST) { - zoap_header_set_code(out.out_zpkt, - ZOAP_RESPONSE_CODE_BAD_REQUEST); - r = 0; + msg->code = COAP_RESPONSE_CODE_BAD_REQUEST; + } else if (r == -EFBIG) { + msg->code = COAP_RESPONSE_CODE_REQUEST_TOO_LARGE; } else { /* Failed to handle the request */ - zoap_header_set_code(out.out_zpkt, - ZOAP_RESPONSE_CODE_INTERNAL_ERROR); - r = 0; + msg->code = COAP_RESPONSE_CODE_INTERNAL_ERROR; } } - return r; -} + /* Free block context when error happened */ + free_block_ctx(block_ctx); -int lwm2m_udp_sendto(struct net_app_ctx *app_ctx, struct net_pkt *pkt) -{ - return net_app_send_pkt(app_ctx, pkt, &app_ctx->default_ctx->remote, - NET_SOCKADDR_MAX_SIZE, K_NO_WAIT, NULL); + return 0; } void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt, bool handle_separate_response, - int (*udp_request_handler)(struct net_app_ctx *app_ctx, - struct zoap_packet *, - struct zoap_packet *)) + udp_request_handler_cb_t udp_request_handler) { struct net_udp_hdr hdr, *udp_hdr; - struct zoap_pending *pending; - struct zoap_reply *reply; - struct zoap_packet response; + struct coap_pending *pending; + struct coap_reply *reply; + struct coap_packet response; struct sockaddr from_addr; - struct zoap_packet response2; - struct net_pkt *pkt2; - int header_len, r; - const u8_t *token; + struct lwm2m_message *msg = NULL; + struct coap_option options[4]; + int r; + u8_t token[8]; u8_t tkl; udp_hdr = net_udp_get_hdr(pkt, &hdr); @@ -2275,72 +2651,35 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt, } #endif - /* - * zoap expects that buffer->data starts at the - * beginning of the CoAP header - */ - header_len = net_pkt_appdata(pkt) - pkt->frags->data; - net_buf_pull(pkt->frags, header_len); - - r = zoap_packet_parse(&response, pkt); + r = coap_packet_parse(&response, pkt, options, 4); if (r < 0) { SYS_LOG_ERR("Invalid data received (err:%d)", r); goto cleanup; } - token = zoap_header_get_token(&response, &tkl); - pending = zoap_pending_received(&response, client_ctx->pendings, + tkl = coap_header_get_token(&response, token); + pending = coap_pending_received(&response, client_ctx->pendings, CONFIG_LWM2M_ENGINE_MAX_PENDING); + /* + * Clear pending pointer because coap_pending_received() calls + * coap_pending_clear, and later when we call lwm2m_release_message() + * it will try and call coap_pending_clear() again if msg->pending + * is != NULL. + */ if (pending) { - /* TODO: If necessary cancel retransmissions */ + msg = find_msg(pending, NULL, client_ctx->messages, + CONFIG_LWM2M_ENGINE_MAX_MESSAGES); + if (msg) { + msg->pending = NULL; + } } SYS_LOG_DBG("checking for reply from [%s]", lwm2m_sprint_ip_addr(&from_addr)); - reply = zoap_response_received(&response, &from_addr, + reply = coap_response_received(&response, &from_addr, client_ctx->replies, CONFIG_LWM2M_ENGINE_MAX_REPLIES); - if (!reply) { - /* - * If no normal response handler is found, then this is - * a new request coming from the server. Let's look - * at registered objects to find a handler. - */ - if (udp_request_handler && - zoap_header_get_type(&response) == ZOAP_TYPE_CON) { - /* Create a response packet if we reach this point */ - r = lwm2m_init_message(&client_ctx->net_app_ctx, - &response2, &pkt2, - ZOAP_TYPE_ACK, - zoap_header_get_code(&response), - zoap_header_get_id(&response), - NULL, -1); - if (r < 0) { - if (pkt2) { - net_pkt_unref(pkt2); - } - goto cleanup; - } - - /* - * The "response" here is actually a new request - */ - r = udp_request_handler(&client_ctx->net_app_ctx, - &response, &response2); - if (r < 0) { - SYS_LOG_ERR("Request handler error: %d", r); - } else { - r = lwm2m_udp_sendto(&client_ctx->net_app_ctx, - pkt2); - if (r < 0) { - SYS_LOG_ERR("Err sending response: %d", - r); - } - } - } else { - SYS_LOG_ERR("No handler for response"); - } - } else { + if (reply) { /* * Separate response is composed of 2 messages, empty ACK with * no token and an additional message with a matching token id @@ -2352,14 +2691,63 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt, * additional flag to decide when to clear the reply callback. */ if (handle_separate_response && !tkl && - zoap_header_get_type(&response) == ZOAP_TYPE_ACK) { + coap_header_get_type(&response) == COAP_TYPE_ACK) { SYS_LOG_DBG("separated response, not removing reply"); - } else { - SYS_LOG_DBG("reply %p handled and removed", reply); - zoap_reply_clear(reply); + goto cleanup; + } + + if (!msg) { + msg = find_msg(pending, reply, client_ctx->messages, + CONFIG_LWM2M_ENGINE_MAX_MESSAGES); } } + if (reply || pending) { + /* free up msg resources */ + if (msg) { + lwm2m_release_message(msg); + } + + SYS_LOG_DBG("reply %p handled and removed", reply); + goto cleanup; + } + + /* + * If no normal response handler is found, then this is + * a new request coming from the server. Let's look + * at registered objects to find a handler. + */ + if (udp_request_handler && + coap_header_get_type(&response) == COAP_TYPE_CON) { + msg = lwm2m_get_message(client_ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + goto cleanup; + } + + /* Create a response message if we reach this point */ + msg->type = COAP_TYPE_ACK; + msg->code = coap_header_get_code(&response); + msg->mid = coap_header_get_id(&response); + /* skip token generation by default */ + msg->tkl = LWM2M_MSG_TOKEN_LEN_SKIP; + + /* process the response to this request */ + r = udp_request_handler(&response, msg); + if (r < 0) { + goto cleanup; + } + + r = lwm2m_send_message(msg); + if (r < 0) { + SYS_LOG_ERR("Err sending response: %d", + r); + lwm2m_release_message(msg); + } + } else { + SYS_LOG_ERR("No handler for response"); + } + cleanup: if (pkt) { net_pkt_unref(pkt); @@ -2379,48 +2767,63 @@ static void udp_receive(struct net_app_ctx *app_ctx, struct net_pkt *pkt, static void retransmit_request(struct k_work *work) { struct lwm2m_ctx *client_ctx; - struct zoap_pending *pending; + struct lwm2m_message *msg; + struct coap_pending *pending; int r; client_ctx = CONTAINER_OF(work, struct lwm2m_ctx, retransmit_work); - pending = zoap_pending_next_to_expire(client_ctx->pendings, + pending = coap_pending_next_to_expire(client_ctx->pendings, CONFIG_LWM2M_ENGINE_MAX_PENDING); if (!pending) { return; } - r = lwm2m_udp_sendto(&client_ctx->net_app_ctx, - pending->pkt); - if (r < 0) { + msg = find_msg(pending, NULL, client_ctx->messages, + CONFIG_LWM2M_ENGINE_MAX_MESSAGES); + if (!msg) { + SYS_LOG_ERR("pending has no valid LwM2M message!"); return; } - if (!zoap_pending_cycle(pending)) { - zoap_pending_clear(pending); + if (!coap_pending_cycle(pending)) { + /* pending request has expired */ + if (msg->message_timeout_cb) { + msg->message_timeout_cb(msg); + } + + /* final unref to release pkt */ + net_pkt_unref(pending->pkt); + lwm2m_release_message(msg); return; } + r = lwm2m_send_message(msg); + if (r < 0) { + SYS_LOG_ERR("Error sending lwm2m message: %d", r); + /* don't error here, retry until timeout */ + } + k_delayed_work_submit(&client_ctx->retransmit_work, pending->timeout); } -static int notify_message_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static int notify_message_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { int ret = 0; u8_t type, code; - type = zoap_header_get_type(response); - code = zoap_header_get_code(response); + type = coap_header_get_type(response); + code = coap_header_get_code(response); SYS_LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'", type, - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code), + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code), sprint_token(reply->token, reply->tkl)); - /* remove observer on ZOAP_TYPE_RESET */ - if (type == ZOAP_TYPE_RESET) { + /* remove observer on COAP_TYPE_RESET */ + if (type == COAP_TYPE_RESET) { if (reply->tkl > 0) { ret = engine_remove_observer(reply->token, reply->tkl); if (ret) { @@ -2437,10 +2840,7 @@ static int notify_message_reply_cb(const struct zoap_packet *response, static int generate_notify_message(struct observe_node *obs, bool manual_trigger) { - struct net_pkt *pkt = NULL; - struct zoap_pending *pending = NULL; - struct zoap_reply *reply = NULL; - struct zoap_packet request; + struct lwm2m_message *msg; struct lwm2m_engine_obj_inst *obj_inst; struct lwm2m_output_context out; struct lwm2m_engine_context context; @@ -2460,7 +2860,6 @@ static int generate_notify_message(struct observe_node *obs, memcpy(&path, &obs->path, sizeof(struct lwm2m_obj_path)); context.path = &path; context.operation = LWM2M_OP_READ; - out.out_zpkt = &request; SYS_LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld", manual_trigger ? "MANUAL" : "AUTO", @@ -2482,18 +2881,31 @@ static int generate_notify_message(struct observe_node *obs, return -EINVAL; } - ret = lwm2m_init_message(&obs->ctx->net_app_ctx, out.out_zpkt, &pkt, - ZOAP_TYPE_CON, ZOAP_RESPONSE_CODE_CONTENT, - 0, obs->token, obs->tkl); - if (ret) { + msg = lwm2m_get_message(obs->ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + return -ENOMEM; + } + + msg->type = COAP_TYPE_CON; + msg->code = COAP_RESPONSE_CODE_CONTENT; + msg->mid = 0; + msg->token = obs->token; + msg->tkl = obs->tkl; + msg->reply_cb = notify_message_reply_cb; + out.out_cpkt = &msg->cpkt; + + ret = lwm2m_init_message(msg); + if (ret < 0) { + SYS_LOG_ERR("Unable to init lwm2m message! (err: %d)", ret); goto cleanup; } /* each notification should increment the obs counter */ obs->counter++; - ret = zoap_add_option_int(out.out_zpkt, ZOAP_OPTION_OBSERVE, - obs->counter); - if (ret) { + ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE, + obs->counter); + if (ret < 0) { SYS_LOG_ERR("OBSERVE option error: %d", ret); goto cleanup; } @@ -2502,9 +2914,9 @@ static int generate_notify_message(struct observe_node *obs, select_writer(&out, obs->format); /* set response content-format */ - ret = zoap_add_option_int(out.out_zpkt, ZOAP_OPTION_CONTENT_FORMAT, - obs->format); - if (ret > 0) { + ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT, + obs->format); + if (ret < 0) { SYS_LOG_ERR("error setting content-format (err:%d)", ret); goto cleanup; } @@ -2512,7 +2924,7 @@ static int generate_notify_message(struct observe_node *obs, ret = do_read_op(obj_inst->obj, &context); if (ret == 0) { if (out.outlen > 0) { - zoap_packet_set_used(out.out_zpkt, out.outlen); + coap_packet_set_used(out.out_cpkt, out.outlen); } else { SYS_LOG_DBG("no data in reply"); } @@ -2521,36 +2933,17 @@ static int generate_notify_message(struct observe_node *obs, goto cleanup; } - pending = lwm2m_init_message_pending(obs->ctx, out.out_zpkt); - if (!pending) { - ret = -ENOMEM; - goto cleanup; - } - - reply = zoap_reply_next_unused(obs->ctx->replies, - CONFIG_LWM2M_ENGINE_MAX_REPLIES); - if (!reply) { - SYS_LOG_ERR("No resources for waiting for replies."); - ret = -ENOMEM; - goto cleanup; - } - - zoap_reply_init(reply, &request); - reply->reply = notify_message_reply_cb; - - ret = lwm2m_udp_sendto(&obs->ctx->net_app_ctx, pkt); + ret = lwm2m_send_message(msg); if (ret < 0) { SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret); goto cleanup; } - SYS_LOG_DBG("NOTIFY MSG: SENT"); - zoap_pending_cycle(pending); - k_delayed_work_submit(&obs->ctx->retransmit_work, pending->timeout); - return ret; + SYS_LOG_DBG("NOTIFY MSG: SENT"); + return 0; cleanup: - lwm2m_init_message_cleanup(pkt, pending, reply); + lwm2m_release_message(msg); return ret; } @@ -2608,6 +3001,34 @@ int lwm2m_engine_set_net_pkt_pool(struct lwm2m_ctx *ctx, } #endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ +void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx) +{ + k_delayed_work_init(&client_ctx->retransmit_work, retransmit_request); + +#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) + net_app_set_net_pkt_pool(&client_ctx->net_app_ctx, + client_ctx->tx_slab, client_ctx->data_pool); +#endif +} + +#if defined(CONFIG_NET_APP_DTLS) +static int setup_cert(struct net_app_ctx *app_ctx, void *cert) +{ +#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) + struct lwm2m_ctx *client_ctx = CONTAINER_OF(app_ctx, + struct lwm2m_ctx, + net_app_ctx); + + mbedtls_ssl_conf_psk(&app_ctx->tls.mbedtls.conf, + (const unsigned char *)client_ctx->client_psk, + client_ctx->client_psk_len, + (const unsigned char *)client_ctx->client_psk_id, + client_ctx->client_psk_id_len); +#endif + return 0; +} +#endif /* CONFIG_NET_APP_DTLS */ + int lwm2m_engine_start(struct lwm2m_ctx *client_ctx, char *peer_str, u16_t peer_port) { @@ -2625,12 +3046,7 @@ int lwm2m_engine_start(struct lwm2m_ctx *client_ctx, goto error_start; } - k_delayed_work_init(&client_ctx->retransmit_work, retransmit_request); - -#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) - net_app_set_net_pkt_pool(&client_ctx->net_app_ctx, - client_ctx->tx_slab, client_ctx->data_pool); -#endif + lwm2m_engine_context_init(client_ctx); /* set net_app callbacks */ ret = net_app_set_cb(&client_ctx->net_app_ctx, @@ -2640,6 +3056,24 @@ int lwm2m_engine_start(struct lwm2m_ctx *client_ctx, goto error_start; } +#if defined(CONFIG_NET_APP_DTLS) + ret = net_app_client_tls(&client_ctx->net_app_ctx, + client_ctx->dtls_result_buf, + client_ctx->dtls_result_buf_len, + INSTANCE_INFO, + strlen(INSTANCE_INFO), + setup_cert, + client_ctx->cert_host, + NULL, + client_ctx->dtls_pool, + client_ctx->dtls_stack, + client_ctx->dtls_stack_len); + if (ret < 0) { + SYS_LOG_ERR("Cannot init DTLS (%d)", ret); + goto error_start; + } +#endif + ret = net_app_connect(&client_ctx->net_app_ctx, client_ctx->net_timeout); if (ret < 0) { @@ -2657,6 +3091,9 @@ int lwm2m_engine_start(struct lwm2m_ctx *client_ctx, static int lwm2m_engine_init(struct device *dev) { + memset(block1_contexts, 0, + sizeof(struct block_context) * NUM_BLOCK1_CONTEXT); + /* start thread to handle OBSERVER / NOTIFY events */ k_thread_create(&engine_thread_data, &engine_thread_stack[0], diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.h b/subsys/net/lib/lwm2m/lwm2m_engine.h index 829977989a608..0ac01147c33ff 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.h +++ b/subsys/net/lib/lwm2m/lwm2m_engine.h @@ -25,13 +25,20 @@ #define LWM2M_FORMAT_OMA_JSON 11543 -#define ZOAP_RESPONSE_CODE_CLASS(x) (x >> 5) -#define ZOAP_RESPONSE_CODE_DETAIL(x) (x & 0x1F) +#define COAP_RESPONSE_CODE_CLASS(x) (x >> 5) +#define COAP_RESPONSE_CODE_DETAIL(x) (x & 0x1F) /* TODO: */ #define NOTIFY_OBSERVER(o, i, r) lwm2m_notify_observer(o, i, r) #define NOTIFY_OBSERVER_PATH(path) lwm2m_notify_observer_path(path) +/* Use this value to skip token generation in an lwm2m_msg */ +#define LWM2M_MSG_TOKEN_LEN_SKIP 0xFF + +/* Establish a request handler callback type */ +typedef int (*udp_request_handler_cb_t)(struct coap_packet *request, + struct lwm2m_message *msg); + char *lwm2m_sprint_ip_addr(const struct sockaddr *addr); int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id); @@ -48,14 +55,14 @@ int lwm2m_get_or_create_engine_obj(struct lwm2m_engine_context *context, struct lwm2m_engine_obj_inst **obj_inst, u8_t *created); -int lwm2m_init_message(struct net_app_ctx *app_ctx, struct zoap_packet *zpkt, - struct net_pkt **pkt, u8_t type, u8_t code, u16_t mid, - const u8_t *token, u8_t tkl); -struct zoap_pending *lwm2m_init_message_pending(struct lwm2m_ctx *client_ctx, - struct zoap_packet *zpkt); -void lwm2m_init_message_cleanup(struct net_pkt *pkt, - struct zoap_pending *pending, - struct zoap_reply *reply); +/* LwM2M context functions */ +void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx); + +/* LwM2M message functions */ +struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx); +void lwm2m_release_message(struct lwm2m_message *msg); +int lwm2m_init_message(struct lwm2m_message *msg); +int lwm2m_send_message(struct lwm2m_message *msg); u16_t lwm2m_get_rd_data(u8_t *client_data, u16_t size); @@ -64,11 +71,22 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_obj_field *obj_field, struct lwm2m_engine_context *context); -int lwm2m_udp_sendto(struct net_app_ctx *app_ctx, struct net_pkt *pkt); +/* CoAP payload functions */ +u8_t *coap_packet_get_payload_ptr(struct coap_packet *cpkt, u16_t *len, + bool start_marker); +int coap_packet_set_used(struct coap_packet *cpkt, u16_t len); + void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt, bool handle_separate_response, - int (*udp_request_handler)(struct net_app_ctx *app_ctx, - struct zoap_packet *request, - struct zoap_packet *response)); + udp_request_handler_cb_t udp_request_handler); + +enum coap_block_size lwm2m_default_block_size(void); + +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT) +u8_t lwm2m_firmware_get_update_state(void); +void lwm2m_firmware_set_update_state(u8_t state); +void lwm2m_firmware_set_update_result(u8_t result); +u8_t lwm2m_firmware_get_update_result(void); +#endif #endif /* LWM2M_ENGINE_H */ diff --git a/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c b/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c index 7c611bae0f330..828363c0e9e78 100644 --- a/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c +++ b/subsys/net/lib/lwm2m/lwm2m_obj_firmware.c @@ -12,7 +12,7 @@ #define SYS_LOG_DOMAIN "lwm2m_obj_firmware" #define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL #include -#include +#include #include #include @@ -59,22 +59,163 @@ static struct lwm2m_engine_obj_inst inst; static struct lwm2m_engine_res_inst res[FIRMWARE_MAX_ID]; static lwm2m_engine_set_data_cb_t write_cb; +static lwm2m_engine_exec_cb_t update_cb; #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT extern int lwm2m_firmware_start_transfer(char *package_uri); #endif +u8_t lwm2m_firmware_get_update_state(void) +{ + return update_state; +} + +void lwm2m_firmware_set_update_state(u8_t state) +{ + bool error = false; + + /* Check LWM2M SPEC appendix E.6.1 */ + switch (state) { + case STATE_DOWNLOADING: + if (update_state != STATE_IDLE) { + error = true; + } + break; + case STATE_DOWNLOADED: + if (update_state != STATE_DOWNLOADING && + update_state != STATE_UPDATING) { + error = true; + } + break; + case STATE_UPDATING: + if (update_state != STATE_DOWNLOADED) { + error = true; + } + break; + case STATE_IDLE: + break; + default: + SYS_LOG_ERR("Unhandled state: %u", state); + return; + } + + if (error) { + SYS_LOG_ERR("Invalid state transition: %u -> %u", + update_state, state); + } + + update_state = state; + NOTIFY_OBSERVER(LWM2M_OBJECT_FIRMWARE_ID, 0, FIRMWARE_STATE_ID); + SYS_LOG_DBG("Update state = %d", update_state); +} + +u8_t lwm2m_firmware_get_update_result(void) +{ + return update_result; +} + +void lwm2m_firmware_set_update_result(u8_t result) +{ + u8_t state; + bool error = false; + + /* Check LWM2M SPEC appendix E.6.1 */ + switch (result) { + case RESULT_DEFAULT: + lwm2m_firmware_set_update_state(STATE_IDLE); + break; + case RESULT_SUCCESS: + if (update_state != STATE_UPDATING) { + error = true; + state = update_state; + } + + lwm2m_firmware_set_update_state(STATE_IDLE); + break; + case RESULT_NO_STORAGE: + case RESULT_OUT_OF_MEM: + case RESULT_CONNECTION_LOST: + case RESULT_UNSUP_FW: + case RESULT_INVALID_URI: + case RESULT_UNSUP_PROTO: + if (update_state != STATE_DOWNLOADING) { + error = true; + state = update_state; + } + + lwm2m_firmware_set_update_state(STATE_IDLE); + break; + case RESULT_INTEGRITY_FAILED: + if (update_state != STATE_DOWNLOADING && + update_state != STATE_UPDATING) { + error = true; + state = update_state; + } + + lwm2m_firmware_set_update_state(STATE_IDLE); + break; + case RESULT_UPDATE_FAILED: + if (update_state != STATE_UPDATING) { + error = true; + state = update_state; + } + + /* Next state could be idle or downloaded */ + break; + default: + SYS_LOG_ERR("Unhandled result: %u", result); + return; + } + + if (error) { + SYS_LOG_ERR("Unexpected result(%u) set while state is %u", + result, state); + } + + update_result = result; + NOTIFY_OBSERVER(LWM2M_OBJECT_FIRMWARE_ID, 0, FIRMWARE_UPDATE_RESULT_ID); + SYS_LOG_DBG("Update result = %d", update_result); +} + static int package_write_cb(u16_t obj_inst_id, u8_t *data, u16_t data_len, bool last_block, size_t total_size) { - SYS_LOG_DBG("PACKAGE WRITE"); + u8_t state; + int ret = 0; + + state = lwm2m_firmware_get_update_state(); + if (state == STATE_IDLE) { + /* TODO: setup timer to check download status, + * make sure it fail after timeout + */ + lwm2m_firmware_set_update_state(STATE_DOWNLOADING); + } else if (state != STATE_DOWNLOADING) { + if (data_len == 0 && state == STATE_DOWNLOADED) { + /* reset to state idle and result default */ + lwm2m_firmware_set_update_result(RESULT_DEFAULT); + return 1; + } + + SYS_LOG_DBG("Cannot download: state = %d", state); + return -EPERM; + } + if (write_cb) { - write_cb(obj_inst_id, data, data_len, last_block, total_size); - return 1; + ret = write_cb(obj_inst_id, data, data_len, + last_block, total_size); + if (ret < 0) { + SYS_LOG_ERR("Failed to store firmware: %d", ret); + lwm2m_firmware_set_update_result( + RESULT_INTEGRITY_FAILED); + } } - return 0; + if (last_block) { + lwm2m_firmware_set_update_state(STATE_DOWNLOADED); + } + + return 1; } static int package_uri_write_cb(u16_t obj_inst_id, @@ -82,10 +223,21 @@ static int package_uri_write_cb(u16_t obj_inst_id, bool last_block, size_t total_size) { SYS_LOG_DBG("PACKAGE_URI WRITE: %s", package_uri); -#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT - lwm2m_firmware_start_transfer(package_uri); - return 1; -#endif + + if (IS_ENABLED(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT)) { + u8_t state = lwm2m_firmware_get_update_state(); + + if (state == STATE_IDLE) { + lwm2m_firmware_set_update_result(RESULT_DEFAULT); + lwm2m_firmware_start_transfer(package_uri); + } else if (state == STATE_DOWNLOADED && data_len == 0) { + /* reset to state idle and result default */ + lwm2m_firmware_set_update_result(RESULT_DEFAULT); + } + + return 1; + } + return 0; } @@ -99,6 +251,48 @@ lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void) return write_cb; } +void lwm2m_firmware_set_update_cb(lwm2m_engine_exec_cb_t cb) +{ + update_cb = cb; +} + +lwm2m_engine_exec_cb_t lwm2m_firmware_get_update_cb(void) +{ + return update_cb; +} + +static int firmware_update_cb(u16_t obj_inst_id) +{ + lwm2m_engine_exec_cb_t callback; + u8_t state; + int ret; + + state = lwm2m_firmware_get_update_state(); + if (state != STATE_DOWNLOADED) { + /* TODO: pass response code to caller, -1 will be shown as + * 4.05 method not allowed in current implementation + */ + SYS_LOG_ERR("State other than downloaded: %d", state); + return -1; + } + + lwm2m_firmware_set_update_state(STATE_UPDATING); + + callback = lwm2m_firmware_get_update_cb(); + if (callback) { + ret = callback(obj_inst_id); + if (ret < 0) { + SYS_LOG_ERR("Failed to update firmware: %d", ret); + lwm2m_firmware_set_update_result( + ret == -EINVAL ? RESULT_INTEGRITY_FAILED : + RESULT_UPDATE_FAILED); + return 0; + } + } + + return 0; +} + static struct lwm2m_engine_obj_inst *firmware_create(u16_t obj_inst_id) { int i = 0; @@ -109,7 +303,8 @@ static struct lwm2m_engine_obj_inst *firmware_create(u16_t obj_inst_id) INIT_OBJ_RES(res, i, FIRMWARE_PACKAGE_URI_ID, 0, package_uri, PACKAGE_URI_LEN, NULL, NULL, package_uri_write_cb, NULL); - INIT_OBJ_RES_DUMMY(res, i, FIRMWARE_UPDATE_ID); + INIT_OBJ_RES_EXECUTE(res, i, FIRMWARE_UPDATE_ID, + firmware_update_cb); INIT_OBJ_RES_DATA(res, i, FIRMWARE_STATE_ID, &update_state, sizeof(update_state)); INIT_OBJ_RES_DATA(res, i, FIRMWARE_UPDATE_RESULT_ID, @@ -130,6 +325,8 @@ static int lwm2m_firmware_init(struct device *dev) /* Set default values */ package_uri[0] = '\0'; + /* Initialize state machine */ + /* TODO: should be restored from the permanent storage */ update_state = STATE_IDLE; update_result = RESULT_DEFAULT; #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT diff --git a/subsys/net/lib/lwm2m/lwm2m_obj_firmware_pull.c b/subsys/net/lib/lwm2m/lwm2m_obj_firmware_pull.c index bb28b919054d9..02e679a240cd2 100644 --- a/subsys/net/lib/lwm2m/lwm2m_obj_firmware_pull.c +++ b/subsys/net/lib/lwm2m/lwm2m_obj_firmware_pull.c @@ -4,40 +4,44 @@ * SPDX-License-Identifier: Apache-2.0 */ -/* - * TODO: - * Support PULL transfer method (from server) - */ - #define SYS_LOG_DOMAIN "lwm2m_obj_firmware_pull" #define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL #include +#include #include -#include +#include +#include #include #include +#include #include #include #include "lwm2m_object.h" #include "lwm2m_engine.h" -#define STATE_IDLE 0 -#define STATE_CONNECTING 1 - #define PACKAGE_URI_LEN 255 #define BUF_ALLOC_TIMEOUT K_SECONDS(1) #define NETWORK_INIT_TIMEOUT K_SECONDS(10) #define NETWORK_CONNECT_TIMEOUT K_SECONDS(10) +#define PACKET_TRANSFER_RETRY_MAX 3 -static u8_t transfer_state; static struct k_work firmware_work; static char firmware_uri[PACKAGE_URI_LEN]; static struct sockaddr firmware_addr; +static struct http_parser_url parsed_uri; static struct lwm2m_ctx firmware_ctx; -static struct zoap_block_context firmware_block_ctx; +static int firmware_retry; +static struct coap_block_context firmware_block_ctx; + +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT) +#define PROXY_URI_LEN 255 +static char proxy_uri[PROXY_URI_LEN]; +#endif + +static void do_transmit_timeout_cb(struct lwm2m_message *msg); static void firmware_udp_receive(struct net_app_ctx *app_ctx, struct net_pkt *pkt, @@ -46,218 +50,424 @@ firmware_udp_receive(struct net_app_ctx *app_ctx, struct net_pkt *pkt, lwm2m_udp_receive(&firmware_ctx, pkt, true, NULL); } -static void retransmit_request(struct k_work *work) +static int transfer_request(struct coap_block_context *ctx, + u8_t *token, u8_t tkl, + coap_reply_t reply_cb) { - struct zoap_pending *pending; - int r; + struct lwm2m_message *msg; + int ret; +#if !defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT) + int i; + int path_len; + char *cursor; + u16_t off; + u16_t len; +#endif - pending = zoap_pending_next_to_expire(firmware_ctx.pendings, - CONFIG_LWM2M_ENGINE_MAX_PENDING); - if (!pending) { - return; + msg = lwm2m_get_message(&firmware_ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + return -ENOMEM; } - r = lwm2m_udp_sendto(&firmware_ctx.net_app_ctx, pending->pkt); - if (r < 0) { - return; - } + msg->type = COAP_TYPE_CON; + msg->code = COAP_METHOD_GET; + msg->mid = 0; + msg->token = token; + msg->tkl = tkl; + msg->reply_cb = reply_cb; + msg->message_timeout_cb = do_transmit_timeout_cb; - if (!zoap_pending_cycle(pending)) { - zoap_pending_clear(pending); - return; + ret = lwm2m_init_message(msg); + if (ret) { + goto cleanup; } - k_delayed_work_submit(&firmware_ctx.retransmit_work, pending->timeout); -} +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT) + char *uri_path = + CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_URI_PATH; + ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, + uri_path, strlen(uri_path)); + if (ret < 0) { + SYS_LOG_ERR("Error adding URI_PATH '%s'", uri_path); + goto cleanup; + } +#else + /* if path is not available, off/len will be zero */ + off = parsed_uri.field_data[UF_PATH].off; + len = parsed_uri.field_data[UF_PATH].len; + cursor = firmware_uri + off; + path_len = 0; + + for (i = 0; i < len; i++) { + if (firmware_uri[off + i] == '/') { + if (path_len > 0) { + ret = coap_packet_append_option(&msg->cpkt, + COAP_OPTION_URI_PATH, + cursor, path_len); + if (ret < 0) { + SYS_LOG_ERR("Error adding URI_PATH"); + goto cleanup; + } + + cursor += path_len + 1; + path_len = 0; + } else { + /* skip current slash */ + cursor += 1; + } + continue; + } -static int transfer_request(struct zoap_block_context *ctx, - const u8_t *token, u8_t tkl, - zoap_reply_t reply_cb) -{ - struct zoap_packet request; - struct net_pkt *pkt = NULL; - struct zoap_pending *pending = NULL; - struct zoap_reply *reply = NULL; - int ret; + if (i == len - 1) { + /* flush the rest */ + ret = coap_packet_append_option(&msg->cpkt, + COAP_OPTION_URI_PATH, + cursor, path_len + 1); + if (ret < 0) { + SYS_LOG_ERR("Error adding URI_PATH"); + goto cleanup; + } + break; + } + path_len += 1; + } +#endif - ret = lwm2m_init_message(&firmware_ctx.net_app_ctx, &request, &pkt, - ZOAP_TYPE_CON, ZOAP_METHOD_GET, - 0, token, tkl); + ret = coap_append_block2_option(&msg->cpkt, ctx); if (ret) { + SYS_LOG_ERR("Unable to add block2 option."); goto cleanup; } - /* hard code URI path here -- should be pulled from package_uri */ - ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - "large-create", sizeof("large-create") - 1); - ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - "1", sizeof("1") - 1); +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT) + ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_PROXY_URI, + firmware_uri, strlen(firmware_uri)); if (ret < 0) { - SYS_LOG_ERR("Error adding URI_QUERY 'large'"); + SYS_LOG_ERR("Error adding PROXY_URI '%s'", firmware_uri); goto cleanup; } - - ret = zoap_add_block2_option(&request, ctx); +#else + /* Ask the server to provide a size estimate */ + ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_SIZE2, 0); if (ret) { - SYS_LOG_ERR("Unable to add block2 option."); + SYS_LOG_ERR("Unable to add size2 option."); goto cleanup; } +#endif - pending = lwm2m_init_message_pending(&firmware_ctx, &request); - if (!pending) { - ret = -ENOMEM; + /* send request */ + ret = lwm2m_send_message(msg); + if (ret < 0) { + SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret); goto cleanup; } - /* set the reply handler */ - if (reply_cb) { - reply = zoap_reply_next_unused(firmware_ctx.replies, - CONFIG_LWM2M_ENGINE_MAX_REPLIES); - if (!reply) { - SYS_LOG_ERR("No resources for waiting for replies."); - ret = -ENOMEM; - goto cleanup; - } + return 0; + +cleanup: + lwm2m_release_message(msg); - zoap_reply_init(reply, &request); - reply->reply = reply_cb; + if (ret == -ENOMEM) { + lwm2m_firmware_set_update_result(RESULT_OUT_OF_MEM); + } else { + lwm2m_firmware_set_update_result(RESULT_CONNECTION_LOST); } - /* send request */ - ret = lwm2m_udp_sendto(&firmware_ctx.net_app_ctx, pkt); + return ret; +} + +static int transfer_empty_ack(u16_t mid) +{ + struct lwm2m_message *msg; + int ret; + + msg = lwm2m_get_message(&firmware_ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + return -ENOMEM; + } + + msg->type = COAP_TYPE_ACK; + msg->code = COAP_CODE_EMPTY; + msg->mid = mid; + + ret = lwm2m_init_message(msg); + if (ret) { + goto cleanup; + } + + ret = lwm2m_send_message(msg); if (ret < 0) { SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret); goto cleanup; } - zoap_pending_cycle(pending); - k_delayed_work_submit(&firmware_ctx.retransmit_work, pending->timeout); return 0; cleanup: - lwm2m_init_message_cleanup(pkt, pending, reply); + lwm2m_release_message(msg); return ret; } static int -do_firmware_transfer_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +do_firmware_transfer_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { int ret; size_t transfer_offset = 0; - const u8_t *token; + u8_t token[8]; u8_t tkl; u16_t payload_len; u8_t *payload; - struct zoap_packet *check_response = (struct zoap_packet *)response; + struct coap_packet *check_response = (struct coap_packet *)response; lwm2m_engine_set_data_cb_t callback; + u8_t resp_code; + struct coap_block_context received_block_ctx; + + /* token is used to determine a valid ACK vs a separated response */ + tkl = coap_header_get_token(check_response, token); - SYS_LOG_DBG("TRANSFER REPLY"); + /* If separated response (ACK) return and wait for response */ + if (!tkl && coap_header_get_type(response) == COAP_TYPE_ACK) { + return 0; + } else if (coap_header_get_type(response) == COAP_TYPE_CON) { + /* Send back ACK so the server knows we received the pkt */ + ret = transfer_empty_ack(coap_header_get_id(check_response)); + if (ret < 0) { + SYS_LOG_ERR("Error transmitting ACK"); + return ret; + } + } - ret = zoap_update_from_block(check_response, &firmware_block_ctx); + /* Check response code from server. Expecting (2.05) */ + resp_code = coap_header_get_code(check_response); + if (resp_code != COAP_RESPONSE_CODE_CONTENT) { + SYS_LOG_ERR("Unexpected response from server: %d.%d", + COAP_RESPONSE_CODE_CLASS(resp_code), + COAP_RESPONSE_CODE_DETAIL(resp_code)); + lwm2m_firmware_set_update_result(RESULT_CONNECTION_LOST); + return -ENOENT; + } + + /* save main firmware block context */ + memcpy(&received_block_ctx, &firmware_block_ctx, + sizeof(firmware_block_ctx)); + + ret = coap_update_from_block(check_response, &firmware_block_ctx); if (ret < 0) { SYS_LOG_ERR("Error from block update: %d", ret); + lwm2m_firmware_set_update_result(RESULT_INTEGRITY_FAILED); return ret; } - /* TODO: Process incoming data */ - payload = zoap_packet_get_payload(check_response, &payload_len); - if (payload_len > 0) { - /* TODO: Determine when to actually advance to next block */ - transfer_offset = zoap_next_block(response, - &firmware_block_ctx); + /* test for duplicate transfer */ + if (firmware_block_ctx.current < received_block_ctx.current) { + SYS_LOG_WRN("Duplicate packet ignored"); + /* restore main firmware block context */ + memcpy(&firmware_block_ctx, &received_block_ctx, + sizeof(firmware_block_ctx)); + return 0; + } + + /* Reach last block if transfer_offset equals to 0 */ + transfer_offset = coap_next_block(check_response, &firmware_block_ctx); + /* Process incoming data */ + payload = coap_packet_get_payload_ptr(check_response, &payload_len, + false); + if (payload_len > 0) { SYS_LOG_DBG("total: %zd, current: %zd", firmware_block_ctx.total_size, firmware_block_ctx.current); - /* callback */ callback = lwm2m_firmware_get_write_cb(); if (callback) { - callback(0, payload, payload_len, - transfer_offset == 0, - firmware_block_ctx.total_size); + ret = callback(0, payload, payload_len, + transfer_offset == 0, + firmware_block_ctx.total_size); + if (ret == -ENOMEM) { + lwm2m_firmware_set_update_result( + RESULT_OUT_OF_MEM); + return ret; + } else if (ret == -ENOSPC) { + lwm2m_firmware_set_update_result( + RESULT_NO_STORAGE); + return ret; + } else if (ret < 0) { + lwm2m_firmware_set_update_result( + RESULT_INTEGRITY_FAILED); + return ret; + } } } - /* TODO: Determine actual completion criteria */ if (transfer_offset > 0) { - token = zoap_header_get_token(check_response, &tkl); + /* More block(s) to come, setup next transfer */ ret = transfer_request(&firmware_block_ctx, token, tkl, do_firmware_transfer_reply_cb); + } else { + /* Download finished */ + lwm2m_firmware_set_update_state(STATE_DOWNLOADED); } return ret; } -static enum zoap_block_size default_block_size(void) +static void do_transmit_timeout_cb(struct lwm2m_message *msg) { - switch (CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_BLOCK_SIZE) { - case 16: - return ZOAP_BLOCK_16; - case 32: - return ZOAP_BLOCK_32; - case 64: - return ZOAP_BLOCK_64; - case 128: - return ZOAP_BLOCK_128; - case 256: - return ZOAP_BLOCK_256; - case 512: - return ZOAP_BLOCK_512; - case 1024: - return ZOAP_BLOCK_1024; - } + u8_t token[8]; + u8_t tkl; - return ZOAP_BLOCK_256; + if (firmware_retry < PACKET_TRANSFER_RETRY_MAX) { + /* retry block */ + SYS_LOG_WRN("TIMEOUT - Sending a retry packet!"); + tkl = coap_header_get_token(&msg->cpkt, token); + + transfer_request(&firmware_block_ctx, token, tkl, + do_firmware_transfer_reply_cb); + firmware_retry++; + } else { + SYS_LOG_ERR("TIMEOUT - Too many retry packet attempts! " + "Aborting firmware download."); + lwm2m_firmware_set_update_result(RESULT_CONNECTION_LOST); + } } static void firmware_transfer(struct k_work *work) { - int ret, port, family; + int ret, family; + u16_t off; + u16_t len; + char tmp; + char *server_addr; /* Server Peer IP information */ - /* TODO: use parser on firmware_uri to determine IP version + port */ - /* TODO: hard code IPv4 + port for now */ - port = 5685; family = AF_INET; -#if defined(CONFIG_NET_IPV6) - if (family == AF_INET6) { +#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT) + server_addr = CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_ADDR; + if (strlen(server_addr) >= PROXY_URI_LEN) { + SYS_LOG_ERR("Invalid Proxy URI: %s", server_addr); + lwm2m_firmware_set_update_result(RESULT_UNSUP_PROTO); + return; + } + + /* Copy required as it gets modified when port is available */ + strcpy(proxy_uri, server_addr); + server_addr = proxy_uri; +#else + server_addr = firmware_uri; +#endif + + http_parser_url_init(&parsed_uri); + ret = http_parser_parse_url(server_addr, + strlen(server_addr), + 0, + &parsed_uri); + if (ret != 0) { + SYS_LOG_ERR("Invalid firmware URI: %s", server_addr); + lwm2m_firmware_set_update_result(RESULT_INVALID_URI); + return; + } + + /* Check schema and only support coap for now */ + if (!(parsed_uri.field_set & (1 << UF_SCHEMA))) { + SYS_LOG_ERR("No schema in package uri"); + lwm2m_firmware_set_update_result(RESULT_INVALID_URI); + return; + } + + /* TODO: enable coaps when DTLS is ready */ + off = parsed_uri.field_data[UF_SCHEMA].off; + len = parsed_uri.field_data[UF_SCHEMA].len; + if (len != 4 || memcmp(server_addr + off, "coap", 4)) { + SYS_LOG_ERR("Unsupported schema"); + lwm2m_firmware_set_update_result(RESULT_UNSUP_PROTO); + return; + } + + if (!(parsed_uri.field_set & (1 << UF_PORT))) { + /* Set to default port of CoAP */ + parsed_uri.port = 5683; + } + + off = parsed_uri.field_data[UF_HOST].off; + len = parsed_uri.field_data[UF_HOST].len; + + /* IPv6 is wrapped by brackets */ + if (off > 0 && server_addr[off - 1] == '[') { + if (!IS_ENABLED(CONFIG_NET_IPV6)) { + SYS_LOG_ERR("Doesn't support IPv6"); + lwm2m_firmware_set_update_result(RESULT_UNSUP_PROTO); + return; + } + + family = AF_INET6; + } else { + /* Distinguish IPv4 or DNS */ + for (int i = off; i < off + len; i++) { + if (!isdigit(server_addr[i]) && + server_addr[i] != '.') { + SYS_LOG_ERR("Doesn't support DNS lookup"); + lwm2m_firmware_set_update_result( + RESULT_UNSUP_PROTO); + return; + } + } + + if (!IS_ENABLED(CONFIG_NET_IPV4)) { + SYS_LOG_ERR("Doesn't support IPv4"); + lwm2m_firmware_set_update_result(RESULT_UNSUP_PROTO); + return; + } + + family = AF_INET; + } + + tmp = server_addr[off + len]; + server_addr[off + len] = '\0'; + + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { firmware_addr.sa_family = family; - /* HACK: use firmware_uri directly as IP address */ - net_addr_pton(firmware_addr.sa_family, firmware_uri, + net_addr_pton(firmware_addr.sa_family, server_addr + off, &net_sin6(&firmware_addr)->sin6_addr); - net_sin6(&firmware_addr)->sin6_port = htons(5685); + net_sin6(&firmware_addr)->sin6_port = htons(parsed_uri.port); } -#endif -#if defined(CONFIG_NET_IPV4) - if (family == AF_INET) { + if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { firmware_addr.sa_family = family; - net_addr_pton(firmware_addr.sa_family, firmware_uri, + net_addr_pton(firmware_addr.sa_family, server_addr + off, &net_sin(&firmware_addr)->sin_addr); - net_sin(&firmware_addr)->sin_port = htons(5685); + net_sin(&firmware_addr)->sin_port = htons(parsed_uri.port); } -#endif + + + /* restore server_addr */ + server_addr[off + len] = tmp; ret = net_app_init_udp_client(&firmware_ctx.net_app_ctx, NULL, - &firmware_addr, NULL, port, + &firmware_addr, NULL, parsed_uri.port, firmware_ctx.net_init_timeout, NULL); if (ret) { - NET_ERR("Could not get an UDP context (err:%d)", ret); + SYS_LOG_ERR("Could not get an UDP context (err:%d)", ret); + lwm2m_firmware_set_update_result(RESULT_CONNECTION_LOST); return; } - SYS_LOG_DBG("Attached to port: %d", port); + SYS_LOG_INF("Connecting to server %s, port %d", server_addr + off, + parsed_uri.port); + + lwm2m_engine_context_init(&firmware_ctx); /* set net_app callbacks */ ret = net_app_set_cb(&firmware_ctx.net_app_ctx, NULL, firmware_udp_receive, NULL, NULL); if (ret) { SYS_LOG_ERR("Could not set receive callback (err:%d)", ret); + lwm2m_firmware_set_update_result(RESULT_CONNECTION_LOST); goto cleanup; } @@ -269,9 +479,9 @@ static void firmware_transfer(struct k_work *work) } /* reset block transfer context */ - zoap_block_transfer_init(&firmware_block_ctx, default_block_size(), 0); - - transfer_request(&firmware_block_ctx, NULL, 0, + coap_block_transfer_init(&firmware_block_ctx, + lwm2m_default_block_size(), 0); + transfer_request(&firmware_block_ctx, coap_next_token(), 8, do_firmware_transfer_reply_cb); return; @@ -294,19 +504,16 @@ int lwm2m_firmware_start_transfer(char *package_uri) net_app_release(&firmware_ctx.net_app_ctx); } - if (transfer_state == STATE_IDLE) { - memset(&firmware_ctx, 0, sizeof(struct lwm2m_ctx)); - firmware_ctx.net_init_timeout = NETWORK_INIT_TIMEOUT; - firmware_ctx.net_timeout = NETWORK_CONNECT_TIMEOUT; - k_work_init(&firmware_work, firmware_transfer); - k_delayed_work_init(&firmware_ctx.retransmit_work, - retransmit_request); - - /* start file transfer work */ - strncpy(firmware_uri, package_uri, PACKAGE_URI_LEN - 1); - k_work_submit(&firmware_work); - return 0; - } + memset(&firmware_ctx, 0, sizeof(struct lwm2m_ctx)); + firmware_retry = 0; + firmware_ctx.net_init_timeout = NETWORK_INIT_TIMEOUT; + firmware_ctx.net_timeout = NETWORK_CONNECT_TIMEOUT; + k_work_init(&firmware_work, firmware_transfer); + lwm2m_firmware_set_update_state(STATE_DOWNLOADING); + + /* start file transfer work */ + strncpy(firmware_uri, package_uri, PACKAGE_URI_LEN - 1); + k_work_submit(&firmware_work); - return -1; + return 0; } diff --git a/subsys/net/lib/lwm2m/lwm2m_object.h b/subsys/net/lib/lwm2m/lwm2m_object.h index 96d2033e6e035..d47e55d884af7 100644 --- a/subsys/net/lib/lwm2m/lwm2m_object.h +++ b/subsys/net/lib/lwm2m/lwm2m_object.h @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include #include @@ -204,7 +204,7 @@ struct lwm2m_engine_obj_inst { }; struct lwm2m_output_context { - struct zoap_packet *out_zpkt; + struct coap_packet *out_cpkt; u8_t writer_flags; /* flags for reader/writer */ u8_t *outbuf; u16_t outsize; @@ -214,7 +214,7 @@ struct lwm2m_output_context { }; struct lwm2m_input_context { - struct zoap_packet *in_zpkt; + struct coap_packet *in_cpkt; u8_t *inbuf; u16_t insize; s32_t inpos; diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.c b/subsys/net/lib/lwm2m/lwm2m_rd_client.c index 6ea1a7c6275e2..e6412858fac13 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.c +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include "lwm2m_object.h" @@ -106,6 +106,8 @@ struct lwm2m_rd_client_info { char ep_name[CLIENT_EP_LEN]; char server_ep[CLIENT_EP_LEN]; + + lwm2m_ctx_event_cb_t event_cb; }; static K_THREAD_STACK_DEFINE(lwm2m_rd_client_thread_stack, @@ -120,10 +122,31 @@ static u8_t client_data[256]; /* allocate some data for the RD */ static struct lwm2m_rd_client_info clients[CLIENT_INSTANCE_COUNT]; static int client_count; -static void set_sm_state(int index, u8_t state) +static void set_sm_state(int index, u8_t sm_state) { + enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE; + + /* Determine if a callback to the app is needed */ + if (sm_state == ENGINE_BOOTSTRAP_DONE) { + event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_COMPLETE; + } else if (clients[index].engine_state == ENGINE_UPDATE_SENT && + sm_state == ENGINE_REGISTRATION_DONE) { + event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE; + } else if (sm_state == ENGINE_REGISTRATION_DONE) { + event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE; + } else if ((sm_state == ENGINE_INIT || + sm_state == ENGINE_DEREGISTERED) && + (clients[index].engine_state > ENGINE_BOOTSTRAP_DONE && + clients[index].engine_state < ENGINE_DEREGISTER)) { + event = LWM2M_RD_CLIENT_EVENT_DISCONNECT; + } + /* TODO: add locking? */ - clients[index].engine_state = state; + clients[index].engine_state = sm_state; + + if (event > LWM2M_RD_CLIENT_EVENT_NONE && clients[index].event_cb) { + clients[index].event_cb(clients[index].ctx, event); + } } static bool sm_is_registered(int index) @@ -144,7 +167,11 @@ static int find_clients_index(const struct sockaddr *addr) struct sockaddr *remote; for (i = 0; i < client_count; i++) { +#if defined(CONFIG_MBEDTLS) + remote = &clients[i].ctx->net_app_ctx.dtls.ctx->remote; +#else remote = &clients[i].ctx->net_app_ctx.default_ctx->remote; +#endif if (clients[i].ctx) { if (remote->sa_family != addr->sa_family) { continue; @@ -177,6 +204,56 @@ static int find_clients_index(const struct sockaddr *addr) return index; } +static int find_rd_client_from_msg(struct lwm2m_message *msg, + struct lwm2m_rd_client_info *rd_clients, + size_t len) +{ + size_t i; + + if (!msg || !rd_clients) { + return -1; + } + + for (i = 0; i < len; i++) { + if (rd_clients[i].ctx && rd_clients[i].ctx == msg->ctx) { + return i; + } + } + + return -1; +} + +static void sm_handle_timeout_state(struct lwm2m_message *msg, + enum sm_engine_state sm_state) +{ + int index; + enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE; + + index = find_rd_client_from_msg(msg, clients, CLIENT_INSTANCE_COUNT); + if (index < 0) { + SYS_LOG_ERR("Can't find RD client from msg: %p!", msg); + return; + } + + if (clients[index].engine_state == ENGINE_BOOTSTRAP_SENT) { + event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_FAILURE; + } else if (clients[index].engine_state == ENGINE_REGISTRATION_SENT) { + event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE; + } else if (clients[index].engine_state == ENGINE_UPDATE_SENT) { + event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_FAILURE; + } else if (clients[index].engine_state == ENGINE_DEREGISTER_SENT) { + event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE; + } else { + /* TODO: unknown timeout state */ + } + + set_sm_state(index, sm_state); + + if (event > LWM2M_RD_CLIENT_EVENT_NONE && clients[index].event_cb) { + clients[index].event_cb(clients[index].ctx, event); + } +} + /* force re-update with remote peer(s) */ void engine_trigger_update(void) { @@ -190,17 +267,17 @@ void engine_trigger_update(void) /* state machine reply callbacks */ -static int do_bootstrap_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static int do_bootstrap_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { u8_t code; int index; - code = zoap_header_get_code(response); + code = coap_header_get_code(response); SYS_LOG_DBG("Bootstrap callback (code:%u.%u)", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); index = find_clients_index(from); if (index < 0) { @@ -208,38 +285,46 @@ static int do_bootstrap_reply_cb(const struct zoap_packet *response, return 0; } - if (code == ZOAP_RESPONSE_CODE_CHANGED) { + if (code == COAP_RESPONSE_CODE_CHANGED) { SYS_LOG_DBG("Considered done!"); set_sm_state(index, ENGINE_BOOTSTRAP_DONE); - } else if (code == ZOAP_RESPONSE_CODE_NOT_FOUND) { + } else if (code == COAP_RESPONSE_CODE_NOT_FOUND) { SYS_LOG_ERR("Failed: NOT_FOUND. Not Retrying."); set_sm_state(index, ENGINE_DO_REGISTRATION); - } else if (code == ZOAP_RESPONSE_CODE_FORBIDDEN) { + } else if (code == COAP_RESPONSE_CODE_FORBIDDEN) { SYS_LOG_ERR("Failed: 4.03 - Forbidden. Not Retrying."); set_sm_state(index, ENGINE_DO_REGISTRATION); } else { /* TODO: Read payload for error message? */ SYS_LOG_ERR("Failed with code %u.%u. Retrying ...", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); set_sm_state(index, ENGINE_INIT); } return 0; } -static int do_registration_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static void do_bootstrap_timeout_cb(struct lwm2m_message *msg) +{ + SYS_LOG_WRN("Bootstrap Timeout"); + + /* Restart from scratch */ + sm_handle_timeout_state(msg, ENGINE_INIT); +} + +static int do_registration_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { - struct zoap_option options[2]; + struct coap_option options[2]; u8_t code; int ret, index; - code = zoap_header_get_code(response); + code = coap_header_get_code(response); SYS_LOG_DBG("Registration callback (code:%u.%u)", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); index = find_clients_index(from); if (index < 0) { @@ -248,8 +333,8 @@ static int do_registration_reply_cb(const struct zoap_packet *response, } /* check state and possibly set registration to done */ - if (code == ZOAP_RESPONSE_CODE_CREATED) { - ret = zoap_find_options(response, ZOAP_OPTION_LOCATION_PATH, + if (code == COAP_RESPONSE_CODE_CREATED) { + ret = coap_find_options(response, COAP_OPTION_LOCATION_PATH, options, 2); if (ret < 0) { return ret; @@ -278,11 +363,11 @@ static int do_registration_reply_cb(const struct zoap_packet *response, clients[index].server_ep); return 0; - } else if (code == ZOAP_RESPONSE_CODE_NOT_FOUND) { + } else if (code == COAP_RESPONSE_CODE_NOT_FOUND) { SYS_LOG_ERR("Failed: NOT_FOUND. Not Retrying."); set_sm_state(index, ENGINE_REGISTRATION_DONE); return 0; - } else if (code == ZOAP_RESPONSE_CODE_FORBIDDEN) { + } else if (code == COAP_RESPONSE_CODE_FORBIDDEN) { SYS_LOG_ERR("Failed: 4.03 - Forbidden. Not Retrying."); set_sm_state(index, ENGINE_REGISTRATION_DONE); return 0; @@ -291,23 +376,31 @@ static int do_registration_reply_cb(const struct zoap_packet *response, /* TODO: Read payload for error message? */ /* Possible error response codes: 4.00 Bad request */ SYS_LOG_ERR("failed with code %u.%u. Re-init network", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); set_sm_state(index, ENGINE_INIT); return 0; } -static int do_update_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static void do_registration_timeout_cb(struct lwm2m_message *msg) +{ + SYS_LOG_WRN("Registration Timeout"); + + /* Restart from scratch */ + sm_handle_timeout_state(msg, ENGINE_INIT); +} + +static int do_update_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { u8_t code; int index; - code = zoap_header_get_code(response); - SYS_LOG_DBG("Update callback (code:%u.%u)", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + code = coap_header_get_code(response); + SYS_LOG_INF("Update callback (code:%u.%u)", + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); index = find_clients_index(from); if (index < 0) { @@ -316,34 +409,42 @@ static int do_update_reply_cb(const struct zoap_packet *response, } /* If NOT_FOUND just continue on */ - if ((code == ZOAP_RESPONSE_CODE_CHANGED) || - (code == ZOAP_RESPONSE_CODE_CREATED)) { + if ((code == COAP_RESPONSE_CODE_CHANGED) || + (code == COAP_RESPONSE_CODE_CREATED)) { set_sm_state(index, ENGINE_REGISTRATION_DONE); - SYS_LOG_DBG("Update Done"); + SYS_LOG_INF("Update Done"); return 0; } /* TODO: Read payload for error message? */ /* Possible error response codes: 4.00 Bad request & 4.04 Not Found */ SYS_LOG_ERR("Failed with code %u.%u. Retrying registration", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); set_sm_state(index, ENGINE_DO_REGISTRATION); return 0; } -static int do_deregister_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, +static void do_update_timeout_cb(struct lwm2m_message *msg) +{ + SYS_LOG_WRN("Registration Update Timeout"); + + /* Re-do registration */ + sm_handle_timeout_state(msg, ENGINE_DO_REGISTRATION); +} + +static int do_deregister_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, const struct sockaddr *from) { u8_t code; int index; - code = zoap_header_get_code(response); + code = coap_header_get_code(response); SYS_LOG_DBG("Deregister callback (code:%u.%u)", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); index = find_clients_index(from); if (index < 0) { @@ -351,13 +452,13 @@ static int do_deregister_reply_cb(const struct zoap_packet *response, return 0; } - if (code == ZOAP_RESPONSE_CODE_DELETED) { + if (code == COAP_RESPONSE_CODE_DELETED) { SYS_LOG_DBG("Deregistration success"); set_sm_state(index, ENGINE_DEREGISTERED); } else { SYS_LOG_ERR("failed with code %u.%u", - ZOAP_RESPONSE_CODE_CLASS(code), - ZOAP_RESPONSE_CODE_DETAIL(code)); + COAP_RESPONSE_CODE_CLASS(code), + COAP_RESPONSE_CODE_DETAIL(code)); if (get_sm_state(index) == ENGINE_DEREGISTER_SENT) { set_sm_state(index, ENGINE_DEREGISTER_FAILED); } @@ -366,11 +467,19 @@ static int do_deregister_reply_cb(const struct zoap_packet *response, return 0; } +static void do_deregister_timeout_cb(struct lwm2m_message *msg) +{ + SYS_LOG_WRN("De-Registration Timeout"); + + /* Abort de-registration and start from scratch */ + sm_handle_timeout_state(msg, ENGINE_INIT); +} + /* state machine step functions */ static int sm_do_init(int index) { - SYS_LOG_DBG("RD Client started with endpoint " + SYS_LOG_INF("RD Client started with endpoint " "'%s' and client lifetime %d", clients[index].ep_name, clients[index].lifetime); @@ -398,72 +507,65 @@ static int sm_do_init(int index) static int sm_do_bootstrap(int index) { - struct zoap_packet request; - struct net_pkt *pkt = NULL; - struct zoap_pending *pending = NULL; - struct zoap_reply *reply = NULL; + struct lwm2m_message *msg; struct net_app_ctx *app_ctx = NULL; - int ret = 0; + int ret; + struct sockaddr *remote; if (clients[index].use_bootstrap && clients[index].bootstrapped == 0 && clients[index].has_bs_server_info) { app_ctx = &clients[index].ctx->net_app_ctx; - ret = lwm2m_init_message(app_ctx, - &request, &pkt, ZOAP_TYPE_CON, - ZOAP_METHOD_POST, 0, NULL, 0); - if (ret) { - goto cleanup; + msg = lwm2m_get_message(clients[index].ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + return -ENOMEM; } - zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - "bs", strlen("bs")); - - snprintf(query_buffer, sizeof(query_buffer) - 1, - "ep=%s", clients[index].ep_name); - zoap_add_option(&request, ZOAP_OPTION_URI_QUERY, - query_buffer, strlen(query_buffer)); + msg->type = COAP_TYPE_CON; + msg->code = COAP_METHOD_POST; + msg->mid = 0; + msg->reply_cb = do_bootstrap_reply_cb; + msg->message_timeout_cb = do_bootstrap_timeout_cb; - pending = lwm2m_init_message_pending(clients[index].ctx, - &request); - if (!pending) { - ret = -ENOMEM; + ret = lwm2m_init_message(msg); + if (ret) { goto cleanup; } - reply = zoap_reply_next_unused(clients[index].ctx->replies, - CONFIG_LWM2M_ENGINE_MAX_REPLIES); - if (!reply) { - SYS_LOG_ERR("No resources for waiting for replies."); - ret = -ENOMEM; - goto cleanup; - } + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, + "bs", strlen("bs")); - zoap_reply_init(reply, &request); - reply->reply = do_bootstrap_reply_cb; + snprintf(query_buffer, sizeof(query_buffer) - 1, + "ep=%s", clients[index].ep_name); + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY, + query_buffer, strlen(query_buffer)); /* log the bootstrap attempt */ +#if defined(CONFIG_MBEDTLS) + remote = &app_ctx->dtls.ctx->remote; +#else + remote = &app_ctx->default_ctx->remote; +#endif SYS_LOG_DBG("Register ID with bootstrap server [%s] as '%s'", - lwm2m_sprint_ip_addr( - &app_ctx->default_ctx->remote), + lwm2m_sprint_ip_addr(remote), query_buffer); - ret = lwm2m_udp_sendto(app_ctx, pkt); + ret = lwm2m_send_message(msg); if (ret < 0) { SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret); goto cleanup; } - zoap_pending_cycle(pending); - k_delayed_work_submit(&clients[index].ctx->retransmit_work, - pending->timeout); set_sm_state(index, ENGINE_BOOTSTRAP_SENT); } - return ret; + return 0; cleanup: - lwm2m_init_message_cleanup(pkt, pending, reply); + lwm2m_release_message(msg); return ret; } @@ -512,109 +614,109 @@ static int sm_bootstrap_done(int index) } static int sm_send_registration(int index, bool send_obj_support_data, - zoap_reply_t reply_cb) + coap_reply_t reply_cb, + lwm2m_message_timeout_cb_t timeout_cb) { struct net_app_ctx *app_ctx = NULL; - struct zoap_packet request; - struct net_pkt *pkt = NULL; - struct zoap_pending *pending = NULL; - struct zoap_reply *reply = NULL; + struct lwm2m_message *msg; u8_t *payload; u16_t client_data_len, len; - int ret = 0; + int ret; + struct sockaddr *remote; app_ctx = &clients[index].ctx->net_app_ctx; + msg = lwm2m_get_message(clients[index].ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + return -ENOMEM; + } /* remember the last reg time */ clients[index].last_update = k_uptime_get(); - ret = lwm2m_init_message(app_ctx, - &request, &pkt, ZOAP_TYPE_CON, - ZOAP_METHOD_POST, 0, NULL, 0); + + msg->type = COAP_TYPE_CON; + msg->code = COAP_METHOD_POST; + msg->mid = 0; + msg->reply_cb = reply_cb; + msg->message_timeout_cb = timeout_cb; + + ret = lwm2m_init_message(msg); if (ret) { goto cleanup; } - zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - LWM2M_RD_CLIENT_URI, - strlen(LWM2M_RD_CLIENT_URI)); + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, + LWM2M_RD_CLIENT_URI, + strlen(LWM2M_RD_CLIENT_URI)); if (!sm_is_registered(index)) { /* include client endpoint in URI QUERY on 1st registration */ - zoap_add_option_int(&request, ZOAP_OPTION_CONTENT_FORMAT, - LWM2M_FORMAT_APP_LINK_FORMAT); + coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT, + LWM2M_FORMAT_APP_LINK_FORMAT); snprintf(query_buffer, sizeof(query_buffer) - 1, "lwm2m=%s", LWM2M_PROTOCOL_VERSION); - zoap_add_option(&request, ZOAP_OPTION_URI_QUERY, - query_buffer, strlen(query_buffer)); + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY, + query_buffer, strlen(query_buffer)); snprintf(query_buffer, sizeof(query_buffer) - 1, "ep=%s", clients[index].ep_name); - zoap_add_option(&request, ZOAP_OPTION_URI_QUERY, - query_buffer, strlen(query_buffer)); + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY, + query_buffer, strlen(query_buffer)); } else { /* include server endpoint in URI PATH otherwise */ - zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - clients[index].server_ep, - strlen(clients[index].server_ep)); + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, + clients[index].server_ep, + strlen(clients[index].server_ep)); } snprintf(query_buffer, sizeof(query_buffer) - 1, "lt=%d", clients[index].lifetime); - zoap_add_option(&request, ZOAP_OPTION_URI_QUERY, - query_buffer, strlen(query_buffer)); + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY, + query_buffer, strlen(query_buffer)); + /* TODO: add supported binding query string */ if (send_obj_support_data) { /* generate the rd data */ client_data_len = lwm2m_get_rd_data(client_data, - sizeof(client_data)); - payload = zoap_packet_get_payload(&request, &len); + sizeof(client_data)); + payload = coap_packet_get_payload_ptr(&msg->cpkt, &len, true); if (!payload) { ret = -EINVAL; goto cleanup; } memcpy(payload, client_data, client_data_len); - ret = zoap_packet_set_used(&request, client_data_len); + ret = coap_packet_set_used(&msg->cpkt, client_data_len); if (ret) { goto cleanup; } } - pending = lwm2m_init_message_pending(clients[index].ctx, &request); - if (!pending) { - ret = -ENOMEM; - goto cleanup; - } - - reply = zoap_reply_next_unused(clients[index].ctx->replies, - CONFIG_LWM2M_ENGINE_MAX_REPLIES); - if (!reply) { - SYS_LOG_ERR("No resources for waiting for replies."); - ret = -ENOMEM; - goto cleanup; - } - - zoap_reply_init(reply, &request); - reply->reply = reply_cb; - - /* log the registration attempt */ - SYS_LOG_DBG("registration sent [%s]", - lwm2m_sprint_ip_addr(&app_ctx->default_ctx->remote)); - - ret = lwm2m_udp_sendto(app_ctx, pkt); + ret = lwm2m_send_message(msg); if (ret < 0) { SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret); goto cleanup; } - zoap_pending_cycle(pending); - k_delayed_work_submit(&clients[index].ctx->retransmit_work, - pending->timeout); - return ret; + /* log the registration attempt */ +#if defined(CONFIG_MBEDTLS) + remote = &app_ctx->dtls.ctx->remote; +#else + remote = &app_ctx->default_ctx->remote; +#endif + SYS_LOG_DBG("registration sent [%s]", + lwm2m_sprint_ip_addr(remote)); + + return 0; cleanup: - lwm2m_init_message_cleanup(pkt, pending, reply); + lwm2m_release_message(msg); return ret; } @@ -626,7 +728,8 @@ static int sm_do_registration(int index) !sm_is_registered(index) && clients[index].has_registration_info) { ret = sm_send_registration(index, true, - do_registration_reply_cb); + do_registration_reply_cb, + do_registration_timeout_cb); if (!ret) { set_sm_state(index, ENGINE_REGISTRATION_SENT); } else { @@ -650,7 +753,8 @@ static int sm_registration_done(int index) forced_update = clients[index].trigger_update; clients[index].trigger_update = 0; ret = sm_send_registration(index, forced_update, - do_update_reply_cb); + do_update_reply_cb, + do_update_timeout_cb); if (!ret) { set_sm_state(index, ENGINE_UPDATE_SENT); } else { @@ -664,59 +768,46 @@ static int sm_registration_done(int index) static int sm_do_deregister(int index) { struct net_app_ctx *app_ctx = NULL; - struct zoap_packet request; - struct net_pkt *pkt = NULL; - struct zoap_pending *pending = NULL; - struct zoap_reply *reply = NULL; + struct lwm2m_message *msg; int ret; app_ctx = &clients[index].ctx->net_app_ctx; - - ret = lwm2m_init_message(app_ctx, - &request, &pkt, ZOAP_TYPE_CON, - ZOAP_METHOD_DELETE, 0, NULL, 0); - if (ret) { - goto cleanup; + msg = lwm2m_get_message(clients[index].ctx); + if (!msg) { + SYS_LOG_ERR("Unable to get a lwm2m message!"); + return -ENOMEM; } - zoap_add_option(&request, ZOAP_OPTION_URI_PATH, - clients[index].server_ep, - strlen(clients[index].server_ep)); + msg->type = COAP_TYPE_CON; + msg->code = COAP_METHOD_DELETE; + msg->mid = 0; + msg->reply_cb = do_deregister_reply_cb; + msg->message_timeout_cb = do_deregister_timeout_cb; - pending = lwm2m_init_message_pending(clients[index].ctx, &request); - if (!pending) { - ret = -ENOMEM; - goto cleanup; - } - - reply = zoap_reply_next_unused(clients[index].ctx->replies, - CONFIG_LWM2M_ENGINE_MAX_REPLIES); - if (!reply) { - SYS_LOG_ERR("No resources for waiting for replies."); - ret = -ENOMEM; + ret = lwm2m_init_message(msg); + if (ret) { goto cleanup; } - zoap_reply_init(reply, &request); - reply->reply = do_deregister_reply_cb; + /* TODO: handle return error */ + coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, + clients[index].server_ep, + strlen(clients[index].server_ep)); SYS_LOG_INF("Deregister from '%s'", clients[index].server_ep); - ret = lwm2m_udp_sendto(app_ctx, pkt); + ret = lwm2m_send_message(msg); if (ret < 0) { SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret); goto cleanup; } - zoap_pending_cycle(pending); - k_delayed_work_submit(&clients[index].ctx->retransmit_work, - pending->timeout); set_sm_state(index, ENGINE_DEREGISTER_SENT); - return ret; + return 0; cleanup: - lwm2m_init_message_cleanup(pkt, pending, reply); + lwm2m_release_message(msg); return ret; } @@ -737,7 +828,7 @@ static void lwm2m_rd_client_service(void) break; case ENGINE_BOOTSTRAP_SENT: - /* wait for bootstrap to be done */ + /* wait for bootstrap to be done or timeout */ break; case ENGINE_BOOTSTRAP_DONE: @@ -749,7 +840,7 @@ static void lwm2m_rd_client_service(void) break; case ENGINE_REGISTRATION_SENT: - /* wait registration to be done */ + /* wait registration to be done or timeout */ break; case ENGINE_REGISTRATION_DONE: @@ -757,7 +848,7 @@ static void lwm2m_rd_client_service(void) break; case ENGINE_UPDATE_SENT: - /* wait update to be done */ + /* wait update to be done or abort */ break; case ENGINE_DEREGISTER: @@ -765,6 +856,7 @@ static void lwm2m_rd_client_service(void) break; case ENGINE_DEREGISTER_SENT: + /* wait for deregister to be done or reset */ break; case ENGINE_DEREGISTER_FAILED: @@ -792,7 +884,8 @@ static void lwm2m_rd_client_service(void) int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, char *peer_str, u16_t peer_port, - const char *ep_name) + const char *ep_name, + lwm2m_ctx_event_cb_t event_cb) { int index, ret = 0; @@ -815,6 +908,7 @@ int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, index = client_count; client_count++; clients[index].ctx = client_ctx; + clients[index].event_cb = event_cb; set_sm_state(index, ENGINE_INIT); strncpy(clients[index].ep_name, ep_name, CLIENT_EP_LEN - 1); SYS_LOG_INF("LWM2M Client: %s", clients[index].ep_name); diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_oma_tlv.c b/subsys/net/lib/lwm2m/lwm2m_rw_oma_tlv.c index 3db0f770d17be..31c56897085b6 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rw_oma_tlv.c +++ b/subsys/net/lib/lwm2m/lwm2m_rw_oma_tlv.c @@ -711,8 +711,8 @@ int do_write_op_tlv(struct lwm2m_engine_obj *obj, pos += len2; } - zoap_header_set_code(context->out->out_zpkt, - ZOAP_RESPONSE_CODE_CREATED); + /* TODO: Fix me */ + /* context->out->out_msg->code = COAP_RESPONSE_CODE_CREATED; */ } else if (tlv.type == OMA_TLV_TYPE_RESOURCE) { path->res_id = tlv.id; path->level = 3; diff --git a/tests/net/lib/zoap/Makefile b/tests/net/lib/coap/Makefile similarity index 100% rename from tests/net/lib/zoap/Makefile rename to tests/net/lib/coap/Makefile diff --git a/tests/net/lib/zoap/prj.conf b/tests/net/lib/coap/prj.conf similarity index 50% rename from tests/net/lib/zoap/prj.conf rename to tests/net/lib/coap/prj.conf index 37f70469b3fa3..21d11421395cc 100644 --- a/tests/net/lib/zoap/prj.conf +++ b/tests/net/lib/coap/prj.conf @@ -1,9 +1,13 @@ -CONFIG_NET_BUF_LOG=y CONFIG_NET_TEST=y -CONFIG_SYS_LOG_NET_BUF_LEVEL=2 CONFIG_NETWORKING=y -CONFIG_NETWORKING_WITH_IPV6=y +CONFIG_NET_IPV6=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=n +CONFIG_NET_IPV4=n +CONFIG_NET_BUF_LOG=y +CONFIG_SYS_LOG_NET_BUF_LEVEL=2 CONFIG_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_ZOAP=y -CONFIG_ZOAP_TEST_API_ENABLE=y +CONFIG_COAP=y +CONFIG_COAP_TEST_API_ENABLE=y +CONFIG_MAIN_STACK_SIZE=2048 diff --git a/tests/net/lib/zoap/src/Makefile b/tests/net/lib/coap/src/Makefile similarity index 100% rename from tests/net/lib/zoap/src/Makefile rename to tests/net/lib/coap/src/Makefile diff --git a/tests/net/lib/coap/src/main.c b/tests/net/lib/coap/src/main.c new file mode 100644 index 0000000000000..b661c8a0d593b --- /dev/null +++ b/tests/net/lib/coap/src/main.c @@ -0,0 +1,1208 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#define COAP_BUF_SIZE 128 +#define COAP_LIMITED_BUF_SIZE 13 + +#define NUM_PENDINGS 3 +#define NUM_OBSERVERS 3 +#define NUM_REPLIES 3 + +NET_PKT_TX_SLAB_DEFINE(coap_pkt_slab, 4); + +NET_BUF_POOL_DEFINE(coap_data_pool, 4, COAP_BUF_SIZE, 0, NULL); + +NET_BUF_POOL_DEFINE(coap_limited_data_pool, 4, COAP_LIMITED_BUF_SIZE, 0, NULL); + +static struct coap_pending pendings[NUM_PENDINGS]; +static struct coap_observer observers[NUM_OBSERVERS]; +static struct coap_reply replies[NUM_REPLIES]; + +/* This is exposed for this test in subsys/net/lib/coap/coap_link_format.c */ +bool _coap_match_path_uri(const char * const *path, + const char *uri, u16_t len); + +/* Some forward declarations */ +static void server_notify_callback(struct coap_resource *resource, + struct coap_observer *observer); + +static int server_resource_1_get(struct coap_resource *resource, + struct coap_packet *request); + +static const char * const server_resource_1_path[] = { "s", "1", NULL }; +static struct coap_resource server_resources[] = { + { .path = server_resource_1_path, + .get = server_resource_1_get, + .notify = server_notify_callback }, + { }, +}; + +#define MY_PORT 12345 +#define peer_addr { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x2 } } } +static struct sockaddr_in6 dummy_addr = { + .sin6_family = AF_INET6, + .sin6_addr = peer_addr }; + +static int test_build_empty_pdu(void) +{ + u8_t result_pdu[] = { 0x40, 0x01, 0x0, 0x0 }; + struct coap_packet cpkt; + struct net_pkt *pkt; + struct net_buf *frag; + int result = TC_FAIL; + int r; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&cpkt, pkt, 1, COAP_TYPE_CON, + 0, NULL, COAP_METHOD_GET, 0); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + if (frag->len != sizeof(result_pdu)) { + TC_PRINT("Different size from the reference packet\n"); + goto done; + } + + if (memcmp(result_pdu, frag->data, frag->len)) { + TC_PRINT("Built packet doesn't match reference packet\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + + TC_END_RESULT(result); + + return result; +} + +static int test_build_simple_pdu(void) +{ + u8_t result_pdu[] = { 0x55, 0xA5, 0x12, 0x34, 't', 'o', 'k', 'e', + 'n', 0xC1, 0x00, 0xFF, 'p', 'a', 'y', 'l', + 'o', 'a', 'd', 0x00 }; + struct coap_packet cpkt; + struct net_pkt *pkt; + struct net_buf *frag; + const char token[] = "token"; + u8_t format = 0; + int result = TC_FAIL; + int r; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + + r = coap_packet_init(&cpkt, pkt, 1, COAP_TYPE_NON_CON, + strlen(token), (u8_t *)token, + COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED, + 0x1234); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + r = coap_packet_append_option(&cpkt, COAP_OPTION_CONTENT_FORMAT, + &format, sizeof(format)); + if (r) { + TC_PRINT("Could not append option\n"); + goto done; + } + + r = coap_packet_append_payload_marker(&cpkt); + if (r) { + TC_PRINT("Failed to set the payload marker\n"); + goto done; + } + + if (memcmp(result_pdu, frag->data, frag->len)) { + TC_PRINT("Built packet doesn't match reference packet\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + + TC_END_RESULT(result); + + return result; +} + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_empty_pdu[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x0c, 0x00, 0x00, + /* CoAP */ +}; + +static int test_parse_empty_pdu(void) +{ + u8_t pdu[] = { 0x40, 0x01, 0, 0 }; + struct net_pkt *pkt; + struct net_buf *frag; + struct coap_packet cpkt; + u8_t ver, type, code; + u16_t id; + int result = TC_FAIL; + int r; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + + net_pkt_append_all(pkt, sizeof(ipv6_empty_pdu), + (u8_t *)ipv6_empty_pdu, K_FOREVER); + net_pkt_append_all(pkt, sizeof(pdu), (u8_t *)pdu, K_FOREVER); + + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + memcpy(frag->data, pdu, sizeof(pdu)); + frag->len = NET_IPV6UDPH_LEN + sizeof(pdu); + + r = coap_packet_parse(&cpkt, pkt, NULL, 0); + if (r) { + TC_PRINT("Could not parse packet\n"); + goto done; + } + + ver = coap_header_get_version(&cpkt); + type = coap_header_get_type(&cpkt); + code = coap_header_get_code(&cpkt); + id = coap_header_get_id(&cpkt); + + if (ver != 1) { + TC_PRINT("Invalid version for parsed packet\n"); + goto done; + } + + if (type != COAP_TYPE_CON) { + TC_PRINT("Packet type doesn't match reference\n"); + goto done; + } + + if (code != COAP_METHOD_GET) { + TC_PRINT("Packet code doesn't match reference\n"); + goto done; + } + + if (id != 0) { + TC_PRINT("Packet id doesn't match reference\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + + TC_END_RESULT(result); + + return result; +} + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_simple_pdu[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x15, 0x00, 0x00, + /* CoAP */ +}; + +static int test_parse_simple_pdu(void) +{ + u8_t pdu[] = { 0x55, 0xA5, 0x12, 0x34, 't', 'o', 'k', 'e', + 'n', 0x00, 0xc1, 0x00, 0xff, 'p', 'a', 'y', + 'l', 'o', 'a', 'd', 0x00 }; + struct coap_packet cpkt; + struct net_pkt *pkt; + struct net_buf *frag; + struct coap_option options[16]; + u8_t ver, type, code, tkl; + const u8_t token[8]; + u16_t id; + int result = TC_FAIL; + int r, count = 16; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + + net_pkt_append_all(pkt, sizeof(ipv6_simple_pdu), + (u8_t *)ipv6_simple_pdu, K_FOREVER); + net_pkt_append_all(pkt, sizeof(pdu), (u8_t *)pdu, K_FOREVER); + + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + r = coap_packet_parse(&cpkt, pkt, NULL, 0); + if (r) { + TC_PRINT("Could not parse packet\n"); + goto done; + } + + ver = coap_header_get_version(&cpkt); + type = coap_header_get_type(&cpkt); + code = coap_header_get_code(&cpkt); + id = coap_header_get_id(&cpkt); + + if (ver != 1) { + TC_PRINT("Invalid version for parsed packet\n"); + goto done; + } + + if (type != COAP_TYPE_NON_CON) { + TC_PRINT("Packet type doesn't match reference\n"); + goto done; + } + + if (code != COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED) { + TC_PRINT("Packet code doesn't match reference\n"); + goto done; + } + + if (id != 0x1234) { + TC_PRINT("Packet id doesn't match reference\n"); + goto done; + } + + tkl = coap_header_get_token(&cpkt, (u8_t *)token); + + if (tkl != 5) { + TC_PRINT("Token length doesn't match reference\n"); + goto done; + } + + if (memcmp(token, "token", tkl)) { + TC_PRINT("Token value doesn't match the reference\n"); + goto done; + } + + count = coap_find_options(&cpkt, COAP_OPTION_CONTENT_FORMAT, + options, count); + if (count != 1) { + TC_PRINT("Unexpected number of options in the packet\n"); + goto done; + } + + if (options[0].len != 1) { + TC_PRINT("Option length doesn't match the reference\n"); + goto done; + } + + if (((u8_t *)options[0].value)[0] != 0) { + TC_PRINT("Option value doesn't match the reference\n"); + goto done; + } + + /* Not existent */ + count = coap_find_options(&cpkt, COAP_OPTION_ETAG, options, count); + if (count) { + TC_PRINT("There shouldn't be any ETAG option in the packet\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + + TC_END_RESULT(result); + + return result; +} + +static int test_retransmit_second_round(void) +{ + struct coap_packet cpkt, resp; + struct coap_pending *pending, *resp_pending; + struct net_pkt *pkt, *resp_pkt = NULL; + struct net_buf *frag; + int result = TC_FAIL; + int r; + u16_t id; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + id = coap_next_id(); + + r = coap_packet_init(&cpkt, pkt, 1, COAP_TYPE_CON, 0, NULL, + COAP_METHOD_GET, id); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + pending = coap_pending_next_unused(pendings, NUM_PENDINGS); + if (!pending) { + TC_PRINT("No free pending\n"); + goto done; + } + + r = coap_pending_init(pending, &cpkt, (struct sockaddr *) &dummy_addr); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + /* We "send" the packet the first time here */ + if (!coap_pending_cycle(pending)) { + TC_PRINT("Pending expired too early\n"); + goto done; + } + + /* We simulate that the first transmission got lost */ + if (!coap_pending_cycle(pending)) { + TC_PRINT("Pending expired too early\n"); + goto done; + } + + resp_pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!resp_pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(resp_pkt, frag); + + r = coap_packet_init(&resp, resp_pkt, 1, COAP_TYPE_ACK, + 0, NULL, COAP_METHOD_GET, id); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + /* Now we get the ack from the remote side */ + resp_pending = coap_pending_received(&resp, pendings, NUM_PENDINGS); + if (pending != resp_pending) { + TC_PRINT("Invalid pending %p should be %p\n", + resp_pending, pending); + goto done; + } + + resp_pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); + if (resp_pending) { + TC_PRINT("There should be no active pendings\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + if (resp_pkt) { + net_pkt_unref(resp_pkt); + } + + TC_END_RESULT(result); + + return result; +} + +static void get_from_ip_addr(struct coap_packet *cpkt, + struct sockaddr_in6 *from) +{ + struct net_udp_hdr hdr, *udp_hdr; + + udp_hdr = net_udp_get_hdr(cpkt->pkt, &hdr); + if (!udp_hdr) { + return; + } + + net_ipaddr_copy(&from->sin6_addr, &NET_IPV6_HDR(cpkt->pkt)->src); + from->sin6_port = udp_hdr->src_port; + from->sin6_family = AF_INET6; +} + +static bool ipaddr_cmp(const struct sockaddr *a, const struct sockaddr *b) +{ + if (a->sa_family != b->sa_family) { + return false; + } + + if (a->sa_family == AF_INET6) { + return net_ipv6_addr_cmp(&net_sin6(a)->sin6_addr, + &net_sin6(b)->sin6_addr); + } else if (a->sa_family == AF_INET) { + return net_ipv4_addr_cmp(&net_sin(a)->sin_addr, + &net_sin(b)->sin_addr); + } + + return false; +} + +static void server_notify_callback(struct coap_resource *resource, + struct coap_observer *observer) +{ + if (!ipaddr_cmp(&observer->addr, + (const struct sockaddr *) &dummy_addr)) { + TC_ERROR("The address of the observer doesn't match.\n"); + return; + } + + coap_remove_observer(resource, observer); + + TC_PRINT("You should see this\n"); +} + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_resource1[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x16, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x16, 0x00, 0x00, + /* CoAP */ +}; + +static int server_resource_1_get(struct coap_resource *resource, + struct coap_packet *request) +{ + struct coap_packet response; + struct coap_observer *observer; + struct sockaddr_in6 from; + struct net_pkt *pkt; + struct net_buf *frag; + char payload[] = "This is the payload"; + u8_t token[8]; + u8_t tkl; + u16_t id; + int r; + + if (!coap_request_is_observe(request)) { + TC_PRINT("The request should enable observing\n"); + return -EINVAL; + } + + observer = coap_observer_next_unused(observers, NUM_OBSERVERS); + if (!observer) { + TC_PRINT("There should be an available observer.\n"); + return -EINVAL; + } + + tkl = coap_header_get_token(request, (u8_t *) token); + id = coap_header_get_id(request); + get_from_ip_addr(request, &from); + + coap_observer_init(observer, request, + (const struct sockaddr *)&from); + + coap_register_observer(resource, observer); + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + return -ENOMEM; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + return -ENOMEM; + } + + net_pkt_frag_add(pkt, frag); + + net_pkt_append_all(pkt, sizeof(ipv6_resource1), + (u8_t *)ipv6_resource1, K_FOREVER); + + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, tkl, token, + COAP_RESPONSE_CODE_OK, id); + if (r < 0) { + TC_PRINT("Unable to initialize packet.\n"); + return -EINVAL; + } + + coap_append_option_int(&response, COAP_OPTION_OBSERVE, resource->age); + + r = coap_packet_append_payload_marker(&response); + if (r) { + TC_PRINT("Failed to set the payload marker\n"); + return -EINVAL; + } + + if (!net_pkt_append_all(pkt, strlen(payload), (u8_t *) payload, + K_FOREVER)) { + TC_PRINT("Failed to append the payload\n"); + return -EINVAL; + } + + resource->user_data = pkt; + + return 0; +} + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_valid_req[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x16, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x16, 0x00, 0x00, + /* CoAP */ +}; + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_not_found_req[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x16, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x16, 0x00, 0x00, + /* CoAP */ +}; + +static int test_observer_server(void) +{ + u8_t valid_request_pdu[] = { + 0x45, 0x01, 0x12, 0x34, + 't', 'o', 'k', 'e', 'n', + 0x60, /* enable observe option */ + 0x51, 's', 0x01, '1', /* path */ + }; + u8_t not_found_request_pdu[] = { + 0x45, 0x01, 0x12, 0x34, + 't', 'o', 'k', 'e', 'n', + 0x60, /* enable observe option */ + 0x51, 's', 0x01, '2', /* path */ + }; + struct coap_packet req; + struct coap_option options[4]; + struct net_pkt *pkt; + struct net_buf *frag; + u8_t opt_num = 4; + int result = TC_FAIL; + int r; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + + net_pkt_append_all(pkt, sizeof(ipv6_valid_req), + (u8_t *)ipv6_valid_req, K_FOREVER); + net_pkt_append_all(pkt, sizeof(valid_request_pdu), + (u8_t *)valid_request_pdu, K_FOREVER); + + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + r = coap_packet_parse(&req, pkt, options, opt_num); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + r = coap_handle_request(&req, server_resources, options, opt_num); + if (r) { + TC_PRINT("Could not handle packet\n"); + goto done; + } + + /* Suppose some time passes */ + + r = coap_resource_notify(&server_resources[0]); + if (r) { + TC_PRINT("Could not notify resource\n"); + goto done; + } + + net_pkt_unref(pkt); + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + + net_pkt_append_all(pkt, sizeof(ipv6_not_found_req), + (u8_t *)ipv6_not_found_req, K_FOREVER); + net_pkt_append_all(pkt, sizeof(not_found_request_pdu), + (u8_t *)not_found_request_pdu, K_FOREVER); + + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + r = coap_packet_parse(&req, pkt, options, opt_num); + if (r) { + TC_PRINT("Could not initialize packet\n"); + goto done; + } + + r = coap_handle_request(&req, server_resources, options, opt_num); + if (r != -ENOENT) { + TC_PRINT("There should be no handler for this resource\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + + TC_END_RESULT(result); + + return result; +} + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_obs_client[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x15, 0x00, 0x00, + /* CoAP */ +}; + +static int resource_reply_cb(const struct coap_packet *response, + struct coap_reply *reply, + const struct sockaddr *from) +{ + TC_PRINT("You should see this\n"); + + return 0; +} + +static int test_observer_client(void) +{ + struct coap_packet req, rsp; + struct coap_reply *reply; + struct coap_option options[4]; + struct net_pkt *pkt, *rsp_pkt = NULL; + struct net_buf *frag; + const char token[] = "token"; + const char * const *p; + u8_t opt_num = 4; + int observe = 0; + int result = TC_FAIL; + int r; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + if (!net_pkt_append_all(pkt, sizeof(ipv6_obs_client), + (u8_t *)ipv6_obs_client, K_FOREVER)) { + TC_PRINT("Unable to append IPv6 header\n"); + goto done; + } + + r = coap_packet_init(&req, pkt, 1, COAP_TYPE_CON, + strlen(token), (u8_t *)token, + COAP_METHOD_GET, coap_next_id()); + if (r < 0) { + TC_PRINT("Unable to initialize request\n"); + goto done; + } + + req.offset = sizeof(ipv6_obs_client); + + /* Enable observing the resource. */ + r = coap_append_option_int(&req, COAP_OPTION_OBSERVE, observe); + if (r < 0) { + TC_PRINT("Unable to add option to request int.\n"); + goto done; + } + + for (p = server_resource_1_path; p && *p; p++) { + r = coap_packet_append_option(&req, COAP_OPTION_URI_PATH, + *p, strlen(*p)); + if (r < 0) { + TC_PRINT("Unable to add option to request.\n"); + goto done; + } + } + + reply = coap_reply_next_unused(replies, NUM_REPLIES); + if (!reply) { + printk("No resources for waiting for replies.\n"); + goto done; + } + + coap_reply_init(reply, &req); + reply->reply = resource_reply_cb; + + /* Server side, not interesting for this test */ + r = coap_packet_parse(&req, pkt, options, opt_num); + if (r) { + TC_PRINT("Could not parse req packet\n"); + goto done; + } + + r = coap_handle_request(&req, server_resources, options, opt_num); + if (r) { + TC_PRINT("Could not handle packet\n"); + goto done; + } + + /* We cheat, and communicate using the resource's user_data */ + rsp_pkt = server_resources[0].user_data; + + /* 'rsp_pkt' contains the response now */ + + r = coap_packet_parse(&rsp, rsp_pkt, options, opt_num); + if (r) { + TC_PRINT("Could not parse rsp packet\n"); + goto done; + } + + reply = coap_response_received(&rsp, + (const struct sockaddr *) &dummy_addr, + replies, NUM_REPLIES); + if (!reply) { + TC_PRINT("Couldn't find a matching waiting reply\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + if (rsp_pkt) { + net_pkt_unref(rsp_pkt); + } + + TC_END_RESULT(result); + + return result; +} + +/* IPv6 + UDP frame (48 bytes) */ +static const unsigned char ipv6_block[] = { + /* IPv6 header starts here */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x17, 0x11, 0xFF, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* UDP */ + 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x17, 0x00, 0x00, + /* CoAP */ +}; + +static int test_block_size(void) +{ + struct coap_block_context req_ctx, rsp_ctx; + struct coap_packet req; + struct net_pkt *pkt = NULL; + struct net_buf *frag; + const char token[] = "token"; + u8_t payload = 0xFE; + int result = TC_FAIL; + int r; + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + if (!net_pkt_append_all(pkt, sizeof(ipv6_block), + (u8_t *)ipv6_block, K_FOREVER)) { + TC_PRINT("Unable to append IPv6 header\n"); + goto done; + } + + r = coap_packet_init(&req, pkt, 1, COAP_TYPE_CON, + strlen(token), (u8_t *) token, + COAP_METHOD_POST, coap_next_id()); + if (r < 0) { + TC_PRINT("Unable to initialize request\n"); + goto done; + } + + req.offset = sizeof(ipv6_block); + + coap_block_transfer_init(&req_ctx, COAP_BLOCK_32, 127); + + r = coap_append_block1_option(&req, &req_ctx); + if (r) { + TC_PRINT("Unable to append block1 option\n"); + goto done; + } + + r = coap_append_size1_option(&req, &req_ctx); + if (r) { + TC_PRINT("Unable to append size1 option\n"); + goto done; + } + + r = coap_packet_append_payload_marker(&req); + if (r) { + TC_PRINT("Unable to append payload marker\n"); + goto done; + } + + r = coap_packet_append_payload(&req, (u8_t *)&payload, + coap_block_size_to_bytes(COAP_BLOCK_32)); + if (r) { + TC_PRINT("Unable to append payload marker\n"); + goto done; + } + + coap_block_transfer_init(&rsp_ctx, COAP_BLOCK_1024, 0); + + r = coap_update_from_block(&req, &rsp_ctx); + if (r < 0) { + TC_PRINT("Couldn't parse Block options\n"); + goto done; + } + + if (rsp_ctx.block_size != COAP_BLOCK_32) { + TC_PRINT("Couldn't get block size from request\n"); + goto done; + } + + if (rsp_ctx.current != 0) { + TC_PRINT("Couldn't get the current block size position\n"); + goto done; + } + + if (rsp_ctx.total_size != 127) { + TC_PRINT("Couldn't packet total size from request\n"); + goto done; + } + + /* Suppose that pkt was sent */ + net_pkt_unref(pkt); + + /* Let's try the second packet */ + coap_next_block(&req, &req_ctx); + + pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); + if (!pkt) { + TC_PRINT("Could not get packet from pool\n"); + goto done; + } + + frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); + if (!frag) { + TC_PRINT("Could not get buffer from pool\n"); + goto done; + } + + net_pkt_frag_add(pkt, frag); + net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); + net_pkt_set_ipv6_ext_len(pkt, 0); + + if (!net_pkt_append_all(pkt, sizeof(ipv6_block), + (u8_t *)ipv6_block, K_FOREVER)) { + TC_PRINT("Unable to append IPv6 header\n"); + goto done; + } + + r = coap_packet_init(&req, pkt, 1, COAP_TYPE_CON, + strlen(token), (u8_t *) token, + COAP_METHOD_POST, coap_next_id()); + if (r < 0) { + TC_PRINT("Unable to initialize request\n"); + goto done; + } + + req.offset = sizeof(ipv6_block); + + r = coap_append_block1_option(&req, &req_ctx); + if (r) { + TC_PRINT("Unable to append block1 option\n"); + goto done; + } + + r = coap_packet_append_payload_marker(&req); + if (r) { + TC_PRINT("Unable to append payload marker\n"); + goto done; + } + + r = coap_packet_append_payload(&req, (u8_t *)&payload, + coap_block_size_to_bytes(COAP_BLOCK_32)); + if (r) { + TC_PRINT("Unable to append payload marker\n"); + goto done; + } + + r = coap_update_from_block(&req, &rsp_ctx); + if (r < 0) { + TC_PRINT("Couldn't parse Block options\n"); + goto done; + } + + if (rsp_ctx.block_size != COAP_BLOCK_32) { + TC_PRINT("Couldn't get block size from request\n"); + goto done; + } + + if (rsp_ctx.current != coap_block_size_to_bytes(COAP_BLOCK_32)) { + TC_PRINT("Couldn't get the current block size position\n"); + goto done; + } + + if (rsp_ctx.total_size != 127) { + TC_PRINT("Couldn't packet total size from request\n"); + goto done; + } + + result = TC_PASS; + +done: + net_pkt_unref(pkt); + + TC_END_RESULT(result); + + return result; +} + +static int test_match_path_uri(void) +{ + int result = TC_FAIL; + const char * const resource_path[] = { + "s", + "1", + "foobar", + "foobar3a", + "foobar3", + "devnull", + NULL + }; + const char *uri; + + uri = "/k"; + if (_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + uri = "/s"; + if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + uri = "/foobar"; + if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + uri = "/foobar2"; + if (_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + uri = "/foobar*"; + if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + uri = "/foobar3*"; + if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + uri = "/devnull*"; + if (_coap_match_path_uri(resource_path, uri, strlen(uri))) { + TC_PRINT("Matching %s failed\n", uri); + goto out; + } + + result = TC_PASS; + +out: + TC_END_RESULT(result); + + return result; + +} + +static const struct { + const char *name; + int (*func)(void); +} tests[] = { + { "Build empty PDU test", test_build_empty_pdu, }, + { "Build simple PDU test", test_build_simple_pdu, }, + { "Parse emtpy PDU test", test_parse_empty_pdu, }, + { "Parse simple PDU test", test_parse_simple_pdu, }, + { "Test retransmission", test_retransmit_second_round, }, + { "Test observer server", test_observer_server, }, + { "Test observer client", test_observer_client, }, + { "Test block sized transfer", test_block_size, }, + { "Test match path uri", test_match_path_uri, }, +}; + +int main(int argc, char *argv[]) +{ + int count, pass, result; + + TC_START("Test Zoap CoAP PDU parsing and building"); + + for (count = 0, pass = 0; count < ARRAY_SIZE(tests); count++) { + if (tests[count].func() == TC_PASS) { + pass++; + } + } + + TC_PRINT("%d / %d tests passed\n", pass, count); + + result = pass == count ? TC_PASS : TC_FAIL; + + TC_END_REPORT(result); + + return 0; +} diff --git a/tests/net/lib/zoap/testcase.yaml b/tests/net/lib/coap/testcase.yaml similarity index 100% rename from tests/net/lib/zoap/testcase.yaml rename to tests/net/lib/coap/testcase.yaml diff --git a/tests/net/lib/zoap/src/main.c b/tests/net/lib/zoap/src/main.c deleted file mode 100644 index 6e15bd194c7f7..0000000000000 --- a/tests/net/lib/zoap/src/main.c +++ /dev/null @@ -1,1169 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include - -#define ZOAP_BUF_SIZE 128 -#define ZOAP_LIMITED_BUF_SIZE 13 - -#define NUM_PENDINGS 3 -#define NUM_OBSERVERS 3 -#define NUM_REPLIES 3 - -NET_PKT_TX_SLAB_DEFINE(zoap_pkt_slab, 4); - -NET_BUF_POOL_DEFINE(zoap_data_pool, 4, ZOAP_BUF_SIZE, 0, NULL); - -NET_BUF_POOL_DEFINE(zoap_limited_data_pool, 4, ZOAP_LIMITED_BUF_SIZE, 0, NULL); - -static struct zoap_pending pendings[NUM_PENDINGS]; -static struct zoap_observer observers[NUM_OBSERVERS]; -static struct zoap_reply replies[NUM_REPLIES]; - -/* This is exposed for this test in subsys/net/lib/zoap/zoap_link_format.c */ -bool _zoap_match_path_uri(const char * const *path, - const char *uri, u16_t len); - -/* Some forward declarations */ -static void server_notify_callback(struct zoap_resource *resource, - struct zoap_observer *observer); - -static int server_resource_1_get(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from); - -static const char * const server_resource_1_path[] = { "s", "1", NULL }; -static struct zoap_resource server_resources[] = { - { .path = server_resource_1_path, - .get = server_resource_1_get, - .notify = server_notify_callback }, - { }, -}; - -#define MY_PORT 12345 -static struct sockaddr_in6 dummy_addr = { - .sin6_family = AF_INET6, - .sin6_addr = IN6ADDR_LOOPBACK_INIT }; - -static int test_build_empty_pdu(void) -{ - u8_t result_pdu[] = { 0x40, 0x01, 0x0, 0x0 }; - struct zoap_packet zpkt; - struct net_pkt *pkt; - struct net_buf *frag; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&zpkt, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - zoap_header_set_version(&zpkt, 1); - zoap_header_set_type(&zpkt, ZOAP_TYPE_CON); - zoap_header_set_code(&zpkt, ZOAP_METHOD_GET); - zoap_header_set_id(&zpkt, 0); - - if (frag->len != sizeof(result_pdu)) { - TC_PRINT("Different size from the reference packet\n"); - goto done; - } - - if (memcmp(result_pdu, frag->data, frag->len)) { - TC_PRINT("Built packet doesn't match reference packet\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int test_build_simple_pdu(void) -{ - u8_t result_pdu[] = { 0x55, 0xA5, 0x12, 0x34, 't', 'o', 'k', 'e', - 'n', 0xC1, 0x00, 0xFF, 'p', 'a', 'y', 'l', - 'o', 'a', 'd', 0x00 }; - struct zoap_packet zpkt; - struct net_pkt *pkt; - struct net_buf *frag; - const char token[] = "token"; - u8_t *appdata, payload[] = "payload"; - u16_t buflen; - u8_t format = 0; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&zpkt, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - zoap_header_set_version(&zpkt, 1); - zoap_header_set_type(&zpkt, ZOAP_TYPE_NON_CON); - zoap_header_set_code(&zpkt, - ZOAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED); - zoap_header_set_id(&zpkt, 0x1234); - - r = zoap_header_set_token(&zpkt, (u8_t *)token, strlen(token)); - if (r) { - TC_PRINT("Could not set token\n"); - goto done; - } - - r = zoap_add_option(&zpkt, ZOAP_OPTION_CONTENT_FORMAT, - &format, sizeof(format)); - if (r) { - TC_PRINT("Could not add option\n"); - goto done; - } - - appdata = zoap_packet_get_payload(&zpkt, &buflen); - if (!appdata || buflen <= sizeof(payload)) { - TC_PRINT("Not enough space to insert payload\n"); - goto done; - } - - if (buflen != (ZOAP_BUF_SIZE - 4 - strlen(token) - 2 - 1)) { - /* - * The remaining length will be the buffer size less - * 4: basic CoAP header - * strlen(token): token length - * 2: options (content-format) - * 1: payload marker (added by zoap_packet_get_payload()) - */ - TC_PRINT("Invalid packet length\n"); - goto done; - } - - memcpy(appdata, payload, sizeof(payload)); - - r = zoap_packet_set_used(&zpkt, sizeof(payload)); - if (r) { - TC_PRINT("Failed to set the amount of bytes used\n"); - goto done; - } - - if (frag->len != sizeof(result_pdu)) { - TC_PRINT("Different size from the reference packet\n"); - goto done; - } - - if (memcmp(result_pdu, frag->data, frag->len)) { - TC_PRINT("Built packet doesn't match reference packet\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int test_build_no_size_for_options(void) -{ - struct zoap_packet zpkt; - struct net_pkt *pkt; - struct net_buf *frag; - const char token[] = "token"; - u8_t format = 0; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_limited_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&zpkt, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - zoap_header_set_version(&zpkt, 1); - zoap_header_set_type(&zpkt, ZOAP_TYPE_NON_CON); - zoap_header_set_code(&zpkt, - ZOAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED); - zoap_header_set_id(&zpkt, 0x1234); - - r = zoap_header_set_token(&zpkt, (u8_t *)token, strlen(token)); - if (r) { - TC_PRINT("Could not set token\n"); - goto done; - } - - /* There won't be enough space for the option value */ - r = zoap_add_option(&zpkt, ZOAP_OPTION_CONTENT_FORMAT, - &format, sizeof(format)); - if (!r) { - TC_PRINT("Shouldn't have added the option, not enough space\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int test_parse_empty_pdu(void) -{ - u8_t pdu[] = { 0x40, 0x01, 0, 0 }; - struct net_pkt *pkt; - struct net_buf *frag; - struct zoap_packet zpkt; - u8_t ver, type, code; - u16_t id; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - memcpy(frag->data, pdu, sizeof(pdu)); - frag->len = sizeof(pdu); - - r = zoap_packet_parse(&zpkt, pkt); - if (r) { - TC_PRINT("Could not parse packet\n"); - goto done; - } - - ver = zoap_header_get_version(&zpkt); - type = zoap_header_get_type(&zpkt); - code = zoap_header_get_code(&zpkt); - id = zoap_header_get_id(&zpkt); - - if (ver != 1) { - TC_PRINT("Invalid version for parsed packet\n"); - goto done; - } - - if (type != ZOAP_TYPE_CON) { - TC_PRINT("Packet type doesn't match reference\n"); - goto done; - } - - if (code != ZOAP_METHOD_GET) { - TC_PRINT("Packet code doesn't match reference\n"); - goto done; - } - - if (id != 0) { - TC_PRINT("Packet id doesn't match reference\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int test_parse_simple_pdu(void) -{ - u8_t pdu[] = { 0x55, 0xA5, 0x12, 0x34, 't', 'o', 'k', 'e', - 'n', 0x00, 0xc1, 0x00, 0xff, 'p', 'a', 'y', - 'l', 'o', 'a', 'd', 0x00 }; - struct zoap_packet zpkt; - struct net_pkt *pkt; - struct net_buf *frag; - struct zoap_option options[16]; - u8_t ver, type, code, tkl; - const u8_t *token, *payload; - u16_t id, len; - int result = TC_FAIL; - int r, count = 16; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - memcpy(frag->data, pdu, sizeof(pdu)); - frag->len = sizeof(pdu); - - r = zoap_packet_parse(&zpkt, pkt); - if (r) { - TC_PRINT("Could not parse packet\n"); - goto done; - } - - ver = zoap_header_get_version(&zpkt); - type = zoap_header_get_type(&zpkt); - code = zoap_header_get_code(&zpkt); - id = zoap_header_get_id(&zpkt); - - if (ver != 1) { - TC_PRINT("Invalid version for parsed packet\n"); - goto done; - } - - if (type != ZOAP_TYPE_NON_CON) { - TC_PRINT("Packet type doesn't match reference\n"); - goto done; - } - - if (code != ZOAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED) { - TC_PRINT("Packet code doesn't match reference\n"); - goto done; - } - - if (id != 0x1234) { - TC_PRINT("Packet id doesn't match reference\n"); - goto done; - } - - token = zoap_header_get_token(&zpkt, &tkl); - - if (!token) { - TC_PRINT("Couldn't extract token from packet\n"); - goto done; - } - - if (tkl != 5) { - TC_PRINT("Token length doesn't match reference\n"); - goto done; - } - - if (memcmp(token, "token", tkl)) { - TC_PRINT("Token value doesn't match the reference\n"); - goto done; - } - - count = zoap_find_options(&zpkt, ZOAP_OPTION_CONTENT_FORMAT, - options, count); - if (count != 1) { - TC_PRINT("Unexpected number of options in the packet\n"); - goto done; - } - - if (options[0].len != 1) { - TC_PRINT("Option length doesn't match the reference\n"); - goto done; - } - - if (((u8_t *)options[0].value)[0] != 0) { - TC_PRINT("Option value doesn't match the reference\n"); - goto done; - } - - /* Not existent */ - count = zoap_find_options(&zpkt, ZOAP_OPTION_ETAG, options, count); - if (count) { - TC_PRINT("There shouldn't be any ETAG option in the packet\n"); - goto done; - } - - payload = zoap_packet_get_payload(&zpkt, &len); - if (!payload || !len) { - TC_PRINT("There should be a payload in the packet\n"); - goto done; - } - - if (len != (strlen("payload") + 1)) { - TC_PRINT("Invalid payload in the packet\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int test_retransmit_second_round(void) -{ - struct zoap_packet zpkt, resp; - struct zoap_pending *pending, *resp_pending; - struct net_pkt *pkt, *resp_pkt = NULL; - struct net_buf *frag; - int result = TC_FAIL; - int r; - u16_t id; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&zpkt, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - id = zoap_next_id(); - - zoap_header_set_version(&zpkt, 1); - zoap_header_set_type(&zpkt, ZOAP_TYPE_CON); - zoap_header_set_code(&zpkt, ZOAP_METHOD_GET); - zoap_header_set_id(&zpkt, id); - - pending = zoap_pending_next_unused(pendings, NUM_PENDINGS); - if (!pending) { - TC_PRINT("No free pending\n"); - goto done; - } - - r = zoap_pending_init(pending, &zpkt, (struct sockaddr *) &dummy_addr); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - /* We "send" the packet the first time here */ - if (!zoap_pending_cycle(pending)) { - TC_PRINT("Pending expired too early\n"); - goto done; - } - - /* We simulate that the first transmission got lost */ - - if (!zoap_pending_cycle(pending)) { - TC_PRINT("Pending expired too early\n"); - goto done; - } - - resp_pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!resp_pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(resp_pkt, frag); - - r = zoap_packet_init(&resp, resp_pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - zoap_header_set_version(&resp, 1); - zoap_header_set_type(&resp, ZOAP_TYPE_ACK); - zoap_header_set_id(&resp, id); /* So it matches the request */ - - /* Now we get the ack from the remote side */ - resp_pending = zoap_pending_received(&resp, pendings, NUM_PENDINGS); - if (pending != resp_pending) { - TC_PRINT("Invalid pending %p should be %p\n", - resp_pending, pending); - goto done; - } - - resp_pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS); - if (resp_pending) { - TC_PRINT("There should be no active pendings\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - if (resp_pkt) { - net_pkt_unref(resp_pkt); - } - - TC_END_RESULT(result); - - return result; -} - -static bool ipaddr_cmp(const struct sockaddr *a, const struct sockaddr *b) -{ - if (a->sa_family != b->sa_family) { - return false; - } - - if (a->sa_family == AF_INET6) { - return net_ipv6_addr_cmp(&net_sin6(a)->sin6_addr, - &net_sin6(b)->sin6_addr); - } else if (a->sa_family == AF_INET) { - return net_ipv4_addr_cmp(&net_sin(a)->sin_addr, - &net_sin(b)->sin_addr); - } - - return false; -} - -static void server_notify_callback(struct zoap_resource *resource, - struct zoap_observer *observer) -{ - if (!ipaddr_cmp(&observer->addr, - (const struct sockaddr *) &dummy_addr)) { - TC_ERROR("The address of the observer doesn't match.\n"); - return; - } - - zoap_remove_observer(resource, observer); - - TC_PRINT("You should see this\n"); -} - -static int server_resource_1_get(struct zoap_resource *resource, - struct zoap_packet *request, - const struct sockaddr *from) -{ - struct zoap_packet response; - struct zoap_observer *observer; - struct net_pkt *pkt; - struct net_buf *frag; - char payload[] = "This is the payload"; - const u8_t *token; - u8_t tkl, *p; - u16_t id, len; - int r; - - if (!zoap_request_is_observe(request)) { - TC_PRINT("The request should enable observing\n"); - return -EINVAL; - } - - observer = zoap_observer_next_unused(observers, NUM_OBSERVERS); - if (!observer) { - TC_PRINT("There should be an available observer.\n"); - return -EINVAL; - } - - token = zoap_header_get_token(request, &tkl); - id = zoap_header_get_id(request); - - zoap_observer_init(observer, request, from); - - zoap_register_observer(resource, observer); - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - return -ENOMEM; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - return -ENOMEM; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&response, pkt); - if (r < 0) { - TC_PRINT("Unable to initialize packet.\n"); - return -EINVAL; - } - - zoap_header_set_version(&response, 1); - zoap_header_set_type(&response, ZOAP_TYPE_ACK); - zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_OK); - zoap_header_set_id(&response, id); - zoap_header_set_token(&response, token, tkl); - - zoap_add_option_int(&response, ZOAP_OPTION_OBSERVE, - resource->age); - - p = zoap_packet_get_payload(&response, &len); - if (p) { - memcpy(p, payload, sizeof(payload)); - } - - r = zoap_packet_set_used(&response, sizeof(payload)); - if (r < 0) { - TC_PRINT("Not enough room for payload.\n"); - return -EINVAL; - } - - resource->user_data = pkt; - - return 0; -} - -static int test_observer_server(void) -{ - u8_t valid_request_pdu[] = { - 0x45, 0x01, 0x12, 0x34, - 't', 'o', 'k', 'e', 'n', - 0x60, /* enable observe option */ - 0x51, 's', 0x01, '1', /* path */ - }; - u8_t not_found_request_pdu[] = { - 0x45, 0x01, 0x12, 0x34, - 't', 'o', 'k', 'e', 'n', - 0x60, /* enable observe option */ - 0x51, 's', 0x01, '2', /* path */ - }; - struct zoap_packet req; - struct net_pkt *pkt; - struct net_buf *frag; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - memcpy(frag->data, valid_request_pdu, sizeof(valid_request_pdu)); - frag->len = sizeof(valid_request_pdu); - - r = zoap_packet_parse(&req, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - r = zoap_handle_request(&req, server_resources, - (const struct sockaddr *) &dummy_addr); - if (r) { - TC_PRINT("Could not handle packet\n"); - goto done; - } - - /* Suppose some time passes */ - - r = zoap_resource_notify(&server_resources[0]); - if (r) { - TC_PRINT("Could not notify resource\n"); - goto done; - } - - net_pkt_unref(pkt); - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - memcpy(frag->data, not_found_request_pdu, - sizeof(not_found_request_pdu)); - frag->len = sizeof(not_found_request_pdu); - - r = zoap_packet_parse(&req, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - r = zoap_handle_request(&req, server_resources, - (const struct sockaddr *) &dummy_addr); - if (r != -ENOENT) { - TC_PRINT("There should be no handler for this resource\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int resource_reply_cb(const struct zoap_packet *response, - struct zoap_reply *reply, - const struct sockaddr *from) -{ - TC_PRINT("You should see this\n"); - - return 0; -} - -static int test_observer_client(void) -{ - struct zoap_packet req, rsp; - struct zoap_reply *reply; - struct net_pkt *pkt, *rsp_pkt = NULL; - struct net_buf *frag; - const char token[] = "rndtoken"; - const char * const *p; - int observe = 0; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&req, pkt); - if (r < 0) { - TC_PRINT("Unable to initialize request\n"); - goto done; - } - - /* FIXME: Could be that zoap_packet_init() sets some defaults */ - zoap_header_set_version(&req, 1); - zoap_header_set_type(&req, ZOAP_TYPE_CON); - zoap_header_set_code(&req, ZOAP_METHOD_GET); - zoap_header_set_id(&req, zoap_next_id()); - zoap_header_set_token(&req, - (const u8_t *) token, strlen(token)); - - /* Enable observing the resource. */ - r = zoap_add_option_int(&req, ZOAP_OPTION_OBSERVE, - observe); - if (r < 0) { - TC_PRINT("Unable to add option to request.\n"); - goto done; - } - - for (p = server_resource_1_path; p && *p; p++) { - r = zoap_add_option(&req, ZOAP_OPTION_URI_PATH, - *p, strlen(*p)); - if (r < 0) { - TC_PRINT("Unable to add option to request.\n"); - goto done; - } - } - - reply = zoap_reply_next_unused(replies, NUM_REPLIES); - if (!reply) { - printk("No resources for waiting for replies.\n"); - goto done; - } - - zoap_reply_init(reply, &req); - reply->reply = resource_reply_cb; - - /* Server side, not interesting for this test */ - r = zoap_packet_parse(&req, pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - r = zoap_handle_request(&req, server_resources, - (const struct sockaddr *) &dummy_addr); - if (r) { - TC_PRINT("Could not handle packet\n"); - goto done; - } - - /* We cheat, and communicate using the resource's user_data */ - rsp_pkt = server_resources[0].user_data; - - /* The uninteresting part ends here */ - - /* 'rsp_pkt' contains the response now */ - - r = zoap_packet_parse(&rsp, rsp_pkt); - if (r) { - TC_PRINT("Could not initialize packet\n"); - goto done; - } - - reply = zoap_response_received(&rsp, - (const struct sockaddr *) &dummy_addr, - replies, NUM_REPLIES); - if (!reply) { - TC_PRINT("Couldn't find a matching waiting reply\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - if (rsp_pkt) { - net_pkt_unref(rsp_pkt); - } - - TC_END_RESULT(result); - - return result; -} - -static int test_block_size(void) -{ - struct zoap_block_context req_ctx, rsp_ctx; - struct zoap_packet req; - struct net_pkt *pkt = NULL; - struct net_buf *frag; - const char token[] = "rndtoken"; - u8_t *payload; - u16_t len; - int result = TC_FAIL; - int r; - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&req, pkt); - if (r < 0) { - TC_PRINT("Unable to initialize request\n"); - goto done; - } - - zoap_block_transfer_init(&req_ctx, ZOAP_BLOCK_32, 127); - - /* FIXME: Could be that zoap_packet_init() sets some defaults */ - zoap_header_set_version(&req, 1); - zoap_header_set_type(&req, ZOAP_TYPE_CON); - zoap_header_set_code(&req, ZOAP_METHOD_POST); - zoap_header_set_id(&req, zoap_next_id()); - zoap_header_set_token(&req, - (const u8_t *) token, strlen(token)); - - zoap_add_block1_option(&req, &req_ctx); - zoap_add_size1_option(&req, &req_ctx); - - payload = zoap_packet_get_payload(&req, &len); - if (!payload) { - TC_PRINT("There's no space for payload in the packet\n"); - goto done; - } - - memset(payload, 0xFE, zoap_block_size_to_bytes(ZOAP_BLOCK_32)); - - zoap_packet_set_used(&req, zoap_block_size_to_bytes(ZOAP_BLOCK_32)); - - zoap_block_transfer_init(&rsp_ctx, ZOAP_BLOCK_1024, 0); - - r = zoap_update_from_block(&req, &rsp_ctx); - if (r < 0) { - TC_PRINT("Couldn't parse Block options\n"); - goto done; - } - - if (rsp_ctx.block_size != ZOAP_BLOCK_32) { - TC_PRINT("Couldn't get block size from request\n"); - goto done; - } - - if (rsp_ctx.current != 0) { - TC_PRINT("Couldn't get the current block size position\n"); - goto done; - } - - if (rsp_ctx.total_size != 127) { - TC_PRINT("Couldn't packet total size from request\n"); - goto done; - } - - /* Suppose that pkt was sent */ - net_pkt_unref(pkt); - - /* Let's try the second packet */ - zoap_next_block(&req, &req_ctx); - - pkt = net_pkt_get_reserve(&zoap_pkt_slab, 0, K_NO_WAIT); - if (!pkt) { - TC_PRINT("Could not get packet from pool\n"); - goto done; - } - - frag = net_buf_alloc(&zoap_data_pool, K_NO_WAIT); - if (!frag) { - TC_PRINT("Could not get buffer from pool\n"); - goto done; - } - - net_pkt_frag_add(pkt, frag); - - r = zoap_packet_init(&req, pkt); - if (r < 0) { - TC_PRINT("Unable to initialize request\n"); - goto done; - } - - /* FIXME: Could be that zoap_packet_init() sets some defaults */ - zoap_header_set_version(&req, 1); - zoap_header_set_type(&req, ZOAP_TYPE_CON); - zoap_header_set_code(&req, ZOAP_METHOD_POST); - zoap_header_set_id(&req, zoap_next_id()); - zoap_header_set_token(&req, - (const u8_t *) token, strlen(token)); - - zoap_add_block1_option(&req, &req_ctx); - - memset(payload, 0xFE, ZOAP_BLOCK_32); - - zoap_packet_set_used(&req, zoap_block_size_to_bytes(ZOAP_BLOCK_32)); - - r = zoap_update_from_block(&req, &rsp_ctx); - if (r < 0) { - TC_PRINT("Couldn't parse Block options\n"); - goto done; - } - - if (rsp_ctx.block_size != ZOAP_BLOCK_32) { - TC_PRINT("Couldn't get block size from request\n"); - goto done; - } - - if (rsp_ctx.current != zoap_block_size_to_bytes(ZOAP_BLOCK_32)) { - TC_PRINT("Couldn't get the current block size position\n"); - goto done; - } - - if (rsp_ctx.total_size != 127) { - TC_PRINT("Couldn't packet total size from request\n"); - goto done; - } - - result = TC_PASS; - -done: - net_pkt_unref(pkt); - - TC_END_RESULT(result); - - return result; -} - -static int test_match_path_uri(void) -{ - int result = TC_FAIL; - const char * const resource_path[] = { - "s", - "1", - "foobar", - "foobar3a", - "foobar3", - "devnull", - NULL - }; - const char *uri; - - uri = "/k"; - if (_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - uri = "/s"; - if (!_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - uri = "/foobar"; - if (!_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - uri = "/foobar2"; - if (_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - uri = "/foobar*"; - if (!_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - uri = "/foobar3*"; - if (!_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - uri = "/devnull*"; - if (_zoap_match_path_uri(resource_path, uri, strlen(uri))) { - TC_PRINT("Matching %s failed\n", uri); - goto out; - } - - result = TC_PASS; - -out: - TC_END_RESULT(result); - - return result; - -} - -static const struct { - const char *name; - int (*func)(void); -} tests[] = { - { "Build empty PDU test", test_build_empty_pdu, }, - { "Build simple PDU test", test_build_simple_pdu, }, - { "No size for options test", test_build_no_size_for_options, }, - { "Parse emtpy PDU test", test_parse_empty_pdu, }, - { "Parse simple PDU test", test_parse_simple_pdu, }, - { "Test retransmission", test_retransmit_second_round, }, - { "Test observer server", test_observer_server, }, - { "Test observer client", test_observer_client, }, - { "Test block sized transfer", test_block_size, }, - { "Test match path uri", test_match_path_uri, }, -}; - -int main(int argc, char *argv[]) -{ - int count, pass, result; - - TC_START("Test Zoap CoAP PDU parsing and building"); - - for (count = 0, pass = 0; count < ARRAY_SIZE(tests); count++) { - if (tests[count].func() == TC_PASS) { - pass++; - } - } - - TC_PRINT("%d / %d tests passed\n", pass, count); - - result = pass == count ? TC_PASS : TC_FAIL; - - TC_END_REPORT(result); - - return 0; -}