diff --git a/components/esp_eth/test_apps/main/esp_eth_test_l2.c b/components/esp_eth/test_apps/main/esp_eth_test_l2.c index 1c3fd76492..55064e25ea 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_l2.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_l2.c @@ -12,7 +12,7 @@ #include "arpa/inet.h" // for ntohs, etc. #include "esp_log.h" -#define TEST_ETH_TYPE 0x2222 +#define TEST_ETH_TYPE 0x3300 #define TEST_CTRL_ETH_TYPE (TEST_ETH_TYPE + 1) #define WAIT_AFTER_CONN_MS 2500 @@ -30,6 +30,7 @@ typedef struct { EventGroupHandle_t eth_event_group; + uint8_t dst_mac_addr[ETH_ADDR_LEN]; int unicast_rx_cnt; int multicast_rx_cnt; int brdcast_rx_cnt; @@ -37,12 +38,13 @@ typedef struct bool check_rx_data; } recv_info_t; + esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) { recv_info_t *recv_info = (recv_info_t*)priv; EventGroupHandle_t eth_event_group = recv_info->eth_event_group; emac_frame_t *pkt = (emac_frame_t *)buffer; // check header - if (pkt->proto == TEST_ETH_TYPE) { // data packet + if (pkt->proto == ntohs(TEST_ETH_TYPE)) { // data packet uint8_t local_mac_addr[ETH_ADDR_LEN]; esp_eth_ioctl(hdl, ETH_CMD_G_MAC_ADDR, local_mac_addr); // check data content @@ -70,6 +72,7 @@ esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t } } else if (ntohs(pkt->proto) == TEST_CTRL_ETH_TYPE) { // control packet if (pkt->data[0] == POKE_RESP) { + memcpy(recv_info->dst_mac_addr, pkt->dest, ETH_ADDR_LEN); printf("Poke response received\n"); xEventGroupSetBits(eth_event_group, ETH_POKE_RESP_RECV_BIT); } @@ -139,6 +142,12 @@ TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]") .brdcast_rx_cnt = 0 }; + uint8_t local_mac_addr[ETH_ADDR_LEN] = {}; + TEST_ESP_OK(mac->get_addr(mac, local_mac_addr)); + // test app will parse the DUT MAC from this line of log output + printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2], + local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]); + TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &recv_info)); TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine @@ -150,7 +159,7 @@ TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]") poke_and_wait(eth_handle, NULL, 0, eth_event_rx_group); emac_frame_t *pkt = malloc(1024); - pkt->proto = TEST_ETH_TYPE; + pkt->proto = htons(TEST_ETH_TYPE); TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, pkt->src)); memset(pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){ @@ -158,7 +167,8 @@ TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]") } TEST_ESP_OK(esp_eth_transmit(eth_handle, pkt, 1024)); - vTaskDelay(pdMS_TO_TICKS(100)); + // give it some time to complete transmit + vTaskDelay(pdMS_TO_TICKS(500)); free(pkt); TEST_ESP_OK(esp_eth_stop(eth_handle)); @@ -268,10 +278,8 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]" // create dummy data packet used for traffic generation emac_frame_t *pkt = calloc(1, 1500); - pkt->proto = TEST_ETH_TYPE; - // we don't care about dest MAC address much, however it is better to not be broadcast or multifcast to not flood - // other network nodes - memset(pkt->dest, 0xBA, ETH_ADDR_LEN); + pkt->proto = htons(TEST_ETH_TYPE); + memcpy(pkt->dest, recv_info.dst_mac_addr, ETH_ADDR_LEN); memcpy(pkt->src, local_mac_addr, ETH_ADDR_LEN); printf("EMAC start/stop stress test under heavy Tx traffic\n"); @@ -323,6 +331,10 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]" free(pkt); + // Add an extra delay to be sure that there is no traffic generated by the test script during the driver un-installation. + // It was observed unintended behavior of the switch used in test environment when link is set down under heavy load. + vTaskDelay(pdMS_TO_TICKS(500)); + TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)); TEST_ESP_OK(esp_event_loop_delete_default()); TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle)); diff --git a/components/esp_eth/test_apps/pytest_esp_eth.py b/components/esp_eth/test_apps/pytest_esp_eth.py index 70d99db93d..806445fc94 100644 --- a/components/esp_eth/test_apps/pytest_esp_eth.py +++ b/components/esp_eth/test_apps/pytest_esp_eth.py @@ -12,7 +12,7 @@ import pytest from pytest_embedded import Dut from scapy.all import Ether, raw -ETH_TYPE = 0x2222 +ETH_TYPE = 0x3300 class EthTestIntf(object): @@ -37,7 +37,7 @@ class EthTestIntf(object): self.target_if = my_if break if self.target_if == '': - raise Exception('network interface not found') + raise RuntimeError('network interface not found') logging.info('Use %s for testing', self.target_if) @contextlib.contextmanager @@ -63,22 +63,30 @@ class EthTestIntf(object): except Exception as e: raise e - def recv_resp_poke(self, i:int=0) -> None: + def recv_resp_poke(self, mac:str, i:int=0) -> None: eth_type_ctrl = self.eth_type + 1 with self.configure_eth_if(eth_type_ctrl) as so: so.settimeout(30) - try: - eth_frame = Ether(so.recv(60)) - if eth_frame.load[0] == 0xfa: + for _ in range(10): + try: + eth_frame = Ether(so.recv(60)) + except Exception as e: + raise e + if mac == eth_frame.src and eth_frame.load[0] == 0xfa: if eth_frame.load[1] != i: - raise Exception('Missed Poke Packet') + raise RuntimeError('Missed Poke Packet') logging.info('Poke Packet received...') eth_frame.dst = eth_frame.src eth_frame.src = so.getsockname()[4] eth_frame.load = bytes.fromhex('fb') # POKE_RESP code so.send(raw(eth_frame)) - except Exception as e: - raise e + break + else: + logging.warning('Unexpected Control packet') + logging.warning('Expected Ctrl command: 0xfa, actual: 0x%x', eth_frame.load[0]) + logging.warning('Source MAC %s', eth_frame.src) + else: + raise RuntimeError('No Poke Packet!') def traffic_gen(self, mac: str, pipe_rcv:connection.Connection) -> None: with self.configure_eth_if() as so: @@ -120,47 +128,57 @@ def ethernet_l2_test(dut: Dut) -> None: with target_if.configure_eth_if() as so: so.settimeout(30) dut.write('"ethernet broadcast transmit"') + res = dut.expect( + r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' + ) # wait for POKE msg to be sure the switch already started forwarding the port's traffic # (there might be slight delay due to the RSTP execution) - target_if.recv_resp_poke() + dut_mac = res.group(1).decode('utf-8') + target_if.recv_resp_poke(mac=dut_mac) + + for _ in range(10): + eth_frame = Ether(so.recv(1024)) + if dut_mac == eth_frame.src: + break + else: + raise RuntimeError('No broadcast received from expected DUT MAC addr') - eth_frame = Ether(so.recv(1024)) for i in range(0, 1010): if eth_frame.load[i] != i & 0xff: - raise Exception('Packet content mismatch') + raise RuntimeError('Packet content mismatch') dut.expect_unity_test_output() dut.expect_exact("Enter next test, or 'enter' to see menu") dut.write('"ethernet recv_pkt"') res = dut.expect( - r'([\s\S]*)' r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' ) + dut_mac = res.group(1).decode('utf-8') # wait for POKE msg to be sure the switch already started forwarding the port's traffic # (there might be slight delay due to the RSTP execution) - target_if.recv_resp_poke() + target_if.recv_resp_poke(mac=dut_mac) target_if.send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame target_if.send_eth_packet('01:00:00:00:00:00') # multicast frame - target_if.send_eth_packet(res.group(2)) # unicast frame + target_if.send_eth_packet(mac=dut_mac) # unicast frame dut.expect_unity_test_output(extra_before=res.group(1)) dut.expect_exact("Enter next test, or 'enter' to see menu") dut.write('"ethernet start/stop stress test under heavy traffic"') res = dut.expect( - r'([\s\S]*)' r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' ) + dut_mac = res.group(1).decode('utf-8') # Start/stop under heavy Tx traffic for tx_i in range(10): - target_if.recv_resp_poke(tx_i) + target_if.recv_resp_poke(dut_mac, tx_i) dut.expect_exact('Ethernet stopped') for rx_i in range(10): - target_if.recv_resp_poke(rx_i) + target_if.recv_resp_poke(dut_mac, rx_i) # Start/stop under heavy Rx traffic pipe_rcv, pipe_send = Pipe(False) - tx_proc = Process(target=target_if.traffic_gen, args=(res.group(2), pipe_rcv, )) + tx_proc = Process(target=target_if.traffic_gen, args=(dut_mac, pipe_rcv, )) tx_proc.start() dut.expect_exact('Ethernet stopped') pipe_send.send(0) # just send some dummy data @@ -188,7 +206,6 @@ def test_esp_ethernet(dut: Dut) -> None: @pytest.mark.parametrize('config', [ 'default_ip101', ], indirect=True) -@pytest.mark.flaky(reruns=3, reruns_delay=5) def test_esp_emac_hal(dut: Dut) -> None: ethernet_int_emac_hal_test(dut) @@ -198,7 +215,6 @@ def test_esp_emac_hal(dut: Dut) -> None: @pytest.mark.parametrize('config', [ 'default_ip101', ], indirect=True) -@pytest.mark.flaky(reruns=3, reruns_delay=5) def test_esp_eth_ip101(dut: Dut) -> None: ethernet_l2_test(dut) @@ -208,6 +224,5 @@ def test_esp_eth_ip101(dut: Dut) -> None: @pytest.mark.parametrize('config', [ 'default_lan8720', ], indirect=True) -@pytest.mark.flaky(reruns=3, reruns_delay=5) def test_esp_eth_lan8720(dut: Dut) -> None: ethernet_l2_test(dut)