zephyr/subsys/net/lib/app/net_app.c
Jukka Rissanen 86689030e8 net: Clarify logging in networking code
Remove network specific default and max log level setting
and start to use the zephyr logging values for those.

Remove LOG_MODULE_REGISTER() from net_core.h and place the
calls into .c files. This is done in order to avoid weird
compiler errors in some cases and to make the code look similar
as other subsystems.

Fixes #11343
Fixes #11659

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
2018-12-07 12:00:04 +02:00

2393 lines
53 KiB
C

/* net_app.c */
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_app, CONFIG_NET_APP_LOG_LEVEL);
#if defined(CONFIG_STDOUT_CONSOLE)
#include <stdio.h>
#define MBEDTLS_PRINT printf
#else
#include <misc/printk.h>
#define MBEDTLS_PRINT (int(*)(const char *, ...)) printk
#endif /* CONFIG_STDOUT_CONSOLE */
#include <zephyr.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <net/net_core.h>
#include <net/net_ip.h>
#include <net/net_if.h>
#include <net/dhcpv4.h>
#include <net/net_mgmt.h>
#include <net/udp.h>
#include <net/net_app.h>
#include "../../ip/udp_internal.h"
#include "net_app_private.h"
#if defined(CONFIG_NET_APP_DTLS_TIMEOUT)
#define DTLS_TIMEOUT K_SECONDS(CONFIG_NET_APP_DTLS_TIMEOUT)
#else
#define DTLS_TIMEOUT K_SECONDS(15)
#endif
#if CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG
static sys_slist_t _net_app_instances;
#endif
void _net_app_register(struct net_app_ctx *ctx)
{
#if CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG
sys_slist_prepend(&_net_app_instances, &ctx->node);
#endif
}
void _net_app_unregister(struct net_app_ctx *ctx)
{
#if CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG
sys_slist_find_and_remove(&_net_app_instances, &ctx->node);
#endif
}
static void net_app_foreach(net_app_ctx_cb_t cb, enum net_app_type type,
void *user_data)
{
#if CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG
struct net_app_ctx *ctx;
SYS_SLIST_FOR_EACH_CONTAINER(&_net_app_instances, ctx, node) {
if (ctx->is_init && ctx->app_type == type) {
if (ctx->app_type == NET_APP_CLIENT &&
!ctx->is_enabled) {
continue;
}
cb(ctx, user_data);
}
}
#endif
}
void net_app_server_foreach(net_app_ctx_cb_t cb, void *user_data)
{
net_app_foreach(cb, NET_APP_SERVER, user_data);
}
void net_app_client_foreach(net_app_ctx_cb_t cb, void *user_data)
{
net_app_foreach(cb, NET_APP_CLIENT, user_data);
}
#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 */
char *_net_app_sprint_ipaddr(char *buf, int buflen,
const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
char ipaddr[NET_IPV6_ADDR_LEN];
net_addr_ntop(addr->sa_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->sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
char ipaddr[NET_IPV4_ADDR_LEN];
net_addr_ntop(addr->sa_family,
&net_sin(addr)->sin_addr,
ipaddr, sizeof(ipaddr));
snprintk(buf, buflen, "%s:%u", ipaddr,
ntohs(net_sin(addr)->sin_port));
#endif
} else {
snprintk(buf, buflen, "<AF_UNSPEC %d>",
addr->sa_family);
}
return buf;
}
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->default_ctx->local);
_net_app_sprint_ipaddr(remote, sizeof(remote),
&ctx->default_ctx->remote);
NET_DBG("net app connect %s %s %s",
log_strdup(local),
ctx->app_type == NET_APP_CLIENT ? "->" : "<-",
log_strdup(remote));
}
#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) {
bool close = true;
if (pkt) {
if (ctx->cb.recv) {
ctx->cb.recv(ctx, pkt, status, ctx->user_data);
}
return;
}
#if defined(CONFIG_NET_TCP)
if (ctx->proto == IPPROTO_TCP) {
int i;
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
if (ctx->server.net_ctxs[i] == net_ctx &&
ctx == net_ctx->net_app) {
net_context_put(net_ctx);
ctx->server.net_ctxs[i] = NULL;
net_ctx->net_app = NULL;
break;
}
}
/* Go through the loop and check if there are any
* active net_contexts. If there is any active net
* context do not call the close callback.
*/
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
if (ctx->server.net_ctxs[i]) {
close = false;
break;
}
}
}
#endif
if (close && ctx->cb.close) {
ctx->cb.close(ctx, 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;
if (!net_ctx || !net_context_is_used(net_ctx)) {
return -ENOENT;
}
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 net_app_ctx *ctx, struct sockaddr *addr,
const char *myaddr, u16_t port)
{
if (myaddr) {
void *inaddr;
if (addr->sa_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->sa_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->sa_family, myaddr, inaddr);
}
/* If the caller did not supply the address where to bind, then
* try to figure it out ourselves.
*/
if (addr->sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
net_ipaddr_copy(&net_sin6(addr)->sin6_addr,
net_if_ipv6_select_src_addr(NULL,
&net_sin6(&ctx->ipv6.remote)->sin6_addr));
#else
return -EPFNOSUPPORT;
#endif
} else if (addr->sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
net_ipaddr_copy(&net_sin(addr)->sin_addr,
net_if_ipv4_select_src_addr(NULL,
&net_sin(&ctx->ipv4.remote)->sin_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)
static void select_default_ctx(struct net_app_ctx *ctx)
{
#if defined(CONFIG_NET_IPV6)
ctx->default_ctx = &ctx->ipv6;
#elif defined(CONFIG_NET_IPV4)
ctx->default_ctx = &ctx->ipv4;
#endif
}
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) {
#if defined(CONFIG_NET_IPV6)
if (ctx->ipv6.local.sa_family == AF_INET6 ||
ctx->ipv6.local.sa_family == AF_UNSPEC) {
ret = setup_ipv6_ctx(ctx, sock_type, proto);
} else {
ret = -EPFNOSUPPORT;
goto fail;
}
if (!ret) {
select_default_ctx(ctx);
}
#endif
#if defined(CONFIG_NET_IPV4)
if (ctx->ipv4.local.sa_family == AF_INET ||
ctx->ipv4.local.sa_family == AF_UNSPEC) {
ret = setup_ipv4_ctx(ctx, sock_type, proto);
} else {
ret = -EPFNOSUPPORT;
goto fail;
}
if (!ret) {
select_default_ctx(ctx);
}
#endif
return ret;
} else {
if (addr->sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
ret = setup_ipv6_ctx(ctx, sock_type, proto);
ctx->default_ctx = &ctx->ipv6;
net_sin6(&ctx->ipv6.local)->sin6_port =
net_sin6(addr)->sin6_port;
#else
ret = -EPFNOSUPPORT;
goto fail;
#endif
} else if (addr->sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
ret = setup_ipv4_ctx(ctx, sock_type, proto);
ctx->default_ctx = &ctx->ipv4;
net_sin(&ctx->ipv4.local)->sin_port =
net_sin(addr)->sin_port;
#else
ret = -EPFNOSUPPORT;
goto fail;
#endif
} else if (addr->sa_family == AF_UNSPEC) {
#if defined(CONFIG_NET_IPV4)
ret = setup_ipv4_ctx(ctx, sock_type, proto);
ctx->default_ctx = &ctx->ipv4;
net_sin(&ctx->ipv4.local)->sin_port =
net_sin(addr)->sin_port;
#endif
/* We ignore the IPv4 error if IPv6 is enabled */
#if defined(CONFIG_NET_IPV6)
ret = setup_ipv6_ctx(ctx, sock_type, proto);
ctx->default_ctx = &ctx->ipv6;
net_sin6(&ctx->ipv6.local)->sin6_port =
net_sin6(addr)->sin6_port;
#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;
_net_app_unregister(ctx);
return 0;
}
#if defined(CONFIG_NET_APP_CLIENT)
static inline
struct net_context *select_client_ctx(struct net_app_ctx *ctx,
const struct sockaddr *dst)
{
if (ctx->proto == IPPROTO_UDP) {
if (!dst) {
if (ctx->is_tls) {
#if defined(CONFIG_NET_APP_DTLS)
if (ctx->dtls.ctx) {
return ctx->dtls.ctx;
} else {
return ctx->default_ctx->ctx;
}
#else
return NULL;
#endif
} else {
return ctx->default_ctx->ctx;
}
} else {
if (ctx->is_tls) {
#if defined(CONFIG_NET_APP_DTLS)
if (ctx->dtls.ctx) {
return ctx->dtls.ctx;
}
#else
return NULL;
#endif
}
goto common_checks;
}
} else {
if (!dst) {
if (ctx->default_ctx->ctx &&
atomic_get(&ctx->default_ctx->ctx->refcount) <= 0) {
ctx->default_ctx->ctx = NULL;
}
return ctx->default_ctx->ctx;
} else {
common_checks:
if (dst->sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
if (ctx->ipv4.ctx &&
atomic_get(&ctx->ipv4.ctx->refcount) <= 0) {
ctx->ipv4.ctx = NULL;
}
return ctx->ipv4.ctx;
#else
return NULL;
#endif
}
if (dst->sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
if (ctx->ipv6.ctx &&
atomic_get(&ctx->ipv6.ctx->refcount) <= 0) {
ctx->ipv6.ctx = NULL;
}
return ctx->ipv6.ctx;
#else
return NULL;
#endif
}
if (dst->sa_family == AF_UNSPEC) {
if (ctx->default_ctx->ctx &&
atomic_get(&ctx->default_ctx->ctx->refcount)
<= 0) {
ctx->default_ctx->ctx = NULL;
}
return ctx->default_ctx->ctx;
}
}
}
return NULL;
}
#else
#define select_client_ctx(...) NULL
#endif /* CONFIG_NET_APP_CLIENT */
#if defined(CONFIG_NET_APP_SERVER)
#if defined(CONFIG_NET_TCP)
static struct net_context *get_server_ctx_without_dst(struct net_app_ctx *ctx)
{
int i;
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
struct net_context *tmp = ctx->server.net_ctxs[i];
if (!tmp || !net_context_is_used(tmp)) {
continue;
}
if (tmp->net_app != ctx) {
continue;
}
NET_DBG("Selecting net_ctx %p iface %p for NULL dst",
tmp, net_context_get_iface(tmp));
return tmp;
}
return NULL;
}
static struct net_context *get_server_ctx(struct net_app_ctx *ctx,
const struct sockaddr *dst)
{
int i;
if (!dst) {
return get_server_ctx_without_dst(ctx);
}
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
struct net_context *tmp = ctx->server.net_ctxs[i];
u16_t port, rport;
if (!tmp || !net_context_is_used(tmp)) {
continue;
}
/* Serve IPv6 first if the user does not care */
if (IS_ENABLED(CONFIG_NET_IPV6) &&
(dst->sa_family == AF_UNSPEC ||
(tmp->remote.sa_family == AF_INET6 &&
dst->sa_family == AF_INET6))) {
struct in6_addr *addr6 = &net_sin6(dst)->sin6_addr;
struct in6_addr *remote6;
remote6 = &net_sin6(&tmp->remote)->sin6_addr;
rport = net_sin6(&tmp->remote)->sin6_port;
port = net_sin6(dst)->sin6_port;
if (net_ipv6_addr_cmp(addr6, remote6) &&
port == rport) {
NET_DBG("Selecting net_ctx %p iface %p for "
"AF_INET6 port %d", tmp,
net_context_get_iface(tmp),
ntohs(rport));
return tmp;
}
}
if (IS_ENABLED(CONFIG_NET_IPV4) &&
(dst->sa_family == AF_UNSPEC ||
(tmp->remote.sa_family == AF_INET &&
dst->sa_family == AF_INET))) {
struct in_addr *addr4 = &net_sin(dst)->sin_addr;
struct in_addr *remote4;
remote4 = &net_sin(&tmp->remote)->sin_addr;
rport = net_sin(&tmp->remote)->sin_port;
port = net_sin(dst)->sin_port;
if (net_ipv4_addr_cmp(addr4, remote4) &&
port == rport) {
NET_DBG("Selecting net_ctx %p iface %p for "
"AF_INET port %d", tmp,
net_context_get_iface(tmp),
ntohs(port));
return tmp;
}
}
}
return get_server_ctx_without_dst(ctx);
}
#endif /* CONFIG_NET_TCP */
static inline
struct net_context *select_server_ctx(struct net_app_ctx *ctx,
const struct sockaddr *dst)
{
if (ctx->proto == IPPROTO_TCP) {
#if defined(CONFIG_NET_TCP)
return get_server_ctx(ctx, dst);
#else
return NULL;
#endif
} else if (ctx->proto == IPPROTO_UDP) {
if (!dst) {
if (ctx->is_tls) {
#if defined(CONFIG_NET_APP_DTLS)
return ctx->dtls.ctx;
#else
return NULL;
#endif
} else {
return ctx->default_ctx->ctx;
}
} else {
if (ctx->is_tls) {
#if defined(CONFIG_NET_APP_DTLS)
return ctx->dtls.ctx;
#else
return NULL;
#endif
}
if (dst->sa_family == AF_INET) {
#if defined(CONFIG_NET_IPV4)
return ctx->ipv4.ctx;
#else
return NULL;
#endif
}
if (dst->sa_family == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
return ctx->ipv6.ctx;
#else
return NULL;
#endif
}
if (dst->sa_family == AF_UNSPEC) {
return ctx->default_ctx->ctx;
}
}
}
return NULL;
}
#else
#define select_server_ctx(...) NULL
#endif /* CONFIG_NET_APP_SERVER */
#if CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_ERR
struct net_context *_net_app_select_net_ctx_debug(struct net_app_ctx *ctx,
const struct sockaddr *dst,
const char *caller,
int line)
#else
struct net_context *_net_app_select_net_ctx(struct net_app_ctx *ctx,
const struct sockaddr *dst)
#endif
{
struct net_context *net_ctx = NULL;
if (ctx->app_type == NET_APP_CLIENT) {
net_ctx = select_client_ctx(ctx, dst);
} else if (ctx->app_type == NET_APP_SERVER) {
net_ctx = select_server_ctx(ctx, dst);
}
#if CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_ERR
NET_DBG("Selecting %p net_ctx (%s():%d)", net_ctx, caller, line);
#endif
return net_ctx;
}
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;
}
/* Get rid of IP + UDP/TCP header if it is there. The IP header
* will be put back just before sending the packet. Normally the
* data that is sent does not contain IP header, but if the caller
* replies the packet directly back, the IP header could be there
* at this point.
*/
if (net_pkt_appdatalen(pkt) > 0) {
int header_len;
header_len = net_buf_frags_len(pkt->frags) -
net_pkt_appdatalen(pkt);
if (header_len > 0) {
net_buf_pull(pkt->frags, header_len);
}
} else {
net_pkt_set_appdatalen(pkt, net_buf_frags_len(pkt->frags));
}
if (ctx->proto == IPPROTO_UDP) {
if (!dst) {
if (net_pkt_family(pkt) == AF_INET) {
#if defined(CONFIG_NET_IPV4)
dst = &ctx->ipv4.remote;
dst_len = sizeof(struct sockaddr_in);
#else
return -EPFNOSUPPORT;
#endif
} else {
if (net_pkt_family(pkt) == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
dst = &ctx->ipv6.remote;
dst_len = sizeof(struct sockaddr_in6);
#else
return -EPFNOSUPPORT;
#endif
} else {
return -EPFNOSUPPORT;
}
}
} else {
if (net_pkt_family(pkt) == AF_INET) {
#if defined(CONFIG_NET_IPV4)
net_ipaddr_copy(net_sin(&ctx->ipv4.remote),
net_sin(dst));
dst_len = sizeof(struct sockaddr_in);
#else
return -EPFNOSUPPORT;
#endif
} else {
if (net_pkt_family(pkt) == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
net_ipaddr_copy(
net_sin6(&ctx->ipv6.remote),
net_sin6(dst));
dst_len = sizeof(struct sockaddr_in6);
#else
return -EPFNOSUPPORT;
#endif
} else {
return -EPFNOSUPPORT;
}
}
}
}
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, dst);
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,
sa_family_t family,
s32_t timeout)
{
struct net_context *net_ctx;
struct sockaddr dst = { 0 };
if (!ctx) {
return NULL;
}
if (!ctx->is_init) {
return NULL;
}
dst.sa_family = family;
net_ctx = _net_app_select_net_ctx(ctx, &dst);
if (!net_ctx) {
return NULL;
}
return net_pkt_get_tx(net_ctx, timeout);
}
struct net_pkt *net_app_get_net_pkt_with_dst(struct net_app_ctx *ctx,
const struct sockaddr *dst,
s32_t timeout)
{
struct net_context *net_ctx;
if (!ctx || !dst) {
return NULL;
}
if (!ctx->is_init) {
return NULL;
}
net_ctx = _net_app_select_net_ctx(ctx, dst);
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)
{
struct net_buf *frag;
if (!ctx || !pkt) {
return NULL;
}
if (!ctx->is_init) {
return NULL;
}
frag = net_pkt_get_frag(pkt, timeout);
if (!frag) {
return NULL;
}
net_pkt_frag_add(pkt, frag);
return frag;
}
int net_app_close(struct net_app_ctx *ctx)
{
struct net_context *net_ctx;
if (!ctx) {
return -EINVAL;
}
if (!ctx->is_init) {
return -ENOENT;
}
#if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS)
if (ctx->tls.tx_pending) {
ctx->tls.close_requested = true;
return -EINPROGRESS;
}
#endif
net_ctx = _net_app_select_net_ctx(ctx, NULL);
if (ctx->cb.close) {
ctx->cb.close(ctx, 0, ctx->user_data);
}
#if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS)
if (ctx->is_tls) {
_net_app_tls_trigger_close(ctx);
}
#endif
#if defined(CONFIG_NET_APP_SERVER) && defined(CONFIG_NET_TCP)
if (net_ctx && ctx->app_type == NET_APP_SERVER) {
int i;
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
if (ctx->server.net_ctxs[i] == net_ctx) {
NET_DBG("Releasing slot %d net_ctx %p",
i, net_ctx);
ctx->server.net_ctxs[i] = NULL;
break;
}
}
}
#endif
if (net_ctx) {
net_ctx->net_app = NULL;
net_context_put(net_ctx);
NET_DBG("Closing net_ctx %p", net_ctx);
}
#if defined(CONFIG_NET_APP_CLIENT)
if (ctx->app_type == NET_APP_CLIENT) {
ctx->is_enabled = false;
/* Make sure we do not re-use the same port if we
* re-connect after close.
*/
#if defined(CONFIG_NET_IPV4)
net_sin(&ctx->ipv4.local)->sin_port = 0;
#endif
#if defined(CONFIG_NET_IPV6)
net_sin6(&ctx->ipv6.local)->sin6_port = 0;
#endif
}
#endif
return 0;
}
int net_app_close2(struct net_app_ctx *ctx, struct net_context *net_ctx)
{
if (!ctx || !net_ctx) {
return -EINVAL;
}
if (!ctx->is_init) {
return -ENOENT;
}
#if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS)
if (ctx->tls.tx_pending) {
ctx->tls.close_requested = true;
return -EINPROGRESS;
}
#endif
if (ctx->cb.close) {
ctx->cb.close(ctx, 0, ctx->user_data);
}
#if defined(CONFIG_NET_APP_SERVER) && defined(CONFIG_NET_TCP)
if (ctx->app_type == NET_APP_SERVER) {
int i;
for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) {
if (ctx->server.net_ctxs[i] == net_ctx) {
ctx->server.net_ctxs[i] = NULL;
break;
}
}
}
#endif
#if defined(CONFIG_NET_APP_CLIENT)
if (ctx->app_type == NET_APP_CLIENT) {
if (net_ctx != _net_app_select_net_ctx(ctx, NULL)) {
return -ENOENT;
}
ctx->is_enabled = false;
/* Make sure we do not re-use the same port if we
* re-connect after close.
*/
#if defined(CONFIG_NET_IPV4)
net_sin(&ctx->ipv4.local)->sin_port = 0;
#endif
#if defined(CONFIG_NET_IPV6)
net_sin6(&ctx->ipv6.local)->sin6_port = 0;
#endif
}
#endif
net_ctx->net_app = NULL;
net_context_put(net_ctx);
return 0;
}
#if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS)
#if defined(MBEDTLS_DEBUG_C) && (CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG)
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,
log_strdup(str));
}
#endif /* MBEDTLS_DEBUG_C && CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG */
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);
}
int _net_app_tls_trigger_close(struct net_app_ctx *ctx)
{
struct net_app_fifo_block *rx_data = NULL;
struct k_mem_block block;
int ret;
ret = k_mem_pool_alloc(ctx->tls.pool, &block,
sizeof(struct net_app_fifo_block),
BUF_ALLOC_TIMEOUT);
if (ret < 0) {
return ret;
}
rx_data = block.data;
rx_data->pkt = NULL;
rx_data->dir = NET_APP_PKT_TX;
memcpy(&rx_data->block, &block, sizeof(struct k_mem_block));
NET_DBG("Triggering connection close");
k_fifo_put(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, (void *)rx_data);
return 0;
}
/* 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;
size_t sent = 0;
int ret, len = 0;
while (size) {
send_buf = net_app_get_net_pkt(ctx, AF_UNSPEC,
BUF_ALLOC_TIMEOUT);
if (!send_buf) {
return MBEDTLS_ERR_SSL_ALLOC_FAILED;
}
sent = net_pkt_append(send_buf, size, (u8_t *)buf + len,
BUF_ALLOC_TIMEOUT);
size -= sent;
len += sent;
if (ctx->proto == IPPROTO_UDP) {
#if defined(CONFIG_NET_APP_DTLS)
if (!ctx->dtls.ctx) {
net_pkt_unref(send_buf);
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
}
ret = net_context_sendto(send_buf,
&ctx->dtls.ctx->remote,
sizeof(ctx->dtls.ctx->remote),
ssl_sent, K_NO_WAIT, NULL,
ctx);
#else
ret = -EPROTONOSUPPORT;
#endif
} else {
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);
if (ctx->tls.close_requested) {
_net_app_tls_trigger_close(ctx);
}
}
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;
if (!ctx->tls.handshake_done) {
/* This means that the initial TLS handshake is not yet
* finished so our packet cannot be sent yet. Try sleeping
* a bit and hope things are ok after that. If not, then
* return error.
*/
k_sleep(K_MSEC(50));
if (!ctx->tls.handshake_done) {
NET_DBG("TLS handshake not yet done, pkt %p not sent",
pkt);
return -EBUSY;
}
}
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;
ctx->tls.tx_pending = true;
/* 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;
}
#if defined(CONFIG_NET_APP_DTLS)
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
static inline void copy_pool_vars(struct net_context *new_context,
struct net_context *listen_context)
{
new_context->tx_slab = listen_context->tx_slab;
new_context->data_pool = listen_context->data_pool;
}
#else
#define copy_pool_vars(...)
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
static void dtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms)
{
struct dtls_timing_context *ctx = data;
#if DTLS_EXTRA_DEBUG == 1
NET_DBG("Setting DTLS delays for %p, int %ums fin %ums",
ctx, int_ms, fin_ms);
#endif
ctx->int_ms = int_ms;
ctx->fin_ms = fin_ms;
if (fin_ms != 0) {
ctx->snapshot = k_uptime_get_32();
}
}
static int dtls_timing_get_delay(void *data)
{
struct dtls_timing_context *timing = data;
unsigned long elapsed_ms;
NET_ASSERT(timing);
#if DTLS_EXTRA_DEBUG == 1
NET_DBG("Get DTLS delays for %p, int %ums fin %ums snapshot %d",
timing, timing->int_ms, timing->fin_ms, timing->snapshot);
#endif
if (timing->fin_ms == 0) {
return -1;
}
elapsed_ms = k_uptime_get_32() - timing->snapshot;
if (elapsed_ms >= timing->fin_ms) {
return 2;
}
if (elapsed_ms >= timing->int_ms) {
return 1;
}
return 0;
}
static void dtls_cleanup(struct net_app_ctx *ctx, bool cancel_timer)
{
if (cancel_timer) {
k_delayed_work_cancel(&ctx->dtls.fin_timer);
}
/* It might be that ctx is already cleared so check it here */
if (ctx->dtls.ctx) {
/* Notify peers that we are closing. This must be called
* here as the call in _net_app_ssl_mainloop() is too
* late. Fixes #8605
*/
mbedtls_ssl_close_notify(&ctx->tls.mbedtls.ssl);
net_udp_unregister(ctx->dtls.ctx->conn_handler);
net_context_put(ctx->dtls.ctx);
ctx->dtls.ctx = NULL;
}
}
static void dtls_timeout(struct k_work *work)
{
struct net_app_ctx *ctx =
CONTAINER_OF(work, struct net_app_ctx, dtls.fin_timer);
NET_DBG("Did not receive DTLS traffic in %dms", DTLS_TIMEOUT);
dtls_cleanup(ctx, false);
}
enum net_verdict _net_app_dtls_established(struct net_conn *conn,
struct net_pkt *pkt,
void *user_data)
{
struct net_app_ctx *ctx = user_data;
struct net_app_fifo_block *rx_data = NULL;
struct k_mem_block block;
struct net_buf *frag;
u16_t offset;
int ret, len;
if (!pkt) {
return NET_DROP;
}
len = net_pkt_get_len(pkt) - net_pkt_ip_hdr_len(pkt) -
net_pkt_ipv6_ext_len(pkt) - sizeof(struct net_udp_hdr);
if (len <= 0) {
return NET_DROP;
}
net_pkt_set_appdatalen(pkt, len);
frag = net_frag_get_pos(pkt, net_pkt_ip_hdr_len(pkt) +
net_pkt_ipv6_ext_len(pkt) +
sizeof(struct net_udp_hdr),
&offset);
if (frag) {
net_pkt_set_appdata(pkt, frag->data + offset);
}
ret = k_mem_pool_alloc(ctx->tls.pool, &block,
sizeof(struct net_app_fifo_block),
BUF_ALLOC_TIMEOUT);
if (ret < 0) {
NET_DBG("Not enough space in DTLS mem pool");
return NET_DROP;
}
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));
NET_DBG("Encrypted DTLS data received in pkt %p", pkt);
k_fifo_put(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, (void *)rx_data);
k_delayed_work_cancel(&ctx->dtls.fin_timer);
k_yield();
k_delayed_work_submit(&ctx->dtls.fin_timer, DTLS_TIMEOUT);
return NET_OK;
}
static int accept_dtls(struct net_app_ctx *ctx,
struct net_context *context,
struct net_pkt *pkt)
{
struct net_context *dtls_context;
struct net_udp_hdr hdr, *udp_hdr;
struct sockaddr remote_addr;
struct sockaddr local_addr;
socklen_t addrlen;
int ret;
udp_hdr = net_udp_get_hdr(pkt, &hdr);
if (!udp_hdr) {
NET_DBG("Dropping invalid pkt %p", pkt);
goto pkt_drop;
}
/* We create a new context that starts to wait data. */
ret = net_context_get(net_pkt_family(pkt), SOCK_DGRAM, IPPROTO_UDP,
&dtls_context);
if (ret < 0) {
NET_DBG("Cannot get accepted context, pkt %p dropped", pkt);
goto pkt_drop;
}
#if defined(CONFIG_NET_IPV6)
if (net_context_get_family(context) == AF_INET6) {
struct sockaddr_in6 *local_addr6 = net_sin6(&local_addr);
struct sockaddr_in6 *remote_addr6 = net_sin6(&remote_addr);
remote_addr6->sin6_family = AF_INET6;
local_addr6->sin6_family = AF_INET6;
local_addr6->sin6_port = udp_hdr->dst_port;
remote_addr6->sin6_port = udp_hdr->src_port;
net_ipaddr_copy(&local_addr6->sin6_addr,
&NET_IPV6_HDR(pkt)->dst);
net_ipaddr_copy(&remote_addr6->sin6_addr,
&NET_IPV6_HDR(pkt)->src);
addrlen = sizeof(struct sockaddr_in6);
} else
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (net_context_get_family(context) == AF_INET) {
struct sockaddr_in *local_addr4 = net_sin(&local_addr);
struct sockaddr_in *remote_addr4 = net_sin(&remote_addr);
remote_addr4->sin_family = AF_INET;
local_addr4->sin_family = AF_INET;
local_addr4->sin_port = udp_hdr->dst_port;
remote_addr4->sin_port = udp_hdr->src_port;
net_ipaddr_copy(&local_addr4->sin_addr,
&NET_IPV4_HDR(pkt)->dst);
net_ipaddr_copy(&remote_addr4->sin_addr,
&NET_IPV4_HDR(pkt)->src);
addrlen = sizeof(struct sockaddr_in);
} else
#endif /* CONFIG_NET_IPV4 */
{
NET_ASSERT_INFO(false, "Invalid protocol family %d",
net_context_get_family(context));
goto ctx_drop;
}
ret = net_context_bind(dtls_context, &local_addr, sizeof(local_addr));
if (ret < 0) {
NET_DBG("Cannot bind accepted DTLS context");
goto ctx_drop;
}
dtls_context->flags |= NET_CONTEXT_REMOTE_ADDR_SET;
memcpy(&dtls_context->remote, &remote_addr, sizeof(remote_addr));
ret = net_udp_register(&dtls_context->remote,
&local_addr,
ntohs(net_sin(&dtls_context->remote)->sin_port),
ntohs(net_sin(&local_addr)->sin_port),
_net_app_dtls_established,
ctx,
&dtls_context->conn_handler);
if (ret < 0) {
NET_DBG("Cannot register accepted DTLS handler (%d)", ret);
goto ctx_drop;
}
copy_pool_vars(dtls_context, context);
net_context_set_state(dtls_context, NET_CONTEXT_CONNECTED);
NET_DBG("New DTLS connection %p accepted", dtls_context);
ctx->dtls.ctx = dtls_context;
k_delayed_work_submit(&ctx->dtls.fin_timer, DTLS_TIMEOUT);
return 0;
ctx_drop:
net_context_unref(dtls_context);
pkt_drop:
net_pkt_unref(pkt);
return -ECONNABORTED;
}
#else /* CONFIG_NET_APP_DTLS */
#define dtls_cleanup(...)
#endif /* CONFIG_NET_APP_DTLS */
/* 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;
}
#if defined(CONFIG_NET_APP_DTLS)
/* Client connections that are initiated by us, are passed through
* as is.
*/
if (ctx->proto == IPPROTO_UDP && ctx->app_type == NET_APP_SERVER) {
if (ctx->dtls.ctx) {
/* There will be a separate handler for these DTLS
* packets so if they are arriving here, then that is
* an error.
*/
NET_DBG("DTLS context already created, pkt %p dropped",
pkt);
net_pkt_unref(pkt);
return;
} else {
ret = accept_dtls(ctx, context, pkt);
if (ret < 0) {
NET_DBG("Cannot accept new DTLS "
"connection (%d)", ret);
net_pkt_unref(pkt);
return;
}
/* The first packet is passed as is in below code,
* subsequent packets are handled by dtls_established()
*/
}
}
#endif /* CONFIG_NET_APP_DTLS */
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));
NET_DBG("Encrypted data received in pkt %p", pkt);
k_fifo_put(&ctx->tls.mbedtls.ssl_ctx.tx_rx_fifo, (void *)rx_data);
/* Make sure that the tls handler thread runs now, even if we receive
* new packets.
*/
k_yield();
}
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);
ctx->tls.tx_pending = false;
return ret;
}
#if defined(CONFIG_NET_APP_DTLS)
static inline void set_remote_endpoint(struct net_app_ctx *ctx,
struct net_pkt *pkt)
{
struct net_udp_hdr hdr, *udp_hdr;
udp_hdr = net_udp_get_hdr(pkt, &hdr);
if (!udp_hdr) {
return;
}
if (net_pkt_family(pkt) == AF_INET) {
#if defined(CONFIG_NET_IPV4)
net_sin(&ctx->ipv4.remote)->sin_port = udp_hdr->src_port;
net_ipaddr_copy(&net_sin(&ctx->ipv4.remote)->sin_addr,
&NET_IPV4_HDR(pkt)->src);
#endif
return;
}
if (net_pkt_family(pkt) == AF_INET6) {
#if defined(CONFIG_NET_IPV6)
net_sin6(&ctx->ipv6.remote)->sin6_port = udp_hdr->src_port;
net_ipaddr_copy(&net_sin6(&ctx->ipv6.remote)->sin6_addr,
&NET_IPV6_HDR(pkt)->src);
#endif
return;
}
}
#endif /* CONFIG_NET_APP_DTLS */
/* 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) {
k_mem_pool_free(&rx_data->block);
ctx->tls.connection_closing = true;
return -EIO;
}
NET_DBG("%s data in pkt %p (len %zd)",
rx_data->dir == NET_APP_PKT_TX ? "Sending plain" :
"Receiving encrypted",
rx_data->pkt, net_pkt_get_len(rx_data->pkt));
/* 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;
}
/* Save the IP header so that we can pass it to application. */
if (!ctx->tls.mbedtls.ssl_ctx.hdr) {
/* Only allocate a IP fragment header once. The header
* is same for every packet so we can ignore the
* duplicated one.
*/
ctx->tls.mbedtls.ssl_ctx.hdr =
net_pkt_get_frag(
ctx->tls.mbedtls.ssl_ctx.rx_pkt,
BUF_ALLOC_TIMEOUT);
if (ctx->tls.mbedtls.ssl_ctx.hdr) {
net_buf_add_mem(
ctx->tls.mbedtls.ssl_ctx.hdr,
ctx->tls.mbedtls.ssl_ctx.frag->data,
len);
}
}
/* 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;
}
#if defined(CONFIG_NET_APP_DTLS)
if (ctx->proto == IPPROTO_UDP) {
set_remote_endpoint(ctx,
ctx->tls.mbedtls.ssl_ctx.rx_pkt);
}
#endif /* CONFIG_NET_APP_DTLS */
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;
}
int _net_app_ssl_mainloop(struct net_app_ctx *ctx)
{
size_t len;
int ret;
ctx->tls.connect_cb_called = false;
reset:
mbedtls_ssl_session_reset(&ctx->tls.mbedtls.ssl);
#if defined(CONFIG_NET_APP_DTLS)
mbedtls_ssl_set_timer_cb(&ctx->tls.mbedtls.ssl,
&ctx->tls.mbedtls.timing_ctx,
dtls_timing_set_delay,
dtls_timing_get_delay);
#if defined(CONFIG_NET_APP_SERVER)
if (ctx->app_type == NET_APP_SERVER) {
ctx->tls.mbedtls.ssl_ctx.client_id =
ctx->tls.mbedtls.ssl_ctx.remaining;
ret = mbedtls_ssl_set_client_transport_id(
&ctx->tls.mbedtls.ssl,
&ctx->tls.mbedtls.ssl_ctx.client_id,
sizeof(char));
if (ret != 0) {
_net_app_print_error(
"mbedtls_ssl_set_client_transport_id "
" returned -0x%x\n\n", ret);
goto close;
}
}
#endif /* CONFIG_NET_APP_SERVER */
#endif /* CONFIG_NET_APP_DTLS */
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 */
ctx->tls.handshake_done = false;
NET_DBG("Starting TLS handshake");
do {
ret = mbedtls_ssl_handshake(&ctx->tls.mbedtls.ssl);
if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
/* If we get MAC verification failure, then it usually
* means that we ran out of heap. As that Invalid MAC
* error is really confusing, give hint about possible
* out of memory issue.
*/
if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) {
NET_DBG("Check CONFIG_MBEDTLS_HEAP_SIZE as "
"you could be out of mem in mbedtls");
}
if (ret < 0) {
goto close;
}
}
} while (ret != 0);
ctx->tls.handshake_done = true;
NET_DBG("TLS handshake done");
/* 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;
(void)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:
ctx->tls.connection_closing = true;
break;
default:
_net_app_print_error(
"mbedtls_ssl_read returned -0x%x",
ret);
break;
}
goto close;
}
if (ctx->cb.recv) {
struct sockaddr dst = { 0 };
struct net_context *net_ctx;
struct net_pkt *pkt;
int len = ret;
int hdr_len = 0;
dst.sa_family = AF_UNSPEC;
/* If we cannot select any net_ctx, then the connection
* is closed already.
*/
net_ctx = _net_app_select_net_ctx(ctx, &dst);
if (!net_ctx) {
ctx->tls.connection_closing = true;
ret = -EIO;
goto close;
}
pkt = net_pkt_get_rx(net_ctx, BUF_ALLOC_TIMEOUT);
if (!pkt) {
ret = -ENOMEM;
goto close;
}
/* Add the IP + UDP/TCP headers if found. This is done
* just in case the application needs to get some info
* from the IP header.
*/
if (ctx->tls.mbedtls.ssl_ctx.hdr) {
/* Needed to skip the protocol header */
hdr_len = ctx->tls.mbedtls.ssl_ctx.hdr->len;
net_pkt_frag_add(pkt,
ctx->tls.mbedtls.ssl_ctx.hdr);
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
net_pkt_set_ip_hdr_len(pkt,
sizeof(struct net_ipv6_hdr));
}
#endif
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
net_pkt_set_ip_hdr_len(pkt,
sizeof(struct net_ipv4_hdr));
}
#endif
ctx->tls.mbedtls.ssl_ctx.hdr = NULL;
}
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);
if (hdr_len) {
struct net_buf *frag;
u16_t pos;
frag = net_frag_get_pos(pkt, hdr_len, &pos);
if (!frag) {
/* FIXME: if pos is 0 here, hdr_len
* bytes were successfully skipped.
* Is closing the connection here the
* right thing?
*/
NET_ERR("could not skip %d bytes",
hdr_len);
net_pkt_unref(pkt);
ret = -EINVAL;
goto close;
}
net_pkt_set_appdata(pkt, frag->data + pos);
} else {
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);
if (ctx->tls.mbedtls.ssl_ctx.hdr) {
net_pkt_frag_unref(ctx->tls.mbedtls.ssl_ctx.hdr);
ctx->tls.mbedtls.ssl_ctx.hdr = NULL;
}
}
#if defined(CONFIG_NET_APP_DTLS)
if (ctx->proto == IPPROTO_UDP && ctx->dtls.ctx) {
NET_DBG("Releasing DTLS context %p", ctx->dtls.ctx);
dtls_cleanup(ctx, true);
}
#endif /* CONFIG_NET_APP_DTLS */
return ret;
}
int _net_app_tls_init(struct net_app_ctx *ctx, int client_or_server)
{
enum net_sock_type sock_type;
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(MBEDTLS_PRINT);
#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) && CONFIG_NET_APP_LOG_LEVEL >= LOG_LEVEL_DBG
mbedtls_debug_set_threshold(CONFIG_MBEDTLS_DEBUG_LEVEL);
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;
}
if (ctx->sock_type == SOCK_DGRAM) {
sock_type = MBEDTLS_SSL_TRANSPORT_DATAGRAM;
} else {
sock_type = MBEDTLS_SSL_TRANSPORT_STREAM;
}
/* Setup SSL defaults etc. */
ret = mbedtls_ssl_config_defaults(&ctx->tls.mbedtls.conf,
client_or_server,
sock_type,
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 defined(CONFIG_NET_APP_DTLS)
if (sock_type == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
ret = mbedtls_ssl_cookie_setup(&ctx->tls.mbedtls.cookie_ctx,
mbedtls_ctr_drbg_random,
&ctx->tls.mbedtls.ctr_drbg);
if (ret != 0) {
_net_app_print_error("mbedtls_ssl_cookie_setup "
"returned -0x%x", ret);
goto exit;
}
mbedtls_ssl_conf_dtls_cookies(&ctx->tls.mbedtls.conf,
mbedtls_ssl_cookie_write,
mbedtls_ssl_cookie_check,
&ctx->tls.mbedtls.cookie_ctx);
k_delayed_work_init(&ctx->dtls.fin_timer, dtls_timeout);
}
#endif /* CONFIG_NET_APP_DTLS */
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);
}
dtls_cleanup(ctx, true);
NET_DBG("TLS thread %p stopped", ctx->tls.tid);
k_thread_abort(ctx->tls.tid);
ctx->tls.tid = 0;
}
#endif /* CONFIG_NET_APP_TLS || CONFIG_NET_APP_DTLS */