netif: added ESP-NETIF L2 TAP interface

pull/7796/head
Ondrej Kosta 2021-06-01 18:58:30 +02:00
rodzic 150818e4f5
commit 3a7a67f174
17 zmienionych plików z 2334 dodań i 187 usunięć

Wyświetl plik

@ -127,6 +127,26 @@ typedef struct {
esp_err_t (*write_phy_reg)(esp_eth_handle_t eth_handle, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value);
} esp_eth_config_t;
/**
* @brief Command list for ioctl API
*
*/
typedef enum {
ETH_CMD_G_MAC_ADDR, /*!< Get MAC address */
ETH_CMD_S_MAC_ADDR, /*!< Set MAC address */
ETH_CMD_G_PHY_ADDR, /*!< Get PHY address */
ETH_CMD_S_PHY_ADDR, /*!< Set PHY address */
ETH_CMD_G_AUTONEGO, /*!< Get PHY Auto Negotiation */
ETH_CMD_S_AUTONEGO, /*!< Set PHY Auto Negotiation */
ETH_CMD_G_SPEED, /*!< Get Speed */
ETH_CMD_S_SPEED, /*!< Set Speed */
ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */
ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */
ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */
ETH_CMD_S_DUPLEX_MODE, /*!< Set Duplex mode */
ETH_CMD_S_PHY_LOOPBACK,/*!< Set PHY loopback */
} esp_eth_io_cmd_t;
/**
* @brief Default configuration for Ethernet driver
*

Wyświetl plik

@ -8,53 +8,12 @@
#include "esp_err.h"
#include "esp_event_base.h"
#include "hal/eth_types.h"
#include "esp_eth_spec.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Maximum Ethernet payload size
*
*/
#define ETH_MAX_PAYLOAD_LEN (1500)
/**
* @brief Minimum Ethernet payload size
*
*/
#define ETH_MIN_PAYLOAD_LEN (46)
/**
* @brief Ethernet frame header size: Dest addr(6 Bytes) + Src addr(6 Bytes) + length/type(2 Bytes)
*
*/
#define ETH_HEADER_LEN (14)
/**
* @brief Optional 802.1q VLAN Tag length
*
*/
#define ETH_VLAN_TAG_LEN (4)
/**
* @brief Jumbo frame payload size
*
*/
#define ETH_JUMBO_FRAME_PAYLOAD_LEN (9000)
/**
* @brief Maximum frame size (1522 Bytes)
*
*/
#define ETH_MAX_PACKET_SIZE (ETH_HEADER_LEN + ETH_VLAN_TAG_LEN + ETH_MAX_PAYLOAD_LEN + ETH_CRC_LEN)
/**
* @brief Minimum frame size (64 Bytes)
*
*/
#define ETH_MIN_PACKET_SIZE (ETH_HEADER_LEN + ETH_MIN_PAYLOAD_LEN + ETH_CRC_LEN)
/**
* @brief Ethernet driver state
*
@ -68,26 +27,6 @@ typedef enum {
ETH_STATE_PAUSE, /*!< Pause ability updated */
} esp_eth_state_t;
/**
* @brief Command list for ioctl API
*
*/
typedef enum {
ETH_CMD_G_MAC_ADDR, /*!< Get MAC address */
ETH_CMD_S_MAC_ADDR, /*!< Set MAC address */
ETH_CMD_G_PHY_ADDR, /*!< Get PHY address */
ETH_CMD_S_PHY_ADDR, /*!< Set PHY address */
ETH_CMD_G_AUTONEGO, /*!< Get PHY Auto Negotiation */
ETH_CMD_S_AUTONEGO, /*!< Set PHY Auto Negotiation */
ETH_CMD_G_SPEED, /*!< Get Speed */
ETH_CMD_S_SPEED, /*!< Set Speed */
ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */
ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */
ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */
ETH_CMD_S_DUPLEX_MODE, /*!< Set Duplex mode */
ETH_CMD_S_PHY_LOOPBACK,/*!< Set PHY loopback */
} esp_eth_io_cmd_t;
/**
* @brief Ethernet mediator
*

Wyświetl plik

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define ETH_ADDR_LEN (6) /* MAC Address length */
#define ETH_HEADER_LEN (14) /* Ethernet frame header size: Dest addr(6 Bytes) + Src addr(6 Bytes) + length/type(2 Bytes) */
#define ETH_VLAN_TAG_LEN (4) /* Optional 802.1q VLAN Tag length */
#define ETH_CRC_LEN (4) /* Ethernet frame CRC length */
#define ETH_MAX_PAYLOAD_LEN (1500) /* Maximum Ethernet payload size */
#define ETH_MIN_PAYLOAD_LEN (46) /* Minimum Ethernet payload size */
#define ETH_JUMBO_FRAME_PAYLOAD_LEN (9000) /* Jumbo frame payload size */
#define ETH_MAX_PACKET_SIZE (ETH_HEADER_LEN + ETH_VLAN_TAG_LEN + ETH_MAX_PAYLOAD_LEN + ETH_CRC_LEN) /* Maximum frame size (1522 Bytes) */
#define ETH_MIN_PACKET_SIZE (ETH_HEADER_LEN + ETH_MIN_PAYLOAD_LEN + ETH_CRC_LEN) /* Minimum frame size (64 Bytes) */
#define ETH_IEEE802_3_MAX_LEN 0x05DC /* Maximum length of IEEE802.3 frame stored in Length/Ethtype field */
/* EtherTypes */
#define ETH_T_8021Q 0x8100 /* 802.1Q VLAN tag */
#define ETH_T_8021AD 0x88A8 /* 802.1ad Service VLAN (double vlan tag) */

Wyświetl plik

@ -10,6 +10,7 @@ set(srcs
"esp_netif_handlers.c"
"esp_netif_objects.c"
"esp_netif_defaults.c"
"vfs_l2tap/esp_vfs_l2tap.c"
"lwip/esp_netif_lwip.c"
"lwip/esp_netif_lwip_defaults.c"
"lwip/esp_netif_sta_list.c")

Wyświetl plik

