diff --git a/include/net/net_app.h b/include/net/net_app.h new file mode 100644 index 00000000000..b85b01faad8 --- /dev/null +++ b/include/net/net_app.h @@ -0,0 +1,897 @@ +/** @file + * @brief Common routines needed in various network applications. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_APP_H +#define __NET_APP_H + +#if defined(CONFIG_NET_APP_TLS) +#if defined(CONFIG_MBEDTLS) +#if !defined(CONFIG_MBEDTLS_CFG_FILE) +#include "mbedtls/config.h" +#else +#include CONFIG_MBEDTLS_CFG_FILE +#endif /* CONFIG_MBEDTLS_CFG_FILE */ + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#define mbedtls_time_t time_t +#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS +#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE +#endif /* MBEDTLS_PLATFORM_C */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* CONFIG_MBEDTLS */ +#endif /* CONFIG_NET_APP_TLS */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Flags that tell what kind of functionality is needed by the application. */ +#define NET_APP_NEED_ROUTER 0x00000001 +#define NET_APP_NEED_IPV6 0x00000002 +#define NET_APP_NEED_IPV4 0x00000004 + +enum net_app_type { + NET_APP_UNSPEC = 0, + NET_APP_SERVER, + NET_APP_CLIENT, +}; + +struct net_app_ctx; + +/** + * @typedef net_app_recv_cb_t + * @brief Network data receive callback. + * + * @details The recv callback is called after a network data is + * received. + * + * @param ctx The context to use. + * @param pkt Network buffer that is received. If the pkt is not NULL, + * then the callback will own the buffer and it needs to to unref the pkt + * as soon as it has finished working with it. On EOF, pkt will be NULL. + * @param status Value is set to 0 if some data or the connection is + * at EOF, <0 if there was an error receiving data, in this case the + * pkt parameter is set to NULL. + * @param user_data The user data given in init call. + */ +typedef void (*net_app_recv_cb_t)(struct net_app_ctx *ctx, + struct net_pkt *pkt, + int status, + void *user_data); + +/** + * @typedef net_app_connect_cb_t + * @brief Connection callback. + * + * @details The connect callback is called after a connection is being + * established. + * + * @param ctx The context to use. + * @param status Status of the connection establishment. This is 0 + * if the connection was established successfully, <0 if there was an + * error. + * @param user_data The user data given in init call. + */ +typedef void (*net_app_connect_cb_t)(struct net_app_ctx *ctx, + int status, + void *user_data); + +/** + * @typedef net_app_send_cb_t + * @brief Network data send callback. + * + * @details The send callback is called after a network data is + * sent. + * + * @param ctx The context to use. + * @param status Value is set to 0 if all data was sent ok, <0 if + * there was an error sending data. >0 amount of data that was + * sent when not all data was sent ok. + * @param user_data_send The user data given in net_app_send() call. + * @param user_data The user data given in init call. + */ +typedef void (*net_app_send_cb_t)(struct net_app_ctx *ctx, + int status, + void *user_data_send, + void *user_data); + +/** + * @typedef net_app_close_cb_t + * @brief Close callback. + * + * @details The close callback is called after a connection is being + * shutdown. + * + * @param ctx The context to use. + * @param status Error code for the closing. + * @param user_data The user data given in init call. + */ +typedef void (*net_app_close_cb_t)(struct net_app_ctx *ctx, + int status, + void *user_data); + +/** Network application callbacks */ +struct net_app_cb { + /** Function that is called when a connection is established. + */ + net_app_connect_cb_t connect; + + /** Function that is called when data is received from network. + */ + net_app_recv_cb_t recv; + + /** Function that is called when net_pkt is sent. + */ + net_app_send_cb_t send; + + /** Function that is called when connection is shutdown. + */ + net_app_close_cb_t close; +}; + +/* This is the same prototype as what net_context_sendto() has + * so that we can override the sending of the data for TLS traffic. + */ +typedef int (*net_app_send_data_t)(struct net_pkt *pkt, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data); + +#if defined(CONFIG_NET_APP_TLS) +/* Internal information for managing TLS data */ +struct tls_context { + struct net_pkt *rx_pkt; + struct net_buf *frag; + struct k_sem tx_sem; + struct k_fifo tx_rx_fifo; + int remaining; +}; + +/* This struct is used to pass data to TLS thread when reading or sending + * data. + */ +struct net_app_fifo_block { + struct k_mem_block block; + struct net_pkt *pkt; + void *token; /* Used when sending data */ + net_context_send_cb_t cb; + u8_t dir; +}; + +#define NET_APP_TLS_POOL_DEFINE(name, count) \ + K_MEM_POOL_DEFINE(name, sizeof(struct net_app_fifo_block), \ + sizeof(struct net_app_fifo_block), count, sizeof(int)) + +#if defined(CONFIG_NET_APP_SERVER) +/** + * @typedef net_app_cert_cb_t + * @brief Callback used when the API user needs to setup the certs. + * + * @param ctx Net app context. + * @param cert MBEDTLS certificate + * @param pkey MBEDTLS private key + * + * @return 0 if ok, <0 if there is an error + */ +typedef int (*net_app_cert_cb_t)(struct net_app_ctx *ctx, + mbedtls_x509_crt *cert, + mbedtls_pk_context *pkey); +#endif /* CONFIG_NET_APP_SERVER */ + +#if defined(CONFIG_NET_APP_CLIENT) +/** + * @typedef net_app_ca_cert_cb_t + * @brief Callback used when the API user needs to setup certs. + * + * @param ctx Net app client context. + * @param ca_cert MBEDTLS certificate. This is of type mbedtls_x509_crt + * if MBEDTLS_X509_CRT_PARSE_C is defined. + * + * @return 0 if ok, <0 if there is an error + */ +typedef int (*net_app_ca_cert_cb_t)(struct net_app_ctx *ctx, + void *ca_cert); +#endif /* CONFIG_NET_APP_CLIENT */ + +/** + * @typedef net_app_entropy_src_cb_t + * @brief Callback used when the API user needs to setup the entropy source. + * @details This is the same as mbedtls_entropy_f_source_ptr callback. + * + * @param data Callback-specific data pointer + * @param output Data to fill + * @param len Maximum size to provide + * @param olen The actual amount of bytes put into the buffer (Can be 0) + */ +typedef int (*net_app_entropy_src_cb_t)(void *data, unsigned char *output, + size_t len, size_t *olen); +#endif /* CONFIG_NET_APP_TLS */ + +/** Network application context. */ +struct net_app_ctx { +#if defined(CONFIG_NET_IPV6) + /** Network IPv6 context. */ + struct net_context *ipv6_ctx; +#endif +#if defined(CONFIG_NET_IPV4) + /** Network IPv4 context. */ + struct net_context *ipv4_ctx; +#endif + + /** Internal function that is called when user data is sent to + * network. By default this is set to net_context_sendto() but + * is overridden for TLS as it requires special handling. + */ + net_app_send_data_t send_data; + + /** Connection callbacks */ + struct net_app_cb cb; + + /** Internal function that is called when data is received from + * network. This will do what ever needed and then pass data to + * application. + */ + net_context_recv_cb_t recv_cb; + + /** Local address */ + struct sockaddr local; + + /** Remote address */ + struct sockaddr remote; + +#if defined(CONFIG_NET_APP_SERVER) + struct { +#if defined(CONFIG_NET_TCP) + /** Currently active network context. This will contain the + * new context that is created after connection is accepted + * when TCP is enabled. + */ + struct net_context *net_ctx; +#endif + } server; +#endif /* CONFIG_NET_APP_SERVER */ + +#if defined(CONFIG_NET_APP_CLIENT) + struct { + /** Connect waiter */ + struct k_sem connect_wait; + +#if defined(CONFIG_DNS_RESOLVER) + /** DNS resolver waiter */ + struct k_sem dns_wait; + + /** DNS query id. This is needed if the query needs to be + * cancelled. + */ + u16_t dns_id; +#endif + } client; +#endif /* CONFIG_NET_APP_CLIENT */ + +#if defined(CONFIG_NET_APP_TLS) + struct { + /** TLS stack for mbedtls library. */ + u8_t *stack; + + /** TLS stack size. */ + int stack_size; + + /** TLS thread id */ + k_tid_t tid; + + /** TLS thread */ + struct k_thread thread; + + /** Memory pool for RX data */ + struct k_mem_pool *pool; + + /** Where the encrypted request is stored, this is to be + * provided by the user. + */ + u8_t *request_buf; + + /** Hostname to be used in the certificate verification */ + const char *cert_host; + + /** Request buffer maximum length */ + size_t request_buf_len; + + /** mbedtls related configuration. */ + struct { +#if defined(CONFIG_NET_APP_SERVER) + net_app_cert_cb_t cert_cb; + mbedtls_x509_crt srvcert; + mbedtls_pk_context pkey; +#endif +#if defined(CONFIG_NET_APP_CLIENT) + net_app_ca_cert_cb_t ca_cert_cb; + mbedtls_x509_crt ca_cert; +#endif + struct tls_context ssl_ctx; + net_app_entropy_src_cb_t entropy_src_cb; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + u8_t *personalization_data; + size_t personalization_data_len; + } mbedtls; + + /** Have we called connect cb yet? */ + bool connect_cb_called; + } tls; +#endif /* CONFIG_NET_APP_TLS */ + +#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) + /** Network packet (net_pkt) memory pool for network contexts attached + * to this network app context. + */ + net_pkt_get_slab_func_t tx_slab; + + /** Network data net_buf pool for network contexts attached to this + * network app context. + */ + net_pkt_get_pool_func_t data_pool; +#endif + + /** User data pointer */ + void *user_data; + + /** Type of the connection (stream or datagram) */ + enum net_sock_type sock_type; + + /** IP protocol type (UDP or TCP) */ + enum net_ip_protocol proto; + + /** Application type (client or server) of this instance */ + enum net_app_type app_type; + + /** Is this context setup or not */ + u8_t is_init : 1; + + /** Is this instance supporting TLS or not. + */ + u8_t is_tls : 1; + + /** Running status of the server. If true, then the server is enabled. + * If false then it is disabled and will not serve clients. + * The server is disabled by default after initialization and will need + * to be enabled manually. + */ + u8_t is_enabled : 1; + + /** Unused bits */ + u8_t _padding : 5; +}; + +/** + * @brief Initialize this network application. + * + * @param app_info String describing this application. + * @param flags Flags related to this application startup. + * @param timeout How long to wait the network setup before continuing + * the startup. + * + * @return 0 if ok, <0 if error. + */ +int net_app_init(const char *app_info, u32_t flags, s32_t timeout); + +#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) +/** + * @brief Configure the net_pkt pool for this context. + * + * @details Use of this function is optional and if the pools are not set, + * then the default TX and DATA pools are used. + * + * @param tx_slab Function which is used when allocating TX network packet. + * This can be NULL in which case default TX memory pool is used. + * @param data_pool Function which is used when allocating data network buffer. + * This can be NULL in which case default DATA net_buf pool is used. + */ +int net_app_set_net_pkt_pool(struct net_app_ctx *ctx, + net_pkt_get_slab_func_t tx_slab, + net_pkt_get_pool_func_t data_pool); +#else +#define net_app_set_net_pkt_pool(...) +#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ + +#if defined(CONFIG_NET_APP_SERVER) +/** + * @brief Create a network server application. + * + * @details Note that caller must create the context and initialize it to 0 + * before calling this function. The context must be valid for the whole + * duration of the application life cycle. This usually means that it + * cannot be allocated from stack. + * + * @param ctx Network application context. + * @param sock_type Connection type (stream or datagram). + * @param proto IP protocol (UDP or TCP) + * @param server_addr Local address of the server. If set to NULL, then the + * API will figure out a proper address where to bind the context. + * @param port UDP or TCP port number where the service is located. This is + * only used if server_addr parameter is NULL. + * @param timeout Timeout for this function call. This timeout tells how + * long to wait while accepting the data from network. + * @param user_data User specific data. + * + * @return 0 if ok, <0 if error. + */ +int net_app_init_server(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto, + struct sockaddr *server_addr, + u16_t port, + void *user_data); + +/** + * @brief Create a TCP server application. + * + * @details Note that caller must create the context and initialize it to 0 + * before calling this function. The context must be valid for the whole + * duration of the application life cycle. This usually means that it + * cannot be allocated from stack. + * + * @param ctx Network application context. + * @param server_addr Local address of the server. If set to NULL, then the + * API will figure out a proper address where to bind the context. + * @param port UDP or TCP port number where the service is located. This is + * only used if server_addr parameter is NULL. + * @param timeout Timeout for this function call. This timeout tells how + * long to wait while accepting the data from network. + * @param user_data User specific data. + * + * @return 0 if ok, <0 if error. + */ +static inline int net_app_init_tcp_server(struct net_app_ctx *ctx, + struct sockaddr *server_addr, + u16_t port, + void *user_data) +{ + return net_app_init_server(ctx, + SOCK_STREAM, + IPPROTO_TCP, + server_addr, + port, + user_data); +} + +/** + * @brief Create a UDP server application. + * + * @details Note that caller must create the context and initialize it to 0 + * before calling this function. The context must be valid for the whole + * duration of the application life cycle. This usually means that it + * cannot be allocated from stack. + * + * @param ctx Network application context. + * @param server_addr Local address of the server. If set to NULL, then the + * API will figure out a proper address where to bind the context. + * @param port UDP or TCP port number where the service is located. This is + * only used if server_addr parameter is NULL. + * @param timeout Timeout for this function call. This timeout tells how + * long to wait while accepting the data from network. + * @param user_data User specific data. + * + * @return 0 if ok, <0 if error. + */ +static inline int net_app_init_udp_server(struct net_app_ctx *ctx, + struct sockaddr *server_addr, + u16_t port, + void *user_data) +{ + return net_app_init_server(ctx, + SOCK_DGRAM, + IPPROTO_UDP, + server_addr, + port, + user_data); +} + +/** + * @brief Wait for an incoming connection. + * + * @details Note that caller must have called net_app_init_server() before + * calling this function. This functionality is separated from init function + * so that user can setup the callbacks after calling init. Only after calling + * this function the server starts to listen connection attempts. This function + * will not block but will initialize the local end point address so that + * receive callback will be called for incoming connection. + * + * @param ctx Network application context. + * + * @return 0 if ok, <0 if error. + */ +int net_app_listen(struct net_app_ctx *ctx); + +#endif /* CONFIG_NET_APP_SERVER */ + +#if defined(CONFIG_NET_APP_CLIENT) +/** + * @brief Create a network client application. + * + * @details Note that caller must create the context and initialize it to 0 + * before calling this function. The context must be valid for the whole + * duration of the application life cycle. This usually means that it + * cannot be allocated from stack. + * + * @param ctx Network application context. + * @param sock_type Connection type (stream or datagram). + * @param proto IP protocol (UDP or TCP) + * @param client_addr Local address of the client. If set to NULL, then the + * API will figure out a proper address where to bind the context. + * @param peer_addr Peer (target) address. If this is NULL, then the + * peer_add_str string and peer_port are used when connecting to peer. + * @param peer_addr_str Peer (target) address as a string. If this is NULL, + * then the peer_addr sockaddr is used to set the peer address. If DNS is + * configured in the system, then the hostname is automatically resolved if + * given here. Note that the port number is optional in the string. If the + * port number is not given in the string, then peer_port variable is used + * instead. + * Following syntex is supported for the address: + * 192.0.2.1 + * 192.0.2.1:5353 + * 2001:db8::1 + * [2001:db8::1]:5353 + * peer.example.com + * peer.example.com:1234 + * @param peer_port Port number where to connect to. Ignored if port number is + * found in the peer_addr_str. + * @param timeout Timeout for this function call. This is used if the API + * needs to do some time consuming operation, like resolving DNS address. + * @param user_data User specific data. + * + * @return 0 if ok, <0 if error. + */ +int net_app_init_client(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto, + struct sockaddr *client_addr, + struct sockaddr *peer_addr, + const char *peer_addr_str, + u16_t peer_port, + s32_t timeout, + void *user_data); + +/** + * @brief Create a TCP client application. + * + * @details Note that caller must create the context and initialize it to 0 + * before calling this function. The context must be valid for the whole + * duration of the application life cycle. This usually means that it + * cannot be allocated from stack. + * + * @param ctx Network application context. + * @param client_addr Local address of the client. If set to NULL, then the + * API will figure out a proper address where to bind the context. + * @param peer_addr Peer (target) address. If this is NULL, then the + * peer_add_str string and peer_port are used when connecting to peer. + * @param peer_addr_str Peer (target) address as a string. If this is NULL, + * then the peer_addr sockaddr is used to set the peer address. If DNS is + * configured in the system, then the hostname is automatically resolved if + * given here. Note that the port number is optional in the string. If the + * port number is not given in the string, then peer_port variable is used + * instead. + * Following syntex is supported for the address: + * 192.0.2.1 + * 192.0.2.1:5353 + * 2001:db8::1 + * [2001:db8::1]:5353 + * peer.example.com + * peer.example.com:1234 + * @param peer_port Port number where to connect to. Ignored if port number is + * found in the peer_addr_str. + * @param timeout Timeout for this function call. This is used if the API + * needs to do some time consuming operation, like resolving DNS address. + * @param user_data User specific data. + * + * @return 0 if ok, <0 if error. + */ +static inline int net_app_init_tcp_client(struct net_app_ctx *ctx, + struct sockaddr *client_addr, + struct sockaddr *peer_addr, + const char *peer_addr_str, + u16_t peer_port, + s32_t timeout, + void *user_data) +{ + return net_app_init_client(ctx, + SOCK_STREAM, + IPPROTO_TCP, + client_addr, + peer_addr, + peer_addr_str, + peer_port, + timeout, + user_data); +} + +/** + * @brief Create a UDP client application. + * + * @details Note that caller must create the context and initialize it to 0 + * before calling this function. The context must be valid for the whole + * duration of the application life cycle. This usually means that it + * cannot be allocated from stack. + * + * @param ctx Network application context. + * @param client_addr Local address of the client. If set to NULL, then the + * API will figure out a proper address where to bind the context. + * @param peer_addr Peer (target) address. If this is NULL, then the + * peer_add_str string and peer_port are used when connecting to peer. + * @param peer_addr_str Peer (target) address as a string. If this is NULL, + * then the peer_addr sockaddr is used to set the peer address. If DNS is + * configured in the system, then the hostname is automatically resolved if + * given here. Note that the port number is optional in the string. If the + * port number is not given in the string, then peer_port variable is used + * instead. + * Following syntex is supported for the address: + * 192.0.2.1 + * 192.0.2.1:5353 + * 2001:db8::1 + * [2001:db8::1]:5353 + * peer.example.com + * peer.example.com:1234 + * @param peer_port Port number where to connect to. Ignored if port number is + * found in the peer_addr_str. + * @param timeout Timeout for this function call. This is used if the API + * needs to do some time consuming operation, like resolving DNS address. + * @param user_data User specific data. + * + * @return 0 if ok, <0 if error. + */ +static inline int net_app_init_udp_client(struct net_app_ctx *ctx, + struct sockaddr *client_addr, + struct sockaddr *peer_addr, + const char *peer_addr_str, + u16_t peer_port, + s32_t timeout, + void *user_data) +{ + return net_app_init_client(ctx, + SOCK_DGRAM, + IPPROTO_UDP, + client_addr, + peer_addr, + peer_addr_str, + peer_port, + timeout, + user_data); +} + +/** + * @brief Establish a network connection to peer. + * + * @param ctx Network application context. + * @param timeout How long to wait the network connection before giving up. + * + * @return 0 if ok, <0 if error. + */ +int net_app_connect(struct net_app_ctx *ctx, s32_t timeout); +#endif /* CONFIG_NET_APP_CLIENT */ + +/** + * @brief Set various network application callbacks. + * + * @param ctx Network app context. + * @param connect_cb Connect callback. + * @param recv_cb Data receive callback. + * @param send_cb Data sent callback. + * @param close_cb Close callback. + * + * @return 0 if ok, <0 if error. + */ +int net_app_set_cb(struct net_app_ctx *ctx, + net_app_connect_cb_t connect_cb, + net_app_recv_cb_t recv_cb, + net_app_send_cb_t send_cb, + net_app_close_cb_t close_cb); + +/** + * @brief Send data that is found in net_pkt to peer. + * + * @details If the function return < 0, then it is caller responsibility + * to unref the pkt. If the packet was sent successfully, then the lower + * IP stack will release the network pkt. + * + * @param ctx Network application context. + * @param pkt Network packet to send. + * @param dst Destination address where to send packet. This is + * ignored for TCP data. + * @param dst_len Destination address structure size + * @param timeout How long to wait the send before giving up. + * @param user_data_send User data specific to this send call. + * + * @return 0 if ok, <0 if error. + */ +int net_app_send_pkt(struct net_app_ctx *ctx, + struct net_pkt *pkt, + const struct sockaddr *dst, + socklen_t dst_len, + s32_t timeout, + void *user_data_send); + +/** + * @brief Send data that is found in user specified buffer to peer. + * + * @param ctx Network application context. + * @param buf Buffer to send. + * @param buf_len Amount of data to send. + * @param dst Destination address where to send packet. This is + * ignored for TCP data. + * @param dst_len Destination address structure size + * @param timeout How long to wait the send before giving up. + * @param user_data_send User data specific to this send call. + * + * @return 0 if ok, <0 if error. + */ +int net_app_send_buf(struct net_app_ctx *ctx, + u8_t *buf, + size_t buf_len, + const struct sockaddr *dst, + socklen_t dst_len, + s32_t timeout, + void *user_data_send); + +/** + * @brief Create network packet. + * + * @param ctx Network application context. + * @param timeout How long to wait the send before giving up. + * + * @return valid net_pkt if ok, NULL if error. + */ +struct net_pkt *net_app_get_net_pkt(struct net_app_ctx *ctx, + s32_t timeout); + +/** + * @brief Create network buffer that will hold network data. + * + * @details The returned net_buf is automatically appended to the + * end of network packet fragment chain. + * + * @param ctx Network application context. + * @param pkt Network packet to where the data buf is appended. + * @param timeout How long to wait the send before giving up. + * + * @return valid net_pkt if ok, NULL if error. + */ +struct net_buf *net_app_get_net_buf(struct net_app_ctx *ctx, + struct net_pkt *pkt, + s32_t timeout); + +/** + * @brief Close a network connection to peer. + * + * @param ctx Network application context. + * + * @return 0 if ok, <0 if error. + */ +int net_app_close(struct net_app_ctx *ctx); + +/** + * @brief Release this network application context. + * + * @details No network data will be received via this context after this + * call. + * + * @param ctx Network application context. + * + * @return 0 if ok, <0 if error. + */ +int net_app_release(struct net_app_ctx *ctx); + + +#if defined(CONFIG_NET_APP_TLS) +#if defined(CONFIG_NET_APP_CLIENT) +/** + * @brief Initialize TLS support for this net_app client context. + * + * @param ctx net_app context. + * @param request_buf Caller supplied buffer where the TLS request will be + * stored + * @param request_buf_len Length of the caller suppied buffer. + * @param personalization_data Personalization data (Device specific + * identifiers) for random number generator. (Can be NULL). + * @param personalization_data_len Length of the personalization data. + * @param cert_cb User supplied callback that setups the certifacates. + * @param cert_host Hostname that is used to verify the server certificate. + * This value is used when net_api API calls mbedtls_ssl_set_hostname() + * which sets the hostname to check against the received server certificate. + * See https://tls.mbed.org/kb/how-to/use-sni for more details. + * This can be left NULL in which case mbedtls will silently skip certificate + * verification entirely. This option is only used if MBEDTLS_X509_CRT_PARSE_C + * is enabled in mbedtls config file. + * @param entropy_src_cb User supplied callback that setup the entropy. This + * can be set to NULL, in which case default entropy source is used. + * @param pool Memory pool for RX data reads. + * @param stack TLS thread stack. + * @param stack_len TLS thread stack size. + * + * @return Return 0 if ok, <0 if error. + */ +int net_app_client_tls(struct net_app_ctx *ctx, + u8_t *request_buf, + size_t request_buf_len, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_ca_cert_cb_t cert_cb, + const char *cert_host, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + u8_t *stack, + size_t stack_size); +#endif /* CONFIG_NET_APP_CLIENT */ + +#if defined(CONFIG_NET_APP_SERVER) +/** + * @brief Initialize TLS support for this net_app server context. + * + * @param ctx net_app context. + * @param request_buf Caller supplied buffer where the TLS request will be + * stored + * @param request_buf_len Length of the caller suppied buffer. + * @param server_banner Print information about started service. This is only + * printed if net_app debugging is activated. The parameter can be set to NULL + * if no extra prints are needed. + * @param personalization_data Personalization data (Device specific + * identifiers) for random number generator. (Can be NULL). + * @param personalization_data_len Length of the personalization data. + * @param cert_cb User supplied callback that setups the certifacates. + * @param entropy_src_cb User supplied callback that setup the entropy. This + * can be set to NULL, in which case default entropy source is used. + * @param pool Memory pool for RX data reads. + * @param stack TLS thread stack. + * @param stack_len TLS thread stack size. + * + * @return Return 0 if ok, <0 if error. + */ +int net_app_server_tls(struct net_app_ctx *ctx, + u8_t *request_buf, + size_t request_buf_len, + const char *server_banner, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_cert_cb_t cert_cb, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + u8_t *stack, + size_t stack_len); + +bool net_app_server_tls_enable(struct net_app_ctx *ctx); +bool net_app_server_tls_disable(struct net_app_ctx *ctx); +#endif /* CONFIG_NET_APP_SERVER */ + +#endif /* CONFIG_NET_APP_SEC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __NET_APP_H */ diff --git a/samples/net/common/Makefile.common b/samples/net/common/Makefile.common index c5c7d7a6665..592f27fba9d 100644 --- a/samples/net/common/Makefile.common +++ b/samples/net/common/Makefile.common @@ -13,9 +13,25 @@ endif ifeq ($(CONFIG_NET_L2_IEEE802154), y) ifeq ($(CONFIG_NET_APP_SETTINGS), y) +ccflags-y += -I${ZEPHYR_BASE}/samples/net/common/ obj-y += ../../common/ieee802154_settings.o endif endif -ccflags-y += -I${ZEPHYR_BASE}/samples/net/common/ -obj-y += ../../common/sample_app_setup.o +ifeq ($(CONFIG_IEEE802154_CC2520),y) +ifeq ($(CONFIG_BOARD_ARDUINO_101),y) +ccflags-y +=-I${ZEPHYR_BASE}/include/drivers/ +obj-y += ../../common/cc2520_a101.o +endif + +ifeq ($(CONFIG_BOARD_FRDM_K64F),y) +ccflags-y +=-I${ZEPHYR_BASE}/drivers/ +ccflags-y +=-I${ZEPHYR_BASE}/include/drivers/ +obj-y += ../../common/cc2520_frdm_k64f.o +endif +endif + +ifeq ($(CONFIG_NET_TESTING), y) +ccflags-y +=-I${ZEPHYR_BASE}/samples/net/common/ +ccflags-y +=-DNET_TESTING_SERVER=1 +endif diff --git a/samples/net/common/net_sample_app.h b/samples/net/common/net_sample_app.h deleted file mode 100644 index 8f19d9afa12..00000000000 --- a/samples/net/common/net_sample_app.h +++ /dev/null @@ -1,29 +0,0 @@ -/** @file - * @brief Common routines needed in various network sample applications. - */ - -/* - * Copyright (c) 2017 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef __NET_SAMPLE_APP_H -#define __NET_SAMPLE_APP_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* What kind of functionality is needed by the application. */ -#define NET_SAMPLE_NEED_ROUTER 0x00000001 -#define NET_SAMPLE_NEED_IPV6 0x00000002 -#define NET_SAMPLE_NEED_IPV4 0x00000004 - -int net_sample_app_init(const char *app_info, u32_t flags, s32_t timeout); - -#ifdef __cplusplus -} -#endif - -#endif /* __NET_SAMPLE_APP_H */ diff --git a/samples/net/http_client/src/main.c b/samples/net/http_client/src/main.c index 5fe308cc325..f00ad3ee3b5 100644 --- a/samples/net/http_client/src/main.c +++ b/samples/net/http_client/src/main.c @@ -18,7 +18,7 @@ #include -#include +#include #include "config.h" @@ -307,7 +307,7 @@ void main(void) { int ret; - ret = net_sample_app_init("Run HTTP client", 0, APP_STARTUP_TIME); + ret = net_app_init("Run HTTP client", 0, APP_STARTUP_TIME); if (ret < 0) { panic("Application init failed"); } diff --git a/samples/net/http_server/src/main.c b/samples/net/http_server/src/main.c index 0d3ae4daa39..14b103b3fb4 100644 --- a/samples/net/http_server/src/main.c +++ b/samples/net/http_server/src/main.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include "config.h" @@ -279,13 +279,13 @@ void main(void) * before continuing. */ #if defined(CONFIG_NET_IPV6) - flags |= NET_SAMPLE_NEED_IPV6; + flags |= NET_APP_NEED_IPV6; #endif #if defined(CONFIG_NET_IPV4) - flags |= NET_SAMPLE_NEED_IPV4; + flags |= NET_APP_NEED_IPV4; #endif - ret = net_sample_app_init(APP_BANNER, flags, APP_STARTUP_TIME); + ret = net_app_init(APP_BANNER, flags, APP_STARTUP_TIME); if (ret < 0) { panic("Application init failed"); } diff --git a/samples/net/https_client/src/Makefile b/samples/net/https_client/src/Makefile index 6bede6e4ed9..587479094c8 100644 --- a/samples/net/https_client/src/Makefile +++ b/samples/net/https_client/src/Makefile @@ -4,6 +4,4 @@ # SPDX-License-Identifier: Apache-2.0 # -include $(ZEPHYR_BASE)/samples/net/common/Makefile.common - obj-y += main.o diff --git a/samples/net/https_client/src/main.c b/samples/net/https_client/src/main.c index b9f1bef9346..429a493ddc7 100644 --- a/samples/net/https_client/src/main.c +++ b/samples/net/https_client/src/main.c @@ -18,7 +18,7 @@ #include -#include +#include #include "config.h" @@ -370,7 +370,7 @@ void main(void) bool failure = false; int ret; - ret = net_sample_app_init("Run HTTPS client", 0, APP_STARTUP_TIME); + ret = net_app_init("Run HTTPS client", 0, APP_STARTUP_TIME); if (ret < 0) { panic("Application init failed"); } diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index eaee99c7059..0c99ca77e09 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -345,6 +345,4 @@ source "subsys/net/ip/Kconfig.rpl" source "subsys/net/ip/Kconfig.stats" -source "subsys/net/ip/Kconfig.app" - endmenu diff --git a/subsys/net/ip/Kconfig.app b/subsys/net/ip/Kconfig.app deleted file mode 100644 index 99092f3585a..00000000000 --- a/subsys/net/ip/Kconfig.app +++ /dev/null @@ -1,113 +0,0 @@ -# Kconfig.app - Options for sample applications - -# -# Copyright (c) 2016 Intel Corporation. -# -# SPDX-License-Identifier: Apache-2.0 -# - -menuconfig NET_APP_SETTINGS - bool "Set network settings for sample applications" - default n - help - Allow IP addresses to be set in config file for - networking client/server sample applications, or - some link-layer dedicated settings like the channel. - Beware this is not meant to be used for proper - provisioning but quick sampling/testing. - -if NET_APP_SETTINGS - -if NET_IPV6 - -config NET_APP_MY_IPV6_ADDR - string "My IPv6 address" - help - Use 2001:db8::1 here if uncertain. - -config NET_APP_PEER_IPV6_ADDR - string "Peer IPv6 address" - help - This is only applicable in client side applications that try - to establish a connection to peer host. - Use 2001:db8::2 here if uncertain. - -endif # NET_IPV6 - -if NET_IPV4 - -config NET_APP_MY_IPV4_ADDR - string "My IPv4 address" - help - Use 192.0.2.1 here if uncertain. - -config NET_APP_PEER_IPV4_ADDR - string "Peer IPv4 address" - help - This is only applicable in client side applications that try - to establish a connection to peer host. - Use 192.0.2.2 here if uncertain. - -endif # NET_IPV4 - -if NET_L2_IEEE802154 || NET_L2_RAW_CHANNEL - -config NET_APP_IEEE802154_DEV_NAME - string "IEEE 802.15.4 device name" - help - The device name to get bindings from in the sample application. - -config NET_APP_IEEE802154_PAN_ID - hex "IEEE 802.15.4 PAN ID" - default 0xabcd - help - The PAN ID to use by default in the sample. - -config NET_APP_IEEE802154_CHANNEL - int "IEEE 802.15.4 channel" - default 26 - help - The channel to use by default in the sample application. - -config NET_APP_IEEE802154_RADIO_TX_POWER - int "IEEE 802.15.4 TX power in dbm" - default 0 - help - The TX power to use by default in the sample application. - See NET_L2_IEEE802154_RADIO_DFLT_TX_POWER for more info. - -config NET_APP_IEEE802154_SECURITY_KEY - string "IEEE 802.15.4 security key" - default "moooh!" - depends on NET_L2_IEEE802154_SECURITY - help - The key string to use for the link-layer security part. - -config NET_APP_IEEE802154_SECURITY_KEY_MODE - int "IEEE 802.15.4 security key mode" - default 0 - range 0 0 - depends on NET_L2_IEEE802154_SECURITY - help - The key mode to use for the link-layer security part. - Only implicit mode is supported, thus 0. - -config NET_APP_IEEE802154_SECURITY_LEVEL - int "IEEE 802.15.4 security level (0-7)" - default 0 - range 0 7 - depends on NET_L2_IEEE802154_SECURITY - help - The security level to use for the link-layer security part. - 0 means no security - 1 authentication only with a 4 bytes length tag - 2 authentication only with a 8 bytes length tag - 3 authentication only with a 16 bytes length tag - 4 encryption only - 5 encryption/authentication with a 4 bytes length tag - 6 encryption/authentication with a 8 bytes length tag - 7 encryption/authentication with a 16 bytes length tag - -endif # NET_L2_IEEE802154 || NET_L2_RAW_CHANNEL - -endif # NET_APP_SETTINGS diff --git a/subsys/net/lib/Kbuild b/subsys/net/lib/Kbuild index 625b3414125..3a06bf9f9f6 100644 --- a/subsys/net/lib/Kbuild +++ b/subsys/net/lib/Kbuild @@ -3,3 +3,4 @@ obj-$(CONFIG_ZOAP) += zoap/ obj-$(CONFIG_DNS_RESOLVER) += dns/ obj-$(CONFIG_MQTT_LIB) += mqtt/ obj-$(CONFIG_HTTP) += http/ +obj-$(CONFIG_NET_APP_SETTINGS) += app/ diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index cf5942dd8c2..96b9091ff96 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -17,3 +17,9 @@ source "subsys/net/lib/mqtt/Kconfig" source "subsys/net/lib/http/Kconfig" endmenu + +menu "Network Applications" + +source "subsys/net/lib/app/Kconfig" + +endmenu diff --git a/subsys/net/lib/Makefile b/subsys/net/lib/Makefile index ae191af75c0..158cb29fd0a 100644 --- a/subsys/net/lib/Makefile +++ b/subsys/net/lib/Makefile @@ -17,3 +17,7 @@ endif ifdef CONFIG_HTTP include $(srctree)/subsys/net/lib/http/Makefile endif + +ifdef CONFIG_NET_APP_SETTINGS +include $(srctree)/subsys/net/lib/app/Makefile +endif diff --git a/subsys/net/lib/app/Kconfig b/subsys/net/lib/app/Kconfig new file mode 100644 index 00000000000..e1ef2aae11d --- /dev/null +++ b/subsys/net/lib/app/Kconfig @@ -0,0 +1,223 @@ +# Kconfig.app - Options for networking applications + +# +# Copyright (c) 2017 Intel Corporation. +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig NET_APP + bool "Network application API support [EXPERIMENTAL]" + default y + select NET_MGMT + select NET_MGMT_EVENT + help + Enable API that helps to create client/server network applications. + This API is experimental and subject to change. + +if NET_APP + +config NET_APP_AUTO_INIT + bool "Init networking support automatically during device startup" + default y + help + If this option is set, then the net_app API is automatically + initialized when the device is started. If you do not wish to do + this, then disable this and call net_app_init() in your application. + +config NET_APP_INIT_PRIO + int "Startup priority for the network application init" + default 95 + depends on NET_APP_AUTO_INIT + +config NET_APP_INIT_TIMEOUT + int "How long to wait for networking to be ready and available" + default 30 + depends on NET_APP_AUTO_INIT + help + The value is in seconds. If for example IPv4 address from DHCPv4 is not + received within this limit, then the net_app_init() call will fail + during the device startup. + +config NET_APP_NEED_IPV6 + bool "This application wants IPv6 support" + depends on NET_APP_AUTO_INIT + select NET_IPV6 + help + The network application needs IPv6 support to function properly. + This option makes sure the network application is initialized properly + in order to use IPv6. + +config NET_APP_NEED_IPV6_ROUTER + bool "This application wants IPv6 router to exists" + depends on NET_APP_AUTO_INIT + depends on NET_IPV6 + help + The network application needs IPv6 router to exists before continuing. + What this means that the application wants to wait until it receives + IPv6 router advertisement message before continuing. + +config NET_APP_NEED_IPV4 + bool "This application wants IPv4 support" + depends on NET_APP_AUTO_INIT + select NET_IPV4 + help + The network application needs IPv4 support to function properly. + This option makes sure the network application is initialized properly + in order to use IPv4. + +config NET_DEBUG_APP + bool "Debug net app library" + default n + help + Enables net app library to output debug messages + +config NET_APP_SERVER + bool "Enable server support" + default n + help + Enables net app library server APIs. + +config NET_APP_CLIENT + bool "Enable client support" + default n + help + Enables net app library client APIs. + +config NET_APP_TLS + bool "Enable TLS support for TCP applications" + default n + select MBEDTLS + help + Enables net app library to use TLS for encrypted communication. + +config NET_DEBUG_APP_TLS_LEVEL + int "Debug level for mbedtls in net app library" + depends on NET_APP_TLS && NET_DEBUG_APP + default 0 + range 0 4 + help + Sets log level for the mbedtls when debugging net_app library. + Levels are (from ext/lib/crypto/mbedtls/include/mbedtls/debug.h): + 0 No debug + 1 Error + 2 State change + 3 Information + 4 Verbose + +config NET_APP_TLS_STACK_SIZE + int "TLS handler thread stack size" + default 8192 + depends on NET_APP_TLS + help + TLS handler thread stack size. The mbedtls routines will use this stack + thus it is by default very large. + +endif # NET_APP + +menuconfig NET_APP_SETTINGS + bool "Set network settings for applications" + default n + depends on NET_APP + help + Allow IP addresses to be set in config file for + networking client/server sample applications, or + some link-layer dedicated settings like the channel. + Beware this is not meant to be used for proper + provisioning but quick sampling/testing. + +if NET_APP_SETTINGS + +if NET_IPV6 + +config NET_APP_MY_IPV6_ADDR + string "My IPv6 address" + help + Use 2001:db8::1 here if uncertain. + +config NET_APP_PEER_IPV6_ADDR + string "Peer IPv6 address" + help + This is only applicable in client side applications that try + to establish a connection to peer host. + Use 2001:db8::2 here if uncertain. + +endif # NET_IPV6 + +if NET_IPV4 + +config NET_APP_MY_IPV4_ADDR + string "My IPv4 address" + help + Use 192.0.2.1 here if uncertain. + +config NET_APP_PEER_IPV4_ADDR + string "Peer IPv4 address" + help + This is only applicable in client side applications that try + to establish a connection to peer host. + Use 192.0.2.2 here if uncertain. + +endif # NET_IPV4 + +if NET_L2_IEEE802154 || NET_L2_RAW_CHANNEL + +config NET_APP_IEEE802154_DEV_NAME + string "IEEE 802.15.4 device name" + help + The device name to get bindings from in the sample application. + +config NET_APP_IEEE802154_PAN_ID + hex "IEEE 802.15.4 PAN ID" + default 0xabcd + help + The PAN ID to use by default in the sample. + +config NET_APP_IEEE802154_CHANNEL + int "IEEE 802.15.4 channel" + default 26 + help + The channel to use by default in the sample application. + +config NET_APP_IEEE802154_RADIO_TX_POWER + int "IEEE 802.15.4 TX power in dbm" + default 0 + help + The TX power to use by default in the sample application. + See NET_L2_IEEE802154_RADIO_DFLT_TX_POWER for more info. + +config NET_APP_IEEE802154_SECURITY_KEY + string "IEEE 802.15.4 security key" + default "moooh!" + depends on NET_L2_IEEE802154_SECURITY + help + The key string to use for the link-layer security part. + +config NET_APP_IEEE802154_SECURITY_KEY_MODE + int "IEEE 802.15.4 security key mode" + default 0 + range 0 0 + depends on NET_L2_IEEE802154_SECURITY + help + The key mode to use for the link-layer security part. + Only implicit mode is supported, thus 0. + +config NET_APP_IEEE802154_SECURITY_LEVEL + int "IEEE 802.15.4 security level (0-7)" + default 0 + range 0 7 + depends on NET_L2_IEEE802154_SECURITY + help + The security level to use for the link-layer security part. + 0 means no security + 1 authentication only with a 4 bytes length tag + 2 authentication only with a 8 bytes length tag + 3 authentication only with a 16 bytes length tag + 4 encryption only + 5 encryption/authentication with a 4 bytes length tag + 6 encryption/authentication with a 8 bytes length tag + 7 encryption/authentication with a 16 bytes length tag + +endif # NET_L2_IEEE802154 || NET_L2_RAW_CHANNEL + +endif # NET_APP_SETTINGS diff --git a/subsys/net/lib/app/Makefile b/subsys/net/lib/app/Makefile new file mode 100644 index 00000000000..3fca06ebf84 --- /dev/null +++ b/subsys/net/lib/app/Makefile @@ -0,0 +1,20 @@ +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +ccflags-$(CONFIG_NET_L2_BLUETOOTH) += -I${ZEPHYR_BASE}/samples/bluetooth/ +ccflags-$(CONFIG_NET_L2_IEEE802154) += -I${ZEPHYR_BASE}/samples/net/common/ + +obj-$(CONFIG_NET_APP) += init.o +obj-$(CONFIG_NET_APP_SERVER) += server.o +obj-$(CONFIG_NET_APP_CLIENT) += client.o + +ifeq ($(CONFIG_NET_APP_SERVER),y) + obj-y += net_app.o +else + ifeq ($(CONFIG_NET_APP_CLIENT),y) + obj-y += net_app.o + endif +endif diff --git a/subsys/net/lib/app/client.c b/subsys/net/lib/app/client.c new file mode 100644 index 00000000000..60e1b1ca4cd --- /dev/null +++ b/subsys/net/lib/app/client.c @@ -0,0 +1,602 @@ +/* client.c */ + +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_NET_DEBUG_APP) +#define SYS_LOG_DOMAIN "net/app" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "net_app_private.h" + +#if defined(CONFIG_NET_APP_TLS) +#define TLS_STARTUP_TIMEOUT K_SECONDS(5) +static int start_tls_client(struct net_app_ctx *ctx); +#endif /* CONFIG_NET_APP_TLS */ + +#if defined(CONFIG_DNS_RESOLVER) +static void dns_cb(enum dns_resolve_status status, + struct dns_addrinfo *info, + void *user_data) +{ + struct net_app_ctx *ctx = user_data; + + if (!(status == DNS_EAI_INPROGRESS && info)) { + return; + } + + if (info->ai_family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + net_ipaddr_copy(&net_sin(&ctx->remote)->sin_addr, + &net_sin(&info->ai_addr)->sin_addr); +#else + goto out; +#endif + } else if (info->ai_family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + net_ipaddr_copy(&net_sin6(&ctx->remote)->sin6_addr, + &net_sin6(&info->ai_addr)->sin6_addr); +#else + goto out; +#endif + } else { + goto out; + } + + ctx->remote.family = info->ai_family; + +out: + k_sem_give(&ctx->client.dns_wait); +} + +static int resolve_name(struct net_app_ctx *ctx, + const char *peer_addr_str, + enum dns_query_type type, + s32_t timeout) +{ + int ret; + + k_sem_init(&ctx->client.dns_wait, 0, 1); + + ret = dns_get_addr_info(peer_addr_str, type, &ctx->client.dns_id, + dns_cb, ctx, timeout); + if (ret < 0) { + NET_ERR("Cannot resolve %s (%d)", peer_addr_str, ret); + ctx->client.dns_id = 0; + return ret; + } + + /* Wait a little longer for the DNS to finish so that + * the DNS will timeout before the semaphore. + */ + if (k_sem_take(&ctx->client.dns_wait, timeout + K_SECONDS(1))) { + NET_ERR("Timeout while resolving %s", peer_addr_str); + ctx->client.dns_id = 0; + return -ETIMEDOUT; + } + + ctx->client.dns_id = 0; + + if (ctx->remote.family == AF_UNSPEC) { + return -EINVAL; + } + + return 0; +} +#endif /* CONFIG_DNS_RESOLVER */ + +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; +} + +static int set_remote_addr(struct net_app_ctx *ctx, + const char *peer_addr_str, + u16_t peer_port, + s32_t timeout) +{ + char addr_str[INET6_ADDRSTRLEN + 1]; + char *addr; + int ret; + + /* If the peer string contains port number, use that and ignore + * the port number parameter. + */ + ret = get_port_number(peer_addr_str, addr_str, sizeof(addr_str)); + if (ret > 0) { + addr = addr_str; + peer_port = ret; + } else { + addr = (char *)peer_addr_str; + } + +#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4) + ret = net_addr_pton(AF_INET6, addr, + &net_sin6(&ctx->remote)->sin6_addr); + if (ret < 0) { + /* Could be hostname, try DNS if configured. */ +#if !defined(CONFIG_DNS_RESOLVER) + NET_ERR("Invalid IPv6 address %s", addr); + return -EINVAL; +#else + ret = resolve_name(ctx, addr, DNS_QUERY_TYPE_AAAA, timeout); + if (ret < 0) { + NET_ERR("Cannot resolve %s (%d)", addr, ret); + return ret; + } +#endif + } + + net_sin6(&ctx->remote)->sin6_port = htons(peer_port); + net_sin6(&ctx->remote)->sin6_family = AF_INET6; +#endif /* IPV6 && !IPV4 */ + +#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) + ret = net_addr_pton(AF_INET, addr, + &net_sin(&ctx->remote)->sin_addr); + if (ret < 0) { + /* Could be hostname, try DNS if configured. */ +#if !defined(CONFIG_DNS_RESOLVER) + NET_ERR("Invalid IPv4 address %s", addr); + return -EINVAL; +#else + ret = resolve_name(ctx, addr, DNS_QUERY_TYPE_A, timeout); + if (ret < 0) { + NET_ERR("Cannot resolve %s (%d)", addr, ret); + return ret; + } +#endif + } + + net_sin(&ctx->remote)->sin_port = htons(peer_port); + net_sin(&ctx->remote)->sin_family = AF_INET; +#endif /* IPV6 && !IPV4 */ + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) + ret = net_addr_pton(AF_INET, addr, + &net_sin(&ctx->remote)->sin_addr); + if (ret < 0) { + ret = net_addr_pton(AF_INET6, addr, + &net_sin6(&ctx->remote)->sin6_addr); + if (ret < 0) { + /* Could be hostname, try DNS if configured. */ +#if !defined(CONFIG_DNS_RESOLVER) + NET_ERR("Invalid IPv4 or IPv6 address %s", addr); + return -EINVAL; +#else + ret = resolve_name(ctx, addr, + DNS_QUERY_TYPE_A, timeout); + if (ret < 0) { + ret = resolve_name(ctx, addr, + DNS_QUERY_TYPE_AAAA, + timeout); + if (ret < 0) { + NET_ERR("Cannot resolve %s (%d)", + addr, ret); + return ret; + } + + goto ipv6; + } + + goto ipv4; +#endif /* !CONFIG_DNS_RESOLVER */ + } else { +#if defined(CONFIG_DNS_RESOLVER) + ipv6: +#endif + net_sin6(&ctx->remote)->sin6_port = + htons(peer_port); + net_sin6(&ctx->remote)->sin6_family = AF_INET6; + } + } else { +#if defined(CONFIG_DNS_RESOLVER) + ipv4: +#endif + net_sin(&ctx->remote)->sin_port = htons(peer_port); + net_sin(&ctx->remote)->sin_family = AF_INET; + } +#endif /* IPV4 && IPV6 */ + + /* If we have not yet figured out what is the protocol family, + * then we cannot continue. + */ + if (ctx->remote.family == AF_UNSPEC) { + NET_ERR("Unknown protocol family."); + return -EPFNOSUPPORT; + } + + return 0; +} + +int net_app_init_client(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto, + struct sockaddr *client_addr, + struct sockaddr *peer_addr, + const char *peer_addr_str, + u16_t peer_port, + s32_t timeout, + void *user_data) +{ + struct sockaddr addr; + int ret; + + if (!ctx) { + return -EINVAL; + } + + if (ctx->is_init) { + return -EALREADY; + } + + memset(&addr, 0, sizeof(addr)); + + if (client_addr) { + memcpy(&addr, client_addr, sizeof(addr)); + } else { + addr.family = AF_UNSPEC; + } + + ctx->app_type = NET_APP_CLIENT; + ctx->user_data = user_data; + ctx->send_data = net_context_sendto; + ctx->recv_cb = _net_app_received; + ctx->proto = proto; + ctx->sock_type = sock_type; + + ret = _net_app_config_local_ctx(ctx, sock_type, proto, &addr); + if (ret < 0) { + goto fail; + } + + if (peer_addr) { + memcpy(&ctx->remote, peer_addr, + sizeof(ctx->remote)); + goto out; + } + + if (!peer_addr_str) { + NET_ERR("Cannot know where to connect."); + ret = -EINVAL; + goto fail; + } + + ret = set_remote_addr(ctx, peer_addr_str, peer_port, timeout); + if (ret < 0) { + goto fail; + } + +#if defined(CONFIG_NET_IPV4) + if (ctx->remote.family == AF_INET) { + ctx->local.family = AF_INET; + _net_app_set_local_addr(&ctx->local, NULL, + net_sin(&ctx->local)->sin_port); + + ret = _net_app_set_net_ctx(ctx, ctx->ipv4_ctx, &ctx->local, + sizeof(struct sockaddr_in), + ctx->proto); + if (ret < 0) { + net_context_put(ctx->ipv4_ctx); + ctx->ipv4_ctx = NULL; + } + } +#endif + +#if defined(CONFIG_NET_IPV6) + if (ctx->remote.family == AF_INET6) { + ctx->local.family = AF_INET6; + _net_app_set_local_addr(&ctx->local, NULL, + net_sin6(&ctx->local)->sin6_port); + + ret = _net_app_set_net_ctx(ctx, ctx->ipv6_ctx, &ctx->local, + sizeof(struct sockaddr_in6), + ctx->proto); + if (ret < 0) { + net_context_put(ctx->ipv6_ctx); + ctx->ipv6_ctx = NULL; + } + } +#endif + + _net_app_print_info(ctx); + +out: + ctx->is_init = true; + +fail: + return ret; + +} + +static void _app_connected(struct net_context *net_ctx, + int status, + void *user_data) +{ + struct net_app_ctx *ctx = user_data; + +#if defined(CONFIG_NET_APP_TLS) + if (ctx->is_tls && ctx->proto == IPPROTO_TCP) { + k_sem_give(&ctx->client.connect_wait); + } +#endif + + net_context_recv(net_ctx, ctx->recv_cb, K_NO_WAIT, ctx); + +#if defined(CONFIG_NET_APP_TLS) + if (ctx->is_tls && ctx->proto == IPPROTO_TCP) { + /* If we have TLS connection, the connect cb is called + * after TLS handshakes are done. + */ + NET_DBG("Postponing TLS connection cb for ctx %p", ctx); + } else +#endif + { + if (ctx->cb.connect) { + ctx->cb.connect(ctx, status, ctx->user_data); + } + } +} + +int net_app_connect(struct net_app_ctx *ctx, s32_t timeout) +{ + struct net_context *net_ctx; + bool started = false; + int ret; + + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + + if (ctx->app_type != NET_APP_CLIENT) { + return -EINVAL; + } + + net_ctx = _net_app_select_net_ctx(ctx); + if (!net_ctx) { + return -EAFNOSUPPORT; + } + +#if defined(CONFIG_NET_APP_TLS) + if (ctx->is_tls && ctx->proto == IPPROTO_TCP && !ctx->tls.tid) { + /* TLS thread is not yet running, start it now */ + ret = start_tls_client(ctx); + if (ret < 0) { + NET_DBG("TLS thread cannot be started (%d)", ret); + return ret; + } + + started = true; + + /* Let the TLS thread run first */ + k_yield(); + } +#else + ARG_UNUSED(started); +#endif /* CONFIG_NET_APP_TLS */ + + ret = net_context_connect(net_ctx, + &ctx->remote, + sizeof(ctx->remote), + _app_connected, + timeout, + ctx); + if (ret < 0) { + NET_DBG("Cannot connect to peer (%d)", ret); + +#if defined(CONFIG_NET_APP_TLS) + if (started) { + _net_app_tls_handler_stop(ctx); + } +#endif + } + + return ret; +} + +#if defined(CONFIG_NET_APP_TLS) +static void tls_client_handler(struct net_app_ctx *ctx, + struct k_sem *startup_sync) +{ + int ret; + + NET_DBG("Starting TLS client thread for %p", ctx); + + ret = _net_app_tls_init(ctx, MBEDTLS_SSL_IS_CLIENT); + if (ret < 0) { + NET_DBG("TLS client init failed"); + return; + } + + k_sem_give(startup_sync); + + /* We wait until TLS connection is established */ + k_sem_take(&ctx->client.connect_wait, K_FOREVER); + + _net_app_ssl_mainloop(ctx); + + mbedtls_ssl_close_notify(&ctx->tls.mbedtls.ssl); + + /* If there is any pending data that have not been processed + * yet, we need to free it here. + */ + if (ctx->tls.mbedtls.ssl_ctx.rx_pkt) { + net_pkt_unref(ctx->tls.mbedtls.ssl_ctx.rx_pkt); + ctx->tls.mbedtls.ssl_ctx.rx_pkt = NULL; + ctx->tls.mbedtls.ssl_ctx.frag = NULL; + } + + if (ctx->cb.close) { + ctx->cb.close(ctx, -ESHUTDOWN, ctx->user_data); + } + + _net_app_tls_handler_stop(ctx); +} + +static int start_tls_client(struct net_app_ctx *ctx) +{ + struct k_sem startup_sync; + + /* Start the thread that handles TLS traffic. */ + if (ctx->tls.tid) { + return -EALREADY; + } + + k_sem_init(&startup_sync, 0, 1); + + ctx->tls.tid = k_thread_create(&ctx->tls.thread, + ctx->tls.stack, + ctx->tls.stack_size, + (k_thread_entry_t)tls_client_handler, + ctx, &startup_sync, 0, + K_PRIO_COOP(7), 0, 0); + + /* Wait until we know that the TLS thread startup was ok */ + if (k_sem_take(&startup_sync, TLS_STARTUP_TIMEOUT) < 0) { + _net_app_tls_handler_stop(ctx); + return -ECANCELED; + } + + return 0; +} + +int net_app_client_tls(struct net_app_ctx *ctx, + u8_t *request_buf, + size_t request_buf_len, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_ca_cert_cb_t cert_cb, + const char *cert_host, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + u8_t *stack, + size_t stack_size) +{ + if (!request_buf || request_buf_len == 0) { + NET_ERR("Request buf must be set"); + return -EINVAL; + } + + /* mbedtls cannot receive or send larger buffer as what is defined + * in a file pointed by CONFIG_MBEDTLS_CFG_FILE. + */ + if (request_buf_len > MBEDTLS_SSL_MAX_CONTENT_LEN) { + NET_ERR("Request buf too large, max len is %d", + MBEDTLS_SSL_MAX_CONTENT_LEN); + return -EINVAL; + } + + if (!cert_cb) { + NET_ERR("Cert callback must be set"); + return -EINVAL; + } + + ctx->is_tls = true; + ctx->send_data = _net_app_tls_sendto; + ctx->recv_cb = _net_app_tls_received; + ctx->tls.request_buf = request_buf; + ctx->tls.request_buf_len = request_buf_len; + ctx->tls.cert_host = cert_host; + ctx->tls.stack = stack; + ctx->tls.stack_size = stack_size; + ctx->tls.mbedtls.ca_cert_cb = cert_cb; + ctx->tls.pool = pool; + ctx->tls.mbedtls.personalization_data = personalization_data; + ctx->tls.mbedtls.personalization_data_len = personalization_data_len; + + if (entropy_src_cb) { + ctx->tls.mbedtls.entropy_src_cb = entropy_src_cb; + } else { + ctx->tls.mbedtls.entropy_src_cb = _net_app_entropy_source; + } + + /* The semaphore is released when the client calls net_app_connect() */ + k_sem_init(&ctx->client.connect_wait, 0, 1); + + /* The mbedtls is initialized in TLS thread because of mbedtls stack + * requirements. TLS thread is started when we get the first client + * request to send data. + */ + return 0; +} +#endif /* CONFIG_NET_APP_TLS */ diff --git a/samples/net/common/sample_app_setup.c b/subsys/net/lib/app/init.c similarity index 78% rename from samples/net/common/sample_app_setup.c rename to subsys/net/lib/app/init.c index 5af8acbdf63..2fa96ce79b6 100644 --- a/samples/net/common/sample_app_setup.c +++ b/subsys/net/lib/app/init.c @@ -1,4 +1,4 @@ -/* sample_app_setup.c */ +/* init.c */ /* * Copyright (c) 2017 Intel Corporation. @@ -6,30 +6,26 @@ * SPDX-License-Identifier: Apache-2.0 */ -#if 1 -#define SYS_LOG_DOMAIN "sample/net" +#if defined(CONFIG_NET_DEBUG_APP) +#define SYS_LOG_DOMAIN "net/app" #define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG #define NET_LOG_ENABLED 1 #endif #include +#include +#include +#include +#include #include #include #include #include #include +#include -#include "net_sample_app.h" - -#if defined(CONFIG_NET_L2_BLUETOOTH) -#include -#include -#endif - -#if defined(CONFIG_NET_L2_IEEE802154) -#include -#endif +#include static struct k_sem waiter = K_SEM_INITIALIZER(waiter, 0, 1); static struct k_sem counter; @@ -41,7 +37,9 @@ static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb, u32_t mgmt_event, struct net_if *iface) { +#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 1 char hr_addr[NET_IPV4_ADDR_LEN]; +#endif int i; if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) { @@ -95,7 +93,9 @@ static void setup_dhcpv4(struct net_if *iface) static void setup_ipv4(struct net_if *iface) { +#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 1 char hr_addr[NET_IPV4_ADDR_LEN]; +#endif struct in_addr addr; if (net_addr_pton(AF_INET, CONFIG_NET_APP_MY_IPV4_ADDR, &addr)) { @@ -128,7 +128,9 @@ static void ipv6_event_handler(struct net_mgmt_event_callback *cb, u32_t mgmt_event, struct net_if *iface) { if (mgmt_event == NET_EVENT_IPV6_DAD_SUCCEED) { +#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 1 char hr_addr[NET_IPV6_ADDR_LEN]; +#endif struct net_if_addr *ifaddr; ifaddr = net_if_ipv6_addr_lookup(&laddr, &iface); @@ -163,7 +165,7 @@ static void setup_ipv6(struct net_if *iface, u32_t flags) return; } - if (flags & NET_SAMPLE_NEED_ROUTER) { + if (flags & NET_APP_NEED_ROUTER) { mask |= NET_EVENT_IPV6_ROUTER_ADD; } @@ -182,7 +184,7 @@ static void setup_ipv6(struct net_if *iface, u32_t flags) #define setup_ipv6(...) #endif /* CONFIG_NET_IPV6 */ -int net_sample_app_init(const char *app_info, u32_t flags, s32_t timeout) +int net_app_init(const char *app_info, u32_t flags, s32_t timeout) { #define LOOP_DEVIDER 10 struct net_if *iface = net_if_get_default(); @@ -193,28 +195,11 @@ int net_sample_app_init(const char *app_info, u32_t flags, s32_t timeout) NET_INFO("%s", app_info); } -#if defined(CONFIG_NET_L2_BLUETOOTH) - if (bt_enable(NULL)) { - NET_ERR("Bluetooth init failed"); - return -EFAULT; - } else { - ipss_init(); - ipss_advertise(); - } -#endif - -#if defined(CONFIG_NET_L2_IEEE802154) - if (ieee802154_sample_setup()) { - NET_ERR("IEEE 802.15.4 setup failed"); - return -EFAULT; - } -#endif - - if (flags & NET_SAMPLE_NEED_IPV6) { + if (flags & NET_APP_NEED_IPV6) { count++; } - if (flags & NET_SAMPLE_NEED_IPV4) { + if (flags & NET_APP_NEED_IPV4) { count++; } @@ -252,3 +237,36 @@ int net_sample_app_init(const char *app_info, u32_t flags, s32_t timeout) return 0; } + +#if defined(CONFIG_NET_APP_AUTO_INIT) +static int init_net_app(struct device *device) +{ + u32_t flags = 0; + int ret; + + ARG_UNUSED(device); + + if (IS_ENABLED(CONFIG_NET_APP_NEED_IPV6)) { + flags |= NET_APP_NEED_IPV6; + } + + if (IS_ENABLED(CONFIG_NET_APP_NEED_IPV6_ROUTER)) { + flags |= NET_APP_NEED_ROUTER; + } + + if (IS_ENABLED(CONFIG_NET_APP_NEED_IPV4)) { + flags |= NET_APP_NEED_IPV4; + } + + /* Initialize the application automatically if needed */ + ret = net_app_init("Initializing network", flags, + K_SECONDS(CONFIG_NET_APP_INIT_TIMEOUT)); + if (ret < 0) { + NET_ERR("Network initialization failed (%d)", ret); + } + + return ret; +} + +SYS_INIT(init_net_app, APPLICATION, CONFIG_NET_APP_INIT_PRIO); +#endif /* CONFIG_NET_APP_AUTO_INIT */ diff --git a/subsys/net/lib/app/net_app.c b/subsys/net/lib/app/net_app.c new file mode 100644 index 00000000000..05e7e232a1b --- /dev/null +++ b/subsys/net/lib/app/net_app.c @@ -0,0 +1,1259 @@ +/* net_app.c */ + +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_NET_DEBUG_APP) +#define SYS_LOG_DOMAIN "net/app" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "net_app_private.h" + +#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) +int net_app_set_net_pkt_pool(struct net_app_ctx *ctx, + net_pkt_get_slab_func_t tx_slab, + net_pkt_get_pool_func_t data_pool) +{ + ctx->tx_slab = tx_slab; + ctx->data_pool = data_pool; + + return 0; +} +#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ + +#if defined(CONFIG_NET_DEBUG_APP) +char *_net_app_sprint_ipaddr(char *buf, int buflen, + const struct sockaddr *addr) +{ + if (addr->family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + char ipaddr[NET_IPV6_ADDR_LEN]; + + net_addr_ntop(addr->family, + &net_sin6(addr)->sin6_addr, + ipaddr, sizeof(ipaddr)); + snprintk(buf, buflen, "[%s]:%u", ipaddr, + ntohs(net_sin6(addr)->sin6_port)); +#endif + } else if (addr->family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + char ipaddr[NET_IPV4_ADDR_LEN]; + + net_addr_ntop(addr->family, + &net_sin(addr)->sin_addr, + ipaddr, sizeof(ipaddr)); + snprintk(buf, buflen, "%s:%u", ipaddr, + ntohs(net_sin(addr)->sin_port)); +#endif + } + + return buf; +} +#endif /* CONFIG_NET_DEBUG_APP */ + +#if defined(CONFIG_NET_APP_SERVER) || defined(CONFIG_NET_APP_CLIENT) +void _net_app_received(struct net_context *net_ctx, + struct net_pkt *pkt, + int status, + void *user_data) +{ + struct net_app_ctx *ctx = user_data; + +#if defined(CONFIG_NET_APP_CLIENT) + if (ctx->app_type == NET_APP_CLIENT) { + if (!pkt) { + if (ctx->cb.close) { + ctx->cb.close(ctx, status, ctx->user_data); + } + + return; + } + + if (ctx->cb.recv) { + ctx->cb.recv(ctx, pkt, status, ctx->user_data); + } + } +#endif + +#if defined(CONFIG_NET_APP_SERVER) + if (ctx->app_type == NET_APP_SERVER) { + if (!pkt) { + if (ctx->cb.close) { + ctx->cb.close(ctx, status, ctx->user_data); + } + + if (ctx->proto == IPPROTO_TCP) { + net_context_put(ctx->server.net_ctx); + ctx->server.net_ctx = NULL; + } + + return; + } + + if (ctx->cb.recv) { + ctx->cb.recv(ctx, pkt, status, ctx->user_data); + } + } +#endif +} +#endif /* CONFIG_NET_APP_SERVER || CONFIG_NET_APP_CLIENT */ + +#if defined(CONFIG_NET_APP_SERVER) || defined(CONFIG_NET_APP_CLIENT) +int _net_app_set_net_ctx(struct net_app_ctx *ctx, + struct net_context *net_ctx, + struct sockaddr *addr, + socklen_t socklen, + enum net_ip_protocol proto) +{ + int ret; + + ret = net_context_bind(net_ctx, addr, socklen); + if (ret < 0) { + NET_ERR("Cannot bind context (%d)", ret); + goto out; + } + +#if defined(CONFIG_NET_APP_SERVER) && defined(CONFIG_NET_TCP) + if (ctx->app_type == NET_APP_SERVER && proto == IPPROTO_TCP) { + ret = net_context_listen(net_ctx, 0); + if (ret < 0) { + NET_ERR("Cannot listen context (%d)", ret); + goto out; + } + + ret = net_context_accept(net_ctx, _net_app_accept_cb, + K_NO_WAIT, ctx); + if (ret < 0) { + NET_ERR("Cannot accept context (%d)", ret); + goto out; + } + + /* TCP recv callback is set after we have accepted the + * connection. + */ + } +#endif /* CONFIG_NET_APP_SERVER && CONFIG_NET_TCP */ + +#if defined(CONFIG_NET_APP_SERVER) && defined(CONFIG_NET_UDP) + if (ctx->app_type == NET_APP_SERVER && proto == IPPROTO_UDP) { + net_context_recv(net_ctx, _net_app_received, K_NO_WAIT, ctx); + } +#endif /* CONFIG_NET_APP_SERVER && CONFIG_NET_UDP */ + +out: + return ret; +} + +int _net_app_set_local_addr(struct sockaddr *addr, const char *myaddr, + u16_t port) +{ + if (myaddr) { + void *inaddr; + + if (addr->family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + inaddr = &net_sin(addr)->sin_addr; + net_sin(addr)->sin_port = htons(port); +#else + return -EPFNOSUPPORT; +#endif + } else if (addr->family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + inaddr = &net_sin6(addr)->sin6_addr; + net_sin6(addr)->sin6_port = htons(port); +#else + return -EPFNOSUPPORT; +#endif + } else { + return -EAFNOSUPPORT; + } + + return net_addr_pton(addr->family, myaddr, inaddr); + } + + /* If the caller did not supply the address where to bind, then + * try to figure it out ourselves. + */ + if (addr->family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + net_ipaddr_copy(&net_sin6(addr)->sin6_addr, + net_if_ipv6_select_src_addr(NULL, + (struct in6_addr *) + net_ipv6_unspecified_address())); +#else + return -EPFNOSUPPORT; +#endif + } else if (addr->family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + struct net_if *iface = net_if_get_default(); + + /* For IPv4 we take the first address in the interface */ + net_ipaddr_copy(&net_sin(addr)->sin_addr, + &iface->ipv4.unicast[0].address.in_addr); +#else + return -EPFNOSUPPORT; +#endif + } + + return 0; +} +#endif /* CONFIG_NET_APP_SERVER || CONFIG_NET_APP_CLIENT */ + +#if defined(CONFIG_NET_APP_SERVER) +#endif /* CONFIG_NET_APP_SERVER */ + +#if defined(CONFIG_NET_IPV4) && (defined(CONFIG_NET_APP_SERVER) || \ + defined(CONFIG_NET_APP_CLIENT)) +static int setup_ipv4_ctx(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto) +{ + int ret; + + ret = net_context_get(AF_INET, sock_type, proto, &ctx->ipv4_ctx); + if (ret < 0) { + NET_ERR("Cannot get network context (%d)", ret); + ctx->ipv4_ctx = NULL; + return ret; + } + + net_context_setup_pools(ctx->ipv4_ctx, ctx->tx_slab, + ctx->data_pool); + + return ret; +} +#endif /* CONFIG_NET_IPV4 */ + +#if defined(CONFIG_NET_IPV6) && (defined(CONFIG_NET_APP_SERVER) || \ + defined(CONFIG_NET_APP_CLIENT)) +static int setup_ipv6_ctx(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto) +{ + int ret; + + ret = net_context_get(AF_INET6, sock_type, proto, &ctx->ipv6_ctx); + if (ret < 0) { + NET_ERR("Cannot get network context (%d)", ret); + ctx->ipv6_ctx = NULL; + return ret; + } + + net_context_setup_pools(ctx->ipv6_ctx, ctx->tx_slab, + ctx->data_pool); + + return ret; +} +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_APP_SERVER) || defined(CONFIG_NET_APP_CLIENT) +int _net_app_config_local_ctx(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto, + struct sockaddr *addr) +{ + int ret; + + if (addr->family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + ret = setup_ipv6_ctx(ctx, sock_type, proto); +#else + ret = -EPFNOSUPPORT; + goto fail; +#endif + } else if (addr->family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + ret = setup_ipv4_ctx(ctx, sock_type, proto); +#else + ret = -EPFNOSUPPORT; + goto fail; +#endif + } else if (addr->family == AF_UNSPEC) { +#if defined(CONFIG_NET_IPV4) + ret = setup_ipv4_ctx(ctx, sock_type, proto); +#endif + /* We ignore the IPv4 error if IPv6 is enabled */ +#if defined(CONFIG_NET_IPV6) + ret = setup_ipv6_ctx(ctx, sock_type, proto); +#endif + } else { + ret = -EINVAL; + goto fail; + } + +fail: + return ret; +} +#endif /* CONFIG_NET_APP_SERVER || CONFIG_NET_APP_CLIENT */ + +int net_app_release(struct net_app_ctx *ctx) +{ + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + +#if defined(CONFIG_NET_IPV6) + if (ctx->ipv6_ctx) { + net_context_put(ctx->ipv6_ctx); + ctx->ipv6_ctx = NULL; + } +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) + if (ctx->ipv4_ctx) { + net_context_put(ctx->ipv4_ctx); + ctx->ipv4_ctx = NULL; + } +#endif /* CONFIG_NET_IPV4 */ + + ctx->is_init = false; + + return 0; +} + +struct net_context *_net_app_select_net_ctx(struct net_app_ctx *ctx) +{ +#if defined(CONFIG_NET_APP_CLIENT) + if (ctx->app_type == NET_APP_CLIENT) { + if (ctx->remote.family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + return ctx->ipv6_ctx; +#endif + } else if (ctx->remote.family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + return ctx->ipv4_ctx; +#endif + } + } +#endif /* CONFIG_NET_APP_CLIENT */ + +#if defined(CONFIG_NET_APP_SERVER) + if (ctx->app_type == NET_APP_SERVER) { + if (ctx->proto == IPPROTO_TCP) { + return ctx->server.net_ctx; + } else if (ctx->proto == IPPROTO_UDP) { + if (ctx->local.family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + return ctx->ipv6_ctx; +#endif + } else if (ctx->local.family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + return ctx->ipv4_ctx; +#endif + } + } + } +#endif /* CONFIG_NET_APP_SERVER */ + + return NULL; +} + +int net_app_set_cb(struct net_app_ctx *ctx, + net_app_connect_cb_t connect_cb, + net_app_recv_cb_t recv_cb, + net_app_send_cb_t send_cb, + net_app_close_cb_t close_cb) +{ + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + + ctx->cb.connect = connect_cb; + ctx->cb.recv = recv_cb; + ctx->cb.send = send_cb; + ctx->cb.close = close_cb; + + return 0; +} + +static void _app_send(struct net_context *net_ctx, + int status, + void *token, + void *user_data) +{ + struct net_app_ctx *ctx = user_data; + + ARG_UNUSED(ctx); + +#if defined(CONFIG_NET_APP_CLIENT) + if (ctx->app_type == NET_APP_CLIENT && ctx->cb.send) { + ctx->cb.send(ctx, status, token, ctx->user_data); + } +#endif + +#if defined(CONFIG_NET_APP_SERVER) + if (ctx->app_type == NET_APP_SERVER && ctx->cb.send) { + ctx->cb.send(ctx, status, token, ctx->user_data); + } +#endif +} + +int net_app_send_pkt(struct net_app_ctx *ctx, + struct net_pkt *pkt, + const struct sockaddr *dst, + socklen_t dst_len, + s32_t timeout, + void *user_data_send) +{ + int ret; + + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + + net_pkt_set_appdatalen(pkt, net_buf_frags_len(pkt->frags)); + + if (!dst && ctx->proto == IPPROTO_UDP) { + dst = &ctx->remote; + + if (ctx->remote.family == AF_INET) { + dst_len = sizeof(struct sockaddr_in); + } else { + dst_len = sizeof(struct sockaddr_in6); + } + } + + ret = ctx->send_data(pkt, dst, dst_len, _app_send, timeout, + user_data_send, ctx); + if (ret < 0) { + NET_DBG("Cannot send to peer (%d)", ret); + } + + return ret; +} + +int net_app_send_buf(struct net_app_ctx *ctx, + u8_t *buf, + size_t buf_len, + const struct sockaddr *dst, + socklen_t dst_len, + s32_t timeout, + void *user_data_send) +{ + struct net_context *net_ctx; + struct net_pkt *pkt; + struct net_buf *frag; + size_t len, pos = 0; + int ret; + + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + + if (!buf_len) { + return -EMSGSIZE; + } + + net_ctx = _net_app_select_net_ctx(ctx); + if (!net_ctx) { + return -ENOENT; + } + + pkt = net_pkt_get_tx(net_ctx, timeout); + if (!pkt) { + return -ENOMEM; + } + + net_pkt_set_appdatalen(pkt, buf_len); + + while (buf_len) { + frag = net_pkt_get_data(net_ctx, timeout); + if (!frag) { + net_pkt_unref(pkt); + return -ENOMEM; + } + + len = net_buf_tailroom(frag); + if (len >= buf_len) { + net_buf_add_mem(frag, buf + pos, buf_len); + net_pkt_frag_add(pkt, frag); + goto send; + } + + net_buf_add_mem(frag, buf + pos, len); + net_pkt_frag_add(pkt, frag); + + pos += len; + buf_len -= len; + } + +send: + ret = ctx->send_data(pkt, dst, dst_len, _app_send, timeout, + user_data_send, ctx); + if (ret < 0) { + NET_DBG("Cannot send to peer (%d)", ret); + net_pkt_unref(pkt); + } + + return ret; +} + +struct net_pkt *net_app_get_net_pkt(struct net_app_ctx *ctx, + s32_t timeout) +{ + struct net_context *net_ctx; + + if (!ctx) { + return NULL; + } + + if (!ctx->is_init) { + return NULL; + } + + net_ctx = _net_app_select_net_ctx(ctx); + if (!net_ctx) { + return NULL; + } + + return net_pkt_get_tx(net_ctx, timeout); +} + +struct net_buf *net_app_get_net_buf(struct net_app_ctx *ctx, + struct net_pkt *pkt, + s32_t timeout) +{ + if (!ctx || !pkt) { + return NULL; + } + + if (!ctx->is_init) { + return NULL; + } + + return net_pkt_get_frag(pkt, timeout); +} + +int net_app_close(struct net_app_ctx *ctx) +{ + struct net_context *net_ctx; + + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + + net_ctx = _net_app_select_net_ctx(ctx); + if (!net_ctx) { + return -EAFNOSUPPORT; + } + + if (ctx->cb.close) { + ctx->cb.close(ctx, 0, ctx->user_data); + } + +#if defined(CONFIG_NET_APP_SERVER) + if (ctx->app_type == NET_APP_SERVER) { + ctx->server.net_ctx = NULL; + } +#endif + + net_context_put(net_ctx); + + return 0; +} + +#if defined(CONFIG_NET_APP_TLS) +#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_APP) +static void my_debug(void *ctx, int level, + const char *file, int line, const char *str) +{ + const char *p, *basename; + int len; + + ARG_UNUSED(ctx); + + /* Extract basename from file */ + for (p = basename = file; *p != '\0'; p++) { + if (*p == '/' || *p == '\\') { + basename = p + 1; + } + + } + + /* Avoid printing double newlines */ + len = strlen(str); + if (str[len - 1] == '\n') { + ((char *)str)[len - 1] = '\0'; + } + + NET_DBG("%s:%04d: |%d| %s", basename, line, level, str); +} +#endif /* MBEDTLS_DEBUG_C && CONFIG_NET_DEBUG_APP */ + +static void ssl_sent(struct net_context *context, + int status, void *token, void *user_data) +{ + struct net_app_ctx *ctx = user_data; + + k_sem_give(&ctx->tls.mbedtls.ssl_ctx.tx_sem); +} + +/* Send encrypted data */ +int _net_app_ssl_tx(void *context, const unsigned char *buf, size_t size) +{ + struct net_app_ctx *ctx = context; + struct net_pkt *send_buf; + int ret, len; + + send_buf = net_app_get_net_pkt(ctx, BUF_ALLOC_TIMEOUT); + if (!send_buf) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + ret = net_pkt_append_all(send_buf, size, (u8_t *)buf, + BUF_ALLOC_TIMEOUT); + if (!ret) { + /* Cannot append data */ + net_pkt_unref(send_buf); + return 0; + } + + len = size; + + ret = net_context_send(send_buf, ssl_sent, K_NO_WAIT, NULL, ctx); + if (ret < 0) { + net_pkt_unref(send_buf); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + k_sem_take(&ctx->tls.mbedtls.ssl_ctx.tx_sem, K_FOREVER); + + return len; +} + +/* This gets plain data and then it passes it to TLS handler thread to be + * encrypted and transmitted to peer. Note that we do not send the data + * directly here because of the mbedtls stack requirements which are quite + * high. So no calls to mbedtls from this processing context. + */ +int _net_app_tls_sendto(struct net_pkt *pkt, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data) +{ + struct net_app_ctx *ctx = user_data; + struct net_app_fifo_block *tx_data; + struct k_mem_block block; + int ret; + + ARG_UNUSED(dst_addr); + ARG_UNUSED(addrlen); + + if (pkt && !net_pkt_appdatalen(pkt)) { + return -EINVAL; + } + + ret = k_mem_pool_alloc(ctx->tls.pool, &block, + sizeof(struct net_app_fifo_block), + BUF_ALLOC_TIMEOUT); + if (ret < 0) { + return -ENOMEM; + } + + tx_data = block.data; + tx_data->pkt = pkt; + tx_data->dir = NET_APP_PKT_TX; + tx_data->token = token; + tx_data->cb = cb; + + /* For freeing memory later */ + memcpy(&tx_data->block, &block, sizeof(struct k_mem_block)); + + k_fifo_put(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, (void *)tx_data); + + return 0; +} + +/* Receive encrypted data from network. Put that data into fifo + * that will be read by tls thread. + */ +void _net_app_tls_received(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data) +{ + struct net_app_ctx *ctx = user_data; + struct net_app_fifo_block *rx_data = NULL; + struct k_mem_block block; + int ret; + + ARG_UNUSED(context); + ARG_UNUSED(status); + + if (pkt && !net_pkt_appdatalen(pkt)) { + net_pkt_unref(pkt); + return; + } + + ret = k_mem_pool_alloc(ctx->tls.pool, &block, + sizeof(struct net_app_fifo_block), + BUF_ALLOC_TIMEOUT); + if (ret < 0) { + if (pkt) { + net_pkt_unref(pkt); + } + + NET_DBG("Not enough space in TLS mem pool"); + return; + } + + rx_data = block.data; + rx_data->pkt = pkt; + rx_data->dir = NET_APP_PKT_RX; + + /* For freeing memory later */ + memcpy(&rx_data->block, &block, sizeof(struct k_mem_block)); + + k_fifo_put(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, (void *)rx_data); +} + +static int tls_sendto(struct net_app_ctx *ctx, + struct net_app_fifo_block *tx_data) +{ + u16_t len; + int ret; + + len = net_pkt_appdatalen(tx_data->pkt); + if (len == 0) { + ret = -EINVAL; + goto out; + } + + ret = net_frag_linearize(ctx->tls.request_buf, + ctx->tls.request_buf_len, + tx_data->pkt, + net_pkt_ip_hdr_len(tx_data->pkt), + len); + if (ret < 0) { + NET_DBG("Cannot linearize send data (%d)", ret); + goto out; + } + + if (ret != len) { + NET_DBG("Linear copy error (%u vs %d)", len, ret); + ret = -EINVAL; + goto out; + } + + do { + ret = mbedtls_ssl_write(&ctx->tls.mbedtls.ssl, + ctx->tls.request_buf, len); + if (ret == MBEDTLS_ERR_NET_CONN_RESET) { + _net_app_print_error( + "peer closed the connection -0x%x", ret); + goto out; + } + + if (ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + if (ret < 0) { + _net_app_print_error( + "mbedtls_ssl_write returned -0x%x", + ret); + goto out; + } + } + } while (ret <= 0); + +out: + if (tx_data->cb) { + tx_data->cb(net_pkt_context(tx_data->pkt), ret, + tx_data->token, ctx); + } + + net_pkt_unref(tx_data->pkt); + + return ret; +} + +/* This will copy data from received net_pkt buf into mbedtls internal buffers. + */ +int _net_app_ssl_mux(void *context, unsigned char *buf, size_t size) +{ + struct net_app_ctx *ctx = context; + struct net_app_fifo_block *rx_data; + u16_t read_bytes; + u8_t *ptr; + int pos; + int len; + int ret = 0; + + if (!ctx->tls.mbedtls.ssl_ctx.frag) { + again: + rx_data = k_fifo_get(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, + K_FOREVER); + if (!rx_data->pkt) { + NET_DBG("Closing %p connection", ctx); + k_mem_pool_free(&rx_data->block); + return -EIO; + } + + /* If the fifo contains something we need to send, then try + * to send it here and then go back waiting more data. + */ + if (rx_data->dir == NET_APP_PKT_TX) { + tls_sendto(ctx, rx_data); + k_mem_pool_free(&rx_data->block); + goto again; + } + + ctx->tls.mbedtls.ssl_ctx.rx_pkt = rx_data->pkt; + + k_mem_pool_free(&rx_data->block); + + read_bytes = net_pkt_appdatalen( + ctx->tls.mbedtls.ssl_ctx.rx_pkt); + + ctx->tls.mbedtls.ssl_ctx.remaining = read_bytes; + ctx->tls.mbedtls.ssl_ctx.frag = + ctx->tls.mbedtls.ssl_ctx.rx_pkt->frags; + + ptr = net_pkt_appdata(ctx->tls.mbedtls.ssl_ctx.rx_pkt); + len = ptr - ctx->tls.mbedtls.ssl_ctx.frag->data; + + if (len > ctx->tls.mbedtls.ssl_ctx.frag->size) { + NET_ERR("Buf overflow (%d > %u)", len, + ctx->tls.mbedtls.ssl_ctx.frag->size); + return -EINVAL; + } + + /* This will get rid of IP header */ + net_buf_pull(ctx->tls.mbedtls.ssl_ctx.frag, len); + } else { + read_bytes = ctx->tls.mbedtls.ssl_ctx.remaining; + ptr = ctx->tls.mbedtls.ssl_ctx.frag->data; + } + + len = ctx->tls.mbedtls.ssl_ctx.frag->len; + pos = 0; + if (read_bytes > size) { + while (ctx->tls.mbedtls.ssl_ctx.frag) { + read_bytes = len < (size - pos) ? len : (size - pos); + +#if RX_EXTRA_DEBUG == 1 + NET_DBG("Copying %d bytes", read_bytes); +#endif + + memcpy(buf + pos, ptr, read_bytes); + + pos += read_bytes; + if (pos < size) { + ctx->tls.mbedtls.ssl_ctx.frag = + ctx->tls.mbedtls.ssl_ctx.frag->frags; + ptr = ctx->tls.mbedtls.ssl_ctx.frag->data; + len = ctx->tls.mbedtls.ssl_ctx.frag->len; + } else { + if (read_bytes == len) { + ctx->tls.mbedtls.ssl_ctx.frag = + ctx->tls.mbedtls.ssl_ctx.frag->frags; + } else { + net_buf_pull( + ctx->tls.mbedtls.ssl_ctx.frag, + read_bytes); + } + + ctx->tls.mbedtls.ssl_ctx.remaining -= size; + return size; + } + } + } else { + while (ctx->tls.mbedtls.ssl_ctx.frag) { +#if RX_EXTRA_DEBUG == 1 + NET_DBG("Copying all %d bytes", len); +#endif + + memcpy(buf + pos, ptr, len); + + pos += len; + ctx->tls.mbedtls.ssl_ctx.frag = + ctx->tls.mbedtls.ssl_ctx.frag->frags; + if (!ctx->tls.mbedtls.ssl_ctx.frag) { + break; + } + + ptr = ctx->tls.mbedtls.ssl_ctx.frag->data; + len = ctx->tls.mbedtls.ssl_ctx.frag->len; + } + + net_pkt_unref(ctx->tls.mbedtls.ssl_ctx.rx_pkt); + ctx->tls.mbedtls.ssl_ctx.rx_pkt = NULL; + ctx->tls.mbedtls.ssl_ctx.frag = NULL; + ctx->tls.mbedtls.ssl_ctx.remaining = 0; + + if (read_bytes != pos) { + return -EIO; + } + + ret = read_bytes; + } + + return ret; +} + +int _net_app_entropy_source(void *data, unsigned char *output, size_t len, + size_t *olen) +{ + u32_t seed; + + ARG_UNUSED(data); + + seed = sys_rand32_get(); + + if (len > sizeof(seed)) { + len = sizeof(seed); + } + + memcpy(output, &seed, len); + + *olen = len; + return 0; +} + +#if defined(CONFIG_NET_DEBUG_APP) +void _net_app_print_info(struct net_app_ctx *ctx) +{ +#define PORT_STR_LEN sizeof("[]:xxxxx") + char local[NET_IPV6_ADDR_LEN + PORT_STR_LEN]; + char remote[NET_IPV6_ADDR_LEN + PORT_STR_LEN]; + + _net_app_sprint_ipaddr(local, sizeof(local), &ctx->local); + _net_app_sprint_ipaddr(remote, sizeof(remote), &ctx->remote); + + NET_DBG("net app connect %s %s %s", + local, + ctx->app_type == NET_APP_CLIENT ? "->" : "<-", + remote); +} +#endif + +int _net_app_ssl_mainloop(struct net_app_ctx *ctx) +{ + size_t len; + int ret; + +reset: + mbedtls_ssl_session_reset(&ctx->tls.mbedtls.ssl); + mbedtls_ssl_set_bio(&ctx->tls.mbedtls.ssl, ctx, + _net_app_ssl_tx, _net_app_ssl_mux, NULL); + + /* SSL handshake. The ssl_rx() function will be called next by + * mbedtls library. The ssl_rx() will block and wait that data is + * received by ssl_received() and passed to it via fifo. After + * receiving the data, this function will then proceed with secure + * connection establishment. + */ + /* Waiting SSL handshake */ + do { + ret = mbedtls_ssl_handshake(&ctx->tls.mbedtls.ssl); + if (ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + if (ret < 0) { + goto close; + } + } + } while (ret != 0); + + /* We call the connect cb only once for each connection. The TLS + * might require new handshakes etc, but application does not need + * to care about that. + */ + if (!ctx->tls.connect_cb_called && ctx->cb.connect) { + NET_DBG("Calling connect cb for ctx %p", ctx); + ctx->cb.connect(ctx, 0, ctx->user_data); + ctx->tls.connect_cb_called = true; + } + + do { + again: + len = ctx->tls.request_buf_len - 1; + memset(ctx->tls.request_buf, 0, ctx->tls.request_buf_len); + + ret = mbedtls_ssl_read(&ctx->tls.mbedtls.ssl, + ctx->tls.request_buf, len); + if (ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + continue; + } + + if (ret <= 0) { + switch (ret) { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + NET_DBG("Connection was closed gracefully"); + goto close; + + case MBEDTLS_ERR_NET_CONN_RESET: + NET_DBG("Connection was reset by peer"); + break; + + case -EIO: + break; + + default: + _net_app_print_error( + "mbedtls_ssl_read returned -0x%x", + ret); + break; + } + + goto close; + } + + if (ctx->cb.recv) { + struct net_pkt *pkt; + int len = ret; + + pkt = net_pkt_get_rx(_net_app_select_net_ctx(ctx), + BUF_ALLOC_TIMEOUT); + if (!pkt) { + ret = -ENOMEM; + goto close; + } + + ret = net_pkt_append_all(pkt, len, + ctx->tls.request_buf, + BUF_ALLOC_TIMEOUT); + if (!ret) { + /* Not all data was appended */ + net_pkt_unref(pkt); + ret = -ENOMEM; + goto close; + } + + net_pkt_set_appdatalen(pkt, len); + net_pkt_set_appdata(pkt, pkt->frags->data); + + ctx->cb.recv(ctx, pkt, 0, ctx->user_data); + + goto again; + } + + } while (ret < 0); + + /* Read another message */ + goto reset; + +close: + /* The -EIO code means that the connection was closed. The error + * value is not known by mbedtls so do not print info about it. + */ + if (ret != -EIO) { + _net_app_print_error("Closing connection -0x%x", ret); + } + + return ret; +} + +int _net_app_tls_init(struct net_app_ctx *ctx, int client_or_server) +{ + int ret; + + k_fifo_init(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo); + k_sem_init(&ctx->tls.mbedtls.ssl_ctx.tx_sem, 0, UINT_MAX); + + mbedtls_platform_set_printf(printk); + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + if (client_or_server == MBEDTLS_SSL_IS_SERVER) { +#if defined(CONFIG_NET_APP_SERVER) + mbedtls_x509_crt_init(&ctx->tls.mbedtls.srvcert); +#endif + } else { +#if defined(CONFIG_NET_APP_CLIENT) + mbedtls_x509_crt_init(&ctx->tls.mbedtls.ca_cert); +#endif + } +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(CONFIG_NET_APP_SERVER) + if (client_or_server == MBEDTLS_SSL_IS_SERVER) { + mbedtls_pk_init(&ctx->tls.mbedtls.pkey); + } +#endif + + mbedtls_ssl_init(&ctx->tls.mbedtls.ssl); + mbedtls_ssl_config_init(&ctx->tls.mbedtls.conf); + mbedtls_entropy_init(&ctx->tls.mbedtls.entropy); + mbedtls_ctr_drbg_init(&ctx->tls.mbedtls.ctr_drbg); + +#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_APP) + mbedtls_debug_set_threshold(DEBUG_THRESHOLD); + mbedtls_ssl_conf_dbg(&ctx->tls.mbedtls.conf, my_debug, NULL); +#endif + + /* Seed the RNG */ + mbedtls_entropy_add_source(&ctx->tls.mbedtls.entropy, + ctx->tls.mbedtls.entropy_src_cb, + NULL, + MBEDTLS_ENTROPY_MAX_GATHER, + MBEDTLS_ENTROPY_SOURCE_STRONG); + + ret = mbedtls_ctr_drbg_seed( + &ctx->tls.mbedtls.ctr_drbg, + mbedtls_entropy_func, + &ctx->tls.mbedtls.entropy, + (const unsigned char *)ctx->tls.mbedtls.personalization_data, + ctx->tls.mbedtls.personalization_data_len); + if (ret != 0) { + _net_app_print_error("mbedtls_ctr_drbg_seed returned -0x%x", + ret); + goto exit; + } + + /* Setup SSL defaults etc. */ + ret = mbedtls_ssl_config_defaults(&ctx->tls.mbedtls.conf, + client_or_server, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + _net_app_print_error("mbedtls_ssl_config_defaults " + "returned -0x%x", ret); + goto exit; + } + + mbedtls_ssl_conf_rng(&ctx->tls.mbedtls.conf, + mbedtls_ctr_drbg_random, + &ctx->tls.mbedtls.ctr_drbg); + + if (client_or_server == MBEDTLS_SSL_IS_SERVER) { + /* Load the certificates and private RSA key. This needs to be + * done by the user so we call a callback that user must have + * provided. + */ +#if defined(CONFIG_NET_APP_SERVER) + ret = ctx->tls.mbedtls.cert_cb(ctx, &ctx->tls.mbedtls.srvcert, + &ctx->tls.mbedtls.pkey); + if (ret != 0) { + goto exit; + } +#endif + } else { +#if defined(CONFIG_NET_APP_CLIENT) + ret = ctx->tls.mbedtls.ca_cert_cb(ctx, + &ctx->tls.mbedtls.ca_cert); + if (ret != 0) { + goto exit; + } +#endif + } + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(CONFIG_NET_APP_SERVER) + if (client_or_server == MBEDTLS_SSL_IS_SERVER) { + mbedtls_ssl_conf_ca_chain(&ctx->tls.mbedtls.conf, + ctx->tls.mbedtls.srvcert.next, + NULL); + + ret = mbedtls_ssl_conf_own_cert(&ctx->tls.mbedtls.conf, + &ctx->tls.mbedtls.srvcert, + &ctx->tls.mbedtls.pkey); + if (ret != 0) { + _net_app_print_error("mbedtls_ssl_conf_own_cert " + "returned -0x%x", ret); + goto exit; + } + } +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + ret = mbedtls_ssl_setup(&ctx->tls.mbedtls.ssl, + &ctx->tls.mbedtls.conf); + if (ret != 0) { + _net_app_print_error("mbedtls_ssl_setup returned -0x%x", ret); + goto exit; + } + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(CONFIG_NET_APP_CLIENT) + if (client_or_server == MBEDTLS_SSL_IS_CLIENT && + ctx->tls.cert_host) { + ret = mbedtls_ssl_set_hostname(&ctx->tls.mbedtls.ssl, + ctx->tls.cert_host); + if (ret != 0) { + _net_app_print_error( + "mbedtls_ssl_set_hostname returned -0x%x", + ret); + goto exit; + } + } +#endif + + NET_DBG("SSL %s setup done", + client_or_server == MBEDTLS_SSL_IS_CLIENT ? "client" : + "server"); + +exit: + /* The mbedtls resources are freed by _net_app_tls_handler_stop() + * which is called if this routine returns < 0 + */ + return ret; +} + +void _net_app_tls_handler_stop(struct net_app_ctx *ctx) +{ + mbedtls_ssl_free(&ctx->tls.mbedtls.ssl); + mbedtls_ssl_config_free(&ctx->tls.mbedtls.conf); + mbedtls_ctr_drbg_free(&ctx->tls.mbedtls.ctr_drbg); + mbedtls_entropy_free(&ctx->tls.mbedtls.entropy); + + /* Empty the fifo just in case there is any received packets + * still there. + */ + while (1) { + struct net_app_fifo_block *tx_rx_data; + + tx_rx_data = k_fifo_get(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, + K_NO_WAIT); + if (!tx_rx_data) { + break; + } + + net_pkt_unref(tx_rx_data->pkt); + + k_mem_pool_free(&tx_rx_data->block); + } + + NET_DBG("TLS thread %p stopped", ctx->tls.tid); + + k_thread_abort(ctx->tls.tid); + ctx->tls.tid = 0; +} +#endif /* CONFIG_NET_APP_TLS */ + diff --git a/subsys/net/lib/app/net_app_private.h b/subsys/net/lib/app/net_app_private.h new file mode 100644 index 00000000000..28c1f151c0a --- /dev/null +++ b/subsys/net/lib/app/net_app_private.h @@ -0,0 +1,113 @@ +/** @file + * @brief Private net_api API routines + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Print extra info about received TLS data */ +#define RX_EXTRA_DEBUG 0 + +#if defined(MBEDTLS_DEBUG_C) +#include +/* - Debug levels (from ext/lib/crypto/mbedtls/include/mbedtls/debug.h) + * - 0 No debug + * - 1 Error + * - 2 State change + * - 3 Informational + * - 4 Verbose + */ +#if defined(CONFIG_NET_DEBUG_APP_TLS_LEVEL) +#define DEBUG_THRESHOLD CONFIG_NET_DEBUG_APP_TLS_LEVEL +#else +#define DEBUG_THRESHOLD 0 +#endif /* CONFIG_NET_DEBUG_APP_TLS_LEVEL */ +#endif + +#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) +#include +#endif + +#if defined(MBEDTLS_ERROR_C) +#define _net_app_print_error(fmt, ret) \ + do { \ + char error[80]; \ + \ + mbedtls_strerror(ret, error, sizeof(error)); \ + \ + NET_ERR(fmt " (%s)", -ret, error); \ + } while (0) +#else +#define _net_app_print_error(fmt, ret) NET_ERR(fmt, -ret) +#endif + +/* Direction of the packet (sending / receiving) */ +enum _net_app_dir { + NET_APP_PKT_UNKNOWN = 0, + NET_APP_PKT_TX = 1, + NET_APP_PKT_RX = 2, +}; + +#define BUF_ALLOC_TIMEOUT 100 + +#if defined(CONFIG_NET_DEBUG_APP) +void _net_app_print_info(struct net_app_ctx *ctx); +#else +#define _net_app_print_info(...) +#endif /* CONFIG_NET_DEBUG_APP */ + +#if defined(CONFIG_NET_APP_SERVER) || defined(CONFIG_NET_APP_CLIENT) +char *_net_app_sprint_ipaddr(char *buf, int buflen, + const struct sockaddr *addr); +void _net_app_received(struct net_context *net_ctx, + struct net_pkt *pkt, + int status, + void *user_data); +int _net_app_set_local_addr(struct sockaddr *addr, const char *myaddr, + u16_t port); +int _net_app_set_net_ctx(struct net_app_ctx *ctx, + struct net_context *net_ctx, + struct sockaddr *addr, + socklen_t socklen, + enum net_ip_protocol proto); +int _net_app_config_local_ctx(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto, + struct sockaddr *addr); +struct net_context *_net_app_select_net_ctx(struct net_app_ctx *ctx); +int _net_app_ssl_mux(void *context, unsigned char *buf, size_t size); +int _net_app_tls_sendto(struct net_pkt *pkt, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *token, + void *user_data); +void _net_app_tls_received(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data); +int _net_app_ssl_mainloop(struct net_app_ctx *ctx); + +#if defined(CONFIG_NET_APP_SERVER) +void _net_app_accept_cb(struct net_context *net_ctx, + struct sockaddr *addr, + socklen_t addrlen, + int status, void *data); +#endif /* CONFIG_NET_APP_SERVER */ + +#if defined(CONFIG_NET_APP_CLIENT) +#endif /* CONFIG_NET_APP_CLIENT */ + +#if defined(CONFIG_NET_APP_TLS) +void _net_app_tls_handler_stop(struct net_app_ctx *ctx); +int _net_app_tls_init(struct net_app_ctx *ctx, int client_or_server); +int _net_app_entropy_source(void *data, unsigned char *output, size_t len, + size_t *olen); +int _net_app_ssl_tx(void *context, const unsigned char *buf, size_t size); +#endif /* CONFIG_NET_APP_TLS */ + +#endif /* CONFIG_NET_APP_SERVER || CONFIG_NET_APP_CLIENT */ diff --git a/subsys/net/lib/app/server.c b/subsys/net/lib/app/server.c new file mode 100644 index 00000000000..d845208a8f2 --- /dev/null +++ b/subsys/net/lib/app/server.c @@ -0,0 +1,363 @@ +/* server.c */ + +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_NET_DEBUG_APP) +#define SYS_LOG_DOMAIN "net/app" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "net_app_private.h" + +#if defined(CONFIG_NET_TCP) +static void new_client(struct net_context *net_ctx, + const struct sockaddr *addr) +{ +#if defined(CONFIG_NET_DEBUG_APP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) +#if defined(CONFIG_NET_IPV6) +#define PORT_STR sizeof("[]:xxxxx") + char buf[NET_IPV6_ADDR_LEN + PORT_STR]; +#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) +#define PORT_STR sizeof(":xxxxx") + char buf[NET_IPV4_ADDR_LEN + PORT_STR]; +#endif + + NET_INFO("Connection from %s (%p)", + _net_app_sprint_ipaddr(buf, sizeof(buf), addr), + net_ctx); +#endif /* CONFIG_NET_DEBUG_APP */ +} + +void _net_app_accept_cb(struct net_context *net_ctx, + struct sockaddr *addr, + socklen_t addrlen, + int status, void *data) +{ + struct net_app_ctx *ctx = data; + + ARG_UNUSED(addr); + ARG_UNUSED(addrlen); + + if (status != 0 || ctx->server.net_ctx) { + /* We are already connected and support only one connection at + * a time so this new connection must be closed. + */ + net_context_put(net_ctx); + + if (ctx->cb.connect) { + if (!status) { + status = -ECONNREFUSED; + } + + ctx->cb.connect(ctx, status, ctx->user_data); + } + + if (ctx->server.net_ctx) { + NET_DBG("Already connected via context %p", + ctx->server.net_ctx); + } + + return; + } + + ctx->server.net_ctx = net_ctx; + + new_client(net_ctx, addr); + + net_context_recv(net_ctx, ctx->recv_cb, K_NO_WAIT, ctx); + + if (ctx->cb.connect) { + ctx->cb.connect(ctx, 0, ctx->user_data); + } +} +#endif /* CONFIG_NET_TCP */ + +int net_app_listen(struct net_app_ctx *ctx) +{ + int ret; + bool dual = false; + + if (!ctx) { + return -EINVAL; + } + + if (!ctx->is_init) { + return -ENOENT; + } + + if (ctx->app_type != NET_APP_SERVER) { + return -EINVAL; + } + +#if defined(CONFIG_NET_IPV4) + if (ctx->local.family == AF_UNSPEC) { + ctx->local.family = AF_INET; + dual = true; + + _net_app_set_local_addr(&ctx->local, NULL, + net_sin(&ctx->local)->sin_port); + } + + ret = _net_app_set_net_ctx(ctx, ctx->ipv4_ctx, &ctx->local, + sizeof(struct sockaddr_in), ctx->proto); + if (ret < 0) { + net_context_put(ctx->ipv4_ctx); + ctx->ipv4_ctx = NULL; + } +#endif + + /* We ignore the IPv4 error if IPv6 is enabled */ + +#if defined(CONFIG_NET_IPV6) + if (ctx->local.family == AF_UNSPEC || dual) { + ctx->local.family = AF_INET6; + + _net_app_set_local_addr(&ctx->local, NULL, + net_sin6(&ctx->local)->sin6_port); + } + + ret = _net_app_set_net_ctx(ctx, ctx->ipv6_ctx, &ctx->local, + sizeof(struct sockaddr_in6), ctx->proto); + if (ret < 0) { + net_context_put(ctx->ipv6_ctx); + ctx->ipv6_ctx = NULL; + } +#endif + + return ret; +} + +int net_app_init_server(struct net_app_ctx *ctx, + enum net_sock_type sock_type, + enum net_ip_protocol proto, + struct sockaddr *server_addr, + u16_t port, + void *user_data) +{ + int ret; + + if (!ctx) { + return -EINVAL; + } + + if (ctx->is_init) { + return -EALREADY; + } + + memset(&ctx->local, 0, sizeof(ctx->local)); + + if (server_addr) { + memcpy(&ctx->local, server_addr, + sizeof(ctx->local)); + } else { + ctx->local.family = AF_UNSPEC; + + if (port == 0) { + return -EINVAL; + } + + net_sin(&ctx->local)->sin_port = htons(port); + } + + ctx->app_type = NET_APP_SERVER; + ctx->user_data = user_data; + ctx->send_data = net_context_sendto; + ctx->recv_cb = _net_app_received; + ctx->proto = proto; + ctx->sock_type = sock_type; + + ret = _net_app_config_local_ctx(ctx, sock_type, proto, + &ctx->local); + if (ret < 0) { + goto fail; + } + + ctx->is_init = true; + +fail: + return ret; +} + +#if defined(CONFIG_NET_APP_TLS) +static inline void new_server(struct net_app_ctx *ctx, + const char *server_banner, + const struct sockaddr *addr) +{ +#if defined(CONFIG_NET_DEBUG_APP) && (CONFIG_SYS_LOG_NET_LEVEL > 2) +#if defined(CONFIG_NET_IPV6) +#define PORT_STR sizeof("[]:xxxxx") + char buf[NET_IPV6_ADDR_LEN + PORT_STR]; +#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) +#define PORT_STR sizeof(":xxxxx") + char buf[NET_IPV4_ADDR_LEN + PORT_STR]; +#endif + + if (addr) { + NET_INFO("%s %s (%p)", server_banner, + _net_app_sprint_ipaddr(buf, sizeof(buf), addr), ctx); + } else { + NET_INFO("%s (%p)", server_banner, ctx); + } +#endif /* CONFIG_NET_DEBUG_APP */ +} + +static void tls_server_handler(struct net_app_ctx *ctx, + struct k_sem *startup_sync) +{ + int ret; + + NET_DBG("Starting TLS server thread for %p", ctx); + + ret = _net_app_tls_init(ctx, MBEDTLS_SSL_IS_SERVER); + if (ret < 0) { + NET_DBG("TLS server init failed"); + return; + } + + k_sem_give(startup_sync); + + while (1) { + _net_app_ssl_mainloop(ctx); + + mbedtls_ssl_close_notify(&ctx->tls.mbedtls.ssl); + + if (ctx->cb.close) { + ctx->cb.close(ctx, -ESHUTDOWN, ctx->user_data); + } + + if (ctx->server.net_ctx) { + NET_DBG("Server context %p removed", + ctx->server.net_ctx); + net_context_put(ctx->server.net_ctx); + ctx->server.net_ctx = NULL; + } + } +} + +#define TLS_STARTUP_TIMEOUT K_SECONDS(5) + +bool net_app_server_tls_enable(struct net_app_ctx *ctx) +{ + struct k_sem startup_sync; + + NET_ASSERT(ctx); + + if (!(ctx->tls.stack && ctx->tls.stack_size > 0)) { + /* No stack or stack size is 0, we cannot enable */ + return false; + } + + ctx->is_enabled = true; + + /* Start the thread that handles TLS traffic. */ + if (!ctx->tls.tid) { + k_sem_init(&startup_sync, 0, 1); + + ctx->tls.tid = k_thread_create(&ctx->tls.thread, + ctx->tls.stack, + ctx->tls.stack_size, + (k_thread_entry_t) + tls_server_handler, + ctx, &startup_sync, 0, + K_PRIO_COOP(7), 0, 0); + + /* Wait until we know that the TLS thread startup was ok */ + if (k_sem_take(&startup_sync, TLS_STARTUP_TIMEOUT) < 0) { + NET_ERR("TLS server handler start failed"); + _net_app_tls_handler_stop(ctx); + return false; + } + } + + return true; +} + +bool net_app_server_tls_disable(struct net_app_ctx *ctx) +{ + NET_ASSERT(ctx); + + ctx->is_enabled = false; + + if (!ctx->tls.tid) { + return false; + } + + _net_app_tls_handler_stop(ctx); + + return true; +} + +int net_app_server_tls(struct net_app_ctx *ctx, + u8_t *request_buf, + size_t request_buf_len, + const char *server_banner, + u8_t *personalization_data, + size_t personalization_data_len, + net_app_cert_cb_t cert_cb, + net_app_entropy_src_cb_t entropy_src_cb, + struct k_mem_pool *pool, + u8_t *stack, + size_t stack_size) +{ + if (!request_buf || request_buf_len == 0) { + NET_ERR("Request buf must be set"); + return -EINVAL; + } + + /* mbedtls cannot receive or send larger buffer as what is defined + * in a file pointed by CONFIG_MBEDTLS_CFG_FILE. + */ + if (request_buf_len > MBEDTLS_SSL_MAX_CONTENT_LEN) { + NET_ERR("Request buf too large, max len is %d", + MBEDTLS_SSL_MAX_CONTENT_LEN); + return -EINVAL; + } + + if (!cert_cb) { + NET_ERR("Cert callback must be set"); + return -EINVAL; + } + + if (server_banner) { + new_server(ctx, server_banner, &ctx->local); + } + + ctx->tls.request_buf = request_buf; + ctx->tls.request_buf_len = request_buf_len; + ctx->is_tls = true; + ctx->tls.stack = stack; + ctx->tls.stack_size = stack_size; + ctx->tls.mbedtls.cert_cb = cert_cb; + ctx->tls.pool = pool; + + if (entropy_src_cb) { + ctx->tls.mbedtls.entropy_src_cb = entropy_src_cb; + } else { + ctx->tls.mbedtls.entropy_src_cb = _net_app_entropy_source; + } + + ctx->tls.mbedtls.personalization_data = personalization_data; + ctx->tls.mbedtls.personalization_data_len = personalization_data_len; + ctx->send_data = _net_app_tls_sendto; + ctx->recv_cb = _net_app_tls_received; + + /* Then mbedtls specific initialization */ + return 0; +} +#endif /* CONFIG_NET_APP_TLS */