Skip to content

Commit

Permalink
Merge branch 'examples/multiinterface_vlan_support' into 'master'
Browse files Browse the repository at this point in the history
network examples: Multiple ethernet interfaces and VLAN support.

Closes IDFGH-7826

See merge request espressif/esp-idf!19708
  • Loading branch information
espressif-abhikroy committed Sep 12, 2022
2 parents 449a995 + f3cc6d4 commit 88525d1
Show file tree
Hide file tree
Showing 9 changed files with 612 additions and 0 deletions.
20 changes: 20 additions & 0 deletions examples/network/vlan_support/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/ethernet/basic/components/ethernet_init)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

project(vlan_support)


# Enabling Vlan by injecting vlan hooks into lwip.
idf_component_get_property(lwip lwip COMPONENT_LIB)
target_compile_options(${lwip} PRIVATE "-I${PROJECT_DIR}/main")

target_compile_definitions(${lwip} PRIVATE "-DESP_IDF_LWIP_HOOK_FILENAME=\"vlan_hooks.h\""
"-DETHARP_SUPPORT_VLAN=1"
"-DLWIP_HOOK_VLAN_CHECK=lwip_vlan_check"
"-DLWIP_HOOK_VLAN_SET=lwip_vlan_set")
114 changes: 114 additions & 0 deletions examples/network/vlan_support/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |

# Ethernet VLAN Support Example

See the [README.md](../../ethernet/README.md) file in the upper level [examples](../../ethernet) directory for more information about examples.

## Overview

This example demonstrates the creation of one virtual network interface over Ethernet, an additional VLAN interface(disabled by default), and an interface without vlan tag. The work flow of the example could be as follows:

1. Install Ethernet driver for standard interface and both the virtual network interface.
2. Setup static IP address to both the virtual network interface.
3. Send DHCP requests and wait for a DHCP lease on the standard interface
4. If get IP address successfully, then you will be able to ping the standard interface of the device.
5. The virtual interfaces can be pinged over the configured static IP address.

Note: The code in vlan_support_main.c can be modified to add more vlan interfaces. The maximum number of interfaces that can be added is specified by the macro `MAX_ETH_NETIF_COUNT`.
## How to use example

### Hardware Required

To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip.

### Configure the project

```
idf.py menuconfig
```
See common configurations for Ethernet examples from [upper level](../../ethernet/README.md#common-configurations).

The Virtual network interface is enabled by default.
An additional virtual network interface can also be enabled.
The VLAN tag, Static IP address, network mask and default gateway of these interfaces are also configurable.

To configure the virtual network interfaces select:
```
Example Configuration --->
```

### Build, Flash, and Run

Build the project and flash it to the board, then run monitor tool to view serial output:

```
idf.py -p PORT build flash monitor
```

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type ``Ctrl-]``.)

See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.

## Example Output

```
I (0) cpu_start: Starting scheduler on APP CPU.
I (423) system_api: Base MAC address is not set
I (423) system_api: read default base MAC address from EFUSE
I (443) esp_eth.netif.netif_glue: 24:d7:eb:bb:f2:1b
I (443) esp_eth.netif.netif_glue: ethernet attached to netif
I (443) esp_eth.netif.netif_glue: 24:d7:eb:bb:f2:1b
I (443) esp_eth.netif.netif_glue: ethernet attached to netif
I (453) esp_eth.netif.netif_glue: 24:d7:eb:bb:f2:1b
I (463) esp_eth.netif.netif_glue: ethernet attached to netif
I (3563) eth_vlan_example: Ethernet Started
I (3563) eth_vlan_example: Ethernet Link Up
I (3563) eth_vlan_example: Ethernet HW Addr 24:d7:eb:bb:f2:1b
I (3563) eth_vlan_example: Ethernet interface(24:d7:eb:bb:f2:1b): ETH_VLAN20, Got IP Address
I (3573) eth_vlan_example: ~~~~~~~~~~~
I (3573) eth_vlan_example: ETHIP:192.168.20.10
I (3583) eth_vlan_example: ETHMASK:192.168.20.1
I (3583) eth_vlan_example: ETHGW:255.255.255.0
I (3593) eth_vlan_example: ~~~~~~~~~~~
I (3593) esp_netif_handlers: eth ip: 192.168.20.10, mask: 192.168.20.1, gw: 255.255.255.0
I (3603) eth_vlan_example: Ethernet interface(24:d7:eb:bb:f2:1b): ETH_VLAN30, Got IP Address
I (3613) eth_vlan_example: ~~~~~~~~~~~
I (3623) eth_vlan_example: ETHIP:192.168.30.10
I (3623) eth_vlan_example: ETHMASK:192.168.30.1
I (3633) eth_vlan_example: ETHGW:255.255.255.0
I (3633) eth_vlan_example: ~~~~~~~~~~~
I (3643) esp_netif_handlers: eth ip: 192.168.30.10, mask: 192.168.30.1, gw: 255.255.255.0
I (7943) eth_vlan_example: Ethernet interface(24:d7:eb:bb:f2:1b): ETH_DEF, Got IP Address
I (7943) eth_vlan_example: ~~~~~~~~~~~
I (7943) eth_vlan_example: ETHIP:192.168.10.100
I (7943) eth_vlan_example: ETHMASK:255.255.255.0
I (7953) eth_vlan_example: ETHGW:192.168.10.1
I (7953) eth_vlan_example: ~~~~~~~~~~~
I (7963) esp_netif_handlers: eth ip: 192.168.10.100, mask: 255.255.255.0, gw: 192.168.10.1
```

