diff --git a/samples/net/echo_loopback/Makefile b/samples/net/echo_loopback/Makefile new file mode 100644 index 0000000000000..ebf59c59f7585 --- /dev/null +++ b/samples/net/echo_loopback/Makefile @@ -0,0 +1,13 @@ +# Makefile - echo client/server test application using loopback interface. + +# +# Copyright (c) 2015 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +BOARD ?= qemu_x86 +CONF_FILE ?= prj_$(BOARD).conf + +include $(ZEPHYR_BASE)/Makefile.inc +include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack diff --git a/samples/net/echo_loopback/prj_qemu_x86.conf b/samples/net/echo_loopback/prj_qemu_x86.conf new file mode 100644 index 0000000000000..c128802440570 --- /dev/null +++ b/samples/net/echo_loopback/prj_qemu_x86.conf @@ -0,0 +1,36 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y +CONFIG_NET_UDP=n +CONFIG_NET_TCP=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_BUF_LOG=y +CONFIG_SYS_LOG_NET_BUF_LEVEL=2 +CONFIG_NET_LOG=y +CONFIG_SYS_LOG_NET_LEVEL=2 +CONFIG_NET_SLIP_TAP=y +CONFIG_SYS_LOG_SHOW_COLOR=y +CONFIG_INIT_STACKS=y +CONFIG_PRINTK=y +CONFIG_NET_STATISTICS=y +CONFIG_NET_PKT_RX_COUNT=20 +CONFIG_NET_PKT_TX_COUNT=20 +CONFIG_NET_BUF_RX_COUNT=40 +CONFIG_NET_BUF_TX_COUNT=40 +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=4 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 +CONFIG_NET_MAX_CONTEXTS=16 +CONFIG_NET_MAX_CONN=16 +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT=y +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_IPV6_ND=n +CONFIG_NET_IPV6_DAD=n +CONFIG_NET_ARP=n +CONFIG_NET_CONTEXT_NET_PKT_POOL=y + +CONFIG_NET_SHELL=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" diff --git a/samples/net/echo_loopback/src/Makefile b/samples/net/echo_loopback/src/Makefile new file mode 100644 index 0000000000000..10794b00a7deb --- /dev/null +++ b/samples/net/echo_loopback/src/Makefile @@ -0,0 +1 @@ +obj-y = echo-loopback.o server.o client.o diff --git a/samples/net/echo_loopback/src/client.c b/samples/net/echo_loopback/src/client.c new file mode 100644 index 0000000000000..b7c84c67a3716 --- /dev/null +++ b/samples/net/echo_loopback/src/client.c @@ -0,0 +1,880 @@ +/* client.c - Networking echo client/server combined */ + +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if 1 +#define SYS_LOG_DOMAIN "echo-cli" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void panic(const char *msg); + +/* Generated by http://www.lipsum.com/ + * 3 paragraphs, 176 words, 1230 bytes of Lorem Ipsum + */ +static char *lorem_ipsum = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Vestibulum id cursus felis, sit amet suscipit velit. Integer " + "facilisis malesuada porta. Nunc at accumsan mauris. Etiam vehicula, " + "arcu consequat feugiat venenatis, tellus velit gravida ligula, quis " + "posuere sem leo eget urna. Curabitur condimentum leo nec orci " + "mattis, nec faucibus dui rutrum. Ut mollis orci in iaculis " + "consequat. Nulla volutpat nibh eu velit sagittis, a iaculis dui " + "aliquam." + "\n" + "Quisque interdum consequat eros a eleifend. Fusce dapibus nisl " + "sit amet velit posuere imperdiet. Quisque accumsan tempor massa " + "sit amet tincidunt. Integer sollicitudin vehicula tristique. Nulla " + "sagittis massa turpis, ac ultricies neque posuere eu. Nulla et " + "imperdiet ex. Etiam venenatis sed lacus tincidunt hendrerit. In " + "libero nisl, congue id tellus vitae, tincidunt tristique mauris. " + "Nullam sed porta massa. Sed condimentum sem eu convallis euismod. " + "Suspendisse lobortis purus faucibus, gravida turpis id, mattis " + "velit. Maecenas eleifend sapien eu tincidunt lobortis. Sed elementum " + "sapien id enim laoreet consequat." + "\n" + "Aenean et neque aliquam, lobortis lectus in, consequat leo. Sed " + "quis egestas nulla. Quisque ac risus quis elit mollis finibus. " + "Phasellus efficitur imperdiet metus." + "\n"; + +#define STACKSIZE 1024*2 + +#define MAX_DBG_PRINT 64 + +static struct net_context *client_udp_send4; +static struct net_context *client_udp_send6; +static struct net_context *client_tcp_send4; +static struct net_context *client_tcp_send6; + +static int ipsum_len; + +/* 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) + +#if defined(CONFIG_NET_TCP) +NET_PKT_TX_SLAB_DEFINE(echo_tx_tcp, 30); +NET_PKT_DATA_POOL_DEFINE(echo_data_tcp, 230); + +static struct k_mem_slab *tx_tcp_pool(void) +{ + return &echo_tx_tcp; +} + +static struct net_buf_pool *data_tcp_pool(void) +{ + return &echo_data_tcp; +} +#endif /* TCP */ + +#if defined(CONFIG_NET_UDP) +NET_PKT_TX_SLAB_DEFINE(echo_tx_udp, 30); +NET_PKT_DATA_POOL_DEFINE(echo_data_udp, 70); + +static struct k_mem_slab *tx_udp_pool(void) +{ + return &echo_tx_udp; +} + +static struct net_buf_pool *data_udp_pool(void) +{ + return &echo_data_udp; +} +#endif /* UDP */ +#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */ + +#define MY_PORT 8484 +#define PEER_PORT 4242 + +struct data { + uint32_t expecting_udp; + uint32_t expecting_tcp; + uint32_t received_tcp; +}; + +static struct { +#if defined(CONFIG_NET_UDP) + /* semaphore for controlling udp data sending */ + struct k_sem recv_ipv6; + struct k_sem recv_ipv4; +#endif /* CONFIG_NET_UDP */ + +#if defined(CONFIG_NET_TCP) + /* semaphore for controlling tcp data sending */ + struct k_sem send_ipv6; + struct k_sem send_ipv4; +#endif /* CONFIG_NET_UDP */ + + struct data ipv4; + struct data ipv6; +} conf; + +#if defined(CONFIG_NET_TCP) +static bool send_tcp_data(struct net_context *ctx, + char *proto, + struct data *data); +#endif /* CONFIG_NET_TCP */ + +#if defined(CONFIG_NET_IPV6) + +/* Default IP address if not found in config file */ +#define PEER_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x1 } } } +#define MY_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x1 } } } + +#define MY_PREFIX_LEN 64 + +static struct in6_addr in6addr_my = MY_IP6ADDR; + +static struct sockaddr_in6 my_addr6 = { + .sin6_family = AF_INET6, + .sin6_port = htons(MY_PORT), +}; + +static struct sockaddr_in6 peer_addr6 = { + .sin6_family = AF_INET6, + .sin6_port = htons(PEER_PORT), +}; + +#if defined(CONFIG_NET_UDP) +NET_STACK_DEFINE(IPv6_UDP, ipv6_udp_stack, STACKSIZE, STACKSIZE); +#endif + +#if defined(CONFIG_NET_TCP) +NET_STACK_DEFINE(IPv6_TCP, ipv6_tcp_stack, STACKSIZE, STACKSIZE); +#endif + +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) +#define MY_IP4ADDR { { { 192, 0, 2, 1 } } } +#define PEER_IP4ADDR { { { 192, 0, 2, 2 } } } + +static struct in_addr in4addr_my = MY_IP4ADDR; + +static struct sockaddr_in my_addr4 = { + .sin_family = AF_INET, + .sin_port = htons(MY_PORT), +}; + +static struct sockaddr_in peer_addr4 = { + .sin_family = AF_INET, + .sin_port = htons(PEER_PORT), +}; + +#if defined(CONFIG_NET_UDP) +NET_STACK_DEFINE(IPv4_UDP, ipv4_udp_stack, STACKSIZE, STACKSIZE); +#endif + +#if defined(CONFIG_NET_TCP) +NET_STACK_DEFINE(IPv4_TCP, ipv4_tcp_stack, STACKSIZE, STACKSIZE); +#endif + +#endif /* CONFIG_NET_IPV4 */ + +#define WAIT_TIME K_SECONDS(2) + +static void client_init(void) +{ + char buf[NET_IPV6_ADDR_LEN]; + +#if defined(CONFIG_NET_IPV6) +#if defined(CONFIG_NET_APP_SETTINGS) + if (net_addr_pton(AF_INET6, + CONFIG_NET_APP_MY_IPV6_ADDR, + &my_addr6.sin6_addr) < 0) { + NET_ERR("Invalid IPv6 address %s", + CONFIG_NET_APP_MY_IPV6_ADDR); + + net_ipaddr_copy(&my_addr6.sin6_addr, &in6addr_my); + } + + NET_DBG("Client IPv6 address %s", + net_addr_ntop(AF_INET6, &my_addr6.sin6_addr, + (char *)buf, sizeof(buf))); + + net_ipaddr_copy(&peer_addr6.sin6_addr, &in6addr_my); + + NET_DBG("Client peer IPv6 address %s", + net_addr_ntop(AF_INET6, &peer_addr6.sin6_addr, + (char *)buf, sizeof(buf))); +#endif + + do { + struct net_if_addr *ifaddr; + + ifaddr = net_if_ipv6_addr_add(net_if_get_default(), + &my_addr6.sin6_addr, + NET_ADDR_MANUAL, 0); + } while (0); + +#if defined(CONFIG_NET_UDP) + k_sem_init(&conf.recv_ipv6, 0, UINT_MAX); +#endif + +#if defined(CONFIG_NET_TCP) + k_sem_init(&conf.send_ipv6, 0, UINT_MAX); +#endif +#endif + +#if defined(CONFIG_NET_IPV4) +#if defined(CONFIG_NET_APP_SETTINGS) + if (net_addr_pton(AF_INET, + CONFIG_NET_APP_MY_IPV4_ADDR, + &my_addr4.sin_addr) < 0) { + NET_ERR("Invalid IPv4 address %s", + CONFIG_NET_APP_MY_IPV4_ADDR); + + net_ipaddr_copy(&my_addr4.sin_addr, &in4addr_my); + } + + NET_DBG("Client IPv4 address %s", + net_addr_ntop(AF_INET, &my_addr4.sin_addr, + (char *)buf, sizeof(buf))); + + net_ipaddr_copy(&peer_addr4.sin_addr, &in4addr_my); + + NET_DBG("Client peer IPv4 address %s", + net_addr_ntop(AF_INET, &peer_addr4.sin_addr, + (char *)buf, sizeof(buf))); +#endif + + net_if_ipv4_addr_add(net_if_get_default(), &my_addr4.sin_addr, + NET_ADDR_MANUAL, 0); + +#if defined(CONFIG_NET_UDP) + k_sem_init(&conf.recv_ipv4, 0, UINT_MAX); +#endif + +#if defined(CONFIG_NET_TCP) + k_sem_init(&conf.send_ipv4, 0, UINT_MAX); +#endif +#endif +} + +static inline bool get_context_client(struct net_context **udp_recv4, + struct net_context **udp_recv6, + struct net_context **tcp_recv4, + struct net_context **tcp_recv6) +{ + int ret; + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_UDP) + ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, udp_recv6); + if (ret < 0) { + NET_ERR("Cannot get network context for IPv6 UDP (%d)", + ret); + return false; + } + + net_context_setup_pools(*udp_recv6, tx_udp_pool, data_udp_pool); + + ret = net_context_bind(*udp_recv6, (struct sockaddr *)&my_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + NET_ERR("Cannot bind IPv6 UDP port %d (%d)", + ntohs(my_addr6.sin6_port), ret); + return false; + } +#endif + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_UDP) + ret = net_context_get(AF_INET, SOCK_DGRAM, IPPROTO_UDP, udp_recv4); + if (ret < 0) { + NET_ERR("Cannot get network context for IPv4 UDP (%d)", + ret); + return false; + } + + net_context_setup_pools(*udp_recv4, tx_udp_pool, data_udp_pool); + + ret = net_context_bind(*udp_recv4, (struct sockaddr *)&my_addr4, + sizeof(struct sockaddr_in)); + if (ret < 0) { + NET_ERR("Cannot bind IPv4 UDP port %d (%d)", + ntohs(my_addr4.sin_port), ret); + return false; + } +#endif + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_TCP) + if (tcp_recv6) { + ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, + tcp_recv6); + if (ret < 0) { + NET_ERR("Cannot get network context " + "for IPv6 TCP (%d)", ret); + return false; + } + + net_context_setup_pools(*tcp_recv6, tx_tcp_pool, data_tcp_pool); + + ret = net_context_bind(*tcp_recv6, + (struct sockaddr *)&my_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + NET_ERR("Cannot bind IPv6 TCP port %d (%d)", + ntohs(my_addr6.sin6_port), ret); + return false; + } + } +#endif + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_TCP) + if (tcp_recv4) { + ret = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, + tcp_recv4); + if (ret < 0) { + NET_ERR("Cannot get network context for IPv4 TCP"); + return false; + } + + net_context_setup_pools(*tcp_recv4, tx_tcp_pool, data_tcp_pool); + + ret = net_context_bind(*tcp_recv4, + (struct sockaddr *)&my_addr4, + sizeof(struct sockaddr_in)); + if (ret < 0) { + NET_ERR("Cannot bind IPv4 TCP port %d", + ntohs(my_addr4.sin_port)); + return false; + } + } +#endif + + return true; +} + +static inline bool wait_reply(const char *name, + struct k_sem *sem) +{ + int ret = k_sem_take(sem, WAIT_TIME); + + ARG_UNUSED(name); + + if (!ret) { + return true; + } + + NET_ERR("wait_reply returned %s", + ret == -EAGAIN ? "on time out" : "directly"); + + return false; +} + +static struct net_pkt *prepare_send_pkt(const char *name, + struct net_context *context, + int expecting_len) +{ + struct net_pkt *send_pkt; + bool status; + + send_pkt = net_pkt_get_tx(context, K_FOREVER); + + NET_ASSERT(send_pkt); + + status = net_pkt_append(send_pkt, expecting_len, lorem_ipsum, + K_FOREVER); + if (!status) { + NET_ERR("%s: cannot create send buf", name); + net_pkt_unref(send_pkt); + return NULL; + } + + return send_pkt; +} + +static inline void udp_sent(struct net_context *context, + int status, + void *bytes_sent, + void *user_data) +{ + ARG_UNUSED(context); + + if (!status) { + NET_INFO("%s: sent %u bytes", (char *)user_data, + POINTER_TO_UINT(bytes_sent)); + } +} + +#if defined(CONFIG_NET_UDP) +static bool compare_udp_data(struct net_pkt *pkt, int expecting_len) +{ + uint8_t *ptr = net_pkt_appdata(pkt); + struct net_buf *buf; + int pos = 0; + int len; + + /* Buf will now point to first fragment with IP header + * in it. + */ + buf = pkt->frags; + + /* Do not include the protocol headers in the first fragment. + * The remaining fragments contain only data so the user data + * length is directly the fragment len. + */ + len = buf->len - (ptr - buf->data); + + while (buf) { + if (memcmp(ptr, lorem_ipsum + pos, len)) { + NET_DBG("Invalid data received"); + return false; + } else { + pos += len; + + buf = buf->frags; + if (!buf) { + break; + } + + ptr = buf->data; + len = buf->len; + } + } + + NET_DBG("Compared %d bytes, all ok", expecting_len); + + return true; +} + +static void client_setup_udp_recv(struct net_context *udp, void *user_data, + net_context_recv_cb_t cb) +{ + int ret; + + ret = net_context_recv(udp, cb, 0, user_data); + if (ret < 0) { + NET_ERR("Cannot receive UDP packets"); + } +} + +static bool send_udp_data(struct net_context *udp, + sa_family_t family, + char *proto, + struct data *data) +{ + bool status = false; + struct net_pkt *send_pkt; + struct sockaddr dst_addr; + socklen_t addrlen; + size_t len; + int ret; + + data->expecting_udp = sys_rand32_get() % ipsum_len; + + send_pkt = prepare_send_pkt(proto, udp, data->expecting_udp); + if (!send_pkt) { + goto out; + } + + len = net_buf_frags_len(send_pkt->frags); + + NET_ASSERT_INFO(data->expecting_udp == len, + "Data to send %d bytes, real len %zu", + data->expecting_udp, len); + + if (family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + addrlen = sizeof(struct sockaddr_in6); + net_ipaddr_copy(&net_sin6(&dst_addr)->sin6_addr, + &peer_addr6.sin6_addr); + net_sin6(&dst_addr)->sin6_port = htons(PEER_PORT); + net_sin6(&dst_addr)->sin6_family = AF_INET6; +#endif + } else { +#if defined(CONFIG_NET_IPV4) + addrlen = sizeof(struct sockaddr_in); + net_ipaddr_copy(&net_sin(&dst_addr)->sin_addr, + &peer_addr4.sin_addr); + net_sin(&dst_addr)->sin_port = htons(PEER_PORT); + net_sin(&dst_addr)->sin_family = AF_INET; +#endif + } + + ret = net_context_sendto(send_pkt, &dst_addr, + addrlen, udp_sent, 0, + UINT_TO_POINTER(len), + proto); + if (ret < 0) { + NET_ERR("Cannot send %s data to peer (%d)", proto, ret); + net_pkt_unref(send_pkt); + } else { + status = true; + } +out: + + return status; +} + +static void client_udp_received(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data) +{ + sa_family_t family = net_pkt_family(pkt); + struct data *data = user_data; + struct k_sem *recv; + + ARG_UNUSED(context); + ARG_UNUSED(status); + + if (family == AF_INET) { + recv = &conf.recv_ipv4; + } else { + recv = &conf.recv_ipv6; + } + + if (data->expecting_udp != net_pkt_appdatalen(pkt)) { + NET_ERR("Sent %d bytes, received %u bytes", + data->expecting_udp, net_pkt_appdatalen(pkt)); + } + + if (!compare_udp_data(pkt, data->expecting_udp)) { + NET_DBG("Data mismatch"); + } + + net_pkt_unref(pkt); + + k_sem_give(recv); +} + +static void send_udp(struct net_context *udp, + sa_family_t family, + char *proto, + struct k_sem *sem, + struct data *data) +{ + client_setup_udp_recv(udp, data, client_udp_received); + + NET_INFO("Starting to send %s data", proto); + + do { + /* We first send a packet, then wait for a packet to arrive. + * If the reply does not come in time, we send another packet. + */ + send_udp_data(udp, family, proto, data); + + NET_DBG("Waiting %s packet", proto); + + if (!wait_reply(proto, sem)) { + NET_DBG("Waited %d bytes but did not receive them.", + data->expecting_udp); + } + + k_yield(); + } while (1); +} + +#endif /* CONFIG_NET_UDP */ + +#if defined(CONFIG_NET_TCP) +static bool compare_tcp_data(struct net_pkt *pkt, int expecting_len, + int received_len) +{ + uint8_t *ptr = net_pkt_appdata(pkt), *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 client_tcp_received(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data) +{ + struct data *data = user_data; + char *proto; + + ARG_UNUSED(status); + + if (!pkt || net_pkt_appdatalen(pkt) == 0) { + if (pkt) { + net_pkt_unref(pkt); + } + + return; + } + + if (net_pkt_family(pkt) == AF_INET6) { + proto = "IPv6"; + } else { + proto = "IPv4"; + } + + 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); + } + + net_pkt_unref(pkt); + + if (data->expecting_tcp <= data->received_tcp) { + if (net_pkt_family(pkt) == AF_INET) { +#if defined(CONFIG_NET_IPV4) + k_sem_give(&conf.send_ipv4); +#endif + } else if (net_pkt_family(pkt) == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + k_sem_give(&conf.send_ipv6); +#endif + } + } +} + +static void setup_tcp_recv(struct net_context *tcp, + net_context_recv_cb_t cb, + void *user_data) +{ + int ret; + + ret = net_context_recv(tcp, cb, 0, user_data); + if (ret < 0) { + NET_ERR("Cannot receive TCP packets (%d)", ret); + } +} + +static void tcp_sent(struct net_context *context, + int status, + void *token, + void *user_data) +{ + uint32_t len = POINTER_TO_UINT(token); + + if (len) { + if (status) { + NET_DBG("%s: len %u status %d", (char *)user_data, + len, status); + } else { + NET_DBG("%s: len %u", (char *)user_data, len); + } + } +} + +static bool send_tcp_data(struct net_context *ctx, + char *proto, + struct data *data) +{ + struct net_pkt *send_pkt; + bool status = false; + size_t len; + int ret; + + data->expecting_tcp = sys_rand32_get() % ipsum_len; + data->received_tcp = 0; + + send_pkt = prepare_send_pkt(proto, ctx, data->expecting_tcp); + if (!send_pkt) { + goto out; + } + + len = net_buf_frags_len(send_pkt->frags); + + NET_ASSERT_INFO(data->expecting_tcp == len, + "%s data to send %d bytes, real len %zu", + proto, data->expecting_tcp, len); + + ret = net_context_send(send_pkt, tcp_sent, 0, + UINT_TO_POINTER(len), proto); + if (ret < 0) { + NET_ERR("Cannot send %s data to peer (%d)", proto, ret); + net_pkt_unref(send_pkt); + } else { + status = true; + } + +out: + return status; +} + +static void tcp_connected(struct net_context *context, + int status, + void *user_data) +{ + /* Start to send data */ + sa_family_t family = POINTER_TO_UINT(user_data); + + NET_DBG("%s connected.", family == AF_INET ? "IPv4" : "IPv6"); + + if (family == AF_INET) { +#if defined(CONFIG_NET_IPV4) + k_sem_give(&conf.send_ipv4); +#else + NET_DBG("IPv4 data skipped."); +#endif + } else if (family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + k_sem_give(&conf.send_ipv6); +#else + NET_DBG("IPv6 data skipped."); +#endif + } +} + +#if defined(CONFIG_NET_IPV4) +static void tcp_connect4(struct net_context *tcp_send) +{ + int ret; + + ret = net_context_connect(tcp_send, + (struct sockaddr *)&peer_addr4, + sizeof(peer_addr4), + tcp_connected, + K_FOREVER, + UINT_TO_POINTER(AF_INET)); + if (ret < 0) { + NET_DBG("Cannot connect to IPv4 peer (%d)", ret); + } + + setup_tcp_recv(tcp_send, client_tcp_received, &conf.ipv4); + + while (1) { + /* Wait until we can send actual data */ + k_sem_take(&conf.send_ipv4, K_FOREVER); + + send_tcp_data(tcp_send, "IPv4", &conf.ipv4); + + net_analyze_stack("TCP IPv4 thread", + ipv4_tcp_stack, sizeof(ipv4_tcp_stack)); + } +} +#endif + +#if defined(CONFIG_NET_IPV6) +static void tcp_connect6(struct net_context *tcp_send) +{ + int ret; + + ret = net_context_connect(tcp_send, + (struct sockaddr *)&peer_addr6, + sizeof(peer_addr6), + tcp_connected, + K_FOREVER, + UINT_TO_POINTER(AF_INET6)); + if (ret < 0) { + NET_DBG("Cannot connect to IPv6 peer (%d)", ret); + } + + setup_tcp_recv(tcp_send, client_tcp_received, &conf.ipv6); + + while (1) { + /* Wait until we can send actual data */ + k_sem_take(&conf.send_ipv6, K_FOREVER); + + send_tcp_data(tcp_send, "IPv6", &conf.ipv6); + + net_analyze_stack("TCP IPv6 thread", + ipv6_tcp_stack, sizeof(ipv6_tcp_stack)); + } +} +#endif /* CONFIG_NET_IPV6 */ +#endif /* CONFIG_NET_TCP */ + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_UDP) +static void send_udp_ipv4(struct net_context *udp) +{ + send_udp(udp, AF_INET, "IPv4", &conf.recv_ipv4, &conf.ipv4); +} +#endif + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_UDP) +static void send_udp_ipv6(struct net_context *udp) +{ + send_udp(udp, AF_INET6, "IPv6", &conf.recv_ipv6, &conf.ipv6); +} +#endif + +void client_startup(void) +{ + client_init(); + + ipsum_len = strlen(lorem_ipsum); + + /* Client setup */ + if (!get_context_client(&client_udp_send4, &client_udp_send6, + &client_tcp_send4, &client_tcp_send6)) { + panic("Cannot get network contexts for client"); + } + + NET_INFO("Client starting to send data"); + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_UDP) + k_thread_spawn(ipv4_udp_stack, STACKSIZE, + (k_thread_entry_t)send_udp_ipv4, + client_udp_send4, NULL, NULL, K_PRIO_COOP(7), 0, 0); +#endif + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_TCP) + k_thread_spawn(ipv4_tcp_stack, STACKSIZE, + (k_thread_entry_t)tcp_connect4, + client_tcp_send4, NULL, NULL, K_PRIO_COOP(7), 0, 0); +#endif + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_UDP) + k_thread_spawn(ipv6_udp_stack, STACKSIZE, + (k_thread_entry_t)send_udp_ipv6, + client_udp_send6, NULL, NULL, K_PRIO_COOP(7), 0, 0); +#endif + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_TCP) + k_thread_spawn(ipv6_tcp_stack, STACKSIZE, + (k_thread_entry_t)tcp_connect6, + client_tcp_send6, NULL, NULL, K_PRIO_COOP(7), 0, 0); +#endif +} diff --git a/samples/net/echo_loopback/src/echo-loopback.c b/samples/net/echo_loopback/src/echo-loopback.c new file mode 100644 index 0000000000000..78ab44869b213 --- /dev/null +++ b/samples/net/echo_loopback/src/echo-loopback.c @@ -0,0 +1,78 @@ +/* echo-loopback.c - Networking echo client/server combined */ + +/* + * Copyright (c) 2016 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if 1 +#define SYS_LOG_DOMAIN "echo-lo" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +void server_startup(void); +void client_startup(void); + +#define STARTUP_STACKSIZE 700 + +NET_STACK_DEFINE(STARTUP, startup_stack, STARTUP_STACKSIZE, + STARTUP_STACKSIZE); + +#if defined(CONFIG_NET_MGMT_EVENT) +static struct net_mgmt_event_callback cb; +#endif + +void panic(const char *msg) +{ + NET_ERR("Panic: %s", msg); + for (;;) { + k_sleep(K_FOREVER); + } +} + +void startup(void) +{ + NET_INFO("Run echo loopback"); + + server_startup(); + client_startup(); + + k_sleep(K_FOREVER); +} + +static void event_iface_up(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + k_thread_spawn(startup_stack, STARTUP_STACKSIZE, + (k_thread_entry_t)startup, + NULL, NULL, NULL, K_PRIO_COOP(7), 0, 0); +} + +void main(void) +{ + struct net_if *iface = net_if_get_default(); + +#if defined(CONFIG_NET_MGMT_EVENT) + /* Subscribe to NET_IF_UP if interface is not ready */ + if (!atomic_test_bit(iface->flags, NET_IF_UP)) { + net_mgmt_init_event_callback(&cb, event_iface_up, + NET_EVENT_IF_UP); + net_mgmt_add_event_callback(&cb); + return; + } +#endif + + event_iface_up(NULL, NET_EVENT_IF_UP, iface); +} diff --git a/samples/net/echo_loopback/src/server.c b/samples/net/echo_loopback/src/server.c new file mode 100644 index 0000000000000..4e1311fda79e9 --- /dev/null +++ b/samples/net/echo_loopback/src/server.c @@ -0,0 +1,500 @@ +/* server.c - Networking echo client/server combined */ + +/* + * Copyright (c) 2017 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if 1 +#define SYS_LOG_DOMAIN "echo-srv" +#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define NET_LOG_ENABLED 1 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void panic(const char *msg); + +#define MAX_DBG_PRINT 64 + +#define MY_PORT 4242 +#define PEER_PORT 8484 + +#if defined(CONFIG_NET_IPV6) + +/* Default IP address if not found in config file */ +#define PEER_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x1 } } } +#define MY_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x1 } } } + +#define MY_PREFIX_LEN 64 + +static struct net_context *server_udp_recv4; +static struct net_context *server_udp_recv6; +static struct net_context *server_tcp_recv4; +static struct net_context *server_tcp_recv6; + +static struct in6_addr in6addr_my = MY_IP6ADDR; + +static struct sockaddr_in6 my_addr6 = { + .sin6_family = AF_INET6, + .sin6_port = htons(MY_PORT), +}; + +static struct sockaddr_in6 peer_addr6 = { + .sin6_family = AF_INET6, + .sin6_port = htons(PEER_PORT), +}; +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) +#define MY_IP4ADDR { { { 192, 0, 2, 1 } } } +#define PEER_IP4ADDR { { { 192, 0, 2, 2 } } } + +static struct in_addr in4addr_my = MY_IP4ADDR; + +static struct sockaddr_in my_addr4 = { + .sin_family = AF_INET, + .sin_port = htons(MY_PORT), +}; + +static struct sockaddr_in peer_addr4 = { + .sin_family = AF_INET, + .sin_port = htons(PEER_PORT), +}; +#endif /* CONFIG_NET_IPV4 */ + +#define WAIT_TIME (2 * MSEC_PER_SEC) + +static void server_init(void) +{ + char buf[NET_IPV6_ADDR_LEN]; + +#if defined(CONFIG_NET_IPV6) +#if defined(CONFIG_NET_APP_SETTINGS) + if (net_addr_pton(AF_INET6, + CONFIG_NET_APP_MY_IPV6_ADDR, + &my_addr6.sin6_addr) < 0) { + NET_ERR("Invalid IPv6 address %s", + CONFIG_NET_APP_MY_IPV6_ADDR); + + net_ipaddr_copy(&my_addr6.sin6_addr, &in6addr_my); + } + + NET_DBG("Server IPv6 address %s", + net_addr_ntop(AF_INET6, &my_addr6.sin6_addr, + (char *)buf, sizeof(buf))); + + net_ipaddr_copy(&peer_addr6.sin6_addr, &in6addr_my); + + NET_DBG("Server peer IPv6 address %s", + net_addr_ntop(AF_INET6, &peer_addr6.sin6_addr, + (char *)buf, sizeof(buf))); +#endif + + do { + struct net_if_addr *ifaddr; + + ifaddr = net_if_ipv6_addr_add(net_if_get_default(), + &my_addr6.sin6_addr, + NET_ADDR_MANUAL, 0); + } while (0); +#endif + +#if defined(CONFIG_NET_IPV4) +#if defined(CONFIG_NET_APP_SETTINGS) + if (net_addr_pton(AF_INET, + CONFIG_NET_APP_MY_IPV4_ADDR, + &my_addr4.sin_addr) < 0) { + NET_ERR("Invalid IPv4 address %s", + CONFIG_NET_APP_MY_IPV4_ADDR); + + net_ipaddr_copy(&my_addr4.sin_addr, &in4addr_my); + } + + NET_DBG("Server IPv4 address %s", + net_addr_ntop(AF_INET, &my_addr4.sin_addr, + (char *)buf, sizeof(buf))); + + net_ipaddr_copy(&peer_addr4.sin_addr, &in4addr_my); + + NET_DBG("Server peer IPv4 address %s", + net_addr_ntop(AF_INET, &peer_addr4.sin_addr, + (char *)buf, sizeof(buf))); +#endif + + net_if_ipv4_addr_add(net_if_get_default(), &my_addr4.sin_addr, + NET_ADDR_MANUAL, 0); +#endif +} + +static inline bool get_context_server(struct net_context **udp_recv4, + struct net_context **udp_recv6, + struct net_context **tcp_recv4, + struct net_context **tcp_recv6) +{ + int ret; + +#if defined(CONFIG_NET_IPV6) + net_ipaddr_copy(&my_addr6.sin6_addr, &in6addr_my); + my_addr6.sin6_family = AF_INET6; + my_addr6.sin6_port = htons(MY_PORT); +#endif + +#if defined(CONFIG_NET_IPV4) + net_ipaddr_copy(&my_addr4.sin_addr, &in4addr_my); + my_addr4.sin_family = AF_INET; + my_addr4.sin_port = htons(MY_PORT); +#endif + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_UDP) + ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, udp_recv6); + if (ret < 0) { + NET_ERR("Cannot get network context for IPv6 UDP (%d)", + ret); + return false; + } + + ret = net_context_bind(*udp_recv6, (struct sockaddr *)&my_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + NET_ERR("Cannot bind IPv6 UDP port %d (%d)", + ntohs(my_addr6.sin6_port), ret); + return false; + } +#endif + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_UDP) + ret = net_context_get(AF_INET, SOCK_DGRAM, IPPROTO_UDP, udp_recv4); + if (ret < 0) { + NET_ERR("Cannot get network context for IPv4 UDP (%d)", + ret); + return false; + } + + ret = net_context_bind(*udp_recv4, (struct sockaddr *)&my_addr4, + sizeof(struct sockaddr_in)); + if (ret < 0) { + NET_ERR("Cannot bind IPv4 UDP port %d (%d)", + ntohs(my_addr4.sin_port), ret); + return false; + } +#endif + +#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_TCP) + if (tcp_recv6) { + ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, + tcp_recv6); + if (ret < 0) { + NET_ERR("Cannot get network context " + "for IPv6 TCP (%d)", ret); + return false; + } + + ret = net_context_bind(*tcp_recv6, + (struct sockaddr *)&my_addr6, + sizeof(struct sockaddr_in6)); + if (ret < 0) { + NET_ERR("Cannot bind IPv6 TCP port %d (%d)", + ntohs(my_addr6.sin6_port), ret); + return false; + } + + ret = net_context_listen(*tcp_recv6, 0); + if (ret < 0) { + NET_ERR("Cannot listen IPv6 TCP (%d)", ret); + return false; + } + } +#endif + +#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_TCP) + if (tcp_recv4) { + ret = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, + tcp_recv4); + if (ret < 0) { + NET_ERR("Cannot get network context for IPv4 TCP"); + return false; + } + + ret = net_context_bind(*tcp_recv4, + (struct sockaddr *)&my_addr4, + sizeof(struct sockaddr_in)); + if (ret < 0) { + NET_ERR("Cannot bind IPv4 TCP port %d", + ntohs(my_addr4.sin_port)); + return false; + } + + ret = net_context_listen(*tcp_recv4, 0); + if (ret < 0) { + NET_ERR("Cannot listen IPv4 TCP"); + return false; + } + } +#endif + + return true; +} + +static struct net_pkt *server_build_reply_buf(const char *name, + struct net_context *context, + struct net_pkt *pkt) +{ + struct net_buf *tmp; + struct net_pkt *reply_pkt; + int header_len; + + NET_INFO("%s received %d bytes", name, net_pkt_appdatalen(pkt)); + + if (net_pkt_appdatalen(pkt) == 0) { + return NULL; + } + + reply_pkt = net_pkt_get_tx(context, K_FOREVER); + + NET_ASSERT(reply_pkt); + + tmp = pkt->frags; + + /* First fragment will contain IP header so move the data + * down in order to get rid of it. + */ + header_len = net_pkt_appdata(pkt) - tmp->data; + + /* After this pull, the tmp->data points directly to application + * data. + */ + net_buf_pull(tmp, header_len); + + /* Note that we cannot use the original data bufs here as those bufs + * are still used in client side TCP and will be released when we + * send ACK. So here we need to copy the data to be sent to client. + */ + while (tmp) { + struct net_buf *frag; + + frag = net_pkt_get_data(context, K_FOREVER); + + if (!net_buf_headroom(tmp)) { + /* If there is no link layer headers in the + * received fragment, then get rid of that also + * in the sending fragment. We end up here + * if MTU is larger than fragment size, this + * is typical for ethernet. + */ + net_buf_push(frag, net_buf_headroom(frag)); + + frag->len = 0; /* to make fragment empty */ + + /* Make sure to set the reserve so that + * in sending side we add the link layer + * header if needed. + */ + net_pkt_set_ll_reserve(reply_pkt, 0); + } + + NET_ASSERT(net_buf_tailroom(frag) >= tmp->len); + + memcpy(net_buf_add(frag, tmp->len), tmp->data, tmp->len); + + net_pkt_frag_add(reply_pkt, frag); + + tmp = tmp->frags; + } + + return reply_pkt; +} + +static void pkt_sent(struct net_context *context, + int status, void *token, void *user_data) +{ + if (!status) { + NET_INFO("Sent %d bytes", POINTER_TO_UINT(token)); + } +} + +#if defined(CONFIG_NET_UDP) +static void server_udp_received(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data) +{ + struct net_pkt *reply_pkt; + struct sockaddr dst_addr; + socklen_t addrlen; + sa_family_t family = net_pkt_family(pkt); + static char dbg[MAX_DBG_PRINT + 1]; + int ret; + + snprintk(dbg, MAX_DBG_PRINT, "UDP IPv%c", + family == AF_INET6 ? '6' : '4'); + + reply_pkt = server_build_reply_buf(dbg, context, pkt); + + net_pkt_unref(pkt); + + if (!reply_pkt) { + return; + } + + if (family == AF_INET6) { +#if defined(CONFIG_NET_IPV6) + addrlen = sizeof(struct sockaddr_in6); + net_ipaddr_copy(&net_sin6(&dst_addr)->sin6_addr, + &peer_addr6.sin6_addr); + net_sin6(&dst_addr)->sin6_port = htons(PEER_PORT); + net_sin6(&dst_addr)->sin6_family = AF_INET6; +#endif + } else { +#if defined(CONFIG_NET_IPV4) + addrlen = sizeof(struct sockaddr_in); + net_ipaddr_copy(&net_sin(&dst_addr)->sin_addr, + &peer_addr4.sin_addr); + net_sin(&dst_addr)->sin_port = htons(PEER_PORT); + net_sin(&dst_addr)->sin_family = AF_INET; +#endif + } + + ret = net_context_sendto(reply_pkt, &dst_addr, addrlen, + pkt_sent, 0, + UINT_TO_POINTER(net_buf_frags_len( + reply_pkt->frags)), + user_data); + if (ret < 0) { + NET_ERR("Cannot send data to peer (%d)", ret); + net_pkt_unref(reply_pkt); + } +} + +static void server_setup_udp_recv(struct net_context *udp_recv4, + struct net_context *udp_recv6) +{ + int ret; + +#if defined(CONFIG_NET_IPV6) + ret = net_context_recv(udp_recv6, server_udp_received, 0, NULL); + if (ret < 0) { + NET_ERR("Cannot receive IPv6 UDP packets"); + } +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) + ret = net_context_recv(udp_recv4, server_udp_received, 0, NULL); + if (ret < 0) { + NET_ERR("Cannot receive IPv4 UDP packets"); + } +#endif /* CONFIG_NET_IPV4 */ +} +#endif /* CONFIG_NET_UDP */ + +#if defined(CONFIG_NET_TCP) +static void server_tcp_received(struct net_context *context, + struct net_pkt *pkt, + int status, + void *user_data) +{ + static char dbg[MAX_DBG_PRINT + 1]; + struct net_pkt *reply_pkt; + sa_family_t family; + int ret; + + if (!pkt) { + /* EOF condition */ + return; + } + + family = net_pkt_family(pkt); + + snprintk(dbg, MAX_DBG_PRINT, "TCP IPv%c", + family == AF_INET6 ? '6' : '4'); + + reply_pkt = server_build_reply_buf(dbg, context, pkt); + + net_pkt_unref(pkt); + + if (!reply_pkt) { + return; + } + + ret = net_context_send(reply_pkt, pkt_sent, K_NO_WAIT, + UINT_TO_POINTER(net_buf_frags_len( + reply_pkt->frags)), + NULL); + if (ret < 0) { + NET_ERR("Cannot send data to peer (%d)", ret); + net_pkt_unref(reply_pkt); + + panic("Cannot send data"); + } +} + +static void tcp_accepted(struct net_context *context, + struct sockaddr *addr, + socklen_t addrlen, + int error, + void *user_data) +{ + int ret; + + NET_DBG("Accept called, context %p error %d", context, error); + + ret = net_context_recv(context, server_tcp_received, 0, NULL); + if (ret < 0) { + NET_ERR("Cannot receive TCP packet (family %d)", + net_context_get_family(context)); + } +} + +static void setup_tcp_accept(struct net_context *tcp_recv4, + struct net_context *tcp_recv6) +{ + int ret; + +#if defined(CONFIG_NET_IPV6) + ret = net_context_accept(tcp_recv6, tcp_accepted, 0, NULL); + if (ret < 0) { + NET_ERR("Cannot receive IPv6 TCP packets (%d)", ret); + } +#endif /* CONFIG_NET_IPV6 */ + +#if defined(CONFIG_NET_IPV4) + ret = net_context_accept(tcp_recv4, tcp_accepted, 0, NULL); + if (ret < 0) { + NET_ERR("Cannot receive IPv4 TCP packets (%d)", ret); + } +#endif /* CONFIG_NET_IPV4 */ +} +#endif /* CONFIG_NET_TCP */ + +void server_startup(void) +{ + server_init(); + + /* Server setup */ + if (!get_context_server(&server_udp_recv4, &server_udp_recv6, + &server_tcp_recv4, &server_tcp_recv6)) { + panic("Cannot get network contexts for server"); + } + +#if defined(CONFIG_NET_TCP) + setup_tcp_accept(server_tcp_recv4, server_tcp_recv6); +#endif + +#if defined(CONFIG_NET_UDP) + server_setup_udp_recv(server_udp_recv4, server_udp_recv6); +#endif + + NET_INFO("Server starting to wait data"); +} diff --git a/samples/net/echo_loopback/testcase.ini b/samples/net/echo_loopback/testcase.ini new file mode 100644 index 0000000000000..d1a24f918c22b --- /dev/null +++ b/samples/net/echo_loopback/testcase.ini @@ -0,0 +1,5 @@ +[test] +tags = net +build_only = true +arch_whitelist = x86 +skip = true diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 0447b81c07a68..54931b4812ac9 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -342,6 +342,8 @@ static struct net_pkt *prepare_segment(struct net_tcp *tcp, tcphdr->urg[0] = 0; tcphdr->urg[1] = 0; + NET_DBG("pkt %p seq %u ack %u", pkt, segment->seq, segment->ack); + if (tail) { net_pkt_frag_add(pkt, tail); } @@ -445,6 +447,10 @@ int net_tcp_prepare_segment(struct net_tcp *tcp, u8_t flags, return -EINVAL; } + NET_DBG("pkt %p tcp %p seq %u ack %u", *send_pkt, tcp, + tcp->send_seq, tcp->send_ack); + net_print_frags("send ", *send_pkt); + tcp->send_seq = seq; if (seq_greater(tcp->send_seq, tcp->recv_max_ack)) { @@ -659,6 +665,8 @@ int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt) retry_timeout(context->tcp), 0); } + net_print_frags("queue ", pkt); + net_tcp_trace(pkt, context->tcp); do_ref_if_needed(pkt); return 0; @@ -789,6 +797,18 @@ void net_tcp_ack_received(struct net_context *ctx, u32_t ack) u32_t seq; bool valid_ack = false; + int i = 0; + struct net_pkt *foo, *tmp; + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(list, foo, tmp, sent_list) { + seq = sys_get_be32(tcphdr->seq) + net_pkt_appdatalen(foo) - 1; + tcphdr = NET_TCP_HDR(foo); + + NET_DBG("[%d] pkt %p tcp %p appdata %d hdr->seq %u seq %u ack %u result %d", + i++, foo, tcp, net_pkt_appdatalen(foo), + sys_get_be32(tcphdr->seq), seq, ack, + seq_greater(ack , seq)); + } + while (!sys_slist_is_empty(list)) { head = sys_slist_peek_head(list); pkt = CONTAINER_OF(head, struct net_pkt, sent_list); @@ -796,10 +816,17 @@ void net_tcp_ack_received(struct net_context *ctx, u32_t ack) seq = sys_get_be32(tcphdr->seq) + net_pkt_appdatalen(pkt) - 1; + NET_DBG("pkt %p tcp %p appdata %d hdr->seq %u seq %u ack %u", + pkt, tcp, net_pkt_appdatalen(pkt), + sys_get_be32(tcphdr->seq), seq, ack); + if (!seq_greater(ack, seq)) { + net_print_frags("ack-recv ", pkt); break; } + NET_DBG("seq %u < ack %u", seq, ack); + if (tcphdr->flags & NET_TCP_FIN) { enum net_tcp_state s = net_tcp_get_state(tcp);