From 04b6c908a02117ac344f978eb0e5c48b1db87246 Mon Sep 17 00:00:00 2001 From: Alon Bar-Lev Date: Wed, 25 Jun 2025 13:12:47 +0300 Subject: [PATCH] contrib: apps: tcpecho_altcp: an altcp variant of tcpecho The altcp is a wonderful mechanism which wraps the raw api and provides the ability to use TLS or other layers. It is also automatically reverted to the raw api with the LWIP_ALTCP is disabled. Having an example of using altcp will allow quick starting point for people who would like to leverage layers. Signed-off-by: Alon Bar-Lev --- contrib/Filelists.cmake | 1 + contrib/Filelists.mk | 1 + contrib/apps/tcpecho_altcp/tcpecho_altcp.c | 301 +++++++++++++++++++++ contrib/apps/tcpecho_altcp/tcpecho_altcp.h | 35 +++ contrib/examples/example_app/test.c | 3 + contrib/ports/win32/msvc/lwIP_Test.vcxproj | 2 + 6 files changed, 343 insertions(+) create mode 100644 contrib/apps/tcpecho_altcp/tcpecho_altcp.c create mode 100644 contrib/apps/tcpecho_altcp/tcpecho_altcp.h diff --git a/contrib/Filelists.cmake b/contrib/Filelists.cmake index a375dd26b..a8cddda1d 100644 --- a/contrib/Filelists.cmake +++ b/contrib/Filelists.cmake @@ -39,6 +39,7 @@ set(lwipcontribapps_SRCS ${LWIP_CONTRIB_DIR}/apps/shell/shell.c ${LWIP_CONTRIB_DIR}/apps/udpecho_raw/udpecho_raw.c ${LWIP_CONTRIB_DIR}/apps/tcpecho_raw/tcpecho_raw.c + ${LWIP_CONTRIB_DIR}/apps/tcpecho_altcp/tcpecho_altcp.c ${LWIP_CONTRIB_DIR}/apps/netio/netio.c ${LWIP_CONTRIB_DIR}/apps/ping/ping.c ${LWIP_CONTRIB_DIR}/apps/socket_examples/socket_examples.c diff --git a/contrib/Filelists.mk b/contrib/Filelists.mk index f0fb48b28..4b9f62932 100644 --- a/contrib/Filelists.mk +++ b/contrib/Filelists.mk @@ -37,6 +37,7 @@ CONTRIBAPPFILES=$(CONTRIBDIR)/apps/httpserver/httpserver-netconn.c \ $(CONTRIBDIR)/apps/shell/shell.c \ $(CONTRIBDIR)/apps/udpecho_raw/udpecho_raw.c \ $(CONTRIBDIR)/apps/tcpecho_raw/tcpecho_raw.c \ + $(CONTRIBDIR)/apps/tcpecho_altcp/tcpecho_altcp.c \ $(CONTRIBDIR)/apps/netio/netio.c \ $(CONTRIBDIR)/apps/ping/ping.c \ $(CONTRIBDIR)/apps/socket_examples/socket_examples.c \ diff --git a/contrib/apps/tcpecho_altcp/tcpecho_altcp.c b/contrib/apps/tcpecho_altcp/tcpecho_altcp.c new file mode 100644 index 000000000..8cb42dc5e --- /dev/null +++ b/contrib/apps/tcpecho_altcp/tcpecho_altcp.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of and a contribution to the lwIP TCP/IP stack. + * + * Credits go to Adam Dunkels (and the current maintainers) of this software. + * + * Christiaan Simons rewrote this file to get a more stable echo example. + */ + +/** + * @file + * TCP echo server example using altcp API. + * + * Echos all bytes sent by connecting client, + * and passively closes when client is done. + * + */ + +#include "lwip/opt.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/altcp.h" +#include "tcpecho_altcp.h" + +#if LWIP_ALTCP && LWIP_CALLBACK_API + +static struct altcp_pcb *tcpecho_altcp_pcb; + +enum tcpecho_altcp_states +{ + ES_NONE = 0, + ES_ACCEPTED, + ES_RECEIVED, + ES_CLOSING +}; + +struct tcpecho_altcp_state +{ + u8_t state; + u8_t retries; + struct altcp_pcb *pcb; + /* pbuf (chain) to recycle */ + struct pbuf *p; +}; + +static void +tcpecho_altcp_free(struct tcpecho_altcp_state *es) +{ + if (es != NULL) { + if (es->p) { + /* free the buffer chain if present */ + pbuf_free(es->p); + } + + mem_free(es); + } +} + +static void +tcpecho_altcp_close(struct altcp_pcb *tpcb, struct tcpecho_altcp_state *es) +{ + altcp_arg(tpcb, NULL); + altcp_sent(tpcb, NULL); + altcp_recv(tpcb, NULL); + altcp_err(tpcb, NULL); + altcp_poll(tpcb, NULL, 0); + + tcpecho_altcp_free(es); + + altcp_close(tpcb); +} + +static void +tcpecho_altcp_send(struct altcp_pcb *tpcb, struct tcpecho_altcp_state *es) +{ + struct pbuf *ptr; + err_t wr_err = ERR_OK; + + while ((wr_err == ERR_OK) && + (es->p != NULL) && + (es->p->len <= altcp_sndbuf(tpcb))) { + ptr = es->p; + + /* enqueue data for transmission */ + wr_err = altcp_write(tpcb, ptr->payload, ptr->len, 1); + if (wr_err == ERR_OK) { + u16_t plen; + + plen = ptr->len; + /* continue with next pbuf in chain (if any) */ + es->p = ptr->next; + if(es->p != NULL) { + /* new reference! */ + pbuf_ref(es->p); + } + /* chop first pbuf from chain */ + pbuf_free(ptr); + /* we can read more data now */ + altcp_recved(tpcb, plen); + } else if(wr_err == ERR_MEM) { + /* we are low on memory, try later / harder, defer to poll */ + es->p = ptr; + } else { + /* other problem ?? */ + } + } +} + +static void +tcpecho_altcp_error(void *arg, err_t err) +{ + struct tcpecho_altcp_state *es; + + LWIP_UNUSED_ARG(err); + + es = (struct tcpecho_altcp_state *)arg; + + tcpecho_altcp_free(es); +} + +static err_t +tcpecho_altcp_poll(void *arg, struct altcp_pcb *tpcb) +{ + err_t ret_err; + struct tcpecho_altcp_state *es; + + es = (struct tcpecho_altcp_state *)arg; + if (es != NULL) { + if (es->p != NULL) { + /* there is a remaining pbuf (chain) */ + tcpecho_altcp_send(tpcb, es); + } else { + /* no remaining pbuf (chain) */ + if(es->state == ES_CLOSING) { + tcpecho_altcp_close(tpcb, es); + } + } + ret_err = ERR_OK; + } else { + /* nothing to be done */ + altcp_abort(tpcb); + ret_err = ERR_ABRT; + } + return ret_err; +} + +static err_t +tcpecho_altcp_sent(void *arg, struct altcp_pcb *tpcb, u16_t len) +{ + struct tcpecho_altcp_state *es; + + LWIP_UNUSED_ARG(len); + + es = (struct tcpecho_altcp_state *)arg; + es->retries = 0; + + if(es->p != NULL) { + /* still got pbufs to send */ + altcp_sent(tpcb, tcpecho_altcp_sent); + tcpecho_altcp_send(tpcb, es); + } else { + /* no more pbufs to send */ + if(es->state == ES_CLOSING) { + tcpecho_altcp_close(tpcb, es); + } + } + return ERR_OK; +} + +static err_t +tcpecho_altcp_recv(void *arg, struct altcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct tcpecho_altcp_state *es; + err_t ret_err; + + LWIP_ASSERT("arg != NULL",arg != NULL); + es = (struct tcpecho_altcp_state *)arg; + if (p == NULL) { + /* remote host closed connection */ + es->state = ES_CLOSING; + if(es->p == NULL) { + /* we're done sending, close it */ + tcpecho_altcp_close(tpcb, es); + } else { + /* we're not done yet */ + tcpecho_altcp_send(tpcb, es); + } + ret_err = ERR_OK; + } else if(err != ERR_OK) { + /* cleanup, for unknown reason */ + LWIP_ASSERT("no pbuf expected here", p == NULL); + ret_err = err; + } + else if(es->state == ES_ACCEPTED) { + /* first data chunk in p->payload */ + es->state = ES_RECEIVED; + /* store reference to incoming pbuf (chain) */ + es->p = p; + tcpecho_altcp_send(tpcb, es); + ret_err = ERR_OK; + } else if (es->state == ES_RECEIVED) { + /* read some more data */ + if(es->p == NULL) { + es->p = p; + tcpecho_altcp_send(tpcb, es); + } else { + struct pbuf *ptr; + + /* chain pbufs to the end of what we recv'ed previously */ + ptr = es->p; + pbuf_cat(ptr,p); + } + ret_err = ERR_OK; + } else { + /* unknown es->state, trash data */ + altcp_recved(tpcb, p->tot_len); + pbuf_free(p); + ret_err = ERR_OK; + } + return ret_err; +} + +static err_t +tcpecho_altcp_accept(void *arg, struct altcp_pcb *newpcb, err_t err) +{ + err_t ret_err; + struct tcpecho_altcp_state *es; + + LWIP_UNUSED_ARG(arg); + if ((err != ERR_OK) || (newpcb == NULL)) { + return ERR_VAL; + } + + /* Unless this pcb should have NORMAL priority, set its priority now. + When running out of pcbs, low priority pcbs can be aborted to create + new pcbs of higher priority. */ + altcp_setprio(newpcb, TCP_PRIO_MIN); + + es = (struct tcpecho_altcp_state *)mem_malloc(sizeof(struct tcpecho_altcp_state)); + if (es != NULL) { + es->state = ES_ACCEPTED; + es->pcb = newpcb; + es->retries = 0; + es->p = NULL; + /* pass newly allocated es to our callbacks */ + altcp_arg(newpcb, es); + altcp_recv(newpcb, tcpecho_altcp_recv); + altcp_err(newpcb, tcpecho_altcp_error); + altcp_poll(newpcb, tcpecho_altcp_poll, 0); + altcp_sent(newpcb, tcpecho_altcp_sent); + ret_err = ERR_OK; + } else { + ret_err = ERR_MEM; + } + return ret_err; +} + +void +tcpecho_altcp_init(void) +{ + tcpecho_altcp_pcb = altcp_new_ip_type(NULL, IPADDR_TYPE_ANY); + if (tcpecho_altcp_pcb != NULL) { + err_t err; + + err = altcp_bind(tcpecho_altcp_pcb, IP_ANY_TYPE, 7); + if (err == ERR_OK) { + tcpecho_altcp_pcb = altcp_listen(tcpecho_altcp_pcb); + altcp_accept(tcpecho_altcp_pcb, tcpecho_altcp_accept); + } else { + /* abort? output diagnostic? */ + } + } else { + /* abort? output diagnostic? */ + } +} + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/contrib/apps/tcpecho_altcp/tcpecho_altcp.h b/contrib/apps/tcpecho_altcp/tcpecho_altcp.h new file mode 100644 index 000000000..2784a70c6 --- /dev/null +++ b/contrib/apps/tcpecho_altcp/tcpecho_altcp.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ +#ifndef LWIP_TCPECHO_ALTCP_H +#define LWIP_TCPECHO_ALTCP_H + +void tcpecho_altcp_init(void); + +#endif /* LWIP_TCPECHO_RAW_H */ diff --git a/contrib/examples/example_app/test.c b/contrib/examples/example_app/test.c index 4f4b5d712..eec4c80f2 100644 --- a/contrib/examples/example_app/test.c +++ b/contrib/examples/example_app/test.c @@ -72,6 +72,7 @@ #include "apps/tcpecho/tcpecho.h" #include "apps/udpecho/udpecho.h" #include "apps/tcpecho_raw/tcpecho_raw.h" +#include "apps/tcpecho_altcp/tcpecho_altcp.h" #include "apps/socket_examples/socket_examples.h" #include "examples/lwiperf/lwiperf_example.h" @@ -558,6 +559,8 @@ apps_init(void) #if LWIP_TCPECHO_APP #if LWIP_NETCONN && defined(LWIP_TCPECHO_APP_NETCONN) tcpecho_init(); +#elif LWIP_ALTCP + tcpecho_altcp_init(); #else /* LWIP_NETCONN && defined(LWIP_TCPECHO_APP_NETCONN) */ tcpecho_raw_init(); #endif diff --git a/contrib/ports/win32/msvc/lwIP_Test.vcxproj b/contrib/ports/win32/msvc/lwIP_Test.vcxproj index 22b39c88e..026ec72f6 100644 --- a/contrib/ports/win32/msvc/lwIP_Test.vcxproj +++ b/contrib/ports/win32/msvc/lwIP_Test.vcxproj @@ -245,6 +245,7 @@ + @@ -285,6 +286,7 @@ +