Now you can ping your ESP32 in the terminal.
You can ping the ESP32 VLAN interface from the VLAN interface of your system with default VLAN tag 20.

## Troubleshooting
### Setup VLAN on Linux
```
vconfig add eth0 20
ip addr add 192.168.20.30/24 dev eth0.20
ip link set eth0.20 up
```
Replace interface eth0 with your system ethernet interface.
### Setup VLAN on Mac
```
sudo ifconfig vlan20 create
sudo ifconfig vlan20 vlan 20 vlandev en5
sudo ifconfig vlan20 inet 192.168.20.30 netmask 255.255.255.0
```
Replace interface en5 with your system ethernet interface.

See common troubleshooting for Ethernet examples from [upper level](../../ethernet/README.md#common-troubleshooting).

(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
2 changes: 2 additions & 0 deletions examples/network/vlan_support/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "vlan_support_main.c" "eth_vlan_utils.c"
INCLUDE_DIRS ".")
60 changes: 60 additions & 0 deletions examples/network/vlan_support/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
menu "Example Configuration"

comment "Virtual Ethernet Interface Configuration"

config EXAMPLE_ETHERNET_VLAN_ID
int "VLAN identifier"
range 1 4094
default 20
help
Set the VLAN Id to the virtual interface

config EXAMPLE_VLAN_STATIC_IPV4_ADDR
string "IPV4 Address"
default "192.168.20.10"
help
The example will set this IPV4 address to this interface.

config EXAMPLE_VLAN_STATIC_ADDR_MASK
string "Subnet Mask"
default "255.255.255.0"

config EXAMPLE_VLAN_STATIC_ADDR_DEF_GW
string "IPV4 Default Gateway"
default "192.168.20.1"

config EXAMPLE_EXTRA_VLAN_INTERFACE
bool "Additional Vlan Interface"
default n
help
Enables an additional VLAN interface

if EXAMPLE_EXTRA_VLAN_INTERFACE
config EXAMPLE_EXTRA_ETHERNET_VLAN_ID
int "VLAN identifier"
range 1 4094
default 30
depends on EXAMPLE_EXTRA_VLAN_INTERFACE
help
Set the VLAN Id to the additional virtual interface

config EXAMPLE_EXTRA_VLAN_STATIC_IPV4_ADDR
string "IPV4 Address"
default "192.168.30.10"
depends on EXAMPLE_EXTRA_VLAN_INTERFACE
help
The example will set this IPV4 address to this interface.

config EXAMPLE_EXTRA_VLAN_STATIC_ADDR_MASK
string "Subnet Mask"
default "255.255.255.0"
depends on EXAMPLE_EXTRA_VLAN_INTERFACE

config EXAMPLE_EXTRA_VLAN_STATIC_ADDR_DEF_GW
string "IPV4 Default Gateway"
default "192.168.30.1"
depends on EXAMPLE_EXTRA_VLAN_INTERFACE

endif #EXAMPLE_EXTRA_VLAN_INTERFACE

endmenu
113 changes: 113 additions & 0 deletions examples/network/vlan_support/main/eth_vlan_utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_netif.h"
#include "esp_eth_netif_glue.h"
#include "esp_netif_net_stack.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_check.h"
#include "lwip/esp_netif_net_stack.h"
#include "sdkconfig.h"
#include "lwip/prot/ethernet.h"
#include "lwip/prot/ieee.h"
#include "eth_vlan_utils.h"
#if CONFIG_ESP_NETIF_L2_TAP
#include "esp_vfs_l2tap.h"
#endif


/**
* @brief This function gets invoked when Ethernet receive a new packets.
*
* @note This function is to be registered as a callback function which get invoked when Ethernet receive a new packets.
*
* @param eth_handle handle of Ethernet driver
* @param buffer buffer of the received packet
* @param length length of the received packet
* @param priv private pointer
*
* @return
* - ESP_OK: input frame buffer to upper stack successfully
* - ESP_FAIL: error occurred when inputting buffer to upper stack
*/
esp_err_t eth_input_to_netif(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv)
{
struct netif *lwip_netif;
u16_t netif_vlan_id;
struct eth_hdr *ethhdr = (struct eth_hdr *)buffer;
struct eth_vlan_hdr *vlan;
esp_vlan_netifs *vlan_netifs = (esp_vlan_netifs *)priv;

if (PP_HTONS(ETHTYPE_VLAN) != ethhdr->type) {
// L2 tap after VLAN is not supported.
#if CONFIG_ESP_NETIF_L2_TAP
esp_err_t ret = ESP_OK;
ret = esp_vfs_l2tap_eth_filter(eth_handle, buffer, (size_t *)&length);
if (length == 0) {
return ret;
}
#endif
return esp_netif_receive(vlan_netifs->esp_netif[0], buffer, length, NULL);
} else {
for (int i = 1; i < vlan_netifs->netif_count; i++) {
lwip_netif = esp_netif_get_netif_impl(vlan_netifs->esp_netif[i]);
netif_vlan_id = *((uint16_t *)netif_get_client_data(lwip_netif, LWIP_NETIF_CLIENT_DATA_INDEX_MAX + 1));

vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR);

if (VLAN_ID(vlan) == netif_vlan_id) {
return esp_netif_receive(vlan_netifs->esp_netif[i], buffer, length, NULL);
}
}

// If the vlan id of the incoming frame doesn't match with any of the interface send it to the default interface.
return esp_netif_receive(vlan_netifs->esp_netif[0], buffer, length, NULL);
}
}


