From beba2ab87ae4447f4341868aa988603bae197dec Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 01/19] net: zstream: API to abstract byte stream communication protocols "zstream" is a new API to allow byte-stream communication abstracting and polymorphic to the underlying transport. Its primary purpose is to abstract network protocol communication over plain TCP and TLS sockets, but the API should be generic enough to abstract communication over other point-to-point byte-oriented transports, e.g. UART or SPI. The patch also includes reference implementation of a stream "class" for SOCK_STREAM (i.e. plain TCP) sockets. Also includes zstream_writeall() convenience function. This function is useful for blocking streams, to account for possibility of short writes. This function will write exactly as many bytes as requested by user, by repeating the operation with remaining data in case as short write(s) happen, unless an error happens before all data is written. The number of bytes actually written is returned using the out-pointer argument (it can be smaller than requested if error occurred). Signed-off-by: Paul Sokolovsky --- include/net/zstream.h | 65 +++++++++++++++++++++++++++ subsys/net/lib/CMakeLists.txt | 1 + subsys/net/lib/zstream/CMakeLists.txt | 6 +++ subsys/net/lib/zstream/zstream.c | 37 +++++++++++++++ subsys/net/lib/zstream/zstream_sock.c | 57 +++++++++++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 include/net/zstream.h create mode 100644 subsys/net/lib/zstream/CMakeLists.txt create mode 100644 subsys/net/lib/zstream/zstream.c create mode 100644 subsys/net/lib/zstream/zstream_sock.c diff --git a/include/net/zstream.h b/include/net/zstream.h new file mode 100644 index 0000000000000..280c70f165b0c --- /dev/null +++ b/include/net/zstream.h @@ -0,0 +1,65 @@ +/** + * @file + * @brief Network stream API definitions + * + * An API to abstract different transport protocols for SOCK_STREAMs, etc. + */ + +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_ZSTREAM_H +#define __NET_ZSTREAM_H + +#include + +struct zstream_api; + +struct zstream { + const struct zstream_api *api; +}; + +struct zstream_api { + ssize_t (*read)(struct zstream *stream, void *buf, size_t size); + ssize_t (*write)(struct zstream *stream, const void *buf, size_t size); + int (*flush)(struct zstream *stream); + int (*close)(struct zstream *stream); +}; + +static inline ssize_t zstream_read(struct zstream *stream, void *buf, + size_t size) +{ + return stream->api->read(stream, buf, size); +} + +static inline ssize_t zstream_write(struct zstream *stream, const void *buf, + size_t size) +{ + return stream->api->write(stream, buf, size); +} + +ssize_t zstream_writeall(struct zstream *stream, const void *buf, size_t size, + size_t *written); + +static inline ssize_t zstream_flush(struct zstream *stream) +{ + return stream->api->flush(stream); +} + +static inline ssize_t zstream_close(struct zstream *stream) +{ + return stream->api->close(stream); +} + +/* Stream object implementation for socket. */ +struct zstream_sock { + const struct zstream_api *api; + int fd; +}; + +int zstream_sock_init(struct zstream_sock *self, int fd); + +#endif /* __NET_ZSTREAM_H */ diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index 5296b3aa08e85..4e1a5911c59c0 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory_ifdef(CONFIG_DNS_RESOLVER dns) add_subdirectory_ifdef(CONFIG_MQTT_LIB mqtt) add_subdirectory_ifdef(CONFIG_NET_APP_SETTINGS app) add_subdirectory_ifdef(CONFIG_NET_SOCKETS sockets) +add_subdirectory_ifdef(CONFIG_NET_SOCKETS zstream) add_subdirectory_ifdef(CONFIG_WEBSOCKET websocket) if(CONFIG_HTTP_PARSER_URL diff --git a/subsys/net/lib/zstream/CMakeLists.txt b/subsys/net/lib/zstream/CMakeLists.txt new file mode 100644 index 0000000000000..957c6369c4484 --- /dev/null +++ b/subsys/net/lib/zstream/CMakeLists.txt @@ -0,0 +1,6 @@ +zephyr_library() + +zephyr_library_sources( + zstream.c + zstream_sock.c + ) diff --git a/subsys/net/lib/zstream/zstream.c b/subsys/net/lib/zstream/zstream.c new file mode 100644 index 0000000000000..71d4a2b18673c --- /dev/null +++ b/subsys/net/lib/zstream/zstream.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include + +ssize_t zstream_writeall(struct zstream *stream, const void *buf, size_t size, + size_t *written) +{ + const char *p = buf; + size_t cur_sz = 0; + + do { + ssize_t out_sz = zstream_write(stream, p, size); + + if (out_sz == -1) { + if (written) { + *written = cur_sz; + } + + return -1; + } + + cur_sz += out_sz; + size -= out_sz; + p += out_sz; + } while (size); + + if (written) { + *written = cur_sz; + } + + return cur_sz; +} diff --git a/subsys/net/lib/zstream/zstream_sock.c b/subsys/net/lib/zstream/zstream_sock.c new file mode 100644 index 0000000000000..710945a4d446c --- /dev/null +++ b/subsys/net/lib/zstream/zstream_sock.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#ifdef __ZEPHYR__ +#include +#else +#include +#include +#endif + +#include + +static ssize_t zstream_sock_read(struct zstream *stream, void *buf, + size_t size) +{ + struct zstream_sock *self = (struct zstream_sock *)stream; + + return recv(self->fd, buf, size, 0); +} + +static ssize_t zstream_sock_write(struct zstream *stream, const void *buf, + size_t size) +{ + struct zstream_sock *self = (struct zstream_sock *)stream; + + return send(self->fd, buf, size, 0); +} + +static int zstream_sock_flush(struct zstream *stream) +{ + return 0; +} + +static int zstream_sock_close(struct zstream *stream) +{ + struct zstream_sock *self = (struct zstream_sock *)stream; + + return close(self->fd); +} + +static const struct zstream_api zstream_sock_api = { + .read = zstream_sock_read, + .write = zstream_sock_write, + .flush = zstream_sock_flush, + .close = zstream_sock_close, +}; + +int zstream_sock_init(struct zstream_sock *self, int fd) +{ + self->api = &zstream_sock_api; + self->fd = fd; + return 0; +} From 76b3243b45f2357c4ab3d29295936421e7922466 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 02/19] tls_conf: Convenience API for TLS configuration settings Provides simple wrappers around mbedTLS APIs to configure system-wide settings and few convenience wrappers for common operations. Majority of settings would be still configured using mbedTLS APIs directly. Signed-off-by: Paul Sokolovsky --- include/net/tls_conf.h | 32 +++ subsys/net/lib/CMakeLists.txt | 1 + subsys/net/lib/tls_conf/CMakeLists.txt | 6 + subsys/net/lib/tls_conf/tls_conf.c | 290 +++++++++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 include/net/tls_conf.h create mode 100644 subsys/net/lib/tls_conf/CMakeLists.txt create mode 100644 subsys/net/lib/tls_conf/tls_conf.c diff --git a/include/net/tls_conf.h b/include/net/tls_conf.h new file mode 100644 index 0000000000000..9ff0bc975624b --- /dev/null +++ b/include/net/tls_conf.h @@ -0,0 +1,32 @@ +/** + * @file + * @brief TLS configuration API definitions + * + * Convenience configuration API for mbedTLS. + */ + +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +struct ztls_cert_key_pair { + mbedtls_x509_crt cert; + mbedtls_pk_context priv_key; +}; + + +int ztls_get_tls_client_conf(mbedtls_ssl_config **out_conf); +int ztls_get_tls_server_conf(mbedtls_ssl_config **out_conf); + +int ztls_conf_add_own_cert_key_pair(mbedtls_ssl_config *conf, + struct ztls_cert_key_pair *pair); + +int ztls_parse_cert_key_pair(struct ztls_cert_key_pair *pair, + const unsigned char *cert, + size_t cert_len, + const unsigned char *priv_key, + size_t priv_key_len); diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index 4e1a5911c59c0..cbe0d02be6d45 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory_ifdef(CONFIG_NET_APP_SETTINGS app) add_subdirectory_ifdef(CONFIG_NET_SOCKETS sockets) add_subdirectory_ifdef(CONFIG_NET_SOCKETS zstream) add_subdirectory_ifdef(CONFIG_WEBSOCKET websocket) +add_subdirectory_ifdef(CONFIG_MBEDTLS tls_conf) if(CONFIG_HTTP_PARSER_URL OR CONFIG_HTTP_PARSER diff --git a/subsys/net/lib/tls_conf/CMakeLists.txt b/subsys/net/lib/tls_conf/CMakeLists.txt new file mode 100644 index 0000000000000..562f80aeda254 --- /dev/null +++ b/subsys/net/lib/tls_conf/CMakeLists.txt @@ -0,0 +1,6 @@ +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_MBEDTLS tls_conf.c) + +zephyr_link_interface_ifdef(CONFIG_MBEDTLS mbedTLS) +zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) diff --git a/subsys/net/lib/tls_conf/tls_conf.c b/subsys/net/lib/tls_conf/tls_conf.c new file mode 100644 index 0000000000000..ca208b9bf5b7f --- /dev/null +++ b/subsys/net/lib/tls_conf/tls_conf.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#if !defined(CONFIG_MBEDTLS_CFG_FILE) +#include "mbedtls/config.h" +#else +#include CONFIG_MBEDTLS_CFG_FILE +#endif /* CONFIG_MBEDTLS_CFG_FILE */ +#include "mbedtls/platform.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __ZEPHYR__ +#include +#include +#include + +#define SYS_LOG_LEVEL 4 +#include + +#else /* __ZEPHYR__ */ + +#define SYS_LOG_ERR(msg, ...) printf(msg "\n", ##__VA_ARGS__) +#define printk printf + +#endif /* __ZEPHYR__ */ + +mbedtls_ssl_config ztls_default_tls_client_conf; +mbedtls_ssl_config ztls_default_tls_server_conf; + +/*static mbedtls_entropy_context ztls_entropy;*/ +static mbedtls_ctr_drbg_context ztls_ctr_drbg; + +#ifdef __ZEPHYR__ + +#ifdef CONFIG_ENTROPY_HAS_DRIVER +static int ztls_entropy_func(void *data, unsigned char *output, size_t len) +{ + int res = entropy_get_entropy(data, output, len); + return res; +} +#else +/* No real entropy driver, use pseudo-random number generator (potentially + * insecure). + */ +static int ztls_entropy_func(void *data, unsigned char *output, size_t len) +{ + size_t i = len / 4; + u32_t v; + + while (i--) { + v = sys_rand32_get(); + UNALIGNED_PUT(v, (u32_t *)output); + output += 4; + } + + i = len & 3; + v = sys_rand32_get(); + while (i--) { + *output++ = v; + v >>= 8; + } + + return 0; +} +#endif + +static int ztls_mbedtls_ctr_drbg_random(void *p_rng, unsigned char *output, + size_t output_len) +{ + static K_MUTEX_DEFINE(mutex); + int res; + + /* Avoid connection lockups due to no entropy, error out instead */ + res = k_mutex_lock(&mutex, K_SECONDS(1)); + if (res < 0) { + return res; + } + + res = mbedtls_ctr_drbg_random(p_rng, output, output_len); + k_mutex_unlock(&mutex); + + return res; +} + +#else /* __ZEPHYR__ */ + +static int null_entropy_func(void *data, unsigned char *output, size_t len) +{ + /* Warning: this is test-only implementation for non-Zephyr + * environments. + */ + (void)data; + (void)output; + (void)len; + return 0; +} + +#define ztls_mbedtls_ctr_drbg_random mbedtls_ctr_drbg_random + +#endif + +#ifdef MBEDTLS_DEBUG_C +static void ztls_mbedtls_debug(void *ctx, int level, const char *file, + int line, const char *str) +{ + (void)ctx; + printk("MBEDTLS%d:%s:%04d: %s\n", level, file, line, str); +} +#endif + +#define ztls_system_is_inited() (ztls_ctr_drbg.f_entropy != NULL) + + +static int ztls_system_init(void) +{ + int ret; + /* TODO: Should use something device-specific, like MAC address */ + static const unsigned char drbg_seed[] = "zephyr"; + struct device *dev = NULL; + +#if defined(__ZEPHYR__) && defined(CONFIG_ENTROPY_HAS_DRIVER) + dev = device_get_binding(CONFIG_ENTROPY_NAME); + + if (!dev) { + SYS_LOG_ERR("can't get entropy device"); + return -ENODEV; + } +#else + printk("*** WARNING: This system lacks entropy driver, " + "TLS communication may be INSECURE! ***\n\n"); +#endif + + /* We don't use mbedTLS entropy pool as of now. */ + /* mbedtls_entropy_init(&p->entropy); */ + + mbedtls_ctr_drbg_init(&ztls_ctr_drbg); +#ifdef __ZEPHYR__ + ret = mbedtls_ctr_drbg_seed(&ztls_ctr_drbg, ztls_entropy_func, dev, + drbg_seed, sizeof(drbg_seed)); +#else + ret = mbedtls_ctr_drbg_seed(&ztls_ctr_drbg, null_entropy_func, NULL, + drbg_seed, sizeof(drbg_seed)); +#endif + if (ret != 0) { + mbedtls_ctr_drbg_free(&ztls_ctr_drbg); + return ret; + } + + return 0; +} + +static int ztls_init_tls_conf(mbedtls_ssl_config *conf, int client_or_serv) +{ + int ret; + + if (!ztls_system_is_inited()) { + ret = ztls_system_init(); + if (ret < 0) { + return ret; + } + } + + mbedtls_ssl_config_init(conf); + + ret = mbedtls_ssl_config_defaults(conf, client_or_serv, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + SYS_LOG_ERR("mbedtls_ssl_config_defaults returned -0x%x", + -ret); + goto cleanup_conf; + } + + mbedtls_ssl_conf_rng(conf, ztls_mbedtls_ctr_drbg_random, + &ztls_ctr_drbg); + + #if 0 + /* These are defaults in mbedTLS */ + mbedtls_ssl_conf_authmode(&ztls_default_tls_client_conf, + MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_authmode(&ztls_default_tls_server_conf, + MBEDTLS_SSL_VERIFY_NONE); + #endif + +#ifdef MBEDTLS_DEBUG_C + mbedtls_ssl_conf_dbg(conf, ztls_mbedtls_debug, NULL); +#ifdef CONFIG_MBEDTLS_DEBUG_LEVEL + /* 3 - info, 4 - debug */ + mbedtls_debug_set_threshold(CONFIG_MBEDTLS_DEBUG_LEVEL); +#endif +#endif + return 0; + +cleanup_conf: + mbedtls_ssl_config_free(conf); + + return ret; + +} + +static int ztls_get_tls_conf_helper(mbedtls_ssl_config **out_conf, + mbedtls_ssl_config *in_conf, + int client_or_serv) +{ + int ret; + + *out_conf = NULL; + ret = ztls_init_tls_conf(in_conf, client_or_serv); + if (ret < 0) { + return ret; + } + + *out_conf = in_conf; + return 0; +} + +int ztls_get_tls_client_conf(mbedtls_ssl_config **out_conf) +{ + return ztls_get_tls_conf_helper(out_conf, + &ztls_default_tls_client_conf, + MBEDTLS_SSL_IS_CLIENT); +} + +int ztls_get_tls_server_conf(mbedtls_ssl_config **out_conf) +{ + return ztls_get_tls_conf_helper(out_conf, + &ztls_default_tls_server_conf, + MBEDTLS_SSL_IS_SERVER); +} + +#ifdef MBEDTLS_X509_CRT_PARSE_C +int ztls_parse_cert_key_pair(struct ztls_cert_key_pair *pair, + const unsigned char *cert, size_t cert_len, + const unsigned char *priv_key, + size_t priv_key_len) +{ + int ret; + + mbedtls_x509_crt_init(&pair->cert); + mbedtls_pk_init(&pair->priv_key); + + ret = mbedtls_x509_crt_parse(&pair->cert, cert, cert_len); + if (ret != 0) { + SYS_LOG_ERR("mbedtls_x509_crt_parse returned -0x%x", -ret); + goto error; + } + + ret = mbedtls_pk_parse_key(&pair->priv_key, priv_key, + priv_key_len, NULL, 0); + if (ret != 0) { + SYS_LOG_ERR("mbedtls_pk_parse_key returned -0x%x", -ret); + goto error; + } + + return 0; + +error: + mbedtls_x509_crt_free(&pair->cert); + mbedtls_pk_free(&pair->priv_key); + + return ret; +} + +int ztls_conf_add_own_cert_key_pair(mbedtls_ssl_config *conf, + struct ztls_cert_key_pair *pair) +{ + int ret = mbedtls_ssl_conf_own_cert(conf, &pair->cert, + &pair->priv_key); + if (ret != 0) { + SYS_LOG_ERR("mbedtls_ssl_conf_own_cert returned -0x%x", -ret); + } + + return ret; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ From ba62845848c33dd2ba22a396154c93abdbe0c272 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 03/19] mbedtls: config-tls1_2.h: Extended TLS 1.2 config A number of (HTTPS) sites on the modern Internet mandate use of forward secrecy via ephemeral key exchange (DHE and ECDHE cyphersuites). Elabling such cyphersuites how leads to increased code size, mbedTLS heap usage, and runtime overheads, so enabling them in the existing config-mini-tls1_2.h doesn't seem like a good idea. Instead, create a new config, config-tls1_2.h, which includes config-mini-tls1_2.h, and enables more options. The idea of this file is to provide configuration as compatible as possible with modern state of TLS 1.2 usage on the Internet, so more options can be enabled in the future. In the meantime, this config enables DHE by default, because it leads to a moderate code size increase (~5K x86). However, DHE cyphersuites are known to be slow during handshake phase (up to 3 times slower than with ephemeral key exchanges, based on reports for OpenSSL). ECDHE is known to be faster (~50% time overhead), but leads to much higher code impact (~15K). So, for now only DHE is enabled. Signed-off-by: Paul Sokolovsky --- .../crypto/mbedtls/configs/config-tls1_2.h | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 ext/lib/crypto/mbedtls/configs/config-tls1_2.h diff --git a/ext/lib/crypto/mbedtls/configs/config-tls1_2.h b/ext/lib/crypto/mbedtls/configs/config-tls1_2.h new file mode 100644 index 0000000000000..640064b03098a --- /dev/null +++ b/ext/lib/crypto/mbedtls/configs/config-tls1_2.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Linaro Limited. + * + * SPDX-License-Identifier: Apache-2.0 + * + * More complete mbedTLS configuration for TLS 1.2 (RFC 5246) for Zephyr, + * extending config-mini-tls1_2.h. + */ + +#ifndef MBEDTLS_CONFIG_TLS1_2_H +#define MBEDTLS_CONFIG_TLS1_2_H + +#if 1 + +/* DHE config - slow but moderate code size impact (~5K x86) */ +#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED +#define MBEDTLS_DHM_C + +#else + +/* ECDHE config - faster but higher code size impact (~15K x86) */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECDH_C +#define MBEDTLS_ECP_C + +#endif + +#include + +#endif /* MBEDTLS_CONFIG_TLS1_2_H */ From fa162e95870e520ec3244cb386d7b31932f66f43 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 04/19] net: zstream: Add network stream wrapper implementation for mbedTLS This implements communication methods of TLS. TLS configuration is handled by the separate "tls_conf" module. It accepts mbedtls_ssl_config*, which can be configured e.g. using tls_conf module. Signed-off-by: Paul Sokolovsky --- include/net/zstream_tls.h | 54 +++++++ subsys/net/lib/zstream/CMakeLists.txt | 5 + subsys/net/lib/zstream/zstream_tls.c | 204 ++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 include/net/zstream_tls.h create mode 100644 subsys/net/lib/zstream/zstream_tls.c diff --git a/include/net/zstream_tls.h b/include/net/zstream_tls.h new file mode 100644 index 0000000000000..e7a9b24e03293 --- /dev/null +++ b/include/net/zstream_tls.h @@ -0,0 +1,54 @@ +/** + * @file + * @brief TLS stream API definitions + * + * Implementation of stream wrapper for mbedTLS. + */ + +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_ZSTREAM_TLS_H +#define __NET_ZSTREAM_TLS_H + +#if defined(CONFIG_MBEDTLS) + +#include +#include + +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/ssl.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/pk.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" + +#include + +/* Stream object implementation for TLS adapter. */ +struct zstream_tls { + const struct zstream_api *api; + + /* Wrapped stream */ + struct zstream *sock; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt ca_cert; + mbedtls_x509_crt srv_cert; + mbedtls_pk_context srv_priv_key; +}; + +int zstream_tls_init(struct zstream_tls *self, struct zstream *sock, + mbedtls_ssl_config *conf, const char *hostname); + +#endif /* CONFIG_MBEDTLS */ + +#endif /* __NET_ZSTREAM_TLS_H */ diff --git a/subsys/net/lib/zstream/CMakeLists.txt b/subsys/net/lib/zstream/CMakeLists.txt index 957c6369c4484..b29b58efef08e 100644 --- a/subsys/net/lib/zstream/CMakeLists.txt +++ b/subsys/net/lib/zstream/CMakeLists.txt @@ -4,3 +4,8 @@ zephyr_library_sources( zstream.c zstream_sock.c ) + +zephyr_library_sources_ifdef(CONFIG_MBEDTLS zstream_tls.c) + +zephyr_link_interface_ifdef(CONFIG_MBEDTLS mbedTLS) +zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) diff --git a/subsys/net/lib/zstream/zstream_tls.c b/subsys/net/lib/zstream/zstream_tls.c new file mode 100644 index 0000000000000..1619dfe9fbf56 --- /dev/null +++ b/subsys/net/lib/zstream/zstream_tls.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#if !defined(CONFIG_MBEDTLS_CFG_FILE) +#include "mbedtls/config.h" +#else +#include CONFIG_MBEDTLS_CFG_FILE +#endif /* CONFIG_MBEDTLS_CFG_FILE */ +#include "mbedtls/platform.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __ZEPHYR__ +#include +#else /* __ZEPHYR__ */ +#include +#include +#define NET_ERR(msg, ...) printf(msg "\n", ##__VA_ARGS__) + +#endif /* __ZEPHYR__ */ + +#include + +static ssize_t zstream_tls_read(struct zstream *stream, void *buf, size_t size) +{ + struct zstream_tls *self = (struct zstream_tls *)stream; + int ret = mbedtls_ssl_read(&self->ssl, buf, size); + + if (ret >= 0) { + return ret; + } + + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + /* End of stream */ + return 0; + } + + if (ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT) { + /* We don't support TLS reconnects over the same socket, + * treat as EOF. + */ + return 0; + } + + if (ret == MBEDTLS_ERR_SSL_WANT_READ + || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + errno = EAGAIN; + return -1; + } + + NET_ERR("mbedtls_ssl_read: -%x\n", -ret); + /* TODO: More error code conversion? */ + errno = EINVAL; + return -1; +} + +static ssize_t zstream_tls_write(struct zstream *stream, const void *buf, + size_t size) +{ + struct zstream_tls *self = (struct zstream_tls *)stream; + int ret = mbedtls_ssl_write(&self->ssl, buf, size); + + if (ret >= 0) { + return ret; + } + + if (ret == MBEDTLS_ERR_SSL_WANT_READ + || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + errno = EAGAIN; + return -1; + } + + /* TODO: More error code conversion? */ + errno = EINVAL; + return -1; +} + +static int zstream_tls_flush(struct zstream *stream) +{ + /* mbedTLS doesn't buffer output data, even for a single + * byte written it will create a TLS record and send at once. + */ + return 0; +} + +int zstream_tls_close(struct zstream *stream) +{ + struct zstream_tls *self = (struct zstream_tls *)stream; + int tls_res = mbedtls_ssl_close_notify(&self->ssl); + /* We need to close underlying stream regardless of TLS close + * notify status, or the stream will be leaked. + */ + int stream_res = zstream_close(self->sock); + + mbedtls_ssl_free(&self->ssl); + + if (tls_res == 0 && stream_res == 0) { + return 0; + } + + if (stream_res == 0) { + /* TODO: Check which mbedTLS errors are possible and improve + * mapping to POSIX codes. + */ + errno = EINVAL; + } + + return -1; +} + +static const struct zstream_api zstream_tls_api = { + .read = zstream_tls_read, + .write = zstream_tls_write, + .flush = zstream_tls_flush, + .close = zstream_tls_close, +}; + +static int zstream_mbedtls_ssl_send(void *ctx, const unsigned char *buf, + size_t len) +{ + struct zstream *sock = ctx; + ssize_t outlen = zstream_write(sock, buf, len); + + if (outlen != -1) { + return outlen; + } + + if (errno == EAGAIN) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + + /* Generic error */ + return MBEDTLS_ERR_NET_SEND_FAILED; +} + +static int zstream_mbedtls_ssl_recv(void *ctx, unsigned char *buf, + size_t len) +{ + struct zstream *sock = ctx; + ssize_t outlen = zstream_read(sock, buf, len); + + if (outlen != -1) { + return outlen; + } + + if (errno == EAGAIN) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + + /* Generic error */ + return MBEDTLS_ERR_NET_RECV_FAILED; +} + +int zstream_tls_init(struct zstream_tls *self, struct zstream *sock, + mbedtls_ssl_config *conf, const char *hostname) +{ + int ret; + + self->api = &zstream_tls_api; + self->sock = sock; + + mbedtls_ssl_init(&self->ssl); + + ret = mbedtls_ssl_setup(&self->ssl, conf); + if (ret != 0) { + goto error; + } + + if (hostname) { + /* Set server hostname for SNI */ + ret = mbedtls_ssl_set_hostname(&self->ssl, hostname); + if (ret != 0) { + NET_ERR("mbedtls_ssl_set_hostname: -0x%x", -ret); + goto error; + } + } + + mbedtls_ssl_set_bio(&self->ssl, sock, zstream_mbedtls_ssl_send, + zstream_mbedtls_ssl_recv, NULL); + + while ((ret = mbedtls_ssl_handshake(&self->ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ + && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + NET_ERR("mbedtls_ssl_handshake: -%x\n", -ret); + goto error; + } + } + + return 0; + +error: + return -1; +} From f739a93ab5005d4e6738b3632fdb35f48f2cc9a8 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 13 Mar 2018 00:42:37 +0200 Subject: [PATCH 05/19] samples: net: zstream: Add conversion of sockets/big_http_download This sample is a port of samples/net/sockets/big_http_download. It thus supports TLS and downloading of files over HTTPS. It's configured to work with real-world sites on the Internet, and thus requires quite large TLS buffer size and various ciphers enabled, thus requiring lots of RAM and ROM, so can run only on high-end boards. Known supported boards are qemu_x86 and frdm_k64f. Signed-off-by: Paul Sokolovsky --- .../zstream/big_http_download/CMakeLists.txt | 7 + .../zstream/big_http_download/Makefile.posix | 16 + .../net/zstream/big_http_download/README.rst | 84 ++++++ .../net/zstream/big_http_download/prj.conf | 39 +++ .../net/zstream/big_http_download/sample.yaml | 11 + .../big_http_download/src/big_http_download.c | 285 ++++++++++++++++++ 6 files changed, 442 insertions(+) create mode 100644 samples/net/zstream/big_http_download/CMakeLists.txt create mode 100644 samples/net/zstream/big_http_download/Makefile.posix create mode 100644 samples/net/zstream/big_http_download/README.rst create mode 100644 samples/net/zstream/big_http_download/prj.conf create mode 100644 samples/net/zstream/big_http_download/sample.yaml create mode 100644 samples/net/zstream/big_http_download/src/big_http_download.c diff --git a/samples/net/zstream/big_http_download/CMakeLists.txt b/samples/net/zstream/big_http_download/CMakeLists.txt new file mode 100644 index 0000000000000..e6bf10afba6f5 --- /dev/null +++ b/samples/net/zstream/big_http_download/CMakeLists.txt @@ -0,0 +1,7 @@ +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake) diff --git a/samples/net/zstream/big_http_download/Makefile.posix b/samples/net/zstream/big_http_download/Makefile.posix new file mode 100644 index 0000000000000..a5a12452e53bc --- /dev/null +++ b/samples/net/zstream/big_http_download/Makefile.posix @@ -0,0 +1,16 @@ +# This makefile builds the sample for a POSIX system, like Linux +# It required mbedTLS to be installed on the system, e.g. +# libmbedtls-dev package on Debian/Ubuntu Linux. + +INC = -I$(ZEPHYR_BASE)/include +LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream +LIBSRC = $(LIBPATH)/zstream_sock.c \ + $(LIBPATH)/zstream_tls.c \ + $(LIBPATH)/../tls_conf/tls_conf.c \ + +LDLIBS = -lmbedtls -lmbedx509 -lmbedcrypto + +CFLAGS = -DCONFIG_MBEDTLS -DCONFIG_MBEDTLS_DEBUG_LEVEL=0 + +big_http_download: src/big_http_download.c + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) $(LDLIBS) -o $@ diff --git a/samples/net/zstream/big_http_download/README.rst b/samples/net/zstream/big_http_download/README.rst new file mode 100644 index 0000000000000..972d7c96743b1 --- /dev/null +++ b/samples/net/zstream/big_http_download/README.rst @@ -0,0 +1,84 @@ +.. _zstream-big-http-download: + +Zstrean API Big HTTP Download Example +##################################### + +Overview +******** + +The zstream/big_http_download sample application for Zephyr implements +a simple HTTP GET client using a BSD Sockets compatible API. It is a +straightforward convesion of the `sockets-big-http-download` sample +to zstream API, thanks to which the sample acquires HTTPS support. + +The source code for this sample application can be found at: +:file:`samples/net/zstream/big_http_download`. + +Requirements +************ + +- :ref:`networking_with_qemu` +- or, a board with hardware networking +- NAT/routing should be set up to allow connections to the Internet +- DNS server should be available on the host to resolve domain names + +Building and Running +******************** + +Build the Zephyr version of the application like this: + +.. zephyr-app-commands:: + :zephyr-app: zstream/net/sockets/big_http_download + :board: + :goals: build + :compact: + +``board_to_use`` defaults to ``qemu_x86``. In this case, you can run the +application in QEMU using ``make run``. If you used another BOARD, you +will need to consult its documentation for application deployment +instructions. You can read about Zephyr support for specific boards in +the documentation at :ref:`boards`. + +After the sample starts, it issues an HTTPS GET request for +https://ftp.gnu.org/gnu/tar/tar-1.13.tar . This site was selected as +providing files of variety of sizes. The particular file selected is +3.8MB in size, so it can show how reliably Zephyr streams non-trivial +amounts of data, while still taking a reasonable amount of time to +complete. While the file is downloaded, its hash is computed (SHA-256 +is used in the source code), and an error message is printed if it +differs from the reference value, as specified in the source code. +After a short pause, the process repeats (in an infinite loop), while +the total counter of the bytes received is kept. Thus the application +can be used to test transfers of much larger amounts of traffic over +a longer time. + +You can edit the source code to issue a request to any other site on +the Internet (or on the local network, in which case no NAT/routing +setup is needed). Note that plain http:// downloads are also supported. + +.. warning:: + + If you are doing extensive testing with this sample, please reference + a file on a local server or a special-purpose testing server of your own + on the Internet. Using files on archive.ubuntu.com is not recommended for + large-scale testing. + +Running application on POSIX Host +================================= + +The same application source code can be built for a POSIX system, e.g. +Linux. + +To build for a host POSIX OS: + +.. code-block:: console + + $ make -f Makefile.posix + +To run: + +.. code-block:: console + + $ ./big_http_download + +The behavior of the application is the same as the Zephyr version. diff --git a/samples/net/zstream/big_http_download/prj.conf b/samples/net/zstream/big_http_download/prj.conf new file mode 100644 index 0000000000000..abc6a4a25cb59 --- /dev/null +++ b/samples/net/zstream/big_http_download/prj.conf @@ -0,0 +1,39 @@ +# General config +CONFIG_NEWLIB_LIBC=y +CONFIG_MAIN_STACK_SIZE=6000 +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_CFG_FILE="config-tls1_2.h" +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=60000 +#CONFIG_MBEDTLS_DEBUG=y + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +CONFIG_NET_PKT_TX_COUNT=6 + +CONFIG_DNS_RESOLVER=y +CONFIG_DNS_SERVER_IP_ADDRESSES=y +CONFIG_DNS_SERVER1="192.0.2.2" + +# Network driver config +CONFIG_TEST_RANDOM_GENERATOR=y + +# Network address config +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_NEED_IPV4=y +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" +CONFIG_NET_APP_MY_IPV4_GW="192.0.2.2" + +# Network debug config +CONFIG_NET_LOG=y +CONFIG_NET_LOG_GLOBAL=y +CONFIG_SYS_LOG_NET_LEVEL=2 +#CONFIG_NET_DEBUG_SOCKETS=y diff --git a/samples/net/zstream/big_http_download/sample.yaml b/samples/net/zstream/big_http_download/sample.yaml new file mode 100644 index 0000000000000..c9744a0168c2b --- /dev/null +++ b/samples/net/zstream/big_http_download/sample.yaml @@ -0,0 +1,11 @@ +sample: + description: Zstream API big HTTP download example + name: big_http_download +tests: + test: + harness: net + min_ram: 110 + min_flash: 192 + # Even with lots of RAM/flash, runs out of it + platform_exclude: minnowboard + tags: net diff --git a/samples/net/zstream/big_http_download/src/big_http_download.c b/samples/net/zstream/big_http_download/src/big_http_download.c new file mode 100644 index 0000000000000..c30c73b4770c3 --- /dev/null +++ b/samples/net/zstream/big_http_download/src/big_http_download.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "mbedtls/md.h" + +#ifndef __ZEPHYR__ + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#define sleep(x) k_sleep(x * 1000) + +#endif + +#include +#include +#include + +/* This URL is parsed in-place, so buffer must be non-const. */ +static char download_url[] = + /*"http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz";*/ + "https://ftp.gnu.org/gnu/tar/tar-1.13.tar"; +/* Quick testing. */ +/* "http://google.com/foo";*/ + +/* To generate: */ +/* print("".join(["\\x%02x" % x for x in list(binascii.unhexlify("hash"))])) */ +static uint8_t download_hash[32] = + /*"\x33\x7c\x37\xd7\xec\x00\x34\x84\x14\x22\x4b\xaa\x6b\xdb\x2d\x43\xf2\xa3\x4e\xf5\x67\x6b\xaf\xcd\xca\xd9\x16\xf1\x48\xb5\xb3\x17";*/ + "\xbe\x12\xfe\x40\xe1\xb2\x02\xd7\x0c\x45\x5d\x78\x4f\xbe\xc8\xcd\xb3\x38\xfe\x01\x1a\xb9\xe8\x62\x95\x81\x35\x68\x90\x6f\x60\x73"; + +#define SSTRLEN(s) (sizeof(s) - 1) +#define CHECK(r) { if (r == -1) { printf("Error: " #r "\n"); } } + +const char *host; +const char *port; +const char *uri_path = ""; +static char response[1024]; +static char response_hash[32]; +mbedtls_md_context_t hash_ctx; +const mbedtls_md_info_t *hash_info; +unsigned int cur_bytes; + +void dump_addrinfo(const struct addrinfo *ai) +{ + printf("addrinfo @%p: ai_family=%d, ai_socktype=%d, ai_protocol=%d, " + "sa_family=%d, sin_port=%x\n", + ai, ai->ai_family, ai->ai_socktype, ai->ai_protocol, + ai->ai_addr->sa_family, + ((struct sockaddr_in *)ai->ai_addr)->sin_port); +} + +void fatal(const char *msg) +{ + printf("Error: %s\n", msg); + exit(1); +} + +ssize_t sendall(struct zstream *stream, const void *buf, size_t len) +{ + while (len) { + ssize_t out_len = zstream_write(stream, buf, len); + if (out_len < 0) { + return out_len; + } + buf = (const char *)buf + out_len; + len -= out_len; + } + + return 0; +} + +int skip_headers(struct zstream *stream) +{ + int state = 0; + + while (1) { + char c; + int st; + + st = zstream_read(stream, &c, 1); + if (st <= 0) { + return st; + } + + if (state == 0 && c == '\r') { + state++; + } else if (state == 1 && c == '\n') { + state++; + } else if (state == 2 && c == '\r') { + state++; + } else if (state == 3 && c == '\n') { + break; + } else { + state = 0; + } + } + + return 1; +} + +void print_hex(const unsigned char *p, int len) +{ + while (len--) { + printf("%02x", *p++); + } +} + +void download(struct addrinfo *ai, mbedtls_ssl_config *tls_conf) +{ + int sock; + struct zstream_sock stream_sock; + struct zstream_tls stream_tls; + struct zstream *stream; + + cur_bytes = 0; + + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + CHECK(sock); + printf("sock = %d\n", sock); + CHECK(connect(sock, ai->ai_addr, ai->ai_addrlen)); + + zstream_sock_init(&stream_sock, sock); + stream = (struct zstream *)&stream_sock; + + if (tls_conf) { + zstream_tls_init(&stream_tls, stream, tls_conf, host); + stream = (struct zstream *)&stream_tls; + } + + sendall(stream, "GET /", SSTRLEN("GET /")); + sendall(stream, uri_path, strlen(uri_path)); + sendall(stream, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n")); + sendall(stream, "Host: ", SSTRLEN("Host: ")); + sendall(stream, host, strlen(host)); + sendall(stream, "\r\n\r\n", SSTRLEN("\r\n\r\n")); + zstream_flush(stream); + + if (skip_headers(stream) <= 0) { + printf("EOF or error in response headers\n"); + goto error; + } + + mbedtls_md_starts(&hash_ctx); + + while (1) { + int len = zstream_read(stream, response, sizeof(response) - 1); + + if (len < 0) { + printf("Error reading response\n"); + goto error; + } + + if (len == 0) { + break; + } + + mbedtls_md_update(&hash_ctx, response, len); + + cur_bytes += len; + printf("%u bytes\r", cur_bytes); + + response[len] = 0; + /*printf("%s\n", response);*/ + } + + printf("\n"); + + mbedtls_md_finish(&hash_ctx, response_hash); + + printf("Hash: "); + print_hex(response_hash, mbedtls_md_get_size(hash_info)); + printf("\n"); + + if (memcmp(response_hash, download_hash, + mbedtls_md_get_size(hash_info)) != 0) { + printf("HASH MISMATCH!\n"); + } + +error: + zstream_close(stream); +} + +int main(void) +{ + static struct addrinfo hints; + struct addrinfo *res; + int st; + char *p; + unsigned int total_bytes = 0; + mbedtls_ssl_config *tls_conf = NULL; + + setbuf(stdout, NULL); + + if (strncmp(download_url, "http://", SSTRLEN("http://")) == 0) { + port = "80"; + p = download_url + SSTRLEN("http://"); + } else if (strncmp(download_url, "https://", + SSTRLEN("https://")) == 0) { + if (ztls_get_tls_client_conf(&tls_conf) < 0) { + printf("Unable to initialize TLS\n"); + return 1; + } + mbedtls_ssl_conf_authmode(tls_conf, MBEDTLS_SSL_VERIFY_NONE); + printf("Warning: site certificate is not validated\n"); + port = "443"; + p = download_url + SSTRLEN("https://"); + } else { + fatal("Only http: and https: URLs are supported"); + } + + /* Parse host part */ + host = p; + while (*p && *p != ':' && *p != '/') { + p++; + } + + /* Store optional port part */ + if (*p == ':') { + *p++ = 0; + port = p; + } + + /* Parse path part */ + while (*p && *p != '/') { + p++; + } + + if (*p == '/') { + *p++ = 0; + uri_path = p; + } + + printf("Preparing HTTP GET request for http%s://%s:%s/%s\n", + (tls_conf ? "s" : ""), host, port, uri_path); + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + st = getaddrinfo(host, port, &hints, &res); + printf("getaddrinfo status: %d\n", st); + + if (st != 0) { + fatal("Unable to resolve address"); + } + + dump_addrinfo(res); + + hash_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + if (!hash_info) { + fatal("Unable to request hash type from mbedTLS"); + } + + mbedtls_md_init(&hash_ctx); + if (mbedtls_md_setup(&hash_ctx, hash_info, 0) < 0) { + fatal("Can't setup mbedTLS hash engine"); + } + + while (1) { + download(res, tls_conf); + + total_bytes += cur_bytes; + printf("Total downloaded so far: %uMB\n", total_bytes / (1024 * 1024)); + + sleep(3); + } + + mbedtls_md_free(&hash_ctx); + + return 0; +} From dcdcf88e5801cb46d09f9aeeb123c28c374b6b5e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 06/19] samples: sockets: http_get: Example of conversion to zstream API These exemplifies changes needed to convert a normal socket based application to use zstream API instead. (Note that this exactly this application is not intended to be converted, it's just a convinient base to show changes required). Signed-off-by: Paul Sokolovsky --- samples/net/sockets/http_get/Makefile.posix | 8 +++++++- samples/net/sockets/http_get/src/http_get.c | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/samples/net/sockets/http_get/Makefile.posix b/samples/net/sockets/http_get/Makefile.posix index 7f0573c2f708d..b3a4e598e9c6d 100644 --- a/samples/net/sockets/http_get/Makefile.posix +++ b/samples/net/sockets/http_get/Makefile.posix @@ -1,4 +1,10 @@ # This makefile builds the sample for a POSIX system, like Linux +INC = -I$(ZEPHYR_BASE)/include + +LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream + +LIBSRC = $(LIBPATH)/zstream_sock.c + http_get: src/http_get.c - $(CC) $^ -o $@ + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) -o $@ diff --git a/samples/net/sockets/http_get/src/http_get.c b/samples/net/sockets/http_get/src/http_get.c index 8785eb8a554a8..0e35b82602383 100644 --- a/samples/net/sockets/http_get/src/http_get.c +++ b/samples/net/sockets/http_get/src/http_get.c @@ -23,6 +23,8 @@ #endif +#include + /* HTTP server to connect to */ #define HTTP_HOST "google.com" /* Port to connect to, as string */ @@ -52,6 +54,8 @@ int main(void) static struct addrinfo hints; struct addrinfo *res; int st, sock; + struct zstream_sock stream_sock; + zstream stream; printf("Preparing HTTP GET request for http://" HTTP_HOST ":" HTTP_PORT HTTP_PATH "\n"); @@ -78,12 +82,17 @@ int main(void) CHECK(sock); printf("sock = %d\n", sock); CHECK(connect(sock, res->ai_addr, res->ai_addrlen)); - CHECK(send(sock, REQUEST, SSTRLEN(REQUEST), 0)); + + zstream_sock_init(&stream_sock, sock); + stream = (zstream)&stream_sock; + + CHECK(zstream_write(stream, REQUEST, SSTRLEN(REQUEST))); + CHECK(zstream_flush(stream)); printf("Response:\n\n"); while (1) { - int len = recv(sock, response, sizeof(response) - 1, 0); + int len = zstream_read(stream, response, sizeof(response) - 1); if (len < 0) { printf("Error reading response\n"); @@ -98,7 +107,7 @@ int main(void) printf("%s\n", response); } - (void)close(sock); + (void)zstream_close(sock); return 0; } From 3ed4149621c9453f67c038bc9362ab9549b32067 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 9 Mar 2018 21:24:22 +0200 Subject: [PATCH 07/19] samples: sockets: http_get: Example of conversion to zstream API These exemplifies changes needed to convert a normal socket based application to use zstream API instead. (Note that this exactly this application is not intended to be converted, it's just a convinient base to show changes required). Signed-off-by: Paul Sokolovsky --- samples/net/sockets/http_get/src/http_get.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/net/sockets/http_get/src/http_get.c b/samples/net/sockets/http_get/src/http_get.c index 0e35b82602383..da2974a84ee23 100644 --- a/samples/net/sockets/http_get/src/http_get.c +++ b/samples/net/sockets/http_get/src/http_get.c @@ -55,7 +55,7 @@ int main(void) struct addrinfo *res; int st, sock; struct zstream_sock stream_sock; - zstream stream; + struct zstream *stream; printf("Preparing HTTP GET request for http://" HTTP_HOST ":" HTTP_PORT HTTP_PATH "\n"); @@ -84,7 +84,7 @@ int main(void) CHECK(connect(sock, res->ai_addr, res->ai_addrlen)); zstream_sock_init(&stream_sock, sock); - stream = (zstream)&stream_sock; + stream = (struct zstream *)&stream_sock; CHECK(zstream_write(stream, REQUEST, SSTRLEN(REQUEST))); CHECK(zstream_flush(stream)); From ad1c7f577e5e5a2d4f02b24e9a774643e937de00 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 08/19] samples: sockets: http_get: Further conversion to use use TLS stream. Signed-off-by: Paul Sokolovsky --- samples/net/sockets/http_get/Makefile.posix | 11 ++++++-- samples/net/sockets/http_get/prj.conf | 6 +++++ samples/net/sockets/http_get/src/http_get.c | 30 ++++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/samples/net/sockets/http_get/Makefile.posix b/samples/net/sockets/http_get/Makefile.posix index b3a4e598e9c6d..b3c25ab57f070 100644 --- a/samples/net/sockets/http_get/Makefile.posix +++ b/samples/net/sockets/http_get/Makefile.posix @@ -4,7 +4,14 @@ INC = -I$(ZEPHYR_BASE)/include LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream -LIBSRC = $(LIBPATH)/zstream_sock.c +LIBSRC = \ + $(LIBPATH)/zstream_sock.c \ + $(LIBPATH)/zstream_tls.c \ + $(LIBPATH)/../tls_conf/tls_conf.c \ + +LDLIBS = -lmbedtls -lmbedx509 -lmbedcrypto + +CFLAGS = -DCONFIG_MBEDTLS -DCONFIG_MBEDTLS_DEBUG_LEVEL=0 http_get: src/http_get.c - $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) -o $@ + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) $(LDLIBS) -o $@ diff --git a/samples/net/sockets/http_get/prj.conf b/samples/net/sockets/http_get/prj.conf index c2e01b27647ee..13240bd49c253 100644 --- a/samples/net/sockets/http_get/prj.conf +++ b/samples/net/sockets/http_get/prj.conf @@ -1,5 +1,11 @@ # General config CONFIG_NEWLIB_LIBC=y +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h" +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=50000 # Networking config CONFIG_NETWORKING=y diff --git a/samples/net/sockets/http_get/src/http_get.c b/samples/net/sockets/http_get/src/http_get.c index da2974a84ee23..cc8b9da3d9f49 100644 --- a/samples/net/sockets/http_get/src/http_get.c +++ b/samples/net/sockets/http_get/src/http_get.c @@ -23,12 +23,14 @@ #endif +#include #include +#include /* HTTP server to connect to */ #define HTTP_HOST "google.com" /* Port to connect to, as string */ -#define HTTP_PORT "80" +#define HTTP_PORT "443" /* HTTP path to request */ #define HTTP_PATH "/" @@ -38,6 +40,8 @@ #define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n" +int http_request(struct zstream *stream); + static char response[1024]; void dump_addrinfo(const struct addrinfo *ai) @@ -55,7 +59,14 @@ int main(void) struct addrinfo *res; int st, sock; struct zstream_sock stream_sock; + struct zstream_tls stream_tls; struct zstream *stream; + mbedtls_ssl_config *tls_conf; + + if (ztls_get_tls_client_conf(&tls_conf) < 0) { + printf("Unable to initialize TLS\n"); + return 1; + } printf("Preparing HTTP GET request for http://" HTTP_HOST ":" HTTP_PORT HTTP_PATH "\n"); @@ -83,9 +94,26 @@ int main(void) printf("sock = %d\n", sock); CHECK(connect(sock, res->ai_addr, res->ai_addrlen)); + /* Wrap socket into a stream */ zstream_sock_init(&stream_sock, sock); stream = (struct zstream *)&stream_sock; + /* Wrap socket stream into a TLS stream */ + mbedtls_ssl_conf_authmode(tls_conf, MBEDTLS_SSL_VERIFY_NONE); + printf("Warning: site certificate is not validated\n"); + + st = zstream_tls_init(&stream_tls, stream, tls_conf, NULL); + if (st < 0) { + printf("Unable to create TLS connection\n"); + return 1; + } + stream = (struct zstream *)&stream_tls; + + return http_request(stream); +} + +int http_request(struct zstream *stream) +{ CHECK(zstream_write(stream, REQUEST, SSTRLEN(REQUEST))); CHECK(zstream_flush(stream)); From c511840125f078c3a370cfd25c41eff5c43b1db2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 09/19] samples: sockets: big_http_download: Convert to zstream_sock Signed-off-by: Paul Sokolovsky --- .../sockets/big_http_download/Makefile.posix | 6 +++- .../big_http_download/src/big_http_download.c | 36 +++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/samples/net/sockets/big_http_download/Makefile.posix b/samples/net/sockets/big_http_download/Makefile.posix index 78e33f7717d08..366ca4f70fc29 100644 --- a/samples/net/sockets/big_http_download/Makefile.posix +++ b/samples/net/sockets/big_http_download/Makefile.posix @@ -2,5 +2,9 @@ # It required mbedTLS to be installed on the system, e.g. # libmbedtls-dev package on Debian/Ubuntu Linux. +INC = -I$(ZEPHYR_BASE)/include +LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream +LIBSRC = $(LIBPATH)/zstream_sock.c + big_http_download: src/big_http_download.c - $(CC) $^ -lmbedcrypto -o $@ + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) -lmbedcrypto -o $@ diff --git a/samples/net/sockets/big_http_download/src/big_http_download.c b/samples/net/sockets/big_http_download/src/big_http_download.c index ccd4a9da4d567..3bd2bb59e8d5b 100644 --- a/samples/net/sockets/big_http_download/src/big_http_download.c +++ b/samples/net/sockets/big_http_download/src/big_http_download.c @@ -27,6 +27,8 @@ #endif +#include + /* This URL is parsed in-place, so buffer must be non-const. */ static char download_url[] = "http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz"; @@ -63,10 +65,10 @@ void fatal(const char *msg) exit(1); } -ssize_t sendall(int sock, const void *buf, size_t len) +ssize_t sendall(struct zstream *stream, const void *buf, size_t len) { while (len) { - ssize_t out_len = send(sock, buf, len, 0); + ssize_t out_len = zstream_write(stream, buf, len); if (out_len < 0) { return out_len; } @@ -77,7 +79,7 @@ ssize_t sendall(int sock, const void *buf, size_t len) return 0; } -int skip_headers(int sock) +int skip_headers(struct zstream *stream) { int state = 0; @@ -85,7 +87,7 @@ int skip_headers(int sock) char c; int st; - st = recv(sock, &c, 1, 0); + st = zstream_read(stream, &c, 1); if (st <= 0) { return st; } @@ -116,6 +118,8 @@ void print_hex(const unsigned char *p, int len) void download(struct addrinfo *ai) { int sock; + struct zstream_sock stream_sock; + struct zstream *stream; cur_bytes = 0; @@ -123,14 +127,18 @@ void download(struct addrinfo *ai) CHECK(sock); printf("sock = %d\n", sock); CHECK(connect(sock, ai->ai_addr, ai->ai_addrlen)); - sendall(sock, "GET /", SSTRLEN("GET /")); - sendall(sock, uri_path, strlen(uri_path)); - sendall(sock, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n")); - sendall(sock, "Host: ", SSTRLEN("Host: ")); - sendall(sock, host, strlen(host)); - sendall(sock, "\r\n\r\n", SSTRLEN("\r\n\r\n")); - - if (skip_headers(sock) <= 0) { + + zstream_sock_init(&stream_sock, sock); + stream = (struct zstream *)&stream_sock; + + sendall(stream, "GET /", SSTRLEN("GET /")); + sendall(stream, uri_path, strlen(uri_path)); + sendall(stream, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n")); + sendall(stream, "Host: ", SSTRLEN("Host: ")); + sendall(stream, host, strlen(host)); + sendall(stream, "\r\n\r\n", SSTRLEN("\r\n\r\n")); + + if (skip_headers(stream) <= 0) { printf("EOF or error in response headers\n"); goto error; } @@ -138,7 +146,7 @@ void download(struct addrinfo *ai) mbedtls_md_starts(&hash_ctx); while (1) { - int len = recv(sock, response, sizeof(response) - 1, 0); + int len = zstream_read(stream, response, sizeof(response) - 1); if (len < 0) { printf("Error reading response\n"); @@ -172,7 +180,7 @@ void download(struct addrinfo *ai) } error: - (void)close(sock); + (void)zstream_close(sock); } int main(void) From 8b68df016114c0de74f1cb0f63366592c457540c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 10/19] samples: sockets: big_http_download: Convert to zstream_tls Signed-off-by: Paul Sokolovsky --- .../sockets/big_http_download/Makefile.posix | 10 +++- .../net/sockets/big_http_download/prj.conf | 5 ++ .../big_http_download/src/big_http_download.c | 47 ++++++++++++++----- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/samples/net/sockets/big_http_download/Makefile.posix b/samples/net/sockets/big_http_download/Makefile.posix index 366ca4f70fc29..a5a12452e53bc 100644 --- a/samples/net/sockets/big_http_download/Makefile.posix +++ b/samples/net/sockets/big_http_download/Makefile.posix @@ -4,7 +4,13 @@ INC = -I$(ZEPHYR_BASE)/include LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream -LIBSRC = $(LIBPATH)/zstream_sock.c +LIBSRC = $(LIBPATH)/zstream_sock.c \ + $(LIBPATH)/zstream_tls.c \ + $(LIBPATH)/../tls_conf/tls_conf.c \ + +LDLIBS = -lmbedtls -lmbedx509 -lmbedcrypto + +CFLAGS = -DCONFIG_MBEDTLS -DCONFIG_MBEDTLS_DEBUG_LEVEL=0 big_http_download: src/big_http_download.c - $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) -lmbedcrypto -o $@ + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) $(LDLIBS) -o $@ diff --git a/samples/net/sockets/big_http_download/prj.conf b/samples/net/sockets/big_http_download/prj.conf index 2a7830c47ff46..0cfc398eeab6c 100644 --- a/samples/net/sockets/big_http_download/prj.conf +++ b/samples/net/sockets/big_http_download/prj.conf @@ -1,7 +1,12 @@ # General config CONFIG_NEWLIB_LIBC=y +CONFIG_MAIN_STACK_SIZE=6000 CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_CFG_FILE="config-tls1_2.h" +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=60000 +#CONFIG_MBEDTLS_DEBUG=y # Networking config CONFIG_NETWORKING=y diff --git a/samples/net/sockets/big_http_download/src/big_http_download.c b/samples/net/sockets/big_http_download/src/big_http_download.c index 3bd2bb59e8d5b..6d807d5d657e1 100644 --- a/samples/net/sockets/big_http_download/src/big_http_download.c +++ b/samples/net/sockets/big_http_download/src/big_http_download.c @@ -27,22 +27,28 @@ #endif +#include #include +#include /* This URL is parsed in-place, so buffer must be non-const. */ static char download_url[] = - "http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz"; + /*"http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz";*/ + "https://ftp.gnu.org/gnu/tar/tar-1.13.tar"; /* Quick testing. */ /* "http://google.com/foo";*/ +/* To generate: */ /* print("".join(["\\x%02x" % x for x in list(binascii.unhexlify("hash"))])) */ -static uint8_t download_hash[32] = "\x33\x7c\x37\xd7\xec\x00\x34\x84\x14\x22\x4b\xaa\x6b\xdb\x2d\x43\xf2\xa3\x4e\xf5\x67\x6b\xaf\xcd\xca\xd9\x16\xf1\x48\xb5\xb3\x17"; +static uint8_t download_hash[32] = + /*"\x33\x7c\x37\xd7\xec\x00\x34\x84\x14\x22\x4b\xaa\x6b\xdb\x2d\x43\xf2\xa3\x4e\xf5\x67\x6b\xaf\xcd\xca\xd9\x16\xf1\x48\xb5\xb3\x17";*/ + "\xbe\x12\xfe\x40\xe1\xb2\x02\xd7\x0c\x45\x5d\x78\x4f\xbe\xc8\xcd\xb3\x38\xfe\x01\x1a\xb9\xe8\x62\x95\x81\x35\x68\x90\x6f\x60\x73"; #define SSTRLEN(s) (sizeof(s) - 1) #define CHECK(r) { if (r == -1) { printf("Error: " #r "\n"); exit(1); } } const char *host; -const char *port = "80"; +const char *port; const char *uri_path = ""; static char response[1024]; static char response_hash[32]; @@ -115,10 +121,11 @@ void print_hex(const unsigned char *p, int len) } } -void download(struct addrinfo *ai) +void download(struct addrinfo *ai, mbedtls_ssl_config *tls_conf) { int sock; struct zstream_sock stream_sock; + struct zstream_tls stream_tls; struct zstream *stream; cur_bytes = 0; @@ -131,12 +138,18 @@ void download(struct addrinfo *ai) zstream_sock_init(&stream_sock, sock); stream = (struct zstream *)&stream_sock; + if (tls_conf) { + zstream_tls_init(&stream_tls, stream, tls_conf, host); + stream = (struct zstream *)&stream_tls; + } + sendall(stream, "GET /", SSTRLEN("GET /")); sendall(stream, uri_path, strlen(uri_path)); sendall(stream, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n")); sendall(stream, "Host: ", SSTRLEN("Host: ")); sendall(stream, host, strlen(host)); sendall(stream, "\r\n\r\n", SSTRLEN("\r\n\r\n")); + zstream_flush(stream); if (skip_headers(stream) <= 0) { printf("EOF or error in response headers\n"); @@ -191,15 +204,27 @@ int main(void) char *p; unsigned int total_bytes = 0; int resolve_attempts = 10; + mbedtls_ssl_config *tls_conf = NULL; setbuf(stdout, NULL); - if (strncmp(download_url, "http://", SSTRLEN("http://")) != 0) { - fatal("Only http: URLs are supported"); + if (strncmp(download_url, "http://", SSTRLEN("http://")) == 0) { + port = "80"; + p = download_url + SSTRLEN("http://"); + } else if (strncmp(download_url, "https://", + SSTRLEN("https://")) == 0) { + if (ztls_get_tls_client_conf(&tls_conf) < 0) { + printf("Unable to initialize TLS\n"); + return 1; + } + mbedtls_ssl_conf_authmode(tls_conf, MBEDTLS_SSL_VERIFY_NONE); + printf("Warning: site certificate is not validated\n"); + port = "443"; + p = download_url + SSTRLEN("https://"); + } else { + fatal("Only http: and https: URLs are supported"); } - p = download_url + SSTRLEN("http://"); - /* Parse host part */ host = p; while (*p && *p != ':' && *p != '/') { @@ -222,8 +247,8 @@ int main(void) uri_path = p; } - printf("Preparing HTTP GET request for http://%s:%s/%s\n", - host, port, uri_path); + printf("Preparing HTTP GET request for http%s://%s:%s/%s\n", + (tls_conf ? "s" : ""), host, port, uri_path); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -256,7 +281,7 @@ int main(void) } while (1) { - download(res); + download(res, tls_conf); total_bytes += cur_bytes; printf("Total downloaded so far: %uMB\n", total_bytes / (1024 * 1024)); From cb3f0d5d29e9f759a1cf8ec2e4a195a3ac9c13c7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 11/19] samples: sockets: echo: Convert to zstream_tls Signed-off-by: Paul Sokolovsky --- samples/net/sockets/echo/Makefile.posix | 18 +++++++- samples/net/sockets/echo/prj.conf | 6 +++ samples/net/sockets/echo/src/socket_echo.c | 52 ++++++++++++++++++++-- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/samples/net/sockets/echo/Makefile.posix b/samples/net/sockets/echo/Makefile.posix index 7bf5def40dd92..8468bd89fe3a7 100644 --- a/samples/net/sockets/echo/Makefile.posix +++ b/samples/net/sockets/echo/Makefile.posix @@ -1,4 +1,18 @@ -# This makefile builds socket_echo sample for POSIX system, like Linux +# This makefile builds the sample for a POSIX system, like Linux +# It required mbedTLS to be installed on the system, e.g. +# libmbedtls-dev package on Debian/Ubuntu Linux. +# To test connection: +# openssl s_client -connect localhost:4242 + +INC = -I$(ZEPHYR_BASE)/include +LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream +LIBSRC = $(LIBPATH)/zstream_sock.c \ + $(LIBPATH)/zstream_tls.c \ + $(LIBPATH)/../tls_conf/tls_conf.c \ + +LDLIBS = -lmbedtls -lmbedx509 -lmbedcrypto + +CFLAGS = -DCONFIG_MBEDTLS -DCONFIG_MBEDTLS_DEBUG_LEVEL=0 socket_echo: src/socket_echo.c - $(CC) $^ -o $@ + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) $(LDLIBS) -o $@ diff --git a/samples/net/sockets/echo/prj.conf b/samples/net/sockets/echo/prj.conf index 96344b682e2e5..875c7081d1f16 100644 --- a/samples/net/sockets/echo/prj.conf +++ b/samples/net/sockets/echo/prj.conf @@ -1,5 +1,11 @@ # General config CONFIG_NEWLIB_LIBC=y +CONFIG_MAIN_STACK_SIZE=6000 +CONFIG_MBEDTLS=y +#CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h" +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=50000 # Networking config CONFIG_NETWORKING=y diff --git a/samples/net/sockets/echo/src/socket_echo.c b/samples/net/sockets/echo/src/socket_echo.c index 539cdaedc0178..19990b5587152 100644 --- a/samples/net/sockets/echo/src/socket_echo.c +++ b/samples/net/sockets/echo/src/socket_echo.c @@ -22,6 +22,12 @@ #endif +#include +#include +#include + +#include "../../../echo_server/src/test_certs.h" + #define PORT 4242 int main(void) @@ -29,6 +35,30 @@ int main(void) int serv; struct sockaddr_in bind_addr; static int counter; + mbedtls_ssl_config *tls_conf; + static struct ztls_cert_key_pair cert_key; + int res; + + if (ztls_get_tls_server_conf(&tls_conf) < 0) { + printf("Unable to initialize TLS\n"); + return 1; + } + + res = ztls_parse_cert_key_pair(&cert_key, + rsa_example_cert_der, + rsa_example_cert_der_len, + rsa_example_keypair_der, + rsa_example_keypair_der_len); + if (res < 0) { + printf("Unable to parse cert/privkey\n"); + return 1; + } + + res = ztls_conf_add_own_cert_key_pair(tls_conf, &cert_key); + if (res < 0) { + printf("Unable to set cert/privkey\n"); + return 1; + } serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -45,15 +75,26 @@ int main(void) struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); char addr_str[32]; + struct zstream_sock stream_sock; + struct zstream_tls stream_tls; + struct zstream *stream; int client = accept(serv, (struct sockaddr *)&client_addr, &client_addr_len); inet_ntop(client_addr.sin_family, &client_addr.sin_addr, addr_str, sizeof(addr_str)); printf("Connection #%d from %s\n", counter++, addr_str); + zstream_sock_init(&stream_sock, client); + stream = (struct zstream *)&stream_sock; + + if (zstream_tls_init(&stream_tls, stream, tls_conf, NULL) < 0) { + printf("Error creating TLS connection\n"); + goto error; + } + stream = (struct zstream *)&stream_tls; while (1) { char buf[128], *p; - int len = recv(client, buf, sizeof(buf), 0); + int len = zstream_read(stream, buf, sizeof(buf)); int out_len; if (len <= 0) { @@ -65,7 +106,7 @@ int main(void) p = buf; do { - out_len = send(client, p, len, 0); + out_len = zstream_write(stream, p, len); if (out_len < 0) { printf("error: send: %d\n", errno); goto error; @@ -73,10 +114,15 @@ int main(void) p += out_len; len -= out_len; } while (len); + + if (zstream_flush(stream) < 0) { + printf("error: flush: %d\n", errno); + goto error; + } } error: - close(client); + zstream_close(stream); printf("Connection from %s closed\n", addr_str); } } From a4a80bb824ca91d9ecace06be30e655746b33a77 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 00:00:29 +0200 Subject: [PATCH 12/19] samples: sockets: echo_async: Convert to zstream_tls. Signed-off-by: Paul Sokolovsky --- samples/net/sockets/echo_async/Makefile.posix | 12 ++- samples/net/sockets/echo_async/prj.conf | 5 ++ .../net/sockets/echo_async/src/socket_echo.c | 79 ++++++++++++++----- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/samples/net/sockets/echo_async/Makefile.posix b/samples/net/sockets/echo_async/Makefile.posix index a166f75d2c247..65d011218ce2c 100644 --- a/samples/net/sockets/echo_async/Makefile.posix +++ b/samples/net/sockets/echo_async/Makefile.posix @@ -1,4 +1,14 @@ # This makefile builds sample for POSIX system, like Linux +INC = -I$(ZEPHYR_BASE)/include +LIBPATH = $(ZEPHYR_BASE)/subsys/net/lib/zstream +LIBSRC = $(LIBPATH)/zstream_sock.c \ + $(LIBPATH)/zstream_tls.c \ + $(LIBPATH)/../tls_conf/tls_conf.c \ + +LDLIBS = -lmbedtls -lmbedx509 -lmbedcrypto + +CFLAGS = -DCONFIG_MBEDTLS -DCONFIG_MBEDTLS_DEBUG_LEVEL=0 + socket_echo: src/socket_echo.c - $(CC) $^ -o $@ + $(CC) $(INC) $(CFLAGS) $^ $(LIBSRC) $(LDLIBS) -o $@ diff --git a/samples/net/sockets/echo_async/prj.conf b/samples/net/sockets/echo_async/prj.conf index bfab069ab9b9a..68096403065bc 100644 --- a/samples/net/sockets/echo_async/prj.conf +++ b/samples/net/sockets/echo_async/prj.conf @@ -1,5 +1,10 @@ # General config CONFIG_NEWLIB_LIBC=y +CONFIG_MAIN_STACK_SIZE=6000 +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=50000 # Networking config CONFIG_NETWORKING=y diff --git a/samples/net/sockets/echo_async/src/socket_echo.c b/samples/net/sockets/echo_async/src/socket_echo.c index a5a077606d079..2a88421be88d7 100644 --- a/samples/net/sockets/echo_async/src/socket_echo.c +++ b/samples/net/sockets/echo_async/src/socket_echo.c @@ -25,6 +25,12 @@ #endif +#include +#include +#include + +#include "../../../echo_server/src/test_certs.h" + /* For Zephyr, keep max number of fd's in sync with max poll() capacity */ #ifdef CONFIG_NET_SOCKETS_POLL_MAX #define NUM_FDS CONFIG_NET_SOCKETS_POLL_MAX @@ -32,11 +38,17 @@ #define NUM_FDS 5 #endif +#define NUM_LISTEN_FDS 2 + #define PORT 4242 /* Number of simultaneous client connections will be NUM_FDS be minus 2 */ struct pollfd pollfds[NUM_FDS]; +struct zstream_sock streams_sock[NUM_FDS]; +struct zstream_tls streams_tls[NUM_FDS]; int pollnum; +static mbedtls_ssl_config *tls_conf; +static struct ztls_cert_key_pair cert_key; static void nonblock(int fd) { @@ -50,7 +62,7 @@ static void block(int fd) fcntl(fd, F_SETFL, fl & ~O_NONBLOCK); } -int pollfds_add(int fd) +int register_sock(int fd) { int i; if (pollnum < NUM_FDS) { @@ -66,20 +78,29 @@ int pollfds_add(int fd) } found: + /* Don't create streams for listening sockets */ + if (i >= NUM_LISTEN_FDS) { + struct zstream *stream; + + zstream_sock_init(&streams_sock[i], fd); + stream = (struct zstream *)&streams_sock[i]; + + if (zstream_tls_init(&streams_tls[i], stream, tls_conf, NULL) < 0) { + printf("Error creating TLS connection\n"); + return -1; + } + } + pollfds[i].fd = fd; pollfds[i].events = POLLIN; return 0; } -void pollfds_del(int fd) +void unregister_sock(int idx) { - for (int i = 0; i < pollnum; i++) { - if (pollfds[i].fd == fd) { - pollfds[i].fd = -1; - break; - } - } + zstream_close((struct zstream *)&streams_tls[idx]); + pollfds[idx].fd = -1; } int main(void) @@ -100,6 +121,27 @@ int main(void) .sin6_addr = IN6ADDR_ANY_INIT, }; + if (ztls_get_tls_server_conf(&tls_conf) < 0) { + printf("Unable to initialize TLS\n"); + return 1; + } + + res = ztls_parse_cert_key_pair(&cert_key, + rsa_example_cert_der, + rsa_example_cert_der_len, + rsa_example_keypair_der, + rsa_example_keypair_der_len); + if (res < 0) { + printf("Unable to parse cert/privkey\n"); + return 1; + } + + res = ztls_conf_add_own_cert_key_pair(tls_conf, &cert_key); + if (res < 0) { + printf("Unable to set cert/privkey\n"); + return 1; + } + serv4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); res = bind(serv4, (struct sockaddr *)&bind_addr4, sizeof(bind_addr4)); if (res == -1) { @@ -124,8 +166,8 @@ int main(void) listen(serv4, 5); listen(serv6, 5); - pollfds_add(serv4); - pollfds_add(serv6); + register_sock(serv4); + register_sock(serv6); printf("Asynchronous TCP echo server waits for connections on port %d...\n", PORT); @@ -145,7 +187,7 @@ int main(void) continue; } int fd = pollfds[i].fd; - if (i < 2) { + if (i < NUM_LISTEN_FDS) { /* If server socket */ int client = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len); @@ -155,23 +197,24 @@ int main(void) addr_str, sizeof(addr_str)); printf("Connection #%d from %s fd=%d\n", counter++, addr_str, client); - if (pollfds_add(client) < 0) { - static char msg[] = "Too many connections\n"; - send(client, msg, sizeof(msg) - 1, 0); + if (register_sock(client) < 0) { close(client); } else { nonblock(client); } } else { char buf[128]; - int len = recv(fd, buf, sizeof(buf), 0); + int len = zstream_read((struct zstream *)&streams_tls[i], buf, sizeof(buf)); if (len <= 0) { if (len < 0) { + if (errno == EAGAIN) { + /* Underlying socket could be readable, but stream still have EAGAIN */ + continue; + } printf("error: recv: %d\n", errno); } error: - pollfds_del(fd); - close(fd); + unregister_sock(i); printf("Connection fd=%d closed\n", fd); } else { int out_len; @@ -185,7 +228,7 @@ int main(void) */ block(fd); for (p = buf; len; len -= out_len) { - out_len = send(fd, p, len, 0); + out_len = zstream_write((struct zstream *)&streams_tls[i], p, len); if (out_len < 0) { printf("error: " "send: %d\n", From 77ee6a40ed65e5e9bb3ca00763f6061cc612090e Mon Sep 17 00:00:00 2001 From: Gil Pitney Date: Thu, 25 Jan 2018 16:15:20 -0800 Subject: [PATCH 13/19] net: async_socket: Asynchronous socket library An API for adapting synchronous BSD socket APIs to applications requiring asynchronous callbacks. Created to ease migration of asynchronous Zephyr IP protocols from net_app/net_context to BSD sockets. Initially, this supports client side TCP sockets, to enable porting the mqtt publisher sample to BSD sockets. Todo: - add UDP async sendto/recvfrom - add server support: accept/listen - handle nonblocking send socket failures (EWOULDBLOCK, EAGAIN, etc). - ensure IPV6 is supported. Signed-off-by: Gil Pitney --- include/net/async_socket.h | 74 ++++ subsys/net/lib/CMakeLists.txt | 1 + subsys/net/lib/Kconfig | 2 + subsys/net/lib/async_socket/CMakeLists.txt | 4 + subsys/net/lib/async_socket/Kconfig | 31 ++ subsys/net/lib/async_socket/async_socket.c | 380 +++++++++++++++++++++ 6 files changed, 492 insertions(+) create mode 100644 include/net/async_socket.h create mode 100644 subsys/net/lib/async_socket/CMakeLists.txt create mode 100644 subsys/net/lib/async_socket/Kconfig create mode 100644 subsys/net/lib/async_socket/async_socket.c diff --git a/include/net/async_socket.h b/include/net/async_socket.h new file mode 100644 index 0000000000000..98fc6add8b6e3 --- /dev/null +++ b/include/net/async_socket.h @@ -0,0 +1,74 @@ +/** + * @file + * @brief Asynchronous sockets API definitions + * + * An API for adapting synchronous BSD socket APIs to applications + * requiring asynchronous callbacks. + * + * Created to ease adaptation of asynchronous IP protocols from + * net_app/net_context to sockets. + */ + +/* + * Copyright (c) 2018, Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_ASYNC_SOCKET_H +#define __NET_ASYNC_SOCKET_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Callbacks, similar in semantics to those of net_context.h */ +typedef void (*async_connect_cb_t)(int sock, + int status, + void *cb_data); + +typedef void (*async_send_cb_t)(int sock, + int bytes_sent, + void *cb_data); + +typedef void (*async_recv_cb_t)(int sock, + void *data, + size_t bytes_received, + void *cb_data); + +/* + * Errors are the same as the corresponding POSIX socket functions: i.e., + * a return value of -1 implicitly sets errno. + */ + +/* For now, same semantics as socket() call: */ +inline int async_socket(int family, int type, int proto) +{ + return socket(family, type, proto); +} + +int async_close(int sock); + +int async_bind(int sock, const struct sockaddr *addr, socklen_t addrlen); + +int async_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, + async_connect_cb_t cb, void *cb_data); + +ssize_t async_send(int sock, const void *buf, size_t len, + async_send_cb_t cb, void *cb_data, int flags); + +/* buf must be unique per sock */ +ssize_t async_recv(int sock, void *buf, size_t max_len, + async_recv_cb_t cb, void *cb_data); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __NET_ASYNC_SOCKET_H */ diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index cbe0d02be6d45..b91a899a50f7f 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory_ifdef(CONFIG_NET_SOCKETS sockets) add_subdirectory_ifdef(CONFIG_NET_SOCKETS zstream) add_subdirectory_ifdef(CONFIG_WEBSOCKET websocket) add_subdirectory_ifdef(CONFIG_MBEDTLS tls_conf) +add_subdirectory_ifdef(CONFIG_NET_ASYNC_SOCKET async_socket) if(CONFIG_HTTP_PARSER_URL OR CONFIG_HTTP_PARSER diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index 8bd37062d9a6f..3e107bb9e0d4f 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -28,4 +28,6 @@ source "subsys/net/lib/app/Kconfig" source "subsys/net/lib/sockets/Kconfig" +source "subsys/net/lib/async_socket/Kconfig" + endmenu diff --git a/subsys/net/lib/async_socket/CMakeLists.txt b/subsys/net/lib/async_socket/CMakeLists.txt new file mode 100644 index 0000000000000..36b8964133c2c --- /dev/null +++ b/subsys/net/lib/async_socket/CMakeLists.txt @@ -0,0 +1,4 @@ +zephyr_include_directories(.) +zephyr_sources( + async_socket.c + ) diff --git a/subsys/net/lib/async_socket/Kconfig b/subsys/net/lib/async_socket/Kconfig new file mode 100644 index 0000000000000..0ed90a063a600 --- /dev/null +++ b/subsys/net/lib/async_socket/Kconfig @@ -0,0 +1,31 @@ +# Kconfig - Async sockets over BSD Sockets + +# +# Copyright (c) 2018, Texas Instruments Incorporated +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig NET_ASYNC_SOCKET + bool "Async socket API over BSD Sockets" + default n + select NET_SOCKETS + select NET_SOCKETS_POSIX_NAMES + select NEWLIB_LIBC # Needed to find + help + Asynchronous adaptation layer over BSD sockets". + +if NET_ASYNC_SOCKET + +config NET_DEBUG_ASYNC_SOCKET + bool "Debug Async Sockets API" + default n + default y if NET_LOG_GLOBAL + help + Enables logging for async sockets code. + +config NET_ASYNC_SOCKET_PRIO + int "Startup priority for the async socket server" + default 90 + +endif # NET_ASYNC_SOCKET diff --git a/subsys/net/lib/async_socket/async_socket.c b/subsys/net/lib/async_socket/async_socket.c new file mode 100644 index 0000000000000..694f571d24e4c --- /dev/null +++ b/subsys/net/lib/async_socket/async_socket.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2018, Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_NET_DEBUG_ASYNC_SOCKETS) +#define SYS_LOG_DOMAIN "net/async_sock" +#define NET_LOG_ENABLED 1 +#endif + +/* Zephyr headers */ +#include +#include +#include + +/* Convenience macro to check for invalid socket fd */ +#define INVALID_SOCK (-1) + +/* Add one for signal_socket to unblock poll() on demand: */ +#define MAX_RCV_CALLBACKS (CONFIG_NET_SOCKETS_POLL_MAX + 1) + +#define LOOPBACK_ADDR "127.0.0.1" +#define DISCARD_PORT 9 + +#define SIGNAL_MSG "POLL" +#define SIGNAL_MSG_SIZE 4 + +#define ASYNC_SOCK_TASK_STACKSIZE 1024 +#define ASYNC_SOCK_TASK_PRIORITY K_HIGHEST_THREAD_PRIO + +/*Socket to callback map, for use by async_recv() and async_sock_server() */ +struct rcv_callbacks { + /* Our socket id */ + int sock; + + /* Buffer for receive and its maximum length */ + void *buf; + size_t max_len; + + /* Callback to be triggered when socket receives data */ + async_recv_cb_t cb; + + /* User data to pass back to the callback */ + void *cb_data; +}; +static struct rcv_callbacks rcv_callback[MAX_RCV_CALLBACKS] = { 0, }; +static int num_rcv_cbs; + +/* Special loopback socket used to unblock the poll() API: */ +static int signal_sock; +static struct sockaddr_in loopback_addr; + +/* Dedicated buffer for loopback message: */ +static u8_t server_rcv_buf[SIGNAL_MSG_SIZE]; + +/* Loopback socket's bind address: */ +static struct sockaddr_in bind_addr = { + .sin_family = AF_INET, + .sin_port = htons(DISCARD_PORT), + .sin_addr = { + .s_addr = htonl(INADDR_ANY), + }, +}; + +/* Async socket server thread: */ +static K_THREAD_STACK_DEFINE(async_sock_task_stack, ASYNC_SOCK_TASK_STACKSIZE); +static struct k_thread async_sock_task_data; + +/* Helper function, to restart poll() loop: */ +static int async_server_restart(void) +{ + int rc, status = 0; + + rc = sendto(signal_sock, SIGNAL_MSG, SIGNAL_MSG_SIZE, 0, + (struct sockaddr *)&loopback_addr, + sizeof(loopback_addr)); + if (rc < 0) { + status = -errno; + } + return status; +} + +/* Receive callback helper mapping functions: */ + +static struct rcv_callbacks *get_rcv_callback_slot(int sock) +{ + struct rcv_callbacks *entry, *end; + + end = rcv_callback + ARRAY_SIZE(rcv_callback); + for (entry = &rcv_callback[0]; entry < end; entry++) { + if (entry->sock == sock) { + return entry; + } + } + return NULL; +} + +static void rcv_callbacks_init(void) +{ + struct rcv_callbacks *entry, *end; + + end = rcv_callback + ARRAY_SIZE(rcv_callback); + for (entry = &rcv_callback[0]; entry < end; entry++) { + entry->sock = INVALID_SOCK; + } +} + +static int rcv_callback_register(int sock, + void *buf, size_t max_len, + async_recv_cb_t cb, void *cb_data) +{ + int status = 0; + struct rcv_callbacks *rcv_cb = NULL; + + /* See if this sock already has a registered callback: */ + rcv_cb = get_rcv_callback_slot(sock); + if (!rcv_cb) { + /* If not, see if we have room: */ + if (num_rcv_cbs == MAX_RCV_CALLBACKS) { + /* No more room: */ + NET_ERR("Increase CONFIG_NET_SOCKETS_POLL_MAX"); + } else { + /* Find new slot for this socket: */ + rcv_cb = get_rcv_callback_slot(INVALID_SOCK); + } + } + + if (rcv_cb) { + rcv_cb->sock = sock; + rcv_cb->buf = buf; + rcv_cb->max_len = max_len; + rcv_cb->cb = cb; + rcv_cb->cb_data = cb_data; + + num_rcv_cbs++; + + /* Now, signal the signal_sock to restart the poll server, + * so it can recreate the ufds[] list and start poll() + * with the newly registered socket. + */ + status = async_server_restart(); + } else { + status = -ENOMEM; + } + + return status; +} + +static void rcv_callback_deregister(short int sock) +{ + int status = 0; + struct rcv_callbacks *rcv_cb = NULL; + + /* Find slot of this socket: */ + rcv_cb = get_rcv_callback_slot(sock); + if (rcv_cb) { + rcv_cb->sock = INVALID_SOCK; + rcv_cb->buf = NULL; + rcv_cb->max_len = 0; + rcv_cb->cb = NULL; + rcv_cb->cb_data = NULL; + + num_rcv_cbs--; + + /* Now, signal the signal_sock to restart the poll server, + * so it can recreate the ufds[] list and start poll() + * *without* the newly un-registered socket. + */ + status = async_server_restart(); + } +} + +/* Fill the poll() ufds argument from the rcv_callback list: */ +static void rcv_callbacks_to_ufds(struct pollfd *fds) +{ + struct rcv_callbacks *entry, *end; + int fds_index = 0; + + end = rcv_callback + ARRAY_SIZE(rcv_callback); + for (entry = &rcv_callback[0], fds_index = 0; entry < end; entry++) { + if (entry->sock != INVALID_SOCK) { + fds[fds_index].fd = entry->sock; + fds[fds_index].events = POLLIN; + fds_index++; + } + } +} + +/* + * Call poll() in a loop, waiting for registered async sockets + * to get signalled; if so, call their registered receive callbacks. + * + * Currently, this only handles receive. Send, accept and other + * events are not handled. + * + * Problem: how to break out of a blocking poll() to reset + * the list of events? (Without using a finite timeout - + * which is not good from a power perspective). + * In Linux, we could use eventfd() to add an fd to the poll() + * list of fds, to enable breaking out of the poll() when + * a new fd is added/removed. + * But, Zephyr's poll() API only supports sockets for now, and Zephyr + * does not yet support something like eventfd(). + * Calling close() on a bespoke socket placed in the list just + * for this purpose *might* unblock a + * poll()/select() operation with a POLLHUP event, + * but that is not implemented, and not clear in the POSIX standard. + * + * Solution: Reserve a UDP socket to 'signal' by sending a + * small message to the LOOPBACK address, unblocking the poll. + * The loopback is processed in Zephyr IP stack before calling + * the L2 driver, so this should work generally. + */ +void async_sock_server(void *unused1, void *unused2, void *unused3) +{ + ARG_UNUSED(unused1); + ARG_UNUSED(unused2); + ARG_UNUSED(unused3); + + static struct pollfd ufds[MAX_RCV_CALLBACKS]; + static struct sockaddr_storage from; + int nfds; + struct pollfd *entry, *end; + struct rcv_callbacks *cb_entry; + size_t size; + int rcv_len; + size_t from_len; + + /* signal_sock is never closed, and always remains in first position */ + ufds[0].fd = signal_sock; + ufds[0].events = POLLIN; + + /* End of ufds[] array */ + end = ufds + MAX_RCV_CALLBACKS; + + while (1) { + /* Update array of polling structs: */ + rcv_callbacks_to_ufds(&ufds[1]); + + /* Wait until any socket gets signalled: */ + nfds = poll(ufds, (num_rcv_cbs + 1), K_FOREVER); + NET_ASSERT(nfds != 0); /* Timeout should be impossible */ + + if (nfds > 0) { + /* signal_sock signalled via loopback msg: */ + if (ufds[0].revents & POLLIN) { + NET_ASSERT(ufds[0].fd == signal_sock); + + /* Just get and discard data: */ + rcv_len = recvfrom(ufds[0].fd, server_rcv_buf, + SIGNAL_MSG_SIZE, 0, + (struct sockaddr *)&from, + &from_len); + NET_ASSERT(rcv_len == SIGNAL_MSG_SIZE); + if (rcv_len != SIGNAL_MSG_SIZE) { + NET_ERR("Received invalid message"); + } + nfds--; + } + + /* For each socket signalled, recv and fire callback: */ + for (entry = &ufds[1]; (entry < end) && nfds; entry++) { + if (entry->revents & POLLIN) { + cb_entry = + get_rcv_callback_slot(entry->fd); + NET_ASSERT(cb_entry != NULL); + + /* Retrieve the socket data: */ + size = recv(cb_entry->sock, + cb_entry->buf, + cb_entry->max_len, + 0); + if (size < 0) { + NET_ERR("Socket errno: %d", + -errno); + } else if (size == 0) { + /* Peer shutdown: */ + async_close(cb_entry->sock); + } else if (cb_entry->cb) { + /* Fire the callback: */ + cb_entry->cb(cb_entry->sock, + cb_entry->buf, + size, + cb_entry->cb_data); + } + nfds--; + } + } + } else { + NET_ERR("poll failed with errno: %d", -errno); + } + } +} + +int async_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, + async_connect_cb_t cb, void *cb_data) +{ + int status; + + status = connect(sock, addr, addrlen); + + if (cb) { + cb(sock, status, cb_data); + } + return(status); +} + +ssize_t async_send(int sock, const void *buf, size_t len, + async_send_cb_t cb, void *cb_data, int flags) +{ + ssize_t bytes_sent; + + bytes_sent = send(sock, buf, len, flags); + + if (cb) { + cb(sock, bytes_sent, cb_data); + } + return bytes_sent; +} + +ssize_t async_recv(int sock, void *buf, size_t max_len, + async_recv_cb_t cb, void *cb_data) +{ + int status; + + /* Store buf, max_len, cb, and cb_data args for this sock id */ + status = rcv_callback_register(sock, buf, max_len, cb, cb_data); + + return status; +} + +int async_close(int sock) +{ + /* Deregister any outstanding receive callbacks: */ + rcv_callback_deregister(sock); + + return close(sock); +} + + +int async_sock_init(struct device *device) +{ + int rc = 0, retval = 0; + + ARG_UNUSED(device); + + rcv_callbacks_init(); + + loopback_addr.sin_family = AF_INET; + rc = net_addr_pton(AF_INET, LOOPBACK_ADDR, &loopback_addr.sin_addr); + NET_ASSERT(!rc); + loopback_addr.sin_port = htons(DISCARD_PORT); + + /* Create a special socket to enable unblocking the server's poll(): */ + signal_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (signal_sock == INVALID_SOCK) { + NET_ERR("Unable to create poll server socket: %d", -errno); + retval = -1; + } else { + rc = bind(signal_sock, (struct sockaddr *)&bind_addr, + sizeof(bind_addr)); + if (rc == -1) { + NET_ERR("Cannot bind poll server socket: %d\n", -errno); + retval = -1; + } else { + /* Start the async_socket receive server: */ + (void)k_thread_create(&async_sock_task_data, + async_sock_task_stack, + ASYNC_SOCK_TASK_STACKSIZE, + async_sock_server, + NULL, NULL, NULL, + ASYNC_SOCK_TASK_PRIORITY, + 0, K_NO_WAIT); + } + } + return retval; +} + +SYS_INIT(async_sock_init, APPLICATION, CONFIG_NET_ASYNC_SOCKET_PRIO); From 9be5a0a6d4037586ea31031d5ae9f72654aad98e Mon Sep 17 00:00:00 2001 From: Gil Pitney Date: Thu, 25 Jan 2018 17:01:20 -0800 Subject: [PATCH 14/19] net: mqtt: Port MQTT library to BSD sockets Goals are to: - provide an example of a non-trivial Zephyr IP protocol working over standard BSD sockets. - enhance portability: since the mqtt code is based on a standard API, it can have value beyond the Zephyr project. - port mqtt with minimal change to the existing Zephyr mqtt API. Design Notes: - the async_socket library is introduced to adapt the synchronous BSD socket APIs to the asynchronous mqtt library. - the async_socket library can be expanded in the future and used for porting other Zephyr asynchronous IP protocols. Validated: - samples/net/mqtt_publisher on qemu_x86 (non-secure sockets only). Todo: - Port TLS support - Fix tests/net/lib/mqtt_* test cases and validate. Signed-off-by: Gil Pitney --- include/net/mqtt.h | 39 ++- include/net/mqtt_types.h | 1 + subsys/net/lib/mqtt/CMakeLists.txt | 1 + subsys/net/lib/mqtt/Kconfig | 2 +- subsys/net/lib/mqtt/mqtt.c | 515 ++++++++++------------------- subsys/net/lib/mqtt/net_utils.c | 126 +++++++ subsys/net/lib/mqtt/net_utils.h | 27 ++ 7 files changed, 362 insertions(+), 349 deletions(-) create mode 100644 subsys/net/lib/mqtt/net_utils.c create mode 100644 subsys/net/lib/mqtt/net_utils.h diff --git a/include/net/mqtt.h b/include/net/mqtt.h index 109b34c93bfe1..ce431edc8a495 100644 --- a/include/net/mqtt.h +++ b/include/net/mqtt.h @@ -8,8 +8,10 @@ #define _MQTT_H_ #include +#if defined(CONFIG_MQTT_LIB_TLS) #include #include +#endif #ifdef __cplusplus extern "C" { @@ -63,8 +65,7 @@ enum mqtt_app { * the state of the received and sent messages. */ struct mqtt_ctx { - /** Net app context structure */ - struct net_app_ctx net_app_ctx; + int sock; s32_t net_init_timeout; s32_t net_timeout; @@ -180,7 +181,10 @@ struct mqtt_ctx { void (*malformed)(struct mqtt_ctx *ctx, u16_t pkt_type); /* Internal use only */ - int (*rcv)(struct mqtt_ctx *ctx, struct net_pkt *); + int (*rcv)(struct mqtt_ctx *ctx, void *buf, size_t len); + + /* Receive buffer for async receive callbacks */ + void *rcv_buf; /** Application type, see: enum mqtt_app */ u8_t app_type; @@ -200,7 +204,7 @@ struct mqtt_ctx { * * @param ctx MQTT context structure * @param app_type See enum mqtt_app - * @retval 0 always + * @retval 0 on success, and <0 if error */ int mqtt_init(struct mqtt_ctx *ctx, enum mqtt_app app_type); @@ -366,12 +370,13 @@ int mqtt_tx_unsubscribe(struct mqtt_ctx *ctx, u16_t pkt_id, u8_t items, * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * @param [in] clean_session MQTT clean session parameter * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_connack(struct mqtt_ctx *ctx, struct net_buf *rx, +int mqtt_rx_connack(struct mqtt_ctx *ctx, void *rx, size_t len, int clean_session); /** @@ -379,89 +384,97 @@ int mqtt_rx_connack(struct mqtt_ctx *ctx, struct net_buf *rx, * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_puback(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_puback(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses and validates the MQTT PUBCOMP message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_pubcomp(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_pubcomp(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses and validates the MQTT PUBREC message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_pubrec(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_pubrec(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses and validates the MQTT PUBREL message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_pubrel(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_pubrel(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses the MQTT PINGRESP message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_pingresp(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_pingresp(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses the MQTT SUBACK message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_suback(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_suback(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses the MQTT UNSUBACK message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL */ -int mqtt_rx_unsuback(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_unsuback(struct mqtt_ctx *ctx, void *rx, size_t len); /** * Parses the MQTT PUBLISH message * * @param [in] ctx MQTT context structure * @param [in] rx Data buffer + * @param [in] len Length of data * * @retval 0 on success * @retval -EINVAL * @retval -ENOMEM */ -int mqtt_rx_publish(struct mqtt_ctx *ctx, struct net_buf *rx); +int mqtt_rx_publish(struct mqtt_ctx *ctx, void *rx, size_t len); /** * @} diff --git a/include/net/mqtt_types.h b/include/net/mqtt_types.h index 4e65642f8d628..5ed0e31adb668 100644 --- a/include/net/mqtt_types.h +++ b/include/net/mqtt_types.h @@ -7,6 +7,7 @@ #ifndef _MQTT_TYPES_H_ #define _MQTT_TYPES_H_ +#include #include #ifdef __cplusplus diff --git a/subsys/net/lib/mqtt/CMakeLists.txt b/subsys/net/lib/mqtt/CMakeLists.txt index 1a946223e06fd..b728270398934 100644 --- a/subsys/net/lib/mqtt/CMakeLists.txt +++ b/subsys/net/lib/mqtt/CMakeLists.txt @@ -1,6 +1,7 @@ zephyr_library() zephyr_library_sources( + net_utils.c mqtt_pkt.c mqtt.c ) diff --git a/subsys/net/lib/mqtt/Kconfig b/subsys/net/lib/mqtt/Kconfig index a3cfdada2421d..952f3dffa4b9b 100644 --- a/subsys/net/lib/mqtt/Kconfig +++ b/subsys/net/lib/mqtt/Kconfig @@ -9,7 +9,7 @@ config MQTT_LIB bool "MQTT Library Support" default n - select NET_APP_CLIENT + select NET_ASYNC_SOCKET help Enable the Zephyr MQTT Library diff --git a/subsys/net/lib/mqtt/mqtt.c b/subsys/net/lib/mqtt/mqtt.c index 3fe328d851ff2..b9cc62ef5e6ba 100644 --- a/subsys/net/lib/mqtt/mqtt.c +++ b/subsys/net/lib/mqtt/mqtt.c @@ -4,23 +4,59 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include +#include "net_utils.h" #include "mqtt_pkt.h" #include -#include -#include +#include +/* + * Issue #5817 workaround: + * Ensuring mqtt.h after socket.h so .connect -> .zsock_connect + * everywhere: + */ +#include #include +#include +#include #include +#define MEM_ALIGN (sizeof(uint32_t)) #define MSG_SIZE CONFIG_MQTT_MSG_MAX_SIZE -#define MQTT_BUF_CTR (1 + CONFIG_MQTT_ADDITIONAL_BUFFER_CTR) +#define MQTT_BUF_CTR (2 + CONFIG_MQTT_ADDITIONAL_BUFFER_CTR) + +#define INVALID_SOCK (-1) -/* Memory pool internally used to handle messages that may exceed the size of - * system defined network buffer. By using this memory pool, routines don't deal - * with fragmentation, so algorithms are more easy to implement. +/* Reset rc to 0 if rc > 0 (meaning, rc bytes sent), indicating success: */ +#define SET_ERRNO_AND_RC(rc) { rc = (rc < 0 ? -errno : 0); } + +/* + * Memory pool for MQTT message buffers. + * The number of buffers should equal the number of mqtt contexts + * created by the application + one. + * - Each mqtt context needs a buffer for sending. + * - One buffer is needed for receiving across all contexts. */ -NET_BUF_POOL_DEFINE(mqtt_msg_pool, MQTT_BUF_CTR, MSG_SIZE, 0, NULL); +K_MEM_SLAB_DEFINE(mqtt_msg_pool, MSG_SIZE, MQTT_BUF_CTR, MEM_ALIGN); + +static void *_mqtt_msg_alloc() +{ + void *buf = NULL; + + if (k_mem_slab_alloc(&mqtt_msg_pool, (void **)&buf, K_NO_WAIT) < 0) { + /* + * We assert, as this is a logic error, due to the application + * overallocating from the mqtt msg pool. + */ + __ASSERT(0, "Increase size of mqtt msg pool"); + } + return buf; +} + +static void _mqtt_msg_free(void *buf) +{ + k_mem_slab_free(&mqtt_msg_pool, (void **)&buf); +} + #define MQTT_PUBLISHER_MIN_MSG_SIZE 2 @@ -30,52 +66,32 @@ NET_BUF_POOL_DEFINE(mqtt_msg_pool, MQTT_BUF_CTR, MSG_SIZE, 0, NULL); int mqtt_tx_connect(struct mqtt_ctx *ctx, struct mqtt_connect_msg *msg) { - struct net_buf *data = NULL; - struct net_pkt *tx = NULL; + void *data = NULL; + u16_t len; int rc; - data = net_buf_alloc(&mqtt_msg_pool, ctx->net_timeout); + data = _mqtt_msg_alloc(); if (data == NULL) { return -ENOMEM; } ctx->clean_session = msg->clean_session ? 1 : 0; - rc = mqtt_pack_connect(data->data, &data->len, MSG_SIZE, msg); + rc = mqtt_pack_connect(data, &len, MSG_SIZE, msg); if (rc != 0) { rc = -EINVAL; - goto exit_connect; - } - - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - rc = -ENOMEM; - goto exit_connect; - } - - net_pkt_frag_add(tx, data); - data = NULL; - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); - if (rc < 0) { - net_pkt_unref(tx); + } else { + rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + SET_ERRNO_AND_RC(rc); } - tx = NULL; - - return rc; - -exit_connect: - net_pkt_frag_unref(data); + _mqtt_msg_free(data); return rc; } int mqtt_tx_disconnect(struct mqtt_ctx *ctx) { - struct net_pkt *tx = NULL; /* DISCONNECT is a zero length message: 2 bytes required, no payload */ u8_t msg[2]; u16_t len; @@ -86,36 +102,18 @@ int mqtt_tx_disconnect(struct mqtt_ctx *ctx) return -EINVAL; } - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - return -ENOMEM; - } - - rc = net_pkt_append_all(tx, len, msg, ctx->net_timeout); - if (rc != true) { - rc = -ENOMEM; - goto exit_disconnect; - } - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); + rc = async_send(ctx->sock, msg, len, NULL, NULL, 0); if (rc < 0) { - goto exit_disconnect; - } - - ctx->connected = 0; - tx = NULL; - - if (ctx->disconnect) { - ctx->disconnect(ctx); + rc = -errno; + } else { + rc = 0; /* Because caller expects 0 for success */ + ctx->connected = 0; + /* Call MQTT client's disconnect callback: */ + if (ctx->disconnect) { + ctx->disconnect(ctx); + } } - return rc; - -exit_disconnect: - net_pkt_unref(tx); - return rc; } @@ -135,7 +133,6 @@ static int mqtt_tx_pub_msgs(struct mqtt_ctx *ctx, u16_t id, enum mqtt_packet pkt_type) { - struct net_pkt *tx = NULL; u8_t msg[4]; u16_t len; int rc; @@ -158,34 +155,12 @@ int mqtt_tx_pub_msgs(struct mqtt_ctx *ctx, u16_t id, } if (rc != 0) { - return -EINVAL; - } - - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - return -ENOMEM; - } - - rc = net_pkt_append_all(tx, len, msg, ctx->net_timeout); - if (rc != true) { - rc = -ENOMEM; - goto exit_send; - } - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); - if (rc < 0) { - goto exit_send; + rc = -EINVAL; + } else { + rc = async_send(ctx->sock, msg, len, NULL, NULL, 0); + SET_ERRNO_AND_RC(rc); } - tx = NULL; - - return rc; - -exit_send: - net_pkt_unref(tx); - return rc; } @@ -211,50 +186,30 @@ int mqtt_tx_pubrel(struct mqtt_ctx *ctx, u16_t id) int mqtt_tx_publish(struct mqtt_ctx *ctx, struct mqtt_publish_msg *msg) { - struct net_buf *data = NULL; - struct net_pkt *tx = NULL; + void *data = NULL; + u16_t len; int rc; - data = net_buf_alloc(&mqtt_msg_pool, ctx->net_timeout); + data = _mqtt_msg_alloc(); if (data == NULL) { return -ENOMEM; } - rc = mqtt_pack_publish(data->data, &data->len, data->size, msg); + rc = mqtt_pack_publish(data, &len, MSG_SIZE, msg); if (rc != 0) { rc = -EINVAL; - goto exit_publish; - } - - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - rc = -ENOMEM; - goto exit_publish; - } - - net_pkt_frag_add(tx, data); - data = NULL; - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); - if (rc < 0) { - net_pkt_unref(tx); + } else { + rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + SET_ERRNO_AND_RC(rc); } - tx = NULL; - - return rc; - -exit_publish: - net_pkt_frag_unref(data); + _mqtt_msg_free(data); return rc; } int mqtt_tx_pingreq(struct mqtt_ctx *ctx) { - struct net_pkt *tx = NULL; u8_t msg[2]; u16_t len; int rc; @@ -264,30 +219,8 @@ int mqtt_tx_pingreq(struct mqtt_ctx *ctx) return -EINVAL; } - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - return -ENOMEM; - } - - rc = net_pkt_append_all(tx, len, msg, ctx->net_timeout); - if (rc != true) { - rc = -ENOMEM; - goto exit_pingreq; - } - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); - if (rc < 0) { - goto exit_pingreq; - } - - tx = NULL; - - return rc; - -exit_pingreq: - net_pkt_unref(tx); + rc = async_send(ctx->sock, msg, len, NULL, NULL, 0); + SET_ERRNO_AND_RC(rc); return rc; } @@ -295,44 +228,25 @@ int mqtt_tx_pingreq(struct mqtt_ctx *ctx) int mqtt_tx_subscribe(struct mqtt_ctx *ctx, u16_t pkt_id, u8_t items, const char *topics[], const enum mqtt_qos qos[]) { - struct net_buf *data = NULL; - struct net_pkt *tx = NULL; + void *data = NULL; + u16_t len; int rc; - data = net_buf_alloc(&mqtt_msg_pool, ctx->net_timeout); + data = _mqtt_msg_alloc(); if (data == NULL) { return -ENOMEM; } - rc = mqtt_pack_subscribe(data->data, &data->len, data->size, + rc = mqtt_pack_subscribe(data, &len, MSG_SIZE, pkt_id, items, topics, qos); if (rc != 0) { rc = -EINVAL; - goto exit_subs; - } - - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - rc = -ENOMEM; - goto exit_subs; - } - - net_pkt_frag_add(tx, data); - data = NULL; - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); - if (rc < 0) { - net_pkt_unref(tx); + } else { + rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + SET_ERRNO_AND_RC(rc); } - tx = NULL; - - return rc; - -exit_subs: - net_pkt_frag_unref(data); + _mqtt_msg_free(data); return rc; } @@ -340,58 +254,38 @@ int mqtt_tx_subscribe(struct mqtt_ctx *ctx, u16_t pkt_id, u8_t items, int mqtt_tx_unsubscribe(struct mqtt_ctx *ctx, u16_t pkt_id, u8_t items, const char *topics[]) { - struct net_buf *data = NULL; - struct net_pkt *tx = NULL; + void *data = NULL; + u16_t len; int rc; - data = net_buf_alloc(&mqtt_msg_pool, ctx->net_timeout); + data = _mqtt_msg_alloc(); if (data == NULL) { return -ENOMEM; } - rc = mqtt_pack_unsubscribe(data->data, &data->len, data->size, pkt_id, + rc = mqtt_pack_unsubscribe(data, &len, MSG_SIZE, pkt_id, items, topics); if (rc != 0) { rc = -EINVAL; - goto exit_unsub; - } - - tx = net_app_get_net_pkt(&ctx->net_app_ctx, - AF_UNSPEC, ctx->net_timeout); - if (tx == NULL) { - rc = -ENOMEM; - goto exit_unsub; - } - - net_pkt_frag_add(tx, data); - data = NULL; - - rc = net_app_send_pkt(&ctx->net_app_ctx, - tx, NULL, 0, ctx->net_timeout, NULL); - if (rc < 0) { - net_pkt_unref(tx); + } else { + rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + SET_ERRNO_AND_RC(rc); } - tx = NULL; - - return rc; - -exit_unsub: - net_pkt_frag_unref(data); + _mqtt_msg_free(data); return rc; } -int mqtt_rx_connack(struct mqtt_ctx *ctx, struct net_buf *rx, int clean_session) +int mqtt_rx_connack(struct mqtt_ctx *ctx, void *rx, size_t len, + int clean_session) { - u16_t len; u8_t connect_rc; u8_t session; u8_t *data; int rc; - data = rx->data; - len = rx->len; + data = (u8_t *)rx; /* CONNACK is 4 bytes len */ rc = mqtt_unpack_connack(data, len, &session, &connect_rc); @@ -448,14 +342,12 @@ int mqtt_rx_connack(struct mqtt_ctx *ctx, struct net_buf *rx, int clean_session) * @retval -EINVAL on error */ static -int mqtt_rx_pub_msgs(struct mqtt_ctx *ctx, struct net_buf *rx, +int mqtt_rx_pub_msgs(struct mqtt_ctx *ctx, void *data, size_t len, enum mqtt_packet type) { int (*unpack)(u8_t *, u16_t, u16_t *) = NULL; int (*response)(struct mqtt_ctx *, u16_t) = NULL; u16_t pkt_id; - u16_t len; - u8_t *data; int rc; switch (type) { @@ -477,9 +369,6 @@ int mqtt_rx_pub_msgs(struct mqtt_ctx *ctx, struct net_buf *rx, return -EINVAL; } - data = rx->data; - len = rx->len; - /* 4 bytes message */ rc = unpack(data, len, &pkt_id); if (rc != 0) { @@ -515,34 +404,34 @@ int mqtt_rx_pub_msgs(struct mqtt_ctx *ctx, struct net_buf *rx, return 0; } -int mqtt_rx_puback(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_puback(struct mqtt_ctx *ctx, void *data, size_t len) { - return mqtt_rx_pub_msgs(ctx, rx, MQTT_PUBACK); + return mqtt_rx_pub_msgs(ctx, data, len, MQTT_PUBACK); } -int mqtt_rx_pubcomp(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_pubcomp(struct mqtt_ctx *ctx, void *data, size_t len) { - return mqtt_rx_pub_msgs(ctx, rx, MQTT_PUBCOMP); + return mqtt_rx_pub_msgs(ctx, data, len, MQTT_PUBCOMP); } -int mqtt_rx_pubrec(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_pubrec(struct mqtt_ctx *ctx, void *data, size_t len) { - return mqtt_rx_pub_msgs(ctx, rx, MQTT_PUBREC); + return mqtt_rx_pub_msgs(ctx, data, len, MQTT_PUBREC); } -int mqtt_rx_pubrel(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_pubrel(struct mqtt_ctx *ctx, void *data, size_t len) { - return mqtt_rx_pub_msgs(ctx, rx, MQTT_PUBREL); + return mqtt_rx_pub_msgs(ctx, data, len, MQTT_PUBREL); } -int mqtt_rx_pingresp(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_pingresp(struct mqtt_ctx *ctx, void *data, size_t len) { int rc; ARG_UNUSED(ctx); /* 2 bytes message */ - rc = mqtt_unpack_pingresp(rx->data, rx->len); + rc = mqtt_unpack_pingresp(data, len); if (rc != 0) { return -EINVAL; @@ -551,18 +440,13 @@ int mqtt_rx_pingresp(struct mqtt_ctx *ctx, struct net_buf *rx) return 0; } -int mqtt_rx_suback(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_suback(struct mqtt_ctx *ctx, void *data, size_t len) { enum mqtt_qos suback_qos[CONFIG_MQTT_SUBSCRIBE_MAX_TOPICS]; u16_t pkt_id; - u16_t len; u8_t items; - u8_t *data; int rc; - data = rx->data; - len = rx->len; - rc = mqtt_unpack_suback(data, len, &pkt_id, &items, CONFIG_MQTT_SUBSCRIBE_MAX_TOPICS, suback_qos); if (rc != 0) { @@ -581,16 +465,11 @@ int mqtt_rx_suback(struct mqtt_ctx *ctx, struct net_buf *rx) return 0; } -int mqtt_rx_unsuback(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_unsuback(struct mqtt_ctx *ctx, void *data, size_t len) { u16_t pkt_id; - u16_t len; - u8_t *data; int rc; - data = rx->data; - len = rx->len; - /* 4 bytes message */ rc = mqtt_unpack_unsuback(data, len, &pkt_id); if (rc != 0) { @@ -609,12 +488,12 @@ int mqtt_rx_unsuback(struct mqtt_ctx *ctx, struct net_buf *rx) return 0; } -int mqtt_rx_publish(struct mqtt_ctx *ctx, struct net_buf *rx) +int mqtt_rx_publish(struct mqtt_ctx *ctx, void *data, size_t len) { struct mqtt_publish_msg msg; int rc; - rc = mqtt_unpack_publish(rx->data, rx->len, &msg); + rc = mqtt_unpack_publish(data, len, &msg); if (rc != 0) { return -EINVAL; } @@ -640,54 +519,6 @@ int mqtt_rx_publish(struct mqtt_ctx *ctx, struct net_buf *rx) return rc; } -/** - * Linearizes an IP fragmented packet - * - * @param [in] ctx MQTT context structure - * @param [in] rx RX IP stack packet - * @param [in] min_size Min message size allowed. This allows us to exit if the - * rx packet is shorter than the expected msg size - * - * @retval Data buffer - * @retval NULL on error - */ -static -struct net_buf *mqtt_linearize_packet(struct mqtt_ctx *ctx, struct net_pkt *rx, - u16_t min_size) -{ - struct net_buf *data = NULL; - u16_t data_len; - u16_t offset; - int rc; - - /* CONFIG_MQTT_MSG_MAX_SIZE is defined via Kconfig. So here it's - * determined if the input packet could fit our data buffer or if - * it has the expected size. - */ - data_len = net_pkt_appdatalen(rx); - if (data_len < min_size || data_len > CONFIG_MQTT_MSG_MAX_SIZE) { - return NULL; - } - - data = net_buf_alloc(&mqtt_msg_pool, ctx->net_timeout); - if (data == NULL) { - return NULL; - } - - offset = net_pkt_get_len(rx) - data_len; - rc = net_frag_linear_copy(data, rx->frags, offset, data_len); - if (rc != 0) { - goto exit_error; - } - - return data; - -exit_error: - net_pkt_frag_unref(data); - - return NULL; -} - /** * Calls the appropriate rx routine for the MQTT message contained in rx * @@ -695,7 +526,8 @@ struct net_buf *mqtt_linearize_packet(struct mqtt_ctx *ctx, struct net_pkt *rx, * (if defined) * * @param ctx MQTT context - * @param rx RX packet + * @param data Received data buffer + * @param data Length of data buffer * * @retval 0 on success * @retval -EINVAL if an unknown message is received @@ -705,47 +537,52 @@ struct net_buf *mqtt_linearize_packet(struct mqtt_ctx *ctx, struct net_pkt *rx, * return codes */ static -int mqtt_parser(struct mqtt_ctx *ctx, struct net_pkt *rx) +int mqtt_parser(struct mqtt_ctx *ctx, void *data, size_t len) { u16_t pkt_type = MQTT_INVALID; - struct net_buf *data = NULL; int rc = -EINVAL; - data = mqtt_linearize_packet(ctx, rx, MQTT_PUBLISHER_MIN_MSG_SIZE); - if (!data) { - return -ENOMEM; + /* + * CONFIG_MQTT_MSG_MAX_SIZE is defined via Kconfig. So here it's + * determined if the received input could fit our data buffer or if + * it has the expected size. + */ + if (len < MQTT_PUBLISHER_MIN_MSG_SIZE || + len > CONFIG_MQTT_MSG_MAX_SIZE) { + return -EMSGSIZE; } - pkt_type = MQTT_PACKET_TYPE(data->data[0]); + pkt_type = MQTT_PACKET_TYPE(((u8_t *)data)[0]); switch (pkt_type) { case MQTT_CONNACK: if (!ctx->connected) { - rc = mqtt_rx_connack(ctx, data, ctx->clean_session); + rc = mqtt_rx_connack(ctx, data, len, + ctx->clean_session); } else { rc = -EINVAL; } break; case MQTT_PUBACK: - rc = mqtt_rx_puback(ctx, data); + rc = mqtt_rx_puback(ctx, data, len); break; case MQTT_PUBREC: - rc = mqtt_rx_pubrec(ctx, data); + rc = mqtt_rx_pubrec(ctx, data, len); break; case MQTT_PUBCOMP: - rc = mqtt_rx_pubcomp(ctx, data); + rc = mqtt_rx_pubcomp(ctx, data, len); break; case MQTT_PINGRESP: - rc = mqtt_rx_pingresp(ctx, data); + rc = mqtt_rx_pingresp(ctx, data, len); break; case MQTT_PUBLISH: - rc = mqtt_rx_publish(ctx, data); + rc = mqtt_rx_publish(ctx, data, len); break; case MQTT_PUBREL: - rc = mqtt_rx_pubrel(ctx, data); + rc = mqtt_rx_pubrel(ctx, data, len); break; case MQTT_SUBACK: - rc = mqtt_rx_suback(ctx, data); + rc = mqtt_rx_suback(ctx, data, len); break; default: rc = -EINVAL; @@ -756,20 +593,18 @@ int mqtt_parser(struct mqtt_ctx *ctx, struct net_pkt *rx) ctx->malformed(ctx, pkt_type); } - net_pkt_frag_unref(data); - return rc; } static -void app_connected(struct net_app_ctx *ctx, int status, void *data) +void async_connected_cb(int sock, int status, void *cb_data) { - struct mqtt_ctx *mqtt = (struct mqtt_ctx *)data; + struct mqtt_ctx *mqtt = (struct mqtt_ctx *)cb_data; - /* net_app_ctx is already referenced to by the mqtt_ctx struct */ - ARG_UNUSED(ctx); + ARG_UNUSED(sock); + ARG_UNUSED(status); - if (!mqtt) { + if (!mqtt || !status) { return; } @@ -779,52 +614,55 @@ void app_connected(struct net_app_ctx *ctx, int status, void *data) } static -void app_recv(struct net_app_ctx *ctx, struct net_pkt *pkt, int status, - void *data) +void async_recv_cb(int sock, void *data, size_t bytes_received, void *cb_data) { - struct mqtt_ctx *mqtt = (struct mqtt_ctx *)data; + struct mqtt_ctx *mqtt = (struct mqtt_ctx *)cb_data; - /* net_app_ctx is already referenced to by the mqtt_ctx struct */ - ARG_UNUSED(ctx); + ARG_UNUSED(sock); - if (status || !pkt) { + if (!data || bytes_received <= 0) { return; } - if (net_pkt_appdatalen(pkt) == 0) { - goto lb_exit; - } - - mqtt->rcv(mqtt, pkt); - -lb_exit: - net_pkt_unref(pkt); + mqtt->rcv(mqtt, data, bytes_received); } int mqtt_connect(struct mqtt_ctx *ctx) { int rc = 0; + struct sockaddr addr; + struct sockaddr peer_addr; if (!ctx) { return -EFAULT; } - rc = net_app_init_tcp_client(&ctx->net_app_ctx, - NULL, - NULL, - ctx->peer_addr_str, - ctx->peer_port, - ctx->net_init_timeout, - ctx); - if (rc < 0) { + /* Duplicate some functionality of net_app_init_tcp_client: + * - Parse peer_addr_str, peer_port + * - Create the socket for this context. + * - TBD: Setup TLS thread + */ + memset(&peer_addr, 0, sizeof(peer_addr)); + memset(&addr, 0, sizeof(addr)); + rc = net_util_init_tcp_client(&addr, &peer_addr, + ctx->peer_addr_str, ctx->peer_port); + if (!rc) { goto error_connect; } - rc = net_app_set_cb(&ctx->net_app_ctx, - app_connected, - app_recv, - NULL, - NULL); + /* Create the socket: */ + ctx->sock = async_socket(addr.sa_family, SOCK_STREAM, IPPROTO_TCP); + if (ctx->sock == INVALID_SOCK) { + rc = -errno; + goto error_connect; + } + + /* + * Setup receive callback, which will call mqtt_parser() on a received + * socket buffer: + */ + rc = async_recv(ctx->sock, ctx->rcv_buf, MSG_SIZE, + async_recv_cb, (void *)ctx); if (rc < 0) { goto error_connect; } @@ -846,7 +684,8 @@ int mqtt_connect(struct mqtt_ctx *ctx) } #endif - rc = net_app_connect(&ctx->net_app_ctx, ctx->net_timeout); + rc = async_connect(ctx->sock, &peer_addr, sizeof(peer_addr), + async_connected_cb, (void *)ctx); if (rc < 0) { goto error_connect; } @@ -862,9 +701,7 @@ int mqtt_connect(struct mqtt_ctx *ctx) return rc; error_connect: - /* clean net app context, so mqtt_connect() can be called repeatedly */ - net_app_close(&ctx->net_app_ctx); - net_app_release(&ctx->net_app_ctx); + mqtt_close(ctx); return rc; } @@ -878,6 +715,8 @@ int mqtt_init(struct mqtt_ctx *ctx, enum mqtt_app app_type) ctx->app_type = app_type; ctx->rcv = mqtt_parser; + ctx->sock = INVALID_SOCK; + #if defined(CONFIG_MQTT_LIB_TLS) if (ctx->tls_hs_timeout == 0) { ctx->tls_hs_timeout = TLS_HS_DEFAULT_TIMEOUT; @@ -886,7 +725,9 @@ int mqtt_init(struct mqtt_ctx *ctx, enum mqtt_app app_type) k_sem_init(&ctx->tls_hs_wait, 0, 1); #endif - return 0; + ctx->rcv_buf = _mqtt_msg_alloc(); + + return (ctx->rcv_buf != NULL ? 0 : -ENOMEM); } int mqtt_close(struct mqtt_ctx *ctx) @@ -895,9 +736,13 @@ int mqtt_close(struct mqtt_ctx *ctx) return -EFAULT; } - if (ctx->net_app_ctx.is_init) { - net_app_close(&ctx->net_app_ctx); - net_app_release(&ctx->net_app_ctx); + if (ctx->sock != INVALID_SOCK) { + async_close(ctx->sock); + ctx->sock = INVALID_SOCK; + } + + if (ctx->rcv_buf) { + _mqtt_msg_free(ctx->rcv_buf); } return 0; diff --git a/subsys/net/lib/mqtt/net_utils.c b/subsys/net/lib/mqtt/net_utils.c new file mode 100644 index 0000000000000..fd51d25dbc802 --- /dev/null +++ b/subsys/net/lib/mqtt/net_utils.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017 Texas Instruments, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The following code was lifted from Zephyr's net_app library, to + * support the mqtt port to sockets, and to decouple the mqtt lib from + * net_app's struct net_app_ctx, which may evolve with the new + * net_pkt/net_buf redesign. + * This ideally could move to a generic net/utils module. + */ + +#include +#include + +#include "net_utils.h" + +static int get_port_number(const char *peer_addr_str, + char *buf, + size_t buf_len) +{ + u16_t port = 0; + char *ptr; + int count, i; + + if (peer_addr_str[0] == '[') { +#if defined(CONFIG_NET_IPV6) + /* IPv6 address with port number */ + int end; + + ptr = strstr(peer_addr_str, "]:"); + if (!ptr) { + return -EINVAL; + } + + end = min(INET6_ADDRSTRLEN, ptr - (peer_addr_str + 1)); + memcpy(buf, peer_addr_str + 1, end); + buf[end] = '\0'; + + port = strtol(ptr + 2, NULL, 10); + + return port; +#else + return -EAFNOSUPPORT; +#endif /* CONFIG_NET_IPV6 */ + } + + count = i = 0; + while (peer_addr_str[i]) { + if (peer_addr_str[i] == ':') { + count++; + } + + i++; + } + + if (count == 1) { +#if defined(CONFIG_NET_IPV4) + /* IPv4 address with port number */ + int end; + + ptr = strstr(peer_addr_str, ":"); + if (!ptr) { + return -EINVAL; + } + + end = min(NET_IPV4_ADDR_LEN, ptr - peer_addr_str); + memcpy(buf, peer_addr_str, end); + buf[end] = '\0'; + + port = strtol(ptr + 1, NULL, 10); + + return port; +#else + return -EAFNOSUPPORT; +#endif /* CONFIG_NET_IPV4 */ + } + + return 0; +} + +bool net_util_init_tcp_client(struct sockaddr *addr, + struct sockaddr *peer_addr, + const char *peer_addr_str, + u16_t peer_port) +{ + const char *base_peer_addr = peer_addr_str; + char base_addr_str[INET6_ADDRSTRLEN + 1]; + int addr_ok = false; + u16_t port = 0; + + /* If the peer string contains port number, use that and + * ignore the port number parameter. + */ + port = get_port_number(peer_addr_str, base_addr_str, + sizeof(base_addr_str)); + if (port > 0) { + base_peer_addr = base_addr_str; + peer_port = port; + } else { + strncpy(base_addr_str, peer_addr_str, + sizeof(base_addr_str) - 1); + } + + addr_ok = net_ipaddr_parse(base_peer_addr, + strlen(base_peer_addr), + peer_addr); + + if (addr_ok) { +#if defined(CONFIG_NET_IPV6) + if (peer_addr->sa_family == AF_INET6) { + net_sin6(peer_addr)->sin6_port = htons(peer_port); + } +#endif +#if defined(CONFIG_NET_IPV4) + if (peer_addr->sa_family == AF_INET) { + net_sin(peer_addr)->sin_port = htons(peer_port); + } +#endif + addr->sa_family = peer_addr->sa_family; + } + + return (addr_ok); +} diff --git a/subsys/net/lib/mqtt/net_utils.h b/subsys/net/lib/mqtt/net_utils.h new file mode 100644 index 0000000000000..1cdd10c1b8496 --- /dev/null +++ b/subsys/net/lib/mqtt/net_utils.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Texas Instruments, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NET_UTILS_H_ +#define _NET_UTILS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool net_util_init_tcp_client(struct sockaddr *addr, + struct sockaddr *peer_addr, + const char *peer_addr_str, + u16_t peer_port); + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_UTILS_H_ */ From decc7aadd9cb31acf17ac8a6c76b41232208bcd3 Mon Sep 17 00:00:00 2001 From: Gil Pitney Date: Thu, 25 Jan 2018 17:34:32 -0800 Subject: [PATCH 15/19] net: samples: mqtt_publisher: remove dependence on net_context.h After the port of mqtt to BSD sockets, there is no need for an mqtt application to include net_context.h. Signed-off-by: Gil Pitney --- samples/net/mqtt_publisher/src/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/net/mqtt_publisher/src/main.c b/samples/net/mqtt_publisher/src/main.c index a69fd367eb542..41bbaf81ce354 100644 --- a/samples/net/mqtt_publisher/src/main.c +++ b/samples/net/mqtt_publisher/src/main.c @@ -7,8 +7,6 @@ #include #include -#include - #include #include #include From 6c1a864a733e54a2f25870ce92046701b25778f0 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 21:13:35 +0200 Subject: [PATCH 16/19] async_socket: Convert to zstream API. Signed-off-by: Paul Sokolovsky --- include/net/async_socket.h | 9 +++--- subsys/net/lib/async_socket/async_socket.c | 37 ++++++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/include/net/async_socket.h b/include/net/async_socket.h index 98fc6add8b6e3..99b4f8ac29330 100644 --- a/include/net/async_socket.h +++ b/include/net/async_socket.h @@ -19,6 +19,7 @@ #define __NET_ASYNC_SOCKET_H #include +#include #ifdef __cplusplus extern "C" { @@ -44,23 +45,23 @@ typedef void (*async_recv_cb_t)(int sock, */ /* For now, same semantics as socket() call: */ -inline int async_socket(int family, int type, int proto) +static inline int async_socket(int family, int type, int proto) { return socket(family, type, proto); } -int async_close(int sock); +int async_close(int sock, struct zstream *stream); int async_bind(int sock, const struct sockaddr *addr, socklen_t addrlen); int async_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, async_connect_cb_t cb, void *cb_data); -ssize_t async_send(int sock, const void *buf, size_t len, +ssize_t async_send(struct zstream *sock, const void *buf, size_t len, async_send_cb_t cb, void *cb_data, int flags); /* buf must be unique per sock */ -ssize_t async_recv(int sock, void *buf, size_t max_len, +ssize_t async_recv(int sock, struct zstream *stream, void *buf, size_t max_len, async_recv_cb_t cb, void *cb_data); #ifdef __cplusplus diff --git a/subsys/net/lib/async_socket/async_socket.c b/subsys/net/lib/async_socket/async_socket.c index 694f571d24e4c..20f9e3f23781f 100644 --- a/subsys/net/lib/async_socket/async_socket.c +++ b/subsys/net/lib/async_socket/async_socket.c @@ -31,9 +31,12 @@ /*Socket to callback map, for use by async_recv() and async_sock_server() */ struct rcv_callbacks { - /* Our socket id */ + /* Underlying socket id for poll() operation */ int sock; + /* Stream for I/O operations */ + struct zstream *stream; + /* Buffer for receive and its maximum length */ void *buf; size_t max_len; @@ -106,7 +109,7 @@ static void rcv_callbacks_init(void) } } -static int rcv_callback_register(int sock, +static int rcv_callback_register(int sock, struct zstream *stream, void *buf, size_t max_len, async_recv_cb_t cb, void *cb_data) { @@ -128,6 +131,7 @@ static int rcv_callback_register(int sock, if (rcv_cb) { rcv_cb->sock = sock; + rcv_cb->stream = stream; rcv_cb->buf = buf; rcv_cb->max_len = max_len; rcv_cb->cb = cb; @@ -267,19 +271,18 @@ void async_sock_server(void *unused1, void *unused2, void *unused3) NET_ASSERT(cb_entry != NULL); /* Retrieve the socket data: */ - size = recv(cb_entry->sock, + size = zstream_read(cb_entry->stream, cb_entry->buf, - cb_entry->max_len, - 0); + cb_entry->max_len); if (size < 0) { NET_ERR("Socket errno: %d", -errno); } else if (size == 0) { /* Peer shutdown: */ - async_close(cb_entry->sock); + async_close(cb_entry->sock, cb_entry->stream); } else if (cb_entry->cb) { /* Fire the callback: */ - cb_entry->cb(cb_entry->sock, + cb_entry->cb(-1, /* should be ignored */ cb_entry->buf, size, cb_entry->cb_data); @@ -306,36 +309,42 @@ int async_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, return(status); } -ssize_t async_send(int sock, const void *buf, size_t len, +ssize_t async_send(struct zstream *sock, const void *buf, size_t len, async_send_cb_t cb, void *cb_data, int flags) { ssize_t bytes_sent; - bytes_sent = send(sock, buf, len, flags); + bytes_sent = zstream_writeall(sock, buf, len, NULL); + if (bytes_sent > 0) { + int res = zstream_flush(sock); + if (res < 0) { + bytes_sent = res; + } + } if (cb) { - cb(sock, bytes_sent, cb_data); + cb(-1, bytes_sent, cb_data); } return bytes_sent; } -ssize_t async_recv(int sock, void *buf, size_t max_len, +ssize_t async_recv(int sock, struct zstream *stream, void *buf, size_t max_len, async_recv_cb_t cb, void *cb_data) { int status; /* Store buf, max_len, cb, and cb_data args for this sock id */ - status = rcv_callback_register(sock, buf, max_len, cb, cb_data); + status = rcv_callback_register(sock, stream, buf, max_len, cb, cb_data); return status; } -int async_close(int sock) +int async_close(int sock, struct zstream *stream) { /* Deregister any outstanding receive callbacks: */ rcv_callback_deregister(sock); - return close(sock); + return zstream_close(stream); } From d38f803341146c5a1c80bbe05ec3bf242cc4b85f Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 1 Mar 2018 21:15:58 +0200 Subject: [PATCH 17/19] net: mqtt: Convert to zstream_sock. Signed-off-by: Paul Sokolovsky --- include/net/mqtt.h | 3 +++ subsys/net/lib/mqtt/mqtt.c | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/net/mqtt.h b/include/net/mqtt.h index ce431edc8a495..bc927936ecb58 100644 --- a/include/net/mqtt.h +++ b/include/net/mqtt.h @@ -7,6 +7,7 @@ #ifndef _MQTT_H_ #define _MQTT_H_ +#include #include #if defined(CONFIG_MQTT_LIB_TLS) #include @@ -66,6 +67,8 @@ enum mqtt_app { */ struct mqtt_ctx { int sock; + zstream stream; + struct zstream_sock stream_sock; s32_t net_init_timeout; s32_t net_timeout; diff --git a/subsys/net/lib/mqtt/mqtt.c b/subsys/net/lib/mqtt/mqtt.c index b9cc62ef5e6ba..318978b6a9879 100644 --- a/subsys/net/lib/mqtt/mqtt.c +++ b/subsys/net/lib/mqtt/mqtt.c @@ -8,6 +8,7 @@ #include "mqtt_pkt.h" #include +#include #include /* * Issue #5817 workaround: @@ -81,7 +82,7 @@ int mqtt_tx_connect(struct mqtt_ctx *ctx, struct mqtt_connect_msg *msg) if (rc != 0) { rc = -EINVAL; } else { - rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + rc = async_send(ctx->stream, data, len, NULL, NULL, 0); SET_ERRNO_AND_RC(rc); } @@ -102,7 +103,7 @@ int mqtt_tx_disconnect(struct mqtt_ctx *ctx) return -EINVAL; } - rc = async_send(ctx->sock, msg, len, NULL, NULL, 0); + rc = async_send(ctx->stream, msg, len, NULL, NULL, 0); if (rc < 0) { rc = -errno; } else { @@ -157,7 +158,7 @@ int mqtt_tx_pub_msgs(struct mqtt_ctx *ctx, u16_t id, if (rc != 0) { rc = -EINVAL; } else { - rc = async_send(ctx->sock, msg, len, NULL, NULL, 0); + rc = async_send(ctx->stream, msg, len, NULL, NULL, 0); SET_ERRNO_AND_RC(rc); } @@ -199,7 +200,7 @@ int mqtt_tx_publish(struct mqtt_ctx *ctx, struct mqtt_publish_msg *msg) if (rc != 0) { rc = -EINVAL; } else { - rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + rc = async_send(ctx->stream, data, len, NULL, NULL, 0); SET_ERRNO_AND_RC(rc); } @@ -219,7 +220,7 @@ int mqtt_tx_pingreq(struct mqtt_ctx *ctx) return -EINVAL; } - rc = async_send(ctx->sock, msg, len, NULL, NULL, 0); + rc = async_send(ctx->stream, msg, len, NULL, NULL, 0); SET_ERRNO_AND_RC(rc); return rc; @@ -242,7 +243,7 @@ int mqtt_tx_subscribe(struct mqtt_ctx *ctx, u16_t pkt_id, u8_t items, if (rc != 0) { rc = -EINVAL; } else { - rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + rc = async_send(ctx->stream, data, len, NULL, NULL, 0); SET_ERRNO_AND_RC(rc); } @@ -268,7 +269,7 @@ int mqtt_tx_unsubscribe(struct mqtt_ctx *ctx, u16_t pkt_id, u8_t items, if (rc != 0) { rc = -EINVAL; } else { - rc = async_send(ctx->sock, data, len, NULL, NULL, 0); + rc = async_send(ctx->stream, data, len, NULL, NULL, 0); SET_ERRNO_AND_RC(rc); } @@ -657,11 +658,14 @@ int mqtt_connect(struct mqtt_ctx *ctx) goto error_connect; } + zstream_sock_init(&ctx->stream_sock, ctx->sock); + ctx->stream = (zstream)&ctx->stream_sock; + /* * Setup receive callback, which will call mqtt_parser() on a received * socket buffer: */ - rc = async_recv(ctx->sock, ctx->rcv_buf, MSG_SIZE, + rc = async_recv(ctx->sock, ctx->stream, ctx->rcv_buf, MSG_SIZE, async_recv_cb, (void *)ctx); if (rc < 0) { goto error_connect; @@ -737,7 +741,7 @@ int mqtt_close(struct mqtt_ctx *ctx) } if (ctx->sock != INVALID_SOCK) { - async_close(ctx->sock); + async_close(ctx->sock, ctx->stream); ctx->sock = INVALID_SOCK; } From 4fd6a2c912be3cf668787b58b5588eda976bb568 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 2 Mar 2018 13:41:19 +0200 Subject: [PATCH 18/19] net: mqtt: Add support for zstream_tls. Signed-off-by: Paul Sokolovsky --- include/net/mqtt.h | 7 +++++- samples/net/mqtt_publisher/prj_qemu_x86.conf | 10 +++++++- subsys/net/lib/mqtt/mqtt.c | 26 ++++++++++++++------ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/include/net/mqtt.h b/include/net/mqtt.h index bc927936ecb58..7221fabc97955 100644 --- a/include/net/mqtt.h +++ b/include/net/mqtt.h @@ -8,6 +8,7 @@ #define _MQTT_H_ #include +#include #include #if defined(CONFIG_MQTT_LIB_TLS) #include @@ -67,8 +68,12 @@ enum mqtt_app { */ struct mqtt_ctx { int sock; - zstream stream; + /* Points to either stream_sock or stream_tls below */ + struct zstream *stream; struct zstream_sock stream_sock; +#if defined(CONFIG_MBEDTLS) + struct zstream_tls stream_tls; +#endif s32_t net_init_timeout; s32_t net_timeout; diff --git a/samples/net/mqtt_publisher/prj_qemu_x86.conf b/samples/net/mqtt_publisher/prj_qemu_x86.conf index 611fb60d677ed..71baf740921b3 100644 --- a/samples/net/mqtt_publisher/prj_qemu_x86.conf +++ b/samples/net/mqtt_publisher/prj_qemu_x86.conf @@ -5,6 +5,13 @@ CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_NET_LOG=y CONFIG_INIT_STACKS=y +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2000 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=15000 +#CONFIG_MBEDTLS_DEBUG=y +#CONFIG_MBEDTLS_DEBUG_LEVEL=3 + CONFIG_NET_PKT_RX_COUNT=16 CONFIG_NET_PKT_TX_COUNT=16 CONFIG_NET_BUF_RX_COUNT=16 @@ -31,7 +38,8 @@ 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_MAIN_STACK_SIZE=2048 +# Big value required for mbedTLS +CONFIG_MAIN_STACK_SIZE=4096 # For IPv6 CONFIG_NET_BUF_DATA_SIZE=256 diff --git a/subsys/net/lib/mqtt/mqtt.c b/subsys/net/lib/mqtt/mqtt.c index 318978b6a9879..7d1b0fc7fa107 100644 --- a/subsys/net/lib/mqtt/mqtt.c +++ b/subsys/net/lib/mqtt/mqtt.c @@ -633,6 +633,7 @@ int mqtt_connect(struct mqtt_ctx *ctx) int rc = 0; struct sockaddr addr; struct sockaddr peer_addr; + mbedtls_ssl_config *tls_conf; if (!ctx) { return -EFAULT; @@ -658,8 +659,25 @@ int mqtt_connect(struct mqtt_ctx *ctx) goto error_connect; } + rc = async_connect(ctx->sock, &peer_addr, sizeof(peer_addr), + async_connected_cb, (void *)ctx); + if (rc < 0) { + goto error_connect; + } + zstream_sock_init(&ctx->stream_sock, ctx->sock); - ctx->stream = (zstream)&ctx->stream_sock; + ctx->stream = (struct zstream *)&ctx->stream_sock; + + rc = ztls_get_tls_client_conf(&tls_conf); + if (rc < 0) { + goto error_connect; + } + mbedtls_ssl_conf_authmode(tls_conf, MBEDTLS_SSL_VERIFY_NONE); + rc = zstream_tls_init(&ctx->stream_tls, ctx->stream, tls_conf, NULL); + if (rc < 0) { + goto error_connect; + } + ctx->stream = (struct zstream *)&ctx->stream_tls; /* * Setup receive callback, which will call mqtt_parser() on a received @@ -688,12 +706,6 @@ int mqtt_connect(struct mqtt_ctx *ctx) } #endif - rc = async_connect(ctx->sock, &peer_addr, sizeof(peer_addr), - async_connected_cb, (void *)ctx); - if (rc < 0) { - goto error_connect; - } - #if defined(CONFIG_MQTT_LIB_TLS) /* TLS handshake is not finished until app_connected is called */ rc = k_sem_take(&ctx->tls_hs_wait, ctx->tls_hs_timeout); From 57c07168507a82b058d638320608289686f87357 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 9 Mar 2018 20:06:03 +0200 Subject: [PATCH 19/19] samples: sockets: http_get: Severely bump hw requirements for TLS. Signed-off-by: Paul Sokolovsky --- samples/net/sockets/http_get/sample.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/net/sockets/http_get/sample.yaml b/samples/net/sockets/http_get/sample.yaml index c0e36f1ccbcb2..6d27170938ce4 100644 --- a/samples/net/sockets/http_get/sample.yaml +++ b/samples/net/sockets/http_get/sample.yaml @@ -4,5 +4,6 @@ sample: tests: test: harness: net - min_ram: 32 + min_flash: 160 + min_ram: 90 tags: net