mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/example_br_test' into 'master'
examples: added Ethernet bridge test See merge request espressif/esp-idf!22558
This commit is contained in:
commit
066abb062d
@ -352,6 +352,16 @@ pytest_examples_esp32_ethernet_ip101:
|
||||
- build_pytest_examples_esp32
|
||||
tags: [ esp32, ip101 ]
|
||||
|
||||
example_test_pytest_esp32_ethernet_bridge:
|
||||
extends:
|
||||
- .pytest_examples_dir_template
|
||||
- .rules:test:example_test-esp32
|
||||
needs:
|
||||
- build_pytest_examples_esp32
|
||||
tags: [ esp32, ethernet_w5500 ]
|
||||
variables:
|
||||
PYTEST_EXTRA_FLAGS: "--dev-passwd ${ETHERNET_TEST_PASSWORD} --dev-user ${ETHERNET_TEST_USER}"
|
||||
|
||||
pytest_examples_esp32_flash_encryption:
|
||||
extends:
|
||||
- .pytest_examples_dir_template
|
||||
|
19
conftest.py
19
conftest.py
@ -123,6 +123,7 @@ ENV_MARKERS = {
|
||||
'esp32eco3': 'Runner with esp32 eco3 connected',
|
||||
'ecdsa_efuse': 'Runner with test ECDSA private keys programmed in efuse',
|
||||
'ccs811': 'Runner with CCS811 connected',
|
||||
'ethernet_w5500': 'SPI Ethernet module with two W5500',
|
||||
# multi-dut markers
|
||||
'ieee802154': 'ieee802154 related tests should run on ieee802154 runners.',
|
||||
'openthread_br': 'tests should be used for openthread border router.',
|
||||
@ -396,6 +397,16 @@ def log_minimum_free_heap_size(dut: IdfDut, config: str) -> Callable[..., None]:
|
||||
return real_func
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dev_password(request: FixtureRequest) -> str:
|
||||
return request.config.getoption('dev_passwd') or ''
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dev_user(request: FixtureRequest) -> str:
|
||||
return request.config.getoption('dev_user') or ''
|
||||
|
||||
|
||||
##################
|
||||
# Hook functions #
|
||||
##################
|
||||
@ -406,6 +417,14 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
||||
help='sdkconfig postfix, like sdkconfig.ci.<config>. (Default: None, which would build all found apps)',
|
||||
)
|
||||
base_group.addoption('--known-failure-cases-file', help='known failure cases file path')
|
||||
base_group.addoption(
|
||||
'--dev-user',
|
||||
help='user name associated with some specific device/service used during the test execution',
|
||||
)
|
||||
base_group.addoption(
|
||||
'--dev-passwd',
|
||||
help='password associated with some specific device/service used during the test execution',
|
||||
)
|
||||
|
||||
|
||||
_idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']()
|
||||
|
@ -4,6 +4,10 @@ examples/network:
|
||||
disable:
|
||||
- if: IDF_TARGET in ["esp32h2"]
|
||||
|
||||
examples/network/bridge:
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
reason: Generic functionality, no need to be run on specific targets
|
||||
examples/network/simple_sniffer:
|
||||
disable:
|
||||
- if: IDF_TARGET in ["esp32h2"]
|
||||
|
@ -1,5 +1,5 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# Bridge Example
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
525
examples/network/bridge/pytest_example_bridge.py
Normal file
525
examples/network/bridge/pytest_example_bridge.py
Normal file
@ -0,0 +1,525 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import time
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
from typing import List, Union
|
||||
|
||||
import netifaces
|
||||
import paramiko # type: ignore
|
||||
import pytest
|
||||
from common_test_methods import get_host_ip_by_interface
|
||||
from netmiko import ConnectHandler
|
||||
from pytest_embedded import Dut
|
||||
|
||||
# Testbed configuration
|
||||
BR_PORTS_NUM = 2
|
||||
IPERF_BW_LIM = 6
|
||||
MIN_UDP_THROUGHPUT = 5
|
||||
MIN_TCP_THROUGHPUT = 4
|
||||
|
||||
|
||||
class EndnodeSsh:
|
||||
def __init__(self, host_ip: str, usr: str, passwd: str):
|
||||
self.host_ip = host_ip
|
||||
self.ssh_client = paramiko.SSHClient()
|
||||
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
self.ssh_client.connect(hostname=self.host_ip,
|
||||
username=usr,
|
||||
password=passwd)
|
||||
self.executor: ThreadPoolExecutor
|
||||
self.async_result: Future
|
||||
|
||||
def exec_cmd(self, cmd: str) -> str:
|
||||
_, stdout, stderr = self.ssh_client.exec_command(cmd)
|
||||
|
||||
out = stdout.read().decode().strip()
|
||||
error = stderr.read().decode().strip()
|
||||
if error:
|
||||
out = ''
|
||||
logging.error('ssh_endnode_exec error: {}'.format(error))
|
||||
|
||||
return out # type: ignore
|
||||
|
||||
def exec_cmd_async(self, cmd: str) -> None:
|
||||
self.executor = ThreadPoolExecutor(max_workers=1)
|
||||
self.async_result = self.executor.submit(self.exec_cmd, cmd)
|
||||
|
||||
def get_async_res(self) -> str:
|
||||
return self.async_result.result(10) # type: ignore
|
||||
|
||||
def close(self) -> None:
|
||||
self.ssh_client.close()
|
||||
|
||||
|
||||
class SwitchSsh:
|
||||
EDGE_SWITCH_5XP = 0
|
||||
EDGE_SWITCH_10XP = 1
|
||||
|
||||
def __init__(self, host_ip: str, usr: str, passwd: str, device_type: int):
|
||||
self.host_ip = host_ip
|
||||
self.type = device_type
|
||||
|
||||
if self.type == self.EDGE_SWITCH_5XP:
|
||||
self.ssh_client = paramiko.SSHClient()
|
||||
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
self.ssh_client.connect(hostname=self.host_ip,
|
||||
username=usr,
|
||||
password=passwd)
|
||||
else:
|
||||
edgeSwitch = {
|
||||
'device_type': 'ubiquiti_edgeswitch',
|
||||
'host': self.host_ip,
|
||||
'username': usr,
|
||||
'password': passwd,
|
||||
}
|
||||
self.ssh_client = ConnectHandler(**edgeSwitch)
|
||||
|
||||
def exec_cmd(self, cmd: Union[str, List[str]]) -> str:
|
||||
if self.type == self.EDGE_SWITCH_5XP:
|
||||
_, stdout, stderr = self.ssh_client.exec_command(cmd)
|
||||
|
||||
out = stdout.read().decode().strip()
|
||||
error = stderr.read().decode().strip()
|
||||
|
||||
if error != 'TSW Init OK!':
|
||||
raise Exception('switch_5xp exec_cmd error: {}'.format(error))
|
||||
else:
|
||||
out = self.ssh_client.send_config_set(cmd, cmd_verify=False, exit_config_mode=False)
|
||||
return out # type: ignore
|
||||
|
||||
def switch_port_down(self, port: int) -> None:
|
||||
if self.type == self.EDGE_SWITCH_5XP:
|
||||
command = '/usr/bin/tswconf debug phy set ' + str(port - 1) + ' 0 0x800'
|
||||
self.exec_cmd(command)
|
||||
else:
|
||||
commands = ['interface GigabitEthernet ' + str(port), 'shutdown']
|
||||
self.exec_cmd(commands)
|
||||
|
||||
def switch_port_up(self, port: int) -> None:
|
||||
if self.type == self.EDGE_SWITCH_5XP:
|
||||
command = '/usr/bin/tswconf debug phy set ' + str(port - 1) + ' 0 0x1000'
|
||||
self.exec_cmd(command)
|
||||
else:
|
||||
commands = ['interface GigabitEthernet' + str(port), 'no shutdown']
|
||||
self.exec_cmd(commands)
|
||||
|
||||
def close(self) -> None:
|
||||
if self.type == self.EDGE_SWITCH_5XP:
|
||||
self.ssh_client.close()
|
||||
else:
|
||||
self.ssh_client.disconnect()
|
||||
|
||||
|
||||
def get_endnode_mac_by_interface(endnode: EndnodeSsh, if_name: str) -> str:
|
||||
ip_info = endnode.exec_cmd(f'ip addr show {if_name}')
|
||||
regex = if_name + r':.*?link/ether ([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})'
|
||||
mac_addr = re.search(regex, ip_info, re.DOTALL)
|
||||
if mac_addr is None:
|
||||
return ''
|
||||
return mac_addr.group(1)
|
||||
|
||||
|
||||
def get_endnode_ip_by_interface(endnode: EndnodeSsh, if_name: str) -> str:
|
||||
ip_info = endnode.exec_cmd(f'ip addr show {if_name}')
|
||||
regex = if_name + r':.*?inet (\d+[.]\d+[.]\d+[.]\d+)\/'
|
||||
ip_addr = re.search(regex, ip_info, re.DOTALL)
|
||||
if ip_addr is None:
|
||||
return ''
|
||||
return ip_addr.group(1)
|
||||
|
||||
|
||||
def get_host_interface_name_in_same_net(ip_addr: str) -> str:
|
||||
ip_net = ipaddress.IPv4Network(f'{ip_addr}/24', strict=False)
|
||||
for interface in netifaces.interfaces():
|
||||
addr = get_host_ip_by_interface(interface)
|
||||
if ipaddress.IPv4Address(addr) in ip_net:
|
||||
return str(interface)
|
||||
return ''
|
||||
|
||||
|
||||
def get_host_mac_by_interface(interface_name: str, addr_type: int = netifaces.AF_LINK) -> str:
|
||||
for _addr in netifaces.ifaddresses(interface_name)[addr_type]:
|
||||
host_mac = _addr['addr'].replace('%{}'.format(interface_name), '')
|
||||
assert isinstance(host_mac, str)
|
||||
return host_mac
|
||||
return ''
|
||||
|
||||
|
||||
def get_host_brcast_ip_by_interface(interface_name: str, ip_type: int = netifaces.AF_INET) -> str:
|
||||
for _addr in netifaces.ifaddresses(interface_name)[ip_type]:
|
||||
host_ip = _addr['broadcast'].replace('%{}'.format(interface_name), '')
|
||||
assert isinstance(host_ip, str)
|
||||
return host_ip
|
||||
return ''
|
||||
|
||||
|
||||
def run_iperf(proto: str, endnode: EndnodeSsh, server_ip: str, bandwidth_lim:int=10, interval:int=5, server_if:str='', client_if:str='') -> float:
|
||||
if proto == 'tcp':
|
||||
proto = ''
|
||||
else:
|
||||
proto = '-u'
|
||||
|
||||
if ipaddress.ip_address(server_ip).is_multicast:
|
||||
# Configure Multicast Server
|
||||
server_proc = subprocess.Popen(['iperf', '-u', '-s', '-i', '1', '-t', '%i' % interval, '-B', '%s%%%s'
|
||||
% (server_ip, server_if)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
# Configure Multicast Client
|
||||
endnode_ip = get_endnode_ip_by_interface(endnode, client_if)
|
||||
if endnode_ip == '':
|
||||
raise Exception('End node IP address not found')
|
||||
client_res = endnode.exec_cmd('iperf -u -c %s -t %i -i 1 -b %iM --ttl 5 -B %s' % (server_ip, interval, bandwidth_lim, endnode_ip))
|
||||
if server_proc.wait(10) is None: # Process did not finish.
|
||||
server_proc.terminate()
|
||||
else:
|
||||
# Configure Server
|
||||
server_proc = subprocess.Popen(['iperf', '%s' % proto, '-s', '-i', '1', '-t', '%i' % interval], text=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
# Configure Client
|
||||
client_res = endnode.exec_cmd('iperf %s -c %s -t %i -i 1 -b %iM' % (proto, server_ip, interval, bandwidth_lim))
|
||||
if server_proc.wait(10) is None: # Process did not finish.
|
||||
server_proc.terminate()
|
||||
|
||||
try:
|
||||
server_res = server_proc.communicate(timeout=15)[0]
|
||||
except subprocess.TimeoutExpired:
|
||||
server_proc.kill()
|
||||
server_res = server_proc.communicate()[0]
|
||||
|
||||
print('\n')
|
||||
print(client_res)
|
||||
print('\n')
|
||||
print(server_res)
|
||||
|
||||
SERVER_BANDWIDTH_LOG_PATTERN = r'(\d+\.\d+)\s*-\s*(\d+.\d+)\s+sec\s+[\d.]+\s+MBytes\s+([\d.]+)\s+Mbits\/sec'
|
||||
performance = re.search(SERVER_BANDWIDTH_LOG_PATTERN, server_res, re.DOTALL)
|
||||
if performance is None:
|
||||
return -1.0
|
||||
return float(performance.group(3))
|
||||
|
||||
|
||||
def send_brcast_msg_host_to_endnode(endnode: EndnodeSsh, host_brcast_ip: str, test_msg: str) -> str:
|
||||
endnode.exec_cmd_async('timeout 4s nc -u -w 0 -l -p 5100')
|
||||
time.sleep(1)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
sock.sendto(test_msg.encode('utf-8'), (host_brcast_ip, 5100))
|
||||
except socket.error as e:
|
||||
raise Exception('Host brcast send failed %s' % e)
|
||||
|
||||
nc_endnode_out = endnode.get_async_res()
|
||||
return nc_endnode_out
|
||||
|
||||
|
||||
def send_brcast_msg_endnode_to_host(endnode: EndnodeSsh, host_brcast_ip: str, test_msg: str) -> str:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.settimeout(5)
|
||||
try:
|
||||
sock.bind(('', 5100))
|
||||
except socket.error as e:
|
||||
raise Exception('Host bind failed %s' % e)
|
||||
|
||||
endnode.exec_cmd('echo -n "%s" | nc -b -w0 -u %s 5100' % (test_msg, host_brcast_ip))
|
||||
|
||||
try:
|
||||
nc_host_out = sock.recv(1500).decode('utf-8')
|
||||
except socket.error as e:
|
||||
raise Exception('Host recv failed %s' % e)
|
||||
|
||||
return nc_host_out
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.ethernet_w5500
|
||||
@pytest.mark.parametrize('config', [
|
||||
'w5500',
|
||||
], indirect=True)
|
||||
def test_esp_eth_bridge(
|
||||
dut: Dut,
|
||||
dev_user: str,
|
||||
dev_password: str
|
||||
) -> None:
|
||||
# ------------------------------ #
|
||||
# Pre-test testbed configuration #
|
||||
# ------------------------------ #
|
||||
# Get switch configuration info from the hostname
|
||||
host_name = socket.gethostname()
|
||||
regex = r'ethVM-(\d+)-(\d+)'
|
||||
sw_info = re.search(regex, host_name, re.DOTALL)
|
||||
if sw_info is None:
|
||||
raise Exception('Unexpected hostname')
|
||||
|
||||
sw_num = int(sw_info.group(1))
|
||||
port_num = int(sw_info.group(2))
|
||||
port_num_endnode = int(port_num) + 1 # endnode address is always + 1 to the host
|
||||
|
||||
endnode = EndnodeSsh(f'10.10.{sw_num}.{port_num_endnode}',
|
||||
dev_user,
|
||||
dev_password)
|
||||
switch1 = SwitchSsh(f'10.10.{sw_num}.100',
|
||||
dev_user,
|
||||
dev_password,
|
||||
SwitchSsh.EDGE_SWITCH_10XP)
|
||||
|
||||
# Collect all addresses in our network
|
||||
# ------------------------------------
|
||||
# Bridge (DUT) MAC
|
||||
br_mac = dut.expect(r'esp_netif_br_glue: ([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})')
|
||||
br_mac = br_mac.group(1).decode('utf-8')
|
||||
logging.info('ESP Bridge MAC %s', br_mac)
|
||||
# Get unique identification of each Ethernet port
|
||||
p1_id = dut.expect(r'Ethernet \((0x[0-9A-Fa-f]{8})\) Started')
|
||||
p1_id = p1_id.group(1).decode('utf-8')
|
||||
p2_id = dut.expect(r'Ethernet \((0x[0-9A-Fa-f]{8})\) Started')
|
||||
p2_id = p2_id.group(1).decode('utf-8')
|
||||
# Bridge (DUT) IP
|
||||
dut.expect_exact('Ethernet Got IP Address')
|
||||
br_ip = dut.expect(r'ETHIP:(\d+[.]\d+[.]\d+[.]\d+)\r')
|
||||
br_ip = br_ip.group(1).decode('utf-8')
|
||||
logging.info('ESP Bridge IP %s', br_ip)
|
||||
|
||||
# Host interface is in the same network as DUT
|
||||
host_if = get_host_interface_name_in_same_net(br_ip)
|
||||
# Test Host MAC
|
||||
host_mac = get_host_mac_by_interface(host_if)
|
||||
logging.info('Host MAC %s', host_mac)
|
||||
# Test Host IP
|
||||
host_ip = get_host_ip_by_interface(host_if, netifaces.AF_INET)
|
||||
logging.info('Host IP %s', host_ip)
|
||||
|
||||
endnode_if = host_if # endnode is a clone of the host
|
||||
# Endnode MAC
|
||||
endnode_mac = get_endnode_mac_by_interface(endnode, endnode_if)
|
||||
logging.info('Endnode MAC %s', endnode_mac)
|
||||
# Toggle link status at the End Node to initiate DHCP request
|
||||
endnode.exec_cmd(f'sudo ip link set down dev {endnode_if}')
|
||||
endnode.exec_cmd(f'sudo ip link set up dev {endnode_if}')
|
||||
# Endnode IP
|
||||
for i in range(15):
|
||||
endnode_ip = get_endnode_ip_by_interface(endnode, endnode_if)
|
||||
if endnode_ip != '':
|
||||
break
|
||||
time.sleep(1)
|
||||
logging.info('End node waiting for DHCP IP addr, %isec...', i)
|
||||
else:
|
||||
raise Exception('End node IP address not found')
|
||||
logging.info('Endnode IP %s', endnode_ip)
|
||||
|
||||
# --------------------------------------------------
|
||||
# TEST Objective 1: Ping the devices on the network
|
||||
# --------------------------------------------------
|
||||
# ping bridge
|
||||
ping_test = subprocess.call(f'ping {br_ip} -c 2', shell=True)
|
||||
if ping_test != 0:
|
||||
raise Exception('ESP bridge is not reachable')
|
||||
|
||||
# ping the end nodes of the network
|
||||
ping_test = subprocess.call(f'ping {endnode_ip} -c 2', shell=True)
|
||||
if ping_test != 0:
|
||||
raise Exception('End node is not reachable')
|
||||
|
||||
# -------------------------------------------------
|
||||
# TEST Objective 2: Ports Link Up/Down combinations
|
||||
# -------------------------------------------------
|
||||
logging.info('link down the port #1')
|
||||
switch1.switch_port_down(port_num)
|
||||
dut.expect_exact(f'Ethernet ({p1_id}) Link Down')
|
||||
|
||||
logging.info('link down both ports')
|
||||
switch1.switch_port_down(port_num_endnode)
|
||||
dut.expect_exact(f'Ethernet ({p2_id}) Link Down')
|
||||
|
||||
logging.info('link up the port #1')
|
||||
switch1.switch_port_up(port_num)
|
||||
dut.expect_exact(f'Ethernet ({p1_id}) Link Up')
|
||||
dut.expect_exact('Ethernet Got IP Address') # DHCP Server is connected to port #1
|
||||
|
||||
logging.info('link down both ports')
|
||||
switch1.switch_port_down(port_num)
|
||||
dut.expect_exact(f'Ethernet ({p1_id}) Link Down')
|
||||
|
||||
logging.info('link up the port #2')
|
||||
switch1.switch_port_up(port_num_endnode)
|
||||
dut.expect_exact(f'Ethernet ({p2_id}) Link Up') # Note: No "Ethernet Got IP Address" since DHCP Server is connected to port #1
|
||||
|
||||
logging.info('link down both ports')
|
||||
switch1.switch_port_down(port_num_endnode)
|
||||
dut.expect_exact(f'Ethernet ({p2_id}) Link Down')
|
||||
|
||||
logging.info('link up both ports')
|
||||
switch1.switch_port_up(port_num_endnode)
|
||||
dut.expect_exact(f'Ethernet ({p2_id}) Link Up')
|
||||
switch1.switch_port_up(port_num) # link up port #1 as last to ensure we Got IP address after link port #2 is up
|
||||
dut.expect_exact(f'Ethernet ({p1_id}) Link Up')
|
||||
dut.expect_exact('Ethernet Got IP Address')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# TEST Objective 3: IP traffic forwarding (iPerf between network end nodes)
|
||||
# --------------------------------------------------------------------------
|
||||
# unicast UDP
|
||||
bandwidth_udp = run_iperf('udp', endnode, host_ip, IPERF_BW_LIM, 5)
|
||||
logging.info('Unicast UDP average bandwidth: %s Mbits/s', bandwidth_udp)
|
||||
|
||||
# unicast TCP
|
||||
bandwidth_tcp = run_iperf('tcp', endnode, host_ip, IPERF_BW_LIM, 5)
|
||||
logging.info('Unicast TCP average bandwidth: %s Mbits/s', bandwidth_tcp)
|
||||
|
||||
# multicast UDP
|
||||
bandwidth_mcast_udp = run_iperf('udp', endnode, '224.0.1.4', IPERF_BW_LIM, 5, host_if, endnode_if)
|
||||
logging.info('Multicast UDP average bandwidth: %s Mbits/s', bandwidth_mcast_udp)
|
||||
|
||||
if bandwidth_udp < MIN_UDP_THROUGHPUT:
|
||||
raise Exception('Unicast UDP throughput expected %.2f, actual %.2f' % (MIN_UDP_THROUGHPUT, bandwidth_udp) + ' Mbits/s')
|
||||
if bandwidth_tcp < MIN_TCP_THROUGHPUT:
|
||||
raise Exception('Unicast TCP throughput expected %.2f, actual %.2f' % (MIN_TCP_THROUGHPUT, bandwidth_tcp) + ' Mbits/s')
|
||||
if bandwidth_mcast_udp < MIN_UDP_THROUGHPUT:
|
||||
raise Exception('Multicast UDP throughput expected %.2f, actual %.2f' % (MIN_UDP_THROUGHPUT, bandwidth_mcast_udp) + ' Mbits/s')
|
||||
|
||||
# ------------------------------------------------
|
||||
# TEST Objective 4: adding/deleting entries in FDB
|
||||
# ------------------------------------------------
|
||||
# At first test the Bridge Example Command Interface
|
||||
MAC_ADDR = '01:02:03:04:05:06'
|
||||
dut.write('\n')
|
||||
dut.expect_exact('bridge>')
|
||||
# invalid MAC format
|
||||
dut.write('add --addr=01:125:02:00:00:0A -d')
|
||||
dut.expect_exact('Ivalid MAC address format')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=01:QA:02:00:00:0A -d')
|
||||
dut.expect_exact('Ivalid MAC address format')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=01:00:02:00:0A -d')
|
||||
dut.expect_exact('Ivalid MAC address format')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
# invalid number of config parameters
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -d -c -p 1')
|
||||
dut.expect_exact('Invalid number or combination of arguments')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -d -c')
|
||||
dut.expect_exact('Invalid number or combination of arguments')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -f -c')
|
||||
dut.expect_exact('Invalid number or combination of arguments')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -d -p 1')
|
||||
dut.expect_exact('Invalid number or combination of arguments')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -f -p 1 -p 2')
|
||||
dut.expect_exact('Invalid number or combination of arguments')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add -p 1')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -p')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('remove')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('remove --addr=' + MAC_ADDR + ' -d')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
# Invalid port interval number
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -p 0')
|
||||
dut.expect_exact('Invalid port number')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -p ' + str(BR_PORTS_NUM + 1))
|
||||
dut.expect_exact('Invalid port number')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
|
||||
# try to add more FDB entries than configured max number
|
||||
for i in range(BR_PORTS_NUM + 1):
|
||||
dut.write('add --addr=01:02:03:00:00:%02x' % i + ' -d')
|
||||
if i < BR_PORTS_NUM:
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
else:
|
||||
dut.expect_exact('Adding FDB entry failed')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
|
||||
# try to remove non-existent FDB entry
|
||||
dut.write('remove --addr=' + MAC_ADDR)
|
||||
dut.expect_exact('Removing FDB entry failed')
|
||||
dut.expect_exact('Command returned non-zero error code: 0x1')
|
||||
|
||||
# remove dummy entries
|
||||
for i in range(BR_PORTS_NUM):
|
||||
dut.write('remove --addr=01:02:03:00:00:%02x' % i)
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
|
||||
# valid multiple ports at once
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -c -p 1 -p 2')
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
dut.write('remove --addr=' + MAC_ADDR)
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
dut.write('add --addr=' + MAC_ADDR + ' -p 1 -p 2')
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
dut.write('remove --addr=' + MAC_ADDR)
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
|
||||
# drop `Endnode` MAC and try to ping it from `Test Host`
|
||||
logging.info('Drop `Endnode` MAC')
|
||||
dut.write('add --addr=' + endnode_mac + ' -d')
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
ping_test = subprocess.call(f'ping {endnode_ip} -c 2', shell=True)
|
||||
if ping_test == 0:
|
||||
raise Exception('End node should not be reachable')
|
||||
logging.info('Remove Drop `Endnode` MAC entry')
|
||||
dut.write('remove --addr=' + endnode_mac)
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
ping_test = subprocess.call(f'ping {endnode_ip} -c 2', shell=True)
|
||||
if ping_test != 0:
|
||||
raise Exception('End node is not reachable')
|
||||
|
||||
# Since we have only two ports on DUT, it is kind of tricky to verify the forwarding directly with devices'
|
||||
# specific MAC addresses. However, we can verify it using broadcast address and to observe the system
|
||||
# behavior in all directions.
|
||||
|
||||
# At first, check normal condition
|
||||
TEST_MSG = 'ESP32 bridge test message'
|
||||
host_brcast_ip = get_host_brcast_ip_by_interface(host_if, netifaces.AF_INET)
|
||||
endnode_recv = send_brcast_msg_host_to_endnode(endnode, host_brcast_ip, TEST_MSG)
|
||||
if endnode_recv != TEST_MSG:
|
||||
raise Exception('Broadcast message was not received by endnode')
|
||||
|
||||
# now, configure forward the broadcast only to port #1
|
||||
dut.write('add --addr=ff:ff:ff:ff:ff:ff -p 1')
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
# we should not be able to receive a message at endnode (no forward to port #2)...
|
||||
endnode_recv = send_brcast_msg_host_to_endnode(endnode, host_brcast_ip, TEST_MSG)
|
||||
if endnode_recv != '':
|
||||
raise Exception('Broadcast message should not be received by endnode')
|
||||
|
||||
# ... but we should be able to do the same in opposite direction (forward to port #1)
|
||||
host_recv = send_brcast_msg_endnode_to_host(endnode, host_brcast_ip, TEST_MSG)
|
||||
if host_recv != TEST_MSG:
|
||||
raise Exception('Broadcast message was not received by host')
|
||||
|
||||
# Remove ARP record from Test host computer. ARP is broadcasted, hence Bridge port does not reply to a request since
|
||||
# it does not receive it (no forward to Bridge port). As a result, Bridge is not pingable.
|
||||
subprocess.call(f'sudo arp -d {br_ip}', shell=True)
|
||||
subprocess.call('arp -a', shell=True)
|
||||
ping_test = subprocess.call(f'ping {br_ip} -c 2', shell=True)
|
||||
if ping_test == 0:
|
||||
raise Exception('Bridge should not be reachable')
|
||||
|
||||
# Remove current broadcast entry and replace it with extended one which includes Bridge port
|
||||
# Now, we should be able to ping the Bridge...
|
||||
dut.write('remove --addr=ff:ff:ff:ff:ff:ff')
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
dut.write('add --addr=ff:ff:ff:ff:ff:ff -p 1 -c')
|
||||
dut.expect_exact('Bridge Config OK!')
|
||||
ping_test = subprocess.call(f'ping {br_ip} -c 2', shell=True)
|
||||
if ping_test != 0:
|
||||
raise Exception('Bridge is not reachable')
|
||||
|
||||
# ...but we should still not be able to receive a message at endnode (no forward to port #2)
|
||||
endnode_recv = send_brcast_msg_host_to_endnode(endnode, host_brcast_ip, TEST_MSG)
|
||||
if endnode_recv != '':
|
||||
raise Exception('Broadcast message should not be received by endnode')
|
||||
|
||||
endnode.close()
|
||||
switch1.close()
|
27
examples/network/bridge/sdkconfig.ci.w5500
Normal file
27
examples/network/bridge/sdkconfig.ci.w5500
Normal file
@ -0,0 +1,27 @@
|
||||
# Restricting to ESP32
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
|
||||
# Configure lwIP and NETIF to enable Bridge
|
||||
CONFIG_ESP_NETIF_TCPIP_LWIP=y
|
||||
CONFIG_ESP_NETIF_BRIDGE_EN=y
|
||||
|
||||
CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=1
|
||||
|
||||
# Configure network interface
|
||||
# CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET is not set
|
||||
CONFIG_EXAMPLE_USE_SPI_ETHERNET=y
|
||||
CONFIG_EXAMPLE_SPI_ETHERNETS_NUM=2
|
||||
# CONFIG_EXAMPLE_USE_DM9051 is not set
|
||||
# CONFIG_EXAMPLE_USE_KSZ8851SNL is not set
|
||||
CONFIG_EXAMPLE_USE_W5500=y
|
||||
CONFIG_EXAMPLE_ETH_SPI_HOST=1
|
||||
CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO=14
|
||||
CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO=13
|
||||
CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO=12
|
||||
CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ=20
|
||||
CONFIG_EXAMPLE_ETH_SPI_CS0_GPIO=15
|
||||
CONFIG_EXAMPLE_ETH_SPI_CS1_GPIO=32
|
||||
CONFIG_EXAMPLE_ETH_SPI_INT0_GPIO=4
|
||||
CONFIG_EXAMPLE_ETH_SPI_INT1_GPIO=33
|
||||
CONFIG_EXAMPLE_ETH_SPI_PHY_RST0_GPIO=-1
|
||||
CONFIG_EXAMPLE_ETH_SPI_PHY_RST1_GPIO=-1
|
@ -19,6 +19,8 @@ rangehttpserver
|
||||
dbus-python; sys_platform == 'linux'
|
||||
protobuf
|
||||
paho-mqtt
|
||||
paramiko
|
||||
netmiko
|
||||
|
||||
# iperf_test_util
|
||||
pyecharts
|
||||
|
Loading…
Reference in New Issue
Block a user