/**
* @brief This function creates configuration for esp-netif Ethernet
*
* @param vlan_id handle of Ethernet driver, used to name the interface key.
* @param vlan_eth_cfg_o output parameter returning the esp-netif ethernet configuration.
*
* @return ESP_OK or ESP_FAIL
*/
esp_err_t get_vlan_netif_config(uint16_t vlan_id, esp_netif_config_t *vlan_eth_cfg_o)
{
// Create new default instance of VLAN esp-netif for Ethernet
char *if_key;
if (asprintf(&if_key, "ETH_VLAN%d", vlan_id) == -1) {
return ESP_FAIL;
}

esp_netif_inherent_config_t *esp_eth_vlan_base_config = malloc(sizeof(esp_netif_inherent_config_t));
if (NULL == esp_eth_vlan_base_config) {
return ESP_FAIL;
}
*esp_eth_vlan_base_config = (esp_netif_inherent_config_t)ESP_NETIF_INHERENT_DEFAULT_ETH();
esp_eth_vlan_base_config->if_key = if_key;

vlan_eth_cfg_o->base = esp_eth_vlan_base_config;
vlan_eth_cfg_o->driver = NULL;
vlan_eth_cfg_o->stack = ESP_NETIF_NETSTACK_DEFAULT_ETH;

return ESP_OK;
}

/**
* @brief This function frees the memory allocated for configuration for esp-netif Ethernet
*
* @param vlan_eth_cfg configuration for esp-netif Ethernet
*/
void free_vlan_config(esp_netif_config_t *vlan_eth_cfg)
{
if ((NULL != vlan_eth_cfg) && (NULL != vlan_eth_cfg->base)) {
free((void *)(vlan_eth_cfg->base->if_key));
free((void *)(vlan_eth_cfg->base));
}
}
50 changes: 50 additions & 0 deletions examples/network/vlan_support/main/eth_vlan_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/


// Maximum number of interface that can be added
#define MAX_ETH_NETIF_COUNT (10)

typedef struct {
esp_netif_t *esp_netif[MAX_ETH_NETIF_COUNT];
uint8_t netif_count;
} esp_vlan_netifs;


/**
* @brief This function gets invoked when Ethernet receive a new packets.
*
* @note This function is to be registered as a callback function which get invoked when Ethernet receive a new packets.
*
* @param eth_handle handle of Ethernet driver
* @param buffer buffer of the received packet
* @param length length of the received packet
* @param priv private pointer
*
* @return
* - ESP_OK: input frame buffer to upper stack successfully
* - ESP_FAIL: error occurred when inputting buffer to upper stack
*/
esp_err_t eth_input_to_netif(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv);


/**
* @brief This function creates configuration for esp-netif Ethernet
*
* @param vlan_id handle of Ethernet driver, used to name the interface key.
* @param vlan_eth_cfg_o output parameter returning the esp-netif ethernet configuration.
*
* @return ESP_OK or ESP_FAIL
*/
esp_err_t get_vlan_netif_config(uint16_t vlan_id, esp_netif_config_t *vlan_eth_cfg_o);


/**
* @brief This function frees the memory allocated for configuration for esp-netif Ethernet
*
* @param vlan_eth_cfg configuration for esp-netif Ethernet
*/
void free_vlan_config(esp_netif_config_t *vlan_eth_cfg);
Loading

0 comments on commit 88525d1

Please sign in to comment.