/* tcp.c - TCP specific code for echo server */ /* * Copyright (c) 2017 Intel Corporation. * Copyright (c) 2018 Nordic Semiconductor ASA. * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_DECLARE(net_echo_server_sample, LOG_LEVEL_DBG); #include #include #include #include #include #include "common.h" #include "certificate.h" #define MAX_CLIENT_QUEUE CONFIG_NET_SAMPLE_NUM_HANDLERS #if defined(CONFIG_NET_IPV4) K_THREAD_STACK_ARRAY_DEFINE(tcp4_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS, STACK_SIZE); static struct k_thread tcp4_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS]; static APP_BMEM bool tcp4_handler_in_use[CONFIG_NET_SAMPLE_NUM_HANDLERS]; #endif #if defined(CONFIG_NET_IPV6) K_THREAD_STACK_ARRAY_DEFINE(tcp6_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS, STACK_SIZE); static struct k_thread tcp6_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS]; static APP_BMEM bool tcp6_handler_in_use[CONFIG_NET_SAMPLE_NUM_HANDLERS]; #endif static void process_tcp4(void); static void process_tcp6(void); K_THREAD_DEFINE(tcp4_thread_id, STACK_SIZE, process_tcp4, NULL, NULL, NULL, THREAD_PRIORITY, IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0, -1); K_THREAD_DEFINE(tcp6_thread_id, STACK_SIZE, process_tcp6, NULL, NULL, NULL, THREAD_PRIORITY, IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0, -1); static ssize_t sendall(int sock, const void *buf, size_t len) { while (len) { ssize_t out_len = send(sock, buf, len, 0); if (out_len < 0) { return out_len; } buf = (const char *)buf + out_len; len -= out_len; } return 0; } static int start_tcp_proto(struct data *data, struct sockaddr *bind_addr, socklen_t bind_addrlen) { int optval; int ret; #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TLS_1_2); #else data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TCP); #endif if (data->tcp.sock < 0) { LOG_ERR("Failed to create TCP socket (%s): %d", data->proto, errno); return -errno; } #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) sec_tag_t sec_tag_list[] = { SERVER_CERTIFICATE_TAG, #if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) PSK_TAG, #endif }; ret = setsockopt(data->tcp.sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list, sizeof(sec_tag_list)); if (ret < 0) { LOG_ERR("Failed to set TCP secure option (%s): %d", data->proto, errno); ret = -errno; } #endif if (bind_addr->sa_family == AF_INET6) { /* Prefer IPv6 temporary addresses */ optval = IPV6_PREFER_SRC_PUBLIC; (void)setsockopt(data->tcp.sock, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, &optval, sizeof(optval)); /* * Bind only to IPv6 without mapping to IPv4, since we bind to * IPv4 using another socket */ optval = 1; (void)setsockopt(data->tcp.sock, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)); } ret = bind(data->tcp.sock, bind_addr, bind_addrlen); if (ret < 0) { LOG_ERR("Failed to bind TCP socket (%s): %d", data->proto, errno); return -errno; } ret = listen(data->tcp.sock, MAX_CLIENT_QUEUE); if (ret < 0) { LOG_ERR("Failed to listen on TCP socket (%s): %d", data->proto, errno); ret = -errno; } return ret; } static void handle_data(void *ptr1, void *ptr2, void *ptr3) { int slot = POINTER_TO_INT(ptr1); struct data *data = ptr2; bool *in_use = ptr3; int offset = 0; int received; int client; int ret; client = data->tcp.accepted[slot].sock; do { received = recv(client, data->tcp.accepted[slot].recv_buffer + offset, sizeof(data->tcp.accepted[slot].recv_buffer) - offset, 0); if (received == 0) { /* Connection closed */ LOG_INF("TCP (%s): Connection closed", data->proto); break; } else if (received < 0) { /* Socket error */ LOG_ERR("TCP (%s): Connection error %d", data->proto, errno); break; } else { atomic_add(&data->tcp.bytes_received, received); } offset += received; #if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) /* To prevent fragmentation of the response, reply only if * buffer is full or there is no more data to read */ if (offset == sizeof(data->tcp.accepted[slot].recv_buffer) || (recv(client, data->tcp.accepted[slot].recv_buffer + offset, sizeof(data->tcp.accepted[slot].recv_buffer) - offset, MSG_PEEK | MSG_DONTWAIT) < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) { #endif ret = sendall(client, data->tcp.accepted[slot].recv_buffer, offset); if (ret < 0) { LOG_ERR("TCP (%s): Failed to send, " "closing socket", data->proto); break; } LOG_DBG("TCP (%s): Received and replied with %d bytes", data->proto, offset); if (++data->tcp.accepted[slot].counter % 1000 == 0U) { LOG_INF("%s TCP: Sent %u packets", data->proto, data->tcp.accepted[slot].counter); } offset = 0; #if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) } #endif } while (true); *in_use = false; (void)close(client); data->tcp.accepted[slot].sock = -1; } static int get_free_slot(struct data *data) { int i; for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { if (data->tcp.accepted[i].sock < 0) { return i; } } return -1; } static int process_tcp(struct data *data) { int client; int slot; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); LOG_INF("Waiting for TCP connection on port %d (%s)...", MY_PORT, data->proto); client = accept(data->tcp.sock, (struct sockaddr *)&client_addr, &client_addr_len); if (client < 0) { LOG_ERR("%s accept error (%d)", data->proto, -errno); return -errno; } slot = get_free_slot(data); if (slot < 0) { LOG_ERR("Cannot accept more connections"); close(client); return 0; } data->tcp.accepted[slot].sock = client; LOG_INF("TCP (%s): Accepted connection", data->proto); #define MAX_NAME_LEN sizeof("tcp6[xxx]") #if defined(CONFIG_NET_IPV6) if (client_addr.sin_family == AF_INET6) { tcp6_handler_in_use[slot] = true; k_thread_create( &tcp6_handler_thread[slot], tcp6_handler_stack[slot], K_THREAD_STACK_SIZEOF(tcp6_handler_stack[slot]), handle_data, INT_TO_POINTER(slot), data, &tcp6_handler_in_use[slot], THREAD_PRIORITY, IS_ENABLED(CONFIG_USERSPACE) ? K_USER | K_INHERIT_PERMS : 0, K_NO_WAIT); if (IS_ENABLED(CONFIG_THREAD_NAME)) { char name[MAX_NAME_LEN]; snprintk(name, sizeof(name), "tcp6[%3d]", (uint8_t)slot); k_thread_name_set(&tcp6_handler_thread[slot], name); } } #endif #if defined(CONFIG_NET_IPV4) if (client_addr.sin_family == AF_INET) { tcp4_handler_in_use[slot] = true; k_thread_create( &tcp4_handler_thread[slot], tcp4_handler_stack[slot], K_THREAD_STACK_SIZEOF(tcp4_handler_stack[slot]), handle_data, INT_TO_POINTER(slot), data, &tcp4_handler_in_use[slot], THREAD_PRIORITY, IS_ENABLED(CONFIG_USERSPACE) ? K_USER | K_INHERIT_PERMS : 0, K_NO_WAIT); if (IS_ENABLED(CONFIG_THREAD_NAME)) { char name[MAX_NAME_LEN]; snprintk(name, sizeof(name), "tcp4[%3d]", (uint8_t)slot); k_thread_name_set(&tcp4_handler_thread[slot], name); } } #endif return 0; } static void process_tcp4(void) { int ret; struct sockaddr_in addr4; (void)memset(&addr4, 0, sizeof(addr4)); addr4.sin_family = AF_INET; addr4.sin_port = htons(MY_PORT); ret = start_tcp_proto(&conf.ipv4, (struct sockaddr *)&addr4, sizeof(addr4)); if (ret < 0) { quit(); return; } while (ret == 0) { ret = process_tcp(&conf.ipv4); if (ret < 0) { break; } } quit(); } static void process_tcp6(void) { int ret; struct sockaddr_in6 addr6; (void)memset(&addr6, 0, sizeof(addr6)); addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(MY_PORT); ret = start_tcp_proto(&conf.ipv6, (struct sockaddr *)&addr6, sizeof(addr6)); if (ret < 0) { quit(); return; } while (ret == 0) { ret = process_tcp(&conf.ipv6); if (ret != 0) { break; } } quit(); } static void print_stats(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct data *data = CONTAINER_OF(dwork, struct data, tcp.stats_print); int total_received = atomic_get(&data->tcp.bytes_received); if (total_received) { if ((total_received / STATS_TIMER) < 1024) { LOG_INF("%s TCP: Received %d B/sec", data->proto, total_received / STATS_TIMER); } else { LOG_INF("%s TCP: Received %d KiB/sec", data->proto, total_received / 1024 / STATS_TIMER); } atomic_set(&data->tcp.bytes_received, 0); } k_work_reschedule(&data->tcp.stats_print, K_SECONDS(STATS_TIMER)); } void start_tcp(void) { int i; for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { conf.ipv6.tcp.accepted[i].sock = -1; conf.ipv4.tcp.accepted[i].sock = -1; #if defined(CONFIG_NET_IPV4) tcp4_handler_in_use[i] = false; #endif #if defined(CONFIG_NET_IPV6) tcp6_handler_in_use[i] = false; #endif } #if defined(CONFIG_NET_IPV6) #if defined(CONFIG_USERSPACE) k_mem_domain_add_thread(&app_domain, tcp6_thread_id); for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { k_thread_access_grant(tcp6_thread_id, &tcp6_handler_thread[i]); k_thread_access_grant(tcp6_thread_id, &tcp6_handler_stack[i]); } #endif k_work_init_delayable(&conf.ipv6.tcp.stats_print, print_stats); k_thread_start(tcp6_thread_id); k_work_reschedule(&conf.ipv6.tcp.stats_print, K_SECONDS(STATS_TIMER)); #endif #if defined(CONFIG_NET_IPV4) #if defined(CONFIG_USERSPACE) k_mem_domain_add_thread(&app_domain, tcp4_thread_id); for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { k_thread_access_grant(tcp4_thread_id, &tcp4_handler_thread[i]); k_thread_access_grant(tcp4_thread_id, &tcp4_handler_stack[i]); } #endif k_work_init_delayable(&conf.ipv4.tcp.stats_print, print_stats); k_thread_start(tcp4_thread_id); k_work_reschedule(&conf.ipv4.tcp.stats_print, K_SECONDS(STATS_TIMER)); #endif } void stop_tcp(void) { int i; /* Not very graceful way to close a thread, but as we may be blocked * in accept or recv call it seems to be necessary */ if (IS_ENABLED(CONFIG_NET_IPV6)) { k_thread_abort(tcp6_thread_id); if (conf.ipv6.tcp.sock >= 0) { (void)close(conf.ipv6.tcp.sock); } for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { #if defined(CONFIG_NET_IPV6) if (tcp6_handler_in_use[i] == true) { k_thread_abort(&tcp6_handler_thread[i]); } #endif if (conf.ipv6.tcp.accepted[i].sock >= 0) { (void)close(conf.ipv6.tcp.accepted[i].sock); } } } if (IS_ENABLED(CONFIG_NET_IPV4)) { k_thread_abort(tcp4_thread_id); if (conf.ipv4.tcp.sock >= 0) { (void)close(conf.ipv4.tcp.sock); } for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) { #if defined(CONFIG_NET_IPV4) if (tcp4_handler_in_use[i] == true) { k_thread_abort(&tcp4_handler_thread[i]); } #endif if (conf.ipv4.tcp.accepted[i].sock >= 0) { (void)close(conf.ipv4.tcp.accepted[i].sock); } } } }