examples: added ESP-NETIF L2 TAP example

This commit is contained in:
Ondrej Kosta 2022-03-31 14:52:27 +02:00 committed by BOT
parent e4217ff868
commit fcdb0306d0
10 changed files with 577 additions and 0 deletions

View File

@ -81,6 +81,16 @@ example_test_pytest_esp32_ethernet_ota:
TARGET: ESP32
ENV_MARKER: ethernet_ota
example_test_pytest_esp32_ethernet_ip101:
extends:
- .pytest_examples_dir_template
- .rules:test:example_test-esp32
needs:
- build_pytest_examples_esp32
variables:
TARGET: ESP32
ENV_MARKER: ip101
.pytest_components_dir_template:
extends: .pytest_template
variables:

View File

@ -182,6 +182,19 @@ All set configuration options have getter counterpart option to read the current
| **ENOSPC** - NETIF L2 receive hook is already taken by other function when trying to assign Network Interface to the file descriptor.
| **ENOSYS** - unsupported operation, passed configuration option does not exists.
fcntl()
^^^^^^^
``fcntl()`` is used to manipulate with properties of opened ESP-NETIF L2 TAP file descriptor.
The following commands manipulate the status flags associated with file descriptor:
* ``F_GETFD`` - the function returns the file descriptor flags, the third argument is ignored.
* ``F_SETFD`` - sets the file descriptor flags to the value specified by the third argument. Zero is returned.
| On error, -1 is returned, and ``errno`` is set to indicate the error.
| **EBADF** - not a valid file descriptor.
| **ENOSYS** - unsupported command.
read()
^^^^^^
Opened and configured ESP-NETIF L2 TAP file descriptor can be accessed by ``read()`` to get inbound frames. The read operation can be either blocking or non-blocking based on actual state of ``O_NONBLOCK`` file status flag. When the file status flag is set blocking, the read operation waits until a frame is received and context is switched to other task. When the file status flag is set non-blocking, the read operation returns immediately. In such case, either a frame is returned if it was already queued or the function indicates the queue is empty. The number of queued frames associated with one file descriptor is limited by :ref:`CONFIG_ESP_NETIF_L2_TAP_RX_QUEUE_SIZE` Kconfig option. Once the number of queued frames reach configured threshold, the newly arriving frames are dropped until the queue has enough room to accept incoming traffic (Tail Drop queue management).
@ -230,6 +243,8 @@ Please refer to the example section for basic initialization of default interfac
- Ethernet: :example_file:`ethernet/basic/main/ethernet_example_main.c`
- L2 TAP: :example_file:`protocols/l2tap/main/l2tap_main.c`
.. only:: CONFIG_ESP_WIFI_SOFTAP_SUPPORT
- WiFi Access Point: :example_file:`wifi/getting_started/softAP/main/softap_example_main.c`

View File

@ -479,6 +479,7 @@ static void eth_stop(void)
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle));
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle));
s_eth_handle = NULL;
ESP_ERROR_CHECK(s_phy->del(s_phy));
ESP_ERROR_CHECK(s_mac->del(s_mac));
@ -486,6 +487,11 @@ static void eth_stop(void)
s_example_esp_netif = NULL;
}
esp_eth_handle_t get_example_eth_handle(void)
{
return s_eth_handle;
}
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
esp_netif_t *get_example_netif(void)

View File

@ -78,6 +78,15 @@ esp_netif_t *get_example_netif(void);
*/
esp_netif_t *get_example_netif_from_desc(const char *desc);
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
/**
* @brief Get the example Ethernet driver handle
*
* @return esp_eth_handle_t
*/
esp_eth_handle_t get_example_eth_handle(void);
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,9 @@
# 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)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(l2tap_example)

View File