@ -37,4 +37,29 @@ menu "ESP NETIF Adapter"
Backward compatible interface to tcpip_adapter is enabled by default to support
legacy TCP/IP stack initialisation code. Disable this option to use only esp-netif
interface.
config ESP_NETIF_L2_TAP
bool "Enable netif L2 TAP support"
help
A user program can read/write link layer (L2) frames from/to ESP TAP device.
The ESP TAP device can be currently associated only with Ethernet physical interfaces.
config ESP_NETIF_L2_TAP_MAX_FDS
depends on ESP_NETIF_L2_TAP
int "Maximum number of opened L2 TAP File descriptors"
range 1 10
default 5
help
Maximum number of opened File descriptors (FD's) associated with ESP TAP device. ESP TAP FD's take up
a certain amount of memory, and allowing fewer FD's to be opened at the same time conserves memory.
config ESP_NETIF_L2_TAP_RX_QUEUE_SIZE
depends on ESP_NETIF_L2_TAP
int "Size of L2 TAP Rx queue"
range 1 100
default 20
help
Maximum number of frames queued in opened File descriptor. Once the queue is full, the newly arriving
frames are dropped until the queue has enough room to accept incoming traffic (Tail Drop queue
management).
endmenu

Wyświetl plik

@ -3,32 +3,52 @@
| (A) USER CODE |
| |
.............| init settings events |
. +----------------------------------------+
. +----------------------------------------+
. . | *
. . V *
--------+ +===========================+ * +-----------------------+
| | new/config get/set | * | |
| | |...*.....| init |
| |---------------------------| * | |
init | | |**** | |
start |********| event handler |*********| DHCP |
stop | | | | |
| |---------------------------| | |
| | | | NETIF |
+-----| | | +-----------------+ |
| glue|----<---| esp_netif_transmit |--<------| netif_output | |
| | | | | | |
| |---->---| esp_netif_receive |-->------| netif_input | |
| | | | + ----------------+ |
| |....<...| esp_netif_free_rx_buffer |...<.....| packet buffer |
+-----| | | | |
| | | | |
(B) | | | +-----------------------+
--------+ +===========================+
communication NETWORK STACK
DRIVER ESP-NETIF
. . | *
--------+ +===========================+ * +-----------------------+
| | new/config get/set | * | |
| | |...*.......| init |
| |---------------------------| * | |
init | | |**** | |
start |********| event handler |***********| DHCP |
stop | | | | |
| |---------------------------| | |
| | | | NETIF |
+-----| | | +-----------------+ |
| glue|----<---| esp_netif_transmit |--<--------| netif_output | |
| | | | | | | |
| |---->---| esp_netif_receive |-->--|-----| netif_input | |
| | | | | | + ----------------+ |
| |....<...| esp_netif_free_rx_buffer |...<.|..|..| packet buffer |
+-----| | | | | | |
| | | | | | (D) |
(B) | | (C) | | | +-----------------------+
--------+ +===========================+ | |
communication | | NETWORK STACK
DRIVER ESP-NETIF | |
| |
+-----------------------+ | |
| | | |
| l2tap_write |-------| |
| | |
| l2tap_eth_filter |----------|
| |
| (E) |
+-----------------------+
ESP-NETIF L2 TAP
## Data/event flow:
* `........` Initialization line from user code to esp-netif and comm driver
* `--<--->--` Data packets going from communication media to TCP/IP stack and back
* `********` Events agregated in ESP-NETIP propagates to driver, user code and network stack
* `|` User settings and runtime configuration
## Components:
### A) User code, boiler plate
@ -68,14 +88,20 @@ Overall application interaction with communication media and network stack
### D) Network stack: no public interaction with user code (wrtt interfaces)
### E) 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 that only Ethernet (IEEE 802.3)
is currently supported.
## Data/event flow:
* `........` Initialization line from user code to esp-netif and comm driver
* `--<--->--` Data packets going from communication media to TCP/IP stack and back
* `********` Events agregated in ESP-NETIP propagates to driver, user code and network stack
* `|` User settings and runtime configuration
From user perspective, 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).
There is only one ESP-NETIF L2 TAP interface device (path name) available. However multiple file descriptors with different configuration
can be opened at a time since the ESP-NETIF L2 TAP interface can be understood as generic entry point to the NETIF internal structure.
Important is then specific configuration of particular file descriptor. It can be configured to give an access to specific Network Interface
identified by ``if_key`` (e.g. `ETH_DEF`) and to filter only specific frames based on their type (e.g. Ethernet type in case of IEEE 802.3).
Filtering only specific frames is crucial since the ESP-NETIF L2 TAP needs to work along with IP stack and so the IP related traffic
(IP, ARP, etc.) should not be passed directly to the user application. Even though such option is still configurable, it is not recommended in
standard use cases. Filtering is also advantageous from a perspective the users application gets access only to frame types it is interested
in and the remaining traffic is either passed to other L2 TAP file descriptors or to IP stack.

Wyświetl plik

@ -1,16 +1,8 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_NETIF_H_
#define _ESP_NETIF_H_
@ -160,6 +152,88 @@ esp_err_t esp_netif_attach(esp_netif_t *esp_netif, esp_netif_iodriver_handle dri
*/
esp_err_t esp_netif_receive(esp_netif_t *esp_netif, void *buffer, size_t len, void *eb);
/**
* @}
*/
/**
* @defgroup ESP_NETIF_L2_TAP_CTRL ESP-NETIF L2 TAP Control API
* @brief Functions to control access to ESP-NETIF Data link layer
*/
/** @addtogroup ESP_NETIF_L2_TAP_CTRL
* @{
*/
/**
* @brief Add transmit hook callback function reference into ESP-NETIF. This callback function
* is then called just prior the ESP-NETIF passes data to network driver.
*
* @param[in] esp_netif Handle to esp-netif instance
* @param[in] hook_fn reference to transmit hook call-back function
* @return
* - ESP_OK - success
* - ESP_ERR_INVALID_ARG
*/
esp_err_t esp_netif_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn);
/**
* @brief Add post transmit hook callback function reference into ESP-NETIF. This callback function
* is then called just after the ESP-NETIF passes data to network driver.
*
* @note Intention of this function is either to release resources allocated by transmit hook function
* or for other use cases such as time stamping, etc.
*
* @param[in] esp_netif Handle to esp-netif instance
* @param[in] hook_fn reference to post transmit hook call-back function
* @return
* - ESP_OK - success
* - ESP_ERR_INVALID_ARG
*/
esp_err_t esp_netif_post_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn);
/**
* @brief Add receive hook callback function reference into ESP-NETIF. This callback function
* is then called when network driver receives data.
*
* @param[in] esp_netif Handle to esp-netif instance
* @param[in] hook_fn reference to receive hook callback function
* @return
* - ESP_OK - success
* - ESP_ERR_INVALID_ARG
*/
esp_err_t esp_netif_recv_hook_attach(esp_netif_t *esp_netif, void *hook_fn);
/**
* @brief Removes reference to previously attachhed transmit hook callback function
*
* @param[in] esp_netif Handle to esp-netif instance
* @return
* - ESP_OK - success
* - ESP_ERR_INVALID_ARG
*/
esp_err_t esp_netif_transmit_hook_detach(esp_netif_t *esp_netif);
/**
* @brief Removes reference to previously attachhed posttransmit hook callback function
*
* @param[in] esp_netif Handle to esp-netif instance
* @return
* - ESP_OK - success
* - ESP_ERR_INVALID_ARG
*/
esp_err_t esp_netif_post_transmit_hook_detach(esp_netif_t *esp_netif);
/**
* @brief Removes reference to previously attachhed receive hook callback function
*
* @param[in] esp_netif Handle to esp-netif instance
* @return
* - ESP_OK - success
* - ESP_ERR_INVALID_ARG
*/
esp_err_t esp_netif_recv_hook_detach(esp_netif_t *esp_netif);
/**
* @}
*/

