network_examples: added LwIP bridge test

This commit is contained in:
Ondrej Kosta
2023-03-31 17:24:16 +02:00
parent 09ecb70cb8
commit eb17a9d6e0
7 changed files with 589 additions and 2 deletions

View File

@ -352,6 +352,16 @@ pytest_examples_esp32_ethernet_ip101:
- build_pytest_examples_esp32 - build_pytest_examples_esp32
tags: [ esp32, ip101 ] 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: pytest_examples_esp32_flash_encryption:
extends: extends:
- .pytest_examples_dir_template - .pytest_examples_dir_template

View File

@ -123,6 +123,7 @@ ENV_MARKERS = {
'esp32eco3': 'Runner with esp32 eco3 connected', 'esp32eco3': 'Runner with esp32 eco3 connected',
'ecdsa_efuse': 'Runner with test ECDSA private keys programmed in efuse', 'ecdsa_efuse': 'Runner with test ECDSA private keys programmed in efuse',
'ccs811': 'Runner with CCS811 connected', 'ccs811': 'Runner with CCS811 connected',
'ethernet_w5500': 'SPI Ethernet module with two W5500',
# multi-dut markers # multi-dut markers
'ieee802154': 'ieee802154 related tests should run on ieee802154 runners.', 'ieee802154': 'ieee802154 related tests should run on ieee802154 runners.',
'openthread_br': 'tests should be used for openthread border router.', '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 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 # # 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)', 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('--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']() _idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']()

View File

@ -4,6 +4,10 @@ examples/network:
disable: disable:
- if: IDF_TARGET in ["esp32h2"] - 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: examples/network/simple_sniffer:
disable: disable:
- if: IDF_TARGET in ["esp32h2"] - if: IDF_TARGET in ["esp32h2"]

View File

@ -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 # Bridge Example
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)

View 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()

View 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

View File

@ -19,6 +19,8 @@ rangehttpserver
dbus-python; sys_platform == 'linux' dbus-python; sys_platform == 'linux'
protobuf protobuf
paho-mqtt paho-mqtt
paramiko
netmiko
# iperf_test_util # iperf_test_util
pyecharts pyecharts