@ -0,0 +1,133 @@
# ESP-NETIF L2 TAP Interface Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of ESP-NETIF L2 TAP Interface. The ESP-NETIF L2 TAP interface is ESP-IDF mechanism utilized to access Data Link Layer (L2 per OSI/ISO) for frame reception and transmission from user application. Its typical usage in embedded world might be implementation of non-IP related protocols such as PTP, Wake on LAN and others.
---
**Note:** Only Ethernet (IEEE 802.3) is currently supported.
---
The ESP-NETIF L2 TAP interface is accessed using file descriptors of VFS which provides a file-like interfacing (using functions like ``open()``, ``read()``, ``write()``, etc.) and its usage is specifically demonstrated by the following:
1) Waiting for a frame using blocking ``read()`` and echoing it back to the originator.
2) Waiting for a frame using ``select()`` with timeout and non-blocking ``read()``, and echoing it back to the originator.
3) Periodical broadcast transmission of "Hello message" to show proper construction of the Ethernet frame to be transmitted.
The work flow of the example is then as follows:
1. Install Ethernet driver.
2. Send DHCP requests and wait for a DHCP lease.
3. If get IP address successfully, then you will be able to ping the device.
4. Initialize ESP-NETIF L2 TAP interface, then you will be able to echo Ethernet frames with specific EthTypes (non-IP traffic).
## How to use example
### Hardware Required
To run this example, it is recommended to use official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it is integrated with a supported Ethernet PHY chip. Besides that, several third-party Ethernet modules which integrate MAC and PHY and provides common communication interface (e.g. SPI, USB, etc) is supported too. Consult "Example Connection Configuration" option in IDF project configuration tool for specific list of supported devices.
#### Pin Assignment
See common pin assignments for [Ethernet examples](../../ethernet/README.md#common-pin-assignments).
### Configure the project
```
idf.py menuconfig
```
See common configurations for [Ethernet examples](../../ethernet/README.md#common-configurations).
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
### Send a message from Host PC
Once the board is flashed and runs, you can send a message to it via raw socket and expect echo of that message. You can utilize ``pytest_example_l2tap_echo.py``. The scripts takes two optional parameters as (order of parameters needs to be kept):
```bash
python3 pytest_example_l2tap_echo.py [<dest_mac_address>] [<host_eth_interface>]
```
* `dest_mac_address` - MAC address of the destination device (the ESP32 board with flashed example). ESP 32 board MAC address is printed by the example so you can find it in monitor output under "Ethernet HW Addr". Broadcast address is used when this parameter is omitted.
* `host_eth_interface` - name of host PC Ethernet device on which the frame is to be transmitted/received. When this parameter is omitted, the script tries to find Ethernet interface automatically. If you need, you can find names of installed Network interfaces by running either of bellow commands on Linux:
```bash
$ ifconfig
$ ip address
$ ls /sys/class/net/
```
---
**Note:** ``pytest_example_l2tap_echo.py`` needs to be run with appropriate rights (e.g. use ``sudo``) since it accesses Network interface at Link (L2) layer, which may be considered harmful in general and so not allowed for regular user accounts.
---
---
**Warning:** ``pytest_example_l2tap_echo.py`` can be currently used only on Linux.
---
## Example Output
Host PC output:
```bash
$ sudo python3 pytest_example_l2tap_echo.py 08:3a:f2:31:20:f7 enx00e04c6801ac
2022-03-31 09:21:19,699 Use enx00e04c6801ac for testing
2022-03-31 09:21:19,737 Sent 52 bytes to 08:3a:f2:31:20:f7
2022-03-31 09:21:19,737 Sent msg: "ESP32 test message with EthType 0x2220"
2022-03-31 09:21:19,739 Received 60 bytes echoed from 08:3a:f2:31:20:f7
2022-03-31 09:21:19,739 Echoed msg: "ESP32 test message with EthType 0x2220"
2022-03-31 09:21:19,764 Use enx00e04c6801ac for testing
2022-03-31 09:21:19,804 Sent 52 bytes to 08:3a:f2:31:20:f7
2022-03-31 09:21:19,805 Sent msg: "ESP32 test message with EthType 0x2221"
2022-03-31 09:21:19,806 Received 60 bytes echoed from 08:3a:f2:31:20:f7
2022-03-31 09:21:19,806 Echoed msg: "ESP32 test message with EthType 0x2221"
```
ESP32 L2 TAP Example output:
```bash
I (5402) example_connect: Connected to example_connect: eth
I (5402) example_connect: - IPv4 address: 192.168.20.103
I (5412) example_connect: - IPv6 address: fe80:0000:0000:0000:0a3a:f2ff:fe31:20f7, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (5422) l2tap_example: Ethernet HW Addr 08:3a:f2:31:20:f7
I (5432) l2tap_example: /dev/net/tap fd 4 successfully opened
I (5432) l2tap_example: /dev/net/tap fd 3 successfully opened
I (5442) l2tap_example: L2 TAP fd 3 configured in blocking mode
I (5442) l2tap_example: L2 TAP fd 4 configured in non-blocking mode
I (5442) l2tap_example: L2 TAP fd 3 successfully bound to `ETH_DEF`
I (5462) l2tap_example: L2 TAP fd 4 successfully bound to `ETH_DEF`
I (5462) l2tap_example: L2 TAP fd 3 Ethernet type filter configured to 0x2220
I (5472) l2tap_example: L2 TAP fd 4 Ethernet type filter configured to 0x2221
I (5442) l2tap_example: /dev/net/tap fd 5 successfully opened
I (5492) l2tap_example: L2 TAP fd 5 configured in blocking mode
I (5492) l2tap_example: L2 TAP fd 5 successfully bound to `ETH_DEF`
I (5502) l2tap_example: L2 TAP fd 5 Ethernet type filter configured to 0x2223
I (12102) l2tap_example: fd 3 received 60 bytes from 00:e0:4c:68:01:ac
I (12162) l2tap_example: fd 4 received 60 bytes from 00:e0:4c:68:01:ac
```
## Troubleshooting
See common troubleshooting for examples using [Ethernet](../../ethernet/README.md#common-troubleshooting).
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "l2tap_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,265 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h> // read/write
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_event.h"
#include "esp_err.h"
#include "esp_eth.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "esp_vfs_l2tap.h"
#include "lwip/prot/ethernet.h" // Ethernet header
#include "arpa/inet.h" // ntohs, etc.
#include "protocol_examples_common.h"
#if !defined(CONFIG_EXAMPLE_CONNECT_ETHERNET)
#error Ethernet interface is not configured to connect.
#endif
#define ETH_INTERFACE "ETH_DEF"
#define ETH_TYPE_FILTER_BLOCK 0x2220
#define ETH_TYPE_FILTER_NOBLOCK 0x2221
#define ETH_TYPE_FILTER_TX 0x2223
#define INVALID_FD -1
typedef struct {
struct eth_hdr header;
char payload[44];
} test_vfs_eth_tap_msg_t;
static const char *TAG = "l2tap_example";
/** Opens and configures L2 TAP file descriptor */
static int init_l2tap_fd(int flags, uint16_t eth_type_filter)
{
int fd = open("/dev/net/tap", flags);
if (fd < 0) {
ESP_LOGE(TAG, "Unable to open L2 TAP interface: errno %d", errno);
goto error;
}
ESP_LOGI(TAG, "/dev/net/tap fd %d successfully opened", fd);
// Check fd block status (just for demonstration purpose)
flags = 0;
flags = fcntl(fd, F_GETFL);
if (flags == -1) {
ESP_LOGE(TAG, "Unable to get L2 TAP fd %d status flag: errno %d", fd, errno);
goto error;
}
if (flags & O_NONBLOCK) {
ESP_LOGI(TAG, "L2 TAP fd %d configured in non-blocking mode", fd);
} else {
ESP_LOGI(TAG, "L2 TAP fd %d configured in blocking mode", fd);
}
// Configure Ethernet interface on which to get raw frames
int ret;
if ((ret = ioctl(fd, L2TAP_S_INTF_DEVICE, ETH_INTERFACE)) == -1) {
ESP_LOGE(TAG, "Unable to bound L2 TAP fd %d with Ethernet device: errno %d", fd, errno);
goto error;
}
ESP_LOGI(TAG, "L2 TAP fd %d successfully bound to `%s`", fd, ETH_INTERFACE);
// Configure Ethernet frames we want to filter out
if ((ret = ioctl(fd, L2TAP_S_RCV_FILTER, &eth_type_filter)) == -1) {
ESP_LOGE(TAG, "Unable to configure fd %d Ethernet type receive filter: errno %d", fd, errno);
goto error;
}
ESP_LOGI(TAG, "L2 TAP fd %d Ethernet type filter configured to 0x%x", fd, eth_type_filter);
return fd;
error:
if (fd != INVALID_FD) {
close(fd);
}
return INVALID_FD;
}
/** Creates "echo" message from received frame */
static void create_echo_frame(test_vfs_eth_tap_msg_t *in_frame, test_vfs_eth_tap_msg_t *out_frame, int len)
{
// Set source address equal to our MAC address
esp_eth_handle_t eth_hndl = get_example_eth_handle();
uint8_t mac_addr[ETH_ADDR_LEN];
esp_eth_ioctl(eth_hndl, ETH_CMD_G_MAC_ADDR, mac_addr);
memcpy(out_frame->header.src.addr, mac_addr, ETH_ADDR_LEN);
// Set destination address equal to source address from where the frame was received
memcpy(out_frame->header.dest.addr, in_frame->header.src.addr, ETH_ADDR_LEN);
// Set Ethernet type
memcpy(&out_frame->header.type, &in_frame->header.type, sizeof(uint16_t));
// Copy the payload
memcpy(out_frame->payload, in_frame->payload, len - ETH_HEADER_LEN);
}
/** Demonstrates usage of L2 TAP in blocking mode */
static void echo_l2tap_task(void *pvParameters)
{
uint8_t rx_buffer[128];
int eth_tap_fd;
// Open and configure L2 TAP File descriptor
if ((eth_tap_fd = init_l2tap_fd(0, ETH_TYPE_FILTER_BLOCK)) == INVALID_FD) {
goto error;
}
while (1) {
ssize_t len = read(eth_tap_fd, rx_buffer, sizeof(rx_buffer));
if (len > 0) {
test_vfs_eth_tap_msg_t *recv_msg = (test_vfs_eth_tap_msg_t *)rx_buffer;
ESP_LOGI(TAG, "fd %d received %d bytes from %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", eth_tap_fd,
len, recv_msg->header.src.addr[0], recv_msg->header.src.addr[1], recv_msg->header.src.addr[2],
recv_msg->header.src.addr[3], recv_msg->header.src.addr[4], recv_msg->header.src.addr[5]);
// Construct echo frame
test_vfs_eth_tap_msg_t echo_msg;
create_echo_frame(recv_msg, &echo_msg, len);
// Send the echo message
ssize_t ret = write(eth_tap_fd, &echo_msg, len);
if (ret == -1) {
ESP_LOGE(TAG, "L2 TAP fd %d write error: errno: %d\n", eth_tap_fd, errno);
break;
}
} else {
ESP_LOGE(TAG, "L2 TAP fd %d read error: errno %d", eth_tap_fd, errno);
break;
}
}
close(eth_tap_fd);
error:
vTaskDelete(NULL);
}
/** Demonstrates usage of L2 TAP non-blocking mode with select */
static void nonblock_l2tap_echo_task(void *pvParameters)
{
uint8_t rx_buffer[128];
int eth_tap_fd;
// Open and configure L2 TAP File descriptor
if ((eth_tap_fd = init_l2tap_fd(O_NONBLOCK, ETH_TYPE_FILTER_NOBLOCK)) == INVALID_FD) {
goto error;
}
while (1) {
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(eth_tap_fd, &rfds);
int ret_sel = select(eth_tap_fd + 1, &rfds, NULL, NULL, &tv);
if (ret_sel > 0) {
ssize_t len = read(eth_tap_fd, rx_buffer, sizeof(rx_buffer));
if (len > 0) {
test_vfs_eth_tap_msg_t *recv_msg = (test_vfs_eth_tap_msg_t *)rx_buffer;
ESP_LOGI(TAG, "fd %d received %d bytes from %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", eth_tap_fd,
len, recv_msg->header.src.addr[0], recv_msg->header.src.addr[1], recv_msg->header.src.addr[2],
recv_msg->header.src.addr[3], recv_msg->header.src.addr[4], recv_msg->header.src.addr[5]);
// Construct echo frame
test_vfs_eth_tap_msg_t echo_msg;
create_echo_frame(recv_msg, &echo_msg, len);
// Send the echo message
ssize_t ret = write(eth_tap_fd, &echo_msg, len);
if (ret == -1) {
ESP_LOGE(TAG, "L2 TAP fd %d write error: errno: %d\n", eth_tap_fd, errno);
break;
}
} else {
ESP_LOGE(TAG, "L2 TAP fd %d read error: errno %d", eth_tap_fd, errno);
break;
}
} else if (ret_sel == 0) {
ESP_LOGD(TAG, "L2 TAP select timeout");
} else {
ESP_LOGE(TAG, "L2 TAP select error: errno %d", errno);
break;
}
}
close(eth_tap_fd);
error:
vTaskDelete(NULL);
}
/** Demonstrates of how to construct Ethernet frame for transmit via L2 TAP */
static void hello_tx_l2tap_task(void *pvParameters)
{
uint16_t eth_type_filter = ETH_TYPE_FILTER_TX;
int eth_tap_fd;
// Open and configure L2 TAP File descriptor
if ((eth_tap_fd = init_l2tap_fd(0, eth_type_filter)) == INVALID_FD) {
goto error;
}
// Construct "Hello" frame
test_vfs_eth_tap_msg_t hello_msg = {
.header = {
.src.addr = {0},
.dest.addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // broadcast address
.type = htons(eth_type_filter) // convert to big endian (network) byte order
},
.payload = "ESP32 hello to everybody!"
};
esp_eth_handle_t eth_hndl = get_example_eth_handle();
esp_eth_ioctl(eth_hndl, ETH_CMD_G_MAC_ADDR, hello_msg.header.src.addr);
while (1) {
// Send the Hello frame
ssize_t ret = write(eth_tap_fd, &hello_msg, ETH_HEADER_LEN + strlen(hello_msg.payload));
if (ret == -1) {
ESP_LOGE(TAG, "L2 TAP fd %d write error: errno: %d\n", eth_tap_fd, errno);
break;
}
vTaskDelay(pdMS_TO_TICKS(3000));
}
close(eth_tap_fd);
error:
vTaskDelete(NULL);
}
void app_main(void)
{
// Initialize L2 TAP VFS interface
ESP_ERROR_CHECK(esp_vfs_l2tap_intf_register(NULL));
// Initialize NVS Flash
ESP_ERROR_CHECK(nvs_flash_init());
// Initialize TCP/IP network interface (should be called only once in application)
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
esp_eth_handle_t eth_hndl = get_example_eth_handle();
uint8_t mac_addr[ETH_ADDR_LEN];
esp_eth_ioctl(eth_hndl, ETH_CMD_G_MAC_ADDR, mac_addr);
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
// Echo received message back to sender (blocks indefinitely)
xTaskCreate(echo_l2tap_task, "echo", 4096, NULL, 6, NULL);
// Echo received message back to sender (blocks with timeout)
xTaskCreate(nonblock_l2tap_echo_task, "echo_no-block", 4096, NULL, 5, NULL);
// Periodically broadcast "Hello message"
xTaskCreate(hello_tx_l2tap_task, "hello_tx", 4096, NULL, 4, NULL);
}

View File

@ -0,0 +1,122 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import contextlib
import logging
import os
import socket
import sys
from typing import Iterator
import pytest
from pytest_embedded import Dut
from scapy.all import Ether, raw
ETH_TYPE_1 = 0x2220
ETH_TYPE_2 = 0x2221
ETH_TYPE_3 = 0x2223
@contextlib.contextmanager
def configure_eth_if(eth_type: int, target_if: str='') -> Iterator[socket.socket]:
if target_if == '':
# try to determine which interface to use
netifs = os.listdir('/sys/class/net/')
logging.info('detected interfaces: %s', str(netifs))
for netif in netifs:
if netif.find('eth') == 0 or netif.find('enx') == 0 or netif.find('enp') == 0 or netif.find('eno') == 0:
target_if = netif
break
if target_if == '':
raise Exception('no network interface found')
logging.info('Use %s for testing', target_if)
so = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(eth_type))
so.bind((target_if, 0))
try:
yield so
finally:
so.close()
def send_recv_eth_frame(payload_str: str, eth_type: int, dest_mac: str, eth_if: str='') -> str:
with configure_eth_if(eth_type, eth_if) as so:
so.settimeout(10)
eth_frame = Ether(dst=dest_mac, src=so.getsockname()[4], type=eth_type) / raw(payload_str.encode())
try:
so.send(raw(eth_frame))
logging.info('Sent %d bytes to %s', len(eth_frame), dest_mac)
logging.info('Sent msg: "%s"', payload_str)
eth_frame_repl = Ether(so.recv(128))
if eth_frame_repl.type == eth_type:
logging.info('Received %d bytes echoed from %s', len(eth_frame_repl), eth_frame_repl.src)
logging.info('Echoed msg: "%s"', eth_frame_repl.load.decode())
except Exception as e:
raise e
# return echoed message and remove possible null characters which might have been appended since
# minimal size of Ethernet frame to be transmitted physical layer is 60B (not including CRC)
return str(eth_frame_repl.load.decode().rstrip('\x00'))
def recv_eth_frame(eth_type: int, eth_if: str='') -> str:
with configure_eth_if(eth_type, eth_if) as so:
so.settimeout(10)
try:
eth_frame = Ether(so.recv(128))
if eth_frame.type == eth_type:
logging.info('Received %d bytes from %s', len(eth_frame), eth_frame.src)
logging.info('Received msg: "%s"', eth_frame.load.decode())
except Exception as e:
raise e
return str(eth_frame.load.decode().rstrip('\x00'))
def actual_test(dut: Dut) -> None:
# Get DUT's MAC address
res = dut.expect(
r'([\s\S]*)'
r'Ethernet HW Addr ([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(2)
# Receive "ESP32 Hello frame"
recv_eth_frame(ETH_TYPE_3)
# Sent a message and receive its echo
message = 'ESP32 test message with EthType ' + hex(ETH_TYPE_1)
echoed = send_recv_eth_frame(message, ETH_TYPE_1, dut_mac)
if echoed == message:
logging.info('PASS')
else:
raise Exception('Echoed message does not match!')
message = 'ESP32 test message with EthType ' + hex(ETH_TYPE_2)
echoed = send_recv_eth_frame(message, ETH_TYPE_2, dut_mac)
if echoed == message:
logging.info('PASS')
else:
raise Exception('Echoed message does not match!')
@pytest.mark.esp32 # internally tested using ESP32 with IP101 but may support all targets with SPI Ethernet
@pytest.mark.ip101
@pytest.mark.flaky(reruns=3, reruns_delay=5)
def test_esp_netif_l2tap_example(dut: Dut) -> None:
actual_test(dut)
if __name__ == '__main__':
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
message_1 = 'ESP32 test message with EthType ' + hex(ETH_TYPE_1)
message_2 = 'ESP32 test message with EthType ' + hex(ETH_TYPE_2)
# Usage: pytest_example_l2tap_echo.py [<dest_mac_address>] [<host_eth_interface>]
if sys.argv[2:]: # if two arguments provided:
send_recv_eth_frame(message_1, ETH_TYPE_1, sys.argv[1], sys.argv[2])
send_recv_eth_frame(message_2, ETH_TYPE_2, sys.argv[1], sys.argv[2])
elif sys.argv[1:]: # if one argument provided:
send_recv_eth_frame(message_1, ETH_TYPE_1, sys.argv[1])
send_recv_eth_frame(message_2, ETH_TYPE_2, sys.argv[1])
else: # if no argument provided:
send_recv_eth_frame(message_1, ETH_TYPE_1, 'ff:ff:ff:ff:ff:ff')
send_recv_eth_frame(message_2, ETH_TYPE_2, 'ff:ff:ff:ff:ff:ff')

View File

@ -0,0 +1,6 @@
# L2 TAP currently supports Ethernet only
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
# Enable L2 TAP
CONFIG_ESP_NETIF_L2_TAP=y