2021-06-01 12:58:30 -04:00
/*
2022-01-24 09:40:11 -05:00
* SPDX - FileCopyrightText : 2021 - 2022 Espressif Systems ( Shanghai ) CO LTD
2021-06-01 12:58:30 -04:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
# include <stdio.h>
# include <string.h>
# include <sys/stat.h>
# include <sys/fcntl.h>
# include <sys/ioctl.h>
# include <sys/types.h>
# include <unistd.h>
# include <errno.h>
# include "freertos/FreeRTOS.h"
# include "freertos/task.h"
# include "freertos/event_groups.h"
# include "freertos/semphr.h"
# include "esp_netif.h"
# include "esp_eth.h"
# include "esp_event.h"
# include "esp_log.h"
# include "driver/gpio.h"
# include "sdkconfig.h"
# include "arpa/inet.h" // for ntohs, etc.
# include "lwip/prot/ethernet.h" // Ethernet headers
# include "unity.h"
# include "test_utils.h"
# include "esp_vfs_l2tap.h"
# if CONFIG_ESP_NETIF_L2_TAP
# define ETH_FILTER_LE 0x7A05
# define ETH_FILTER_BE 0x057A
# define ETH_START_BIT BIT(0)
# define ETH_STOP_BIT BIT(1)
# define ETH_CONNECT_BIT BIT(2)
# define ETH_GOT_IP_BIT BIT(3)
# define DEFAULT_SEND_DELAY_MS 1000
static const char * TAG = " l2tap_test " ;
typedef struct {
esp_netif_t * eth_netif ;
esp_eth_mac_t * mac ;
esp_eth_phy_t * phy ;
void * glue ;
esp_eth_handle_t eth_handle ;
} test_vfs_eth_network_t ;
typedef struct {
test_vfs_eth_network_t * eth_network_hndls_p ;
int32_t eth_type ;
uint32_t send_delay_ms ;
} send_task_control_t ;
typedef struct {
struct eth_hdr header ;
union {
int cnt ;
char str [ 44 ] ;
} ;
} test_vfs_eth_tap_msg_t ;
/* =============================================================================
* Common Routines
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* @ brief Event handler for Ethernet events
*
*/
static void eth_event_handler ( void * arg , esp_event_base_t event_base ,
int32_t event_id , void * event_data )
{
EventGroupHandle_t eth_event_group = ( EventGroupHandle_t ) arg ;
uint8_t mac_addr [ 6 ] = { 0 } ;
/* we can get the ethernet driver handle from event data */
esp_eth_handle_t eth_handle = * ( esp_eth_handle_t * ) event_data ;
switch ( event_id ) {
case ETHERNET_EVENT_CONNECTED :
esp_eth_ioctl ( eth_handle , ETH_CMD_G_MAC_ADDR , mac_addr ) ;
ESP_LOGI ( TAG , " Ethernet Link Up " ) ;
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 ] ) ;
xEventGroupSetBits ( eth_event_group , ETH_CONNECT_BIT ) ;
break ;
case ETHERNET_EVENT_DISCONNECTED :
ESP_LOGI ( TAG , " Ethernet Link Down " ) ;
break ;
case ETHERNET_EVENT_START :
xEventGroupSetBits ( eth_event_group , ETH_START_BIT ) ;
ESP_LOGI ( TAG , " Ethernet Started " ) ;
break ;
case ETHERNET_EVENT_STOP :
xEventGroupSetBits ( eth_event_group , ETH_STOP_BIT ) ;
ESP_LOGI ( TAG , " Ethernet Stopped " ) ;
break ;
default :
break ;
}
}
/**
* @ brief Event handler for IP_EVENT_ETH_GOT_IP
*
*/
static void got_ip_event_handler ( void * arg , esp_event_base_t event_base ,
int32_t event_id , void * event_data )
{
EventGroupHandle_t eth_event_group = ( EventGroupHandle_t ) arg ;
ip_event_got_ip_t * event = ( ip_event_got_ip_t * ) event_data ;
const esp_netif_ip_info_t * ip_info = & event - > ip_info ;
ESP_LOGI ( TAG , " Ethernet Got IP Address " ) ;
ESP_LOGI ( TAG , " ~~~~~~~~~~~ " ) ;
ESP_LOGI ( TAG , " ETHIP: " IPSTR , IP2STR ( & ip_info - > ip ) ) ;
ESP_LOGI ( TAG , " ETHMASK: " IPSTR , IP2STR ( & ip_info - > netmask ) ) ;
ESP_LOGI ( TAG , " ETHGW: " IPSTR , IP2STR ( & ip_info - > gw ) ) ;
ESP_LOGI ( TAG , " ~~~~~~~~~~~ " ) ;
xEventGroupSetBits ( eth_event_group , ETH_GOT_IP_BIT ) ;
}
/**
* @ brief Initializes Ethernet interface and starts driver ( internal EMAC & IP101 PHY )
*
*/
static void ethernet_init ( test_vfs_eth_network_t * network_hndls )
{
EventBits_t bits = 0 ;
EventGroupHandle_t eth_event_group = xEventGroupCreate ( ) ;
TEST_ASSERT ( eth_event_group ! = NULL ) ;
test_case_uses_tcpip ( ) ;
TEST_ESP_OK ( esp_event_loop_create_default ( ) ) ;
// create TCP/IP netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH ( ) ;
network_hndls - > eth_netif = esp_netif_new ( & netif_cfg ) ;
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG ( ) ;
2022-01-24 09:40:11 -05:00
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG ( ) ;
network_hndls - > mac = esp_eth_mac_new_esp32 ( & esp32_emac_config , & mac_config ) ;
2021-06-01 12:58:30 -04:00
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG ( ) ;
network_hndls - > phy = esp_eth_phy_new_ip101 ( & phy_config ) ;
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG ( network_hndls - > mac , network_hndls - > phy ) ;
network_hndls - > eth_handle = NULL ;
// install Ethernet driver
TEST_ESP_OK ( esp_eth_driver_install ( & eth_config , & network_hndls - > eth_handle ) ) ;
// combine driver with netif
network_hndls - > glue = esp_eth_new_netif_glue ( network_hndls - > eth_handle ) ;
TEST_ESP_OK ( esp_netif_attach ( network_hndls - > eth_netif , network_hndls - > glue ) ) ;
// register user defined event handers
TEST_ESP_OK ( esp_event_handler_register ( ETH_EVENT , ESP_EVENT_ANY_ID , & eth_event_handler , eth_event_group ) ) ;
TEST_ESP_OK ( esp_event_handler_register ( IP_EVENT , IP_EVENT_ETH_GOT_IP , & got_ip_event_handler , eth_event_group ) ) ;
// set PHY loopback mode
bool loopback_en = true ;
esp_eth_ioctl ( network_hndls - > eth_handle , ETH_CMD_S_PHY_LOOPBACK , & loopback_en ) ;
TEST_ESP_OK ( esp_eth_start ( network_hndls - > eth_handle ) ) ;
// wait for Ethernet start
bits = xEventGroupWaitBits ( eth_event_group , ETH_START_BIT , true , true , pdMS_TO_TICKS ( 10000 ) ) ;
TEST_ASSERT ( ( bits & ETH_START_BIT ) = = ETH_START_BIT ) ;
bits = xEventGroupWaitBits ( eth_event_group , ETH_CONNECT_BIT , true , true , pdMS_TO_TICKS ( 10000 ) ) ;
TEST_ASSERT ( ( bits & ETH_CONNECT_BIT ) = = ETH_CONNECT_BIT ) ;
}
/**
* @ brief De - initializes Ethernet interface
*
*/
static void ethernet_deinit ( test_vfs_eth_network_t * network_hndls )
{
TEST_ESP_OK ( esp_eth_stop ( network_hndls - > eth_handle ) ) ;
TEST_ESP_OK ( esp_eth_del_netif_glue ( network_hndls - > glue ) ) ;
/* driver should be uninstalled within 2 seconds */
//TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
esp_eth_driver_uninstall ( network_hndls - > eth_handle ) ;
TEST_ESP_OK ( network_hndls - > phy - > del ( network_hndls - > phy ) ) ;
TEST_ESP_OK ( network_hndls - > mac - > del ( network_hndls - > mac ) ) ;
TEST_ESP_OK ( esp_event_handler_unregister ( IP_EVENT , IP_EVENT_ETH_GOT_IP , got_ip_event_handler ) ) ;
TEST_ESP_OK ( esp_event_handler_unregister ( ETH_EVENT , ESP_EVENT_ANY_ID , eth_event_handler ) ) ;
esp_netif_destroy ( network_hndls - > eth_netif ) ;
TEST_ESP_OK ( esp_event_loop_delete_default ( ) ) ;
}
// Global test message send by "send_task"
static test_vfs_eth_tap_msg_t s_test_msg = {
. header = {
. src . addr = { 0 } ,
. dest . addr = { 0x01 , 0x00 , 0x00 , 0x00 , 0xBE , 0xEF } ,
. type = ETH_FILTER_BE ,
} ,
. str = " This is ESP32 L2 TAP test msg "
} ;
/**
* @ brief " Asynchronously " sends global test message ( send can be delayed per actual needs )
*
*/
static void send_task ( void * task_param )
{
send_task_control_t * send_control = ( send_task_control_t * ) task_param ;
test_vfs_eth_network_t * eth_network_hndls = send_control - > eth_network_hndls_p ;
esp_eth_ioctl ( eth_network_hndls - > eth_handle , ETH_CMD_G_MAC_ADDR , & s_test_msg . header . src . addr ) ;
if ( send_control - > eth_type > = 0 & & send_control - > eth_type < = 0xFFFF ) {
s_test_msg . header . type = htons ( send_control - > eth_type ) ;
} else {
s_test_msg . header . type = ETH_FILTER_BE ;
}
int eth_tap_fd_send = open ( " /dev/net/tap " , O_NONBLOCK ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd_send ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd_send , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// delay message write
vTaskDelay ( pdMS_TO_TICKS ( send_control - > send_delay_ms ) ) ;
int n = write ( eth_tap_fd_send , & s_test_msg , sizeof ( s_test_msg ) ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , n ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd_send ) ) ;
vTaskDelete ( NULL ) ;
}
/* =============================================================================
* Tests
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* @ brief Verifies vfs register / unregister functions
*
*/
TEST_CASE ( " esp32 l2tap - vfs register " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
int eth_tap_fd ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ESP_LOGI ( TAG , " Verify that L2 TAP VFS can be registered only once... " ) ;
TEST_ASSERT_EQUAL ( ESP_ERR_INVALID_STATE , esp_vfs_l2tap_intf_register ( NULL ) ) ;
//ethernet_init(ð_network_hndls);
eth_tap_fd = open ( " /dev/net/tap " , O_NONBLOCK ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
ESP_LOGI ( TAG , " Verify that L2 TAP VFS cannot be unregistered prior closing FD's... " ) ;
TEST_ASSERT_EQUAL ( ESP_ERR_INVALID_STATE , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ESP_LOGI ( TAG , " Verify that L2 TAP VFS can be registered with different base path... " ) ;
l2tap_vfs_config_t config = {
. base_path = " /dev/super_tap " ,
} ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( & config ) ) ;
eth_tap_fd = open ( " /dev/net/tap " , O_NONBLOCK ) ;
TEST_ASSERT_EQUAL ( - 1 , eth_tap_fd ) ;
eth_tap_fd = open ( " /dev/super_tap " , O_NONBLOCK ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
ESP_LOGI ( TAG , " Verify that L2 TAP VFS cannot be unregistered with default base path... " ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
TEST_ASSERT_EQUAL ( ESP_ERR_INVALID_STATE , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( " /dev/super_tap " ) ) ;
}
/**
* @ brief Verifies open and close functions
*
*/
typedef struct {
int eth_tap_fd ;
SemaphoreHandle_t sem ;
bool on_select ;
} open_close_task_ctrl_t ;
static void open_read_task ( void * task_param )
{
char in_buffer [ 300 ] = { 0 } ;
open_close_task_ctrl_t * task_control = ( open_close_task_ctrl_t * ) task_param ;
task_control - > eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , task_control - > eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( task_control - > eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( task_control - > eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( task_control - > eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
xSemaphoreGive ( task_control - > sem ) ;
if ( task_control - > on_select = = true ) {
ESP_LOGI ( TAG , " task1: going to block on select... " ) ;
struct timeval tv ;
tv . tv_sec = 1 ;
tv . tv_usec = 0 ;
fd_set rfds ;
FD_ZERO ( & rfds ) ;
FD_SET ( task_control - > eth_tap_fd , & rfds ) ;
// it is expected that blocking select is not unblocked by close and it timeouts (the fd number may be reused later
// though and so select released but that's not tested here)
TEST_ASSERT_EQUAL ( 0 , select ( task_control - > eth_tap_fd + 1 , & rfds , NULL , NULL , & tv ) ) ;
ESP_LOGI ( TAG , " task1: select timeout " ) ;
// get an error when try to use closed fd
TEST_ASSERT_EQUAL ( - 1 , read ( task_control - > eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) ;
} else {
ESP_LOGI ( TAG , " task1: going to block on read... " ) ;
// it is expected that blocking read is unblocked by close
TEST_ASSERT_EQUAL ( - 1 , read ( task_control - > eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) ;
ESP_LOGI ( TAG , " task1: unblocked " ) ;
}
xSemaphoreGive ( task_control - > sem ) ;
vTaskDelete ( NULL ) ;
}
static void close_task ( void * task_param )
{
open_close_task_ctrl_t * task_control = ( open_close_task_ctrl_t * ) task_param ;
vTaskDelay ( pdMS_TO_TICKS ( 500 ) ) ;
ESP_LOGI ( TAG , " task2: closing... " ) ;
TEST_ASSERT_EQUAL ( 0 , close ( task_control - > eth_tap_fd ) ) ;
vTaskDelete ( NULL ) ;
}
TEST_CASE ( " esp32 l2tap - open/close " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
open_close_task_ctrl_t task_control ;
task_control . sem = xSemaphoreCreateBinary ( ) ;
task_control . on_select = false ;
// ==========================================================
// Close when blocking on read
// ==========================================================
ESP_LOGI ( TAG , " Verify closing blocking read from higher priority task... " ) ;
xTaskCreate ( open_read_task , " open_read_task " , 4096 , & task_control , 5 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 1000 ) ) ) ;
xTaskCreate ( close_task , " close_task " , 4096 , & task_control , 10 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 1000 ) ) ) ;
ESP_LOGI ( TAG , " Verify closing blocking read from lower priority task... " ) ;
xTaskCreate ( open_read_task , " open_read_task " , 4096 , & task_control , 10 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 1000 ) ) ) ;
// Close blocking read from lower priority task
xTaskCreate ( close_task , " close_task " , 4096 , & task_control , 5 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 1000 ) ) ) ;
// ==========================================================
// Close when blocking on select
// ==========================================================
task_control . on_select = true ;
ESP_LOGI ( TAG , " Verify closing blocking select from higher priority task... " ) ;
xTaskCreate ( open_read_task , " open_read_task " , 4096 , & task_control , 5 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 2000 ) ) ) ;
xTaskCreate ( close_task , " close_task " , 4096 , & task_control , 10 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 2000 ) ) ) ;
ESP_LOGI ( TAG , " Verify closing blocking select from lower priority task... " ) ;
xTaskCreate ( open_read_task , " open_read_task " , 4096 , & task_control , 10 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 2000 ) ) ) ;
// Close blocking read from lower priority task
xTaskCreate ( close_task , " close_task " , 4096 , & task_control , 5 , NULL ) ;
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_control . sem , pdMS_TO_TICKS ( 2000 ) ) ) ;
// ==========================================================
// Limit of opened FD's
// ==========================================================
ESP_LOGI ( TAG , " Verify limit of opened fds... " ) ;
int eth_tap_fds [ CONFIG_ESP_NETIF_L2_TAP_MAX_FDS + 10 ] ;
int i ;
for ( i = 0 ; i < CONFIG_ESP_NETIF_L2_TAP_MAX_FDS + 10 ; i + + ) {
if ( ( eth_tap_fds [ i ] = open ( " /dev/net/tap " , 0 ) ) = = - 1 ) {
break ;
}
}
TEST_ASSERT_EQUAL ( CONFIG_ESP_NETIF_L2_TAP_MAX_FDS , i ) ;
while ( - - i > = 0 ) {
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fds [ i ] ) ) ;
}
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
vSemaphoreDelete ( task_control . sem ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies that read does not block when fd is opened in non - blocking mode
*
*/
TEST_CASE ( " esp32 l2tap - non blocking read " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
int n ;
char in_buffer [ 1500 ] = { 0 } ;
int loop_cnt = 0 ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , O_NONBLOCK ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
// ==========================================================
// Verify simple non-blocking read operations
// ==========================================================
ESP_LOGI ( TAG , " Verify simple non-blocking read operations... " ) ;
send_task_control_t send_task_ctrl = {
. eth_network_hndls_p = & eth_network_hndls ,
. eth_type = - 1 ,
. send_delay_ms = DEFAULT_SEND_DELAY_MS ,
} ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
// Verify the read does not block
while ( loop_cnt < 100 ) {
if ( ( n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) > 0 ) {
ESP_LOG_BUFFER_HEX ( TAG , in_buffer , n ) ;
ESP_LOGI ( TAG , " recv test string: %s " , ( ( test_vfs_eth_tap_msg_t * ) in_buffer ) - > str ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
break ;
} else {
TEST_ASSERT_EQUAL ( EAGAIN , errno ) ;
}
vTaskDelay ( pdMS_TO_TICKS ( 100 ) ) ;
loop_cnt + + ;
}
ESP_LOGI ( TAG , " elapsed loop_cnts to read %d " , loop_cnt ) ;
TEST_ASSERT_GREATER_THAN ( 0 , loop_cnt ) ;
TEST_ASSERT_LESS_THAN ( 100 , loop_cnt ) ;
TEST_ASSERT_EQUAL ( sizeof ( s_test_msg ) , n ) ;
// ==========================================================
// Verify non-blocking successful read operations used along with select
// ==========================================================
ESP_LOGI ( TAG , " Verify non-blocking successful read operations used along with select... " ) ;
memset ( in_buffer , 0 , sizeof ( in_buffer ) ) ;
// Wait up to x seconds
struct timeval tv ;
tv . tv_sec = 4 ;
tv . tv_usec = 0 ;
send_task_ctrl . eth_network_hndls_p = & eth_network_hndls ;
send_task_ctrl . send_delay_ms = DEFAULT_SEND_DELAY_MS ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
fd_set rfds ;
FD_ZERO ( & rfds ) ;
FD_SET ( eth_tap_fd , & rfds ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , select ( eth_tap_fd + 1 , & rfds , NULL , NULL , & tv ) ) ;
TEST_ASSERT_GREATER_THAN ( - 1 , FD_ISSET ( eth_tap_fd , & rfds ) ) ;
loop_cnt = 0 ;
while ( loop_cnt < 100 ) {
if ( ( n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) > 0 ) {
ESP_LOG_BUFFER_HEX ( TAG , in_buffer , n ) ;
ESP_LOGI ( TAG , " recv test string: %s " , ( ( test_vfs_eth_tap_msg_t * ) in_buffer ) - > str ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
break ;
}
vTaskDelay ( pdMS_TO_TICKS ( 100 ) ) ;
loop_cnt + + ;
}
ESP_LOGI ( TAG , " elapsed loop_cnts to read %d " , loop_cnt ) ;
TEST_ASSERT_EQUAL ( 0 , loop_cnt ) ;
TEST_ASSERT_EQUAL ( sizeof ( s_test_msg ) , n ) ;
// ==========================================================
// Verify non-blocking unsuccessful read operations used along with select
// ==========================================================
ESP_LOGI ( TAG , " Verify non-blocking unsuccessful read operations used along with select... " ) ;
memset ( in_buffer , 0 , sizeof ( in_buffer ) ) ;
// Wait up to x seconds
tv . tv_sec = 2 ;
tv . tv_usec = 0 ;
// no send_task => select will timeout
FD_ZERO ( & rfds ) ;
FD_SET ( eth_tap_fd , & rfds ) ;
TEST_ASSERT_EQUAL ( 0 , select ( eth_tap_fd + 1 , & rfds , NULL , NULL , & tv ) ) ;
TEST_ASSERT_EQUAL ( EAGAIN , errno ) ;
n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_EQUAL ( EAGAIN , errno ) ;
TEST_ASSERT_EQUAL ( - 1 , n ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies that read blocks when fd opened in blocking mode
*
*/
TEST_CASE ( " esp32 l2tap - blocking read " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
int n ;
char in_buffer [ 1500 ] = { 0 } ;
int loop_cnt = 0 ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
// ==========================================================
// Verify simple blocking read operations
// ==========================================================
ESP_LOGI ( TAG , " Verify simple blocking read operations... " ) ;
send_task_control_t send_task_ctrl = {
. eth_network_hndls_p = & eth_network_hndls ,
. eth_type = - 1 ,
. send_delay_ms = DEFAULT_SEND_DELAY_MS ,
} ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
// Verify the read does block
while ( loop_cnt < 100 ) {
if ( ( n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) > 0 ) {
ESP_LOG_BUFFER_HEX ( TAG , in_buffer , n ) ;
ESP_LOGI ( TAG , " recv test string: %s " , ( ( test_vfs_eth_tap_msg_t * ) in_buffer ) - > str ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
break ;
}
vTaskDelay ( pdMS_TO_TICKS ( 100 ) ) ;
loop_cnt + + ;
}
ESP_LOGI ( TAG , " elapsed loop_cnts to read %d " , loop_cnt ) ;
TEST_ASSERT_EQUAL ( 0 , loop_cnt ) ;
TEST_ASSERT_EQUAL ( sizeof ( s_test_msg ) , n ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
vTaskDelay ( pdMS_TO_TICKS ( 50 ) ) ; // just for sure to give some time to send task close fd
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies write
*
*/
TEST_CASE ( " esp32 l2tap - write " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
ESP_LOGI ( TAG , " Verify the write is not successful when use different Ethernet type than the fd is configured to... " ) ;
test_vfs_eth_tap_msg_t test_msg = {
. header = {
. src . addr = { 0 } ,
. dest . addr = { 0x01 , 0x00 , 0x00 , 0x00 , 0xBE , 0xEF } ,
. type = 0 ,
}
} ;
// set different Ethernet type than the fd is configured to
test_msg . header . type = htons ( ETH_FILTER_LE + 10 ) ;
TEST_ASSERT_EQUAL ( - 1 , write ( eth_tap_fd , & test_msg , sizeof ( test_msg ) ) ) ;
TEST_ASSERT_EQUAL ( EBADMSG , errno ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies that concrurent access to shared resource ( Ethernet ) is correctly handled
*
*/
# define NUM_OF_FDS 2
# define NUM_OF_TASKS 2
typedef struct {
uint16_t task_id ;
uint16_t eth_filter ;
SemaphoreHandle_t semaphore ;
} task_info_t ;
static void multi_fds_task ( void * task_param )
{
task_info_t * task_info = ( task_info_t * ) task_param ;
uint16_t eth_filter = task_info - > eth_filter ;
int eth_tap_fds [ NUM_OF_FDS ] ;
test_vfs_eth_tap_msg_t test_msg = {
. header = {
. src . addr = { 0 } ,
. dest . addr = { 0x01 , 0x00 , 0x00 , 0x00 , 0xBE , 0xEF } ,
. type = 0 ,
}
} ;
char in_buffer [ sizeof ( test_msg ) ] = { 0 } ;
for ( int i = 0 ; i < sizeof ( eth_tap_fds ) / sizeof ( int ) ; i + + ) {
eth_tap_fds [ i ] = open ( " /dev/net/tap " , O_NONBLOCK ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fds [ i ] ) ;
uint16_t eth_type_filter = eth_filter + i ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fds [ i ] , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fds [ i ] , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fds [ i ] , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
}
int msg_cnt ;
for ( msg_cnt = 0 ; msg_cnt < 5000 ; msg_cnt + + ) {
for ( int i = 0 ; i < sizeof ( eth_tap_fds ) / sizeof ( int ) ; i + + ) {
test_msg . header . type = htons ( eth_filter + i ) ;
test_msg . cnt = msg_cnt ;
TEST_ASSERT_NOT_EQUAL ( - 1 , write ( eth_tap_fds [ i ] , & test_msg , sizeof ( test_msg ) ) ) ;
memset ( in_buffer , 0 , sizeof ( in_buffer ) ) ;
struct timeval tv ;
tv . tv_sec = 0 ;
tv . tv_usec = 300 * 1000 ;
fd_set rfds ;
FD_ZERO ( & rfds ) ;
FD_SET ( eth_tap_fds [ i ] , & rfds ) ;
if ( select ( eth_tap_fds [ i ] + 1 , & rfds , NULL , NULL , & tv ) > - 1 ) {
if ( FD_ISSET ( eth_tap_fds [ i ] , & rfds ) ) {
int n = read ( eth_tap_fds [ i ] , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL ( msg_cnt , ( ( test_vfs_eth_tap_msg_t * ) in_buffer ) - > cnt ) ;
} else {
TEST_FAIL_MESSAGE ( " time out, frame was not successfully written (due to possible race condition) " ) ;
}
} else {
TEST_FAIL_MESSAGE ( " select error " ) ;
}
}
}
for ( int i = 0 ; i < sizeof ( eth_tap_fds ) / sizeof ( int ) ; i + + ) {
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fds [ i ] ) ) ;
}
ESP_LOGI ( TAG , " multi_fds_task %u done " , task_info - > task_id ) ;
xSemaphoreGive ( task_info - > semaphore ) ;
vTaskDelete ( NULL ) ;
}
TEST_CASE ( " esp32 l2tap - read/write multiple fd's used by multiple tasks " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
task_info_t task_info [ NUM_OF_TASKS ] ;
for ( int i = 0 ; i < NUM_OF_TASKS ; i + + ) {
task_info [ i ] . task_id = i ;
task_info [ i ] . eth_filter = 0x750A + i * 100 ;
task_info [ i ] . semaphore = xSemaphoreCreateBinary ( ) ;
}
xTaskCreate ( multi_fds_task , " multi_fds_task_1 " , 4096 , & task_info [ 0 ] , tskIDLE_PRIORITY + 2 , NULL ) ;
xTaskCreate ( multi_fds_task , " multi_fds_task_2 " , 4096 , & task_info [ 1 ] , tskIDLE_PRIORITY + 4 , NULL ) ;
for ( int i = 0 ; i < NUM_OF_TASKS ; i + + ) {
TEST_ASSERT_NOT_EQUAL ( pdFALSE , xSemaphoreTake ( task_info [ i ] . semaphore , pdMS_TO_TICKS ( 20 * 1000 ) ) ) ;
vSemaphoreDelete ( task_info [ i ] . semaphore ) ;
}
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies proper functionality of ioctl RCV_FILTER option
*
*/
TEST_CASE ( " esp32 l2tap - ioctl - RCV_FILTER " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
char in_buffer [ 1500 ] = { 0 } ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
uint16_t eth_type_filter_get = 0 ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_RCV_FILTER , & eth_type_filter_get ) ) ;
TEST_ASSERT_EQUAL ( eth_type_filter , eth_type_filter_get ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
ESP_LOGI ( TAG , " Confirm that we are able to receive message with default Ethertype... " ) ;
send_task_control_t send_task_ctrl = {
. eth_network_hndls_p = & eth_network_hndls ,
. eth_type = - 1 ,
. send_delay_ms = DEFAULT_SEND_DELAY_MS ,
} ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
int n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
// Set new value of Ethernet type filter
eth_type_filter = ETH_FILTER_LE + 100 ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_RCV_FILTER , & eth_type_filter_get ) ) ;
TEST_ASSERT_EQUAL ( eth_type_filter , eth_type_filter_get ) ;
// Send the test message with the old (default) value of the Ethernet filter
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
ESP_LOGI ( TAG , " Confirm that the message was not received (select timeouts)... " ) ;
struct timeval tv ;
tv . tv_sec = 0 ;
tv . tv_usec = 1000 * 1000 ;
fd_set rfds ;
FD_ZERO ( & rfds ) ;
FD_SET ( eth_tap_fd , & rfds ) ;
TEST_ASSERT_EQUAL ( 0 , select ( eth_tap_fd + 1 , & rfds , NULL , NULL , & tv ) ) ;
// Send the test message with the new value of the Ethernet filter
send_task_ctrl . eth_type = eth_type_filter ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
ESP_LOGI ( TAG , " Verify that the message with new Etherbet type is received... " ) ;
n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
// Open another raw ethernet fd and try to set the same filter as for the first one
int eth_tap_fd_2 = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd_2 ) ;
ESP_LOGI ( TAG , " Verify that the setting the same Ethernet type to other fd was unsuccessful... " ) ;
TEST_ASSERT_EQUAL ( - 1 , ioctl ( eth_tap_fd_2 , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
TEST_ASSERT_EQUAL ( EINVAL , errno ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd_2 , L2TAP_G_RCV_FILTER , & eth_type_filter_get ) ) ;
TEST_ASSERT_EQUAL ( 0 , eth_type_filter_get ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd_2 ) ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
vTaskDelay ( pdMS_TO_TICKS ( 50 ) ) ; // just for sure to give some time to send task close fd
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies proper functionality of ioctl INTF_DEVICE option
*
*/
static esp_err_t dummy_rx_hook ( esp_netif_t * esp_netif , void * buff , size_t * size )
{
return ESP_OK ;
}
TEST_CASE ( " esp32 l2tap - ioctl - INTF_DEVICE " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
char in_buffer [ 1500 ] = { 0 } ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
uint16_t eth_type_filter_get = 0 ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_RCV_FILTER , & eth_type_filter_get ) ) ;
TEST_ASSERT_EQUAL ( eth_type_filter , eth_type_filter_get ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
ESP_LOGI ( TAG , " Confirm that we are able to receive message at default netif... " ) ;
send_task_control_t send_task_ctrl = {
. eth_network_hndls_p = & eth_network_hndls ,
. eth_type = - 1 ,
. send_delay_ms = DEFAULT_SEND_DELAY_MS ,
} ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
int n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
ESP_LOGI ( TAG , " Try to set non-existing Ethernet interface... " ) ;
TEST_ASSERT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_NOT_DEF " ) ) ;
TEST_ASSERT_EQUAL ( ENODEV , errno ) ;
ESP_LOGI ( TAG , " Verify that previous setting is kept... " ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 10 , NULL ) ; // set higher priority, we need to be sure that "send" task closes FD prior main task
n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
ESP_LOGI ( TAG , " Verify ioctl response when Ethernet interface has already attached recv hook... " ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_netif_recv_hook_attach ( eth_network_hndls . eth_netif , & dummy_rx_hook ) ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Try to set Ethernet interface with already attached recv hook
TEST_ASSERT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
TEST_ASSERT_EQUAL ( ENOSPC , errno ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL ( NULL , if_key_str ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
vTaskDelay ( pdMS_TO_TICKS ( 50 ) ) ; // just for sure to give some time to send task close fd
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies proper functionality of ioctl unknown option
*
*/
TEST_CASE ( " esp32 l2tap - ioctl - unknown " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Try to execute unknown option
uint32_t option_val = 0x0BAD ;
TEST_ASSERT_EQUAL ( - 1 , ioctl ( eth_tap_fd , 412 , & option_val ) ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies proper functionality of fcntl
*
*/
TEST_CASE ( " esp32 l2tap - fcntl " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
char in_buffer [ 1500 ] = { 0 } ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
uint16_t eth_type_filter_get = 0 ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_RCV_FILTER , & eth_type_filter_get ) ) ;
TEST_ASSERT_EQUAL ( eth_type_filter , eth_type_filter_get ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
send_task_control_t send_task_ctrl = {
. eth_network_hndls_p = & eth_network_hndls ,
. eth_type = - 1 ,
. send_delay_ms = DEFAULT_SEND_DELAY_MS ,
} ;
// Confirm the read blocks by default
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
int n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
// Set non-blocking
int flags = fcntl ( eth_tap_fd , F_GETFL ) ;
TEST_ASSERT_GREATER_THAN ( - 1 , flags ) ;
flags | = O_NONBLOCK ;
TEST_ASSERT_EQUAL ( 0 , fcntl ( eth_tap_fd , F_SETFL , flags ) ) ;
int flags_new = fcntl ( eth_tap_fd , F_GETFL ) ;
TEST_ASSERT_GREATER_THAN ( - 1 , flags_new ) ;
TEST_ASSERT_EQUAL ( flags_new , flags ) ;
// Verify the read does not block
int loop_cnt = 0 ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
while ( loop_cnt < 100 ) {
if ( ( n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) > 0 ) {
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
break ;
} else {
TEST_ASSERT_EQUAL ( EAGAIN , errno ) ;
}
vTaskDelay ( pdMS_TO_TICKS ( 100 ) ) ;
loop_cnt + + ;
}
ESP_LOGI ( TAG , " elapsed loop_cnts to read %d " , loop_cnt ) ;
TEST_ASSERT_GREATER_THAN ( 0 , loop_cnt ) ;
TEST_ASSERT_LESS_THAN ( 100 , loop_cnt ) ;
TEST_ASSERT_EQUAL ( sizeof ( s_test_msg ) , n ) ;
// Set back to blocking
flags = fcntl ( eth_tap_fd , F_GETFL ) ;
TEST_ASSERT_GREATER_THAN ( - 1 , flags ) ;
flags & = ~ O_NONBLOCK ;
TEST_ASSERT_EQUAL ( 0 , fcntl ( eth_tap_fd , F_SETFL , flags ) ) ;
flags_new = fcntl ( eth_tap_fd , F_GETFL ) ;
TEST_ASSERT_GREATER_THAN ( - 1 , flags_new ) ;
TEST_ASSERT_EQUAL ( flags_new , flags ) ;
loop_cnt = 0 ;
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
while ( loop_cnt < 100 ) {
if ( ( n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ) > 0 ) {
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
break ;
} else {
TEST_ASSERT_NOT_EQUAL ( EAGAIN , errno ) ;
}
vTaskDelay ( pdMS_TO_TICKS ( 100 ) ) ;
loop_cnt + + ;
}
ESP_LOGI ( TAG , " elapsed loop_cnts to read %d " , loop_cnt ) ;
TEST_ASSERT_EQUAL ( 0 , loop_cnt ) ;
// Try to use unsupported operation
int new_fd = fcntl ( eth_tap_fd , F_DUPFD , 0 ) ;
TEST_ASSERT_EQUAL ( - 1 , new_fd ) ;
TEST_ASSERT_EQUAL ( ENOSYS , errno ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
vTaskDelay ( pdMS_TO_TICKS ( 50 ) ) ; // just for sure to give some time to send task close fd
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
/* ============================================================================= */
/**
* @ brief Verifies option to modify outbound Ethernet frames
*
*/
static char append_msg [ ] = " !append msg! " ;
static esp_err_t mod_eth_frame ( esp_netif_t * esp_netif , void * * buff , size_t * size )
{
uint8_t * mod_buff ;
mod_buff = malloc ( * size + sizeof ( append_msg ) ) ;
memcpy ( mod_buff , * buff , * size ) ;
memcpy ( & mod_buff [ * size ] , & append_msg , sizeof ( append_msg ) ) ;
* buff = mod_buff ;
* size + = sizeof ( append_msg ) ;
return ESP_OK ;
}
static void mod_eth_frame_clear ( esp_netif_t * esp_netif , void * buff , size_t size )
{
free ( buff ) ;
}
TEST_CASE ( " esp32 l2tap - netif tx hook " , " [ethernet][test_env=UT_T2_Ethernet] " )
{
test_vfs_eth_network_t eth_network_hndls ;
int eth_tap_fd ;
char in_buffer [ 1500 ] = { 0 } ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_register ( NULL ) ) ;
ethernet_init ( & eth_network_hndls ) ;
eth_tap_fd = open ( " /dev/net/tap " , 0 ) ;
TEST_ASSERT_NOT_EQUAL ( - 1 , eth_tap_fd ) ;
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
uint16_t eth_type_filter = ETH_FILTER_LE ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_RCV_FILTER , & eth_type_filter ) ) ;
uint16_t eth_type_filter_get = 0 ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_RCV_FILTER , & eth_type_filter_get ) ) ;
TEST_ASSERT_EQUAL ( eth_type_filter , eth_type_filter_get ) ;
// Set Ethernet interface on which to get raw frames
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_S_INTF_DEVICE , " ETH_DEF " ) ) ;
// Check the Ethernet interface was assigned
char * if_key_str ;
TEST_ASSERT_NOT_EQUAL ( - 1 , ioctl ( eth_tap_fd , L2TAP_G_INTF_DEVICE , & if_key_str ) ) ;
TEST_ASSERT_EQUAL_STRING ( " ETH_DEF " , if_key_str ) ;
send_task_control_t send_task_ctrl = {
. eth_network_hndls_p = & eth_network_hndls ,
. eth_type = - 1 ,
. send_delay_ms = 0 ,
} ;
// Send normal test message without any registered transmit hook at first
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
int n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL ( sizeof ( s_test_msg ) , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n ) ;
// Attach transmit hook function to modify outbound Ethernet frames
TEST_ASSERT_EQUAL ( ESP_OK , esp_netif_transmit_hook_attach ( eth_network_hndls . eth_netif , & mod_eth_frame ) ) ;
// Attach post transmit hook function to clear buffer created by mod_eth_frame
TEST_ASSERT_EQUAL ( ESP_OK , esp_netif_post_transmit_hook_attach ( eth_network_hndls . eth_netif , & mod_eth_frame_clear ) ) ;
// Verify the outbound traffic was modified as expected
xTaskCreate ( send_task , " raw_eth_send_task " , 1024 , & send_task_ctrl , tskIDLE_PRIORITY + 2 , NULL ) ;
n = read ( eth_tap_fd , in_buffer , sizeof ( in_buffer ) ) ;
TEST_ASSERT_GREATER_THAN ( 0 , n ) ;
TEST_ASSERT_EQUAL ( sizeof ( s_test_msg ) + sizeof ( append_msg ) , n ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & s_test_msg , in_buffer , n - sizeof ( append_msg ) ) ;
TEST_ASSERT_EQUAL_UINT8_ARRAY ( & append_msg , & in_buffer [ sizeof ( s_test_msg ) ] , n - sizeof ( s_test_msg ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_netif_transmit_hook_detach ( eth_network_hndls . eth_netif ) ) ;
TEST_ASSERT_EQUAL ( ESP_OK , esp_netif_post_transmit_hook_detach ( eth_network_hndls . eth_netif ) ) ;
TEST_ASSERT_EQUAL ( 0 , close ( eth_tap_fd ) ) ;
vTaskDelay ( pdMS_TO_TICKS ( 50 ) ) ; // just for sure to give some time to send task close fd
TEST_ASSERT_EQUAL ( ESP_OK , esp_vfs_l2tap_intf_unregister ( NULL ) ) ;
ethernet_deinit ( & eth_network_hndls ) ;
}
# endif // CONFIG_ESP_NETIF_L2_TAP