Wyświetl plik

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_netif.h"
#include "esp_err.h"
#define L2TAP_VFS_DEFAULT_PATH "/dev/net/tap"
#define L2TAP_VFS_CONFIG_DEFAULT() \
{ \
.base_path = L2TAP_VFS_DEFAULT_PATH, \
}
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
const char* base_path;
} l2tap_vfs_config_t;
typedef enum {
L2TAP_S_RCV_FILTER,
L2TAP_G_RCV_FILTER,
L2TAP_S_INTF_DEVICE,
L2TAP_G_INTF_DEVICE,
} l2tap_ioctl_opt_t;
/**
* @brief Add L2 TAP virtual filesystem driver
*
* This function must be called prior usage of ESP-NETIF L2 TAP Interface
*
* @param config L2 TAP virtual filesystem driver configuration. Default base path /dev/net/tap is used when this paramenter is NULL.
* @return esp_err_t
* - ESP_OK on success
*/
esp_err_t esp_vfs_l2tap_intf_register(l2tap_vfs_config_t *config);
/**
* @brief Removes L2 TAP virtual filesystem driver
*
* @param base_path Base path to the L2 TAP virtual filesystem driver. Default path /dev/net/tap is used when this paramenter is NULL.
* @return esp_err_t
* - ESP_OK on success
*/
esp_err_t esp_vfs_l2tap_intf_unregister(const char *base_path);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -77,6 +77,13 @@ do {
action; \
} while(0)
#if CONFIG_ESP_NETIF_L2_TAP
/**
* @brief Transmit timeout when multiple accesses to network driver
*/
#define ESP_NETIF_TX_TIMEOUT 250
#endif
//
// Internal types
//
@ -473,6 +480,15 @@ esp_netif_t *esp_netif_new(const esp_netif_config_t *esp_netif_config)
return NULL;
}
#if CONFIG_ESP_NETIF_L2_TAP
esp_netif->transmit_mutex = xSemaphoreCreateMutex();
if (!esp_netif->transmit_mutex) {
ESP_LOGE(TAG, "Failed to create L2 TAP transmit mutex");
free(esp_netif);
return NULL;
}
#endif // CONFIG_ESP_NETIF_L2_TAP
// Create ip info
esp_netif_ip_info_t *ip_info = calloc(1, sizeof(esp_netif_ip_info_t));
if (!ip_info) {
@ -585,6 +601,9 @@ void esp_netif_destroy(esp_netif_t *esp_netif)
esp_netif_destroy_related(esp_netif);
free(esp_netif->lwip_netif);
free(esp_netif->hostname);
#if CONFIG_ESP_NETIF_L2_TAP
vSemaphoreDelete(esp_netif->transmit_mutex);
#endif // CONFIG_ESP_NETIF_L2_TAP
if (s_last_default_esp_netif == esp_netif) {
// clear last default netif if it happens to be this just destroyed interface
s_last_default_esp_netif = NULL;
@ -658,6 +677,92 @@ esp_err_t esp_netif_get_mac(esp_netif_t *esp_netif, uint8_t mac[])
return ESP_OK;
}
#if CONFIG_ESP_NETIF_L2_TAP
esp_err_t esp_netif_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn)
{
esp_err_t ret = ESP_FAIL;
if (esp_netif != NULL) {
if (esp_netif->transmit_hook == NULL) { // if hook function is not assigned
esp_netif->transmit_hook = hook_fn;
ret = ESP_OK;
} else if (esp_netif->transmit_hook == hook_fn) { // indicate OK when trying to assign the same hook function
ret = ESP_OK;
}
} else {
ret = ESP_ERR_INVALID_ARG;
}
return ret;
}
esp_err_t esp_netif_post_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn)
{
esp_err_t ret = ESP_FAIL;
if (esp_netif != NULL) {
if (esp_netif->post_transmit_hook == NULL) { // if hook function is not assigned
esp_netif->post_transmit_hook = hook_fn;
ret = ESP_OK;
} else if (esp_netif->post_transmit_hook == hook_fn) { // indicate OK when trying to assign the same hook function
ret = ESP_OK;
}
} else {
ret = ESP_ERR_INVALID_ARG;
}
return ret;
}
esp_err_t esp_netif_recv_hook_attach(esp_netif_t *esp_netif, void *hook_fn)
{
esp_err_t ret = ESP_FAIL;
if (esp_netif != NULL) {
if (esp_netif->receive_hook == NULL) { // if hook function is not assigned
esp_netif->receive_hook = hook_fn;
ret = ESP_OK;
} else if (esp_netif->receive_hook == hook_fn) { // indicate OK when trying to assign the same hook function
ret = ESP_OK;
}
} else {
ret = ESP_ERR_INVALID_ARG;
}
return ret;
}
esp_err_t esp_netif_transmit_hook_detach(esp_netif_t *esp_netif)
{
if (esp_netif != NULL) {
esp_netif->transmit_hook = NULL;
return ESP_OK;
}
return ESP_ERR_INVALID_ARG;
}
esp_err_t esp_netif_post_transmit_hook_detach(esp_netif_t *esp_netif)
{
if (esp_netif != NULL) {
esp_netif->post_transmit_hook = NULL;
return ESP_OK;
}
return ESP_ERR_INVALID_ARG;
}
esp_err_t esp_netif_recv_hook_detach(esp_netif_t *esp_netif)
{
if (esp_netif != NULL) {
esp_netif->receive_hook = NULL;
return ESP_OK;
}
return ESP_ERR_INVALID_ARG;
}
#endif // CONFIG_ESP_NETIF_L2_TAP
#if ESP_DHCPS
static void esp_netif_dhcps_cb(u8_t client_ip[4])
{
@ -823,7 +928,7 @@ static esp_err_t esp_netif_stop_api(esp_netif_api_msg_t *msg)
netif_set_down(lwip_netif);
esp_netif_lwip_remove(esp_netif);
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);;
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);
return ESP_OK;
}
@ -835,7 +940,7 @@ esp_err_t esp_netif_stop(esp_netif_t *esp_netif)
// No need to stop PPP interface in lwip thread
esp_err_t ret = esp_netif_stop_ppp(esp_netif->related_data);
if (ret == ESP_OK) {
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);;
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);
}
return ret;
#endif
@ -844,7 +949,7 @@ esp_err_t esp_netif_stop(esp_netif_t *esp_netif)
// No need to stop SLIP interface in lwip thread
esp_err_t ret = esp_netif_stop_slip(esp_netif);
if (ret == ESP_OK) {
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);;
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);
}
return ret;
#endif
@ -873,7 +978,28 @@ void esp_netif_free_rx_buffer(void *h, void* buffer)
esp_err_t esp_netif_transmit(esp_netif_t *esp_netif, void* data, size_t len)
{
return (esp_netif->driver_transmit)(esp_netif->driver_handle, data, len);
esp_err_t ret;
#if CONFIG_ESP_NETIF_L2_TAP
if (xSemaphoreTake(esp_netif->transmit_mutex, pdMS_TO_TICKS(ESP_NETIF_TX_TIMEOUT)) == pdFALSE) {
return ESP_FAIL;
}
if (esp_netif->transmit_hook != NULL) {
void *p_data = data;
esp_netif->transmit_hook(esp_netif, &p_data, &len);
ret = (esp_netif->driver_transmit)(esp_netif->driver_handle, p_data, len);
if (esp_netif->post_transmit_hook != NULL) {
esp_netif->post_transmit_hook(esp_netif, p_data, len);
}
xSemaphoreGive(esp_netif->transmit_mutex);
return ret;
}
#endif // CONFIG_ESP_NETIF_L2_TAP
ret = (esp_netif->driver_transmit)(esp_netif->driver_handle, data, len);
#if CONFIG_ESP_NETIF_L2_TAP
xSemaphoreGive(esp_netif->transmit_mutex);
#endif // CONFIG_ESP_NETIF_L2_TAP
return ret;
}
esp_err_t esp_netif_transmit_wrap(esp_netif_t *esp_netif, void *data, size_t len, void *pbuf)
@ -883,7 +1009,21 @@ esp_err_t esp_netif_transmit_wrap(esp_netif_t *esp_netif, void *data, size_t len
esp_err_t esp_netif_receive(esp_netif_t *esp_netif, void *buffer, size_t len, void *eb)
{
esp_netif->lwip_input_fn(esp_netif->netif_handle, buffer, len, eb);
#if CONFIG_ESP_NETIF_L2_TAP
if (esp_netif->receive_hook != NULL) {
esp_netif->receive_hook(esp_netif, buffer, &len);
// The receive_hook function may alter original frame (then keeps the len > 0) or it may take full control over the frame and sets len to 0 to
// not be populated further
if (len > 0) {
esp_netif->lwip_input_fn(esp_netif->netif_handle, buffer, len, eb);
}
} else {
#endif // CONFIG_ESP_NETIF_L2_TAP
esp_netif->lwip_input_fn(esp_netif->netif_handle, buffer, len, eb);
#if CONFIG_ESP_NETIF_L2_TAP
}
#endif // CONFIG_ESP_NETIF_L2_TAP
return ESP_OK;
}

Wyświetl plik

@ -1,16 +1,8 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -115,6 +107,14 @@ struct esp_netif_obj {
esp_err_t (*driver_transmit)(void *h, void *buffer, size_t len);
esp_err_t (*driver_transmit_wrap)(void *h, void *buffer, size_t len, void *pbuf);
void (*driver_free_rx_buffer)(void *h, void* buffer);
#if CONFIG_ESP_NETIF_L2_TAP
SemaphoreHandle_t transmit_mutex;
// L2 manipulation hooks
esp_err_t (*transmit_hook)(void *h, void **buffer, size_t *len);
void (*post_transmit_hook)(void *h, void *buffer, size_t len);
esp_err_t (*receive_hook)(void *h, void *buffer, size_t *len);
#endif
// dhcp related
esp_netif_dhcp_status_t dhcpc_status;

Wyświetl plik

@ -0,0 +1,645 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdatomic.h>
#include <sys/fcntl.h>
#include <sys/param.h>
#include <sys/queue.h>
#include "arpa/inet.h" // for ntohs, etc.
#include "errno.h"
#include "esp_vfs_l2tap.h"
#include "lwip/prot/ethernet.h" // Ethernet headers
#include "esp_vfs.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_netif_net_stack.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#if CONFIG_ESP_NETIF_L2_TAP
#define INVALID_FD (-1)
#define L2TAP_MAX_FDS CONFIG_ESP_NETIF_L2_TAP_MAX_FDS
#define RX_QUEUE_MAX_SIZE CONFIG_ESP_NETIF_L2_TAP_RX_QUEUE_SIZE
typedef enum {
L2TAP_SOCK_STATE_READY,
L2TAP_SOCK_STATE_OPENED,
L2TAP_SOCK_STATE_CLOSING
} l2tap_socket_state_t;
typedef struct {
_Atomic l2tap_socket_state_t state;
bool non_blocking;
esp_netif_t *netif;
uint16_t ethtype_filter;
QueueHandle_t rx_queue;
SemaphoreHandle_t close_done_sem;
} l2tap_context_t;
typedef struct {
void *buff;
size_t len;
} frame_queue_entry_t;
typedef struct {
esp_vfs_select_sem_t select_sem;
fd_set *readfds;
fd_set *writefds;
fd_set *errorfds;
fd_set readfds_orig;
fd_set writefds_orig;
fd_set errorfds_orig;
} l2tap_select_args_t;
typedef enum {
L2TAP_SELECT_READ_NOTIF,
L2TAP_SELECT_WRITE_NOTIF,
L2TAP_SELECT_ERR_NOTIF
} l2tap_select_notif_e;
static l2tap_context_t s_l2tap_sockets[L2TAP_MAX_FDS] = {0};
static bool s_is_registered = false;
static portMUX_TYPE s_critical_section_lock = portMUX_INITIALIZER_UNLOCKED;
static l2tap_select_args_t **s_registered_selects = NULL;
static int32_t s_registered_select_cnt = 0;
static const char *TAG = "vfs_l2tap";
static void l2tap_select_notify(int fd, l2tap_select_notif_e select_notif);
/* ================== Utils ====================== */
static esp_err_t init_rx_queue(l2tap_context_t *l2tap_socket)
{
l2tap_socket->rx_queue = xQueueCreate(RX_QUEUE_MAX_SIZE, sizeof(frame_queue_entry_t));
ESP_RETURN_ON_FALSE(l2tap_socket->rx_queue, ESP_ERR_NO_MEM, TAG, "create work queue failed");
return ESP_OK;
}
static esp_err_t push_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t len)
{
frame_queue_entry_t frame_info;
frame_info.buff = buff;
frame_info.len = len;
// try send to queue and check if the queue is full
if (xQueueSend(l2tap_socket->rx_queue, &frame_info, 0) != pdTRUE) {
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
static ssize_t pop_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t len)
{
TickType_t timeout = portMAX_DELAY;
if (l2tap_socket->non_blocking) {
timeout = 0;
}
frame_queue_entry_t frame_info;
if (xQueueReceive(l2tap_socket->rx_queue, &frame_info, timeout) == pdTRUE) {
// empty queue was issued indicating the fd is going to be closed
if (frame_info.len == 0) {
// indicate to "clean_task" that task waiting for queue was unblocked
push_rx_queue(l2tap_socket, NULL, 0);
goto err;
}
if (len > frame_info.len) {
len = frame_info.len;
}
memcpy(buff, frame_info.buff, len);
free(frame_info.buff);
} else {
goto err;
}
return len;
err:
return -1;
}
static bool rx_queue_empty(l2tap_context_t *l2tap_socket)
{
return (uxQueueMessagesWaiting(l2tap_socket->rx_queue) == 0);
}
static void flush_rx_queue(l2tap_context_t *l2tap_socket)
{
frame_queue_entry_t frame_info;
while (xQueueReceive(l2tap_socket->rx_queue, &frame_info, 0) == pdTRUE) {
if (frame_info.len > 0) {
free(frame_info.buff);
}
}
}
static void delete_rx_queue(l2tap_context_t *l2tap_socket)
{
vQueueDelete(l2tap_socket->rx_queue);
l2tap_socket->rx_queue = NULL;
}
static inline void l2tap_lock(void)
{
portENTER_CRITICAL(&s_critical_section_lock);
}
static inline void l2tap_unlock(void)
{
portEXIT_CRITICAL(&s_critical_section_lock);
}
/* ================== ESP NETIF intf ====================== */
esp_err_t l2tap_eth_filter(esp_netif_t *esp_netif, void *buff, size_t *size)
{
struct eth_hdr *eth_header = buff;
uint16_t eth_type = ntohs(eth_header->type);
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
if (atomic_load(&s_l2tap_sockets[i].state) == L2TAP_SOCK_STATE_OPENED) {
l2tap_lock(); // read of socket config needs to be atomic since it can be manipulated from other task
if (s_l2tap_sockets[i].netif == esp_netif && (s_l2tap_sockets[i].ethtype_filter == eth_type ||
// IEEE 802.2 Frame is identified based on its length which is less than IEEE802.3 max length (Ethernet II Types IDs start over this value)
// Note that IEEE 802.2 LLC resolution is expected to be performed by upper stream app
(s_l2tap_sockets[i].ethtype_filter <= ETH_IEEE802_3_MAX_LEN && eth_type <= ETH_IEEE802_3_MAX_LEN))) {
l2tap_unlock();
if (push_rx_queue(&s_l2tap_sockets[i], buff, *size) != ESP_OK) {
// just tail drop when queue is full
free(buff);
ESP_LOGD(TAG, "fd %d rx queue is full", i);
}
l2tap_lock();
if (s_registered_select_cnt) {
l2tap_select_notify(i, L2TAP_SELECT_READ_NOTIF);
}
l2tap_unlock();
*size = 0; // the frame is not passed to IP stack when size set to 0
goto end;
} else {
l2tap_unlock();
}
}
}
end:
return ESP_OK;
}
/* ====================== vfs ====================== */
static int l2tap_open(const char *path, int flags, int mode)
{
int fd;
// Find free fd and initialize
for (fd = 0; fd < L2TAP_MAX_FDS; fd++) {
l2tap_socket_state_t exp_state = L2TAP_SOCK_STATE_READY;
if (atomic_compare_exchange_strong(&s_l2tap_sockets[fd].state, &exp_state,
L2TAP_SOCK_STATE_OPENED)) {
if (init_rx_queue(&s_l2tap_sockets[fd]) != ESP_OK) {
atomic_store(&s_l2tap_sockets[fd].state, L2TAP_SOCK_STATE_READY);
goto err;
}
s_l2tap_sockets[fd].ethtype_filter = 0x0;
s_l2tap_sockets[fd].netif = NULL;
s_l2tap_sockets[fd].non_blocking = ((flags & O_NONBLOCK) == O_NONBLOCK);
return fd;
}
}
err:
return INVALID_FD;
}
static ssize_t l2tap_write(int fd, const void *data, size_t size)
{
ssize_t ret = -1;
if (size == 0) {
return 0;
}
if (atomic_load(&s_l2tap_sockets[fd].state) == L2TAP_SOCK_STATE_OPENED) {
if (s_l2tap_sockets[fd].ethtype_filter > ETH_IEEE802_3_MAX_LEN &&
((struct eth_hdr *)data)->type != htons(s_l2tap_sockets[fd].ethtype_filter)) {
// bad message
errno = EBADMSG;
goto err;
}
if (esp_netif_transmit(s_l2tap_sockets[fd].netif, (void *)data, size) == ESP_OK) {
ret = size;
} else {
// I/O error
errno = EIO;
}
} else {
// bad file desc
errno = EBADF;
}
err:
return ret;
}
static ssize_t l2tap_read(int fd, void *data, size_t size)
{
// fd might be in process of closing (close was already called but preempted)
if (atomic_load(&s_l2tap_sockets[fd].state) != L2TAP_SOCK_STATE_OPENED) {
// bad file desc
errno = EBADF;
return -1;
}
if (size == 0) {
return 0;
}
ssize_t actual_size = -1;
if ((actual_size = pop_rx_queue(&s_l2tap_sockets[fd], data, size)) < 0) {
errno = EAGAIN;
}
return actual_size;
}
// This function checks if any other socket is associated with inputting ESP netif. If there is no other socket associated with that netif, Raw Ethernet
// filter function is detached so the netif hook could be used by other components.
static esp_err_t l2tap_eth_filter_fn_detach(l2tap_context_t *l2tap_socket)
{
esp_netif_t *netif = l2tap_socket->netif;
if (netif == NULL) {
return ESP_FAIL;
}
l2tap_socket->netif = NULL;
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
if (s_l2tap_sockets[i].netif == netif) {
// other socket is associated to the same ESP netif, keep the filter function attached
return ESP_OK;
}
}
// no socket is associated to the ESP netif, we are free to detach the filter function
return esp_netif_recv_hook_detach(netif);
}
static esp_err_t l2tap_eth_filter_fn_attach(l2tap_context_t *l2tap_socket, esp_netif_t *new_netif)
{
if (new_netif == NULL) {
return ESP_ERR_INVALID_ARG;
}
// check if filter function can be detached from the old netif
l2tap_eth_filter_fn_detach(l2tap_socket);
l2tap_socket->netif = new_netif;
// Try to attach filter function to newly selected netif
if (esp_netif_recv_hook_attach(l2tap_socket->netif, &l2tap_eth_filter) != ESP_OK) {
l2tap_socket->netif = NULL;
return ESP_FAIL;
}
return ESP_OK;
}
void l2tap_clean_task(void *task_param)
{
l2tap_context_t *l2tap_socket = (l2tap_context_t *)task_param;
// push empty queue to unblock possibly blocking task
push_rx_queue(l2tap_socket, NULL, 0);
// wait for the indication that blocking task was executed (unblocked)
pop_rx_queue(l2tap_socket, NULL, 0);
// now, all higher priority tasks should finished their execution and new accesses to the queue were prevended
// by L2TAP_SOCK_STATE_CLOSING => we are free to free queue resources
flush_rx_queue(l2tap_socket);
delete_rx_queue(l2tap_socket);
// check if filter function can be detached & detach
// lock, we don't want to other tasks modify other sockets context info
l2tap_lock();
l2tap_eth_filter_fn_detach(l2tap_socket);
l2tap_unlock();
// unblock task which originally called close
xSemaphoreGive(l2tap_socket->close_done_sem);
// all done, delete itsefl
vTaskDelete(NULL);
}
static int l2tap_close(int fd)
{
if (atomic_load(&s_l2tap_sockets[fd].state) != L2TAP_SOCK_STATE_OPENED) {
// not valid opened fd
errno = EBADF;
return -1;
}
// prevent any further manipulations with the socket (already started will be finished though)
atomic_store(&s_l2tap_sockets[fd].state, L2TAP_SOCK_STATE_CLOSING);
if ((s_l2tap_sockets[fd].close_done_sem = xSemaphoreCreateBinary()) == NULL) {
ESP_LOGE(TAG, "create close_done_sem failed");
return -1;
}
// If one task is blocked in I/O operation and another task tries to close the fd, the first task is
// unblocked by pushing empty queue in low priority task (to ensure context switch to the first task).
// The first's task read operation then ends with error and the low priority task frees the queue resources.
if (xTaskCreate(l2tap_clean_task, "l2tap_clean_task", 1024, &s_l2tap_sockets[fd], tskIDLE_PRIORITY, NULL) == pdFAIL) {
ESP_LOGE(TAG, "create l2tap_clean_task failed");
return -1;
}
// wait for the low priority close task & then delete the semaphore
xSemaphoreTake(s_l2tap_sockets[fd].close_done_sem, portMAX_DELAY);
vSemaphoreDelete(s_l2tap_sockets[fd].close_done_sem); // no worries to delete, this task owns the semaphore
// indicate that socket is ready to be used again
atomic_store(&s_l2tap_sockets[fd].state, L2TAP_SOCK_STATE_READY);
return 0;
}
static int l2tap_ioctl(int fd, int cmd, va_list args)
{
switch (cmd) {
case L2TAP_S_RCV_FILTER: ;
uint16_t *new_ethtype_filter = va_arg(args, uint16_t *);
l2tap_lock();
// do nothing when same filter is to be set
if (s_l2tap_sockets[fd].ethtype_filter != *new_ethtype_filter) {
// check if the ethtype filter is not already used by other socket
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
if (atomic_load(&s_l2tap_sockets[i].state) == L2TAP_SOCK_STATE_OPENED &&
s_l2tap_sockets[i].ethtype_filter == *new_ethtype_filter) {
// invalid argument
errno = EINVAL;
l2tap_unlock();
goto err;
}
}
s_l2tap_sockets[fd].ethtype_filter = *new_ethtype_filter;
}
l2tap_unlock();
break;
case L2TAP_G_RCV_FILTER: ;
uint16_t *ethtype_filter_dest = va_arg(args, uint16_t *);
*ethtype_filter_dest = s_l2tap_sockets[fd].ethtype_filter;
break;
case L2TAP_S_INTF_DEVICE: ;
const char *str = va_arg(args, const char *);
esp_netif_t *new_netif = esp_netif_get_handle_from_ifkey(str);
if (new_netif == NULL) {
// No such device
errno = ENODEV;
goto err;
}
l2tap_lock();
if (l2tap_eth_filter_fn_attach(&s_l2tap_sockets[fd], new_netif) != ESP_OK) {
// No space on device - netif hook is already taken
errno = ENOSPC;
l2tap_unlock();
goto err;
}
l2tap_unlock();
break;
case L2TAP_G_INTF_DEVICE: ;
const char **str_p = va_arg(args, const char **);
if (s_l2tap_sockets[fd].netif != NULL) {
*str_p = esp_netif_get_ifkey(s_l2tap_sockets[fd].netif);
} else {
*str_p = NULL;
}
break;
default:
// unsupported operation
errno = ENOSYS;
goto err;
break;
}
va_end(args);
return 0;
err:
va_end(args);
return -1;
}
static int l2tap_fcntl(int fd, int cmd, int arg)
{
int result = 0;
if (cmd == F_GETFL) {
if (s_l2tap_sockets[fd].non_blocking) {
result |= O_NONBLOCK;
}
} else if (cmd == F_SETFL) {
s_l2tap_sockets[fd].non_blocking = (arg & O_NONBLOCK) != 0;
} else {
// unsupported operation
result = -1;
errno = ENOSYS;
}
return result;
}
#ifdef CONFIG_VFS_SUPPORT_SELECT
static esp_err_t register_select(l2tap_select_args_t *args)
{
esp_err_t ret = ESP_ERR_INVALID_ARG;
if (args) {
const int new_size = s_registered_select_cnt + 1;
l2tap_select_args_t **registered_selects_new;
if ((registered_selects_new = realloc(s_registered_selects, new_size * sizeof(l2tap_select_args_t *))) == NULL) {
ret = ESP_ERR_NO_MEM;
} else {
s_registered_selects = registered_selects_new;
s_registered_selects[s_registered_select_cnt] = args;
s_registered_select_cnt = new_size;
ret = ESP_OK;
}
}
return ret;
}
static esp_err_t unregister_select(l2tap_select_args_t *args)
{
esp_err_t ret = ESP_OK;
if (args) {
ret = ESP_ERR_INVALID_STATE;
for (int i = 0; i < s_registered_select_cnt; ++i) {
if (s_registered_selects[i] == args) {
const int new_size = s_registered_select_cnt - 1;
// The item is removed by overwriting it with the last item. The subsequent rellocation will drop the
// last item.
s_registered_selects[i] = s_registered_selects[new_size];
s_registered_selects = realloc(s_registered_selects, new_size * sizeof(l2tap_select_args_t *));
if (s_registered_selects || new_size == 0) {
s_registered_select_cnt = new_size;
ret = ESP_OK;
} else {
ret = ESP_ERR_NO_MEM;
}
break;
}
}
}
return ret;
}
static void l2tap_select_notify(int fd, l2tap_select_notif_e select_notif)
{
for (int i = 0; i < s_registered_select_cnt; i++) {
l2tap_select_args_t *args = s_registered_selects[i];
if (args) {
switch (select_notif) {
case L2TAP_SELECT_READ_NOTIF:
if (FD_ISSET(fd, &args->readfds_orig)) {
FD_SET(fd, args->readfds);
esp_vfs_select_triggered(args->select_sem);
}
break;
case L2TAP_SELECT_WRITE_NOTIF:
if (FD_ISSET(fd, &args->writefds_orig)) {
FD_SET(fd, args->writefds);
esp_vfs_select_triggered(args->select_sem);
}
break;
case L2TAP_SELECT_ERR_NOTIF:
if (FD_ISSET(fd, &args->errorfds_orig)) {
FD_SET(fd, args->errorfds);
esp_vfs_select_triggered(args->select_sem);
}
break;
}
}
}
}
static esp_err_t l2tap_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
esp_vfs_select_sem_t select_sem, void **end_select_args)
{
const int max_fds = MIN(nfds, L2TAP_MAX_FDS);
*end_select_args = NULL;
l2tap_select_args_t *args = malloc(sizeof(l2tap_select_args_t));
if (args == NULL) {
return ESP_ERR_NO_MEM;
}
args->select_sem = select_sem;
args->readfds = readfds;
args->writefds = writefds;
args->errorfds = exceptfds;
args->readfds_orig = *readfds; // store the original values because they will be set to zero
args->writefds_orig = *writefds;
args->errorfds_orig = *exceptfds;
FD_ZERO(readfds);
FD_ZERO(writefds);
FD_ZERO(exceptfds);
l2tap_lock();
for (int i = 0; i < max_fds; i++) {
if (FD_ISSET(i, &args->readfds_orig)) {
if (!rx_queue_empty(&s_l2tap_sockets[i])) {
// signalize immediately when data is buffered
FD_SET(i, readfds);
esp_vfs_select_triggered(args->select_sem);
}
}
}
esp_err_t ret = register_select(args);
if (ret != ESP_OK) {
l2tap_unlock();
free(args);
return ret;
}
l2tap_unlock();
*end_select_args = args;
return ESP_OK;
}
static esp_err_t l2tap_end_select(void *end_select_args)
{
l2tap_select_args_t *args = end_select_args;
if (args == NULL) {
return ESP_OK;
}
l2tap_lock();
esp_err_t ret = unregister_select(args);
l2tap_unlock();
if (args) {
free(args);
}
return ret;
}
#endif //CONFIG_VFS_SUPPORT_SELECT
esp_err_t esp_vfs_l2tap_intf_register(l2tap_vfs_config_t *config)
{
l2tap_vfs_config_t def_config = L2TAP_VFS_CONFIG_DEFAULT();
if (config == NULL) {
ESP_LOGD(TAG, "vfs is to be registered with default settings");
config = &def_config;
}
ESP_RETURN_ON_FALSE(!s_is_registered, ESP_ERR_INVALID_STATE, TAG, "vfs is already registered");
s_is_registered = true;
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.write = &l2tap_write,
.open = &l2tap_open,
.close = &l2tap_close,
.read = &l2tap_read,
.fcntl = &l2tap_fcntl,
.ioctl = &l2tap_ioctl,
#ifdef CONFIG_VFS_SUPPORT_SELECT
.start_select = &l2tap_start_select,
.end_select = &l2tap_end_select,
#endif // CONFIG_VFS_SUPPORT_SELECT
};
ESP_RETURN_ON_ERROR(esp_vfs_register(config->base_path, &vfs, NULL), TAG, "vfs register error");
return ESP_OK;
}
esp_err_t esp_vfs_l2tap_intf_unregister(const char *base_path)
{
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
ESP_RETURN_ON_FALSE(atomic_load(&s_l2tap_sockets[i].state) == L2TAP_SOCK_STATE_READY,
ESP_ERR_INVALID_STATE, TAG, "all FDs need to be closed");
}
if (base_path == NULL) {
ESP_RETURN_ON_ERROR(esp_vfs_unregister(L2TAP_VFS_DEFAULT_PATH), TAG, "vfs un-register error");
} else {
ESP_RETURN_ON_ERROR(esp_vfs_unregister(base_path), TAG, "vfs un-register error");
}
s_is_registered = false;
return ESP_OK;
}
#endif // CONFIG_ESP_NETIF_L2_TAP

