esp_eth pytest: increased robustness of the L2 test

Added filtering frames based on MAC address
This commit is contained in:
Ondrej Kosta 2023-03-31 16:23:15 +02:00
parent 6987644d67
commit f6715c9c29
2 changed files with 57 additions and 30 deletions

View File

@ -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));

View File

@ -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)
for _ in range(10):
try:
eth_frame = Ether(so.recv(60))
if eth_frame.load[0] == 0xfa:
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')
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)