| /* tcp.c - TCP specific code for echo client */ |
| |
| /* |
| * Copyright (c) 2017 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #if 1 |
| #define SYS_LOG_DOMAIN "echo-client" |
| #define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG |
| #define NET_LOG_ENABLED 1 |
| #endif |
| |
| #include <zephyr.h> |
| #include <errno.h> |
| #include <stdio.h> |
| |
| #include <net/net_pkt.h> |
| #include <net/net_core.h> |
| #include <net/net_context.h> |
| |
| #include <net/net_app.h> |
| |
| #include "common.h" |
| |
| static struct net_app_ctx tcp6; |
| static struct net_app_ctx tcp4; |
| |
| static int connected_count; |
| |
| /* Note that both tcp and udp can share the same pool but in this |
| * example the UDP context and TCP context have separate pools. |
| */ |
| #if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) |
| NET_PKT_TX_SLAB_DEFINE(echo_tx_tcp, 15); |
| NET_PKT_DATA_POOL_DEFINE(echo_data_tcp, 30); |
| |
| static struct k_mem_slab *tx_tcp_slab(void) |
| { |
| return &echo_tx_tcp; |
| } |
| |
| static struct net_buf_pool *data_tcp_pool(void) |
| { |
| return &echo_data_tcp; |
| } |
| #else |
| #define tx_tcp_slab NULL |
| #define data_tcp_pool NULL |
| #endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */ |
| |
| #if defined(CONFIG_NET_APP_TLS) |
| #define HOSTNAME "localhost" /* for cert verification if that is enabled */ |
| |
| /* The result buf size is set to large enough so that we can receive max size |
| * buf back. Note that mbedtls needs also be configured to have equal size |
| * value for its buffer size. See MBEDTLS_SSL_MAX_CONTENT_LEN option in TLS |
| * config file. |
| */ |
| #define RESULT_BUF_SIZE 1500 |
| static u8_t tls_result_ipv6[RESULT_BUF_SIZE]; |
| static u8_t tls_result_ipv4[RESULT_BUF_SIZE]; |
| |
| #if !defined(CONFIG_NET_APP_TLS_STACK_SIZE) |
| #define CONFIG_NET_APP_TLS_STACK_SIZE 6144 |
| #endif /* CONFIG_NET_APP_TLS_STACK_SIZE */ |
| |
| #define INSTANCE_INFO "Zephyr TLS echo-client #1" |
| |
| /* Note that each net_app context needs its own stack as there will be |
| * a separate thread needed. |
| */ |
| NET_STACK_DEFINE(NET_APP_TLS_IPv4, net_app_tls_stack_ipv4, |
| CONFIG_NET_APP_TLS_STACK_SIZE, CONFIG_NET_APP_TLS_STACK_SIZE); |
| |
| NET_STACK_DEFINE(NET_APP_TLS_IPv6, net_app_tls_stack_ipv6, |
| CONFIG_NET_APP_TLS_STACK_SIZE, CONFIG_NET_APP_TLS_STACK_SIZE); |
| |
| NET_APP_TLS_POOL_DEFINE(ssl_pool, 10); |
| #else |
| #define tls_result_ipv6 NULL |
| #define tls_result_ipv4 NULL |
| #define net_app_tls_stack_ipv4 NULL |
| #define net_app_tls_stack_ipv6 NULL |
| #endif /* CONFIG_NET_APP_TLS */ |
| |
| #if defined(CONFIG_NET_APP_TLS) |
| /* Load the certificates and private RSA key. */ |
| |
| #include "test_certs.h" |
| |
| static int setup_cert(struct net_app_ctx *ctx, void *cert) |
| { |
| #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) |
| mbedtls_ssl_conf_psk(&ctx->tls.mbedtls.conf, |
| client_psk, sizeof(client_psk), |
| (const unsigned char *)client_psk_id, |
| sizeof(client_psk_id) - 1); |
| #endif |
| |
| #if defined(MBEDTLS_X509_CRT_PARSE_C) |
| { |
| mbedtls_x509_crt *ca_cert = cert; |
| int ret; |
| |
| ret = mbedtls_x509_crt_parse_der(ca_cert, |
| ca_certificate, |
| sizeof(ca_certificate)); |
| if (ret != 0) { |
| NET_ERR("mbedtls_x509_crt_parse_der failed " |
| "(-0x%x)", -ret); |
| return ret; |
| } |
| |
| mbedtls_ssl_conf_ca_chain(&ctx->tls.mbedtls.conf, |
| ca_cert, NULL); |
| |
| /* In this example, we skip the certificate checks. In real |
| * life scenarios, one should always verify the certificates. |
| */ |
| mbedtls_ssl_conf_authmode(&ctx->tls.mbedtls.conf, |
| MBEDTLS_SSL_VERIFY_REQUIRED); |
| |
| mbedtls_ssl_conf_cert_profile(&ctx->tls.mbedtls.conf, |
| &mbedtls_x509_crt_profile_default); |
| #define VERIFY_CERTS 0 |
| #if VERIFY_CERTS |
| mbedtls_ssl_conf_authmode(&ctx->tls.mbedtls.conf, |
| MBEDTLS_SSL_VERIFY_OPTIONAL); |
| #else |
| ; |
| #endif /* VERIFY_CERTS */ |
| } |
| #endif /* MBEDTLS_X509_CRT_PARSE_C */ |
| |
| return 0; |
| } |
| #endif /* CONFIG_NET_APP_TLS */ |
| |
| static void send_tcp_data(struct net_app_ctx *ctx, |
| struct data *data) |
| { |
| struct net_pkt *pkt; |
| size_t len; |
| int ret; |
| |
| do { |
| data->expecting_tcp = sys_rand32_get() % ipsum_len; |
| } while (data->expecting_tcp == 0); |
| |
| data->received_tcp = 0; |
| |
| pkt = prepare_send_pkt(ctx, data->proto, data->expecting_tcp); |
| if (!pkt) { |
| return; |
| } |
| |
| len = net_pkt_get_len(pkt); |
| |
| NET_ASSERT_INFO(data->expecting_tcp == len, |
| "%s data to send %d bytes, real len %zu", |
| data->proto, data->expecting_tcp, len); |
| |
| ret = net_app_send_pkt(ctx, pkt, NULL, 0, K_FOREVER, |
| UINT_TO_POINTER(len)); |
| if (ret < 0) { |
| NET_ERR("Cannot send %s data to peer (%d)", data->proto, ret); |
| net_pkt_unref(pkt); |
| } |
| } |
| |
| static bool compare_tcp_data(struct net_pkt *pkt, int expecting_len, |
| int received_len) |
| { |
| u8_t *ptr = net_pkt_appdata(pkt); |
| const char *start; |
| int pos = 0; |
| struct net_buf *frag; |
| int len; |
| |
| /* frag will point to first fragment with IP header in it. |
| */ |
| frag = pkt->frags; |
| |
| /* Do not include the protocol headers for the first fragment. |
| * The remaining fragments contain only data so the user data |
| * length is directly the fragment len. |
| */ |
| len = frag->len - (ptr - frag->data); |
| |
| start = lorem_ipsum + received_len; |
| |
| while (frag) { |
| if (memcmp(ptr, start + pos, len)) { |
| NET_DBG("Invalid data received"); |
| return false; |
| } |
| |
| pos += len; |
| |
| frag = frag->frags; |
| if (!frag) { |
| break; |
| } |
| |
| ptr = frag->data; |
| len = frag->len; |
| } |
| |
| NET_DBG("Compared %d bytes, all ok", net_pkt_appdatalen(pkt)); |
| |
| return true; |
| } |
| |
| static void tcp_received(struct net_app_ctx *ctx, |
| struct net_pkt *pkt, |
| int status, |
| void *user_data) |
| { |
| struct data *data = ctx->user_data; |
| |
| ARG_UNUSED(user_data); |
| ARG_UNUSED(status); |
| |
| if (!pkt || net_pkt_appdatalen(pkt) == 0) { |
| if (pkt) { |
| net_pkt_unref(pkt); |
| } |
| |
| return; |
| } |
| |
| NET_DBG("Sent %d bytes, received %u bytes", |
| data->expecting_tcp, net_pkt_appdatalen(pkt)); |
| |
| if (!compare_tcp_data(pkt, data->expecting_tcp, data->received_tcp)) { |
| NET_DBG("Data mismatch"); |
| } else { |
| data->received_tcp += net_pkt_appdatalen(pkt); |
| } |
| |
| if (data->expecting_tcp <= data->received_tcp) { |
| /* Send more data */ |
| send_tcp_data(ctx, data); |
| } |
| |
| net_pkt_unref(pkt); |
| } |
| |
| static void tcp_connected(struct net_app_ctx *ctx, |
| int status, |
| void *user_data) |
| { |
| if (status < 0) { |
| return; |
| } |
| |
| connected_count++; |
| |
| if (IS_ENABLED(CONFIG_NET_UDP)) { |
| if (IS_ENABLED(CONFIG_NET_IPV6) && |
| IS_ENABLED(CONFIG_NET_IPV4)) { |
| if (connected_count > 1) { |
| k_sem_give(&tcp_ready); |
| } |
| } else { |
| k_sem_give(&tcp_ready); |
| } |
| } |
| |
| send_tcp_data(ctx, user_data); |
| } |
| |
| static int connect_tcp(struct net_app_ctx *ctx, const char *peer, |
| void *user_data, u8_t *result_buf, |
| size_t result_buf_len, u8_t *stack, size_t stack_size) |
| { |
| struct data *data = user_data; |
| int ret; |
| |
| ret = net_app_init_tcp_client(ctx, NULL, NULL, peer, PEER_PORT, |
| WAIT_TIME, user_data); |
| if (ret < 0) { |
| NET_ERR("Cannot init %s TCP client (%d)", data->proto, ret); |
| goto fail; |
| } |
| |
| #if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) |
| net_app_set_net_pkt_pool(ctx, tx_tcp_slab, data_tcp_pool); |
| #endif |
| |
| ret = net_app_set_cb(ctx, tcp_connected, tcp_received, NULL, NULL); |
| if (ret < 0) { |
| NET_ERR("Cannot set callbacks (%d)", ret); |
| goto fail; |
| } |
| |
| #if defined(CONFIG_NET_APP_TLS) |
| ret = net_app_client_tls(ctx, |
| result_buf, |
| result_buf_len, |
| INSTANCE_INFO, |
| strlen(INSTANCE_INFO), |
| setup_cert, |
| HOSTNAME, |
| NULL, |
| &ssl_pool, |
| stack, |
| stack_size); |
| if (ret < 0) { |
| NET_ERR("Cannot init TLS"); |
| goto fail; |
| } |
| #endif |
| |
| ret = net_app_connect(ctx, CONNECT_TIME); |
| if (ret < 0) { |
| NET_ERR("Cannot connect TCP (%d)", ret); |
| goto fail; |
| } |
| |
| fail: |
| return ret; |
| } |
| |
| int start_tcp(void) |
| { |
| int ret = 0; |
| |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| ret = connect_tcp(&tcp6, CONFIG_NET_APP_PEER_IPV6_ADDR, |
| &conf.ipv6, tls_result_ipv6, |
| sizeof(tls_result_ipv6), |
| net_app_tls_stack_ipv6, |
| K_THREAD_STACK_SIZEOF( |
| net_app_tls_stack_ipv6)); |
| if (ret < 0) { |
| NET_ERR("Cannot init IPv6 TCP client (%d)", ret); |
| } |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| ret = connect_tcp(&tcp4, CONFIG_NET_APP_PEER_IPV4_ADDR, |
| &conf.ipv4, tls_result_ipv4, |
| sizeof(tls_result_ipv4), |
| net_app_tls_stack_ipv4, |
| K_THREAD_STACK_SIZEOF( |
| net_app_tls_stack_ipv4)); |
| if (ret < 0) { |
| NET_ERR("Cannot init IPv6 TCP client (%d)", ret); |
| } |
| } |
| |
| return ret; |
| } |
| |
| void stop_tcp(void) |
| { |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| net_app_close(&tcp6); |
| net_app_release(&tcp6); |
| } |
| |
| if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| net_app_close(&tcp4); |
| net_app_release(&tcp4); |
| } |
| } |