diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 398c5b6c94..08ce2be5aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1084,6 +1084,23 @@ test_weekend_mqtt: ENV_FILE: "$CI_PROJECT_DIR/components/mqtt/weekend_test/env.yml" CONFIG_FILE: "$CI_PROJECT_DIR/components/mqtt/weekend_test/config.yml" +test_weekend_network: + <<: *example_test_template + stage: target_test + image: $CI_DOCKER_REGISTRY/rpi-net-suite$BOT_DOCKER_IMAGE_TAG + tags: + - ESP32 + - Example_WIFI + only: + variables: + - $BOT_LABEL_WEEKEND_TEST + variables: + TEST_CASE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test" + TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw" + LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS" + ENV_FILE: "$CI_PROJECT_DIR/components/lwip/weekend_test/env.yml" + CONFIG_FILE: "$CI_PROJECT_DIR/components/lwip/weekend_test/config.yml" + .test_template: &test_template stage: target_test when: on_success diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index 9dde9d0108..6a15d6f5c8 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -87,6 +87,7 @@ set(COMPONENT_SRCS "apps/dhcpserver/dhcpserver.c" "port/esp32/freertos/sys_arch.c" "port/esp32/netif/dhcp_state.c" "port/esp32/netif/ethernetif.c" + "port/esp32/netif/nettestif.c" "port/esp32/netif/wlanif.c") if(CONFIG_LWIP_PPP_SUPPORT) diff --git a/components/lwip/port/esp32/include/netif/nettestif.h b/components/lwip/port/esp32/include/netif/nettestif.h new file mode 100644 index 0000000000..aa76ed8079 --- /dev/null +++ b/components/lwip/port/esp32/include/netif/nettestif.h @@ -0,0 +1,33 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef _NETTEST_LWIP_IF_H_ +#define _NETTEST_LWIP_IF_H_ + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +err_t nettestif_init(struct netif *netif); + +void nettestif_input(void *buffer, u16_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _NETTEST_LWIP_IF_H_ */ \ No newline at end of file diff --git a/components/lwip/port/esp32/netif/dhcp_state.c b/components/lwip/port/esp32/netif/dhcp_state.c index ede094bd4f..8e2e3d9f38 100644 --- a/components/lwip/port/esp32/netif/dhcp_state.c +++ b/components/lwip/port/esp32/netif/dhcp_state.c @@ -26,7 +26,7 @@ #define VALID_NETIF_ID(id) ((id < ESP_IF_MAX) && (id != ESP_IF_WIFI_AP)) static uint32_t restored_ip_addr[TCPIP_ADAPTER_IF_MAX]; -static const char *interface_key[] = {"IF_STA", "IF_AP", "IF_ETH"}; +static const char *interface_key[] = {"IF_STA", "IF_AP", "IF_ETH", "IF_TEST"}; _Static_assert(sizeof(interface_key) / sizeof(char*) == TCPIP_ADAPTER_IF_MAX, "Number interface keys differs from number of interfaces"); diff --git a/components/lwip/port/esp32/netif/nettestif.c b/components/lwip/port/esp32/netif/nettestif.c new file mode 100644 index 0000000000..741f542797 --- /dev/null +++ b/components/lwip/port/esp32/netif/nettestif.c @@ -0,0 +1,105 @@ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/wlanif.h" + +#include +#include + +#include "tcpip_adapter.h" + +static struct netif *g_last_netif = NULL; + + +err_t nettestif_output(struct netif *netif, struct pbuf *p) +{ + int i; + char *dat = p->payload; + + /* output the packet to stdout */ + printf("\nPacketOut:["); + for (i=0; ilen; i++) { + printf("%02x", *dat++); + } + printf("]\n"); + + return ERR_OK; +} + + +err_t nettestif_init(struct netif *netif) +{ + + g_last_netif = netif; + + netif->hostname = "espressif"; + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 100); + + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + netif->linkoutput = nettestif_output; + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + +#if ESP_LWIP +#if LWIP_IGMP + netif->flags |= NETIF_FLAG_IGMP; +#endif +#endif + return ERR_OK; + +} + +void nettestif_input(void *buffer, u16_t len) +{ + struct pbuf *p; + if (g_last_netif == NULL) { + printf("error!"); + return; + } + + printf("simul in: %d\n", len); + if (len==0) return; + + p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); + p->l2_owner = NULL; + memcpy(p->payload, buffer, len); + + /* full packet send to tcpip_thread to process + * on success - the packet is processed and deallocated in tcpip stack + * on failure - log error and deallocate the packet + */ + if (g_last_netif->input(p, g_last_netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + } + +} diff --git a/components/lwip/weekend_test/config.yml b/components/lwip/weekend_test/config.yml new file mode 100644 index 0000000000..5a10e26ff6 --- /dev/null +++ b/components/lwip/weekend_test/config.yml @@ -0,0 +1,3 @@ +CaseConfig: +- name: lwip_test_suite + diff --git a/components/lwip/weekend_test/env.yml b/components/lwip/weekend_test/env.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/lwip/weekend_test/esp32_netsuite.cfg b/components/lwip/weekend_test/esp32_netsuite.cfg new file mode 100644 index 0000000000..abace97e56 --- /dev/null +++ b/components/lwip/weekend_test/esp32_netsuite.cfg @@ -0,0 +1,11 @@ +[LOGGING] +FileMask := LOG_ALL | DEBUG | TTCN_DEBUG +ConsoleMask := LOG_ALL | DEBUG | TTCN_DEBUG +LogSourceInfo := Yes + +[MODULE_PARAMETERS] +libtest.m_ip_dst := "10.0.0.1" +libtest.m_ip_src := "10.0.0.2" + +[EXECUTE] +esp32_netsuite.control diff --git a/components/lwip/weekend_test/esp32_netsuite.ttcn b/components/lwip/weekend_test/esp32_netsuite.ttcn new file mode 100644 index 0000000000..38970fd1fd --- /dev/null +++ b/components/lwip/weekend_test/esp32_netsuite.ttcn @@ -0,0 +1,11 @@ +module esp32_netsuite { +import from tcp_suite all; + +control { + execute(tc_tcp_002()); + execute(tc_tcp_003()); + execute(tc_tcp_004()); + execute(tc_tcp_005()); +} + +} diff --git a/components/lwip/weekend_test/net_suite_test.py b/components/lwip/weekend_test/net_suite_test.py new file mode 100644 index 0000000000..ce6340a2da --- /dev/null +++ b/components/lwip/weekend_test/net_suite_test.py @@ -0,0 +1,164 @@ +import re +import os +import sys +import socket +from threading import Thread, Event +import subprocess +import time +from shutil import copyfile + +try: + import IDF +except ImportError: + # this is a test case write with tiny-test-fw. + # to run test cases outside tiny-test-fw, + # we need to set environment variable `TEST_FW_PATH`, + # then get and insert `TEST_FW_PATH` to sys path before import FW module + test_fw_path = os.getenv("TEST_FW_PATH") + if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + import IDF + +import DUT +import Utility + +stop_sock_listener = Event() +stop_io_listener = Event() +sock = None +client_address = None +manual_test = False + + +def io_listener(dut1): + global sock + global client_address + data = b'' + while not stop_io_listener.is_set(): + try: + data = dut1.expect(re.compile(r"PacketOut:\[([a-fA-F0-9]+)\]"), timeout=5) + except DUT.ExpectTimeout: + continue + if data != () and data[0] != b'': + packet_data = data[0] + print("Packet_data>{}<".format(packet_data)) + response = bytearray.fromhex(packet_data.decode()) + print("Sending to socket:") + packet = ' '.join(format(x, '02x') for x in bytearray(response)) + print("Packet>{}<".format(packet)) + if client_address is not None: + sock.sendto(response, ('127.0.0.1', 7777)) + + +def sock_listener(dut1): + global sock + global client_address + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(5) + server_address = '0.0.0.0' + server_port = 7771 + server = (server_address, server_port) + sock.bind(server) + try: + while not stop_sock_listener.is_set(): + try: + payload, client_address = sock.recvfrom(1024) + packet = ' '.join(format(x, '02x') for x in bytearray(payload)) + print("Received from address {}, data {}".format(client_address, packet)) + dut1.write(str.encode(packet)) + except socket.timeout: + pass + finally: + sock.close() + sock = None + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def lwip_test_suite(env, extra_data): + global stop_io_listener + global stop_sock_listener + """ + steps: | + 1. Rebuilds test suite with esp32_netsuite.ttcn + 2. Starts listeners on stdout and socket + 3. Execute ttcn3 test suite + 4. Collect result from ttcn3 + """ + dut1 = env.get_dut("net_suite", "examples/system/network_tests") + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "net_suite.bin") + bin_size = os.path.getsize(binary_file) + IDF.log_performance("net_suite", "{}KB".format(bin_size // 1024)) + IDF.check_performance("net_suite", bin_size // 1024) + dut1.start_app() + thread1 = Thread(target=sock_listener, args=(dut1, )) + thread2 = Thread(target=io_listener, args=(dut1, )) + if not manual_test: + # Variables refering to esp32 ttcn test suite + TTCN_SRC = 'esp32_netsuite.ttcn' + TTCN_CFG = 'esp32_netsuite.cfg' + # System Paths + netsuite_path = os.getenv("NETSUITE_PATH") + netsuite_src_path = os.path.join(netsuite_path, "src") + test_dir = os.path.dirname(os.path.realpath(__file__)) + # Building the suite + print("Rebuilding the test suite") + print("-------------------------") + # copy esp32 specific files to ttcn net-suite dir + copyfile(os.path.join(test_dir, TTCN_SRC), os.path.join(netsuite_src_path, TTCN_SRC)) + copyfile(os.path.join(test_dir, TTCN_CFG), os.path.join(netsuite_src_path, TTCN_CFG)) + proc = subprocess.Popen(['bash', '-c', 'cd ' + netsuite_src_path + ' && source make.sh'], + cwd=netsuite_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = proc.stdout.read() + print("Note: First build step we expect failure (titan/net_suite build system not suitable for multijob make)") + print(output) + proc = subprocess.Popen(['bash', '-c', 'cd ' + netsuite_src_path + ' && make'], + cwd=netsuite_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + print("Note: This time all dependencies shall be generated -- multijob make shall pass") + output = proc.stdout.read() + print(output) + # Executing the test suite + thread1.start() + thread2.start() + time.sleep(2) + print("Executing the test suite") + print("------------------------") + proc = subprocess.Popen(['ttcn3_start', os.path.join(netsuite_src_path,'test_suite'), os.path.join(netsuite_src_path, TTCN_CFG)], + stdout=subprocess.PIPE) + output = proc.stdout.read() + print(output) + print("Collecting results") + print("------------------") + verdict_stats = re.search('(Verdict statistics:.*)', output) + if verdict_stats: + verdict_stats = verdict_stats.group(1) + else: + verdict_stats = b"" + verdict = re.search('Overall verdict: pass', output) + if verdict: + print("Test passed!") + Utility.console_log(verdict_stats, "green") + else: + Utility.console_log(verdict_stats, "red") + raise ValueError('Test failed with: {}'.format(verdict_stats)) + else: + try: + # Executing the test suite + thread1.start() + thread2.start() + time.sleep(2) + while True: + time.sleep(0.5) + except KeyboardInterrupt: + pass + print("Executing done, waiting for tests to finish") + print("-------------------------------------------") + stop_io_listener.set() + stop_sock_listener.set() + thread1.join() + thread2.join() + + +if __name__ == '__main__': + print("Manual execution, please build and start ttcn in a separate console") + manual_test = True + lwip_test_suite() diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 3953984264..033dfd0e76 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -96,6 +96,7 @@ typedef enum { TCPIP_ADAPTER_IF_STA = 0, /**< Wi-Fi STA (station) interface */ TCPIP_ADAPTER_IF_AP, /**< Wi-Fi soft-AP interface */ TCPIP_ADAPTER_IF_ETH, /**< Ethernet interface */ + TCPIP_ADAPTER_IF_TEST, /**< tcpip stack test interface */ TCPIP_ADAPTER_IF_MAX } tcpip_adapter_if_t; @@ -692,6 +693,19 @@ esp_err_t tcpip_adapter_get_netif(tcpip_adapter_if_t tcpip_if, void ** netif); */ bool tcpip_adapter_is_netif_up(tcpip_adapter_if_t tcpip_if); +/** + * @brief Cause the TCP/IP stack to start the test interface with specified MAC and IP. + * Test interface is used to exercise network stack with injected packets from SW. + * + * @param[in] mac Set MAC address of this interface + * @param[in] ip_info Set IP address of this interface + * + * @return + * - ESP_OK + * - ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS + * - ESP_ERR_NO_MEM + */ +esp_err_t tcpip_adapter_test_start(uint8_t *mac, tcpip_adapter_ip_info_t *ip_info); /** * @brief Install default event handlers for Ethernet interface diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 4720fadc2d..c8570a832c 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -32,6 +32,7 @@ #endif #include "netif/wlanif.h" #include "netif/ethernetif.h" +#include "netif/nettestif.h" #include "dhcpserver/dhcpserver.h" #include "dhcpserver/dhcpserver_options.h" @@ -161,6 +162,8 @@ static esp_err_t tcpip_adapter_update_default_netif(void) netif_set_default(esp_netif[TCPIP_ADAPTER_IF_ETH]); } else if (netif_is_up(esp_netif[TCPIP_ADAPTER_IF_AP])) { netif_set_default(esp_netif[TCPIP_ADAPTER_IF_AP]); + } else if (netif_is_up(esp_netif[TCPIP_ADAPTER_IF_TEST])) { + netif_set_default(esp_netif[TCPIP_ADAPTER_IF_TEST]); } return ESP_OK; @@ -210,6 +213,8 @@ static esp_err_t tcpip_adapter_start(tcpip_adapter_if_t tcpip_if, uint8_t *mac, dhcps_status = TCPIP_ADAPTER_DHCP_STARTED; } + } else if (tcpip_if == TCPIP_ADAPTER_IF_TEST) { + netif_set_up(esp_netif[tcpip_if]); } tcpip_adapter_update_default_netif(); @@ -229,6 +234,13 @@ esp_err_t tcpip_adapter_sta_start(uint8_t *mac, tcpip_adapter_ip_info_t *ip_info return tcpip_adapter_start(TCPIP_ADAPTER_IF_STA, mac, ip_info); } +esp_err_t tcpip_adapter_test_start(uint8_t *mac, tcpip_adapter_ip_info_t *ip_info) +{ + esp_netif_init_fn[TCPIP_ADAPTER_IF_TEST] = nettestif_init; + return tcpip_adapter_start(TCPIP_ADAPTER_IF_TEST, mac, ip_info); +} + + esp_err_t tcpip_adapter_ap_start(uint8_t *mac, tcpip_adapter_ip_info_t *ip_info) { esp_netif_init_fn[TCPIP_ADAPTER_IF_AP] = wlanif_init_ap; diff --git a/examples/system/network_tests/CMakeLists.txt b/examples/system/network_tests/CMakeLists.txt new file mode 100644 index 0000000000..a38b6545e9 --- /dev/null +++ b/examples/system/network_tests/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(net_suite) diff --git a/examples/system/network_tests/Makefile b/examples/system/network_tests/Makefile new file mode 100644 index 0000000000..466d993316 --- /dev/null +++ b/examples/system/network_tests/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := net_suite + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/system/network_tests/README.md b/examples/system/network_tests/README.md new file mode 100644 index 0000000000..c9aefd8d34 --- /dev/null +++ b/examples/system/network_tests/README.md @@ -0,0 +1,66 @@ +# Intel net test suite for LwIP network stack + +This project provides a test interface to esp32 network stack in order to execute standard set of +Intel network test suite defined in TTCN3 framework. + +## Important notice +*This is an internal ESP-IDF test and not a user project example* + +## Execute net test suite + +These network tests could be executed in both manual or automated mode in CI. + +Note: TTCN3 engine works reliably only on Linux and Windows. + +## Setup TTCN3 + +* Clone a repository https://github.com/intel/net-test-suites.git and install titan core a described in the README.md +* Copy files `esp32_netsuite.cfg` and `esp32_netsuite.ttcn` (located in `$IDF_PATH/components/lwip/weekend_test`) to `src` subdir of the cloned repository `net-test-suites` +* Rebuild the netsuite tests (according to README.md in net-test-suite) by executing `source make.sh` in `src` subdir + + +## Build application + +``` +cd $IDF_PATH/examples/system/network_tests +make defconfig +make -j4 +make flash +``` + +## Run test +Open two terminals (1) and (2) +1) Start the test server which would pass packets from TTCN3 test suite into ESP32 board in `$IDF_PATH/components/lwip/weekend_test` +``` +python net_suite_test.py +``` + +2) Start test suite in TTCN3 environment in `src` subdir of the cloned repository `net-test-suites.git` +``` +ttcn3_start test_suite esp32_netsuite.cfg +``` + +## Internal connection + +Purpose of this test is to execute standard network suite on a ESP32 network stack. + +DUT (Network stack under test) runs normally on target, but a specific interface `TCPIP_ADAPTER_IF_TEST` was created for passing arbitrary data to +and from the network stack. Embedded code `net_suite.c` implements an application which serves stdin/stdout and propagates the data to/from this test interface. + +Standard Intel net suite executed by TTCN3 engine uses udp ports for input/ouput of network packets. Python script `net_suite.py` translates this communication +from/to those udp ports to stdin/stdout, where after propagating over USB/UART to the ESP32 board are processed in the network stack (on the target). + +Actual test execution, progress, evaluation and test reporting is done using standard net-test-suite scripts running on PC. + +``` + PC ++---------------------------------------------------------+ ESP32 board +| | +----------------------------------------+ +| TTCN3 engine | | +----------------------------------+ | +| | | | net_suite.c | | +| +-----------------+ +--------------+ | | | +------------------------+ | +| | net-test-suite |--7777/udp--| net_suite.py |--stdout---------| -----> | tcpip_adapter/lwip | | +| | |--7771/udp--| |--stdin----------| <----- | TCPIP_ADAPTER_IF_TEST | | +| +-----------------+ +--------------+ | | +---------+------------------------+ | ++---------------------------------------------------------+ +----------------------------------------+ +``` diff --git a/examples/system/network_tests/main/CMakeLists.txt b/examples/system/network_tests/main/CMakeLists.txt new file mode 100644 index 0000000000..c16f42adb9 --- /dev/null +++ b/examples/system/network_tests/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "net_suite.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/system/network_tests/main/component.mk b/examples/system/network_tests/main/component.mk new file mode 100644 index 0000000000..61f8990c31 --- /dev/null +++ b/examples/system/network_tests/main/component.mk @@ -0,0 +1,8 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/examples/system/network_tests/main/net_suite.c b/examples/system/network_tests/main/net_suite.c new file mode 100644 index 0000000000..2fae24835b --- /dev/null +++ b/examples/system/network_tests/main/net_suite.c @@ -0,0 +1,180 @@ +/* Net-suite test code + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "driver/uart.h" +#include "esp_console.h" +#include "esp_vfs_dev.h" +#include "linenoise/linenoise.h" + +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/tcp.h" +void nettestif_input(void *buffer, u16_t len); + +/* these data configures ARP cache so the test IPs are knows */ +static char arp1[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, + 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x01 +}; + +/* Test data (ICMP packet) for verification of tcp ip test netif + 00-00-00-00-00-01-00-00-00-00-00-02-08-00-45-00-00-1c-00-00-00-00-ff-01-a7-de-0a-00-00-02-0a-00-00-01-08-00-f7-fd-00-01-00-01 +*/ + +/* creating test pcb */ +static struct tcp_pcb *test_pcb; + +err_t test_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + return ERR_OK; +} + +void test_error(void *arg, err_t err) +{ + printf("Error CB from pcb %d\n", err); +} + +err_t test_poll(void *arg, struct tcp_pcb *tpcb) +{ + return ERR_OK; +} + +err_t test_accept(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + tcp_setprio(newpcb, TCP_PRIO_MIN); + tcp_arg(newpcb, NULL); + tcp_recv(newpcb, test_recv); + tcp_err(newpcb, test_error); + tcp_poll(newpcb, test_poll, 0); + + return ERR_OK; +} + +void test_tcp_init(void) +{ + test_pcb = tcp_new(); + if (test_pcb != NULL) { + err_t err; + /* Binding this test_pcb to 4242 to accept connections on this port + * - this has to be configured as DUT endpoint + * - all network traffic from and to network stack is tracked in nettestif + */ + err = tcp_bind(test_pcb, IP_ADDR_ANY, 4242); + if (err == ERR_OK) { + test_pcb = tcp_listen(test_pcb); + tcp_accept(test_pcb, test_accept); + } else { + printf("cannot bind test_pcb\n"); + abort(); + } + } else { + printf("cannot create test_pcb\n"); + abort(); + } +} + +/** + * @brief Process line read from serial input, character by character + * + * Converts from hex string to byte stream, so it can be processed + * in test network interface + * + * @param line + * @param packet + * + * @return size of packet + */ +static size_t process_line(char* line, char* packet) +{ + size_t count = 0; + size_t i; + + for (i=0; i< strlen(line); i++) { + char c = line[i]; + // accept both separators between bytes + if (c == '-' || c == ' ') { + ++count; + // Processing numeric characters + } else if (c >= '0' && c <= '9') { + packet[count] *= 16; + packet[count] += c - '0'; + // Processing alpha-numeric hex characters + } else if (c >= 'a' && c <= 'f') { + packet[count] *= 16; + packet[count] += c - 'a' + 10; + } + } + + if (i>0 && strlen(line)>0) { + count++; + } + return count; +} + +void app_main() +{ + char packet[128]; + + tcpip_adapter_ip_info_t ip_info; + + uint8_t ap_mac[6] = { 0,0,0,0,0,1}; + IP4_ADDR(&ip_info.ip, 10, 0 , 0, 1); + IP4_ADDR(&ip_info.gw, 10, 0 , 0, 1); + IP4_ADDR(&ip_info.netmask, 255, 255 , 255, 0); + + tcpip_adapter_init(); + + tcpip_adapter_test_start(ap_mac, &ip_info); + + // initializes TCP endpoint on DUT per https://github.com/intel/net-test-suites#21-endpoints + test_tcp_init(); + // Inject ARP packet to let the network stack know about IP/MAC of the counterpart + nettestif_input(arp1, sizeof(arp1)); + // Initialize VFS & UART so we can use std::cout/cin + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0) ); + /* Tell VFS to use UART driver */ + esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + /* Move the caret to the beginning of the next line on '\n' */ + esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + linenoiseSetDumbMode(1); + + /* Now read from stdin and pass the data to test netif */ + while (1) { + size_t size; + char* line = linenoise(""); + if (!line) { + continue; + } + + size = process_line(line, packet); + + nettestif_input(packet, size); + + linenoiseFree(line); + } + +}