Wyświetl plik

@ -1,23 +1,9 @@
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
/**
* @brief Ethernet frame CRC length
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#define ETH_CRC_LEN (4)
#pragma once
/**
* @brief Ethernet interface

Wyświetl plik

@ -12,41 +12,52 @@ Some ESP-NETIF API functions are intended to be called by application code, for
In many cases, applications do not need to call ESP-NETIF APIs directly as they are called from the default network event handlers.
ESP-NETIF component is a successor of the tcpip_adapter, former network interface abstraction, which has become deprecated since IDF v4.1.
Please refer to the :doc:`/api-reference/network/tcpip_adapter_migration` section in case existing applications to be ported to use the esp-netif API instead.
ESP-NETIF component is a successor of the tcpip_adapter, former network interface abstraction, which has become deprecated since IDF v4.1. Please refer to the :doc:`/api-reference/network/tcpip_adapter_migration` section in case existing applications to be ported to use the esp-netif API instead.
ESP-NETIF architecture
----------------------
.. code-block:: text
| (A) USER CODE |
| |
.............| init settings events |
. +----------------------------------------+
. . | *
. . | *
--------+ +===========================+ * +-----------------------+
| | new/config get/set | * | |
| | |...*.....| init |
| |---------------------------| * | |
init | | |**** | |
start |********| event handler |*********| DHCP |
stop | | | | |
| |---------------------------| | |
| | | | NETIF |
+-----| | | +-----------------+ |
| glue|----<---| esp_netif_transmit |--<------| netif_output | |
| | | | | | |
| |---->---| esp_netif_receive |-->------| netif_input | |
| | | | + ----------------+ |
| |....<...| esp_netif_free_rx_buffer |...<.....| packet buffer |
+-----| | | | |
| | | | (D) |
(B) | | (C) | +-----------------------+
--------+ +===========================+
communication NETWORK STACK
DRIVER ESP-NETIF
--------+ +===========================+ * +-----------------------+
| | new/config get/set | * | |
| | |...*.......| init |
| |---------------------------| * | |
init | | |**** | |
start |********| event handler |***********| DHCP |
stop | | | | |
| |---------------------------| | |
| | | | NETIF |
+-----| | | +-----------------+ |
| glue|----<---| esp_netif_transmit |--<--------| netif_output | |
| | | | | | | |
| |---->---| esp_netif_receive |-->--|-----| netif_input | |
| | | | | | + ----------------+ |
| |....<...| esp_netif_free_rx_buffer |...<.|..|..| packet buffer |
+-----| | | | | | |
| | | | | | (D) |
(B) | | (C) | | | +-----------------------+
--------+ +===========================+ | |
communication | | NETWORK STACK
DRIVER ESP-NETIF | |
| |
+-----------------------+ | |
| | | |
| l2tap_write |-------| |
| | |
| l2tap_eth_filter |----------|
| |
| (E) |
+-----------------------+
ESP-NETIF L2 TAP
Data and event flow in the diagram
@ -66,8 +77,7 @@ ESP-NETIF interaction
A) User code, boiler plate
^^^^^^^^^^^^^^^^^^^^^^^^^^
Overall application interaction with a specific IO driver for communication media and configured TCP/IP network stack
is abstracted using ESP-NETIF APIs and outlined as below:
Overall application interaction with a specific IO driver for communication media and configured TCP/IP network stack is abstracted using ESP-NETIF APIs and outlined as below:
A) Initialization code
@ -107,10 +117,7 @@ Communication driver plays these two important roles in relation with ESP-NETIF:
C) ESP-NETIF, former tcpip_adapter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ESP-NETIF is an intermediary between an IO driver and a network stack, connecting packet data path between these two.
As that it provides a set of interfaces for attaching a driver to ESP-NETIF object (runtime) and
configuring a network stack (compile time). In addition to that a set of API is provided to control network interface lifecycle
and its TCP/IP properties. As an overview, the ESP-NETIF public interface could be divided into these 6 groups:
ESP-NETIF is an intermediary between an IO driver and a network stack, connecting packet data path between these two. As that it provides a set of interfaces for attaching a driver to ESP-NETIF object (runtime) and configuring a network stack (compile time). In addition to that a set of API is provided to control network interface lifecycle and its TCP/IP properties. As an overview, the ESP-NETIF public interface could be divided into these 6 groups:
1) Initialization APIs (to create and configure ESP-NETIF instance)
2) Input/Output API (for passing data between IO driver and network stack)
@ -132,8 +139,87 @@ and its TCP/IP properties. As an overview, the ESP-NETIF public interface could
D) Network stack
^^^^^^^^^^^^^^^^
Network stack has no public interaction with application code with regard to public interfaces and shall be fully abstracted by
ESP-NETIF API.
Network stack has no public interaction with application code with regard to public interfaces and shall be fully abstracted by ESP-NETIF API.
E) 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 that only Ethernet (IEEE 802.3) is currently supported.
From user perspective, 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). Refer to :doc:`/api-reference/storage/vfs` to learn more.
There is only one ESP-NETIF L2 TAP interface device (path name) available. However multiple file descriptors with different configuration can be opened at a time since the ESP-NETIF L2 TAP interface can be understood as generic entry point to the NETIF internal structure. Important is then specific configuration of particular file descriptor. It can be configured to give an access to specific Network Interface identified by ``if_key`` (e.g. `ETH_DEF`) and to filter only specific frames based on their type (e.g. Ethernet type in case of IEEE 802.3). Filtering only specific frames is crucial since the ESP-NETIF L2 TAP needs to work along with IP stack and so the IP related traffic (IP, ARP, etc.) should not be passed directly to the user application. Even though such option is still configurable, it is not recommended in standard use cases. Filtering is also advantageous from a perspective the users application gets access only to frame types it is interested in and the remaining traffic is either passed to other L2 TAP file descriptors or to IP stack.
ESP-NETIF L2 TAP Interface Usage Manual
---------------------------------------
Initialization
^^^^^^^^^^^^^^
To be able to use the ESP-NETIF L2 TAP interface, it needs to be enabled in Kconfig by :ref:`CONFIG_ESP_NETIF_L2_TAP` first and then registered by :cpp:func:`esp_vfs_l2tap_intf_register()` prior usage of any VFS function.
open()
^^^^^^
Once the ESP-NETIF L2 TAP is registered, it can be opened at path name “/dev/net/tap”. The same path path name can be opened multiple times up to :ref:`CONFIG_ESP_NETIF_L2_TAP_MAX_FDS` and multiple file descriptors with with different configuration may access the Data Link Layer in the NETIF.
The ESP-NETIF L2 TAP can be opened with ``O_NONBLOCK`` file status flag to the ``read()`` does not block. Note that the ``write()`` may block in current implementation when accessing a Network interface since it is a shared resource among multiple ESP-NETIF L2 TAP file descriptors and IP stack, and there is currently no queuing mechanism deployed. The file status flag can be retrieved and modified using ``fcntl()``.
On success, ``open()`` returns the new file descriptor (a nonnegative integer). On error, -1 is returned and ``errno`` is set to indicate the error.
ioctl()
^^^^^^^
The newly opened ESP-NETIF L2 TAP file descriptor needs to be configured prior its usage since it is not bounded to any specific Network Interface and no frame type filter is configured. The following configuration options are available to do so:
* ``L2TAP_S_INTF_DEVICE`` - bounds the file descriptor to specific Network Interface which is identified by its ``if_key``. Network Interface ``if_key`` is passed to ``ioctl()`` as the third parameter. Note that default Network Interfaces ``if_key``'s used in ESP-IDF can be found in :component_file:`esp_netif/include/esp_netif_defaults.h`.
* ``L2TAP_S_RCV_FILTER`` - sets the filter to frames with this type to be passed to the file descriptor. In case of Ethernet frames, the frames are to be filtered based on Length/Ethernet type field. In case the filter value is set less than or equal to 0x05DC, the Ethernet type field is considered to represent IEEE802.3 Length Field and all frames with values in interval <0, 0x05DC> at that field are to be passed to the file descriptor. The IEEE802.2 logical link control (LLC) resolution is then expected to be performed by users application. In case the filter value is set greater than 0x05DC, the Ethernet type field is considered to represent protocol identification and only frames which are equal to the set value are to be passed to the file descriptor.
All set configuration options have getter counterpart option to read the current settings.
.. note::
VLAN tagged frames are currently not recognized. If user needs to process VLAN tagged frames, they need set filter to be equal to VLAN tag (i.e. 0x8100 or 0x88A8) and process the VLAN tagged frames in user application.
| On success, ``ioctl()`` returns 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
| **EBADF** - not a valid file descriptor.
| **EINVAL** - invalid configuration argument. Ethernet type filter is already used by other file descriptor.
| **ENODEV** - no such Network Interface which is tried to be assigned to the file descriptor exists.
| **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.
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).
| On success, ``read()`` returns the number of bytes read. Zero is returned when size of the destination buffer is 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
| **EBADF** - not a valid file descriptor.
| **EAGAIN** - the file descriptor has been marked non-blocking (``O_NONBLOCK``), and the read would block.
write()
^^^^^^^
A raw Data Link Layer frame can be sent to Network Interface via opened and configured ESP-NETIF L2 TAP file descriptor. Users application is responsible to construct the whole frame except for fields which are added automatically by the physical interface device. The following fields need to be constructed by the user's application in case of Ethernet link: source/destination MAC addresses, Ethernet type, actual protocol header and user data. See below for more information about Ethernet frame structure.
.. code-block:: text
+-------------------+-------------------+-------------+------------------------------- --+
| Destination MAC | Source MAC | Type/Length | Payload (protocol header/data) ... |
+-------------------+-------------------+-------------+------------------------------- --+
6B 6B 2B 0-1486B
In other words, there is no additional frame processing performed by the ESP-NETIF L2 TAP interface. It only checks the Ethernet type of the frame is the same as the filter configured in the file descriptor. If the Ethernet type is different, an error is returned and the frame is not sent. Note that the ``write()`` may block in current implementation when accessing a Network interface since it is a shared resource among multiple ESP-NETIF L2 TAP file descriptors and IP stack, and there is currently no queuing mechanism deployed.
| On success, ``write()`` returns the number of bytes written. Zero is returned when size of the input buffer is 0. On error, -1 is returned, and ``errno`` is set to indicate the error.
| **EBADF** - not a valid file descriptor.
| **EBADMSG** - Ethernet type of the frame is different then file descriptor configured filter.
| **EIO** - Network interface not available or busy.
close()
^^^^^^^
Opened ESP-NETIF L2 TAP file descriptor can be closed by the ``close()`` to free its allocated resources. The ESP-NETIF L2 TAP implementation of ``close()`` may block. On the other hand, it is thread safe and can be called from different task than the file descriptor is actually used. If such situation occurs and one task is blocked in I/O operation and another task tries to close the file descriptor, the first task is unblocked. The first's task read operation then ends with error.
| On success, ``close()`` returns zero. On error, -1 is returned, and ``errno`` is set to indicate the error.
| **EBADF** - not a valid file descriptor.
select()
^^^^^^^^
Select is used in a standard way, just :ref:`CONFIG_VFS_SUPPORT_SELECT` needs to be enabled to be the ``select()`` function available.
ESP-NETIF programmer's manual
@ -151,14 +237,12 @@ For more specific cases please consult this guide: :doc:`/api-reference/network/
WiFi default initialization
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The initialization code as well as registering event handlers for default interfaces,
such as softAP and station, are provided in two separate APIs to facilitate simple startup code for most applications:
The initialization code as well as registering event handlers for default interfaces, such as softAP and station, are provided in two separate APIs to facilitate simple startup code for most applications:
* :cpp:func:`esp_netif_create_default_wifi_ap()`
* :cpp:func:`esp_netif_create_default_wifi_sta()`
Please note that these functions return the ``esp_netif`` handle, i.e. a pointer to a network interface object allocated and
configured with default settings, which as a consequence, means that:
Please note that these functions return the ``esp_netif`` handle, i.e. a pointer to a network interface object allocated and configured with default settings, which as a consequence, means that:
* The created object has to be destroyed if a network de-initialization is provided by an application using :cpp:func:`esp_netif_destroy_default_wifi()`.
* These *default* interfaces must not be created multiple times, unless the created handle is deleted using :cpp:func:`esp_netif_destroy()`.

Wyświetl plik

@ -41,14 +41,17 @@ Thread
Thread is an IPv6-based mesh networking technology for IoT.
Code examples for the Thread API are provided in the :example:`openthread` directory of ESP-IDF examples.
IP Network Layer
================
ESP-NETIF
=========
.. toctree::
:maxdepth: 1
ESP-NETIF <esp_netif.rst>
IP Network Layer
================
.. toctree::
:hidden:

Wyświetl plik

@ -674,14 +674,12 @@ components/esp_local_ctrl/src/esp_local_ctrl_transport_ble.c
components/esp_local_ctrl/src/esp_local_ctrl_transport_httpd.c
components/esp_netif/esp_netif_handlers.c
components/esp_netif/esp_netif_objects.c
components/esp_netif/include/esp_netif.h
components/esp_netif/include/esp_netif_net_stack.h
components/esp_netif/include/esp_netif_ppp.h
components/esp_netif/include/esp_netif_slip.h
components/esp_netif/include/esp_netif_sta_list.h
components/esp_netif/loopback/esp_netif_loopback.c
components/esp_netif/lwip/esp_netif_lwip_defaults.c
components/esp_netif/lwip/esp_netif_lwip_internal.h
components/esp_netif/lwip/esp_netif_lwip_ppp.c
components/esp_netif/lwip/esp_netif_lwip_ppp.h
components/esp_netif/lwip/esp_netif_lwip_slip.c
@ -1309,7 +1307,6 @@ components/hal/include/hal/dac_types.h
components/hal/include/hal/ds_hal.h
components/hal/include/hal/emac_hal.h
components/hal/include/hal/esp_flash_err.h
components/hal/include/hal/eth_types.h
components/hal/include/hal/gpio_hal.h
components/hal/include/hal/i2c_hal.h
components/hal/include/hal/i2c_types.h

Wyświetl plik

@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32"
TEST_COMPONENTS=esp_netif
CONFIG_ESP_NETIF_L2_TAP=y